Module ocs_rating
This library module implements utility functions
for handling rating in the ocs application.
Copyright © 2016 - 2026 SigScale Global Inc.
This library module implements utility functions
for handling rating in the ocs application.
aka_cred() = #aka_cred{k = binary(), opc = binary(), dif = integer()}
bucket() = #bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}
bucket_attributes() = #{bucket_type := normal | session, from_bucket => [bucket_source()], reservations => reservations()}
bucket_source() = #{id := string(), amount := pos_integer(), unit_size := pos_integer(), unit_price := pos_integer(), expire := pos_integer() | undefined}
bucket_status() = active | expired | suspended
pla_ref() = #pla_ref{id = string() | undefined | '_', href = string() | undefined | '_', name = string() | undefined | '_', class_type = string() | undefined | '_', base_type = string() | undefined | '_', schema = string() | undefined | '_', ref_type = string() | undefined | '_'}
product_price_type() = recurring | one_time | usage | tariff | pla_ref()
quantity() = #quantity{amount = integer(), units = atom() | string()}
range() = #range{lower = quantity(), upper = quantity()}
rate() = #rate{numerator = quantity(), denominator = quantity()}
recur_period() = hourly | daily | weekly | monthly | yearly
reservation() = #{ts := pos_integer(), debit := non_neg_integer(), reserve := non_neg_integer(), service_id => non_neg_integer(), charging_key => non_neg_integer()}
reservations() = #{SesssionID::list() => reservation()}
service_status() = feasibilityChecked | designed | reserved | active | inactive | terminated
rate(Protocol, ServiceType, ServiceId, ChargingKey, ServiceNetwork, SubscriberIDs, Timestamp, Address, Direction, Flag, DebitAmounts, ReserveAmounts, SessionAttributes) -> Result
- Protocol = radius | diameter | nrf
- ServiceType = 1 | 2 | 12 | 32251 | 32255 | 32260 | 32276 | 32275 | 32274 | binary()
- ServiceId = integer() | undefined
- ChargingKey = integer() | undefined
- ServiceNetwork = string() | binary() | undefined
- SubscriberIDs = [SubscriberID]
- SubscriberID = string() | binary()
- Timestamp = calendar:datetime()
- Address = string() | binary() | undefined
- Direction = answer | originate | undefined
- Flag = initial | interim | final | event
- DebitAmounts = [{UnitType, Amount}]
- ReserveAmounts = [{UnitType, Amount}] | undefined
- UnitType = octets | seconds | messages
- Amount = integer()
- SessionAttributes = [tuple()]
- Result = {ok, Service, GrantedAmount} | {ok, Service, Rated} | {ok, Service, GrantedAmount, Rated} | {out_of_credit, RedirectServerAddress, SessionList} | {out_of_credit, RedirectServerAddress, SessionList, Rated} | {disabled, SessionList} | {ok, {pla_ref, Price}} | {error, Reason}
- Price = #price{name = string() | undefined, description = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, type = product_price_type() | undefined, period = recur_period() | undefined, units = cents | octets | seconds | messages | undefined, size = integer() | undefined, amount = integer() | undefined, currency = string() | undefined, char_value_use = [#char_value_use{name = string() | undefined, description = string() | undefined, type = string() | undefined, min = non_neg_integer() | undefined, max = pos_integer() | undefined, specification = '_' | string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, values = [#char_value{default = boolean() | undefined, units = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, value = quantity() | range() | rate() | term() | undefined, from = term() | undefined, to = term() | undefined, type = string() | undefined, interval = open | closed | closed_bottom | closed_top | undefined, regex = {CompiledRegEx::re:mp(), OriginalRegEx::string()} | undefined}]}], alteration = #alteration{name = string() | undefined, description = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, type = product_price_type() | undefined, period = recur_period() | undefined, units = cents | octets | seconds | messages | undefined, size = integer() | undefined, amount = integer() | undefined, currency = string() | undefined} | undefined}
- Service = #service{name = binary() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', state = service_status() | undefined | '_', password = binary() | aka_cred() | undefined | '_', attributes = [tuple()] | undefined | '_', product = (ProductRef::string() | undefined | '_'), enabled = boolean() | '_', disconnect = boolean() | '_', session_attributes = [{TS::pos_integer(), Attributes::[tuple()]}] | '_', characteristics = [{Name::string(), Value::term()}] | '_', multisession = boolean() | '_', last_modified = tuple() | undefined | '_'}
- GrantedAmount = {UnitType, Amount}
- Rated = [#rated{bucket_value = non_neg_integer() | undefined | '_', bucket_type = cents | octets | seconds | messages | undefined | '_', currency = string() | undefined | '_', is_billed = boolean() | '_', is_tax_exempt = boolean() | undefined | '_', tariff_type = atom() | undefined | '_', product = term() | undefined | '_', price_name = string() | undefined | '_', price_type = tariff | usage | event | undefined | '_', description = string() | undefined | '_', tax_excluded_amount = non_neg_integer() | undefined | '_', tax_included_amount = non_neg_integer() | undefined | '_', tax_rate = integer() | undefined | '_', usage_rating_tag = usage | included | non_included | undefined | '_'}]
- SessionList = [{TS::pos_integer(), SessionAttributes}]
- RedirectServerAddress = string() | undefined
- Reason = offer_not_found | product_not_found | service_not_found | invalid_service_type | invalid_bundle_product | term()
Handle rating and balance management for used and reserved unit amounts.
Subscriber balance buckets are permanently reduced by the
amount in DebitAmounts and bucket reservations are made
of the amounts in ReserveAmounts. The subscribed
Product Offering provides one or more
Product Offering Price (POP) used to rate the
service usage.
Centralized Unit Determination
If ReserveAmounts is an empty list, and Flag is initial,
interim or event, the UnitType and Amount are determined
by applicable POP.
Decentralized Unit Determination
If ReserveAmounts is a non empty list, and Flag is initial,
interim or event, the list provides one or more alternate
measures of service with different UnitType allowing the
applicable POP to determine which UnitType is used, with the
Amount explicitly provided.
Immediate Event Charging (IEC)
If Flag is event then DebitAmounts should be an empty list
and centralized, or decentralized, unit determination on
ReserveAmounts provides a UnitType and Amount to be
immediately debited (without reservation).
If ReserveAmounts is undefined no reservation is performed.
The value of DebitAmounts is a list of one or more alternate
measures of the service usage with different UnitType
(i.e. octets and seconds) allowing the applied POP to
determine the UnitType used for rating.
If successful returns {ok, Service, GrantedAmount} for initial
and interim updates, {ok, Service, Rated} for final or
{ok, Service, GrantedAmount, Rated} for event.
If subscriber's balance is insufficient to cover the DebitAmounts
and ReserveAmounts returns
{out_of_credit, RedirectServerAddress, SessionList}
for initial or interim and
{out_of_credit, RedirectServerAddress, SessionList, Rated}
for final.
Returns {disabled, SessionList} if the subscriber is not enabled.
The value of SessionList describes the known active sessions
which should be disconnected.
A successful call with Flag value of initial starts a session
which must eventually be released by another call with exactly
the same SessionAttributes and a Flag value of final.
charge(Protocol, Flag, SubscriberIDs, ServiceId, ChargingKey, ServiceNetwork, Address, Prices, DebitAmounts, ReserveAmounts, SessionAttributes) -> Result
- Protocol = radius | diameter | nrf
- Flag = initial | interim | final | event
- SubscriberIDs = [SubscriberID]
- SubscriberID = string() | binary()
- ServiceId = integer() | undefined
- ChargingKey = integer() | undefined
- ServiceNetwork = [48..57]
- Address = [48..57]
- Prices = [#price{name = string() | undefined, description = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, type = product_price_type() | undefined, period = recur_period() | undefined, units = cents | octets | seconds | messages | undefined, size = integer() | undefined, amount = integer() | undefined, currency = string() | undefined, char_value_use = [#char_value_use{name = string() | undefined, description = string() | undefined, type = string() | undefined, min = non_neg_integer() | undefined, max = pos_integer() | undefined, specification = '_' | string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, values = [#char_value{default = boolean() | undefined, units = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, value = quantity() | range() | rate() | term() | undefined, from = term() | undefined, to = term() | undefined, type = string() | undefined, interval = open | closed | closed_bottom | closed_top | undefined, regex = {CompiledRegEx::re:mp(), OriginalRegEx::string()} | undefined}]}], alteration = #alteration{name = string() | undefined, description = string() | undefined, start_date = pos_integer() | undefined, end_date = pos_integer() | undefined, type = product_price_type() | undefined, period = recur_period() | undefined, units = cents | octets | seconds | messages | undefined, size = integer() | undefined, amount = integer() | undefined, currency = string() | undefined} | undefined}]
- DebitAmounts = [{UnitType, Amount}]
- ReserveAmounts = [{UnitType, Amount}] | undefined
- UnitType = octets | seconds | messages
- Amount = integer()
- SessionAttributes = list()
- Result = {ok, Service, GrantedAmount} | {ok, Service, Rated} | {ok, Service, GrantedAmount, Rated} | {out_of_credit, RedirectServerAddress, SessionList} | {out_of_credit, RedirectServerAddress, SessionList, Rated} | {disabled, SessionList} | {error, Reason}
- Service = #service{name = binary() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', state = service_status() | undefined | '_', password = binary() | aka_cred() | undefined | '_', attributes = [tuple()] | undefined | '_', product = (ProductRef::string() | undefined | '_'), enabled = boolean() | '_', disconnect = boolean() | '_', session_attributes = [{TS::pos_integer(), Attributes::[tuple()]}] | '_', characteristics = [{Name::string(), Value::term()}] | '_', multisession = boolean() | '_', last_modified = tuple() | undefined | '_'}
- GrantedAmount = {UnitType, Amount}
- Amount = integer()
- Rated = [#rated{bucket_value = non_neg_integer() | undefined | '_', bucket_type = cents | octets | seconds | messages | undefined | '_', currency = string() | undefined | '_', is_billed = boolean() | '_', is_tax_exempt = boolean() | undefined | '_', tariff_type = atom() | undefined | '_', product = term() | undefined | '_', price_name = string() | undefined | '_', price_type = tariff | usage | event | undefined | '_', description = string() | undefined | '_', tax_excluded_amount = non_neg_integer() | undefined | '_', tax_included_amount = non_neg_integer() | undefined | '_', tax_rate = integer() | undefined | '_', usage_rating_tag = usage | included | non_included | undefined | '_'}]
- SessionList = [{pos_integer(), [tuple()]}]
- RedirectServerAddress = string() | undefined
- Reason = term()
Handle balance management for used and reserved unit amounts.
authorize(Protocol, ServiceType, SubscriberIDs, Password, Timestamp, Address, Direction, SessionAttributes) -> Result
- Protocol = radius | diameter | nrf
- ServiceType = 1 | 2 | 12 | 32251 | 32255 | 32260 | 32276 | 32274 | 32275 | binary() | undefined
- SubscriberIDs = [SubscriberID]
- SubscriberID = binary() | string()
- Password = binary() | string() | undefined | {ChapId::0..255, ChapPassword::binary(), Challenge::binary()}
- Timestamp = calendar:datetime()
- Address = string() | undefined
- Direction = answer | originate | undefined
- SessionAttributes = [tuple()]
- Result = {authorized, Subscriber, Attributes, SessionList} | {unauthorized, Reason, SessionList}
- Subscriber = #service{name = binary() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', state = service_status() | undefined | '_', password = binary() | aka_cred() | undefined | '_', attributes = [tuple()] | undefined | '_', product = (ProductRef::string() | undefined | '_'), enabled = boolean() | '_', disconnect = boolean() | '_', session_attributes = [{TS::pos_integer(), Attributes::[tuple()]}] | '_', characteristics = [{Name::string(), Value::term()}] | '_', multisession = boolean() | '_', last_modified = tuple() | undefined | '_'}
- Attributes = [tuple()]
- SessionList = [tuple()]
- Reason = disabled | bad_password | service_not_found | out_of_credit | offer_not_found | price_not_found | table_lookup_failed
Authorize access request.
If authorized returns attributes to be included in Access-Accept response.
When subscriber's product instance includes the radiusReserveSessionTime
characteristic a reservation is attempted for the given value of seconds.
A Session-Timeout attribute will be included with the actual reservation.
session_attributes(Attributes) -> SessionAttributes
Extract RADIUS session related attributes.
update_session(Type, Charge, Reserve, ServiceId, ChargingKey, SessionId, Buckets) -> Result
- Type = octets | seconds | cents | messages
- Charge = non_neg_integer()
- Reserve = non_neg_integer()
- ServiceId = non_neg_integer() | undefined
- ChargingKey = non_neg_integer() | undefined
- SessionId = [tuple()]
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- Result = {Charged, Reserved, NewBuckets}
- Charged = non_neg_integer()
- Reserved = non_neg_integer()
- NewBuckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
Perform debit and reservation for a session.
Finds reservations matching SessionId, ChargingKey
and ServiceId.
Empty or expired buckets are removed when no session
reservations remain.
Returns {Charged, Reserved, NewBuckets} where
Charged is the total amount debited from bucket(s),
Reserved is the total amount of quota reservation,
and NewBuckets is the updated bucket list.
The Reserve amount is not additive, the Reserved
amount is the new total reservation.
3GPP RS 32.299 6.3.8 Support of re-authorization:
"New quota allocations [...] override any remaining held quota"
charge_session(Type, Charge, ServiceId, ChargingKey, SessionId, Buckets) -> Result
- Type = octets | seconds | cents | messages
- Charge = non_neg_integer()
- ServiceId = non_neg_integer() | undefined
- ChargingKey = non_neg_integer() | undefined
- SessionId = string() | binary()
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- Result = {Charged, NewBuckets}
- Charged = non_neg_integer()
- NewBuckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
Peform final charging for a session.
Returns {Charged, NewBuckets} where
Charged is the total amount debited from the buckets
and NewBuckets is the updated bucket list.
charge_event(Type, Charge, Buckets) -> Result
- Type = octets | seconds | cents | messages
- Charge = non_neg_integer()
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- Result = {Charged, NewBuckets}
- Charged = non_neg_integer()
- NewBuckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
Peform immediate event charging (IEC).
Returns {Charged, NewBuckets} where
Charged is the total amount debited from the buckets
and NewBuckets is the updated bucket list.
convert(ProductId, Price, Type, UnitPrice, UnitSize, TotalSize, ServiceId, ChargingKey, SessionId, Buckets) -> Result
- ProductId = string()
- Price = pos_integer()
- Type = octets | seconds | messages
- ServiceId = non_neg_integer() | undefined
- ChargingKey = non_neg_integer() | undefined
- UnitPrice = pos_integer()
- UnitSize = pos_integer()
- TotalSize = pos_integer()
- SessionId = string() | binary()
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- Result = {ok, Buckets} | false
Convert cents to Type bucket(s) of Size.
Tops up existing bucket for session if found,
otherwise creates session bucket(s) of Type.
remove_session(SessionId, SessionList) -> NewSessionList
- SessionId = [tuple()]
- SessionList = [{pos_integer(), [tuple()]}]
- NewSessionList = [{pos_integer(), [tuple()]}]
Remove session identification attributes set from active sessions list.
add_session(SessionAttributes, SessionList) -> SessionList
- SessionAttributes = [tuple()]
- SessionList = [{pos_integer(), SessionAttributes}]
Add session identification attributes set to active sessions list.
get_session_id(SessionAttributes) -> SessionId
- SessionAttributes = [tuple()]
- SessionId = [tuple()]
Get the session identifier value.
Returns a list of Nrf/DIAMETER/RADIUS attributes.
split_by_price(Buckets) -> Result
Split out buckets with price name.
sort_by_age(Buckets) -> Buckets
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
Sort Buckets with soonest to expire first.
sort_from_bucket(FromBucket) -> FromBucket
Sort BucketSource with soonest to expire first.
price_units(Amount, UnitSize, UnitPrice) -> {TotalUnits, TotalPrice}
- Amount = non_neg_integer()
- UnitSize = pos_integer()
- UnitPrice = pos_integer()
- TotalUnits = pos_integer()
- TotalPrice = pos_integer()
Calculate total size and price.
refund(ServiceId, ChargingKey, SessionId, Buckets) -> Buckets
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- ServiceId = non_neg_integer() | undefined
- ChargingKey = non_neg_integer() | undefined
- SessionId = [tuple()]
Refund unused reservations.
get_final(ServiceId, ChargingKey, SessionId, Refund, Buckets) -> Result
- ServiceId = non_neg_integer() | undefined
- ChargingKey = non_neg_integer() | undefined
- SessionId = [tuple()]
- Refund = {RefundUnits, RefundAmount}
- RefundUnits = octets | seconds | messages
- RefundAmount = non_neg_integer()
- Buckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
- Result = {Debits, NewBuckets}
- Debits = #{}
- NewBuckets = [#bucket{id = string() | undefined | '_' | '$1', name = string() | undefined | '_', start_date = pos_integer() | undefined | '_', end_date = pos_integer() | undefined | '_', status = bucket_status() | undefined | '_', remain_amount = integer() | '_', attributes = bucket_attributes() | '_', units = octets | cents | seconds | messages | undefined | '_', price = string() | '_', product = [ProdRef::term()] | '_', last_modified = tuple() | undefined | '_'}]
Get total debited and remaining amounts, refund and
remove all reservations, for session.
send_notifications(T) -> any()
notify_accumulated_balance(AccBlance) -> any()
accumulated_balance(Buckets, ProdRef) -> any()
accumulated_balance(Buckets, ProdRef, X3) -> any()
build_acc(Buckets, Name, Units, ProdRef, X5, AccBalance) -> any()
Generated by EDoc