feat(claims): enrich claims with insurance details and item specifics
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\Billing\Controller;
|
||||
use App\Auth\Entity\User;
|
||||
use App\Billing\Entity\Claim;
|
||||
use App\Billing\Repository\ClaimRepository;
|
||||
use App\Billing\Repository\InvoiceItemRepository;
|
||||
use App\Billing\Repository\InvoiceRepository;
|
||||
use App\Billing\Service\ClaimService;
|
||||
use App\Billing\Service\InvoiceService;
|
||||
@@ -28,6 +29,7 @@ class BillingController extends BaseController
|
||||
public function __construct(
|
||||
private readonly InvoiceService $invoiceService,
|
||||
private readonly InvoiceRepository $invoiceRepo,
|
||||
private readonly InvoiceItemRepository $invoiceItemRepo,
|
||||
private readonly ClaimService $claimService,
|
||||
private readonly ClaimRepository $claimRepo,
|
||||
private readonly PatientSessionRepository $sessionRepo,
|
||||
@@ -36,11 +38,46 @@ class BillingController extends BaseController
|
||||
private readonly InsuranceRepository $insuranceRepo,
|
||||
) {}
|
||||
|
||||
private function claimArray(Claim $claim): array
|
||||
/**
|
||||
* مطالبات را با نام بیمه و جزئیات هر آیتم (نوع/نام، تاریخ مراجعه، تعداد) غنی میکند.
|
||||
* @param Claim[] $claims
|
||||
*/
|
||||
private function enrichClaims(array $claims): array
|
||||
{
|
||||
$data = $claim->toArray();
|
||||
$data['insurance_name'] = $this->insuranceRepo->find($claim->getInsuranceId())?->getName();
|
||||
return $data;
|
||||
// جمعآوری همهی invoice_item_idها در یک رفتوآمد
|
||||
$invoiceItemIds = [];
|
||||
foreach ($claims as $claim) {
|
||||
foreach ($claim->getItems() as $ci) {
|
||||
$invoiceItemIds[] = $ci->getInvoiceItemId();
|
||||
}
|
||||
}
|
||||
$itemDetails = $this->invoiceItemRepo->detailsForIds(array_values(array_unique($invoiceItemIds)));
|
||||
|
||||
$sessionIds = [];
|
||||
foreach ($itemDetails as $d) {
|
||||
if ($d['patient_session_id'] !== null) {
|
||||
$sessionIds[] = $d['patient_session_id'];
|
||||
}
|
||||
}
|
||||
$sessionDates = $this->sessionRepo->datesForIds(array_values(array_unique($sessionIds)));
|
||||
|
||||
return array_map(function (Claim $claim) use ($itemDetails, $sessionDates) {
|
||||
$data = $claim->toArray();
|
||||
$data['insurance_name'] = $this->insuranceRepo->find($claim->getInsuranceId())?->getName();
|
||||
|
||||
$data['items'] = array_map(function (array $item) use ($itemDetails, $sessionDates) {
|
||||
$detail = $itemDetails[$item['invoice_item_id']] ?? null;
|
||||
$sessionId = $detail['patient_session_id'] ?? null;
|
||||
$item['title'] = $detail['title'] ?? null;
|
||||
$item['is_visit'] = $detail !== null && $detail['service_item_id'] === null;
|
||||
$item['quantity'] = $detail['quantity'] ?? 1;
|
||||
$item['total_rials'] = $detail['total_rials'] ?? null;
|
||||
$item['visit_date'] = $sessionId !== null ? ($sessionDates[$sessionId] ?? null) : null;
|
||||
return $item;
|
||||
}, $data['items']);
|
||||
|
||||
return $data;
|
||||
}, $claims);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/billing/invoices', methods: ['POST'])]
|
||||
@@ -116,7 +153,7 @@ class BillingController extends BaseController
|
||||
|
||||
$claims = $this->claimService->createFromInvoice($invoice);
|
||||
|
||||
return $this->success(['data' => array_map(fn(Claim $c) => $this->claimArray($c), $claims)], 201);
|
||||
return $this->success(['data' => $this->enrichClaims($claims)], 201);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/billing/claims', methods: ['GET'])]
|
||||
@@ -130,7 +167,7 @@ class BillingController extends BaseController
|
||||
$status = $request->query->get('status') ?: null;
|
||||
$claims = $this->claimRepo->findByTenant($entityType, $entityId, $status);
|
||||
|
||||
return $this->success(['data' => array_map(fn(Claim $c) => $this->claimArray($c), $claims)]);
|
||||
return $this->success(['data' => $this->enrichClaims($claims)]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/billing/claims/{uuid}/{action}', methods: ['POST'], requirements: ['action' => 'submit|approve|reject|pay'])]
|
||||
@@ -160,7 +197,7 @@ class BillingController extends BaseController
|
||||
'reason' => trim($data['reason'] ?? ''),
|
||||
]);
|
||||
|
||||
return $this->success(['data' => $this->claimArray($claim)]);
|
||||
return $this->success(['data' => $this->enrichClaims([$claim])[0]]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/billing/reports/insurance-debt', methods: ['GET'])]
|
||||
|
||||
@@ -20,4 +20,38 @@ class InvoiceItemRepository extends ServiceEntityRepository
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* جزئیات چند آیتم صورتحساب برای نمایش در مطالبات — کلیددار بر اساس id آیتم.
|
||||
* @param int[] $ids
|
||||
* @return array<int, array{title: string, service_item_id: int|null, quantity: int, total_rials: int, patient_session_id: int|null}>
|
||||
*/
|
||||
public function detailsForIds(array $ids): array
|
||||
{
|
||||
if ($ids === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->createQueryBuilder('ii')
|
||||
->select('ii.id AS id', 'ii.title AS title', 'ii.serviceItemId AS service_item_id',
|
||||
'ii.quantity AS quantity', 'ii.totalRials AS total_rials',
|
||||
'inv.patientSessionId AS patient_session_id')
|
||||
->join('ii.invoice', 'inv')
|
||||
->where('ii.id IN (:ids)')
|
||||
->setParameter('ids', $ids)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$out[(int) $r['id']] = [
|
||||
'title' => $r['title'],
|
||||
'service_item_id' => $r['service_item_id'] !== null ? (int) $r['service_item_id'] : null,
|
||||
'quantity' => (int) $r['quantity'],
|
||||
'total_rials' => (int) $r['total_rials'],
|
||||
'patient_session_id' => $r['patient_session_id'] !== null ? (int) $r['patient_session_id'] : null,
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,14 +52,15 @@ class InvoiceService
|
||||
// خدمات
|
||||
foreach ($session->getServices() as $sessionService) {
|
||||
$item = $sessionService->getServiceItem();
|
||||
$qty = max(1, $sessionService->getQuantity());
|
||||
$unitPrice = $this->tariffService->resolvePrice($item);
|
||||
$total = new Money($unitPrice);
|
||||
$total = new Money($unitPrice * $qty);
|
||||
|
||||
$baseRule = $this->tenantInsuranceService->coverageRuleForService($entityType, $entityId, $baseId, $item->getId());
|
||||
$suppRule = $this->tenantInsuranceService->coverageRuleForService($entityType, $entityId, $suppId, $item->getId());
|
||||
|
||||
$breakdown = $this->calculator->calculateItem($total, $baseRule, $suppRule);
|
||||
$invoice->addItem(new InvoiceItem($invoice, $item->getName(), $unitPrice, 1, $breakdown, $item->getId()));
|
||||
$invoice->addItem(new InvoiceItem($invoice, $item->getName(), $unitPrice, $qty, $breakdown, $item->getId()));
|
||||
}
|
||||
|
||||
$invoice->recalculateTotals();
|
||||
|
||||
@@ -82,6 +82,14 @@ class PatientSession
|
||||
public function getInsuranceBaseId(): ?int { return $this->insuranceBaseId; }
|
||||
public function getInsuranceSupplementaryId(): ?int { return $this->insuranceSupplementaryId; }
|
||||
public function getServices(): Collection { return $this->services; }
|
||||
|
||||
public function addService(SessionService $service): self
|
||||
{
|
||||
if (!$this->services->contains($service)) {
|
||||
$this->services->add($service);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function getVisitPriceRials(): int { return $this->visitPriceRials; }
|
||||
public function getBaseInsuranceDiscountPercent(): float { return (float) $this->baseInsuranceDiscountPercent; }
|
||||
public function getSupplementaryDiscountPercent(): float { return (float) $this->supplementaryDiscountPercent; }
|
||||
|
||||
@@ -64,4 +64,29 @@ class PatientSessionRepository extends ServiceEntityRepository
|
||||
$this->getEntityManager()->persist($session);
|
||||
$this->getEntityManager()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* تاریخ ثبت چند مراجعه — کلیددار بر اساس id جلسه.
|
||||
* @param int[] $ids
|
||||
* @return array<int, int> sessionId => createdAt
|
||||
*/
|
||||
public function datesForIds(array $ids): array
|
||||
{
|
||||
if ($ids === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->createQueryBuilder('s')
|
||||
->select('s.id AS id', 's.createdAt AS created_at')
|
||||
->where('s.id IN (:ids)')
|
||||
->setParameter('ids', $ids)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$out[(int) $r['id']] = (int) $r['created_at'];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ class PatientService
|
||||
$qty = max(1, (int) ($svc['quantity'] ?? 1));
|
||||
$ss = new SessionService($session, $item, $staff, $qty);
|
||||
$this->sessionServiceRepo->save($ss);
|
||||
$session->addService($ss);
|
||||
}
|
||||
|
||||
return $session;
|
||||
|
||||
Reference in New Issue
Block a user