-- Import-mode companies (gl_auto_post=false) get their GL from the Buildium pull, -- so locally-entered bank transactions never reached the GL and were invisible on -- GL-based reports (P&L, Income Statement, Balance Sheet) even though they -- reconciled. This lets those manual transactions post to the GL. -- -- A transaction posts UNLESS it is: voided; sourced from the Buildium GL pull -- (journal_entry_line_id set — would double-count); explicitly excluded -- (exclude_from_gl — used to freeze pre-existing manual/duplicate register rows on -- already-tied books so they don't double-post if later edited); a transfer/deposit -- leg (handled by post_transfer_gl / post_deposit_gl); or missing an account/counter. -- -- One-time data ops applied alongside this migration (not repeatable, so kept out -- of the migration body): (a) linked every Buildium-sourced register row to its GL -- bank line via (account,date,direction,amount) matching; (b) set -- exclude_from_gl=true on all remaining pre-existing unlinked manual rows for -- import companies EXCEPT Bent Oak; (c) posted Bent Oak's 4 verified-missing -- operating rows (interest/insurance/electric/water-fee). alter table accounting.transactions add column if not exists exclude_from_gl boolean not null default false; create or replace function accounting.post_transaction_gl(_txn_id uuid) returns void language plpgsql security definer set search_path to 'public', 'accounting' as $function$ declare t accounting.transactions%rowtype; _counter uuid; _je uuid; _amt numeric; begin select * into t from accounting.transactions where id=_txn_id; if not found then return; end if; perform accounting._gl_clear(t.company_id, 'acmacc_txn', t.id::text); if coalesce(t.voided,false) then return; end if; if t.journal_entry_line_id is not null then return; end if; -- already in GL via Buildium pull if coalesce(t.exclude_from_gl,false) then return; end if; -- frozen legacy/duplicate register row if t.transfer_id is not null or t.deposit_id is not null then return; end if; if t.account_id is null then return; end if; _amt := coalesce(t.amount,0); if _amt = 0 then return; end if; _counter := case when t.customer_id is not null then accounting.coa_ar(t.company_id) when t.coa_account_id is not null then t.coa_account_id when t.vendor_id is not null then accounting.coa_ap(t.company_id) else null end; if _counter is null then return; end if; insert into accounting.journal_entries (company_id, date, description, reference, external_source, external_id) values (t.company_id, t.date, coalesce(nullif(t.description,''), 'Bank transaction'), t.reference, 'acmacc_txn', t.id::text) returning id into _je; if t.type = 'credit' then insert into accounting.journal_entry_lines (journal_entry_id, account_id, debit, credit, description) values (_je, t.account_id, _amt, 0, t.description), (_je, _counter, 0, _amt, t.description); else insert into accounting.journal_entry_lines (journal_entry_id, account_id, debit, credit, description) values (_je, _counter, _amt, 0, t.description), (_je, t.account_id, 0, _amt, t.description); end if; end$function$;