-- Void support for bank-register transactions. Voided rows stay for audit but
-- are excluded from the cached account balance and, for gl_managed companies,
-- reversed out of the GL (post_transaction_gl clears + posts nothing when voided).
alter table accounting.transactions
  add column if not exists voided boolean not null default false,
  add column if not exists voided_at timestamptz,
  add column if not exists voided_by uuid;

-- post_transaction_gl: a voided txn clears its prior GL entry and posts nothing.
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 not accounting.gl_managed(t.company_id) then return; end if;
  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$;

-- sync_account_balance: exclude voided transactions from every cached-balance sum.
-- (Full body re-created so each SUM filters COALESCE(voided,false)=false.)
create or replace function accounting.sync_account_balance()
 returns trigger language plpgsql security definer set search_path to 'public'
as $function$
DECLARE v_computed numeric;
BEGIN
  IF TG_OP IN ('INSERT','UPDATE') THEN
    IF NEW.account_id IS NOT NULL THEN
      SELECT COALESCE(SUM(CASE WHEN type='credit' THEN amount ELSE -amount END),0) INTO v_computed
        FROM accounting.transactions WHERE account_id=NEW.account_id AND COALESCE(voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=NEW.account_id;
    END IF;
    IF NEW.coa_account_id IS NOT NULL THEN
      SELECT COALESCE(SUM(CASE WHEN a.type IN ('expense','asset') THEN CASE WHEN t.type='debit' THEN t.amount ELSE -t.amount END
                              ELSE CASE WHEN t.type='credit' THEN t.amount ELSE -t.amount END END),0) INTO v_computed
        FROM accounting.transactions t JOIN accounting.accounts a ON a.id=t.coa_account_id
       WHERE t.coa_account_id=NEW.coa_account_id AND COALESCE(t.voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=NEW.coa_account_id;
    END IF;
  END IF;
  IF TG_OP='UPDATE' THEN
    IF OLD.account_id IS NOT NULL AND OLD.account_id IS DISTINCT FROM NEW.account_id THEN
      SELECT COALESCE(SUM(CASE WHEN type='credit' THEN amount ELSE -amount END),0) INTO v_computed
        FROM accounting.transactions WHERE account_id=OLD.account_id AND COALESCE(voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=OLD.account_id;
    END IF;
    IF OLD.coa_account_id IS NOT NULL AND OLD.coa_account_id IS DISTINCT FROM NEW.coa_account_id THEN
      SELECT COALESCE(SUM(CASE WHEN a.type IN ('expense','asset') THEN CASE WHEN t.type='debit' THEN t.amount ELSE -t.amount END
                              ELSE CASE WHEN t.type='credit' THEN t.amount ELSE -t.amount END END),0) INTO v_computed
        FROM accounting.transactions t JOIN accounting.accounts a ON a.id=t.coa_account_id
       WHERE t.coa_account_id=OLD.coa_account_id AND COALESCE(t.voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=OLD.coa_account_id;
    END IF;
  END IF;
  IF TG_OP='DELETE' THEN
    IF OLD.account_id IS NOT NULL THEN
      SELECT COALESCE(SUM(CASE WHEN type='credit' THEN amount ELSE -amount END),0) INTO v_computed
        FROM accounting.transactions WHERE account_id=OLD.account_id AND COALESCE(voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=OLD.account_id;
    END IF;
    IF OLD.coa_account_id IS NOT NULL THEN
      SELECT COALESCE(SUM(CASE WHEN a.type IN ('expense','asset') THEN CASE WHEN t.type='debit' THEN t.amount ELSE -t.amount END
                              ELSE CASE WHEN t.type='credit' THEN t.amount ELSE -t.amount END END),0) INTO v_computed
        FROM accounting.transactions t JOIN accounting.accounts a ON a.id=t.coa_account_id
       WHERE t.coa_account_id=OLD.coa_account_id AND COALESCE(t.voided,false)=false;
      UPDATE accounting.accounts SET balance=v_computed, updated_at=now() WHERE id=OLD.coa_account_id;
    END IF;
    RETURN OLD;
  END IF;
  RETURN NEW;
END;
$function$;
