Skip to content

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_id to an MSISDN and determines valid billing periods (if year and month are 0, it selects all unbilled periods).
  • Invokes DB_layer::billOne to total the rated amounts (monetary + free-unit usage).
  • Inserts a billing::Bill record with status NEW.
  • Updates each participating CDR's event_status to BILLED to prevent double billing.
  • Output: Returns 0 on comprehensive success or -1 if 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 Rater or 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:

  1. Protobuf Ownership: When an entity like billing::Subscriber is fetched via DB_layer::select, it is passed by reference.
  2. Handlers & Pointers: When Charge::fromCCR generates a new charge instance from a network event, it utilizes std::make_unique to 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 NEW bills. It then pushes individual PDF generation tasks into the global THREAD_POOL, guaranteeing the main reactor loop is never blocked by layout rendering.
  • Context Injection: Extracts relevant historical limits based on prepaid or postpaid subscriber types. Note that data mapping strictly pre-formats values in C++ before handing them to the simplistic Inja template engine (e.g., using Utils::month(bill.billed_month()) + " " + std::to_string(bill.billed_year()) to generate "March 2026", and Utils::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) or Utils::BILL_STATUS_UNPAID (postpaid), and triggers a CRM Notification.