feat: early check-in / late checkout fees with room-level configuration

- DB migration 016: add early_checkin_fee, late_checkout_fee to rooms table
- Backend rooms: support new fee columns in POST/PATCH
- Backend hotel-settings GET: also returns check_in_time, check_out_time
- Room type + RoomPayload: add earlyCheckinFee, lateCheckoutFee fields
- RoomModal: add fee amount inputs in pricing section
- SettingsPage: add toggles for early_checkin_enabled / late_checkout_enabled
- BookingDetailPanel: on check-in shows fee warning + adds payment record if
  current time is before checkInTime and fee is set on the room; same logic
  for check-out after checkOutTime

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 19:24:36 +03:00
parent 9f1dd0c8c9
commit edd493e24e
8 changed files with 195 additions and 25 deletions

View File

@@ -72,6 +72,8 @@ export function RoomModal({ open, room, categories = [], onClose, onSave, onDele
hourlyRate: room?.hourlyRate ?? 1000,
categoryId: room?.categoryId ?? '',
description: room?.description ?? '',
earlyCheckinFee: room?.earlyCheckinFee ?? '',
lateCheckoutFee: room?.lateCheckoutFee ?? '',
})
const { amenities: allAmenities } = useAmenities()
@@ -148,6 +150,8 @@ export function RoomModal({ open, room, categories = [], onClose, onSave, onDele
beds: beds.length > 0 ? beds : undefined,
extraPlace: extraPlace.enabled ? extraPlace : undefined,
childPolicy: childPolicy.enabled ? childPolicy : undefined,
earlyCheckinFee: form.earlyCheckinFee !== '' ? Number(form.earlyCheckinFee) : undefined,
lateCheckoutFee: form.lateCheckoutFee !== '' ? Number(form.lateCheckoutFee) : undefined,
})
}
@@ -303,6 +307,34 @@ export function RoomModal({ open, room, categories = [], onClose, onSave, onDele
)}
</div>
{/* Early check-in / late checkout fees */}
<div className="rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
<div className="px-4 py-3 bg-slate-50 dark:bg-slate-700/40 border-b border-slate-200 dark:border-slate-700">
<p className="text-sm font-medium text-slate-700 dark:text-slate-300">Тарифы за ранний/поздний заезд</p>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Применяются если включено в настройках отеля</p>
</div>
<div className="p-4 grid grid-cols-2 gap-3">
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Ранний заезд </label>
<input
type="number" min={0} className="input"
placeholder="0 = бесплатно"
value={form.earlyCheckinFee}
onChange={e => set('earlyCheckinFee', e.target.value)}
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Поздний выезд </label>
<input
type="number" min={0} className="input"
placeholder="0 = бесплатно"
value={form.lateCheckoutFee}
onChange={e => set('lateCheckoutFee', e.target.value)}
/>
</div>
</div>
</div>
{/* Amenities */}
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">Удобства</label>