78 lines
5.6 KiB
Python
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
|