Symptom
"It's right on the review screen, but the sent / saved value is 0 or empty."
Cause
The review screen (GET) and the actual send (POST) each called the same builder function. Only one injected the exchange-rate parameter. GET displayed a price in the hundreds of thousands of won; POST got None → int(None or 0) → sent 0.
How to spot it
Fix
int(x or 0) turns "I don't know" into "zero" and sends it quietly.Not only two code paths — the canonical system and a local copy diverge too
The sourcing map's krw disagreed with the marketplace's actual listed price. The marketplace is canonical; the local map is a copy. Compute margin from the copy and you get the wrong answer. The split isn't limited to two code paths; "canonical system vs local copy" is the same shape.
In the same map, 618 of 6,541 records had both usd and jpy empty. A source can be linked and still be useless: a record missing the price is equivalent to an unlinked record as far as the margin check is concerned. Treat a missing required input the same way you treat a missing link.
An upstream 0 turns a derived price negative
One channel had products whose sale-price field was 0 (a temporary-failure status, among others). Pulled as-is and fed into a derived price for another channel — cost − random — the result is a negative price. The derivation formula assumes the upstream value is sane. Put a lower-bound check right before the derivation. A 0 is often not "a value" but "no value" wearing different clothes.