Terminal
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
-- Two honesty fixes to the section 14 reporting views.
|
||||||
|
--
|
||||||
|
-- Both are about a number that reads as a fact when it is really an absence.
|
||||||
|
-- Neither changes what anyone is permitted to see.
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- 1. A household with nothing past due owes zero, not "unknown"
|
||||||
|
-- ============================================================================
|
||||||
|
--
|
||||||
|
-- SUM(...) FILTER over no matching rows returns NULL, so past_due_cents came
|
||||||
|
-- back NULL for any household that is simply up to date. v_campus_billing_summary
|
||||||
|
-- already wraps the identical expression in COALESCE; this brings the household
|
||||||
|
-- view into line so callers can add the two together without special-casing.
|
||||||
|
--
|
||||||
|
-- worst_days_overdue is deliberately left NULL: there is no meaningful "zero
|
||||||
|
-- days overdue" for an invoice that was never overdue, and 0 would read as
|
||||||
|
-- "due today".
|
||||||
|
CREATE OR REPLACE VIEW public.v_household_receivables
|
||||||
|
WITH (security_invoker = true) AS
|
||||||
|
SELECT
|
||||||
|
d.household_id,
|
||||||
|
d.household_name,
|
||||||
|
COUNT(DISTINCT d.student_id) AS students,
|
||||||
|
SUM(d.total_cents) AS invoiced_cents,
|
||||||
|
SUM(d.amount_paid_cents) AS paid_cents,
|
||||||
|
SUM(d.balance_due_cents) AS balance_cents,
|
||||||
|
COALESCE(SUM(d.balance_due_cents) FILTER (WHERE d.is_past_due), 0) AS past_due_cents,
|
||||||
|
MAX(d.days_overdue) FILTER (WHERE d.is_past_due) AS worst_days_overdue
|
||||||
|
FROM public.v_billing_detail d
|
||||||
|
WHERE d.household_id IS NOT NULL
|
||||||
|
GROUP BY d.household_id, d.household_name;
|
||||||
|
|
||||||
|
GRANT SELECT ON public.v_household_receivables TO authenticated;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- 2. Unfunded scholarship money: absent is not the same as zero
|
||||||
|
-- ============================================================================
|
||||||
|
--
|
||||||
|
-- scholarship_pending_cents reads public.scholarships and public.payments,
|
||||||
|
-- whose RLS admits only billing admins, auditors and a student's own parents.
|
||||||
|
-- Under security_invoker a campus administrator's subquery therefore matched
|
||||||
|
-- nothing and the tile rendered $0 — indistinguishable from "all funding
|
||||||
|
-- received", which is the opposite of the truth and the more reassuring of the
|
||||||
|
-- two readings.
|
||||||
|
--
|
||||||
|
-- The column now returns NULL for anyone who cannot see scholarship data, so
|
||||||
|
-- the dashboard can render "—" and say nothing rather than something false.
|
||||||
|
-- The gate mirrors the scholarships SELECT policy exactly; widening who may see
|
||||||
|
-- this number is a policy decision, not a reporting one, and is not made here.
|
||||||
|
CREATE OR REPLACE VIEW public.v_campus_billing_summary
|
||||||
|
WITH (security_invoker = true) AS
|
||||||
|
SELECT
|
||||||
|
c.id AS campus_id,
|
||||||
|
c.name AS campus_name,
|
||||||
|
COUNT(d.invoice_id) AS invoice_count,
|
||||||
|
COALESCE(SUM(d.total_cents), 0) AS total_invoiced_cents,
|
||||||
|
COALESCE(SUM(d.amount_paid_cents), 0) AS total_collected_cents,
|
||||||
|
COALESCE(SUM(d.balance_due_cents), 0) AS total_unpaid_cents,
|
||||||
|
COALESCE(SUM(d.balance_due_cents) FILTER (WHERE d.is_past_due), 0) AS past_due_cents,
|
||||||
|
COALESCE(SUM(d.scholarship_cents), 0) AS scholarship_applied_cents,
|
||||||
|
COALESCE(SUM(d.balance_due_cents) FILTER (WHERE d.on_payment_plan), 0)
|
||||||
|
AS payment_plan_balance_cents,
|
||||||
|
COUNT(DISTINCT d.student_id) FILTER (WHERE d.is_past_due) AS delinquent_accounts,
|
||||||
|
CASE
|
||||||
|
WHEN public.is_billing_admin() OR public.is_auditor() THEN
|
||||||
|
COALESCE((
|
||||||
|
SELECT SUM(sc.award_amount_cents)
|
||||||
|
FROM public.scholarships sc
|
||||||
|
JOIN public.students st ON st.id = sc.student_id
|
||||||
|
WHERE st.primary_campus_id = c.id
|
||||||
|
AND sc.status = 'active'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM public.payments p
|
||||||
|
WHERE p.scholarship_id = sc.id AND p.status = 'posted')
|
||||||
|
), 0)
|
||||||
|
ELSE NULL
|
||||||
|
END AS scholarship_pending_cents
|
||||||
|
FROM public.campuses c
|
||||||
|
LEFT JOIN public.v_billing_detail d ON d.campus_id = c.id
|
||||||
|
GROUP BY c.id, c.name;
|
||||||
|
|
||||||
|
GRANT SELECT ON public.v_campus_billing_summary TO authenticated;
|
||||||
Reference in New Issue
Block a user