101 lines
2.9 KiB
PHP
101 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Domain\Models\ValueObjects;
|
|
|
|
use Domain\Enums\ProductBrand;
|
|
use Domain\Enums\TaxRate;
|
|
use Domain\Enums\Units\OrderUnit;
|
|
use Domain\Models\ValueObjects\Entities\ProductId;
|
|
use Domain\Services\PricingService;
|
|
use Money\Money;
|
|
|
|
final readonly class BasketProduct
|
|
{
|
|
public function __construct(
|
|
public ProductId $id,
|
|
public string $name,
|
|
public int $qty,
|
|
public Money $unitPriceHt,
|
|
public Money $unitDiscount,
|
|
public TaxRate $taxRate,
|
|
public int $weightGrams,
|
|
public ?ProductBrand $brand,
|
|
public ?IncoId $incoId,
|
|
public OrderUnit $orderUnit,
|
|
public string $slug,
|
|
public ?string $picture,
|
|
public ?string $description,
|
|
public bool $weightVariable,
|
|
public float $discountRate,
|
|
public Money $priceKgTtc,
|
|
/** @var array<string,mixed> */
|
|
public array $options = []
|
|
) {
|
|
if (trim($name) === '') {
|
|
throw new \DomainException('name cannot be empty');
|
|
}
|
|
if (trim($slug) === '') {
|
|
throw new \DomainException('slug cannot be empty');
|
|
}
|
|
if ($qty <= 0) {
|
|
throw new \DomainException('qty has to be positive');
|
|
}
|
|
if (! $unitPriceHt->isPositive()) {
|
|
throw new \DomainException('unitPriceHt has to be positive');
|
|
}
|
|
if (! $unitDiscount->isPositive() && ! $unitDiscount->isZero()) {
|
|
throw new \DomainException('unitDiscount cannot be negative');
|
|
}
|
|
if (! $priceKgTtc->isPositive()) {
|
|
throw new \DomainException('priceKgTtc has to be positive');
|
|
}
|
|
if ($discountRate < 0 || $discountRate > 1) {
|
|
throw new \DomainException('discountRate has to be between 0 and 1');
|
|
}
|
|
}
|
|
|
|
public function unitPriceHt(): Money
|
|
{
|
|
return $this->unitPriceHt;
|
|
}
|
|
|
|
public function unitTtc(): Money
|
|
{
|
|
$unitPriceDiscounted = $this->unitPriceHt->subtract($this->unitDiscount);
|
|
|
|
// applyTo renvoie déjà le montant TTC (900 + 180 = 1080)
|
|
return $this->taxRate->applyTo($unitPriceDiscounted);
|
|
}
|
|
|
|
public function discountTotal(): Money
|
|
{
|
|
return $this->unitDiscount->multiply($this->qty, PricingService::ROUNDING);
|
|
}
|
|
|
|
public function priceTotalHt(): Money
|
|
{
|
|
return $this->unitPriceHt->multiply($this->qty, PricingService::ROUNDING);
|
|
}
|
|
|
|
public function subtotalHt(): Money
|
|
{
|
|
return $this->priceTotalHt()->subtract($this->discountTotal());
|
|
}
|
|
|
|
public function taxTotal(): Money
|
|
{
|
|
return $this->totalTtc()->subtract($this->subtotalHt());
|
|
}
|
|
|
|
public function totalTtc(): Money
|
|
{
|
|
return $this->taxRate->applyTo($this->subtotalHt());
|
|
}
|
|
|
|
public function weightTotal(): int
|
|
{
|
|
return $this->weightGrams * $this->qty;
|
|
}
|
|
}
|