zestawienia

This commit is contained in:
Mateusz Gruszczyński
2026-07-14 14:09:40 +02:00
parent 654bffded0
commit bb5d992913
5 changed files with 202 additions and 5 deletions
+131 -1
View File
@@ -101,6 +101,51 @@ def parse_fuel_entry_form(entry):
entry.wholesale_source = (request.form.get("wholesale_source") or "").strip() or None
return vehicle
def calculate_entry_settlement(entry, fallback_settings=None):
"""Calculate retail, invoice and effective company cost for one fuel entry."""
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_deduction_percent = entry_settings.vat_deduction_percent
retail_price = Decimal(entry.price_per_liter)
rule = FuelCardStationRule.query.filter_by(
fuel_card_id=entry.fuel_card_id,
station_company_id=entry.station_company_id,
).first() if entry.used_fuel_card and entry.fuel_card_id and entry.station_company_id else None
uses_last_price = bool(rule and rule.use_orlen_last_price and entry.wholesale_price is not None)
if uses_last_price:
settlement_net = Decimal(entry.wholesale_price)
else:
settlement_net = retail_price / (Decimal("1") + Decimal(str(vat_rate)) / Decimal("100"))
if rule:
settlement_net *= Decimal("1") - Decimal(rule.discount_net_percent or 0) / Decimal("100")
settlement_net += Decimal(rule.surcharge_net_per_liter or 0)
settlement_net = max(settlement_net, Decimal("0"))
price_costs = calculate_net_price_costs(settlement_net, vat_rate, vat_deduction_percent)
liters = Decimal(entry.liters)
retail_gross = retail_price * liters
invoice_gross = price_costs["invoice_gross"] * liters
deductible_vat = price_costs["deductible_vat"] * liters
effective_cost = price_costs["effective_cost"] * liters
last_price_saving = retail_gross - invoice_gross if uses_last_price else Decimal("0")
return {
"liters": liters,
"retail_price": retail_price,
"settlement_net": settlement_net,
"invoice_price": price_costs["invoice_gross"],
"effective_price": price_costs["effective_cost"],
"retail_gross": retail_gross,
"invoice_gross": invoice_gross,
"deductible_vat": deductible_vat,
"effective_cost": effective_cost,
"last_price_saving": last_price_saving,
"uses_last_price": uses_last_price,
"vat_rate": Decimal(str(vat_rate)),
"vat_deduction_percent": Decimal(str(vat_deduction_percent)),
}
def accessible_vehicles():
if current_user.role == "admin":
return Vehicle.query.order_by(Vehicle.name).all()
@@ -118,7 +163,7 @@ def dashboard():
try: year, mon = map(int, month.split("-"))
except ValueError: year, mon = datetime.now().year, datetime.now().month; month=f"{year:04d}-{mon:02d}"
entries = FuelEntry.query.filter(FuelEntry.vehicle_id.in_(ids), extract("year", FuelEntry.fueled_at)==year, extract("month", FuelEntry.fueled_at)==mon).order_by(FuelEntry.fueled_at.desc()).all() if ids else []
totals = {"gross": Decimal("0"), "deductible_vat": Decimal("0"), "final_cost": Decimal("0"), "liters": Decimal("0")}
totals = {"gross": Decimal("0"), "deductible_vat": Decimal("0"), "final_cost": Decimal("0"), "liters": Decimal("0"), "last_price_saving": Decimal("0")}
by_vehicle = defaultdict(lambda: {"gross": 0, "payable": 0, "liters": 0}); invoices = defaultdict(lambda: {"gross": 0, "payable": 0, "count": 0}); settlements = {}
for e in entries:
entry_settings = e.vehicle.company or settings
@@ -148,6 +193,8 @@ def dashboard():
totals["deductible_vat"] += deductible_vat
totals["final_cost"] += effective_gross
totals["liters"] += liters
last_price_saving = Decimal(str(e.gross)) - invoice_gross if uses_station_last_price else Decimal("0")
totals["last_price_saving"] += last_price_saving
settlements[e.id] = {
"normal_price": normal_price,
"net_price": float(settlement_net),
@@ -157,6 +204,7 @@ def dashboard():
"invoice_gross": float(invoice_gross),
"payable_gross": float(effective_gross),
"uses_last_price": uses_station_last_price,
"last_price_saving": float(last_price_saving),
"vat_rate": float(vat_rate),
"vat_deduction_percent": float(vat_deduction_percent),
}
@@ -456,6 +504,88 @@ def orlen_sync():
@main.get("/admin/reports")
@login_required
@roles("boss", "admin")
def admin_reports():
today = date.today()
period = request.args.get("period", "month")
company_id = request.args.get("company_id", type=int)
month = request.args.get("month", today.strftime("%Y-%m"))
year = request.args.get("year", today.year, type=int)
date_from_raw = request.args.get("date_from", "")
date_to_raw = request.args.get("date_to", "")
if current_user.role == "boss":
company_id = current_user.company_id
companies = CompanySettings.query.order_by(CompanySettings.name).all() if current_user.role == "admin" else [current_user.company]
try:
if period == "year":
start_date, end_date = date(year, 1, 1), date(year, 12, 31)
elif period == "custom":
start_date = date.fromisoformat(date_from_raw)
end_date = date.fromisoformat(date_to_raw)
if end_date < start_date:
raise ValueError
else:
period = "month"
selected_year, selected_month = map(int, month.split("-"))
start_date = date(selected_year, selected_month, 1)
if selected_month == 12:
end_date = date(selected_year, 12, 31)
else:
end_date = date(selected_year, selected_month + 1, 1)
end_date = date.fromordinal(end_date.toordinal() - 1)
except (TypeError, ValueError):
period = "month"
month = today.strftime("%Y-%m")
start_date = date(today.year, today.month, 1)
next_month = date(today.year + (today.month == 12), 1 if today.month == 12 else today.month + 1, 1)
end_date = date.fromordinal(next_month.toordinal() - 1)
query = FuelEntry.query.join(Vehicle).join(User, Vehicle.owner_id == User.id).filter(
FuelEntry.fueled_at >= datetime.combine(start_date, datetime.min.time()),
FuelEntry.fueled_at < datetime.combine(date.fromordinal(end_date.toordinal() + 1), datetime.min.time()),
)
if company_id:
query = query.filter(or_(Vehicle.company_id == company_id, and_(Vehicle.company_id.is_(None), User.company_id == company_id)))
entries = query.order_by(FuelEntry.fueled_at.asc()).all()
zero = Decimal("0")
totals = {"retail": zero, "invoice": zero, "effective": zero, "vat_deducted": zero, "saving": zero, "liters": zero, "count": 0, "last_price_count": 0}
by_month = defaultdict(lambda: {"retail": 0.0, "invoice": 0.0, "effective": 0.0, "saving": 0.0, "liters": 0.0, "count": 0})
by_vehicle = defaultdict(lambda: {"retail": 0.0, "invoice": 0.0, "effective": 0.0, "saving": 0.0, "liters": 0.0, "count": 0})
by_company = defaultdict(lambda: {"retail": 0.0, "invoice": 0.0, "effective": 0.0, "saving": 0.0, "liters": 0.0, "count": 0})
for entry in entries:
row = calculate_entry_settlement(entry)
totals["retail"] += row["retail_gross"]
totals["invoice"] += row["invoice_gross"]
totals["effective"] += row["effective_cost"]
totals["vat_deducted"] += row["deductible_vat"]
totals["saving"] += row["last_price_saving"]
totals["liters"] += row["liters"]
totals["count"] += 1
totals["last_price_count"] += int(row["uses_last_price"])
company = entry.vehicle.company or entry.vehicle.owner.company
keys = ((by_month, entry.fueled_at.strftime("%Y-%m")), (by_vehicle, entry.vehicle.name), (by_company, company.name if company else "Bez firmy"))
for bucket, key in keys:
bucket[key]["retail"] += float(row["retail_gross"])
bucket[key]["invoice"] += float(row["invoice_gross"])
bucket[key]["effective"] += float(row["effective_cost"])
bucket[key]["saving"] += float(row["last_price_saving"])
bucket[key]["liters"] += float(row["liters"])
bucket[key]["count"] += 1
totals["saving_percent"] = (totals["saving"] / totals["retail"] * Decimal("100")) if totals["retail"] else zero
return render_template(
"admin_reports.html", companies=[c for c in companies if c], company_id=company_id, period=period, month=month, year=year,
date_from=date_from_raw, date_to=date_to_raw, start_date=start_date, end_date=end_date, totals=totals,
by_month=dict(sorted(by_month.items())), by_vehicle=dict(sorted(by_vehicle.items())), by_company=dict(sorted(by_company.items())),
)
@main.get("/admin/fuel-entries")
@login_required
@roles("boss", "admin")