Refactor booking system: Remove unused policies, packages, and related entities

- Removed package consumption flags and related properties from PriceQuote.
- Eliminated unused domain event publishing for policies and waitlist in Schedule.
- Cleaned up BookingEngineSeeder by removing package and policy related logic.
- Updated SeedScenariosCommand to reflect removal of policies from output.
- Dropped policy, package, treatment course, cancellation, waitlist, and domain event tables in migration.
- Removed domain event assertions from tests related to resource blocking.
This commit is contained in:
hamed
2026-08-01 20:50:47 +03:30
parent 9486721fa3
commit c4f1f25c80
27 changed files with 129 additions and 1510 deletions
+1 -22
View File
@@ -36,7 +36,6 @@ class PricingController extends BaseController
private readonly PriceSnapshotRepository $snapshots,
private readonly ServiceItemRepository $items,
private readonly PricingEngine $engine,
private readonly PatientRecordRepository $patients,
private readonly BranchResolver $branches,
private readonly TenantOwnershipChecker $ownership,
private readonly EntityManagerInterface $em,
@@ -212,27 +211,7 @@ class PricingController extends BaseController
$at = is_numeric($data['at'] ?? null) ? (int) $data['at'] : time();
$policy = is_array($data['policy'] ?? null) ? $data['policy'] : [];
// بیمار اختیاری است: بدون او پکیج معنا ندارد و قیمت همان قیمت کامل است.
$patient = is_string($data['patient_uuid'] ?? null)
? $this->requirePatient($user, $data['patient_uuid'])
: null;
return $this->success($this->engine->quote($service, $items, $address, $at, $policy, $patient)->toArray());
}
private function requirePatient(User $user, string $uuid): \App\Patient\Entity\PatientRecord
{
$patient = $this->patients->findOneBy(['uuid' => $uuid]);
[$entityType, $entityId] = $this->branches->pair($user);
if ($patient === null
|| $patient->getEntityType() !== $entityType
|| $patient->getEntityId() !== $entityId
) {
throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'بیمار یافت نشد', 404);
}
return $patient;
return $this->success($this->engine->quote($service, $items, $address, $at, $policy)->toArray());
}
/**
-82
View File
@@ -6,14 +6,10 @@ use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Repository\ServiceBranchOverrideRepository;
use App\ClinicService\Repository\TariffRepository;
use App\Doctor\Entity\DoctorAddress;
use App\Package\Service\PackageConsumptionService;
use App\Patient\Entity\PatientRecord;
use App\Pricing\Repository\PriceListItemRepository;
use App\Pricing\Repository\PriceListRepository;
use App\Pricing\ValueObject\PriceQuote;
use App\Policy\Entity\Policy;
use App\Policy\Engine\PricingPolicyEngine;
use App\Policy\Service\PolicySchema;
use App\Representation\Service\JalaliDateService;
/**
@@ -38,8 +34,6 @@ use App\Representation\Service\JalaliDateService;
final class PricingEngine
{
public function __construct(
private readonly PricingPolicyEngine $pricingPolicies,
private readonly PackageConsumptionService $packages,
private readonly PriceListRepository $priceLists,
private readonly PriceListItemRepository $priceListItems,
private readonly ServiceBranchOverrideRepository $overrides,
@@ -62,7 +56,6 @@ final class PricingEngine
DoctorAddress $address,
int $at,
array $policy = [],
?PatientRecord $patient = null,
): PriceQuote {
$entityType = $address->tenantEntityType();
$entityId = $address->tenantEntityId();
@@ -80,22 +73,6 @@ final class PricingEngine
$subtotal = $base + $itemsTotal;
// ── پکیج ──────────────────────────────────────────────────────────────
// پکیج **قیمت پایهٔ سرویس** را می‌پوشاند، نه آیتم‌های اضافه: «شش جلسه لیزر»
// یعنی شش بار خودِ لیزر، نه هر چیزی که کنارش انتخاب شود.
$usable = $patient === null ? null : $this->packages->firstUsable($patient, $service, $at);
$covered = 0;
if ($usable !== null) {
$covered = min($base, $subtotal);
$subtotal -= $covered;
}
// ── تخفیف ─────────────────────────────────────────────────────────────
// قوانین دستهٔ «قیمت» کنار سیاست دستیِ درخواست می‌نشینند، نه به‌جایش: تخفیفی
// که اپراتور دستی می‌دهد و تخفیفی که قانون می‌دهد هر دو واقعی‌اند.
$policy = $this->mergePolicyDiscounts($service, $items, $address, $at, $subtotal, $policy, $sources);
[$discount, $discounts] = $this->discountFor($subtotal, $policy);
// تخفیف بیشتر از مبلغ، مبلغ را **صفر** می‌کند نه منفی: بدهی منفی یعنی کلینیک
@@ -125,13 +102,6 @@ final class PricingEngine
$deposit = max(0, min($deposit, $final));
if ($covered > 0) {
$discounts[] = [
'label' => sprintf('پوشش پکیج «%s»', $usable?->getPackage()->getName() ?? '—'),
'rials' => $covered,
'kind' => 'package',
];
}
return new PriceQuote(
baseRials: $base,
@@ -144,61 +114,9 @@ final class PricingEngine
depositRials: $deposit,
discounts: $discounts,
sources: $sources,
packageWillBeConsumed: $usable !== null,
packageUuid: $usable?->getUuid(),
);
}
/**
* اثر قوانین «قیمت» را به سیاست درخواست اضافه می‌کند.
*
* شناسه و **نسخهٔ** هر قانون در `sources` ثبت می‌شود تا فاکتور بتواند سه ماه بعد
* بگوید کدام نسخه رویش اعمال شده بود.
*
* @param ServiceItem[] $items
* @param array<string, mixed> $policy
* @param array<string, mixed> $sources
* @return array<string, mixed>
*/
private function mergePolicyDiscounts(
ServiceItem $service,
array $items,
DoctorAddress $address,
int $at,
int $subtotal,
array $policy,
array &$sources,
): array {
$outcome = $this->pricingPolicies->evaluate(
$address->tenantEntityType(),
$address->tenantEntityId(),
[
'item_count' => count($items),
'subtotal_rials' => $subtotal,
'patient_tags' => $policy['patient_tags'] ?? [],
'visit_count' => $policy['visit_count'] ?? 0,
],
$address,
$service,
$at,
);
if ($outcome->appliedPolicies === []) {
return $policy;
}
$sources['applied_policies'] = $outcome->appliedPolicies;
$policy['discount_percent'] = (float) ($policy['discount_percent'] ?? 0)
+ (float) $outcome->effect(PolicySchema::EFFECT_DISCOUNT_PERCENT, 0);
$policy['discount_rials'] = (int) ($policy['discount_rials'] ?? 0)
+ (int) $outcome->effect(PolicySchema::EFFECT_DISCOUNT_RIALS, 0);
$policy['discount_label'] ??= $outcome->appliedPolicies[0]['name'];
return $policy;
}
/**
* @param array<string, string> $sources
-8
View File
@@ -22,12 +22,6 @@ final readonly class PriceQuote
public int $depositRials,
public array $discounts = [],
public array $sources = [],
/**
* پکیج در پیش‌نمایش **مصرف نمی‌شود** — فقط اعلام می‌شود. مصرف واقعی هنگام
* ثبت نهایی است، وگرنه هر رفرش صفحه یک جلسه از بیمار می‌گرفت.
*/
public bool $packageWillBeConsumed = false,
public ?string $packageUuid = null,
) {}
public function breakdown(): array
@@ -46,8 +40,6 @@ final readonly class PriceQuote
'tax_rials' => $this->taxRials,
'final_rials' => $this->finalRials,
'deposit_rials' => $this->depositRials,
'package_will_be_consumed' => $this->packageWillBeConsumed,
'package_uuid' => $this->packageUuid,
'breakdown' => $this->breakdown(),
];
}