133 lines
4.7 KiB
Python
133 lines
4.7 KiB
Python
from datetime import date, datetime
|
||
from decimal import Decimal, InvalidOperation
|
||
from .extensions import db
|
||
from .models import AppSetting
|
||
|
||
KEYS = {
|
||
"enabled": "global_vat_override_enabled",
|
||
"name": "global_vat_override_name",
|
||
"rate": "global_vat_override_rate",
|
||
"start": "global_vat_override_start",
|
||
"end": "global_vat_override_end",
|
||
}
|
||
|
||
|
||
def _value(key, default=""):
|
||
setting = db.session.get(AppSetting, KEYS[key])
|
||
return setting.value if setting else default
|
||
|
||
|
||
def global_vat_override():
|
||
rate_raw = _value("rate")
|
||
try:
|
||
rate = Decimal(rate_raw) if rate_raw != "" else None
|
||
except InvalidOperation:
|
||
rate = None
|
||
return {
|
||
"enabled": _value("enabled", "0") == "1",
|
||
"name": _value("name", "Projekt rządowy"),
|
||
"rate": rate,
|
||
"start": _parse_date(_value("start")),
|
||
"end": _parse_date(_value("end")),
|
||
}
|
||
|
||
|
||
def _parse_date(value):
|
||
if not value:
|
||
return None
|
||
try:
|
||
return date.fromisoformat(value)
|
||
except ValueError:
|
||
return None
|
||
|
||
|
||
def effective_vat_rate(nominal_rate, on_date=None):
|
||
override = global_vat_override()
|
||
if not override["enabled"] or override["rate"] is None:
|
||
return Decimal(str(nominal_rate))
|
||
if isinstance(on_date, datetime):
|
||
on_date = on_date.date()
|
||
on_date = on_date or date.today()
|
||
if override["start"] and on_date < override["start"]:
|
||
return Decimal(str(nominal_rate))
|
||
if override["end"] and on_date > override["end"]:
|
||
return Decimal(str(nominal_rate))
|
||
return override["rate"]
|
||
|
||
|
||
|
||
def vat_override_for_date(nominal_rate, on_date=None):
|
||
"""Return a complete, immutable VAT-action snapshot for a date."""
|
||
override = global_vat_override()
|
||
if isinstance(on_date, datetime):
|
||
on_date = on_date.date()
|
||
on_date = on_date or date.today()
|
||
applies = bool(
|
||
override["enabled"]
|
||
and override["rate"] is not None
|
||
and (not override["start"] or on_date >= override["start"])
|
||
and (not override["end"] or on_date <= override["end"])
|
||
)
|
||
return {
|
||
"applied": applies,
|
||
"name": override["name"] if applies else None,
|
||
"rate": override["rate"] if applies else Decimal(str(nominal_rate)),
|
||
"start": override["start"] if applies else None,
|
||
"end": override["end"] if applies else None,
|
||
}
|
||
|
||
|
||
def freeze_vat_action(entry, nominal_rate, previous_fueled_at=None):
|
||
"""Attach VAT action conditions to an entry without rewriting valid history."""
|
||
fueled_date = entry.fueled_at.date() if isinstance(entry.fueled_at, datetime) else entry.fueled_at
|
||
previous_date = previous_fueled_at.date() if isinstance(previous_fueled_at, datetime) else previous_fueled_at
|
||
if entry.vat_action_frozen and (previous_date is None or previous_date == fueled_date):
|
||
return
|
||
|
||
snapshot = vat_override_for_date(nominal_rate, fueled_date)
|
||
entry.vat_action_frozen = True
|
||
entry.vat_action_applied = snapshot["applied"]
|
||
entry.vat_action_name = snapshot["name"]
|
||
entry.vat_action_rate = snapshot["rate"] if snapshot["applied"] else None
|
||
entry.vat_action_start = snapshot["start"]
|
||
entry.vat_action_end = snapshot["end"]
|
||
|
||
|
||
def entry_vat_rate(entry, nominal_rate):
|
||
if entry.vat_action_frozen:
|
||
if entry.vat_action_applied and entry.vat_action_rate is not None:
|
||
return Decimal(str(entry.vat_action_rate))
|
||
return Decimal(str(nominal_rate))
|
||
return effective_vat_rate(nominal_rate, entry.fueled_at)
|
||
|
||
def save_global_vat_override(form):
|
||
enabled = "enabled" in form
|
||
name = (form.get("name") or "Projekt rządowy").strip()
|
||
rate_raw = (form.get("rate") or "").strip()
|
||
start_raw = (form.get("start") or "").strip()
|
||
end_raw = (form.get("end") or "").strip()
|
||
|
||
if enabled:
|
||
try:
|
||
rate = Decimal(rate_raw)
|
||
except InvalidOperation as exc:
|
||
raise ValueError("Podaj prawidłową globalną stawkę VAT.") from exc
|
||
if rate < 0 or rate > 100:
|
||
raise ValueError("Globalna stawka VAT musi mieścić się w zakresie 0–100%.")
|
||
if start_raw and not _parse_date(start_raw):
|
||
raise ValueError("Nieprawidłowa data rozpoczęcia.")
|
||
if end_raw and not _parse_date(end_raw):
|
||
raise ValueError("Nieprawidłowa data zakończenia.")
|
||
if start_raw and end_raw and start_raw > end_raw:
|
||
raise ValueError("Data zakończenia nie może być wcześniejsza niż rozpoczęcia.")
|
||
|
||
values = {
|
||
KEYS["enabled"]: "1" if enabled else "0",
|
||
KEYS["name"]: name,
|
||
KEYS["rate"]: rate_raw,
|
||
KEYS["start"]: start_raw,
|
||
KEYS["end"]: end_raw,
|
||
}
|
||
for key, value in values.items():
|
||
db.session.merge(AppSetting(key=key, value=value))
|