Skip to content
This repository has been archived by the owner on Feb 2, 2022. It is now read-only.

Incorrect prorating/reimbursement #257

Open
lexdewilligen opened this issue Oct 3, 2020 · 0 comments
Open

Incorrect prorating/reimbursement #257

lexdewilligen opened this issue Oct 3, 2020 · 0 comments

Comments

@lexdewilligen
Copy link
Contributor

As I already suspected in #189. I believe there is something wrong with the current way of prorating/reimbursing. To give a small summary, we have the following requirements for our subscriptions:

  1. One invoice (the day does not matter, may be a fixed date, may also be variable based on the day an organization subscribed)
  2. Variable quantity (for every user that registers for an organization, we increment the subscription quantity)
  3. Multiplan (currently we create a new subscription for every chosen module, so an organization would get multiple invoices if we were to run Cashier::run() everyday)

Variable quantity is something that's built into cashier, so no problems there. One invoice can be fixed by running Cashier::run() once per month (#65). All due order items will be processed into one invoice. However, the quantity does result in wrong reimbursement. More info on our situation can be found in #189, but I've also created a litte schema to further clarify what currently happens:
Web 1920 – 1

If I'm correct, we miss out on the red marked area which is never paid for since the organization does not pay at the start of this cycle, but on the end. I believe the problem should be tackled in these lines of code:

    /**
     * Wrap up the current billing cycle, apply modifications to this subscription and start a new cycle.
     *
     * @param \Closure $applyNewSettings
     * @param \Carbon\Carbon|null $now
     * @param bool $invoiceNow
     * @return \Laravel\Cashier\Subscription
     */
    public function restartCycleWithModifications(\Closure $applyNewSettings, ?Carbon $now = null, $invoiceNow = true)
    {
        $now = $now ?: now();

        return DB::transaction(function () use ($applyNewSettings, $now, $invoiceNow) {

            // Wrap up current billing cycle
            $this->removeScheduledOrderItem();
            $reimbursement = $this->reimburseUnusedTime($now);

            $orderItems = (new OrderItemCollection([$reimbursement]))->filter();

            // Apply new subscription settings
            call_user_func($applyNewSettings);

            if ($this->onTrial()) {

                // Reschedule next cycle's OrderItem using the new subscription settings
                $orderItems[] = $this->scheduleNewOrderItemAt($this->trial_ends_at);
            } else { // Start a new billing cycle using the new subscription settings

                // Reset the billing cycle
                $this->cycle_started_at = $now;
                $this->cycle_ends_at = $now;

                // Create a new OrderItem, starting a new billing cycle
                $orderItems[] = $this->scheduleNewOrderItemAt($now);
            }

            $this->save();

            if ($invoiceNow) {
                $order = Order::createFromItems($orderItems);
                $order->processPayment();
            }

            return $this;
        });
    }

The $this->reimburseUnusedTime($now) should maybe check whether the 'used time' has already been charged and otherwise leave an orderItem for this short cycle instead of reimbursing the time left. What do you think @sandervanhooft?

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

No branches or pull requests

1 participant