fix: issue invoice on demand so view-invoice buttons actually work
The admin SPA never called POST /billing/invoices, so every session had invoice_uuid = null and the view-invoice buttons on the patient services tab (and MyPatients visit modal) were permanently disabled. Add a shared useIssueInvoice hook (idempotent create + finalize when draft) and wire it into: - DetailsStep: the wizard's final step now really issues the invoice - SessionServiceCard / PatientDetailPage: clicking view-invoice on a session without an invoice issues it first, then opens the summary - MyPatientsPage visit modal: same, replacing the dead disabled button Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ import {
|
||||
} from "../lib/patientForm";
|
||||
import type { ApiResponse, PaginatedResponse } from "../lib/api";
|
||||
import { api } from "../lib/api";
|
||||
import { useIssueInvoice } from "../hooks/useIssueInvoice";
|
||||
import {
|
||||
formatDate,
|
||||
formatDateTime,
|
||||
@@ -210,6 +211,16 @@ function MyPatientsPageInner() {
|
||||
>("visits");
|
||||
const [expandedVisit, setExpandedVisit] = useState<string | null>(null);
|
||||
const [invoiceUuid, setInvoiceUuid] = useState<string | null>(null);
|
||||
|
||||
// «مشاهده فاکتور»: نبودِ invoice_uuid یعنی اول صادر شود (idempotent create + finalize).
|
||||
const issueInvoiceMut = useIssueInvoice(selectedRecord?.uuid);
|
||||
const viewInvoice = (s: PatientSession) => {
|
||||
if (s.invoice_uuid) { setExpandedVisit(null); setInvoiceUuid(s.invoice_uuid); return; }
|
||||
issueInvoiceMut.mutate(s.uuid, {
|
||||
onSuccess: (iv) => { setExpandedVisit(null); setInvoiceUuid(iv); },
|
||||
onError: () => toast.error("صدور فاکتور ناموفق بود"),
|
||||
});
|
||||
};
|
||||
const [sessionPage, setSessionPage] = useState(1);
|
||||
const [sessionModal, setSessionModal] = useState(false);
|
||||
const [editSession, setEditSession] = useState<PatientSession | null>(null);
|
||||
@@ -1340,7 +1351,8 @@ function MyPatientsPageInner() {
|
||||
onClose={() => setExpandedVisit(null)}
|
||||
settling={settleSessionMut.isPending}
|
||||
onSettle={(uuid) => settleSessionMut.mutate(uuid)}
|
||||
onViewInvoice={(uuid) => { setExpandedVisit(null); setInvoiceUuid(uuid); }}
|
||||
onViewInvoice={viewInvoice}
|
||||
issuing={issueInvoiceMut.isPending}
|
||||
onEdit={(s) => { setExpandedVisit(null); setEditSession(s); }}
|
||||
/>
|
||||
|
||||
@@ -1878,6 +1890,7 @@ function VisitDetailModal({
|
||||
session,
|
||||
onClose,
|
||||
settling,
|
||||
issuing,
|
||||
onSettle,
|
||||
onViewInvoice,
|
||||
onEdit,
|
||||
@@ -1885,8 +1898,10 @@ function VisitDetailModal({
|
||||
session: PatientSession | null;
|
||||
onClose: () => void;
|
||||
settling: boolean;
|
||||
issuing?: boolean;
|
||||
onSettle: (uuid: string) => void;
|
||||
onViewInvoice: (uuid: string) => void;
|
||||
/** کل session پاس میشود؛ نبودِ invoice_uuid یعنی caller باید فاکتور را صادر کند. */
|
||||
onViewInvoice: (session: PatientSession) => void;
|
||||
onEdit: (s: PatientSession) => void;
|
||||
}) {
|
||||
if (!session) return null;
|
||||
@@ -1916,11 +1931,9 @@ function VisitDetailModal({
|
||||
<PencilIcon style={{ width: 15 }} /> ویرایش
|
||||
</button>
|
||||
{paid ? (
|
||||
session.invoice_uuid ? (
|
||||
<button className="cp-btn-secondary" onClick={() => onViewInvoice(session.invoice_uuid!)}>مشاهده فاکتور</button>
|
||||
) : (
|
||||
<button className="cp-btn-secondary" disabled>پرداخت شده</button>
|
||||
)
|
||||
<button className="cp-btn-secondary" disabled={issuing} onClick={() => onViewInvoice(session)}>
|
||||
{issuing ? 'در حال صدور…' : 'مشاهده فاکتور'}
|
||||
</button>
|
||||
) : (
|
||||
<button className="cp-btn-primary" disabled={settling} onClick={() => onSettle(session.uuid)}>تکمیل پرداخت</button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user