mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Accounting: selectable-source / multi-line manual deposits
Deposits no longer force the credit side through Undeposited Funds — the structural cause of negative Undeposited balances. A deposit can now credit any account(s): interest income, a refund, an insurance reimbursement, cash straight to the bank, etc. - Schema: add accounting.deposit_lines (deposit_id, company_id, account_id, amount, memo) for the credit side, plus deposits.source_account_id as a single-source fallback. RLS mirrors deposits (staff + company member). - post_deposit_gl: Dr bank for the total; Cr each deposit_lines row's account for its amount; no lines -> Cr source_account_id; neither -> Cr Undeposited Funds (backward compatible — existing deposits stay Dr Bank / Cr Undeposited). Remainder safety net keeps the entry balanced. New trg_acct_deposit_line_gl re-posts when lines change (header trigger fires before lines exist). - Make Deposit page: GL-driven submit writes the deposit header + deposit_lines and marks selected payments deposited. Adds an "Other deposit lines" grid (account + amount + memo) alongside the existing Undeposited selection, with a running grand total and a soft guard against over-crediting Undeposited. Drops the old bank/Undeposited register-transaction inserts and manual balance pokes (never exercised in production; carried a money-in sign bug). Deposits are GL-only, consistent with the sync-created deposits already in the DB. Verified Dr/Cr for single-source and multi-line scenarios against the live GL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
-- Manual Deposits: let a deposit's source (credit) account be selectable instead of
|
||||
-- always Undeposited Funds, and support multi-line deposits. This removes the forced
|
||||
-- routing through Undeposited Funds (the structural cause of negative Undeposited
|
||||
-- balances) and lets a deposit book interest income, refunds, reimbursements, etc.
|
||||
|
||||
-- Single-source fallback: a deposit with no lines credits this account (default
|
||||
-- Undeposited Funds when null), keeping the existing "deposit received payments" flow.
|
||||
alter table accounting.deposits
|
||||
add column if not exists source_account_id uuid references accounting.accounts(id);
|
||||
|
||||
-- Multi-line credits: one deposit = Dr Bank (total) and a set of credit lines, each
|
||||
-- with its own account and amount. The deposit's amount equals the sum of its lines.
|
||||
create table if not exists accounting.deposit_lines (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
deposit_id uuid not null references accounting.deposits(id) on delete cascade,
|
||||
company_id uuid not null references accounting.companies(id) on delete cascade,
|
||||
account_id uuid not null references accounting.accounts(id),
|
||||
amount numeric not null default 0,
|
||||
memo text,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
create index if not exists idx_deposit_lines_deposit on accounting.deposit_lines(deposit_id);
|
||||
|
||||
alter table accounting.deposit_lines enable row level security;
|
||||
|
||||
drop policy if exists "Accounting staff full access" on accounting.deposit_lines;
|
||||
create policy "Accounting staff full access" on accounting.deposit_lines
|
||||
for all to authenticated
|
||||
using (accounting.is_accounting_staff()) with check (accounting.is_accounting_staff());
|
||||
|
||||
drop policy if exists "Members CRUD deposit_lines" on accounting.deposit_lines;
|
||||
create policy "Members CRUD deposit_lines" on accounting.deposit_lines
|
||||
for all to authenticated
|
||||
using (accounting.is_company_member(company_id, auth.uid()))
|
||||
with check (accounting.is_company_member(company_id, auth.uid()));
|
||||
|
||||
grant select, insert, update, delete on accounting.deposit_lines to authenticated, service_role;
|
||||
|
||||
-- The deposit header trigger posts GL on insert (before any lines exist), so re-post
|
||||
-- whenever the lines change too. post_deposit_gl clears + reposts, so this is idempotent.
|
||||
create or replace function accounting.tg_deposit_line_gl()
|
||||
returns trigger language plpgsql security definer set search_path to 'public', 'accounting' as $$
|
||||
begin
|
||||
begin
|
||||
perform accounting.post_deposit_gl(coalesce(new.deposit_id, old.deposit_id));
|
||||
exception when others then raise warning 'accounting: deposit line GL post failed: %', sqlerrm; end;
|
||||
return coalesce(new, old);
|
||||
end$$;
|
||||
|
||||
drop trigger if exists trg_acct_deposit_line_gl on accounting.deposit_lines;
|
||||
create trigger trg_acct_deposit_line_gl
|
||||
after insert or update or delete on accounting.deposit_lines
|
||||
for each row execute function accounting.tg_deposit_line_gl();
|
||||
Reference in New Issue
Block a user