Core Logic
This document extracts and elaborates on the business rules historically embedded in the C++ Doxygen headers.
The Rating Engine
The Rater class is the heart of the platform's pricing logic. It implements the monetary calculations for both online (DIAMETER) and offline (CDR) usage.
Rating Context
The rater accepts billing::Charge (prepaid) or billing::CDR (postpaid) protobuf messages.
| Component | Responsibility |
|---|---|
Rater::pre_rate |
Reads the associated Service ID and determines the appropriate REF_Price_plan from the locally cached Refdata singleton. Returns an error if the plan is missing. |
Rater::rate |
The core calculation. Applies base rates per unit (minute / KB / event). Checks against Free_units buckets specific to that price plan. Handles time-of-day offsets if defined in the plan reference data. |
Rater::post_rate |
Marks the object as RATED, finalizes discounts, and calculates the final_price attribute to prepare for DB insertion. |
Price Resolution
If a user has an active bundle (e.g., 500 MB data remaining), Rater::rate will first decrement the specific service Free_units bucket of the subscriber before applying monetary generic charges to final_price.
Database Interaction (DB_layer)
The DB_layer encapsulates all raw MySQL X DevAPI queries. It manages connection pooling and entity hydration.
Key Methods
billOne
Responsible for transitioning a subscriber's unbilled, rated CDRs into a formal billing::Bill. While DB_layer::billOne directly executes the raw aggregation query, BillingHandler::billOne (the 5-parameter canonical entry point) orchestrates the full flow.
- Input:
billing_id,subscriber_id,year,month,rater_ptr. - Logic:
- Resolves
subscriber_idto anMSISDNand determines valid billing periods (ifyearandmonthare0, it selects all unbilled periods). - Invokes
DB_layer::billOneto total the rated amounts (monetary + free-unit usage). - Inserts a
billing::Billrecord with statusNEW. - Updates each participating CDR's
event_statustoBILLEDto prevent double billing. - Output: Returns
0on comprehensive success or-1if any step natively fails.
getUsedFreeUnits
Retrieves cumulative usage metrics for a specific time boundary.
- Input:
billing::Subscriber &sub,int month,int year. - Logic: Runs an aggregate sum query on the CDRs and historical balances. This is used by the
Rateror the CRM UI to determine if a customer has hit their monthly cap.
Memory Management in C++
Because the application relies heavily on dynamically allocated Protobuf messages traversing the ACE Reactor event loop, memory safety is enforced via specific patterns:
- Protobuf Ownership: When an entity like
billing::Subscriberis fetched viaDB_layer::select, it is passed by reference. - Handlers & Pointers: When
Charge::fromCCRgenerates a new charge instance from a network event, it utilizesstd::make_uniqueto prevent leaks inside the detached worker threads.
Invoice Generation (BillFormatterHandler)
The BillFormatterHandler operates as an asynchronous background task within the ACE_Reactor.
- Thread Pooling: Upon timer execution, it scans the database for any
NEWbills. It then pushes individual PDF generation tasks into the globalTHREAD_POOL, guaranteeing the main reactor loop is never blocked by layout rendering. - Context Injection: Extracts relevant historical limits based on
prepaidorpostpaidsubscriber types. Note that data mapping strictly pre-formats values in C++ before handing them to the simplistic Inja template engine (e.g., usingUtils::month(bill.billed_month()) + " " + std::to_string(bill.billed_year())to generate "March 2026", andUtils::formatCurrency(amount)for monetary floats). - Outputs & Templates: HTML templates load from
./templates(postpaid.html/prepaid.html). The Inja parser dictates loop chunking limits based on template constraints (e.g.,if loop.index1 % 20 == 1). Fully generated PDFs are saved securely in period directories under./invoices. - Completion States: A generated bill is transitioned to
Utils::BILL_STATUS_PAID(prepaid) orUtils::BILL_STATUS_UNPAID(postpaid), and triggers a CRM Notification.