"""crm rates: tarifarios (rate_sheets/lanes/breaks/charges) Revision ID: f8a9b0c1d2e3 Revises: e7f8a9b0c1d2 Create Date: 2026-07-27 00:00:00.000000 Módulo Tarifario: base de costos para Cotizaciones (import por Excel + motor de costeo). """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "f8a9b0c1d2e3" down_revision: Union[str, None] = "e7f8a9b0c1d2" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None SCHEMA = "crm" def _scoped() -> list[sa.Column]: return [ sa.Column("tenant_id", sa.Integer(), nullable=False), sa.Column("company_id", sa.Integer(), nullable=False), sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.text("now()")), sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.text("now()")), sa.Column("deleted_at", sa.DateTime(), nullable=True), ] def _idx(table: str) -> None: op.create_index(f"ix_{SCHEMA}_{table}_id", table, ["id"], schema=SCHEMA) op.create_index(f"ix_{SCHEMA}_{table}_tenant_id", table, ["tenant_id"], schema=SCHEMA) op.create_index(f"ix_{SCHEMA}_{table}_company_id", table, ["company_id"], schema=SCHEMA) def upgrade() -> None: # ----- rate_sheets ----- op.create_table( "rate_sheets", sa.Column("id", sa.Integer(), nullable=False), sa.Column("supplier_id", sa.Integer(), nullable=True), sa.Column("mode", sa.String(length=20), nullable=False), sa.Column("name", sa.String(length=255), nullable=False), sa.Column("currency", sa.String(length=3), nullable=True, server_default=sa.text("'USD'")), sa.Column("valid_from", sa.Date(), nullable=True), sa.Column("valid_to", sa.Date(), nullable=True), sa.Column("default_origin", sa.String(length=20), nullable=True), sa.Column("status", sa.String(length=20), nullable=False, server_default=sa.text("'borrador'")), sa.Column("source_file", sa.String(length=512), nullable=True), sa.Column("source_url", sa.String(length=1024), nullable=True), sa.Column("notes", sa.Text(), nullable=True), sa.Column("created_by", sa.String(length=64), nullable=True), sa.Column("updated_by", sa.String(length=64), nullable=True), *_scoped(), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["tenant_id"], ["core.tenants.id"]), sa.ForeignKeyConstraint(["supplier_id"], [f"{SCHEMA}.suppliers.id"]), schema=SCHEMA, ) _idx("rate_sheets") op.create_index("ix_crm_rate_sheets_mode", "rate_sheets", ["mode"], schema=SCHEMA) op.create_index("ix_crm_rate_sheets_supplier_id", "rate_sheets", ["supplier_id"], schema=SCHEMA) # ----- rate_lanes ----- op.create_table( "rate_lanes", sa.Column("id", sa.Integer(), nullable=False), sa.Column("rate_sheet_id", sa.Integer(), nullable=False), sa.Column("origin", sa.String(length=20), nullable=True), sa.Column("destination", sa.String(length=20), nullable=True), sa.Column("region", sa.String(length=60), nullable=True), sa.Column("equipment_type", sa.String(length=20), nullable=True), sa.Column("rate_unit", sa.String(length=20), nullable=True), sa.Column("min_charge", sa.Numeric(precision=14, scale=4), nullable=True), sa.Column("flat_rate", sa.Numeric(precision=14, scale=4), nullable=True), sa.Column("transit_days", sa.Integer(), nullable=True), sa.Column("notes", sa.Text(), nullable=True), *_scoped(), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["tenant_id"], ["core.tenants.id"]), sa.ForeignKeyConstraint(["rate_sheet_id"], [f"{SCHEMA}.rate_sheets.id"]), schema=SCHEMA, ) _idx("rate_lanes") op.create_index("ix_crm_rate_lanes_rate_sheet_id", "rate_lanes", ["rate_sheet_id"], schema=SCHEMA) op.create_index("ix_crm_rate_lanes_origin", "rate_lanes", ["origin"], schema=SCHEMA) op.create_index("ix_crm_rate_lanes_destination", "rate_lanes", ["destination"], schema=SCHEMA) # ----- rate_breaks ----- op.create_table( "rate_breaks", sa.Column("id", sa.Integer(), nullable=False), sa.Column("rate_lane_id", sa.Integer(), nullable=False), sa.Column("from_qty", sa.Numeric(precision=12, scale=3), nullable=False, server_default=sa.text("0")), sa.Column("rate", sa.Numeric(precision=14, scale=4), nullable=False, server_default=sa.text("0")), *_scoped(), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["tenant_id"], ["core.tenants.id"]), sa.ForeignKeyConstraint(["rate_lane_id"], [f"{SCHEMA}.rate_lanes.id"]), schema=SCHEMA, ) _idx("rate_breaks") op.create_index("ix_crm_rate_breaks_rate_lane_id", "rate_breaks", ["rate_lane_id"], schema=SCHEMA) # ----- rate_charges ----- op.create_table( "rate_charges", sa.Column("id", sa.Integer(), nullable=False), sa.Column("rate_sheet_id", sa.Integer(), nullable=True), sa.Column("rate_lane_id", sa.Integer(), nullable=True), sa.Column("concept", sa.String(length=60), nullable=False), sa.Column("charge_type", sa.String(length=20), nullable=False, server_default=sa.text("'fijo'")), sa.Column("value", sa.Numeric(precision=14, scale=4), nullable=True), sa.Column("condition", sa.Text(), nullable=True), *_scoped(), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["tenant_id"], ["core.tenants.id"]), sa.ForeignKeyConstraint(["rate_sheet_id"], [f"{SCHEMA}.rate_sheets.id"]), sa.ForeignKeyConstraint(["rate_lane_id"], [f"{SCHEMA}.rate_lanes.id"]), schema=SCHEMA, ) _idx("rate_charges") def downgrade() -> None: op.drop_table("rate_charges", schema=SCHEMA) op.drop_table("rate_breaks", schema=SCHEMA) op.drop_table("rate_lanes", schema=SCHEMA) op.drop_table("rate_sheets", schema=SCHEMA)