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

@@ -18,18 +18,32 @@ class QClasses(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "fa_classes" # QClases
__table_args__ = (
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"},
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
class_id: Mapped[int] = mapped_column(Integer, nullable=False)
import_tariff_code: Mapped[Optional[str]] = mapped_column(String(10), nullable=True) # FRACCIONIMPO
import_tariff_type: Mapped[Optional[str]] = mapped_column(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
import_tariff_code: Mapped[Optional[str]] = mapped_column(
String(10), nullable=True
) # FRACCIONIMPO
import_tariff_type: Mapped[Optional[str]] = mapped_column(
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
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 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
@@ -22,4 +29,4 @@ class DocumentTypeDigitization(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(Integer)
code: Mapped[str] = mapped_column(String(10), nullable=False, index=True)
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(
None, max_length=20, description="Location Description")
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):

View File

@@ -32,4 +32,4 @@ class Port(Base, TenantScopedMixin, TimestampMixin):
# New column requested
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
# 1. GUniMedACE
class UnitOfMeasureACE(Base, TimestampMixin):
__tablename__ = "unit_of_measure_ace"
__table_args__ = (
@@ -28,8 +26,6 @@ class UnitOfMeasureACE(Base, TimestampMixin):
# 2. GUMOMA
class UnitOfMeasureOMA(Base, TimestampMixin):
__tablename__ = "unit_of_measure_oma"
__table_args__ = (
@@ -43,8 +39,6 @@ class UnitOfMeasureOMA(Base, TimestampMixin):
# 3. GUMAme
class UnitOfMeasureAmerican(Base, TimestampMixin):
__tablename__ = "unit_of_measure_american"
__table_args__ = (
@@ -58,8 +52,6 @@ class UnitOfMeasureAmerican(Base, TimestampMixin):
# 4. GUMAduana
class UnitOfMeasureCustoms(Base, TimestampMixin):
__tablename__ = "unit_of_measure_customs"
__table_args__ = (
@@ -68,52 +60,15 @@ class UnitOfMeasureCustoms(Base, TimestampMixin):
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
code: Mapped[str] = mapped_column(String(2), nullable=False) # CLAVE
description: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
scaii_unit_code: Mapped[Optional[str]] = mapped_column(
String(5), nullable=True
) # UNIDADSCAII
code: Mapped[str] = mapped_column(Integer, nullable=False) # CLAVE
description: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
# 5. GUniMedida (Main)
class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "units_of_measure"
__table_args__ = (
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",
),
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_code"),
{"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_en: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
customs_code: Mapped[Optional[str]] = mapped_column(
String(2), nullable=True
) # CLAVE_AMEX
american_code: Mapped[Optional[str]] = mapped_column(
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
customs_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_customs.code"), nullable=True) # CLAVE_AMEX
american_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_american.code"), nullable=True) # CLAVE_AAMER
ace_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_ace.code"), nullable=True) # CLAVEACE
oma_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_oma.code"), nullable=True) # CLAVEOMA
# Relationships omitted for simplicity or need explicit primaryjoin
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship(
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"
)
american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship(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")
# 6. GUniMed (General/Conversion)
class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "units_of_measure_general"
__table_args__ = (
@@ -178,14 +117,10 @@ class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
)
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)
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(
String(2), nullable=True
) # CLAVE_ADUANA
ace_code: Mapped[Optional[str]] = mapped_column(
String(4), nullable=True
) # CLAVEACE
customs_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_customs.code"), nullable=True) # CLAVE_ADUANA
ace_code: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.unit_of_measure_ace.code"), nullable=True) # CLAVEACE
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(

View File

@@ -1,24 +1,24 @@
seed = [
('KGS', 'Kilo'),
('KW', 'Kilowatt'),
('MILLR', 'Millar'),
('JGO', 'Juego'),
('KWH', 'Kilowatt/Hora'),
('TON', 'Tonelada'),
('BARR', 'Barril'),
('GRN', 'Gramo Neto'),
('DEC', 'Decenas'),
('CIEN', 'Cientos'),
('DOCE', 'Decenas (Docenas)'),
('GR', 'Gramo'),
('CAJA', 'Caja'),
('PZA', 'Botella'),
('CARAT', 'Carat'),
('MT', 'Metro Lineal'),
('M2', 'Metro Cuadrado'),
('M3', 'Metro Cubico'),
('PZA', 'Pieza'),
('PZA', 'Cabeza'),
('LT', 'Litro'),
('PAR', 'Par'),
("1", "Kilo"),
("2", "Gramo"),
("3", "Metro Lineal"),
("4", "Metro Cuadrado"),
("5", "Metro Cubico"),
("6", "Pieza"),
("7", "Cabeza"),
("8", "Litro"),
("9", "Par"),
("10", "Kilowatt"),
("11", "Millar"),
("12", "Juego"),
("13", "Kilowatt/Hora"),
("14", "Tonelada"),
("15", "Barril"),
("16", "Gramo Neto"),
("17", "Decenas"),
("18", "Cientos"),
("19", "Decenas"),
("20", "Caja"),
("21", "Botella"),
("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,
Text,
TIMESTAMP,
func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from core.database import Base
@@ -101,7 +102,7 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
# Dates
invoice_date: Mapped[datetime] = mapped_column(Date) # FECHAFACTURA
capture_date: Mapped[datetime] = mapped_column(
TIMESTAMP(timezone=False), default=datetime.now
TIMESTAMP(timezone=False), server_default=func.now()
) # FECHACAPTURA + HORAACTUAL
emission_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAEMISION
@@ -153,13 +154,13 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
# Generation flags
generate_id: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # GENERAID
generate_desc_parties: Mapped[Optional[str]] = mapped_column(
String(12)
) # GENDESCPARTIDAS / Generar descripción de partidas
apply_manual_discount: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # APLICADESCMANUAL
# Bulk & Downloads
@@ -287,7 +288,7 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
Integer
) # APENDICE17 / Apéndice 17
is_regime_change: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # ESCAMBIOREGIMEN / Es cambio de régimen
which_exchange_rate: Mapped[Optional[str]] = mapped_column(
String(5)
@@ -299,15 +300,15 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
String(5)
) # ACTVALOR / Actualizar valor
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)
# Ownership & Balances
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
generate_balances: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # GENERARSALDOS / Generar saldos
was_reviewed_by_company: Mapped[Optional[bool]] = mapped_column(
Boolean
@@ -413,102 +414,102 @@ class InvoiceFinancials(Base, TenantScopedMixin, TimestampMixin):
# Merchandise Values (MN = National Currency, ME = Foreign Currency, MC = Third Currency)
value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOMN/VALOREXPOMN/VALORENTMN/VALORSALMN
value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOME/VALOREXPOME/VALORENTME/VALORSALME
value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
Numeric(23, 8), default=0, server_default="0"
) # VALORIMPOMC/VALOREXPOMC
# Customs Value
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
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
# Raw Materials
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
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
# Aggregate Value
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
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
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
# Mexican Merchandise Value
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
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
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
# National Packaging
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
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
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
# Costs & Increments
freight: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
Numeric(19, 8), default=0, server_default="0"
) # FLETE / Flete
insurance: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
Numeric(19, 8), default=0, server_default="0"
) # SEGUROS / Seguros
insurance_value: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
Numeric(19, 8), default=0, server_default="0"
) # VALSEGUROS / Valor seguros
packaging: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
Numeric(19, 8), default=0, server_default="0"
) # EMBALAJES / Embalajes
other_increments: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
Numeric(19, 8), default=0, server_default="0"
) # OTROSINCREMENTA / Otros incrementables
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
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
# Taxes
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
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
iva_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
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)
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
seal_value_2500: Mapped[Optional[bool]] = mapped_column(
Boolean
@@ -554,7 +555,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
String(10)
) # TRANSPORTISTAAME / Transportista americano
transport_type: Mapped[TransportType] = mapped_column(
String(15), default="none"
String(15), server_default="none"
) # TRANSPORTE / Tipo de transporte
transport_num: Mapped[Optional[str]] = mapped_column(
String(20)
@@ -566,7 +567,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
String(80)
) # CONDUCTOR / Nombre del conductor
is_rail: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # ESFERROCARRIL / Es ferrocarril
rail_id: Mapped[Optional[str]] = mapped_column(
String(31)
@@ -655,7 +656,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
# Delivery Control
delivered_status: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # ENTREGADO / Estado de entrega
received_by: Mapped[Optional[str]] = mapped_column(
String(50)
@@ -671,7 +672,7 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
# CTM Process
is_ctm_process: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
Boolean, default=False, server_default="false"
) # SETRATAPROCESOCTM / Se trata de proceso CTM
# Relationship

View File

@@ -89,7 +89,9 @@ class Part(Base, TenantScopedMixin, TimestampMixin):
export_code: Mapped[Optional[str]] = mapped_column(String(2))
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))
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)
ace_trailer_number = Column(String(10), nullable=True)
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)
entity_code = Column(String(1), nullable=True)

View File

@@ -44,21 +44,29 @@ class License(Base, TimestampMixin):
)
# 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(
SQLEnum(LicenseStatus), default=LicenseStatus.PENDING, nullable=False
SQLEnum(LicenseStatus),
default=LicenseStatus.PENDING,
server_default="PENDING",
nullable=False,
)
# Límites del plan
max_users = Column(Integer, default=5, nullable=False)
max_storage_gb = Column(Integer, default=10, nullable=False)
max_monthly_operations = Column(Integer, default=1000, nullable=False)
max_users = Column(Integer, server_default="5", nullable=False)
max_storage_gb = Column(Integer, server_default="10", nullable=False)
max_monthly_operations = Column(Integer, server_default="1000", nullable=False)
# Features habilitadas (booleans)
feature_api_access = Column(Boolean, default=True)
feature_advanced_reports = Column(Boolean, default=False)
feature_integrations = Column(Boolean, default=False)
feature_dedicated_support = Column(Boolean, default=False)
feature_api_access = Column(Boolean, default=True, server_default="true")
feature_advanced_reports = Column(Boolean, default=False, server_default="false")
feature_integrations = Column(Boolean, default=False, server_default="false")
feature_dedicated_support = Column(Boolean, default=False, server_default="false")
# Vigencia
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_end = Column(DateTime(timezone=True), nullable=False)
active_users = Column(Integer, default=0)
storage_used_gb = Column(Integer, default=0)
operations_count = Column(Integer, default=0)
api_calls_count = Column(Integer, default=0)
active_users = Column(Integer, default=0, server_default="0")
storage_used_gb = Column(Integer, default=0, server_default="0")
operations_count = Column(Integer, default=0, server_default="0")
api_calls_count = Column(Integer, default=0, server_default="0")
def __repr__(self):
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 .licenses.routes import router as licenses_router
from .permissions.routes import router as permissions_router
from .tenants.routes import router as tenants_router
from .user_tenant.routes import router as user_tenant_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(users_router, prefix="/core", tags=["core / users"])
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"])

View File

@@ -37,7 +37,12 @@ class Tenant(Base, TimestampMixin):
slug = Column(String(100), unique=True, nullable=False, index=True)
# 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 = Column(String(255), nullable=False)
@@ -51,7 +56,7 @@ class Tenant(Base, TimestampMixin):
contact_phone = Column(String(50))
# Estado
is_active = Column(Boolean, default=True, nullable=False)
is_active = Column(Boolean, default=True, server_default="true", nullable=False)
# Relación con UserTenant
user_relations: Mapped[List["UserTenant"]] = relationship(

View File

@@ -45,7 +45,9 @@ class UserTenant(Base, TenantScopedMixin, TimestampMixin):
)
# 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)
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
class TrailerType(Base, TenantScopedMixin, TimestampMixin):
class TrailerType(Base, TimestampMixin):
__tablename__ = "trailer_type"
__table_args__ = (
{"schema": "a76"},
{"schema": "public"},
)
trailer_type_key = Column(String(2), primary_key=True, nullable=False)