Files
clinicpro/assets/admin/hooks/useMyPayments.ts
T

112 lines
4.0 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { api } from '../lib/api';
import type { ApiResponse, PaginatedResponse } from '../lib/api';
/**
* وضعیت پرداخت صورتحساب — مشتق از مجموع پرداخت‌های مراجعه در برابر سهم بیمار،
* نه ستون `invoices.status` (که فقط چرخه‌ی حیات را نگه می‌دارد).
*/
export type PaymentRowStatus = 'paid' | 'partial' | 'unsettled';
/** One recorded invoice on the flat payments list (node 1). */
export interface PaymentRow {
invoice_uuid: string;
patient_uuid: string;
patient_name: string | null;
national_code: string | null;
issued_at: number;
amount_rials: number;
paid_rials: number;
status: PaymentRowStatus;
}
export interface PaymentFilters {
page: number;
national_code?: string;
status?: string; // paid | partial | unsettled
from?: number; // unix seconds
to?: number; // unix seconds
}
/** همان وضعیت مشتق‌شده، روی جدول جزئیات بیمار (node 2). */
export type InvoiceRowStatus = PaymentRowStatus;
export interface PatientInvoiceRow {
uuid: string;
number: number;
issued_at: number;
total_rials: number;
patient_rials: number;
paid_rials: number;
status: InvoiceRowStatus;
service_title: string | null;
items: Array<{ uuid: string; title: string; quantity: number; total_rials: number; patient_rials: number }>;
payments: InvoicePaymentRow[];
}
/** یک پرداخت ثبت‌شده روی مراجعه‌ی همان صورتحساب. */
export interface InvoicePaymentRow {
method: string;
amount_rials: number;
paid_at: number;
created_by_name: string | null;
}
export interface PatientInvoicesPayload {
patient: { uuid: string; name: string | null; national_code: string | null };
data: PatientInvoiceRow[];
summary: PaymentsSummary;
meta: { totalRecords: number; totalPages: number; currentPage: number };
}
const LIMIT = 20;
/** Node 1 — flat, paginated list of the current tenant's recorded invoices. */
export function usePayments(filters: PaymentFilters) {
const qs = new URLSearchParams({ page: String(filters.page), limit: String(LIMIT) });
if (filters.national_code) qs.set('national_code', filters.national_code);
if (filters.status) qs.set('status', filters.status);
if (filters.from) qs.set('from', String(filters.from));
if (filters.to) qs.set('to', String(filters.to));
return useQuery<PaginatedResponse<PaymentRow>>({
queryKey: ['payments', filters],
queryFn: () => api.get(`/api/v1/my/billing/payments?${qs.toString()}`),
// ورود دوباره به صفحه همیشه داده را تازه می‌کند (پرداخت‌ها لحظه‌ای عوض می‌شوند).
refetchOnMount: 'always',
});
}
export interface PaymentsSummary {
total_rials: number;
paid_rials: number;
unsettled_rials: number;
invoices_count: number;
}
/** Summary over the same filters as the list, so the stat cards match the table. */
export function usePaymentsSummary(filters: Omit<PaymentFilters, 'page'>) {
const qs = new URLSearchParams();
if (filters.national_code) qs.set('national_code', filters.national_code);
if (filters.status) qs.set('status', filters.status);
if (filters.from) qs.set('from', String(filters.from));
if (filters.to) qs.set('to', String(filters.to));
return useQuery<ApiResponse<PaymentsSummary>>({
queryKey: ['payments-summary', filters],
queryFn: () => api.get(`/api/v1/my/billing/payments/summary?${qs.toString()}`),
refetchOnMount: 'always',
});
}
/** Node 2 — a single patient's recorded invoices (header + paginated list). */
export function usePatientInvoices(patientUuid: string | undefined, page: number) {
return useQuery<ApiResponse<PatientInvoicesPayload>>({
queryKey: ['patient-invoices', patientUuid, page],
queryFn: () => api.get(`/api/v1/my/billing/patients/${patientUuid}/invoices?page=${page}&limit=${LIMIT}`),
enabled: !!patientUuid,
});
}
export const MY_PAYMENTS_LIMIT = LIMIT;