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))