1 (edited by andijani 11/02/2026 02:06:56 pm)

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.

Post's attachments

invoice_db.inc 32.4 kb, file has never been downloaded. 

You don't have the permssions to download the attachments of this post.