[v0.10.4] *generic or *sms balance deductions

85 views
Skip to first unread message

Marcin Kowalczyk

unread,
Dec 30, 2025, 9:47:25 AM12/30/25
to CGRateS
Hi,

 I want to create following scenario:

User can get bundle of lets say 100 text messages. Now when he sends message to PL_Fixed he should be deducted  3 units from bundle, PL_Mobile 2 credits, US_Fixed or Mobile 1 credit. 
When his bundle is over he should fallback to monetary wit regular rates - ie 0,1 for PL_Mobile, and 0.005 for US

But seems as long as *generic or *data balance should be used cgrates ommits ratingplan and dedcuts only value from Usage field.
I did following test - user no generic od data balances matching category:

{"method":"CDRsV2.ProcessExternalCDR","params":[{"OriginID":"bund_test_1","ToR":"*generic","RequestType":"*pseudoprepaid","SetupTime":"1867088103767","AnswerTime":"1867088103767","Tenant":"cgrates.org","Account":"test","Subject":"DEFAULT","Destination":"48517204852312312413431411","Usage":"3","Category":"bunde-test","ExtraFields":{},"OriginHost":"localhost","Source":"localhost"}]}


in ngrep I see "Responder.Debit" "MatchedSubject" goes to  *out:cgrates.org:bunde-test:*any like expected, rating plan is selected 


{"id":13,"result":{"Category":"bunde-test","Tenant":"cgrates.org","Subject":"DEFAULT","Account":"test","Destination":"48517204852312312413431411","ToR":"*generic","Cost":6,"Timespans":[{"TimeStart":"2029-03-01T19:35:03.767Z","TimeEnd":"2029-03-01T19:35:03.767000001Z","Cost":2,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"RoundingMethod":"*up","RoundingDecimals":4,"MaxCost":0,"MaxCostStrategy":"","Rates":[{"GroupIntervalStart":0,"Value":2,"RateIncrement":1,"RateUnit":1}]},"Weight":50},"DurationIndex":1,"Increments":[{"Duration":1,"Cost":2,"BalanceInfo":{"Unit":null,"Monetary":{"UUID":"07f8607c-4244-4838-8349-db0dcba8e837","ID":"*default","Value":1,"RateInterval":null},"AccountID":"cgrates.org:test"},"CompressFactor":1}],"RoundIncrement":null,"MatchedSubject":"*out:cgrates.org:bunde-test:*any","MatchedPrefix":"4851","MatchedDestId":"LP_PL_mobile","RatingPlanId":"RP_2025-12-30_123651_LEADS","CompressFactor":1},{"TimeStart":"2029-03-01T19:35:03.767000001Z","TimeEnd":"2029-03-01T19:35:03.767000003Z","Cost":4,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"RoundingMethod":"*up","RoundingDecimals":4,"MaxCost":0,"MaxCostStrategy":"","Rates":[{"GroupIntervalStart":0,"Value":2,"RateIncrement":1,"RateUnit":1}]},"Weight":50},"DurationIndex":3,"Increments":[{"Duration":1,"Cost":2,"BalanceInfo":{"Unit":null,"Monetary":{"UUID":"07f8607c-4244-4838-8349-db0dcba8e837","ID":"*default","Value":-3,"RateInterval":null},"AccountID":"cgrates.org:test"},"CompressFactor":2}],"RoundIncrement":null,"MatchedSubject":"*out:cgrates.org:bunde-test:*any","MatchedPrefix":"4851","MatchedDestId":"LP_PL_mobile","RatingPlanId":"RP_2025-12-30_123651_LEADS","CompressFactor":1}],"RatedUsage":3,"AccountSummary":{"Tenant":"cgrates.org","ID":"test","BalanceSummaries":[{"UUID":"0010aa9f-0f86-4371-8923-b56d06dd436c","ID":"lead1","Type":"*generic","Value":994,"Disabled":false},{"UUID":"07f8607c-4244-4838-8349-db0dcba8e837","ID":"*default","Type":"*monetary","Value":-3,"Disabled":false},{"UUID":"721a30e0-2961-480b-8bdd-eace6eb8bd7f","ID":"lead","Type":"*monetary","Value":994,"Disabled":false}],"AllowNegative":false,"Disabled":false}},"error":null}

now I add balance *generic with high prio:

{
    "method": "APIerSv1.SetBalance",
    "params": [
        {
            "Tenant": "cgrates.org",
            "Account": "test",
            "BalanceType": "*generic",
            "Value": 1000,
            "Balance": {
                "ID": "bunde-test",
                 "Weight": 10000,
                 "Categories": "bunde-test"
            },
            "ActionExtraData": null,
            "Cdrlog": true
        }
    ],
    "id": 6
}

I run almost same CDR (origin changed to avoid duplicate)

{"method":"CDRsV2.ProcessExternalCDR","params":[{"OriginID":"bund_test_balance","ToR":"*generic","RequestType":"*pseudoprepaid","SetupTime":"1867088103767","AnswerTime":"1867088103767","Tenant":"cgrates.org","Account":"test","Subject":"DEFAULT","Destination":"48517204852312312413431411","Usage":"3","Category":"bunde-test","ExtraFields":{},"OriginHost":"localhost","Source":"localhost"}]}

 and in responder goes to "MatchedSubject":"ffd6cf63-247b-4fff-8624-20f8ab46ec7c" and  deducts only 3 units not 6 like expected

T 127.0.0.1:2012 -> 127.0.0.1:42212 [AP] #159
{"id":6,"result":{"Category":"bunde-test","Tenant":"cgrates.org","Subject":"DEFAULT","Account":"test","Destination":"48517204852312312413431411","ToR":"*generic","Cost":0,"Timespans":[{"TimeStart":"2029-03-01T19:35:03.767Z","TimeEnd":"2029-03-01T19:35:03.767000003Z","Cost":0,"RateInterval":{"Timing":null,"Rating":{"ConnectFee":0,"RoundingMethod":"","RoundingDecimals":0,"MaxCost":0,"MaxCostStrategy":"","Rates":[{"GroupIntervalStart":0,"Value":0,"RateIncrement":1,"RateUnit":1}]},"Weight":0},"DurationIndex":0,"Increments":[{"Duration":1,"Cost":0,"BalanceInfo":{"Unit":{"UUID":"ffd6cf63-247b-4fff-8624-20f8ab46ec7c","ID":"bunde-test","Value":997,"DestinationID":"48517204852312312413431411","Consumed":1,"ToR":"*generic","RateInterval":null},"Monetary":null,"AccountID":"cgrates.org:test"},"CompressFactor":3}],"RoundIncrement":null,"MatchedSubject":"ffd6cf63-247b-4fff-8624-20f8ab46ec7c","MatchedPrefix":"48517204852312312413431411","MatchedDestId":"*any","RatingPlanId":"*none","CompressFactor":1}],"RatedUsage":3,"AccountSummary":{"Tenant":"cgrates.org","ID":"test","BalanceSummaries":[{"UUID":"0010aa9f-0f86-4371-8923-b56d06dd436c","ID":"lead1","Type":"*generic","Value":994,"Disabled":false},{"UUID":"ffd6cf63-247b-4fff-8624-20f8ab46ec7c","ID":"bunde-test","Type":"*generic","Value":997,"Disabled":false},{"UUID":"07f8607c-4244-4838-8349-db0dcba8e837","ID":"*default","Type":"*monetary","Value":-9,"Disabled":false},{"UUID":"721a30e0-2961-480b-8bdd-eace6eb8bd7f","ID":"lead","Type":"*monetary","Value":994,"Disabled":false}],"AllowNegative":false,"Disabled":false}},"error":null}


Pricing looks following:

ubuntu@cgrates-staging:~/pricing/cgrates/tiers$ grep -R LP_PL_mobile *
DestinationRates.csv:DR_2025-12-30_123651_LEADS,LP_PL_mobile,RATE_LEADS_T2,*up,4,,
ubuntu@cgrates-staging:~/pricing/cgrates/tiers$ grep RATE_LEADS_T2 Rates.csv
RATE_LEADS_T2,0,2,1,1,0
ubuntu@cgrates-staging:~/pricing/cgrates$ grep "bunde-test" RatingProfiles.csv
cgrates.org,bunde-test,*any,2025-12-30T11:36:51Z,RP_2025-12-30_123651_LEADS,DEFAULT
ubuntu@cgrates-staging:~/pricing/cgrates$



Is there a way to handle such scenario?

 I've tried with 0.10.4 and CGR...@v0.10.5~dev-20251024182112-2873811b14fe and behaviour seems to be the same.

Cheers! 

Armir Veliaj

unread,
Jan 6, 2026, 7:01:14 AMJan 6
to CGRateS

Hi Marcin,

For implementing your case, there are two options you can consider.

First, if in your scenario you plan to use only *sms, we would suggest the use of *sms balance instead of *generic, with factors used as in this example below:


{
  "method": "APIerSv1.SetBalance",
  "params": [
    {
      "Tenant": "cgrates.org",
      "Account": "1001",
      "BalanceType": "*sms",
      "Value": 100,
      "Balance": {
        "Factors": {
          "PL_Fixed": 3,
          "PL_Mobile": 2,
          "US_Fixed": 1
        },
        "ID": "balance_sms"
      },
      "ActionExtraData": {
        "BalanceID": "~*acnt.BalanceID",
        "NewFactors": "~*acnt.Factors"
      },
      "Cdrlog": true
    }
  ],
  "id": 8
}

You can set it using AttributeS, based on destination filters.


The second option would be creating another *monetary balance instead of *sms or *generic on top of your default one, using higher Weight and RatingSubject while defining the rates for "PL_Fixed", "PL_Mobile", "US_Fixed" in a separate rating plan attached to that specific RatingSubject.

Hope it helps!

Thanks,
Armir

Marcin Kowalczyk

unread,
Jan 7, 2026, 7:25:01 AMJan 7
to CGRateS
Hi Amir,

 Thanks for prompt reply. 

For option 1: 
In case I change pricing and Factor for PL_Fixed form 3 to 2 (it's global rate change) than we would need to iterate over all accounts and do change there? Or it can be done other way? 

For option 2:
I was thinking similar way, but than we cannot do fallback to monetary for rate IE 0.5 per SMS if sms balance is depleated. Then always rate would be ie 3 for PL_Fixed ?

Armir Veliaj

unread,
Jan 7, 2026, 10:30:02 AMJan 7
to CGRateS
Hi Marcin,


Please find inline answers to your questions below.



On Wednesday, January 7, 2026 at 1:25:01 PM UTC+1 kow...@gmail.com wrote:
Hi Amir,

 Thanks for prompt reply. 

For option 1: 
In case I change pricing and Factor for PL_Fixed form 3 to 2 (it's global rate change) than we would need to iterate over all accounts and do change there? Or it can be done other way? 
 
Yes, you need to update it on all accounts the moment when you need to change the rating.
Or, you can define all possible values in factor alias and then select the alias via attributes. 

For option 2:
I was thinking similar way, but than we cannot do fallback to monetary for rate IE 0.5 per SMS if sms balance is depleated. Then always rate would be ie 3 for PL_Fixed ?

The moment you start consuming the first balance, the RatingSubject defined in that balance will be used. As soon as it falls back to the default monetary balance, system defaults will be applied.
So the rate will not remain the same, but will change (for example, from 3 to 0.5 as in your case). 

Hope this clarifies.

Thanks,
Armir 

Marcin Kowalczyk

unread,
Jan 7, 2026, 11:07:25 AMJan 7
to CGRateS
Thanks,

I've just tested and seems option 2 does a job for me.  But from system performance perspective it will be better to stick virtual monetary balance for sms units or *sms balance with  Factors?

Regards
Marcin

Armir Veliaj

unread,
Jan 8, 2026, 10:15:29 AMJan 8
to CGRateS
Hi Marcin,

Regarding system performance, it won't cause any noticeable difference in CGRateS under normal conditions. However, due to simplicity and not being as complex as factor balances that need the use of Attributes, choosing the second option of *monetary can be a better choice both for usability and during high loads may have a slight advantage in performance compared to balance factors, so you can stick with using it.

Thanks,
Armir
Reply all
Reply to author
Forward
0 new messages