mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
Accounting: prior-period reconcile items, void txns, unified report filters, accrual-only
1. Reconciliation now shows ALL outstanding (unreconciled) items on/before the statement date, including ones from prior periods — removed the prior-recon date floor. Finalized items still drop off (they carry a reconciliation_id). 2. Void transactions in Banking and Reconciliation. New accounting.transactions .voided flag (+ voided_at/by); voided rows stay visible (strikethrough + VOID badge) but are excluded from the running balance, register totals, cached account balance, and reconciliation. post_transaction_gl reverses the GL for gl_managed companies; un-void supported from Banking. 3. Unified report filters: the single Period bar on the Reports page now drives every report. General Ledger, Trial Balance, AR Aging (Property), Pre-Paid Homeowners, Cash Disbursement, and Reserve Fund no longer have their own date pickers — they consume the shared from/to (range) or to (as-of). 4. Accrual only: removed the cash-basis toggle from Trial Balance and General Ledger (the data was always accrual GL anyway; the cash label was misleading). All income/expense reports recognize on billed/issue date. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
-- 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$;
|
||||
Reference in New Issue
Block a user