156 lines
5.8 KiB
Python
156 lines
5.8 KiB
Python
from __future__ import annotations
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
from sqlalchemy import inspect, text
|
|
from .extensions import db
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Migration:
|
|
version: str
|
|
upgrade: Callable[[], None]
|
|
|
|
|
|
def add_station_access_mode() -> None:
|
|
inspector = inspect(db.engine)
|
|
columns = {column["name"] for column in inspector.get_columns("company_settings")}
|
|
if "station_access_mode" not in columns:
|
|
db.session.execute(text(
|
|
"ALTER TABLE company_settings ADD COLUMN station_access_mode VARCHAR(40) "
|
|
"NOT NULL DEFAULT 'all_prefer_favorites'"
|
|
))
|
|
|
|
|
|
def add_fuel_entry_vat_action_snapshot() -> None:
|
|
inspector = inspect(db.engine)
|
|
columns = {column["name"] for column in inspector.get_columns("fuel_entry")}
|
|
additions = {
|
|
"vat_action_frozen": "BOOLEAN NOT NULL DEFAULT 0",
|
|
"vat_action_applied": "BOOLEAN NOT NULL DEFAULT 0",
|
|
"vat_action_name": "VARCHAR(160)",
|
|
"vat_action_rate": "NUMERIC(6, 3)",
|
|
"vat_action_start": "DATE",
|
|
"vat_action_end": "DATE",
|
|
}
|
|
for name, definition in additions.items():
|
|
if name not in columns:
|
|
db.session.execute(text(f"ALTER TABLE fuel_entry ADD COLUMN {name} {definition}"))
|
|
|
|
settings = dict(db.session.execute(text(
|
|
"SELECT key, value FROM app_setting WHERE key IN "
|
|
"('global_vat_override_enabled','global_vat_override_name','global_vat_override_rate',"
|
|
"'global_vat_override_start','global_vat_override_end')"
|
|
)).all())
|
|
if settings.get("global_vat_override_enabled") == "1" and settings.get("global_vat_override_rate"):
|
|
conditions = ["vat_action_applied = 0"]
|
|
params = {
|
|
"name": settings.get("global_vat_override_name") or "Projekt rządowy",
|
|
"rate": settings["global_vat_override_rate"],
|
|
"start": settings.get("global_vat_override_start") or None,
|
|
"end": settings.get("global_vat_override_end") or None,
|
|
}
|
|
if params["start"]:
|
|
conditions.append("DATE(fueled_at) >= DATE(:start)")
|
|
if params["end"]:
|
|
conditions.append("DATE(fueled_at) <= DATE(:end)")
|
|
db.session.execute(text(f"""
|
|
UPDATE fuel_entry
|
|
SET vat_action_frozen = 1,
|
|
vat_action_applied = 1,
|
|
vat_action_name = :name,
|
|
vat_action_rate = :rate,
|
|
vat_action_start = :start,
|
|
vat_action_end = :end
|
|
WHERE {' AND '.join(conditions)}
|
|
"""), params)
|
|
db.session.execute(text("UPDATE fuel_entry SET vat_action_frozen = 1 WHERE vat_action_frozen = 0"))
|
|
|
|
|
|
|
|
def add_fuel_entry_full_settlement_snapshot() -> None:
|
|
inspector = inspect(db.engine)
|
|
columns = {column["name"] for column in inspector.get_columns("fuel_entry")}
|
|
additions = {
|
|
"settlement_frozen": "BOOLEAN NOT NULL DEFAULT 0",
|
|
"snapshot_vat_rate": "NUMERIC(6, 3)", "snapshot_vat_deduction_percent": "NUMERIC(6, 3)",
|
|
"snapshot_uses_last_price": "BOOLEAN NOT NULL DEFAULT 0", "snapshot_discount_percent": "NUMERIC(6, 3)",
|
|
"snapshot_surcharge_per_liter": "NUMERIC(10, 4)", "snapshot_rule_source": "VARCHAR(160)",
|
|
"snapshot_base_net_price": "NUMERIC(12, 6)", "snapshot_settlement_net_price": "NUMERIC(12, 6)",
|
|
"result_retail_net_price": "NUMERIC(12, 6)", "result_invoice_price": "NUMERIC(12, 6)",
|
|
"result_effective_price": "NUMERIC(12, 6)", "result_retail_gross": "NUMERIC(14, 4)",
|
|
"result_retail_net_total": "NUMERIC(14, 4)", "result_settlement_net_total": "NUMERIC(14, 4)",
|
|
"result_invoice_gross": "NUMERIC(14, 4)", "result_deductible_vat": "NUMERIC(14, 4)",
|
|
"result_effective_cost": "NUMERIC(14, 4)", "result_saving_net": "NUMERIC(14, 4)",
|
|
"result_gross_saving": "NUMERIC(14, 4)", "settlement_snapshot_source": "VARCHAR(120)",
|
|
"settlement_migration_note": "VARCHAR(255)"
|
|
}
|
|
for name, definition in additions.items():
|
|
if name not in columns:
|
|
db.session.execute(text(f"ALTER TABLE fuel_entry ADD COLUMN {name} {definition}"))
|
|
|
|
|
|
MIGRATIONS: tuple[Migration, ...] = (
|
|
Migration("20260713_company_station_access_mode", add_station_access_mode),
|
|
Migration("20260719_fuel_entry_vat_action_snapshot", add_fuel_entry_vat_action_snapshot),
|
|
Migration("20260720_fuel_entry_full_settlement_snapshot", add_fuel_entry_full_settlement_snapshot),
|
|
)
|
|
|
|
def ensure_migration_table() -> None:
|
|
inspector = inspect(db.engine)
|
|
if inspector.has_table("schema_migration"):
|
|
return
|
|
|
|
db.session.execute(text("""
|
|
CREATE TABLE schema_migration (
|
|
version VARCHAR(100) PRIMARY KEY,
|
|
applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
"""))
|
|
db.session.commit()
|
|
|
|
|
|
def migrate_database() -> list[str]:
|
|
"""Uruchamia wyłącznie niewykonane migracje."""
|
|
ensure_migration_table()
|
|
|
|
applied = {
|
|
row[0]
|
|
for row in db.session.execute(text("SELECT version FROM schema_migration"))
|
|
}
|
|
completed: list[str] = []
|
|
|
|
for migration in MIGRATIONS:
|
|
if migration.version in applied:
|
|
continue
|
|
|
|
try:
|
|
migration.upgrade()
|
|
db.session.execute(
|
|
text("INSERT INTO schema_migration (version) VALUES (:version)"),
|
|
{"version": migration.version},
|
|
)
|
|
db.session.commit()
|
|
completed.append(migration.version)
|
|
except Exception:
|
|
db.session.rollback()
|
|
raise
|
|
|
|
return completed
|
|
|
|
|
|
def main() -> None:
|
|
from . import create_app
|
|
|
|
app = create_app()
|
|
with app.app_context():
|
|
applied = migrate_database()
|
|
|
|
if applied:
|
|
print("Wykonane migracje: " + ", ".join(applied))
|
|
else:
|
|
print("Brak nowych migracji.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|