zapamietanie akcji
This commit is contained in:
@@ -15,6 +15,7 @@ from ..extensions import db
|
||||
from ..models import AppSetting, CompanySettings, FuelCard, FuelCardPolicy, FuelCardStationRule, FuelEntry, FuelStationCompany, FuelStationPoint, OrlenPrice, User, Vehicle
|
||||
from ..services import POLISH_REGIONS, aggregate_ure_companies, calculate_costs, fetch_orlen_price, fetch_orlen_range, fetch_ure_stations, invoice_period
|
||||
from ..station_catalog import sync_station_catalog
|
||||
from ..vat import freeze_vat_action
|
||||
|
||||
FUEL_TYPES = ("PB95", "PB98", "DIESEL", "LPG")
|
||||
|
||||
@@ -44,5 +45,6 @@ def create_fuel_entry():
|
||||
rule=FuelCardStationRule.query.filter_by(fuel_card_id=card.id,station_company_id=station_company.id).first()
|
||||
if rule and not rule.allowed:return fail('Ta stacja jest zablokowana dla wybranej karty paliwowej',403)
|
||||
e=FuelEntry(vehicle_id=vehicle.id,user_id=current_user.id,fueled_at=fueled_at,liters=Decimal(d['liters']),price_per_liter=Decimal(d['price_per_liter']),fuel_type=d['fuel_type'],odometer=odo,station=station_company.company_name if station_company else d.get('station'),station_company_id=station_company.id if station_company else None,fuel_card_id=card.id if used_card else None,invoice_number=d.get('invoice_number'),used_fuel_card=used_card,wholesale_price=wholesale,wholesale_source=source)
|
||||
freeze_vat_action(e, settings.vat_rate)
|
||||
vehicle.current_odometer=odo;db.session.add(e);db.session.commit();return response({'id':e.id,'redirect':url_for('main.dashboard')},'Zapisano tankowanie',201)
|
||||
except Exception as exc:db.session.rollback();return fail(str(exc),400)
|
||||
|
||||
@@ -21,8 +21,54 @@ def add_station_access_mode() -> None:
|
||||
))
|
||||
|
||||
|
||||
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"))
|
||||
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
def ensure_migration_table() -> None:
|
||||
|
||||
+6
-2
@@ -11,7 +11,7 @@ from .models import User, Vehicle, FuelEntry, CompanySettings, AppSetting, Orlen
|
||||
from .services import calculate_costs, fetch_orlen_price, fetch_orlen_range, invoice_period, fetch_ure_stations, aggregate_ure_companies, POLISH_REGIONS
|
||||
from .station_catalog import sync_station_catalog
|
||||
from . import THEMES
|
||||
from .vat import effective_vat_rate, global_vat_override, save_global_vat_override
|
||||
from .vat import entry_vat_rate, freeze_vat_action, global_vat_override, save_global_vat_override
|
||||
from .domain.costs import calculate_net_price_costs, calculate_net_price_comparison
|
||||
|
||||
main = Blueprint("main", __name__)
|
||||
@@ -70,6 +70,7 @@ def parse_fuel_entry_form(entry):
|
||||
fuel_type = (request.form.get("fuel_type") or "").upper()
|
||||
if fuel_type not in FUEL_TYPES:
|
||||
raise ValueError("Nieprawidłowy rodzaj paliwa")
|
||||
previous_fueled_at = entry.fueled_at
|
||||
fueled_at = datetime.fromisoformat(request.form.get("fueled_at", ""))
|
||||
liters = Decimal(request.form.get("liters", ""))
|
||||
price = Decimal(request.form.get("price_per_liter", ""))
|
||||
@@ -99,6 +100,8 @@ def parse_fuel_entry_form(entry):
|
||||
wholesale_price = (request.form.get("wholesale_price") or "").strip()
|
||||
entry.wholesale_price = Decimal(wholesale_price) if wholesale_price else None
|
||||
entry.wholesale_source = (request.form.get("wholesale_source") or "").strip() or None
|
||||
settings = vehicle.company or (vehicle.owner.company if vehicle.owner else None) or CompanySettings.query.first()
|
||||
freeze_vat_action(entry, settings.vat_rate, previous_fueled_at)
|
||||
return vehicle
|
||||
|
||||
|
||||
@@ -110,7 +113,7 @@ def calculate_entry_settlement(entry, fallback_settings=None):
|
||||
separately and never counted as a Last Price saving.
|
||||
"""
|
||||
entry_settings = entry.vehicle.company or (entry.vehicle.owner.company if entry.vehicle and entry.vehicle.owner else None) or fallback_settings or CompanySettings.query.first()
|
||||
vat_rate = effective_vat_rate(entry_settings.vat_rate, entry.fueled_at)
|
||||
vat_rate = entry_vat_rate(entry, entry_settings.vat_rate)
|
||||
vat_deduction_percent = entry_settings.vat_deduction_percent
|
||||
retail_price = Decimal(entry.price_per_liter)
|
||||
rule = FuelCardStationRule.query.filter_by(
|
||||
@@ -318,6 +321,7 @@ def fuel():
|
||||
except Exception: pass
|
||||
station_label = station_company.company_name if station_company else request.form.get("station")
|
||||
e = FuelEntry(vehicle_id=vehicle.id, user_id=current_user.id, fueled_at=fueled_at, liters=Decimal(request.form["liters"]), price_per_liter=Decimal(request.form["price_per_liter"]), fuel_type=request.form["fuel_type"], odometer=odometer, station=station_label, station_company_id=station_company.id if station_company else None, invoice_number=request.form.get("invoice_number"), used_fuel_card="used_fuel_card" in request.form, wholesale_price=wholesale, wholesale_source=source)
|
||||
freeze_vat_action(e, settings.vat_rate)
|
||||
vehicle.current_odometer = odometer; db.session.add(e); db.session.commit(); response = ok_response("Zapisano tankowanie", redirect=url_for("main.dashboard")); return response or redirect(url_for("main.dashboard"))
|
||||
allowed=list(settings.allowed_stations)
|
||||
company_favorites=list(settings.favorite_stations)
|
||||
|
||||
@@ -171,6 +171,12 @@ class FuelEntry(db.Model):
|
||||
used_fuel_card = db.Column(db.Boolean, default=False, nullable=False)
|
||||
wholesale_price = db.Column(db.Numeric(10, 4))
|
||||
wholesale_source = db.Column(db.String(120))
|
||||
vat_action_frozen = db.Column(db.Boolean, default=False, nullable=False)
|
||||
vat_action_applied = db.Column(db.Boolean, default=False, nullable=False)
|
||||
vat_action_name = db.Column(db.String(160))
|
||||
vat_action_rate = db.Column(db.Numeric(6, 3))
|
||||
vat_action_start = db.Column(db.Date)
|
||||
vat_action_end = db.Column(db.Date)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
vehicle = db.relationship("Vehicle", back_populates="fuel_entries")
|
||||
user = db.relationship("User")
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
[data-bs-theme="dark"] .table thead th{color:#adb5bd}
|
||||
[data-bs-theme="dark"] .text-body-secondary,[data-bs-theme="dark"] .text-muted{color:#aeb4bb!important}
|
||||
[data-bs-theme="dark"] .bg-body-tertiary{background-color:#181b1f!important}
|
||||
[data-bs-theme="dark"] .dropdown-menu{background:#202327;border-color:#3a3f45;box-shadow:0 .75rem 2rem rgba(0,0,0,.35)}
|
||||
[data-bs-theme="dark"] .dropdown-menu{background:#202327;border-color:#3a3f45;box-shadow:0 .4rem 1.1rem rgba(0,0,0,.22)}
|
||||
[data-bs-theme="dark"] .dropdown-item{color:#e9ecef}
|
||||
[data-bs-theme="dark"] .dropdown-item:hover,[data-bs-theme="dark"] .dropdown-item:focus{background:#2b3035;color:#fff}
|
||||
|
||||
.vat-action-row>td{background-color:rgba(var(--bs-warning-rgb),.075)}
|
||||
.vat-action-row:hover>td{background-color:rgba(var(--bs-warning-rgb),.13)!important}
|
||||
.vat-action-badge{background:rgba(var(--bs-warning-rgb),.2);color:var(--bs-body-color);border:1px solid rgba(var(--bs-warning-rgb),.45);margin-top:.25rem}
|
||||
[data-bs-theme="dark"] .vat-action-row>td{background-color:rgba(var(--bs-warning-rgb),.09)}
|
||||
[data-bs-theme="dark"] .vat-action-row:hover>td{background-color:rgba(var(--bs-warning-rgb),.15)!important}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:root{--app-radius:1rem;--app-shadow:0 .4rem 1.5rem rgba(15,23,42,.07);--app-shadow-hover:0 .75rem 2rem rgba(15,23,42,.11)}
|
||||
html,body{min-height:100%}body{min-height:100vh;display:flex;flex-direction:column;background:var(--bs-tertiary-bg)}
|
||||
html,body{min-height:100%}body{min-height:100vh;min-height:100dvh;display:flex;flex-direction:column;background:var(--bs-tertiary-bg);padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}
|
||||
.app-navbar{background:color-mix(in srgb,var(--bs-body-bg) 92%,transparent);border-bottom:1px solid var(--bs-border-color-translucent);backdrop-filter:blur(14px);box-shadow:0 .15rem .75rem rgba(15,23,42,.04)}
|
||||
.navbar-brand{font-weight:800;letter-spacing:-.04em;white-space:nowrap}.app-brand,.auth-brand{font-size:1.4rem;line-height:1;text-decoration:none}.brand-fuel{color:var(--bs-emphasis-color)}.brand-track{color:var(--bs-primary)}
|
||||
.app-brand::after,.auth-brand::after{content:"";display:inline-block;width:.38rem;height:.38rem;margin-left:.18rem;border-radius:50%;background:var(--bs-warning);box-shadow:0 0 0 .2rem color-mix(in srgb,var(--bs-warning) 18%,transparent);vertical-align:.15em}
|
||||
@@ -17,11 +17,13 @@ html,body{min-height:100%}body{min-height:100vh;display:flex;flex-direction:colu
|
||||
.app-brand{font-family:inherit;font-size:1.4rem!important;font-weight:800!important;line-height:1!important;letter-spacing:-.04em!important}
|
||||
.app-navbar .nav-link{font-size:1rem;line-height:1.5}
|
||||
|
||||
[data-bs-theme="dark"]{--app-shadow:0 .45rem 1.6rem rgba(0,0,0,.28);--app-shadow-hover:0 .8rem 2.2rem rgba(0,0,0,.38)}
|
||||
[data-bs-theme="dark"]{--app-shadow:0 .25rem .9rem rgba(0,0,0,.16);--app-shadow-hover:0 .45rem 1.25rem rgba(0,0,0,.22)}
|
||||
[data-bs-theme="dark"] body{background:#17191c;color:#f1f3f5}
|
||||
[data-bs-theme="dark"] .app-navbar{background:rgba(24,26,29,.94);border-bottom-color:#34383d;box-shadow:0 .2rem 1rem rgba(0,0,0,.32)}
|
||||
[data-bs-theme="dark"] .app-navbar{background:rgba(24,26,29,.94);border-bottom-color:#34383d;box-shadow:0 .1rem .45rem rgba(0,0,0,.18)}
|
||||
[data-bs-theme="dark"] .app-footer{background:#1b1d20;border-top-color:#34383d}
|
||||
[data-bs-theme="dark"] .brand-fuel{color:#f8f9fa}
|
||||
[data-bs-theme="dark"] .auth-page{background:radial-gradient(circle at 20% 10%,rgba(55,139,230,.16),transparent 36%),radial-gradient(circle at 85% 85%,rgba(255,193,7,.08),transparent 32%),#141619}
|
||||
@media(max-width:991.98px){.app-main{padding-top:1.35rem}.app-navbar .navbar-collapse{border-top:1px solid var(--bs-border-color-translucent);margin-top:.75rem}.app-navbar .nav-link{padding:.7rem .8rem}.page-header{align-items:stretch;flex-direction:column}}
|
||||
@media(max-width:575.98px){.app-main{padding-left:1rem;padding-right:1rem}.page-header h1{font-size:1.65rem}.auth-main{padding-top:1.25rem;padding-bottom:1.25rem}}
|
||||
|
||||
@supports(padding:max(0px)){.app-navbar{padding-top:max(.5rem,env(safe-area-inset-top))}.app-footer{padding-bottom:max(1.25rem,env(safe-area-inset-bottom))}}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<html lang="pl" data-bs-theme="{{ app_theme.mode }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="theme-color" content="#0d6efd">
|
||||
<title>{% block title %}FuelTrack{% endblock %}</title>
|
||||
<link id="bootstrap-theme" rel="stylesheet" href="{{ app_theme.url }}">
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
</div>
|
||||
<div class="alert alert-info small"><strong>Wykres brutto:</strong> porównuje wartość po cenie detalicznej z ostateczną kwotą faktury brutto. Cena rozliczeniowa uwzględnia Last Price albo cenę detaliczną netto, następnie rabat procentowy i dopłatę przypisaną do karty/stacji. Oszczędność handlowa Last Price nadal jest liczona netto.</div>
|
||||
<div class="row g-4"><div class="col-lg-7"><div class="card"><div class="card-body"><h2 class="h5">Kwoty brutto według pojazdu</h2><p class="small text-body-secondary">Detal brutto, ostateczna kwota do zapłaty brutto oraz różnica. Rabaty i dopłaty użytkownika są uwzględniane.</p><div class="chart-box"><canvas id="cost-chart"></canvas><div id="cost-chart-empty" class="chart-empty d-none">Brak danych dla wybranego miesiąca.</div></div></div></div></div><div class="col-lg-5"><div class="card h-100"><div class="card-body"><h2 class="h5">Okresy faktur</h2><small class="text-body-secondary">{% if split_enabled %}Podział według ustawień firmy{% else %}Podział dzienny wyłączony{% endif %}</small><table class="table mt-2"><thead><tr><th>Okres</th><th>Pozycje</th><th>Faktura*</th><th>Koszt po VAT</th></tr></thead><tbody>{% for key,row in invoices.items() %}<tr><td>{{key}}</td><td>{{row.count}}</td><td>{{'%.2f'|format(row.gross)}} zł</td><td>{{'%.2f'|format(row.payable)}} zł</td></tr>{% else %}<tr><td colspan="4">Brak danych</td></tr>{% endfor %}</tbody></table><div class="small text-body-secondary">* Kwota brutto: cena rozliczeniowa netto + VAT właściwy dla firmy.</div></div></div></div>
|
||||
<div class="card mt-4"><div class="card-body"><h2 class="h5">Tankowania</h2><div class="table-responsive"><table class="table table-hover align-middle"><thead><tr><th>Data</th><th>Auto</th><th>Stacja</th><th>Litry</th><th>Detal brutto</th><th>Detal netto</th><th>Last Price netto</th><th>Oszczędność netto</th><th>Faktura brutto</th><th>Koszt po VAT</th></tr></thead><tbody>{% for e in entries %}{% set s=settlements[e.id] %}<tr><td>{{e.fueled_at.strftime('%d.%m.%Y')}}</td><td>{{e.vehicle.name}}</td><td>{{e.station or '—'}}</td><td>{{e.liters}}</td><td>{{'%.4f'|format(s.retail_price)}} zł/l<br><small>{{'%.2f'|format(s.retail_gross)}} zł</small></td><td>{{'%.4f'|format(s.retail_net_price)}} zł/l<br><small>{{'%.2f'|format(s.retail_net_total)}} zł</small></td><td>{% if s.uses_last_price %}<span class="badge text-bg-info">Last Price</span><br>{{'%.4f'|format(s.settlement_net)}} zł/l{% else %}{{'%.4f'|format(s.settlement_net)}} zł/l{% endif %}<br><small>{{'%.2f'|format(s.settlement_net_total)}} zł</small></td><td>{% if s.uses_last_price %}<strong>{{'%.2f'|format(s.saving_net)}} zł</strong><br><small>{{'%.4f'|format(s.saving_net_per_liter)}} zł/l</small>{% else %}—{% endif %}</td><td>{{'%.4f'|format(s.invoice_price)}} zł/l<br><strong>{{'%.2f'|format(s.invoice_gross)}} zł</strong><br><small>VAT {{'%.2f'|format(s.vat_rate)}}%</small></td><td>{{'%.4f'|format(s.effective_price)}} zł/l<br><strong>{{'%.2f'|format(s.effective_cost)}} zł</strong><br><small>odliczenie VAT {{'%.0f'|format(s.vat_deduction_percent)}}%</small></td></tr>{% else %}<tr><td colspan="10">Brak tankowań w tym miesiącu.</td></tr>{% endfor %}</tbody></table></div></div></div>
|
||||
<div class="card mt-4"><div class="card-body"><h2 class="h5">Tankowania</h2><div class="table-responsive"><table class="table table-hover align-middle"><thead><tr><th>Data</th><th>Auto</th><th>Stacja</th><th>Litry</th><th>Detal brutto</th><th>Detal netto</th><th>Last Price netto</th><th>Oszczędność netto</th><th>Faktura brutto</th><th>Koszt po VAT</th></tr></thead><tbody>{% for e in entries %}{% set s=settlements[e.id] %}<tr class="{% if e.vat_action_applied %}vat-action-row{% endif %}"{% if e.vat_action_applied %} title="Akcja: {{ e.vat_action_name }} · VAT {{ '%.2f'|format(e.vat_action_rate|float) }}%{% if e.vat_action_start or e.vat_action_end %} · {{ e.vat_action_start or 'bez daty początkowej' }}–{{ e.vat_action_end or 'bez daty końcowej' }}{% endif %}"{% endif %}><td>{{e.fueled_at.strftime('%d.%m.%Y')}}{% if e.vat_action_applied %}<br><span class="badge vat-action-badge">{{e.vat_action_name or 'Akcja rządowa'}}</span>{% endif %}</td><td>{{e.vehicle.name}}</td><td>{{e.station or '—'}}</td><td>{{e.liters}}</td><td>{{'%.4f'|format(s.retail_price)}} zł/l<br><small>{{'%.2f'|format(s.retail_gross)}} zł</small></td><td>{{'%.4f'|format(s.retail_net_price)}} zł/l<br><small>{{'%.2f'|format(s.retail_net_total)}} zł</small></td><td>{% if s.uses_last_price %}<span class="badge text-bg-info">Last Price</span><br>{{'%.4f'|format(s.settlement_net)}} zł/l{% else %}{{'%.4f'|format(s.settlement_net)}} zł/l{% endif %}<br><small>{{'%.2f'|format(s.settlement_net_total)}} zł</small></td><td>{% if s.uses_last_price %}<strong>{{'%.2f'|format(s.saving_net)}} zł</strong><br><small>{{'%.4f'|format(s.saving_net_per_liter)}} zł/l</small>{% else %}—{% endif %}</td><td>{{'%.4f'|format(s.invoice_price)}} zł/l<br><strong>{{'%.2f'|format(s.invoice_gross)}} zł</strong><br><small>VAT {{'%.2f'|format(s.vat_rate)}}%</small></td><td>{{'%.4f'|format(s.effective_price)}} zł/l<br><strong>{{'%.2f'|format(s.effective_cost)}} zł</strong><br><small>odliczenie VAT {{'%.0f'|format(s.vat_deduction_percent)}}%</small></td></tr>{% else %}<tr><td colspan="10">Brak tankowań w tym miesiącu.</td></tr>{% endfor %}</tbody></table></div></div></div>
|
||||
{% endblock %}{% block scripts %}<script>FuelTrack.renderBarChart('cost-chart', {{by_vehicle|tojson}});</script>{% endblock %}
|
||||
|
||||
+45
@@ -55,6 +55,51 @@ def effective_vat_rate(nominal_rate, on_date=None):
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user