Files
fuel_track/app/settlement.py
T
Mateusz Gruszczyński 697ac6da30 zapamietanie akcji
2026-07-19 19:15:21 +02:00

78 lines
5.6 KiB
Python

from decimal import Decimal
from .models import CompanySettings, FuelCardStationRule
from .domain.costs import calculate_net_price_costs, calculate_net_price_comparison
from .vat import entry_vat_rate
D0 = Decimal('0')
def _d(value, default='0'):
return Decimal(str(value if value is not None else default))
def live_conditions(entry, fallback_settings=None):
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 = _d(entry_vat_rate(entry, settings.vat_rate))
deduction = _d(settings.vat_deduction_percent)
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
policy = entry.vehicle.fuel_card_policy if entry.used_fuel_card else None
uses_last = bool(entry.wholesale_price is not None and ((rule and rule.use_orlen_last_price) or (not rule and policy and policy.use_orlen_last_price)))
if rule:
discount, surcharge, source = _d(rule.discount_net_percent), _d(rule.surcharge_net_per_liter), 'Reguła karty dla stacji'
elif policy:
discount, surcharge, source = _d(policy.discount_percent), _d(policy.surcharge_per_liter), 'Polityka karty pojazdu'
else:
discount, surcharge, source = D0, D0, 'Brak rabatu/dopłaty'
return {'vat_rate': vat_rate, 'vat_deduction_percent': deduction, 'uses_last_price': uses_last,
'discount_percent': max(D0, min(discount, Decimal('100'))), 'surcharge_per_liter': surcharge,
'rule_source': source}
def snapshot_conditions(entry, fallback_settings=None):
if entry.settlement_frozen:
return {'vat_rate': _d(entry.snapshot_vat_rate), 'vat_deduction_percent': _d(entry.snapshot_vat_deduction_percent),
'uses_last_price': bool(entry.snapshot_uses_last_price), 'discount_percent': _d(entry.snapshot_discount_percent),
'surcharge_per_liter': _d(entry.snapshot_surcharge_per_liter), 'rule_source': entry.snapshot_rule_source or 'Migawka'}
return live_conditions(entry, fallback_settings)
def calculate(entry, fallback_settings=None, conditions=None):
c = conditions or snapshot_conditions(entry, fallback_settings)
vat, deduction = c['vat_rate'], c['vat_deduction_percent']
retail, liters = _d(entry.price_per_liter), _d(entry.liters)
uses_last = bool(c['uses_last_price'] and entry.wholesale_price is not None)
base_net = _d(entry.wholesale_price) if uses_last else retail / (Decimal('1') + vat / Decimal('100'))
settlement_net = max(D0, base_net * (Decimal('1') - c['discount_percent'] / Decimal('100')) + c['surcharge_per_liter'])
costs = calculate_net_price_costs(settlement_net, vat, deduction)
comparison = calculate_net_price_comparison(retail, settlement_net, liters, vat)
retail_gross = retail * liters
invoice_gross = costs['invoice_gross'] * liters
result = {
'liters': liters, 'retail_price': retail, 'retail_net_price': comparison['retail_net_price'],
'base_net_price': base_net, 'settlement_net': settlement_net, 'invoice_price': costs['invoice_gross'],
'effective_price': costs['effective_cost'], 'retail_gross': retail_gross,
'retail_net_total': comparison['retail_net_total'], 'settlement_net_total': comparison['settlement_net_total'],
'invoice_gross': invoice_gross, 'deductible_vat': costs['deductible_vat'] * liters,
'effective_cost': costs['effective_cost'] * liters,
'saving_net': comparison['saving_net_total'] if uses_last else D0,
'saving_net_per_liter': comparison['saving_net_per_liter'] if uses_last else D0,
'gross_saving': max(retail_gross - invoice_gross, D0),
'gross_saving_per_liter': max(retail - costs['invoice_gross'], D0),
'discount_percent': c['discount_percent'], 'surcharge_per_liter': c['surcharge_per_liter'],
'uses_last_price': uses_last, 'vat_rate': vat, 'vat_deduction_percent': deduction,
'rule_source': c.get('rule_source') or 'Migawka'}
return result
def freeze(entry, fallback_settings=None, conditions=None, source='Automatycznie przy zapisie', migration_note=None):
c = conditions or live_conditions(entry, fallback_settings)
result = calculate(entry, fallback_settings, c)
entry.settlement_frozen = True
entry.snapshot_vat_rate = c['vat_rate']; entry.snapshot_vat_deduction_percent = c['vat_deduction_percent']
entry.snapshot_uses_last_price = bool(c['uses_last_price']); entry.snapshot_discount_percent = c['discount_percent']
entry.snapshot_surcharge_per_liter = c['surcharge_per_liter']; entry.snapshot_rule_source = c.get('rule_source') or source
entry.snapshot_base_net_price = result['base_net_price']; entry.snapshot_settlement_net_price = result['settlement_net']
entry.result_retail_net_price = result['retail_net_price']; entry.result_invoice_price = result['invoice_price']
entry.result_effective_price = result['effective_price']; entry.result_retail_gross = result['retail_gross']
entry.result_retail_net_total = result['retail_net_total']; entry.result_settlement_net_total = result['settlement_net_total']
entry.result_invoice_gross = result['invoice_gross']; entry.result_deductible_vat = result['deductible_vat']
entry.result_effective_cost = result['effective_cost']; entry.result_saving_net = result['saving_net']
entry.result_gross_saving = result['gross_saving']; entry.settlement_snapshot_source = source
entry.settlement_migration_note = migration_note
return result