feat: replace abstract exchange rate with sample-based conversion

- Instead of entering a hard-to-calculate rate like 0.00773,
  users now enter a real sample (e.g. receipt=1000 KES, claimed=7.73 USD)
- The system computes the rate automatically: 7.73 / 1000 = 0.00773
- Users can get the sample values from their payment app notification
- Much more intuitive, especially for currencies with small exchange rates
This commit is contained in:
Claus Lohmar 2026-05-30 12:33:48 +00:00
parent 4895b3d608
commit e00c3373cb
2 changed files with 54 additions and 9 deletions

View file

@ -98,13 +98,22 @@ func (h *EventHandler) CreateEvent(w http.ResponseWriter, r *http.Request) {
baseCurrency := r.FormValue("base_currency") baseCurrency := r.FormValue("base_currency")
if baseCurrency == "" { if baseCurrency == "" {
baseCurrency = "EUR" baseCurrency = "USD"
} }
// Compute exchange rate from user-provided sample:
// e.g. receipt=1000 KES, claimed=7.73 USD → rate = 7.73 / 1000 = 0.00773
exchangeRate := 1.0 exchangeRate := 1.0
if v := r.FormValue("exchange_rate"); v != "" { sampleReceipt := r.FormValue("sample_receipt_amount")
if parsed, err := strconv.ParseFloat(v, 64); err == nil && parsed > 0 { sampleClaim := r.FormValue("sample_claim_amount")
exchangeRate = parsed if sampleReceipt != "" && sampleClaim != "" {
sampleReceiptVal, err1 := strconv.ParseFloat(sampleReceipt, 64)
sampleClaimVal, err2 := strconv.ParseFloat(sampleClaim, 64)
if err1 == nil && err2 == nil && sampleReceiptVal > 0 && sampleClaimVal > 0 {
exchangeRate = sampleClaimVal / sampleReceiptVal
log.Printf("INFO [%s] handlers: CreateEvent: computed rate %.6f from sample %s %s → %.2f %s",
time.Now().Format(time.RFC3339), exchangeRate,
r.FormValue("sample_receipt_currency"), sampleReceipt, sampleClaimVal, baseCurrency)
} }
} }

View file

@ -39,18 +39,54 @@
<label for="base_currency">Claim Currency</label> <label for="base_currency">Claim Currency</label>
<select id="base_currency" name="base_currency" required> <select id="base_currency" name="base_currency" required>
<option value="EUR">EUR - Euro</option> <option value="EUR">EUR - Euro</option>
<option value="USD">USD - US Dollar</option> <option value="USD" selected>USD - US Dollar</option>
<option value="GBP">GBP - British Pound</option> <option value="GBP">GBP - British Pound</option>
<option value="KES" selected>KES - Kenyan Shilling</option> <option value="KES">KES - Kenyan Shilling</option>
<option value="CHF">CHF - Swiss Franc</option> <option value="CHF">CHF - Swiss Franc</option>
<option value="SEK">SEK - Swedish Krona</option> <option value="SEK">SEK - Swedish Krona</option>
<option value="NOK">NOK - Norwegian Krone</option> <option value="NOK">NOK - Norwegian Krone</option>
<option value="PLN">PLN - Polish Zloty</option> <option value="PLN">PLN - Polish Zloty</option>
</select> </select>
</div> </div>
<div class="form-group"> </div>
<label for="exchange_rate">Exchange Rate (1 {receipt currency} = ? {claim currency})</label> <div class="card" style="padding: 0.75rem; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 0.5rem; margin-bottom: 0.5rem;">
<input type="number" id="exchange_rate" name="exchange_rate" step="0.000001" min="0.000001" value="1.0" required inputmode="decimal"> <div style="font-size: 0.875rem; font-weight: 600; color: #166534; margin-bottom: 0.5rem;">
💱 Conversion Sample
</div>
<p style="font-size: 0.8rem; color: #4b5563; margin-bottom: 0.75rem;">
Enter a real receipt amount and what you were actually charged in your claim currency.
The exchange rate is calculated automatically. Check your payment app/bank notification for the exact converted amount.
</p>
<div class="form-row">
<div class="form-group">
<label for="sample_receipt_amount">Receipt Amount</label>
<input type="number" id="sample_receipt_amount" name="sample_receipt_amount" step="0.01" min="0.01" placeholder="e.g. 1000" required inputmode="decimal">
</div>
<div class="form-group" style="flex: 0 0 120px;">
<label for="sample_receipt_currency">Currency</label>
<select id="sample_receipt_currency" name="sample_receipt_currency" required>
<option value="KES" selected>KES</option>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="GBP">GBP</option>
<option value="JPY">JPY</option>
<option value="CHF">CHF</option>
<option value="SEK">SEK</option>
<option value="NOK">NOK</option>
<option value="DKK">DKK</option>
<option value="PLN">PLN</option>
<option value="ZAR">ZAR</option>
<option value="NGN">NGN</option>
<option value="TZS">TZS</option>
<option value="UGX">UGX</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="sample_claim_amount">What you claimed / were charged</label>
<input type="number" id="sample_claim_amount" name="sample_claim_amount" step="0.01" min="0.01" placeholder="e.g. 7.73" required inputmode="decimal">
</div>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary btn-block">Create Event</button> <button type="submit" class="btn btn-primary btn-block">Create Event</button>