fix(admin): send visit_price_rials from appointment create modal & drawer
NewAppointmentModal and NewAppointmentDrawer never read the require-visit- price setting nor sent visit_price_rials, so booking failed with 422 "هزینه ویزیت الزامی است" when the flag was on. Add a visit-price field (prefilled from the free-visit price), require it when the flag is set, and include visit_price_rials in the POST payload, matching AppointmentCreatePage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,8 @@ import { toast } from 'sonner';
|
||||
import { api } from '../lib/api';
|
||||
import type { PaginatedResponse, ApiResponse } from '../lib/api';
|
||||
import type { Appointment } from '../types';
|
||||
import { formatDate, toGregorianDate, formatTime, toEnglishDigits, sanitizeMobileInput } from '../lib/utils';
|
||||
import { formatDate, toGregorianDate, formatTime, toEnglishDigits, sanitizeMobileInput, rialToToman, tomanToRial } from '../lib/utils';
|
||||
import PriceInput from '../components/ui/PriceInput';
|
||||
import { useAuthStore } from '../stores/authStore';
|
||||
import Pagination from '../components/ui/Pagination';
|
||||
import AppointmentFiltersModal, { applyAppointmentFilters, EMPTY_FILTERS } from '../components/AppointmentFiltersModal';
|
||||
@@ -118,6 +119,20 @@ export function NewAppointmentModal({
|
||||
const role = useAuthStore(s => 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({
|
||||
</>
|
||||
)}
|
||||
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<label style={labelSx}>
|
||||
هزینه ویزیت (تومان){requireVisit && <span style={{ color: 'var(--danger)' }}> *</span>}
|
||||
</label>
|
||||
<PriceInput
|
||||
value={visitPriceToman}
|
||||
onChange={(v) => { setVisitPriceToman(v); setVisitPriceTouched(true); }}
|
||||
style={{ ...inputSx, direction: 'ltr' }}
|
||||
/>
|
||||
{requireVisit && visitPriceToman <= 0 && (
|
||||
<div style={{ fontSize: 12, color: 'var(--danger)', marginTop: 6 }}>هزینه ویزیت الزامی است</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
||||
<button className="btn sm" onClick={onClose}>انصراف</button>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user