feat: replace custom discount with absolute value in favor of percentage

This commit is contained in:
2026-09-08 17:11:00 +02:00
parent d6c2cfaf1c
commit f8ff4b82a3
15 changed files with 105 additions and 50 deletions
@@ -56,7 +56,7 @@ class AccommodationBookingControllerTest extends TestCase
$booking->setAccommodationDiscount(10);
$booking->setBoardServiceDiscount(20);
$booking->setAdditionalServicesDiscount(30);
$booking->setTotalDiscount(5);
$booking->setTotalDiscountAmount(5000);
$booking->setTotalDiscountLabel('Treuerabatt');
$booking->setStatus(AccommodationBookingStatus::Confirmed);
$booking->setOrigin(AccommodationBookingOrigin::Direct);
@@ -129,7 +129,7 @@ class AccommodationBookingControllerTest extends TestCase
'accommodationDiscount' => 10,
'boardServiceDiscount' => 20,
'additionalServicesDiscount' => 30,
'totalDiscount' => 5,
'totalDiscountAmount' => 5000,
'totalDiscountLabel' => 'Treuerabatt',
'totalPrice' => 11111,
'pricingCurrency' => 'EUR',
@@ -74,13 +74,13 @@ class AccommodationBookingTypeTest extends TestCase
}
/**
* The label is printed verbatim on the customer's PDF, so a percentage without one is
* The label is printed verbatim on the customer's PDF, so an amount without one is
* refused — and in the Default group, so that even a draft cannot store the pair half-set.
*/
public function testTotalDiscountRequiresALabel(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscount(5);
$booking->setTotalDiscountAmount(5000);
$violations = $this->validate($booking, ['Default']);
@@ -91,17 +91,17 @@ class AccommodationBookingTypeTest extends TestCase
public function testALabelledTotalDiscountPassesValidation(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscount(5);
$booking->setTotalDiscountAmount(5000);
$booking->setTotalDiscountLabel('Treuerabatt');
self::assertCount(0, $this->validate($booking, ['Default']));
}
/**
* The reverse is harmless — without a percentage the label never reaches a breakdown row —
* The reverse is harmless — without an amount the label never reaches a breakdown row —
* so it is deliberately left valid rather than blocking a half-typed edit.
*/
public function testALabelWithoutAPercentageIsAccepted(): void
public function testALabelWithoutAnAmountIsAccepted(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscountLabel('Treuerabatt');
@@ -101,33 +101,32 @@ class AccommodationBookingBreakdownCalculatorTest extends TestCase
/**
* On its own the total discount has nothing to sit below, so no subtotal row is offered and
* the percentage applies to the plain total.
* the amount comes straight off the plain total.
*/
public function testTotalDiscountAloneAppliesToTheTotalWithoutASubtotal(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscount(10);
$booking->setTotalDiscountAmount(1235);
$booking->setTotalDiscountLabel('Treuerabatt');
$breakdown = $this->createCalculator()->withDiscounts($booking, $this->breakdown());
self::assertSame([], $breakdown['discounts']);
self::assertSame(['label' => 'Treuerabatt', 'percent' => 10, 'amount' => 1235], $breakdown['totalDiscountDetails']);
self::assertSame(['label' => 'Treuerabatt', 'amount' => 1235], $breakdown['totalDiscountDetails']);
self::assertNull($breakdown['discountSubtotal']);
self::assertSame(12345 - 1235, $breakdown['discountedTotal']);
}
/**
* The worked example from the specification: the total discount compounds on the subtotal
* left by the section discounts, not on the gross total — 5 % of 930,00 € is 46,50 €, where
* 5 % of the gross 1.000,00 € would have been 50,00 €.
* The worked example from the specification: a fixed 50,00 € off the 930,00 € left by the
* section discounts, which is where the Zwischensumme row comes from.
*/
public function testTotalDiscountCompoundsOnTheSubtotalAfterSectionDiscounts(): void
public function testTotalDiscountIsSubtractedFromTheSubtotalAfterSectionDiscounts(): void
{
$booking = new AccommodationBooking();
$booking->setAccommodationDiscount(10);
$booking->setBoardServiceDiscount(5);
$booking->setTotalDiscount(5);
$booking->setTotalDiscountAmount(5000);
$booking->setTotalDiscountLabel('Treuerabatt');
$breakdown = $this->createCalculator()->withDiscounts($booking, [
@@ -138,14 +137,30 @@ class AccommodationBookingBreakdownCalculatorTest extends TestCase
// accommodation: 60000*10% = 6000, board: 20000*5% = 1000 → subtotal 93000
self::assertSame(93000, $breakdown['discountSubtotal']);
self::assertSame(['label' => 'Treuerabatt', 'percent' => 5, 'amount' => 4650], $breakdown['totalDiscountDetails']);
self::assertSame(88350, $breakdown['discountedTotal']);
self::assertSame(['label' => 'Treuerabatt', 'amount' => 5000], $breakdown['totalDiscountDetails']);
self::assertSame(88000, $breakdown['discountedTotal']);
}
/**
* A sum larger than the price is clamped rather than refused: prices move after the discount
* was agreed, and a negative total would be worse than a free stay.
*/
public function testTotalDiscountLargerThanTheSubtotalIsClampedToIt(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscountAmount(20000);
$booking->setTotalDiscountLabel('Treuerabatt');
$breakdown = $this->createCalculator()->withDiscounts($booking, $this->breakdown());
self::assertSame(['label' => 'Treuerabatt', 'amount' => 12345], $breakdown['totalDiscountDetails']);
self::assertSame(0, $breakdown['discountedTotal']);
}
public function testTotalDiscountThatAmountsToNothingIsSkipped(): void
{
$booking = new AccommodationBooking();
$booking->setTotalDiscount(20);
$booking->setTotalDiscountAmount(2000);
$booking->setTotalDiscountLabel('Treuerabatt');
$breakdown = $this->createCalculator()->withDiscounts($booking, ['total' => 0]);