| Runtime |
Production and normal runtime use PostgreSQL. SQLite is allowed only for tests and legacy import tooling. |
Runtime startup refuses SQLite so bill state, cache decisions, and downloaded-run history come from PostgreSQL. |
| Bill target |
Every fetch target must resolve to a USC from a tenant, portion, or explicit request payload. |
Missing USC is treated as setup work, not a downloadable bill. |
| SC number |
NORTH targets are expected to have an SC number when available. SOUTH targets can proceed with USC-only flow. |
The UI flags missing NORTH SC setup; the fetcher still preserves payload, tenant, and portion identifiers for tracing. |
| DISCOM |
Only NORTH and SOUTH are valid. The request value wins, then tenant, then portion, then NORTH by default. |
Blank or invalid DISCOM values fall back to NORTH unless a tenant or portion explicitly says SOUTH. |
| NORTH PDF |
NORTH bill PDFs and bill metadata come from the TGNPDCL duplicate-bill portal. |
The PDF bill date, due date, amount, units, and last paid fields are treated as the current bill-cycle evidence. |
| SOUTH PDF |
SOUTH bill PDFs use the TGSPDCL/TSSPDCL LT billing flow and SOUTH-specific parsing. |
SOUTH metadata and paid text are interpreted separately from NORTH because the portals return different page shapes. |
| Portal health |
Bulk fetch checks NORTH and SOUTH portal reachability before starting. |
If a portal looks down, the UI asks for confirmation before starting that bulk fetch. |
| Concurrency |
Bulk fetches run one bill at a time, and the bill-fetch service serializes fetch requests with a process lock. |
External portals are not hit by parallel requests, reducing timeouts and inconsistent portal responses. |
| Cache skip |
skip_download_if_cached defaults to true for ordinary fetches. |
A current paid/no-due archive or an existing same-cycle PDF can be reused instead of redownloading. |
| Fresh fetch |
Fresh fetch actions set cache skip to false. |
The fetcher still avoids reusing a paid-static live cycle when payment history proves the current cycle is already settled. |
| Duplicate PDFs |
Downloaded PDFs are hashed, and existing same-hash downloaded bills can be reused. |
Old downloaded records for the same USC and different hash may be cleaned after a newer successful download. |
| Current cycle |
The bill cycle is inferred from bill_date first, then due_date, then the run creation time. |
Cycle matching, static-paid locks, cache reuse, and UI current-month paid status all depend on that cycle date. |
| NORTH due status |
NORTH status checks BillDesk first, then falls back to TGNPDCL portal metadata and operator payment history when needed. |
A paid/no-due NORTH result needs either reliable BillDesk due data or same-cycle payment-history proof when the PDF disagrees. |
| SOUTH due status |
SOUTH status checks SOUTH BillDesk text, including the no-bills-pending style response. |
SOUTH paid markers, last paid amount, and arrears are evaluated with SOUTH-specific rules. |
| Positive PDF amount |
A current positive PDF amount is due unless same-cycle payment proof says it is paid. |
For NORTH, BillDesk zero due no longer hides a positive current PDF unless payment history proves that bill cycle was paid. |
| Zero or negative PDF amount |
A current PDF amount of zero or negative means no payment is due for that cycle. |
BillDesk is skipped or ignored for that case; the original zero/negative PDF amount is preserved and due_amount_checked is set to zero. |
| Payment date |
A payment before bill_date is assumed to belong to a previous bill cycle. |
Last paid date and amount must both cover the current bill cycle before the bill is treated as paid. |
| BillDesk failures |
Timeouts, operator errors, and unclear BillDesk pages are not treated as proof of payment. |
The run stays due-safe unless portal metadata or payment history provides a stronger no-due signal. |
| Metadata quality |
A completed bill should have a file, amount signal, date signal, and due-status signal. |
If the PDF downloads but metadata is incomplete, the run can be marked metadata_incomplete instead of silently looking complete. |
| UI buckets |
Unpaid Bills shows latest non-paid bill results; Paid Bills shows current-month paid/no-due results. |
Older paid/no-due rows are kept for history but are not counted as the current paid bill unless they have a current cycle lock. |
| WhatsApp |
WhatsApp delivery only happens when requested and after a bill result is available. |
Missing phone numbers block delivery, not the bill fetch itself. |
| Storage |
PDF files are written under the configured storage directory and served through /storage. |
The database stores the resolved file path, and the UI converts storage paths into PDF links. |