148 lines
4.6 KiB
Python
Executable File
148 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",
|
|
"v4.5": "v4.5_customer_max_discount.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()
|