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
@@ -90,6 +90,11 @@ export default function CreateStep({ recordUuid, profile, onCreated, onCancel, e
const { data: sectionsData } = useQuery<ApiResponse<ServiceSection[]>>({
queryKey: ['service-sections'], queryFn: () => api.get('/api/v1/service-sections'),
});
// همه‌ی سرویس‌ها (سراسری) تا بتوان بدون انتخاب بخش هم جستجو و انتخاب کرد.
const { data: allItemsData } = useQuery<ApiResponse<ServiceItem[]>>({
queryKey: ['service-items-all'],
queryFn: () => api.get('/api/v1/service-items'),
});
const { data: itemsData } = useQuery<ApiResponse<ServiceItem[]>>({
queryKey: ['service-items-for-session', sectionUuid],
queryFn: () => api.get(`/api/v1/service-items/${sectionUuid}`),
@@ -144,7 +149,8 @@ export default function CreateStep({ recordUuid, profile, onCreated, onCancel, e
const suppOpts = contracts.filter(c => c.insurance_kind === 'supplementary').map(c => ({ value: String(c.insurance_id), label: c.insurance_name ?? `#${c.insurance_id}` }));
const sectionOptions = (sectionsData?.data ?? []).map(s => ({ value: s.uuid, label: s.name }));
const serviceItems = (itemsData?.data ?? []).filter(i => i.active);
// با انتخاب بخش، فقط سرویس‌های همان بخش؛ بدون بخش، همه‌ی سرویس‌ها (قابل جستجو).
const serviceItems = (sectionUuid ? (itemsData?.data ?? []) : (allItemsData?.data ?? [])).filter(i => i.active);
const itemOptions = serviceItems.map(i => ({ value: i.uuid, label: i.name }));
const currentItem = serviceItems.find(i => i.uuid === itemUuid);
@@ -325,7 +331,7 @@ export default function CreateStep({ recordUuid, profile, onCreated, onCancel, e
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
<div style={{ width: '50%' }}>
<span style={fieldLabel}>انتخاب سرویس</span>
<SearchableSelect inputId="service-select" options={itemOptions} value={itemUuid} onChange={v => setItemUuid(v ? String(v) : '')} placeholder="انتخاب کنید..." isDisabled={!sectionUuid} />
<SearchableSelect inputId="service-select" options={itemOptions} value={itemUuid} onChange={v => setItemUuid(v ? String(v) : '')} placeholder="جستجو و انتخاب سرویس..." />
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 4, marginTop: 20, flexShrink: 0 }}>
<FilesServiceAddCard color="#6B7280" />