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
+15 -10
View File
@@ -64,11 +64,13 @@ export default function InvoiceSummaryModal({ invoiceUuid, onClose }: { invoiceU
const session = inv?.session ?? null;
const paid = inv?.status === 'paid';
// با session: مبالغ واقعی پرداخت؛ بدون آن (فاکتور قدیمی): heuristic قبلی.
// با session: مبالغ واقعی پرداخت (مبلغ نهایی منهای تخفیف و پرداخت‌ها)؛ بدون آن heuristic قبلی.
const remaining = session
? session.final_price_rials - session.paid_total_rials
? Math.max(0, session.final_price_rials - (session.discount_rials ?? 0) - session.paid_total_rials)
: inv ? (paid ? 0 : inv.patient_rials) : 0;
const paidAmount = session ? session.paid_total_rials : inv ? inv.total_rials - remaining : 0;
// وضعیت واقعی پرداخت (مستقل از وضعیت فریزشده‌ی فاکتور): تسویه‌شده اگر مانده صفر.
const statusLabel = session ? (remaining <= 0 ? 'تسویه شده' : 'بدهکار') : (inv ? (STATUS_LABEL[inv.status] ?? inv.status) : '');
return (
<Modal open={!!invoiceUuid} onClose={onClose} title="خلاصه فاکتور" size="xl">
@@ -82,7 +84,7 @@ export default function InvoiceSummaryModal({ invoiceUuid, onClose }: { invoiceU
rows={[[
formatDate(session?.session_at ?? inv.issued_at),
session?.paid_at ? formatDate(session.paid_at) : paid ? formatDate(inv.issued_at) : '—',
STATUS_LABEL[inv.status] ?? inv.status,
<span style={{ color: remaining > 0 ? '#d32f2f' : '#388e3c', fontWeight: 600 }}>{statusLabel}</span>,
]]}
/>
<SectionTable
@@ -125,13 +127,16 @@ export default function InvoiceSummaryModal({ invoiceUuid, onClose }: { invoiceU
title="پرداختی ها"
cols={['ردیف', 'شیوه پرداخت', 'مبلغ', 'تاریخ و ساعت', 'ثبت‌کننده']}
rows={session.payments.length
? session.payments.map((p, i) => [
i + 1,
METHOD_LABELS[p.method] ?? p.method,
formatRial(p.amount_rials),
p.paid_at ? formatDateTime(p.paid_at) : '-',
p.created_by_name ?? '-',
])
? [
...session.payments.map((p, i) => [
i + 1,
METHOD_LABELS[p.method] ?? p.method,
formatRial(p.amount_rials),
p.paid_at ? formatDateTime(p.paid_at) : '-',
p.created_by_name ?? '-',
]),
['', <span style={{ fontWeight: 700 }}>مجموع پرداختیها</span>, <span style={{ fontWeight: 700 }}>{formatRial(session.paid_total_rials)}</span>, '', ''],
]
: [['-', '-', '-', '-', '-']]}
/>
)}