Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Flat-Rate Policies #131

Open
lummish opened this issue Feb 12, 2024 · 7 comments
Open

Support for Flat-Rate Policies #131

lummish opened this issue Feb 12, 2024 · 7 comments

Comments

@lummish
Copy link
Collaborator

lummish commented Feb 12, 2024

Is your feature request related to a problem? Please describe.

Certain cities have requested the implementation of a flat-rate fee structure for parking, for example:

For sessions lasting between 0 and 15 minutes, no charge will be assessed.
For sessions lasting between 15 and 30 minutes, a charge of $1.50 will be assessed.
For sessions lasting between 30 and 60 minutes, a charge of $3.00 will be assessed.

A few examples of this policy at work:

A vehicle parks for a total of 7 minutes and is charged $0.00 for the session.
A vehicle parks for a total of 16 minutes and is charged $1.50 for the session.
A vehicle parks for a total of 28 minutes and is charged $1.50 for the session.
A vehicle parks for a total of 40 minutes and is charged $3.00 for the session.

At present, the specification for policy definition does not allow for expression of this kind of policy; the current implementation depends on policies and their corresponding rates being assessed in an additive fashion, rather than as a flat fee for different session durations.

Describe the solution you'd like

We propose the addition of a single new property to the Rule type called rate_application_type.

The new property would assume one of two values: additive or flat, where the additive value would correspond to the current definition and behavior of a Rule, and the flat value would correspond to the proposed new behavior.

This property would be optional, and when not specified would designate the current behavior (i.e. additive).

When the rate_application_type for a given rule is defined as additive, the current behavior will persist. That is to say, the elements in the rate array for the given rule will apply additively.

When the rate_application_type for a given rule is defined as flat, only one element of the rate array of the rule will apply to a session.

As an illustrative example, let's use the policy described above. We could define a rule to represent this policy like so:

{
  "activity": "parking",
  "max_stay": 60,
  "max_stay_unit": "minute",
  "rate_application_type": "flat",
  "rate": [
    {
      "rate": 0,
      "rate_unit": "minute",
      "increment_duration": "minute",
      "start_duration": 0,
      "end_duration": 15
    },
    {
      "rate": 150,
      "rate_unit": "minute",
      "increment_duration": "minute",
      "start_duration": 15,
      "end_duration": 30
    },
    {
      "rate": 300,
      "rate_unit": "minute",
      "increment_duration": "minute",
      "start_duration": 30,
      "end_duration": 60
    }
  ]
}

Is this a breaking change

A breaking change would require consumers or implementors of an API to modify their code for it to continue to function (ex: renaming of a required field or the change in data type of an existing field). A non-breaking change would allow existing code to continue to function (ex: addition of an optional field or the creation of a new optional endpoint).

  • No, not breaking

Impacted Spec

For which spec is this feature being requested?

  • `Curbs``

Describe alternatives you've considered

We explored implementing the desired feature by modifying the Policy and Rate types, however it seems that the most sensible place to make the change is on the Rule type.

cc: @michael-automotus

@jiffyclub
Copy link

I think this may technically be possible in the current rate structure by setting the increment_duration value (which is an integer, not a string) to the same duration as the payment period, e.g. like this:

{
  "activity": "parking",
  "max_stay": 60,
  "max_stay_unit": "minute",
  "rate": [
    {
      "rate": 0,
      "rate_unit": "minute",
      "increment_duration": 15,
      "start_duration": 0,
      "end_duration": 15
    },
    {
      "rate": 150,
      "rate_unit": "minute",
      "increment_duration": 15,
      "start_duration": 15,
      "end_duration": 30
    },
    {
      "rate": 150,
      "rate_unit": "minute",
      "increment_duration": 30,
      "start_duration": 30,
      "end_duration": 60
    }
  ]
}

I think that rate structure would make it so the vehicle was charged $1.50 for being parked during the 15-30 durations and then another $1.50 for being parked during the 30-60 durations for a total of $3.00.

@lummish
Copy link
Collaborator Author

lummish commented May 10, 2024

@jiffyclub I'm not sure this is entirely correct, at least not based upon how I'm reading the specification.

Let's look at the second rate in your definition in particular. My interpretation of this would be that the price applied here would be $1.50 per minute, where the driver would be billed a flat rate of $22.50 for parking in the zone for a duration of between 15 and 30 minutes.

I think we would need to divide the rate by 15 to achieve our initially proposed policy, so the policy would look like this:

{
  "activity": "parking",
  "max_stay": 60,
  "max_stay_unit": "minute",
  "rate": [
    {
      "rate": 0,
      "rate_unit": "minute",
      "increment_duration": 15,
      "start_duration": 0,
      "end_duration": 15
    },
    {
      "rate": 10,
      "rate_unit": "minute",
      "increment_duration": 15,
      "start_duration": 15,
      "end_duration": 30
    },
    {
      "rate": 10,
      "rate_unit": "minute",
      "increment_duration": 30,
      "start_duration": 30,
      "end_duration": 60
    }
  ]
}

I believe you are correct that our desired policy representation can be achieved with the policy specification as is, assuming of course that the intended flat rate divides evenly into the number of rate_units in the increment_duration (which may not always be the case.) Additionally, I would contend that this representation is rather confusing to read/define in JSON form, and would also be somewhat difficult to render in a GUI in such a way that a driver could easily comprehend the rate structure.

Perhaps there is a non-breaking modification we could introduce (either to the Rate object or to the Rule object) that would allow us to specify that a rate (or all rates attached to a rule) is defined based on the price per increment rather the price per rate unit, which would make the policy easier to comprehend and would additionally remove the constraint requiring that the total rate paid for a particular duration is evenly divisible by the increment_duration.

I'm a bit torn as to where this modification would belong. An argument could be made either for the Rate object or the Rule object:

  • Rate: Allows additional flexibility in terms of how a policy is defined, i.e. certain duration periods could be billed on a per-rate-unit basis while others are defined on a per-increment basis. I haven't seen such a use case come across my desk yet, but that doesn't preclude the possibility that one could present itself in the future.
  • Rule: Does not provide the same degree of flexibility in defining the pricing unit basis as introducing a change to the Rate object; this is perhaps actually an advantage. Putting myself in the driver's seat, I can imagine getting pretty frustrated trying to figure out how much I will owe for parking if I am sometimes billed by the minute, and sometimes billed in larger increments depending on how long I park in a zone.

@lummish
Copy link
Collaborator Author

lummish commented May 14, 2024

One possibility would be adding a new property to the Rate object called rate_basis, which could assume one of two values: "unit" or "increment".

If the new property is not defined, the default value is assumed to be "unit" as that would preserve the current behavior.

If the new property is defined, the following conditions apply:

  • If rate_basis is "unit", then the rate shall be interpreted to be defined in terms of price per rate_unit (e.g. price per minute).
  • If rate_basis is "increment", then the rate shall be interpreted to be defined in terms of price per increment_duration.

For example, let's examine the following rate:

    {
      "rate": 150,
      "rate_unit": "minute",
      "increment_duration": 15,
      "start_duration": 15,
      "end_duration": 30
    }

In this case, the rate is defined as 150 per minute, with a minimum payment of $22.50 (i.e. 15 minutes * $1.50 / minute).

If we were to instead express this rate like so:

    {
      "rate": 150,
      "rate_unit": "minute",
      "rate_basis": "increment",
      "increment_duration": 15,
      "start_duration": 15,
      "end_duration": 30
    }

Then the rate is defined as 150 per 15 minutes. So the minimum payment is simply $1.50.

@kas2020-commits
Copy link

kas2020-commits commented May 17, 2024

I think I agree with @lummish here in that flat-rate policies are discontinuous step function-based rate policies whereas the current notion of rates assumes a linear model that scales with time. In this way the rate field is actually the gradient, so for a flat-rate model you would want to set rate to 0. However this is obviously not going to work because we don't have a way to set the second parameter of the linear model to a non-zero value.

If we were to support flat-rate policies I think the simplest solution would be to add an optional, default-initialized field called rate_intercept or similar where the default would be 0. This would make it backwards-compatible with the current schema and also allow flat-rates. In this solution the rate policy for OP would be:

{
  "activity": "parking",
  "max_stay": 60,
  "max_stay_unit": "minute",
  "rate": [
    {
      "rate": 0,
      "rate_unit": "minute",
      "start_duration": 0,
      "end_duration": 15
    },
    {
      "rate": 0,
      "rate_intercept": 150,
      "rate_unit": "minute",
      "start_duration": 15,
      "end_duration": 30
    },
    {
      "rate": 0,
      "rate_intercept": 150,
      "rate_unit": "minute",
      "start_duration": 30,
      "end_duration": 60
    }
  ]
}

So the vehicle will be charged nothing for minutes 0-15, $1.50 for any active minutes 15-30 and $1.50 for any active minutes between 30-60. Since the rates apply on individual chunks of the duration itself, they are additive and hence relative to each other, so rate_intercept would be the change in the rate intercept relative to the prior rate.

Note that from the definitions of start_duration and end_duration the 2nd rate stops applying at the 30th minute, and so you do not need to define rate_intercept relative to 2nd rate because the 3rd rate will be the only active rate from 30 to 60 minutes.

EDIT: Fixed incorrect explination, oops.

@schnuerle
Copy link
Member

Would like to hear back from @jiffyclub to see if he still thinks this is possible in the current spec.

@LaurentG-AMD
Copy link

LaurentG-AMD commented May 23, 2024

I think @jiffyclub's interpretation is correct.

  • increment_duration sets X
  • "rate_unit: minutes" tells us X is in minutes, not that we charge by the minute.

So if increment_duration is set as the payment period, we charge the full period as soon as the even hits that period. So for an event duration of 28 minutes in the above exemple, we would charge the full first period since 28 > 15, for a subtotal of 0$, and the full second period since 15 < 28 < 30, for a subtotal of 1,50$, for a grand total of 1,50$.

The thing that is probably missing is some langages/exemples to document this.

@jiffyclub
Copy link

I think either of these are possible depending on how you interpret the spec, hinging on whether we interpret the rate to be per rate_unit or per increment_duration. Looking closer at the spec it does say "per rate_unit". There is also an increment_amount field that I am not at all sure what it is for, and the description is not helping, but could potentially be useful here, @schnuerle do you remember?

A flag like rate_basis would definitely make things entirely explicit. I would prefer it on the rate, not the rule, so that rates are self-contained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants