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