public function process(Event $event, RuleConfig $config) { $entries = new ArrayCollection(); $configRange = new DateRange($config->start, $config->end); $eventRange = new DateRange($event->start, $event->end); if ($configRange->isEmpty()) { return $entries; } $amountPerRange = new Money($config->amount, $config->currency); $configDays = (int) $configRange->getStart()->diff($configRange->getEnd())->format('%R%a') + 1; $totalDays = (int) $eventRange->getStart()->diff($eventRange->getEnd())->format('%R%a') + 1; // If we match up nicely, just bill at the fixed value and we're done if ($eventRange->equals($configRange)) { $rangeTotal = $amountPerRange; $entry = new Entry(); $entry->amount = $rangeTotal->round(2); $entry->currency = $rangeTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s for the entire range', $event->start->format('Y-m-d'), $event->end->format('Y-m-d'), $totalDays, $totalDays == 1 ? 'day' : 'days', $amountPerRange->format()); $entries->add($entry); } else { // (amount per fixed range / days in fixed range) * days billed $proratedTotal = $amountPerRange->div($configDays)->mul($totalDays); $entry = new Entry(); $entry->amount = $proratedTotal->round(2); $entry->currency = $proratedTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s for part of %s to %s (%s %s) (prorated daily)', $event->start->format('Y-m-d'), $event->end->format('Y-m-d'), $totalDays, $totalDays == 1 ? 'day' : 'days', $amountPerRange->format(), $configRange->getStart()->format('Y-m-d'), $configRange->getEnd()->format('Y-m-d'), $configDays, $configDays == 1 ? 'day' : 'days'); $entries->add($entry); } return $entries; }
public function process(Event $event, RuleConfig $config) { $entries = new ArrayCollection(); // Days: Day difference + 1 $days = (int) $event->start->diff($event->end)->format('%R%a') + 1; $amountPerDay = new Money($config->amount, $config->currency); // Safely calculate daily amount // amountPerDay * number of days $total = $amountPerDay->mul($days); $entry = new Entry(); $entry->amount = $total->round(2); $entry->currency = $total->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per day', $event->start->format('Y-m-d'), $event->end->format('Y-m-d'), $days, $days === 1 ? 'day' : 'days', $amountPerDay->format()); $entries->add($entry); return $entries; }
public function process(Event $event, RuleConfig $config) { $entries = new ArrayCollection(); // Nights: Day difference $nights = (int) $event->start->diff($event->end)->format('%R%a'); // Nothing to do. if ($nights <= 0) { return $entries; } $amountPerNight = new Money($config->amount, $config->currency); // Safely calculate nightly amount // amountPerNight * number of nights $total = $amountPerNight->mul($nights); $entry = new Entry(); $entry->amount = $total->round(2); $entry->currency = $total->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per night', $event->start->format('Y-m-d'), $event->end->format('Y-m-d'), $nights, $nights === 1 ? 'night' : 'nights', $amountPerNight->format()); $entries->add($entry); return $entries; }
public function process(Event $event, RuleConfig $config) { $entries = new ArrayCollection(); $eventRange = new DateRange($event->start, $event->end); // Days: Day difference + 1 $totalDays = (int) $eventRange->getStart()->diff($eventRange->getEnd())->format('%R%a') + 1; // The total number of full weeks $weeks = (int) floor($totalDays / 7); // The number of leftover days after full weeks are counted $leftoverDays = (int) $totalDays % 7; $amountPerWeek = new Money($config->amount, $config->currency); // amount per week * number of weeks $weekTotal = $amountPerWeek->mul($weeks); // (amount per week / 7) * leftover days (prorate) $leftoverDayTotal = $amountPerWeek->div(7)->mul($leftoverDays); // If we have solid weeks, make an entry for that total if ($weeks > 0) { $fullWeekEndDate = clone $eventRange->getEnd(); $fullWeekEndDate->modify("-{$leftoverDays} day"); $entry = new entry(); $entry->amount = $weekTotal->round(2); $entry->currency = $weekTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per week', $eventRange->getStart()->format('Y-m-d'), $fullWeekEndDate->format('Y-m-d'), $weeks, $weeks == 1 ? 'week' : 'weeks', $amountPerWeek->format()); $entries->add($entry); } if ($leftoverDays > 0) { // Start 1 day after the end of the previous full week(s), if there // were any $partialWeekStartDate = clone $eventRange->getEnd(); $partialWeekStartDate->modify("-{$leftoverDays} day")->modify('+1 day'); $entry = new Entry(); $entry->amount = $leftoverDayTotal->round(2); $entry->currency = $leftoverDayTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per week (prorated daily)', $partialWeekStartDate->format('Y-m-d'), $eventRange->getEnd()->format('Y-m-d'), $leftoverDays, $leftoverDays == 1 ? 'day' : 'days', $amountPerWeek->format()); $entries->add($entry); } return $entries; }
/** * Asserts the validity of a given value and converts it to a Money object * * @param mixed $money * @return Money * @throws InvalidArgumentException if a value is not valid money */ protected function assertAndConvertValidMoney($money) { if (is_numeric($money)) { $money = new Money($money, $this->getCurrency(), $this->getScale()); } else { if (!$money instanceof Money) { throw new InvalidArgumentException('Value must be either numeric or an instance of Money'); } } if ($this->getCurrency() !== $money->getCurrency()) { throw new InvalidArgumentException('Value must be of the same currency as ' . $this->getCurrency()); } return $money; }
public function process(Event $event, RuleConfig $config) { $entries = new ArrayCollection(); // Create a pointer because we’re about to get procedural $pointer = clone $event->start; // If we don't start on the first of the month, we will need to // calculate "leading" days before the first full month (if there is // one). $leadingDays = 0; $leadingDaysInMonth = 0; if ($pointer->format('j') != 1) { $leadingStartAudit = $pointer->format('Y-m-d'); $leadingEnd = min($event->end, new DateTime($pointer->format('Y-m-t'))); $leadingEndAudit = $leadingEnd->format('Y-m-d'); $leadingDaysInMonth = (int) $pointer->format('t'); $leadingDays = (int) $pointer->diff($leadingEnd)->format('%R%a') + 1; $pointer->modify("+{$leadingDays} days"); } // Initial number of full months $months = 0; // Get the date of the last day in the month our pointer is in $endOfMonth = new DateTime($pointer->format('Y-m-t')); $fullMonthStartAudit = $pointer->format('F Y'); // If the pointer points at the first, and the end date is at or at or // beyond the end of the current month, we can count a full month. while ($pointer->format('j') == 1 && $event->end >= $endOfMonth) { $fullMonthEndAudit = $endOfMonth->format('F Y'); $months += 1; // Now that we counted a full month, advance pointer 1 day past the // end of the month, which should be the first day of the next month $pointer = clone $endOfMonth; $pointer->modify('+1 day'); // Update the new end of the month based on the new pointer value $endOfMonth = new DateTime($pointer->format('Y-m-t')); } $trailingDays = 0; $trailingDaysInMonth = 0; if ($pointer <= $event->end) { $trailingStartAudit = $pointer->format('Y-m-d'); $trailingEndAudit = $event->end->format('Y-m-d'); $trailingDaysInMonth = (int) $pointer->format('t'); // Our pointer should be exactly where it needs to be to calculate any // "trailing" days $trailingDays = (int) $pointer->diff($event->end)->format('%R%a') + 1; } $amountPerMonth = new Money($config->amount, $config->currency); // amount per month * number of months $monthTotal = $amountPerMonth->mul($months); if ($leadingDays > 0) { // (amount per month / number of days in month) * leftover days (prorate) $leadingDayTotal = $amountPerMonth->div($leadingDaysInMonth)->mul($leadingDays); $entry = new Entry(); $entry->amount = $leadingDayTotal->round(2); $entry->currency = $leadingDayTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per month (prorated daily)', $leadingStartAudit, $leadingEndAudit, $leadingDays, $leadingDays == 1 ? 'day' : 'days', $amountPerMonth->format()); $entries->add($entry); } // If we have solid months, make an entry for that total if ($months > 0) { $entry = new Entry(); $entry->amount = $monthTotal->round(2); $entry->currency = $monthTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s (%s %s) @ %s per month', $months == 1 ? $fullMonthStartAudit : "{$fullMonthStartAudit} to {$fullMonthEndAudit}", $months, $months == 1 ? 'month' : 'months', $amountPerMonth->format()); $entries->add($entry); } if ($trailingDays > 0) { // (amount per month / number of days in month) * leftover days (prorate) $trailingDayTotal = $amountPerMonth->div($trailingDaysInMonth)->mul($trailingDays); $entry = new Entry(); $entry->amount = $trailingDayTotal->round(2); $entry->currency = $trailingDayTotal->getCurrency(); $entry->code = $config->code; $entry->description = sprintf('%s to %s (%s %s) @ %s per month (prorated daily)', $trailingStartAudit, $trailingEndAudit, $trailingDays, $trailingDays == 1 ? 'day' : 'days', $amountPerMonth->format()); $entries->add($entry); } return $entries; }
/** * Format a currency amount according to the specified locale. * * @param $value float Value to format * @return string */ public function formatCurrency($amount, $currency = 'USD') { $money = new Money($amount, $currency); return $money->format(); }
public function testPositiveFormatEURInEnUs() { $m = new Money(1077.701, 'EUR'); $m->setFormatter(new NumberFormatter('en-US', NumberFormatter::CURRENCY)); $this->assertSame('€1,077.70', $m->format()); }