feat: Add currency and unit support across models, routes, and templates; enhance UI for consumption, costs, and production

This commit is contained in:
2025-10-21 09:53:04 +02:00
parent 139ae04538
commit fcea39deb0
18 changed files with 354 additions and 31 deletions

View File

@@ -111,21 +111,27 @@ def seeded_ui_data(db_session: Session) -> Generator[Dict[str, Any], None, None]
scenario_id=scenario.id,
amount=1_000_000.0,
description="Drill purchase",
currency_code="USD",
)
opex = Opex(
scenario_id=scenario.id,
amount=250_000.0,
description="Fuel spend",
currency_code="USD",
)
consumption = Consumption(
scenario_id=scenario.id,
amount=1_200.0,
description="Diesel (L)",
unit_name="Liters",
unit_symbol="L",
)
production = ProductionOutput(
scenario_id=scenario.id,
amount=800.0,
description="Ore (tonnes)",
unit_name="Tonnes",
unit_symbol="t",
)
equipment = Equipment(
scenario_id=scenario.id,

View File

@@ -25,6 +25,8 @@ def test_create_consumption(client: TestClient) -> None:
"scenario_id": scenario_id,
"amount": 125.5,
"description": "Fuel usage baseline",
"unit_name": "Liters",
"unit_symbol": "L",
}
response = client.post("/api/consumption/", json=payload)
@@ -34,6 +36,7 @@ def test_create_consumption(client: TestClient) -> None:
assert body["scenario_id"] == scenario_id
assert body["amount"] == pytest.approx(125.5)
assert body["description"] == "Fuel usage baseline"
assert body["unit_symbol"] == "L"
def test_list_consumption_returns_created_items(client: TestClient) -> None:
@@ -46,6 +49,8 @@ def test_list_consumption_returns_created_items(client: TestClient) -> None:
"scenario_id": scenario_id,
"amount": amount,
"description": f"Consumption {amount}",
"unit_name": "Tonnes",
"unit_symbol": "t",
},
)
assert response.status_code == 201

View File

@@ -7,6 +7,7 @@ from main import app
def setup_module(module):
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
@@ -34,12 +35,14 @@ def test_create_and_list_capex_and_opex():
"scenario_id": sid,
"amount": 1000.0,
"description": "Initial capex",
"currency_code": "USD",
}
resp2 = client.post("/api/costs/capex", json=capex_payload)
assert resp2.status_code == 200
capex = resp2.json()
assert capex["scenario_id"] == sid
assert capex["amount"] == 1000.0
assert capex["currency_code"] == "USD"
resp3 = client.get("/api/costs/capex")
assert resp3.status_code == 200
@@ -51,12 +54,14 @@ def test_create_and_list_capex_and_opex():
"scenario_id": sid,
"amount": 500.0,
"description": "Recurring opex",
"currency_code": "USD",
}
resp4 = client.post("/api/costs/opex", json=opex_payload)
assert resp4.status_code == 200
opex = resp4.json()
assert opex["scenario_id"] == sid
assert opex["amount"] == 500.0
assert opex["currency_code"] == "USD"
resp5 = client.get("/api/costs/opex")
assert resp5.status_code == 200
@@ -71,8 +76,12 @@ def test_multiple_capex_entries():
for amount in amounts:
resp = client.post(
"/api/costs/capex",
json={"scenario_id": sid, "amount": amount,
"description": f"Capex {amount}"},
json={
"scenario_id": sid,
"amount": amount,
"description": f"Capex {amount}",
"currency_code": "EUR",
},
)
assert resp.status_code == 200
@@ -91,8 +100,12 @@ def test_multiple_opex_entries():
for amount in amounts:
resp = client.post(
"/api/costs/opex",
json={"scenario_id": sid, "amount": amount,
"description": f"Opex {amount}"},
json={
"scenario_id": sid,
"amount": amount,
"description": f"Opex {amount}",
"currency_code": "CAD",
},
)
assert resp.status_code == 200

View File

@@ -25,6 +25,8 @@ def test_create_production_record(client: TestClient) -> None:
"scenario_id": scenario_id,
"amount": 475.25,
"description": "Daily output",
"unit_name": "Tonnes",
"unit_symbol": "t",
}
response = client.post("/api/production/", json=payload)
@@ -33,6 +35,7 @@ def test_create_production_record(client: TestClient) -> None:
assert created["scenario_id"] == scenario_id
assert created["amount"] == pytest.approx(475.25)
assert created["description"] == "Daily output"
assert created["unit_symbol"] == "t"
def test_list_production_filters_by_scenario(client: TestClient) -> None:
@@ -46,6 +49,8 @@ def test_list_production_filters_by_scenario(client: TestClient) -> None:
"scenario_id": scenario_id,
"amount": amount,
"description": f"Output {amount}",
"unit_name": "Kilograms",
"unit_symbol": "kg",
},
)
assert response.status_code == 201