Version 14 (modified by 11 hours ago) ( diff ) | ,
---|
SQL Views
The application uses SQL VIEWs to simplify common queries and improve maintainability.
v_invoice_analytics
Purpose: Shows invoice info with client and company names plus business calculations
What it does: Joins Invoice, Client, and Tenant tables and calculates days overdue, payment status, and value categories
Usage: Makes it easier to see which client each invoice belongs to and get business insights without writing complex queries
SELECT * FROM v_invoice_analytics WHERE invoice_status = 'PENDING';
v_client_financial_summary
Purpose: Shows total amounts for each client with risk analysis
What it does: Groups invoices by client and calculates total paid, pending, overdue amounts plus payment behavior and risk levels
Usage: Provides a complete overview of each client's financial status and payment history
SELECT * FROM v_client_financial_summary ORDER BY total_invoiced DESC;
API Integration
The views are used in API endpoints to provide simplified data access:
src/app/api/clients/summary/route.ts: Uses v_client_financial_summary view for detailed client analytics
src/app/api/invoices/totals/route.ts: Uses v_invoice_analytics view for invoice analytics
Benefits
Simpler queries: No need to write complex JOINs every time
Consistent data: Same calculation logic everywhere
Easier to maintain: Change the view, all queries get updated
Better performance: Database can optimize the view
The views are implemented in: sql/03_business_views.sql
Transactions
The application uses database transactions in several key operations:
- Invoice creation: When creating a new invoice, a transaction ensures that both the invoice and its items are created atomically
- Status updates: Invoice status changes are handled within transactions to maintain data consistency
- Client updates: When updating client information, related operations are wrapped in transactions
Transactions are implemented in the following files:
src/app/api/invoices/[id]/route.ts
: For updating and deleting invoicessrc/app/api/invoices/route.ts
: For creating new invoices
Triggers
The system implements a sophisticated trigger system for invoice management:
invoice_status_update_trigger
: Automatically fires when an invoice status changes- Logs status changes in
InvoiceStatusHistory
- Creates notifications in
InvoiceNotifications
- Updates client status based on payment history
- Automatically extends due dates when needed
- Calculates and updates financial metrics
- Logs status changes in
The trigger is implemented in sql/01_invoice_status_trigger.sql
.
Indexes
Several optimized indexes are implemented for better query performance:
- Client email lookups:
idx_client_email
: Basic email search optimizationidx_client_email_tenant
: Composite index for tenant-specific email searchesidx_client_email_lower
: Case-insensitive email searches
- Invoice management:
idx_invoice_status_history_invoice_id
: Optimizes status history lookupsidx_invoice_status_history_changed_at
: Improves date-based queriesidx_invoice_notifications_invoice_id
: Speeds up notification retrievalidx_invoice_notifications_processed
: Partial index for unprocessed notifications
- Complex invoice analytics: (/agencyos/sql/04_complex_invoice_analytics_index.sql
idx_invoice_complex_analytics
: Multi-column index for tenant-specific analytics with status, currency, monthly grouping, and amount filteringidx_invoice_overdue_analysis
: Optimized for overdue invoice queries with tenant, status, due date, and amount sortingidx_invoice_financial_reporting
: Quarterly financial reporting with currency, status, and amount categorizationidx_invoice_number_search
: Case-insensitive invoice number search with tenant isolationidx_client_invoice_performance
: Client performance metrics with status-based priority orderingidx_invoice_temporal_analytics
: Time-series analysis with year, month, and day-of-week grouping
Using EXPLAIN
on the Client
table
- Before creating the index:
EXPLAIN ANALYZE SELECT * FROM "Client" WHERE email = 'client12@example.com';
- After adding index and creating 1000 rows:
EXPLAIN ANALYZE SELECT * FROM "Client" WHERE email = 'client12@example.com';
Index Scan using idx_client_email on "Client" (cost=0.28..8.29 rows=1 width=303) Index Cond: (email = 'client12@example.com'::text)
Using EXPLAIN
on complex invoice analytics
- Multi-tenant analytics query:
EXPLAIN ANALYZE SELECT invoice_status, invoice_currency, DATE_TRUNC('month', invoice_issue_date) as month, SUM(invoice_total_amount) as total_amount, COUNT(*) as invoice_count FROM "Invoice" WHERE tenant_id = 'tenant_123' AND invoice_status IN ('PENDING', 'PROCESSING', 'OVERDUE', 'PAID') AND invoice_issue_date >= '2024-01-01' AND invoice_currency = 'EUR' GROUP BY invoice_status, invoice_currency, DATE_TRUNC('month', invoice_issue_date) ORDER BY month DESC, total_amount DESC;
- Result with complex index:
Index Only Scan using idx_invoice_complex_analytics on "Invoice" (cost=0.14..12.49 rows=11 width=24) Index Cond: ((tenant_id = 'tenant_123') AND (invoice_currency = 'EUR'::"Currency") AND (invoice_issue_date >= '2024-01-01 00:00:00'::timestamp without time zone)) Heap Fetches: 11
Attachments (2)
- index.jpg (196.4 KB ) - added by 4 days ago.
- invoice_analytics.jpg (239.7 KB ) - added by 11 hours ago.
Download all attachments as: .zip