import { toast } from 'sonner'; import { formatRial, formatDateTime } from '../../lib/utils'; import type { SessionCardData } from '../SessionServiceCard'; import { METHOD_LABELS } from './PaymentStep'; import { useIssueInvoice } from '../../hooks/useIssueInvoice'; const primaryBtn: React.CSSProperties = { background: '#5559CE', color: '#fff', border: 'none', borderRadius: 4, height: 46, fontSize: 14, fontWeight: 500, cursor: 'pointer' }; const ghostBtn: React.CSSProperties = { background: 'transparent', color: '#5559CE', border: '1px solid #5559CE', borderRadius: 4, height: 46, fontSize: 14, fontWeight: 500, cursor: 'pointer' }; interface Props { session: SessionCardData; /** برای invalidate شدن لیست sessions بعد از صدور فاکتور. */ recordUuid?: string; onBack: () => void; onFinish: () => void; } /** * گام «جزییات» — پورت tauri Step3Final با دیتای واقعی: * خلاصه‌ی سرویس/هزینه/تخفیف + لیست پرداخت‌شده‌ها + مانده. مشترک بین * NewSessionPage (ویزارد سه‌گامه) و SessionPaymentPage (دوگامه). * «صدور فاکتور» فاکتور را واقعاً صادر می‌کند (create + finalize) و بعد onFinish. */ export default function DetailsStep({ session, recordUuid, onBack, onFinish }: Props) { const issueMut = useIssueInvoice(recordUuid); const issue = () => { if (session.invoice_uuid) { onFinish(); return; } issueMut.mutate(session.uuid, { onSuccess: () => onFinish(), onError: () => toast.error('صدور فاکتور ناموفق بود'), }); }; const finalPrice = session.final_price_rials ?? 0; const discountRials = session.discount_rials ?? 0; const debt = session.patient_debt_rials ?? 0; const payments = session.payments ?? []; const visitPrice = session.visit_price_rials ?? 0; const serviceNames = (session.services ?? []).map((s) => s.service_name || s.name).filter(Boolean) as string[]; if (visitPrice > 0) serviceNames.unshift('ویزیت'); // زمان مراجعه: زمان واقعی نوبت؛ در نبود آن، زمان ثبت پرونده. const visitAt = session.session_at ?? session.created_at ?? null; // آیتم‌های هزینه‌ی تفکیک‌شده: ویزیت + هر سرویس + کالای مصرفی. // کالای مصرفی هم باید بیاید چون سرور آن را در سهم بیمار می‌آورد // (PatientService::…$consumablesTotal)؛ نبودش ریز هزینه‌ها را با جمع ناسازگار می‌کرد. const costItems: Array<{ label: string; rials: number }> = []; if (visitPrice > 0) costItems.push({ label: 'ویزیت', rials: visitPrice }); (session.services ?? []).forEach((s) => { costItems.push({ label: (s.service_name || s.name) ?? 'سرویس', rials: s.line_total_rials ?? (s.price_rials ?? 0) * (s.quantity ?? 1), }); }); (session.consumables ?? []).forEach((c) => { costItems.push({ label: c.item_name ?? 'کالای مصرفی', rials: c.line_total_rials ?? 0, }); }); // خطوط بالا ناخالص‌اند (پیش از بیمه) ولی finalPrice سهمِ بیمار پس از بیمه است. // پس وقتی بیمه هست، جمعِ خطوط را ناخالص نشان بده و سهم بیمه را جدا کم کن. const grossTotal = session.gross_total_rials ?? finalPrice; const baseInsurance = session.base_insurance_rials ?? 0; const suppInsurance = session.supplementary_insurance_rials ?? 0; const hasInsurance = baseInsurance > 0 || suppInsurance > 0; return ( <> {/* گام جزییات — پورت tauri Step3Final با دیتای واقعی */}
{visitAt && (
تاریخ و ساعت مراجعه: {formatDateTime(visitAt)}
)}
{serviceNames.length ? serviceNames.join(' - ') : 'ویزیت'} {session.doctor_name || '—'}
{costItems.length > 0 && (
{costItems.map((it, i) => (
{it.label} {formatRial(it.rials)}
))} {hasInsurance && ( <>
جمع کل خدمات {formatRial(grossTotal)}
{baseInsurance > 0 && (
سهم بیمه پایه {formatRial(baseInsurance)}
)} {suppInsurance > 0 && (
سهم بیمه تکمیلی {formatRial(suppInsurance)}
)} )}
{hasInsurance ? 'سهم بیمار' : 'جمع کل'} {formatRial(finalPrice)}
)}
هزینه سرویس: {formatRial(finalPrice)} تخفیف: {formatRial(discountRials)}
پرداخت شده ها: {payments.length === 0 ? ( پرداختی ثبت نشده است ) : payments.map((p) => (
{METHOD_LABELS[p.method] ?? p.method} مبلغ: {formatRial(p.amount_rials)}
{(p.paid_at ?? p.created_at) && {formatDateTime(p.paid_at ?? p.created_at!)}} {p.created_by_name && ثبت: {p.created_by_name}}
))}
مبلغ باقی مانده: {formatRial(debt)}
); }