docs: update README with comprehensive deposit system documentation
- Add new '💰 Deposit System' section with full business logic
- Document 24-hour late cancellation rule
- Explain optional forgive_no_show boolean for admin forgiveness
- Explain optional enforce_deposits boolean for admin booking override
- Document no-show accumulation (2+ in 6 months = 3 deposits)
- Document deposit reduction on payment
- Add API examples and implementation files reference
- Update payment_type enum to include all types (deposit, full, tip, balance, partial)
- Add deposit examples showing different scenarios
- Update Simplified Deposits status line with current behavior
- Remove outdated 48h notice reference
This commit is contained in:
@@ -319,7 +319,7 @@ docker compose exec backend sh
|
||||
| Image Metadata Stripping | ✅ | ❌ | EXIF/GPS stripped on upload via `imaging` library |
|
||||
| Profile Pictures | ✅ | ✅ | Upload to separate bucket, cropper, circular display, CalDAV sync |
|
||||
| Auto-Booking Status | ✅ | ✅ | Auto-transition: confirmed → in_progress → completed based on time |
|
||||
| Simplified Deposits | ✅ | ⚠️ | `deposits_required` INT on users table (3 default), 48h notice, reduces on payment |
|
||||
|| Simplified Deposits | ✅ | ⚠️ | **24h late cancellation rule**: < 24h = no-show (+3 deposits, optional forgiveness), ≥ 24h = normal cancellation. Admin can bypass checks. Reduces by 1 per payment on completed booking.
|
||||
| Contact Page | ✅ | ✅ | Dynamic data from first admin user via `/api/contact` endpoint |
|
||||
| Email Verification | ✅ | ❌ | Verification codes, generate/check endpoints |
|
||||
| Calendar Export | ✅ | ⚠️ | ICS download endpoint, Add to Calendar button (backend complete) |
|
||||
@@ -373,6 +373,147 @@ All three flows integrate with the **holiday/exceptional hours** system to preve
|
||||
|
||||
---
|
||||
|
||||
## 💰 Deposit System
|
||||
|
||||
### Overview
|
||||
|
||||
The deposit system is a **simplified user-level tracking mechanism** that enforces a 3-deposit requirement before new bookings are allowed. It's designed to reduce booking abandonment and protect against chronic no-shows.
|
||||
|
||||
**Key Concepts**:
|
||||
- `deposits_required`: Integer (0-3) on users table tracking outstanding deposit obligations
|
||||
- `deposit_required`: Boolean on bookings table, snapshotted at creation time
|
||||
- `deposit_amount`: Calculated as 20% of booking total for display
|
||||
- `deposit_paid`: True when pre-start payments cover the deposit amount
|
||||
|
||||
### Cancellation Rules (Late = < 24 Hours)
|
||||
|
||||
| Scenario | Time Until Start | Forgiveness | Result Status | Penalty | User Blocked? |
|
||||
|----------|------------------|-------------|---------------|---------|---------------|
|
||||
| **Late cancellation (normal)** | < 24h | No/Omitted | `no_show` | +3 deposits (resets to 3, not +=) | Yes* |
|
||||
| **Late cancellation (forgiven)** | < 24h | Yes | `client_cancelled` | None | No |
|
||||
| **Normal cancellation** | ≥ 24h | Any | `client_cancelled` | None | No |
|
||||
| **Pending cancellation** | Any | Any | Deleted | None | No |
|
||||
|
||||
*Assuming `deposits_required` > 0 after penalty
|
||||
|
||||
### API: User Cancellation
|
||||
|
||||
**Endpoint**: `PUT /api/bookings/{id}/cancel`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"reason": "no_show",
|
||||
"forgive_no_show": true // Optional: true = forgive penalty, false/omitted = enforce penalty
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- `< 24h` without forgiveness → `status = no_show`, `deposits_required = 3`
|
||||
- `< 24h` with forgiveness → `status = client_cancelled`, no penalty
|
||||
- `≥ 24h` → `status = client_cancelled`, no penalty (always)
|
||||
|
||||
### Admin Booking Creation with Deposit Control
|
||||
|
||||
**Endpoint**: `POST /api/admin/bookings`
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"user_id": "USR123",
|
||||
"start_time": "2026-03-10T10:00:00Z",
|
||||
"service_ids": ["SVC1"],
|
||||
"enforce_deposits": false // Optional: true (default) = enforce checks, false = bypass checks
|
||||
}
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- `enforce_deposits = true` (default): Apply one-active-booking limit if `deposits_required > 0`
|
||||
- `enforce_deposits = false`: Bypass all deposit checks, allow multiple active bookings
|
||||
|
||||
**Use Cases**:
|
||||
- `true`: Standard workflow, ensure users clear deposits before booking again
|
||||
- `false`: Emergency/special cases where admin needs to override deposit restrictions
|
||||
|
||||
### No-Show Accumulation
|
||||
|
||||
**Rule**: 2+ unforgiven no-shows in rolling 6 months → `deposits_required = 3`
|
||||
|
||||
**Trigger**: Automatic via `ApplyDepositsIfNeeded()` when:
|
||||
1. User has status = `no_show` (without forgiveness flag)
|
||||
2. Booking occurred within last 6 months
|
||||
3. Not yet marked as "forgiven" in `forgiven_no_shows` table
|
||||
4. Count ≥ 2
|
||||
|
||||
**Consequence**: User blocked from new bookings (one-active-booking limit enforced)
|
||||
|
||||
### Deposit Reduction
|
||||
|
||||
**Rule**: When booking transitions to `completed` with ≥ 1 payment → `deposits_required -= 1`
|
||||
|
||||
**Logic**:
|
||||
- Minimum: 0 (never negative)
|
||||
- Reduction happens once per booking (not per payment)
|
||||
- User must complete bookings with payments to clear all 3 deposits
|
||||
|
||||
### Deposit Fields in Booking API Response
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "BK123",
|
||||
"deposit_required": true, // Snapshot: was deposit required at booking time?
|
||||
"deposit_amount": 24.50, // 20% of total_amount
|
||||
"deposit_paid": false, // Sum of pre-start payments >= deposit_amount?
|
||||
"deposit_deadline": "2026-03-09T10:00:00Z", // start_time - 24 hours
|
||||
"total_amount": 122.50,
|
||||
"amount_paid": 0.00,
|
||||
"amount_due": 122.50
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Files
|
||||
|
||||
| File | Changes | Details |
|
||||
|------|---------|----------|
|
||||
| `backend/handlers/bookings/bookings.go` | Cancellation logic | 24h threshold, optional `forgive_no_show` boolean |
|
||||
| `backend/handlers/bookings/manage.go` | Admin booking creation | Optional `enforce_deposits` boolean, deposit checks |
|
||||
| `backend/handlers/bookings/manage.go` | Removed function | `ForgiveNoShowsForUser()` (now per-cancellation) |
|
||||
|
||||
### Database Schema
|
||||
|
||||
| Table | Column | Type | Purpose |
|
||||
|-------|--------|------|----------|
|
||||
| `users` | `deposits_required` | INT | Outstanding deposit count (0-3) |
|
||||
| `bookings` | `deposit_required` | BOOLEAN | Snapshot of requirement at booking time |
|
||||
| `payments` | `payment_type` | ENUM | Includes `deposit`, `full`, `tip`, `balance`, `partial` |
|
||||
| `forgiven_no_shows` | `booking_id` | CHAR(12) | Tracks forgiven no-shows for 6-month accumulation |
|
||||
|
||||
### Deposit Examples
|
||||
|
||||
**Example 1: New User Books**
|
||||
```
|
||||
User: deposits_required = 0
|
||||
→ Booking: deposit_required = false
|
||||
→ Response: deposit_amount shown, but deposit_paid always false
|
||||
```
|
||||
|
||||
**Example 2: User with Deposits Late Cancels**
|
||||
```
|
||||
User: deposits_required = 1
|
||||
Cancels < 24h without forgiveness
|
||||
→ Result: deposits_required = 3 (reset, not incremented)
|
||||
→ User blocked from new bookings
|
||||
```
|
||||
|
||||
**Example 3: Admin Overrides Deposits**
|
||||
```
|
||||
Admin creates booking with enforce_deposits = false
|
||||
User has deposits_required = 2 + active booking
|
||||
→ Result: Booking created successfully
|
||||
→ Deposit check bypassed
|
||||
```
|
||||
---
|
||||
|
||||
## 🔍 Helpful Greps
|
||||
|
||||
### All Three Booking Flows
|
||||
@@ -498,9 +639,9 @@ grep -rn "console\.log" frontend/src/ --include="*.svelte" --include="*.ts"
|
||||
|------|--------|
|
||||
| `account_role` | `user`, `admin` |
|
||||
| `account_type` | `standard`, `vip`, `guest` |
|
||||
| `booking_status` | `pending`, `confirmed`, `in_progress`, `completed`, `client_cancelled`, `we_cancelled`, `re-schedule`, `no_show`, `no_deposit` |
|
||||
| `payment_type` | `deposit`, `full`, `refund` |
|
||||
| `payment_method` | `cash`, `card`, `bank_transfer`, `square` |
|
||||
|| `booking_status` | `pending`, `confirmed`, `in_progress`, `completed`, `client_cancelled`, `we_cancelled`, `re-schedule`, `no_show`, `no_deposit` |
|
||||
|| `payment_type` | `deposit`, `full`, `tip`, `balance`, `partial` |
|
||||
|| `payment_method` | `online_square`, `in_person_card`, `cash`, `giftcard`, `discount` |
|
||||
| `payment_status` | `pending`, `completed`, `failed`, `refunded` |
|
||||
| `admin_notification_reason` | `pending_booking`, `cancelled_booking`, `rescheduled_booking`, `1_week_no_pay`, `1_month_no_pay`, `affiliate_claim`, `late_cancellation`, `no_deposit`, `deposit_paid` |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user