Allocation & splitting
Split an amount without losing or inventing a minor unit. allocate() takes ratios — ints, integer strings or decimals. Every part is within one minor unit of its exact share, and the parts always sum back to the original:
$total = Money::ofMajor('100', 'EUR');
[$a, $b, $c] = $total->allocate(1, 1, 1); // 33.34, 33.33, 33.33 — sums back to 100.00
$total->allocate(70, 30); // 70.00, 30.00
$total->allocate('0.5', '0.25', '0.25'); // 50.00, 25.00, 25.00 — decimal ratios work too
$total->split(3); // same as allocate(1, 1, 1)
Money::ofMajor('30', 'EUR')->ratioTo(Money::ofMajor('120', 'EUR')); // Ratio 1/4, exactHow it works
- Each share is ⌊|amount| × ratio / Σ ratios⌋; the leftover units go one each to the largest exact remainders, ties to the lower index.
- The sign is re-applied afterwards, so negative amounts split symmetrically.
- Zero-ratio slots receive zero.
- split($parts) is allocate() with equal ratios, for 1 to 10 000 parts.
- No ratios, a negative ratio or a zero total throw InvalidAllocation.
Order discounts across lines
DiscountAllocator spreads an order-level discount over line totals with the same largest-remainder method, weighted by the line amounts. The shares sum to the discount exactly and no share ever exceeds its line:
use RoundlyConsulting\Money\Discounts\DiscountAllocator;
$shares = app(DiscountAllocator::class)->allocate(
Money::ofMajor('10', 'EUR'), // the order-level discount
Money::ofMajor('30', 'EUR'), // line totals…
Money::ofMajor('20', 'EUR'),
Money::ofMajor('50', 'EUR'),
);
// 3.00, 2.00, 5.00 EUR — the shares sum to the discount, and 0 ≤ share ≤ lineA discount larger than the lines together throws InvalidDiscount; mixed currencies throw CurrencyMismatch. The allocator is bound as a singleton.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.