from __future__ import annotations import math import pytest from services.pricing import ( PricingInput, PricingMetadata, PricingResult, calculate_pricing, ) def test_calculate_pricing_with_explicit_penalties() -> None: pricing_input = PricingInput( metal="copper", ore_tonnage=100_000, head_grade_pct=1.2, recovery_pct=90, payable_pct=96, reference_price=8_500, treatment_charge=100_000, smelting_charge=0, moisture_pct=10, moisture_threshold_pct=8, moisture_penalty_per_pct=3_000, impurity_ppm={"As": 100}, impurity_thresholds={"As": 0}, impurity_penalty_per_ppm={"As": 2}, premiums=50_000, fx_rate=1.0, currency_code="usd", ) result = calculate_pricing(pricing_input) assert isinstance(result, PricingResult) assert math.isclose(result.payable_metal_tonnes, 1_036.8, rel_tol=1e-6) assert math.isclose(result.gross_revenue, 1_036.8 * 8_500, rel_tol=1e-6) assert math.isclose(result.moisture_penalty, 6_000, rel_tol=1e-6) assert math.isclose(result.impurity_penalty, 200, rel_tol=1e-6) assert math.isclose(result.net_revenue, 8_756_600, rel_tol=1e-6) assert result.treatment_smelt_charges == pytest.approx(100_000) assert result.currency == "USD" def test_calculate_pricing_with_metadata_defaults() -> None: metadata = PricingMetadata( default_payable_pct=95, default_currency="EUR", moisture_threshold_pct=7, moisture_penalty_per_pct=2_000, impurity_thresholds={"Pb": 50}, impurity_penalty_per_ppm={"Pb": 1.5}, ) pricing_input = PricingInput( metal="lead", ore_tonnage=50_000, head_grade_pct=5, recovery_pct=85, payable_pct=None, reference_price=2_000, treatment_charge=30_000, smelting_charge=20_000, moisture_pct=9, moisture_threshold_pct=None, moisture_penalty_per_pct=None, impurity_ppm={"Pb": 120}, premiums=12_000, fx_rate=1.2, currency_code=None, ) result = calculate_pricing(pricing_input, metadata=metadata) expected_payable = 50_000 * 0.05 * 0.85 * 0.95 assert math.isclose(result.payable_metal_tonnes, expected_payable, rel_tol=1e-6) assert result.moisture_penalty == pytest.approx((9 - 7) * 2_000) assert result.impurity_penalty == pytest.approx((120 - 50) * 1.5) assert result.treatment_smelt_charges == pytest.approx(50_000) assert result.currency == "EUR" assert result.net_revenue > 0 def test_calculate_pricing_currency_override() -> None: pricing_input = PricingInput( metal="gold", ore_tonnage=10_000, head_grade_pct=2.5, recovery_pct=92, payable_pct=98, reference_price=60_000, treatment_charge=40_000, smelting_charge=10_000, moisture_pct=5, moisture_threshold_pct=7, moisture_penalty_per_pct=1_000, premiums=25_000, fx_rate=1.0, currency_code="cad", ) metadata = PricingMetadata(default_currency="USD") result = calculate_pricing( pricing_input, metadata=metadata, currency="CAD") assert result.currency == "CAD" assert result.net_revenue > 0 def test_calculate_pricing_multiple_inputs_aggregate() -> None: metadata = PricingMetadata(default_currency="USD") inputs = [ PricingInput( metal="copper", ore_tonnage=10_000, head_grade_pct=1.5, recovery_pct=88, payable_pct=95, reference_price=8_000, treatment_charge=20_000, smelting_charge=5_000, moisture_pct=7, moisture_threshold_pct=8, moisture_penalty_per_pct=1_000, premiums=0, fx_rate=1.0, currency_code=None, ), PricingInput( metal="copper", ore_tonnage=8_000, head_grade_pct=1.1, recovery_pct=90, payable_pct=96, reference_price=8_000, treatment_charge=18_000, smelting_charge=4_000, moisture_pct=9, moisture_threshold_pct=8, moisture_penalty_per_pct=1_000, premiums=0, fx_rate=1.0, currency_code="usd", ), ] results = [calculate_pricing(i, metadata=metadata) for i in inputs] assert all(result.currency == "USD" for result in results) assert sum(result.net_revenue for result in results) > 0