feat(discount): apply a discount rule to a session with audit

Extend PatientService::applyDiscount to carry the source rule id/label and
add applyDiscountRule, which computes the rial amount via the engine
(DiscountEngine::computeForRule now public) and records the rule for audit.
updateSession accepts discount_rule_uuid (owner-scoped, takes precedence over
the manual discount; ''/null clears). Verified end-to-end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-17 11:55:16 +03:30
co-authored by Claude Fable 5
parent 6cc42c941e
commit b3b1dad832
4 changed files with 62 additions and 15 deletions
+19 -8
View File
@@ -36,14 +36,7 @@ class DiscountEngine
$out = [];
foreach ($rules as $rule) {
if (!$this->withinValidity($rule, $now)) {
continue;
}
$base = $this->applicableBase($rule, $session, $final, $now);
if ($base === null) {
continue;
}
$rials = min($this->computeRials($rule, $base), $remaining);
$rials = $this->computeForRule($session, $rule, $now, $remaining);
if ($rials <= 0) {
continue;
}
@@ -61,6 +54,24 @@ class DiscountEngine
return $out;
}
/**
* مبلغ تخفیف ریالیِ یک قانون برای یک پرونده؛ 0 اگر برقرار نباشد.
* $remaining را ندهی، از خود session محاسبه می‌شود.
*/
public function computeForRule(PatientSession $session, DiscountRule $rule, ?int $now = null, ?int $remaining = null): int
{
$now ??= time();
$remaining ??= max(0, $session->getFinalPriceRials() - $session->getPaidTotalRials());
if (!$this->withinValidity($rule, $now)) {
return 0;
}
$base = $this->applicableBase($rule, $session, $session->getFinalPriceRials(), $now);
if ($base === null) {
return 0;
}
return min($this->computeRials($rule, $base), $remaining);
}
private function withinValidity(DiscountRule $rule, int $now): bool
{
if ($rule->getValidFrom() !== null && $now < $rule->getValidFrom()) {