Files
Autoparts-DB/pos/migrations/runner.py
consultoria-as ce66212223 feat(pos/workshop): add lightweight workshop/taller module
- Add DB migration v4.4_workshop.sql (sale_id, service_catalog,
  reserved_quantity, SO_RESERVE/SO_RELEASE operation types).
- Extend service_order_engine with inventory reservation, release,
  convert-to-sale, mechanic assignment, and service catalog CRUD.
- Extend service_order_bp with /reserve, /convert-to-sale,
  /assign-mechanic, and /service-catalog endpoints.
- Create workshop Kanban UI: workshop.html, workshop.js, workshop.css.
- Add /pos/workshop route and sidebar navigation (sidebar.js + inline
  templates).
- Add 11 unit tests with mocked cursors.
- Update FASES_IMPLEMENTADAS.md with FASE 9 documentation.

Tests: 92 passing (61 console + 20 Facturapi + 11 workshop).
2026-06-15 05:34:35 +00:00

147 lines
4.6 KiB
Python
Executable File

#!/usr/bin/env python3
# /home/Autopartes/pos/migrations/runner.py
"""Apply schema migrations to all tenant databases."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from tenant_db import get_master_conn, get_tenant_conn_by_dbname
MIGRATIONS_DIR = os.path.dirname(os.path.abspath(__file__))
# Migration registry: version -> filename
MIGRATIONS = {
"v1.0": "v1.0_initial.sql",
"v1.1": "v1.1_pos_tables.sql",
"v1.2": "v1.2_subdomain.sql",
"v1.3": "v1.3_fleet.sql",
"v1.4": "v1.4_whatsapp.sql",
"v1.5": "v1.5_returns.sql",
"v1.6": "v1.6_marketplace.sql",
"v1.7": "v1.7_plates.sql",
"v1.8": "v1.8_performance_indexes.sql",
"v1.9": "v1.9_redis_cache.sql",
"v2.0": "v2.0_multi_currency.sql",
"v2.1": "v2.1_suppliers.sql",
"v2.2": "v2.2_alerts_warranty.sql",
"v2.3": "v2.3_metabase.sql",
"v2.4": "v2.4_crm_enhanced.sql",
"v2.5": "v2.5_service_orders.sql",
"v2.6": "v2.6_bnpl_erp.sql",
"v2.7": "v2.7_notifications.sql",
"v2.8": "v2.8_savings.sql",
"v2.9": "v2.9_logistics.sql",
"v3.0": "v3.0_public_api.sql",
"v3.1": "v3.1_inventory_vehicle_compat.sql",
"v3.2": "v3.2_db_performance.sql",
"v3.2.1": "v3.2_qwen_vehicle_compat.sql",
"v3.3": "v3.3_marketplace_any_part.sql",
"v3.3.1": "v3.3_materialized_view.sql",
"v3.4": "v3.4_meli_integration.sql",
"v3.5": "v3.5_meli_questions.sql",
"v3.5.1": "v3.5_whatsapp_state_machine.sql",
"v3.6": "v3.6_dropshipping.sql",
"v3.7": "v3.7_sku_aliases.sql",
"v3.8": "v3.8_supplier_catalog.sql",
"v3.9": "v3.9_supplier_catalog_prices.sql",
"v4.0": "v4.0_multi_branch.sql",
"v4.1": "v4.1_global_invoice.sql",
"v4.2": "v4.2_meli_sync_queue.sql",
"v4.3": "v4.3_facturapi.sql",
"v4.4": "v4.4_workshop.sql",
}
def get_all_tenants():
"""Get all tenants with their current schema version."""
conn = get_master_conn()
cur = conn.cursor()
cur.execute("""
SELECT t.id, t.db_name, t.name, COALESCE(v.version, 'v0.0') as version
FROM tenants t
LEFT JOIN tenant_schema_version v ON t.id = v.tenant_id
WHERE t.is_active = true
""")
tenants = cur.fetchall()
cur.close()
conn.close()
return tenants
def apply_migration(db_name, version):
"""Apply a single migration to a tenant DB."""
filename = MIGRATIONS[version]
filepath = os.path.join(MIGRATIONS_DIR, filename)
if not os.path.exists(filepath):
print(f" ERROR: Migration file not found: {filepath}")
return False
with open(filepath) as f:
sql = f.read()
# Skip migrations marked for manual/non-tenant execution
first_line = sql.splitlines()[0].strip() if sql.strip() else ""
if first_line.startswith(": SKIP") or first_line.startswith("-- : SKIP"):
print(" SKIP (manual/non-tenant migration)")
return True
conn = get_tenant_conn_by_dbname(db_name)
cur = conn.cursor()
try:
cur.execute(sql)
conn.commit()
return True
except Exception as e:
conn.rollback()
print(f" ERROR: {e}")
return False
finally:
cur.close()
conn.close()
def run_migrations():
"""Apply pending migrations to all tenants."""
tenants = get_all_tenants()
sorted_versions = sorted(MIGRATIONS.keys())
print(f"Found {len(tenants)} active tenants")
print(f"Available migrations: {sorted_versions}")
for tenant_id, db_name, name, current_version in tenants:
print(f"\n[{name}] (db={db_name}, current={current_version})")
for version in sorted_versions:
if version <= current_version:
continue
print(f" Applying {version}...", end=" ")
if apply_migration(db_name, version):
# Update version in master
master_conn = get_master_conn()
master_cur = master_conn.cursor()
master_cur.execute(
"""
INSERT INTO tenant_schema_version (tenant_id, version)
VALUES (%s, %s)
ON CONFLICT (tenant_id) DO UPDATE SET version = %s, updated_at = NOW()
""",
(tenant_id, version, version),
)
master_conn.commit()
master_cur.close()
master_conn.close()
print("OK")
else:
print("FAILED — stopping migrations for this tenant")
break
print("\nDone.")
if __name__ == "__main__":
run_migrations()