feat(migrations): update franchise to percentage in tenant_insurances and tenant_service_coverage

- Changed franchise_rials to franchise_percent in tenant_insurances and tenant_service_coverage tables.
- Reset old rial values to 0/NULL as they are not convertible to percentage.

feat(command): add SeedInsuranceScenarioCommand for seeding insurance data

- Implemented a command to seed supplementary insurance contracts, patients, and claims for a specified doctor.
- Includes functionality for purging existing scenario data and generating new entries with predefined contracts and patient scenarios.
This commit is contained in:
hamed
2026-07-29 13:28:59 +03:30
parent 11b4dcdd34
commit 4f4bce9fe2
31 changed files with 1497 additions and 137 deletions
@@ -19,14 +19,14 @@ interface CoverageRow {
service_item_uuid: string | null;
covered: boolean;
coverage_percent: number | null;
franchise_rials: number | null;
franchise_percent: number | null;
ceiling_rials: number | null;
}
interface Draft {
covered: boolean;
coverage_percent: number | null;
franchise_rials: number | null;
franchise_percent: number | null;
ceiling_rials: number | null;
}
@@ -46,20 +46,22 @@ function ContractCard({ contract, item }: { contract: TenantInsurance; item: Ser
const rows = (data as any)?.data?.data as CoverageRow[] | undefined;
const existing = rows?.find((r) => r.service_item_uuid === item.uuid);
const [draft, setDraft] = useState<Draft>({ covered: true, coverage_percent: null, franchise_rials: null, ceiling_rials: null });
// متن خام فیلد درصد جدا از مقدار عددی نگه داشته می‌شود تا کاربر بتواند فیلد را خالی کند.
const [draft, setDraft] = useState<Draft>({ covered: true, coverage_percent: null, franchise_percent: null, ceiling_rials: null });
// متن خام فیلدهای درصد جدا از مقدار عددی نگه داشته می‌شود تا کاربر بتواند فیلد را خالی کند.
const [percentText, setPercentText] = useState('');
const [franchiseText, setFranchiseText] = useState('');
useEffect(() => {
setDraft(existing
? {
covered: existing.covered,
coverage_percent: existing.coverage_percent,
franchise_rials: existing.franchise_rials,
franchise_percent: existing.franchise_percent,
ceiling_rials: existing.ceiling_rials,
}
: { covered: true, coverage_percent: null, franchise_rials: null, ceiling_rials: null });
: { covered: true, coverage_percent: null, franchise_percent: null, ceiling_rials: null });
setPercentText(existing?.coverage_percent == null ? '' : String(existing.coverage_percent));
setFranchiseText(existing?.franchise_percent == null ? '' : String(existing.franchise_percent));
}, [existing]);
const saveMut = useMutation({
@@ -68,7 +70,7 @@ function ContractCard({ contract, item }: { contract: TenantInsurance; item: Ser
service_item_uuid: item.uuid,
covered: draft.covered,
coverage_percent: draft.coverage_percent,
franchise_rials: draft.franchise_rials,
franchise_percent: draft.franchise_percent,
ceiling_rials: draft.ceiling_rials,
}),
onSuccess: () => {
@@ -144,14 +146,19 @@ function ContractCard({ contract, item }: { contract: TenantInsurance; item: Ser
/>
</div>
<div style={{ minWidth: 0 }}>
<label className="field-label">فرانشیز</label>
<PriceInput
className="input"
style={{ height: 40 }}
value={draft.franchise_rials ?? 0}
onChange={(v) => setDraft((d) => ({ ...d, franchise_rials: v || null }))}
placeholder="۰"
min={0}
<label className="field-label">فرانشیز (درصد)</label>
<input
type="text" inputMode="numeric" dir="ltr" className="input"
style={{ height: 40, textAlign: 'left' }}
aria-label="فرانشیز درصد خدمت"
value={franchiseText}
placeholder="بدون فرانشیز"
onChange={(e) => {
const digits = digitsOnly(e.target.value, 3);
setFranchiseText(digits);
setDraft((d) => ({ ...d, franchise_percent: parseUserNumberClamped(digits, 0, 100) }));
}}
onBlur={() => setFranchiseText(draft.franchise_percent == null ? '' : String(draft.franchise_percent))}
/>
</div>
<div style={{ minWidth: 0 }}>