Our tests use hourly billing periods. This is a mistake, cause hours are always equal. Months are not.
So here's a problem not covered by tests:
public function collectCharges(SubscriptionInterface $subscription, BillingPeriod $billing_period) {
$start_date = $subscription->getStartDate();
$billing_type = $subscription->getBillingSchedule()->getBillingType();
if ($billing_type == BillingScheduleInterface::BILLING_TYPE_PREPAID) {
// The subscription has either ended, or is scheduled for cancellation,
// meaning there's nothing left to prepay.
if ($subscription->getState()->getId() != 'active' ||
$subscription->hasScheduledChange('state', 'canceled')) {
return [];
}
$billing_schedule = $subscription->getBillingSchedule()->getPlugin();
// The initial order (which starts the subscription) pays the first
// billing period, so the base charge is always for the next one.
// The October recurring order (ending on Nov 1st) charges for November.
$base_billing_period = $billing_schedule->generateNextBillingPeriod($start_date, $billing_period);
}
The $base_billing_period is set on the charge.
The $charge->getBillingPeriod() is always prorated against the order billing period.
So November is prorated against October. Which doesn't make any sense whatsoever.
We need to be able to say "don't prorate this charge".
Similar problem:
public function collectTrialCharges(SubscriptionInterface $subscription, BillingPeriod $trial_period) {
$start_date = $subscription->getStartDate();
$billing_type = $subscription->getBillingSchedule()->getBillingType();
if ($billing_type == BillingScheduleInterface::BILLING_TYPE_PREPAID) {
$billing_schedule = $subscription->getBillingSchedule()->getPlugin();
// The base charge for prepaid subscriptions always covers the next
// period, which in the case of trials is the first billing period.
$base_unit_price = $subscription->getUnitPrice();
$base_billing_period = $billing_schedule->generateFirstBillingPeriod($start_date);
$base_billing_period = $this->adjustBillingPeriod($base_billing_period, $subscription);
}
Imagine a 14 day trial, with the trial period being Mar 1st - Mar 15th. The first billing period is Mar 1st - Mar 31st. The base charge needs to pay for Mar 15th - Mar 31st (used portion of the first billing period). We need to be able to say "prorate this charge against the first billing period, not against the trial period".
Proraters always act on the order item, so we can't move prorating from RecurringOrderManager into the SubscriptionType.
Instead, our best option is to allow charges to specify a full_billing_period. We can then have $charge->getBillingPeriod() / $charge->getFullBillingPeriod() / $charge->needsProration(). It would also make sense to rename the params on the ProraterInterface. Right now they're $partial_period and $period, instead of $billing_period and $full_billing_period.
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | 3038991-5.patch | 27.61 KB | bojanz |
Comments
Comment #2
bojanz commentedComment #3
bojanz commentedComment #4
bojanz commentedMoved the tests to monthly intervals in #3039395: Improve the tests, move from hourly to monthly intervals.
The default test billing schedule is postpaid, so the tests still pass. Let's introduce additional coverage here.
Comment #5
bojanz commentedComment #7
bojanz commentedCommitted.