Topic: Realized FX not posted when a PO prepayment is moved to the invoice
Claude Assisted.
Version: FrontAccounting 2.4.19 and 2.4.20 (same code in both)
Summary
When an advance payment allocated to a Purchase Order is transferred to the
supplier invoice on invoice entry, the exchange difference between the payment
rate and the invoice rate is never posted. The supplier sub-ledger shows the
invoice fully settled, but GL Accounts Payable keeps the FX difference as a
residual balance. Normal allocations (Allocate Supplier Payments) post the FX
correctly; only the PO-prepayment path is affected.
Steps to reproduce (home currency CAD, supplier in USD)
1. Purchase Order Entry: USD supplier, date 11/01, 1,000.00 USD.
2. Supplier payment dated 11/01, 1,000.00 USD at rate 1.30; allocate it to the PO
(the PO is only offered for allocation before it is received).
3. Receive the PO on 11/02.
4. Enter the supplier invoice for that delivery dated 11/03 at rate 1.40.
Expected
The invoice GL shows an exchange variation pair for the advance:
Dr Accounts Payable 100.00 / Cr Exchange Variances 100.00
(1,000 x (1.40 - 1.30)), and GL AP for the supplier nets to zero.
Actual
No exchange variation is posted. The invoice is fully allocated in the
sub-ledger, but GL AP for the supplier is left at Cr 100.00.
Cause
add_supp_invoice() (purchasing/includes/db/invoice_db.inc) moves the PO
allocations with get_po_prepayments() -> reallocate_payments() ->
allocate_payment(). allocate_payment() only rewrites supp_allocations and the
alloc totals; it never calls exchange_variation(), unlike
allocation_cart::write().
Suggested fix
After reallocate_payments() in add_supp_invoice(), for supplier invoices, call
exchange_variation() for each allocation now pointing at the new invoice,
using the payment's own date (as allocation_cart::write() does):
if ($trans_type == ST_SUPPINVOICE && empty($GLOBALS['no_exchange_variations'])) {
$res = db_query("SELECT a.trans_type_from, a.trans_no_from, a.amt, t.tran_date
FROM ".TB_PREF."supp_allocations a
JOIN ".TB_PREF."supp_trans t ON t.type=a.trans_type_from
AND t.trans_no=a.trans_no_from AND t.supplier_id=a.person_id
WHERE a.trans_type_to=".ST_SUPPINVOICE." AND a.trans_no_to=".db_escape($invoice_id)."
AND a.person_id=".db_escape($supp_trans->supplier_id));
while ($a = db_fetch($res))
if ($a['trans_type_from'] != ST_JOURNAL)
exchange_variation($a['trans_type_from'], $a['trans_no_from'],
ST_SUPPINVOICE, $invoice_id, sql2date($a['tran_date']),
$a['amt'], PT_SUPPLIER);
}
Note for invoice re-entry (edit): the invoice gets a new number and existing
allocations are moved the same way. FX previously booked on the payment side
(memo "Supplier Invoice <old no>") is not removed by void_transaction(), so a
complete fix should remove the old pair's FX before re-posting. Related: the
allocation re-save duplicate FX issue below makes this worse.
