fix(patient): consistent debt/paid status, invoice totals, global service search

- patient_debt_rials now = session remaining (final - discount - paid), so the
  card status and invoice status stay consistent with is_paid even after a
  session is edited post-invoicing (no more '0 remaining but تکمیل پرداخت').
- InvoiceSummaryModal: remaining subtracts discount; the فاکتور status reflects
  actual settlement (تسویه شده / بدهکار), and the payments table shows a
  «مجموع پرداختی‌ها» total row.
- Add GET /api/v1/service-items (all owner services) and make the service
  picker searchable across all services without first choosing a section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-17 15:53:28 +03:30
co-authored by Claude Fable 5
parent e9108e7590
commit 385b81fae0
6 changed files with 56 additions and 19 deletions
@@ -122,6 +122,20 @@ class ClinicServiceController extends BaseController
// ── Service Items ────────────────────────────────────────────────────────
/** همه‌ی سرویس‌های owner در همه‌ی بخش‌ها — برای انتخاب/جستجوی سراسری. */
#[Route('/api/v1/service-items', methods: ['GET'])]
public function listAllItems(#[CurrentUser] User $user): JsonResponse
{
[$entityType, $entityId] = $this->resolveEntity($user);
$items = array_map(
fn(ServiceItem $i) => $i->toArray(),
$this->itemRepo->findByEntity($entityType, $entityId)
);
return $this->success($items);
}
#[Route('/api/v1/service-items/{sectionUuid}', methods: ['GET'])]
public function listItems(string $sectionUuid, #[CurrentUser] User $user): JsonResponse
{
@@ -29,6 +29,18 @@ class ServiceItemRepository extends ServiceEntityRepository
->getResult();
}
/** همه‌ی سرویس‌های یک owner (در همه‌ی بخش‌ها) — برای انتخاب/جستجوی سراسری. */
public function findByEntity(string $entityType, int $entityId): array
{
return $this->createQueryBuilder('i')
->join('i.section', 's')
->where('s.entityType = :type')->setParameter('type', $entityType)
->andWhere('s.entityId = :id')->setParameter('id', $entityId)
->orderBy('i.name', 'ASC')
->getQuery()
->getResult();
}
/**
* Count services per section in a single query (avoids N+1 in the section list).
*
+3 -7
View File
@@ -982,13 +982,9 @@ class PatientController extends BaseController
$data['invoice_uuid'] = $invoice?->getUuid();
$data['invoice_status'] = $invoice?->getStatus();
if ($data['is_paid']) {
$data['patient_debt_rials'] = 0;
} else {
// سهم بیمار (از فاکتور در صورت وجود) منهای تخفیف تسویه و پرداخت‌های جزئی
$share = $invoice !== null ? $invoice->getPatientRials() : $session->getFinalPriceRials();
$data['patient_debt_rials'] = max(0, $share - $session->getDiscountRials() - $session->getPaidTotalRials());
}
// مانده‌ی بدهی همیشه از خودِ مراجعه (سازگار با is_paid): مبلغ نهایی منهای تخفیف
// و پرداخت‌ها. اگر مراجعه پس از صدور فاکتور ویرایش شود، همین منبعِ واحد ملاک است.
$data['patient_debt_rials'] = $session->getRemainingRials();
return $data;
}