diff --git a/assets/admin/components/NewAppointmentDrawer.tsx b/assets/admin/components/NewAppointmentDrawer.tsx index 9dcf0664..73551fb7 100644 --- a/assets/admin/components/NewAppointmentDrawer.tsx +++ b/assets/admin/components/NewAppointmentDrawer.tsx @@ -9,7 +9,7 @@ import PersianDateInput from './ui/PersianDateInput'; import PriceInput from './ui/PriceInput'; import SearchableSelect from './ui/SearchableSelect'; import { WalletChargeLink } from './AppointmentActions'; -import { tehranWallClockToUnix, tomanToRial, toEnglishDigits, sanitizeMobileInput } from '../lib/utils'; +import { tehranWallClockToUnix, tomanToRial, rialToToman, toEnglishDigits, sanitizeMobileInput } from '../lib/utils'; interface Option { uuid: string; name?: string; full_name?: string } interface PatientRow { uuid: string; user_name?: string; user_mobile?: string; user_national_code?: string } @@ -108,6 +108,19 @@ export default function NewAppointmentDrawer({ doctorUuid, defaultDate, queryKey const [status, setStatus] = useState('pending'); const [note, setNote] = useState(''); + // ── هزینه ویزیت — الزامی بودن از تنظیمات «الزامی کردن هزینه ویزیت» (فیلد UI تومان، + // API ریالی). بدون این مقدار وقتی فلگ فعال است backend خطای ۴۲۲ می‌دهد. + const pricingQ = useQuery>({ + queryKey: ['insurance-pricing'], queryFn: () => api.get('/api/v1/insurance-pricing'), + }); + const freeVisit = (pricingQ.data as any)?.data?.free_visit_price_rials ?? 0; + const requireVisit = (pricingQ.data as any)?.data?.require_visit_price ?? false; + const [visitPriceToman, setVisitPriceToman] = useState(0); + const [visitPriceTouched, setVisitPriceTouched] = useState(false); + useEffect(() => { + if (!visitPriceTouched && freeVisit > 0) setVisitPriceToman(rialToToman(freeVisit)); + }, [freeVisit, visitPriceTouched]); + const effectiveName = pickedPatient?.user_name || name.trim(); const effectiveMobile = pickedPatient?.user_mobile || mobile.trim(); const effectiveNationalCode = (pickedPatient?.user_national_code || nationalCode).replace(/\D/g, ''); @@ -116,8 +129,9 @@ export default function NewAppointmentDrawer({ doctorUuid, defaultDate, queryKey : serviceMode ? (serviceUuids.length > 0 && !!pickedSlot) : (!!start && !!end); + const visitPriceValid = !requireVisit || visitPriceToman > 0; const valid = !!doctorUuid && !!date && effectiveName.length >= 2 && effectiveMobile.length >= 10 - && effectiveNationalCode.length === 10 && timingValid; + && effectiveNationalCode.length === 10 && timingValid && visitPriceValid; const create = useMutation({ mutationFn: async () => { @@ -136,6 +150,7 @@ export default function NewAppointmentDrawer({ doctorUuid, defaultDate, queryKey ...(serviceMode ? { service_item_uuids: serviceUuids } : itemUuid ? { service_item_uuid: itemUuid } : {}), ...(staffUuid ? { staff_uuid: staffUuid } : {}), ...(depositRequired ? { deposit_required: true, deposit_amount_rials: tomanToRial(depositToman) } : {}), + ...(visitPriceToman > 0 ? { visit_price_rials: tomanToRial(visitPriceToman) } : {}), ...(note.trim() ? { note: note.trim() } : {}), }; const res: any = await api.post('/api/v1/my/appointment', payload); @@ -340,6 +355,17 @@ export default function NewAppointmentDrawer({ doctorUuid, defaultDate, queryKey )} +
هزینه ویزیت:
+ +
+ { setVisitPriceToman(v); setVisitPriceTouched(true); }} /> +
+ {requireVisit && visitPriceToman <= 0 && ( +
هزینه ویزیت الزامی است
+ )} +
s.primaryRole); const createEndpoint = role === 'admin' ? '/api/v1/admin/appointment' : '/api/v1/my/appointment'; + // هزینه ویزیت — الزامی بودن از تنظیمات «الزامی کردن هزینه ویزیت». فیلد UI تومان، + // API ریالی (visit_price_rials). بدون این مقدار، وقتی فلگ فعال است backend خطای ۴۲۲ می‌دهد. + const pricingQ = useQuery<{ data: { free_visit_price_rials: number; require_visit_price: boolean } }>({ + queryKey: ['insurance-pricing'], + queryFn: () => api.get('/api/v1/insurance-pricing'), + }); + const freeVisit = (pricingQ.data as any)?.data?.free_visit_price_rials ?? 0; + const requireVisit = (pricingQ.data as any)?.data?.require_visit_price ?? false; + const [visitPriceToman, setVisitPriceToman] = useState(0); + const [visitPriceTouched, setVisitPriceTouched] = useState(false); + useEffect(() => { + if (!visitPriceTouched && freeVisit > 0) setVisitPriceToman(rialToToman(freeVisit)); + }, [freeVisit, visitPriceTouched]); + const mobileValid = /^09\d{9}$/.test(mobile); const serviceTimingValid = !serviceMode || (pick.serviceUuids.length > 0 && !!pick.slot); // یک بیمارِ یافت‌شده که کد ملی دارد، بدون فرم اضافی قابل استفاده است. @@ -127,7 +142,8 @@ export function NewAppointmentModal({ const effectiveName = foundWithNationalCode ? (lookup?.name ?? '') : patientName.trim(); const effectiveNationalCode = foundWithNationalCode ? (lookup?.national_code ?? '') : nationalCode; const detailsValid = effectiveName.length >= 2 && effectiveNationalCode.length === 10; - const isValid = mobileValid && (foundWithNationalCode || (needsDetails && detailsValid)) && serviceTimingValid; + const visitPriceValid = !requireVisit || visitPriceToman > 0; + const isValid = mobileValid && (foundWithNationalCode || (needsDetails && detailsValid)) && serviceTimingValid && visitPriceValid; const search = useMutation({ mutationFn: () => api.get(`/api/v1/my/appointment/patient-lookup?mobile=${encodeURIComponent(mobile)}`), @@ -149,6 +165,7 @@ export function NewAppointmentModal({ patient_name: effectiveName, patient_national_code: effectiveNationalCode, ...(serviceMode ? { service_item_uuids: pick.serviceUuids } : {}), + ...(visitPriceToman > 0 ? { visit_price_rials: tomanToRial(visitPriceToman) } : {}), }), onSuccess: () => { toast.success('نوبت با موفقیت ثبت شد'); @@ -271,6 +288,20 @@ export function NewAppointmentModal({ )} +
+ + { setVisitPriceToman(v); setVisitPriceTouched(true); }} + style={{ ...inputSx, direction: 'ltr' }} + /> + {requireVisit && visitPriceToman <= 0 && ( +
هزینه ویزیت الزامی است
+ )} +
+