getId() !== null ? $this->invoiceRepo->findBySession($session->getId()) : null; if ($existing !== null) { return $existing; } $invoice = new Invoice($entityType, $entityId); $invoice->setPatientSessionId($session->getId()) ->setPatientRecordId($session->getRecord()->getId()) ->setBaseInsuranceId($session->getInsuranceBaseId()) ->setSupplementaryInsuranceId($session->getInsuranceSupplementaryId()); $baseId = $session->getInsuranceBaseId(); $suppId = $session->getInsuranceSupplementaryId(); // ویزیت $visitPrice = $session->getVisitPriceRials(); if ($visitPrice > 0) { $baseRule = $this->tenantInsuranceService->coverageRule($entityType, $entityId, $baseId); $suppRule = $this->tenantInsuranceService->coverageRule($entityType, $entityId, $suppId); $breakdown = $this->calculator->calculateItem(new Money($visitPrice), $baseRule, $suppRule); $invoice->addItem(new InvoiceItem($invoice, 'ویزیت', $visitPrice, 1, $breakdown, null)); } // خدمات foreach ($session->getServices() as $sessionService) { $item = $sessionService->getServiceItem(); $qty = max(1, $sessionService->getQuantity()); $unitPrice = $this->tariffService->resolvePrice($item); $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, $qty, $breakdown, $item->getId())); } $invoice->recalculateTotals(); $this->invoiceRepo->save($invoice); return $invoice; } public function finalize(Invoice $invoice): void { $invoice->finalize(); $this->invoiceRepo->save($invoice); } /** * Paginated per-patient payment summary for a tenant. Each row gains a * derived status: `paid` (nothing outstanding), `unpaid` (nothing paid * yet), `unsettled` (partially paid). * * @param array{national_code?:?string,status?:?string,from?:?int,to?:?int} $filters * @return array{items: list>, total: int} */ public function patientPaymentList(string $entityType, int $entityId, array $filters, int $page, int $limit): array { $items = array_map(static function (array $row): array { $row['status'] = $row['remaining_rials'] === 0 ? 'paid' : ($row['paid_rials'] === 0 ? 'unpaid' : 'unsettled'); return $row; }, $this->invoiceRepo->patientPaymentSummary($entityType, $entityId, $filters, $page, $limit)); return [ 'items' => $items, 'total' => $this->invoiceRepo->countPatientPaymentSummary($entityType, $entityId, $filters), ]; } /** * A patient's recorded invoices, shaped for the detail table: number, issue * time, a single service title (first item, "+ more" when several), total, * a two-state status (paid|unsettled), and the full item breakdown. * * @return array{items: list>, total: int} */ public function patientInvoiceList(string $entityType, int $entityId, int $recordId, int $page, int $limit): array { $items = array_map(function (Invoice $invoice): array { $lineItems = array_map(static fn(InvoiceItem $i) => $i->toArray(), $invoice->getItems()->toArray()); $title = match (count($lineItems)) { 0 => null, 1 => $lineItems[0]['title'], default => $lineItems[0]['title'] . ' و موارد دیگر', }; return [ 'uuid' => $invoice->getUuid(), 'number' => $invoice->getId(), 'issued_at' => $invoice->getIssuedAt(), 'total_rials' => $invoice->getTotalRials(), 'status' => $invoice->getStatus() === Invoice::STATUS_PAID ? 'paid' : 'unsettled', 'service_title' => $title, 'items' => $lineItems, ]; }, $this->invoiceRepo->invoicesForPatient($entityType, $entityId, $recordId, $page, $limit)); return [ 'items' => $items, 'total' => $this->invoiceRepo->countInvoicesForPatient($entityType, $entityId, $recordId), ]; } }