So far, the port of batch_plan.py has used 37.5 entered as a number. The real script gets it as text. Each evening, the head roaster pastes the order from the web shop’s export as ORDER_KG_TEXT = "37.5", and the float() call turns it into a number.
That paste is the one part a person types, so it’s where things go wrong. You’ll port the conversion, see what JavaScript does with a bad paste, and add the check Python gave you for free.
Convert the Pasted Order
The Python script converts the text with float(), then works out the green beans and the price:
ORDER_KG_TEXT = "37.5"
WEIGHT_LOSS = 0.15
GREEN_PRICE_PER_KG = 7.4
SHIPPING_EUR = 12
order_kg = float(ORDER_KG_TEXT)
green_kg = order_kg / (1 - WEIGHT_LOSS)
total = round(green_kg * GREEN_PRICE_PER_KG, 2) + SHIPPING_EUR
print(f"Green beans: {green_kg:,.1f} kg")
print(f"Green coffee: €{total:,.2f} with delivery")
The planner uses Number for ordinary whole and decimal values. Number(text) is a starting point for these conversions, not an exact replacement for every float(text) or int(text) call. It consumes the whole text and allows surrounding whitespace, as float() does, but their accepted spellings differ: Number("0x10") gives 16 where float() raises an error.
One difference is that Number("37.5") gives 37.5 where int("37.5") raises, and Number.isInteger() checks only whether the converted value is whole. It still accepts "3.0", which int(text) rejects, and large integer text can lose precision before this check.
For brevity, the port leaves out the rounding from Rounding With Math.round() and the formatting, which comes later:
const ORDER_KG_TEXT = "37.5";
const WEIGHT_LOSS = 0.15;
const GREEN_PRICE_PER_KG = 7.4;
const SHIPPING_EUR = 12;
const orderKg = Number(ORDER_KG_TEXT);
const greenKg = orderKg / (1 - WEIGHT_LOSS);
const total = greenKg * GREEN_PRICE_PER_KG + SHIPPING_EUR;
console.log(`Green beans: ${greenKg} kg`);
console.log(`Green coffee: €${total} with delivery`);