function it_can_tell_when_a_due_date_is_exactly_one_year_earlier()
 {
     $this->beConstructedThrough('fromString', ['2014-01-01']);
     $this->shouldBeExactlyOneYearEarlierThan(DueDate::fromString('2015-01-01'));
     $this->shouldNotBeExactlyOneYearEarlierThan(DueDate::fromString('2015-01-02'));
     $this->shouldNotBeExactlyOneYearEarlierThan(DueDate::fromString('2015-02-01'));
     $this->shouldNotBeExactlyOneYearEarlierThan(DueDate::fromString('2013-01-01'));
 }
 function it_should_spread_the_bursary_when_the_plan_spans_multiple_years($baseCalculator, PaymentPlan $planBeforeBursary, PaymentPlan $finalPlan)
 {
     $baseCalculator->getPlan(Def::withName('Foo'), Money::GBP(250), Argument::any())->willReturn($planBeforeBursary);
     $planBeforeBursary->getPlannedPayments()->willReturn([PlannedPayment::immediate(Money::GBP(50)), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2014-01-01')), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2014-06-01')), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2015-01-01')), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2015-06-01'))]);
     $planBeforeBursary->getShortDescription()->willReturn('foo');
     $planBeforeBursary->getLongDescription()->willReturn('foofoo');
     $finalPlan = new PaymentPlan([PlannedPayment::immediate(Money::GBP(50)), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2014-01-01')), PlannedPayment::withDueDate(Money::GBP(40), DueDate::fromString('2014-06-01')), PlannedPayment::withDueDate(Money::GBP(50), DueDate::fromString('2015-01-01')), PlannedPayment::withDueDate(Money::GBP(40), DueDate::fromString('2015-06-01'))], 'foo', 'foofoo');
     $this->getPlan(Def::withName('Foo'), Money::GBP(230), Params::fromArray(['bursary_total_deduction' => 20]))->shouldBeLike($finalPlan);
 }
 function testIntegration()
 {
     $definition = PlanDefinition::withNameAndAttributesAsArray('PercentOnDate', ['short_description' => '20/40/40 split', 'long_description' => '20% immediately, 40% on 2014/01/01, 40% on 2014/03/01, except that bursaries are always taken from the final instalment', 'payments' => ['immediate' => 0.2, '2014-01-01' => 0.4, '2014-03-01' => 0.4], 'modifiers' => ['bursary_off_final_payment']]);
     $params = PlanParameters::fromArray(['bursary_total_deduction' => 10]);
     $this->assertTrue($this->factory->isAvailable($definition, Money::GBP(90), $params));
     $plan = $this->factory->getPlan($definition, Money::GBP(90), $params);
     /**
      * The net booking cost is 90, which includes a 10 deduction for a bursary. Without the bursary, we'd expect
      * the 100 to be split as 20/40/40. With the bursary_off_final_payment modifier, this becomes 20/40/30
      */
     $this->assertEquals(new PaymentPlan([PlannedPayment::immediate(Money::GBP(20)), PlannedPayment::withDueDate(Money::GBP(40), DueDate::fromString('2014-01-01')), PlannedPayment::withDueDate(Money::GBP(30), DueDate::fromString('2014-03-01'))], '20/40/40 split', '20% immediately, 40% on 2014/01/01, 40% on 2014/03/01, except that bursaries are always taken from the final instalment'), $plan);
 }
 /**
  * @param PlanDefinition $definition
  * @param Money $amountToPay
  * @param PlanParameters $parameters
  * @return PaymentPlan
  */
 public function getPlan(PlanDefinition $definition, Money $amountToPay, PlanParameters $parameters)
 {
     /** @var array $paymentProportions */
     $paymentProportions = $definition->getAttribute('payments');
     //The values of the payments array is a ratios array
     /** @var Money[] $amounts */
     $amounts = $amountToPay->allocate(array_values($paymentProportions));
     $payments = array_map(function ($rawDate, $amount) {
         //rawDate is either 'immediate' or a 'Y-m-d' string, or invalid.
         if ($rawDate === 'immediate') {
             return PlannedPayment::immediate($amount);
         }
         return PlannedPayment::withDueDate($amount, DueDate::fromString($rawDate));
     }, array_keys($paymentProportions), $amounts);
     return new PaymentPlan($payments, $definition->getAttribute('short_description'), $definition->getAttribute('long_description'));
 }
 function it_should_return_a_plan_based_on_definition_attributes()
 {
     $expected = new PaymentPlan([PlannedPayment::immediate(Money::GBP(10)), PlannedPayment::withDueDate(Money::GBP(70), DueDate::fromString('2014-01-01')), PlannedPayment::withDueDate(Money::GBP(20), DueDate::fromString('2015-01-01'))], 'Foo', 'FooFoo');
     $this->getPlan(PlanDefinition::withNameAndAttributesAsArray('PercentOnDate', ['short_description' => 'Foo', 'long_description' => 'FooFoo', 'payments' => ['immediate' => 0.1, '2014-01-01' => 0.7, '2015-01-01' => 0.2]]), Money::GBP(100), PlanParameters::none())->shouldBeLike($expected);
 }