Refactor database models to use server defaults for boolean and timestamp fields

- Updated `TimestampMixin` to use `server_default` for `created_at` and `updated_at` fields.
- Modified various models in the `fa_classes`, `doc_types_dig`, `ports`, `units_of_measure`, `invoices`, `parts`, `trailers`, `licenses`, `tenants`, and `user_tenant` modules to set `server_default` for boolean fields and other relevant fields.
- Adjusted the `trailer_types` model to change the schema from `a76` to `public`.
- Implemented database migration execution during application startup in `main.py`.
- Removed old Alembic migration execution logic from the entrypoint script.
- Updated seed data for units of measure and removed unused seed files.
This commit is contained in:
2026-01-14 15:54:37 -06:00
parent afb3669c59
commit 4079a0cc0f
21 changed files with 329 additions and 639 deletions

View File

@@ -1,333 +0,0 @@
"""create material_types table
Revision ID: 531bf8cdae06
Revises:
Create Date: 2025-10-19 18:23:39.613953
"""
# pylint: disable=no-member
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "531bf8cdae06"
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(
"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"),
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(
"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(
"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(
"sectors",
sa.Column("key", sa.String(length=8), nullable=False),
sa.Column("description", sa.String(length=150), nullable=False),
sa.Column("authorized", sa.SmallInteger(), nullable=False),
sa.PrimaryKeyConstraint("key", name="sectors_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.Column("ame_key", sa.String(length=2), nullable=True),
sa.PrimaryKeyConstraint("m3_key", "description", name="states_pkey"),
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(
"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",
)
# 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('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'
)
# 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('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'
)
# 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('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'
)
# 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=10), nullable=False),
sa.Column('description', sa.String(length=50), nullable=True),
sa.Column('scaii_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'
)
# 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=10), 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(
['customs_code'],
['a76.unit_of_measure_customs.code'],
name='fk_uom_customs'
),
sa.ForeignKeyConstraint(
['american_code'],
['a76.unit_of_measure_american.code'],
name='fk_uom_american'
),
sa.ForeignKeyConstraint(
['ace_code'],
['a76.unit_of_measure_ace.code'],
name='fk_uom_ace'
),
sa.ForeignKeyConstraint(
['oma_code'],
['a76.unit_of_measure_oma.code'],
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=10), 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=10), nullable=True),
sa.Column('american_unit_code', sa.String(length=5), 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.ForeignKeyConstraint(
['customs_code'],
['a76.unit_of_measure_customs.code'],
name='fk_uom_general_customs'
),
sa.ForeignKeyConstraint(
['ace_code'],
['a76.unit_of_measure_ace.code'],
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")
op.drop_table("transport_modes", schema="public")
op.drop_table("states", schema="public")
op.drop_table("sectors", 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("invoice_types", schema="public")
op.drop_table("incoterms", 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_table("containers", schema="public")
# ### end Alembic commands ###

View File

@@ -50,18 +50,37 @@ from api.v1.modules.public.reference_data.transport_modes.seed import (
from api.v1.modules.public.reference_data.transport_types.seed import ( from api.v1.modules.public.reference_data.transport_types.seed import (
seed as transport_types_seed, seed as transport_types_seed,
) )
from api.v1.modules.public.reference_data.trailer_types.seed import (
seed as trailer_types_seed,
)
from api.v1.modules.public.reference_data.valuation_methods.seed import ( from api.v1.modules.public.reference_data.valuation_methods.seed import (
seed as valuation_methods_seed, seed as valuation_methods_seed,
) )
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_med import seed as units_of_measure_seed from api.v1.modules.a76.general_catalogs.units_of_measure.seed import (
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_ace import seed as ace_seed seed as units_of_measure_seed,
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_oma import seed as oma_seed )
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_ame import seed as ame_seed from api.v1.modules.a76.general_catalogs.units_of_measure.seed_ace import (
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_adua import seed as adua_seed seed as ace_seed,
)
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_oma import (
seed as oma_seed,
)
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_ame import (
seed as ame_seed,
)
from api.v1.modules.a76.general_catalogs.units_of_measure.seed_adua import (
seed as adua_seed,
)
from api.v1.modules.core.permissions.seed import (
seed_invoices,
seed_user,
seed_report,
seed_roles,
)
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision: str = "7937209f9718" revision: str = "7937209f9718"
down_revision: Union[str, Sequence[str], None] = "531bf8cdae06" down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None
@@ -69,25 +88,10 @@ depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None: def upgrade() -> None:
"""Upgrade schema.""" """Upgrade schema."""
# --- AMPLIAR COLUMNAS ANTES DE INSERTAR DATOS ---
op.execute("ALTER TABLE a76.unit_of_measure_ace ALTER COLUMN code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.unit_of_measure_oma ALTER COLUMN code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.unit_of_measure_american ALTER COLUMN code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.unit_of_measure_customs ALTER COLUMN code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.units_of_measure ALTER COLUMN code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.units_of_measure ALTER COLUMN customs_code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.units_of_measure ALTER COLUMN american_code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.units_of_measure ALTER COLUMN ace_code TYPE VARCHAR(20);")
op.execute("ALTER TABLE a76.units_of_measure ALTER COLUMN oma_code TYPE VARCHAR(20);")
# --- AGREGAR COLUMNAS deleted_at ---
op.execute("ALTER TABLE a76.units_of_measure ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP;")
op.execute("ALTER TABLE a76.units_of_measure_general ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMP;")
# --- UTILIDAD DE FORMATEO --- # --- UTILIDAD DE FORMATEO ---
def format_value(val): def format_value(val):
if val is None or str(val).strip() == '' or str(val).upper() == 'NONE': if val is None or str(val).strip() == "" or str(val).upper() == "NONE":
return 'NULL' return "NULL"
return f"'{str(val).replace(chr(39), chr(39)*2)}'" return f"'{str(val).replace(chr(39), chr(39)*2)}'"
# --- SEEDS PUBLIC (Tablas base) --- # --- SEEDS PUBLIC (Tablas base) ---
@@ -302,6 +306,20 @@ def upgrade() -> None:
""" """
) )
values_trailert = ", ".join(
[
f"('{code}', '{desc.replace(chr(39), chr(39)*2)}')"
for code, desc in trailer_types_seed
]
)
op.execute(
f"""
INSERT INTO public.trailer_type (trailer_type_key, description) VALUES
{values_trailert}
ON CONFLICT (trailer_type_key) DO NOTHING;
"""
)
values_vm = ", ".join( values_vm = ", ".join(
[ [
f"('{key}', '{desc.replace(chr(39), chr(39)*2)}')" f"('{key}', '{desc.replace(chr(39), chr(39)*2)}')"
@@ -319,94 +337,159 @@ def upgrade() -> None:
# --- SEEDS A76 (Unidades de Medida) --- # --- SEEDS A76 (Unidades de Medida) ---
# ACE # ACE
val_ace = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in ace_seed]) val_ace = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_ace (code, description) VALUES {val_ace} ON CONFLICT ON CONSTRAINT uq_uom_ace_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in ace_seed]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_ace (code, description) VALUES {val_ace} ON CONFLICT ON CONSTRAINT uq_uom_ace_code DO NOTHING;"
)
# OMA # OMA
val_oma = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in oma_seed]) val_oma = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_oma (code, description) VALUES {val_oma} ON CONFLICT ON CONSTRAINT uq_uom_oma_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in oma_seed]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_oma (code, description) VALUES {val_oma} ON CONFLICT ON CONSTRAINT uq_uom_oma_code DO NOTHING;"
)
# AME # AME
val_ame = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in ame_seed]) val_ame = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_american (code, description) VALUES {val_ame} ON CONFLICT ON CONSTRAINT uq_uom_american_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in ame_seed]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_american (code, description) VALUES {val_ame} ON CONFLICT ON CONSTRAINT uq_uom_american_code DO NOTHING;"
)
# ADUA (Customs) # ADUA (Customs)
val_adua = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in adua_seed]) val_adua = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_customs (code, description) VALUES {val_adua} ON CONFLICT ON CONSTRAINT uq_uom_customs_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in adua_seed]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_customs (code, description) VALUES {val_adua} ON CONFLICT ON CONSTRAINT uq_uom_customs_code DO NOTHING;"
)
# Recolectar códigos adicionales que faltan en los catálogos # Recolectar códigos adicionales que faltan en los catálogos
additional_customs = set() additional_customs = set()
additional_american = set() additional_american = set()
additional_ace = set() additional_ace = set()
additional_oma = set() additional_oma = set()
existing_customs = {c for c, d in adua_seed} existing_customs = {c for c, d in adua_seed}
existing_american = {c for c, d in ame_seed} existing_american = {c for c, d in ame_seed}
existing_ace = {c for c, d in ace_seed} existing_ace = {c for c, d in ace_seed}
existing_oma = {c for c, d in oma_seed} existing_oma = {c for c, d in oma_seed}
for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed: for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed:
if customs and customs.strip() and customs not in existing_customs: if customs and customs.strip() and customs not in existing_customs:
additional_customs.add((customs, f'Auto-generated from {code}')) additional_customs.add((customs, f"Auto-generated from {code}"))
if american and american.strip() and american not in existing_american: if american and american.strip() and american not in existing_american:
additional_american.add((american, f'Auto-generated from {code}')) additional_american.add((american, f"Auto-generated from {code}"))
if ace and ace.strip() and ace not in existing_ace: if ace and ace.strip() and ace not in existing_ace:
additional_ace.add((ace, f'Auto-generated from {code}')) additional_ace.add((ace, f"Auto-generated from {code}"))
if oma and oma.strip() and oma not in existing_oma: if oma and oma.strip() and oma not in existing_oma:
additional_oma.add((oma, f'Auto-generated from {code}')) additional_oma.add((oma, f"Auto-generated from {code}"))
# Insertar códigos adicionales # Insertar códigos adicionales
if additional_customs: if additional_customs:
val_add_customs = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in additional_customs]) val_add_customs = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_customs (code, description) VALUES {val_add_customs} ON CONFLICT ON CONSTRAINT uq_uom_customs_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in additional_customs]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_customs (code, description, a76_unit_code) VALUES {val_add_customs} ON CONFLICT ON CONSTRAINT uq_uom_customs_code DO NOTHING;"
)
if additional_american: if additional_american:
val_add_american = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in additional_american]) val_add_american = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_american (code, description) VALUES {val_add_american} ON CONFLICT ON CONSTRAINT uq_uom_american_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in additional_american]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_american (code, description) VALUES {val_add_american} ON CONFLICT ON CONSTRAINT uq_uom_american_code DO NOTHING;"
)
if additional_ace: if additional_ace:
val_add_ace = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in additional_ace]) val_add_ace = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_ace (code, description) VALUES {val_add_ace} ON CONFLICT ON CONSTRAINT uq_uom_ace_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in additional_ace]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_ace (code, description) VALUES {val_add_ace} ON CONFLICT ON CONSTRAINT uq_uom_ace_code DO NOTHING;"
)
if additional_oma: if additional_oma:
val_add_oma = ", ".join([f"({format_value(c)}, {format_value(d)})" for c, d in additional_oma]) val_add_oma = ", ".join(
op.execute(f"INSERT INTO a76.unit_of_measure_oma (code, description) VALUES {val_add_oma} ON CONFLICT ON CONSTRAINT uq_uom_oma_code DO NOTHING;") [f"({format_value(c)}, {format_value(d)})" for c, d in additional_oma]
)
op.execute(
f"INSERT INTO a76.unit_of_measure_oma (code, description) VALUES {val_add_oma} ON CONFLICT ON CONSTRAINT uq_uom_oma_code DO NOTHING;"
)
# TABLA MAESTRA UOM # TABLA MAESTRA UOM
val_uom = ", ".join([ # TODO: Generar tenant_id y company_id correctos
f"({format_value(code)}, {format_value(desc)}, {format_value(desc_en)}, " val_uom = ", ".join(
f"{format_value(customs)}, {format_value(american)}, {format_value(ace)}, {format_value(oma)}, 1, 1)" [
for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed f"({format_value(code)}, {format_value(desc)}, {format_value(desc_en)}, "
]) f"{format_value(customs)}, {format_value(american)}, {format_value(ace)}, {format_value(oma)}, 1, 1)"
op.execute(f""" for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed
]
)
op.execute("ALTER TABLE a76.units_of_measure DISABLE TRIGGER ALL;")
op.execute(
f"""
INSERT INTO a76.units_of_measure INSERT INTO a76.units_of_measure
(code, description, description_en, customs_code, american_code, ace_code, oma_code, tenant_id, company_id) (code, description, description_en, customs_code, american_code, ace_code, oma_code, tenant_id, company_id)
VALUES {val_uom} VALUES {val_uom}
ON CONFLICT (code, tenant_id, company_id) DO NOTHING; ON CONFLICT (code, tenant_id, company_id) DO NOTHING;
""") """
)
op.execute("ALTER TABLE a76.units_of_measure ENABLE TRIGGER ALL;")
# --- SEEDS CORE (Permissions) ---
# Combinar todas las seeds de permisos
all_permissions = seed_invoices + seed_user + seed_report + seed_roles
values_permissions = ", ".join(
[
f"({format_value(code)}, {format_value(description)}, {format_value(module)}, {format_value(action)})"
for code, description, module, action in all_permissions
]
)
if values_permissions:
op.execute(
f"""
INSERT INTO core.permissions (code, description, module, action)
VALUES {values_permissions}
ON CONFLICT (code) DO NOTHING;
"""
)
def downgrade() -> None: def downgrade() -> None:
"""Downgrade schema.""" """Downgrade schema."""
op.execute("DELETE FROM a76.units_of_measure;") op.drop_table("valuation_methods", schema="public")
op.execute("DELETE FROM a76.unit_of_measure_customs;") op.drop_table("transport_types", schema="public")
op.execute("DELETE FROM a76.unit_of_measure_american;") op.drop_table("transport_modes", schema="public")
op.execute("DELETE FROM a76.unit_of_measure_oma;") op.drop_table("sectors", schema="public")
op.execute("DELETE FROM a76.unit_of_measure_ace;") op.drop_table("payment_methods", schema="public")
op.execute("DELETE FROM public.valuation_methods;") op.drop_table("material_types", schema="public")
op.execute("DELETE FROM public.transport_types;") op.drop_table("invoice_types", schema="public")
op.execute("DELETE FROM public.transport_modes;") op.drop_table("incoterms", schema="public")
op.execute("DELETE FROM public.sectors;") op.drop_table("customs_warehouses", schema="public")
op.execute("DELETE FROM public.payment_methods;") op.drop_table("customs_sections", schema="public")
op.execute("DELETE FROM public.material_types;") op.drop_table("currency_types", schema="public")
op.execute("DELETE FROM public.invoice_types;") op.drop_table("countries", schema="public")
op.execute("DELETE FROM public.incoterms;") op.drop_table("containers", schema="public")
op.execute("DELETE FROM public.customs_warehouses;") op.drop_table("code_pedimento_regimens", schema="public")
op.execute("DELETE FROM public.customs_sections;") op.drop_table("pedimento_regimens", schema="public")
op.execute("DELETE FROM public.currency_types;") op.drop_table("pedimento_codes", schema="public")
op.execute("DELETE FROM public.countries;") op.drop_table("user_client_permissions", schema="core")
op.execute("DELETE FROM public.containers;") op.drop_table("user_client_roles", schema="core")
op.execute("DELETE FROM public.code_pedimento_regimens;") op.drop_table("role_permissions", schema="core")
op.execute("DELETE FROM public.pedimento_regimens;") op.drop_table("client_roles", schema="core")
op.execute("DELETE FROM public.pedimento_codes;") op.drop_table("permissions", schema="core")
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")

View File

@@ -9,10 +9,10 @@ class TimestampMixin:
"""Mixin for common timestamp fields""" """Mixin for common timestamp fields"""
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=func.now() DateTime, nullable=False, server_default=func.now()
) )
updated_at: Mapped[datetime] = mapped_column( updated_at: Mapped[datetime] = mapped_column(
DateTime, nullable=False, default=func.now(), onupdate=func.now() DateTime, nullable=False, server_default=func.now(), onupdate=func.now()
) )
deleted_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) deleted_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)

View File

@@ -18,18 +18,32 @@ class QClasses(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "fa_classes" # QClases __tablename__ = "fa_classes" # QClases
__table_args__ = ( __table_args__ = (
PrimaryKeyConstraint("id", name="qclases_pk"), PrimaryKeyConstraint("id", name="qclases_pk"),
ForeignKeyConstraint(["class_id"], ["a76.classes.id"], name="fk_qclasses_classes"), ForeignKeyConstraint(
["class_id"], ["a76.classes.id"], name="fk_qclasses_classes"
),
{"schema": "a24"}, {"schema": "a24"},
) )
id: Mapped[int] = mapped_column(Integer, primary_key=True) id: Mapped[int] = mapped_column(Integer, primary_key=True)
class_id: Mapped[int] = mapped_column(Integer, nullable=False) class_id: Mapped[int] = mapped_column(Integer, nullable=False)
import_tariff_code: Mapped[Optional[str]] = mapped_column(String(10), nullable=True) # FRACCIONIMPO import_tariff_code: Mapped[Optional[str]] = mapped_column(
import_tariff_type: Mapped[Optional[str]] = mapped_column(String(6), nullable=True) # TIPOFRACIMPO String(10), nullable=True
export_tariff_code: Mapped[Optional[str]] = mapped_column(String(10), nullable=True) # FRACCIONEXPO ) # FRACCIONIMPO
export_tariff_type: Mapped[Optional[str]] = mapped_column(String(6), nullable=True) # TIPOFRACEXPO import_tariff_type: Mapped[Optional[str]] = mapped_column(
depreciation_rate: Mapped[Optional[Decimal]] = mapped_column(Numeric(5, 2), nullable=True) # TASADEPRECIA String(6), nullable=True
) # TIPOFRACIMPO
export_tariff_code: Mapped[Optional[str]] = mapped_column(
String(10), nullable=True
) # FRACCIONEXPO
export_tariff_type: Mapped[Optional[str]] = mapped_column(
String(6), nullable=True
) # TIPOFRACEXPO
depreciation_rate: Mapped[Optional[Decimal]] = mapped_column(
Numeric(5, 2), nullable=True
) # TASADEPRECIA
fda_code: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # FDA fda_code: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # FDA
eccn_code: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # ECCN eccn_code: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # ECCN
class_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) # HABILITADESHABILITACLASE class_enabled: Mapped[bool] = mapped_column(
Boolean, default=True, server_default="true", nullable=False
) # HABILITADESHABILITACLASE

View File

@@ -1,6 +1,13 @@
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base from core.database import Base
from sqlalchemy import Boolean, Integer, PrimaryKeyConstraint, String, Text, UniqueConstraint from sqlalchemy import (
Boolean,
Integer,
PrimaryKeyConstraint,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
@@ -22,4 +29,4 @@ class DocumentTypeDigitization(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer) id: Mapped[int] = mapped_column(Integer)
code: Mapped[str] = mapped_column(String(10), nullable=False, index=True) code: Mapped[str] = mapped_column(String(10), nullable=False, index=True)
description: Mapped[str] = mapped_column(Text, nullable=False) description: Mapped[str] = mapped_column(Text, nullable=False)
active: Mapped[bool] = mapped_column(Boolean, default=True) active: Mapped[bool] = mapped_column(Boolean, default=True, server_default="true")

View File

@@ -11,7 +11,7 @@ class PortBase(BaseModel):
location_description: Optional[str] = Field( location_description: Optional[str] = Field(
None, max_length=20, description="Location Description") None, max_length=20, description="Location Description")
port_type: PortType = Field( port_type: PortType = Field(
default=PortType.ENTRY, description="Port Type (ENTRY, EXIT, BOTH)") server_default=PortType.ENTRY, description="Port Type (ENTRY, EXIT, BOTH)")
class PortCreate(PortBase): class PortCreate(PortBase):

View File

@@ -32,4 +32,4 @@ class Port(Base, TenantScopedMixin, TimestampMixin):
# New column requested # New column requested
port_type: Mapped[PortType] = mapped_column( port_type: Mapped[PortType] = mapped_column(
String(15), nullable=False, default=PortType.ENTRY) String(15), nullable=False, server_default=PortType.ENTRY)

View File

@@ -13,8 +13,6 @@ from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base from core.database import Base
# 1. GUniMedACE # 1. GUniMedACE
class UnitOfMeasureACE(Base, TimestampMixin): class UnitOfMeasureACE(Base, TimestampMixin):
__tablename__ = "unit_of_measure_ace" __tablename__ = "unit_of_measure_ace"
__table_args__ = ( __table_args__ = (
@@ -28,8 +26,6 @@ class UnitOfMeasureACE(Base, TimestampMixin):
# 2. GUMOMA # 2. GUMOMA
class UnitOfMeasureOMA(Base, TimestampMixin): class UnitOfMeasureOMA(Base, TimestampMixin):
__tablename__ = "unit_of_measure_oma" __tablename__ = "unit_of_measure_oma"
__table_args__ = ( __table_args__ = (
@@ -43,8 +39,6 @@ class UnitOfMeasureOMA(Base, TimestampMixin):
# 3. GUMAme # 3. GUMAme
class UnitOfMeasureAmerican(Base, TimestampMixin): class UnitOfMeasureAmerican(Base, TimestampMixin):
__tablename__ = "unit_of_measure_american" __tablename__ = "unit_of_measure_american"
__table_args__ = ( __table_args__ = (
@@ -58,8 +52,6 @@ class UnitOfMeasureAmerican(Base, TimestampMixin):
# 4. GUMAduana # 4. GUMAduana
class UnitOfMeasureCustoms(Base, TimestampMixin): class UnitOfMeasureCustoms(Base, TimestampMixin):
__tablename__ = "unit_of_measure_customs" __tablename__ = "unit_of_measure_customs"
__table_args__ = ( __table_args__ = (
@@ -68,52 +60,15 @@ class UnitOfMeasureCustoms(Base, TimestampMixin):
) )
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
code: Mapped[str] = mapped_column(String(2), nullable=False) # CLAVE code: Mapped[str] = mapped_column(Integer, nullable=False) # CLAVE
description: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) description: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
scaii_unit_code: Mapped[Optional[str]] = mapped_column(
String(5), nullable=True
) # UNIDADSCAII
# 5. GUniMedida (Main) # 5. GUniMedida (Main)
class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin): class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "units_of_measure" __tablename__ = "units_of_measure"
__table_args__ = ( __table_args__ = (
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_code"), UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_code"),
ForeignKeyConstraint(
["customs_code"],
[
"a76.unit_of_measure_customs.code"
],
use_alter=True,
name="fk_uom_customs",
),
ForeignKeyConstraint(
["american_code"],
[
"a76.unit_of_measure_american.code"
],
use_alter=True,
name="fk_uom_american",
),
ForeignKeyConstraint(
["ace_code"],
[
"a76.unit_of_measure_ace.code"
],
use_alter=True,
name="fk_uom_ace",
),
ForeignKeyConstraint(
["oma_code"],
[
"a76.unit_of_measure_oma.code"
],
use_alter=True,
name="fk_uom_oma",
),
{"schema": "a76", "extend_existing": True}, {"schema": "a76", "extend_existing": True},
) )
@@ -122,35 +77,19 @@ class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin):
description: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) description: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
description_en: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) description_en: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
customs_code: Mapped[Optional[str]] = mapped_column( customs_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_customs.code"), nullable=True) # CLAVE_AMEX
String(2), nullable=True american_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_american.code"), nullable=True) # CLAVE_AAMER
) # CLAVE_AMEX ace_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_ace.code"), nullable=True) # CLAVEACE
american_code: Mapped[Optional[str]] = mapped_column( oma_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_oma.code"), nullable=True) # CLAVEOMA
String(3), nullable=True
) # CLAVE_AAMER
ace_code: Mapped[Optional[str]] = mapped_column(
String(4), nullable=True
) # CLAVEACE
oma_code: Mapped[Optional[str]] = mapped_column(
String(10), nullable=True
) # CLAVEOMA
# Relationships omitted for simplicity or need explicit primaryjoin # Relationships omitted for simplicity or need explicit primaryjoin
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship() customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship( american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship(overlaps="customs_unit")
overlaps="customs_unit" ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(overlaps="american_unit,customs_unit")
) oma_unit: Mapped[Optional["UnitOfMeasureOMA"]] = relationship(overlaps="ace_unit,american_unit,customs_unit")
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(
overlaps="american_unit,customs_unit"
)
oma_unit: Mapped[Optional["UnitOfMeasureOMA"]] = relationship(
overlaps="ace_unit,american_unit,customs_unit"
)
# 6. GUniMed (General/Conversion) # 6. GUniMed (General/Conversion)
class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin): class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "units_of_measure_general" __tablename__ = "units_of_measure_general"
__table_args__ = ( __table_args__ = (
@@ -178,14 +117,10 @@ class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
) )
mexico_unit: Mapped[Optional[str]] = mapped_column(String(5), nullable=True) mexico_unit: Mapped[Optional[str]] = mapped_column(String(5), nullable=True)
# UNIDAD_AME (Note: GUniMed has UNIDAD_AME varchar(5), but GUMAme has CLAVE varchar(3). Keeping as string for now) # UNIDAD_AME (Note: GUniMed has UNIDAD_AME varchar(5), but GUMAme has CLAVE varchar(3). Keeping as string for now)
american_unit_code: Mapped[Optional[str]] = mapped_column(String(5), nullable=True) american_unit_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_american.code"), nullable=True)
customs_code: Mapped[Optional[str]] = mapped_column( customs_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_customs.code"), nullable=True) # CLAVE_ADUANA
String(2), nullable=True ace_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_ace.code"), nullable=True) # CLAVEACE
) # CLAVE_ADUANA
ace_code: Mapped[Optional[str]] = mapped_column(
String(4), nullable=True
) # CLAVEACE
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship() customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship( ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(

View File

@@ -1,24 +1,24 @@
seed = [ seed = [
('KGS', 'Kilo'), ("1", "Kilo"),
('KW', 'Kilowatt'), ("2", "Gramo"),
('MILLR', 'Millar'), ("3", "Metro Lineal"),
('JGO', 'Juego'), ("4", "Metro Cuadrado"),
('KWH', 'Kilowatt/Hora'), ("5", "Metro Cubico"),
('TON', 'Tonelada'), ("6", "Pieza"),
('BARR', 'Barril'), ("7", "Cabeza"),
('GRN', 'Gramo Neto'), ("8", "Litro"),
('DEC', 'Decenas'), ("9", "Par"),
('CIEN', 'Cientos'), ("10", "Kilowatt"),
('DOCE', 'Decenas (Docenas)'), ("11", "Millar"),
('GR', 'Gramo'), ("12", "Juego"),
('CAJA', 'Caja'), ("13", "Kilowatt/Hora"),
('PZA', 'Botella'), ("14", "Tonelada"),
('CARAT', 'Carat'), ("15", "Barril"),
('MT', 'Metro Lineal'), ("16", "Gramo Neto"),
('M2', 'Metro Cuadrado'), ("17", "Decenas"),
('M3', 'Metro Cubico'), ("18", "Cientos"),
('PZA', 'Pieza'), ("19", "Decenas"),
('PZA', 'Cabeza'), ("20", "Caja"),
('LT', 'Litro'), ("21", "Botella"),
('PAR', 'Par'), ("22", "Carat"),
] ]

View File

@@ -1,44 +0,0 @@
seed = [
# (code, desc_es, desc_en, customs, american, ace, oma)
('BARR', 'BARRIL', 'BARIEL', '', 'BBL', 'BLL', ''),
('BD FT', 'PIE TABLA', 'BD FEET', '', 'FT', 'BFT', ''),
('BOLS', 'BOLSA', 'BAG', '', 'PCS', 'BG', ''),
('BTL', 'BOTELLA', 'BOTTLE', '', 'PCS', 'BO', ''),
('BULT', 'BULTO', 'BULK', '', 'PCS', 'VQ', ''),
('CAJA', 'CAJA', 'BOX', '', '', 'BX', ''),
('CARAT', 'CARAT', 'CARAT', '', '', 'HE', ''),
('CBZA', 'CABEZA', 'HEAD', '', 'PCS', 'Z4', ''),
('CIEN', 'CIENTO', 'CIEN', '', '', 'CEN', ''),
('CM', 'CENTIMETRO', 'CM', 'CM', 'CM', 'CMT', ''),
('CM2', 'CENTIMETRO CUADRADO', 'CM2', 'CM2', 'CM2', 'CMK', ''),
('DEC', 'DECENA', '', '', '', 'DC', ''),
('DM', 'DECIMETRO', 'DM', '', '', 'DMT', ''),
('DM2', 'DECIMETRO CUADRADO', 'SQ DM', '', '', 'DMK', ''),
('DOCE', 'DOCENA', 'DOZ', '', 'DOZ', 'DZN', 'DZ'),
('FOZ', 'ONZA LIQUIDA', 'FOZ', 'FOZ', 'FOZ', 'OZA', ''),
('FT', 'PIES', 'FT', 'FT', 'FT', 'LF', ''),
('FT2', 'PIE CUADRADO', 'FT2', '', 'SFT', 'FTK', ''),
('GAL', 'GALON', 'GAL', 'GAL', 'GAL', 'GLL', ''),
('GR', 'GRAMO', 'GRAM', '', '', 'GRM', ''),
('IN', 'PULGADA', 'IN', '', '', 'LI', ''),
('IN2', 'PULGADA CUADRADA', 'IN2', '', '', 'INK', ''),
('JGO', 'JUEGO', 'SET', '', '', 'SET', ''),
('KGS', 'KILOGRAMOS', 'KGS', '', 'KG2', 'KGM', ''),
('LB', 'LIBRAS', 'LB', '', '', 'LBR', ''),
('LT', 'LITRO', 'LT', 'LT', 'L', 'LTR', ''),
('M2', 'METRO CUADRADO', 'M2', 'M2', 'M2', 'MTK', ''),
('M3', 'METRO CUBICO', 'M3', 'M3', 'M3', 'MTQ', ''),
('MI', 'MILLA', 'MILE', '', 'KM', 'SMI', ''),
('MILLR', 'MILLAR', 'MILLR', '', '', 'MIL', ''),
('MT', 'METROS', 'MT', 'MT', 'M', 'MTR', ''),
('OZ', 'ONZA', 'OZ', 'FOZ', 'FOZ', 'OZ', ''),
('PAR', 'PAR', 'PAIR', '', '', 'PB', ''),
('PQ', 'PAQUETE', 'PACKAGE', '', 'PCS', 'PK_1', ''),
('PZA', 'PIEZA', 'PCS', 'PCS', 'PCS', 'C62_1', ''),
('QGL', 'CUARTO DE GALON', 'QGL', '', '', 'QT', ''),
('ROLL', 'ROLLO', 'ROLL', '', '', 'RO', ''),
('TON', 'TONELADA', 'TON', 'TON', 'TON', 'TNE_1', ''),
('TOZ', 'ONZA TROY', 'TOZ', '', 'TOZ', 'APZ', ''),
('YD', 'YARDA', 'YD', 'YD', 'YD', 'YRD', ''),
('YD2', 'YARDA CUADRADA', 'YD2', '', 'SYD', 'YDK', ''),
]

View File

@@ -10,6 +10,7 @@ from sqlalchemy import (
String, String,
Text, Text,
TIMESTAMP, TIMESTAMP,
func,
) )
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from core.database import Base from core.database import Base
@@ -101,7 +102,7 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
# Dates # Dates
invoice_date: Mapped[datetime] = mapped_column(Date) # FECHAFACTURA invoice_date: Mapped[datetime] = mapped_column(Date) # FECHAFACTURA
capture_date: Mapped[datetime] = mapped_column( capture_date: Mapped[datetime] = mapped_column(
TIMESTAMP(timezone=False), default=datetime.now TIMESTAMP(timezone=False), server_default=func.now()
) # FECHACAPTURA + HORAACTUAL ) # FECHACAPTURA + HORAACTUAL
emission_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAEMISION emission_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAEMISION
@@ -153,13 +154,13 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
# Generation flags # Generation flags
generate_id: Mapped[Optional[bool]] = mapped_column( generate_id: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # GENERAID ) # GENERAID
generate_desc_parties: Mapped[Optional[str]] = mapped_column( generate_desc_parties: Mapped[Optional[str]] = mapped_column(
String(12) String(12)
) # GENDESCPARTIDAS / Generar descripción de partidas ) # GENDESCPARTIDAS / Generar descripción de partidas
apply_manual_discount: Mapped[Optional[bool]] = mapped_column( apply_manual_discount: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # APLICADESCMANUAL ) # APLICADESCMANUAL
# Bulk & Downloads # Bulk & Downloads
@@ -287,7 +288,7 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
Integer Integer
) # APENDICE17 / Apéndice 17 ) # APENDICE17 / Apéndice 17
is_regime_change: Mapped[Optional[bool]] = mapped_column( is_regime_change: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # ESCAMBIOREGIMEN / Es cambio de régimen ) # ESCAMBIOREGIMEN / Es cambio de régimen
which_exchange_rate: Mapped[Optional[str]] = mapped_column( which_exchange_rate: Mapped[Optional[str]] = mapped_column(
String(5) String(5)
@@ -299,15 +300,15 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
String(5) String(5)
) # ACTVALOR / Actualizar valor ) # ACTVALOR / Actualizar valor
is_pedimento_pending: Mapped[Optional[bool]] = mapped_column( is_pedimento_pending: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # PED_PENDIENTE_ASIGNAR (Mapear 1 -> True, 0 -> False) ) # PED_PENDIENTE_ASIGNAR (Mapear 1 -> True, 0 -> False)
# Ownership & Balances # Ownership & Balances
is_owner_of_goods: Mapped[Optional[bool]] = mapped_column( is_owner_of_goods: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # ESDUENOMCIA / Es dueño de mercancía ) # ESDUENOMCIA / Es dueño de mercancía
generate_balances: Mapped[Optional[bool]] = mapped_column( generate_balances: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # GENERARSALDOS / Generar saldos ) # GENERARSALDOS / Generar saldos
was_reviewed_by_company: Mapped[Optional[bool]] = mapped_column( was_reviewed_by_company: Mapped[Optional[bool]] = mapped_column(
Boolean Boolean
@@ -413,102 +414,102 @@ class InvoiceFinancials(Base, TenantScopedMixin, TimestampMixin):
# Merchandise Values (MN = National Currency, ME = Foreign Currency, MC = Third Currency) # Merchandise Values (MN = National Currency, ME = Foreign Currency, MC = Third Currency)
value_mn: Mapped[Optional[float]] = mapped_column( value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOMN/VALOREXPOMN/VALORENTMN/VALORSALMN ) # VALORIMPOMN/VALOREXPOMN/VALORENTMN/VALORSALMN
value_me: Mapped[Optional[float]] = mapped_column( value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOME/VALOREXPOME/VALORENTME/VALORSALME ) # VALORIMPOME/VALOREXPOME/VALORENTME/VALORSALME
value_mc: Mapped[Optional[float]] = mapped_column( value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOMC/VALOREXPOMC ) # VALORIMPOMC/VALOREXPOMC
# Customs Value # Customs Value
customs_value_mn: Mapped[Optional[float]] = mapped_column( customs_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORADUANASMN / Valor en aduanas MN ) # VALORADUANASMN / Valor en aduanas MN
customs_value_me: Mapped[Optional[float]] = mapped_column( customs_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORADUANASME / Valor en aduanas ME ) # VALORADUANASME / Valor en aduanas ME
# Raw Materials # Raw Materials
raw_material_value_mn: Mapped[Optional[float]] = mapped_column( raw_material_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORMPMN / Valor materia prima MN ) # VALORMPMN / Valor materia prima MN
raw_material_value_me: Mapped[Optional[float]] = mapped_column( raw_material_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORMPME / Valor materia prima ME ) # VALORMPME / Valor materia prima ME
# Aggregate Value # Aggregate Value
aggregate_value_mn: Mapped[Optional[float]] = mapped_column( aggregate_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORAGREMN / Valor agregado MN ) # VALORAGREMN / Valor agregado MN
aggregate_value_me: Mapped[Optional[float]] = mapped_column( aggregate_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORAGREME / Valor agregado ME ) # VALORAGREME / Valor agregado ME
aggregate_value_mc: Mapped[Optional[float]] = mapped_column( aggregate_value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORAGREMC / Valor agregado MC ) # VALORAGREMC / Valor agregado MC
# Mexican Merchandise Value # Mexican Merchandise Value
mexican_value_mn: Mapped[Optional[float]] = mapped_column( mexican_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORVMEXMN / Valor mercancía mexicana MN ) # VALORVMEXMN / Valor mercancía mexicana MN
mexican_value_me: Mapped[Optional[float]] = mapped_column( mexican_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORVMEXME / Valor mercancía mexicana ME ) # VALORVMEXME / Valor mercancía mexicana ME
mexican_value_mc: Mapped[Optional[float]] = mapped_column( mexican_value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORVMEXMC / Valor mercancía mexicana MC ) # VALORVMEXMC / Valor mercancía mexicana MC
# National Packaging # National Packaging
national_packaging_mn: Mapped[Optional[float]] = mapped_column( national_packaging_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALEMPAQUENACMN / Valor empaque nacional MN ) # VALEMPAQUENACMN / Valor empaque nacional MN
national_packaging_me: Mapped[Optional[float]] = mapped_column( national_packaging_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALEMPAQUENACME / Valor empaque nacional ME ) # VALEMPAQUENACME / Valor empaque nacional ME
national_packaging_mc: Mapped[Optional[float]] = mapped_column( national_packaging_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALEMPAQUENACMC / Valor empaque nacional MC ) # VALEMPAQUENACMC / Valor empaque nacional MC
# Costs & Increments # Costs & Increments
freight: Mapped[Optional[float]] = mapped_column( freight: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0 Numeric(19, 8), default=0, server_default="0"
) # FLETE / Flete ) # FLETE / Flete
insurance: Mapped[Optional[float]] = mapped_column( insurance: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0 Numeric(19, 8), default=0, server_default="0"
) # SEGUROS / Seguros ) # SEGUROS / Seguros
insurance_value: Mapped[Optional[float]] = mapped_column( insurance_value: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0 Numeric(19, 8), default=0, server_default="0"
) # VALSEGUROS / Valor seguros ) # VALSEGUROS / Valor seguros
packaging: Mapped[Optional[float]] = mapped_column( packaging: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0 Numeric(19, 8), default=0, server_default="0"
) # EMBALAJES / Embalajes ) # EMBALAJES / Embalajes
other_increments: Mapped[Optional[float]] = mapped_column( other_increments: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0 Numeric(19, 8), default=0, server_default="0"
) # OTROSINCREMENTA / Otros incrementables ) # OTROSINCREMENTA / Otros incrementables
total_increments_mn: Mapped[Optional[float]] = mapped_column( total_increments_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # TOTALINCREMMN / Total incrementables MN ) # TOTALINCREMMN / Total incrementables MN
total_increments_me: Mapped[Optional[float]] = mapped_column( total_increments_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # TOTALINCREMME / Total incrementables ME ) # TOTALINCREMME / Total incrementables ME
# Taxes # Taxes
iva_mn: Mapped[Optional[float]] = mapped_column( iva_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # IVAEXPOMN/VALORIVAMN / IVA en MN ) # IVAEXPOMN/VALORIVAMN / IVA en MN
iva_me: Mapped[Optional[float]] = mapped_column( iva_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # IVAEXPOME/VALORIVAME / IVA en ME ) # IVAEXPOME/VALORIVAME / IVA en ME
iva_mc: Mapped[Optional[float]] = mapped_column( iva_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # IVAEXPOMC / IVA en MC ) # IVAEXPOMC / IVA en MC
iva_factor: Mapped[Optional[str]] = mapped_column( iva_factor: Mapped[Optional[str]] = mapped_column(
String(10) String(10)
) # FACTORIVA / Factor IVA (puede ser varchar en imports) ) # FACTORIVA / Factor IVA (puede ser varchar en imports)
tax_value_me: Mapped[Optional[float]] = mapped_column( tax_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0 Numeric(23, 8), default=0, server_default="0"
) # VALORIMPUESTOME / Valor impuesto ME ) # VALORIMPUESTOME / Valor impuesto ME
seal_value_2500: Mapped[Optional[bool]] = mapped_column( seal_value_2500: Mapped[Optional[bool]] = mapped_column(
Boolean Boolean
@@ -554,7 +555,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
String(10) String(10)
) # TRANSPORTISTAAME / Transportista americano ) # TRANSPORTISTAAME / Transportista americano
transport_type: Mapped[TransportType] = mapped_column( transport_type: Mapped[TransportType] = mapped_column(
String(15), default="none" String(15), server_default="none"
) # TRANSPORTE / Tipo de transporte ) # TRANSPORTE / Tipo de transporte
transport_num: Mapped[Optional[str]] = mapped_column( transport_num: Mapped[Optional[str]] = mapped_column(
String(20) String(20)
@@ -566,7 +567,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
String(80) String(80)
) # CONDUCTOR / Nombre del conductor ) # CONDUCTOR / Nombre del conductor
is_rail: Mapped[Optional[bool]] = mapped_column( is_rail: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # ESFERROCARRIL / Es ferrocarril ) # ESFERROCARRIL / Es ferrocarril
rail_id: Mapped[Optional[str]] = mapped_column( rail_id: Mapped[Optional[str]] = mapped_column(
String(31) String(31)
@@ -655,7 +656,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
# Delivery Control # Delivery Control
delivered_status: Mapped[Optional[bool]] = mapped_column( delivered_status: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # ENTREGADO / Estado de entrega ) # ENTREGADO / Estado de entrega
received_by: Mapped[Optional[str]] = mapped_column( received_by: Mapped[Optional[str]] = mapped_column(
String(50) String(50)
@@ -671,7 +672,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
# CTM Process # CTM Process
is_ctm_process: Mapped[Optional[bool]] = mapped_column( is_ctm_process: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False Boolean, default=False, server_default="false"
) # SETRATAPROCESOCTM / Se trata de proceso CTM ) # SETRATAPROCESOCTM / Se trata de proceso CTM
# Relationship # Relationship

View File

@@ -89,7 +89,9 @@ class Part(Base, TenantScopedMixin, TimestampMixin):
export_code: Mapped[Optional[str]] = mapped_column(String(2)) export_code: Mapped[Optional[str]] = mapped_column(String(2))
exclusion_symbol: Mapped[Optional[str]] = mapped_column(String(19)) exclusion_symbol: Mapped[Optional[str]] = mapped_column(String(19))
is_active: Mapped[Optional[bool]] = mapped_column(Boolean, default=True) is_active: Mapped[Optional[bool]] = mapped_column(
Boolean, default=True, server_default="true"
)
part_photo: Mapped[Optional[str]] = mapped_column(String(255)) part_photo: Mapped[Optional[str]] = mapped_column(String(255))
creation_date: Mapped[Optional[int]] = mapped_column() creation_date: Mapped[Optional[int]] = mapped_column()

View File

@@ -12,7 +12,7 @@ class Trailer(Base, TenantScopedMixin, TimestampMixin):
trailer_number = Column(String(20), primary_key=True, nullable=False) trailer_number = Column(String(20), primary_key=True, nullable=False)
ace_trailer_number = Column(String(10), nullable=True) ace_trailer_number = Column(String(10), nullable=True)
trailer_type_key = Column( trailer_type_key = Column(
String(2), ForeignKey("a76.trailer_type.trailer_type_key"), nullable=True String(2), ForeignKey("public.trailer_type.trailer_type_key"), nullable=True
) )
seal = Column(String(15), nullable=True) seal = Column(String(15), nullable=True)
entity_code = Column(String(1), nullable=True) entity_code = Column(String(1), nullable=True)

View File

@@ -44,21 +44,29 @@ class License(Base, TimestampMixin):
) )
# Plan y características # Plan y características
plan = Column(SQLEnum(LicensePlan), default=LicensePlan.FREE, nullable=False) plan = Column(
SQLEnum(LicensePlan),
default=LicensePlan.FREE,
server_default="FREE",
nullable=False,
)
status = Column( status = Column(
SQLEnum(LicenseStatus), default=LicenseStatus.PENDING, nullable=False SQLEnum(LicenseStatus),
default=LicenseStatus.PENDING,
server_default="PENDING",
nullable=False,
) )
# Límites del plan # Límites del plan
max_users = Column(Integer, default=5, nullable=False) max_users = Column(Integer, server_default="5", nullable=False)
max_storage_gb = Column(Integer, default=10, nullable=False) max_storage_gb = Column(Integer, server_default="10", nullable=False)
max_monthly_operations = Column(Integer, default=1000, nullable=False) max_monthly_operations = Column(Integer, server_default="1000", nullable=False)
# Features habilitadas (booleans) # Features habilitadas (booleans)
feature_api_access = Column(Boolean, default=True) feature_api_access = Column(Boolean, default=True, server_default="true")
feature_advanced_reports = Column(Boolean, default=False) feature_advanced_reports = Column(Boolean, default=False, server_default="false")
feature_integrations = Column(Boolean, default=False) feature_integrations = Column(Boolean, default=False, server_default="false")
feature_dedicated_support = Column(Boolean, default=False) feature_dedicated_support = Column(Boolean, default=False, server_default="false")
# Vigencia # Vigencia
starts_at = Column(DateTime(timezone=True), nullable=False) starts_at = Column(DateTime(timezone=True), nullable=False)
@@ -85,10 +93,10 @@ class LicenseUsage(Base, TimestampMixin):
period_start = Column(DateTime(timezone=True), nullable=False) period_start = Column(DateTime(timezone=True), nullable=False)
period_end = Column(DateTime(timezone=True), nullable=False) period_end = Column(DateTime(timezone=True), nullable=False)
active_users = Column(Integer, default=0) active_users = Column(Integer, default=0, server_default="0")
storage_used_gb = Column(Integer, default=0) storage_used_gb = Column(Integer, default=0, server_default="0")
operations_count = Column(Integer, default=0) operations_count = Column(Integer, default=0, server_default="0")
api_calls_count = Column(Integer, default=0) api_calls_count = Column(Integer, default=0, server_default="0")
def __repr__(self): def __repr__(self):
return f"<LicenseUsage(tenant_id={self.tenant_id}, operations={self.operations_count})>" return f"<LicenseUsage(tenant_id={self.tenant_id}, operations={self.operations_count})>"

View File

@@ -1,5 +1,6 @@
from .auth.routes import router as auth_router from .auth.routes import router as auth_router
from .licenses.routes import router as licenses_router from .licenses.routes import router as licenses_router
from .permissions.routes import router as permissions_router
from .tenants.routes import router as tenants_router from .tenants.routes import router as tenants_router
from .user_tenant.routes import router as user_tenant_router from .user_tenant.routes import router as user_tenant_router
from .users.routes import router as users_router from .users.routes import router as users_router
@@ -13,4 +14,5 @@ router.include_router(tenants_router, prefix="/core", tags=["core / tenants"])
router.include_router(user_tenant_router, prefix="/core", tags=["core / user-tenants"]) router.include_router(user_tenant_router, prefix="/core", tags=["core / user-tenants"])
router.include_router(users_router, prefix="/core", tags=["core / users"]) router.include_router(users_router, prefix="/core", tags=["core / users"])
router.include_router(licenses_router, prefix="/core", tags=["core / licenses"]) router.include_router(licenses_router, prefix="/core", tags=["core / licenses"])
router.include_router(permissions_router, prefix="/core", tags=["core / permissions"])
router.include_router(dashboard_router, prefix="/core", tags=["core / dashboard"]) router.include_router(dashboard_router, prefix="/core", tags=["core / dashboard"])

View File

@@ -37,7 +37,12 @@ class Tenant(Base, TimestampMixin):
slug = Column(String(100), unique=True, nullable=False, index=True) slug = Column(String(100), unique=True, nullable=False, index=True)
# Tipo de tenant (compartido o dedicado) # Tipo de tenant (compartido o dedicado)
type = Column(SQLEnum(TenantType), default=TenantType.SHARED, nullable=False) type = Column(
SQLEnum(TenantType),
default=TenantType.SHARED,
server_default="SHARED",
nullable=False,
)
# Keycloak realm asociado # Keycloak realm asociado
keycloak_realm = Column(String(255), nullable=False) keycloak_realm = Column(String(255), nullable=False)
@@ -51,7 +56,7 @@ class Tenant(Base, TimestampMixin):
contact_phone = Column(String(50)) contact_phone = Column(String(50))
# Estado # Estado
is_active = Column(Boolean, default=True, nullable=False) is_active = Column(Boolean, default=True, server_default="true", nullable=False)
# Relación con UserTenant # Relación con UserTenant
user_relations: Mapped[List["UserTenant"]] = relationship( user_relations: Mapped[List["UserTenant"]] = relationship(

View File

@@ -45,7 +45,9 @@ class UserTenant(Base, TenantScopedMixin, TimestampMixin):
) )
# Estado de la relación # Estado de la relación
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) is_active: Mapped[bool] = mapped_column(
Boolean, default=True, server_default="true", nullable=False
)
# Información adicional - Rol del usuario en este tenant (opcional) # Información adicional - Rol del usuario en este tenant (opcional)
role: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) role: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)

View File

@@ -3,10 +3,10 @@ from core.database import Base
from sqlalchemy import Column, ForeignKeyConstraint, String from sqlalchemy import Column, ForeignKeyConstraint, String
class TrailerType(Base, TenantScopedMixin, TimestampMixin): class TrailerType(Base, TimestampMixin):
__tablename__ = "trailer_type" __tablename__ = "trailer_type"
__table_args__ = ( __table_args__ = (
{"schema": "a76"}, {"schema": "public"},
) )
trailer_type_key = Column(String(2), primary_key=True, nullable=False) trailer_type_key = Column(String(2), primary_key=True, nullable=False)

View File

@@ -4,6 +4,7 @@ Backend API con FastAPI + Keycloak + SQLAlchemy
""" """
import logging import logging
import subprocess
from api.v1.router import router as api_v1_router from api.v1.router import router as api_v1_router
from core.config import settings from core.config import settings
@@ -75,12 +76,19 @@ async def http_exception_handler(request: Request, exc: HTTPException):
) )
def run_migrations():
subprocess.run(
["alembic", "upgrade", "head"],
check=True
)
# Inicializar la base de datos # Inicializar la base de datos
@app.on_event("startup") @app.on_event("startup")
async def on_startup(): async def on_startup():
"""Evento de inicio de la aplicación""" """Evento de inicio de la aplicación"""
logger.info("Iniciando la aplicación Anexo76...") logger.info("Iniciando la aplicación Anexo76...")
init_db() init_db()
run_migrations()
logger.info("Base de datos inicializada correctamente.") logger.info("Base de datos inicializada correctamente.")

View File

@@ -41,18 +41,18 @@ wait_for_tcp "${CORE_DB_HOST:-postgres-a76}" "${CORE_DB_PORT:-5432}" "PostgreSQL
wait_for_tcp "keycloak" "8080" "Keycloak" wait_for_tcp "keycloak" "8080" "Keycloak"
# Ejecutar migraciones de Alembic # Ejecutar migraciones de Alembic
if [ -d "/app/alembic" ]; then #if [ -d "/app/alembic" ]; then
echo "Ejecutando migraciones de Alembic..." # echo "Ejecutando migraciones de Alembic..."
alembic upgrade head || { # alembic upgrade head || {
echo "⚠ WARNING: Error al ejecutar migraciones" # echo "⚠ WARNING: Error al ejecutar migraciones"
echo " Verificando estado de la base de datos..." # echo " Verificando estado de la base de datos..."
alembic current || echo " No se pudo determinar la versión actual" # alembic current || echo " No se pudo determinar la versión actual"
} # }
echo "✓ Migraciones completadas" # echo "✓ Migraciones completadas"
else #else
echo "⚠ WARNING: Directorio /app/alembic no encontrado" # echo "⚠ WARNING: Directorio /app/alembic no encontrado"
echo " Las migraciones de base de datos no se ejecutaron" # echo " Las migraciones de base de datos no se ejecutaron"
fi #fi
echo "==========================================" echo "=========================================="
echo "Iniciando aplicación FastAPI..." echo "Iniciando aplicación FastAPI..."

View File

@@ -530,7 +530,7 @@ if [ $TENANT_QUERY_STATUS -ne 0 ]; then
echo -e "${RED}✗ Error al consultar el tenant:${NC}" echo -e "${RED}✗ Error al consultar el tenant:${NC}"
echo "$TENANT_ID_RESULT" echo "$TENANT_ID_RESULT"
echo -e "${YELLOW}Verificando si la tabla existe...${NC}" echo -e "${YELLOW}Verificando si la tabla existe...${NC}"
docker exec -e PGPASSWORD="${POSTGRES_PASSWORD}" anexo76-postgres-a76 psql -h localhost -p 5432 -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "\dt core.tenants;" exec_pg_sql "\dt core.tenants;"
exit 1 exit 1
fi fi
@@ -540,7 +540,7 @@ if [ -z "$TENANT_ID" ]; then
echo -e "${RED}✗ Error: No se pudo obtener el ID del tenant${NC}" echo -e "${RED}✗ Error: No se pudo obtener el ID del tenant${NC}"
echo "Resultado de la consulta: '$TENANT_ID_RESULT'" echo "Resultado de la consulta: '$TENANT_ID_RESULT'"
echo -e "${YELLOW}Intentando ver todos los tenants...${NC}" echo -e "${YELLOW}Intentando ver todos los tenants...${NC}"
docker exec -e PGPASSWORD="${POSTGRES_PASSWORD}" anexo76-postgres-a76 psql -h localhost -p 5432 -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "SELECT id, slug FROM core.tenants LIMIT 10;" exec_pg_sql "SELECT id, slug FROM core.tenants LIMIT 10;"
exit 1 exit 1
fi fi