35 lines
1.6 KiB
PHP
35 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Brick\Math\BigDecimal;
|
|
use Brick\Math\RoundingMode;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class QuotationPricing
|
|
{
|
|
/** Discount applies to the unit price before VAT; the net unit is rounded once to cents.
|
|
* @return array{gross_unit_minor: int, discount_type: string, discount_value: string, net_unit_minor: int}
|
|
*/
|
|
public function unit(string $price, string $type = 'none', string $value = '0'): array
|
|
{
|
|
$gross = BigDecimal::of($price)->multipliedBy(100)->toInt();
|
|
$discount = BigDecimal::of($value);
|
|
if ($gross < 1 || $discount->isNegative() || ! in_array($type, ['none', 'percent', 'amount'], true) ||
|
|
($type === 'none' && ! $discount->isZero()) || ($type === 'percent' && $discount->isGreaterThan(100))) {
|
|
throw ValidationException::withMessages(['items' => 'Indique um desconto unitário válido: percentagem ou valor em MZN, antes do IVA.']);
|
|
}
|
|
$reduction = match ($type) {
|
|
'percent' => BigDecimal::of($gross)->multipliedBy($discount)->dividedBy(100, 4),
|
|
'amount' => $discount->multipliedBy(100),
|
|
default => BigDecimal::zero(),
|
|
};
|
|
$net = BigDecimal::of($gross)->minus($reduction)->toScale(0, RoundingMode::HalfUp)->toInt();
|
|
if ($net < 1) {
|
|
throw ValidationException::withMessages(['items' => 'O preço unitário após desconto deve ser pelo menos 0,01 MZN.']);
|
|
}
|
|
|
|
return ['gross_unit_minor' => $gross, 'discount_type' => $type, 'discount_value' => (string) $discount->toScale(2), 'net_unit_minor' => $net];
|
|
}
|
|
}
|