3

Azure Rate Card API returns a MeterRates field (see documentation). Azure UsageAggregate gives a quantity (see documentation).

According to the azure support page. This is the forum to ask questions.

So, how are meter rates applied?

Example meter rates:

{"0":20, "100":15, "200":10}

If I have a quantity of 175 is the amount 100*20 + 75*15 or 175*15?

Why specify an included quantity?

Example: rates:{"0":23} with included quantitiy 10 could be expressed as rates:

{"0":0, "10":23}
Remi Guan
  • 21,506
  • 17
  • 64
  • 87
Steve Brownell
  • 1,310
  • 1
  • 12
  • 18
  • 1
    Billing questions are considered off topic here, you might do better asking on Serverfault. – Michael B Jan 20 '16 at 19:42
  • 1
    what? this is a tech question about how to do the calculation with the info received from the Usage and Rate Card api's. – Steve Brownell Jan 20 '16 at 20:04
  • @SteveBrownell this isn't a programming question. Actually I'm not sure what your actual question is. What meters are you talking about? Right now there's no context around it. – David Makogon Jan 20 '16 at 20:23
  • fair enough. I've added some references to give the question some clarity. – Steve Brownell Jan 20 '16 at 20:44
  • 1
    +1. Not sure why this question is downvoted and marked for closure. It is about consumption of Billing API which IMHO is a perfectly valid programming question. – Gaurav Mantri Jan 21 '16 at 02:41

3 Answers3

5

example meter rates: {"0":20, "100":15, "200":10}

if I have a quantity of 175 is the amount 100*20 + 75*15 or 175*15 ?

The pricing is tiered pricing. So when you get the rates it essentially tells you that:

  • from 0 - 99 units, the unit rate is 20
  • from 100 - 199 units, the unit rate is 15
  • from 200 units and above, the unit rate is 10

Based on this logic, your calculation should be:

99 * 20 + 75 * 15 = 3105

One thing which confuses me though is the upper limit. Above calculation is based on the information I received from Azure Billing team. What confused me is what would happen if the consumption is say 99.5 units? For the first 99 units it is fine but I am not sure how the additional 0.5 units will be calculated.

Brendan Green
  • 11,676
  • 5
  • 44
  • 76
Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • Thanks for the answer. I am wondering if it shouldn't be [ 99 * 20 + 76 * 15 = 3105 ] or [ 100 * 20 + 75 * 15 = 3105 ] – Kashif Nazar Aug 18 '16 at 06:12
2

Guarav gets at the core of the issue and is why I marked it as the answer. Based on that I devised the following code to implement the logic. It falls in two parts:

  1. Creating a bucket list from the meter rates
  2. Processing a quantity with the bucket list to determine an amount

The following function creates a list of buckets (each bucket object is a simple POCO with Min, Max and Rate properties). The list is attached to a meter object that has the other properties from the rate card api.

        private Dictionary<int, RateBucket> ParseRateBuckets(string rates)
    {
        dynamic dRates = JsonConvert.DeserializeObject(rates);
        var rateContainer = (JContainer)dRates;

        var buckets = new Dictionary<int, RateBucket>();
        var bucketNumber = 0;
        foreach (var jToken in rateContainer.Children())
        {
            var jProperty = jToken as JProperty;
            if (jProperty != null)
            {
                var bucket = new RateBucket
                {
                    Min = Convert.ToDouble(jProperty.Name),
                    Rate = Convert.ToDouble(jProperty.Value.ToString())
                };

                if (bucketNumber > 0)
                    buckets[bucketNumber - 1].Max = bucket.Min;

                buckets.Add(bucketNumber, bucket);
            }

            bucketNumber++;
        }

        return buckets;
    }

The second function uses the meter object with two useful properties: the bucket list, and the included quantity. According to the rate card documentation (as I read it) you don't start counting billable quantity until after you surpass the included quantity. I'm sure there's some refactoring that could be done here, but the ordered processing of the buckets is the key point.

I think I've addressed issue on the quantity by recognizing that it's a double and not an integer. Therefore the quantity associated with any single bucket is the difference between the bucket max and the bucket min (unless we've only filled a partial bucket).

        private double CalculateUsageCost(RateCardMeter meter, double quantity)
    {
        var amount = 0.0;

        quantity -= meter.IncludedQuantity;

        if (quantity > 0)
        {
            for (var i = 0; i < meter.RateBuckets.Count; i++)
            {
                var bucket = meter.RateBuckets[i];
                if (quantity > bucket.Min)
                {
                    if (bucket.Max.HasValue && quantity > bucket.Max)
                        amount += (bucket.Max.Value - bucket.Min)*bucket.Rate;
                    else
                        amount += (quantity - bucket.Min)*bucket.Rate;
                }
            }
        }
        return amount;
    }

Finally, the documentation is unclear about the time scope for the tiers. If I get a discounted price based on quantity, over what time scope do I aggregate quantity? The usage api allows me to pull data either daily or hourly. I want to pull my data hourly so I can correlate my costs by time of day. But when is it appropriate to actually calculate the bill? Seems like hourly is wrong, daily may work, but it might only be appropriate over the entire month.

Steve Brownell
  • 1,310
  • 1
  • 12
  • 18
  • The answer to time scope deals with the billing cycle. I'm trying to get a monetary amount to assign to a particular piece of usage. Therefore the quantity for a particular piece of usage is incremental from the previous pieces. So, according to the initial question let's assume that I'm trying to calculate a piece of usage with a quantity of 8.0, and that the sum of all prior usage in the billing cycle is 97.0. (Remember that the quantities are doubles not integers.) – Steve Brownell Jan 21 '16 at 13:59
  • my quantity is 97.0 + 8.0, so I need to figure out that I need 100.0 - 97.0 from the first bucket and 105.0 - 100.0 from the second bucket. or (100.0 - 97.0) * 20 + (105.0 - 100.0) * 15 = 135.00 – Steve Brownell Jan 21 '16 at 13:59
0

Recently I just did this similar task. Following is my example (I think you can use regex to remove those characters rather than like me using replace). The first function parse the rate info string to generate a key-value pair collection, and the second function is used to calculate the total price.

private Dictionary<float, double> GetRatesDetail(string r)
{
    Dictionary<float, double> pairs = null;
    if(string.IsNullOrEmpty(r) || r.Length <=2)
    {
        pairs = new Dictionary<float, double>();
        pairs.Add(0, 0);
    }
    else
    {
        pairs = r.Replace("{", "").Replace("}", "").Split(',')
        .Select(value => value.Split(':'))
        .ToDictionary(pair => float.Parse(pair[0].Replace("\"", "")), pair => double.Parse(pair[1]));
    }

    return pairs;
}

public decimal Process(Dictionary<float, double> rates, decimal quantity)
{
    double ret = 0;

    foreach (int key in rates.Keys.OrderByDescending(k => k))
    {
        if (quantity >= key)
        {
            ret += ((double)quantity - key) * rates[key];
            quantity = key;
        }
    }

    return (decimal)ret;
}
Nick Lu
  • 21
  • 3