feat: add booking workflow for service businesses (salons, clinics, massage)
This commit is contained in:
@@ -443,7 +443,8 @@ Provider availability depends on configuration. Common providers include:
|
||||
|---------|-------------|-------|
|
||||
| `/landing-page` | Create landing page CMS from HTML mockups | ollama-cloud/kimi-k2.5 |
|
||||
| `/commerce` | Create e-commerce site with products, cart, payments | qwen/qwen3-coder:free |
|
||||
| `/blog` | Create blog/CMS with posts, comments, SEO | qwen/qwen3-coder:free |
|
||||
| `/blog` | Create blog/CMS with posts, comments, SEO | qwen/qeen3-coder:free |
|
||||
| `/booking` | Create booking system for services/appointments | qwen/qwen3-coder:free |
|
||||
| `/pipeline` | Run full agent pipeline for issue | - |
|
||||
| `/feature` | Full feature development pipeline | qwen/qwen3-coder:free |
|
||||
| `/code` | Quick code generation | qwen/qwen3-coder:free |
|
||||
@@ -510,6 +511,22 @@ Provider availability depends on configuration. Common providers include:
|
||||
- RSS/Atom feeds and sitemap generation
|
||||
- Media library management
|
||||
|
||||
### Booking System Domain
|
||||
|
||||
**Location**: `.kilo/skills/booking/SKILL.md`
|
||||
|
||||
**Purpose**: Domain knowledge for building booking and appointment systems.
|
||||
|
||||
**Capabilities**:
|
||||
- Service management with categories and pricing
|
||||
- Staff scheduling and availability
|
||||
- Real-time slot calculation
|
||||
- Booking flow (service → staff → date/time → customer)
|
||||
- Status management (pending, confirmed, completed, cancelled)
|
||||
- Email/SMS notifications
|
||||
- Calendar integration (Google, iCal)
|
||||
- Revenue and utilization reports
|
||||
|
||||
---
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
1541
.kilo/commands/booking.md
Normal file
1541
.kilo/commands/booking.md
Normal file
File diff suppressed because it is too large
Load Diff
639
.kilo/skills/booking/SKILL.md
Normal file
639
.kilo/skills/booking/SKILL.md
Normal file
@@ -0,0 +1,639 @@
|
||||
---
|
||||
name: booking
|
||||
description: Booking domain knowledge - appointments, services, staff, schedules, reservations
|
||||
---
|
||||
|
||||
# Booking Skill
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides domain knowledge for building booking and appointment systems: services, staff, schedules, availability, reservations, notifications.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Service Management
|
||||
- Service categories
|
||||
- Service duration and pricing
|
||||
- Service variants (different durations)
|
||||
- Required resources
|
||||
|
||||
### Staff Management
|
||||
- Staff profiles
|
||||
- Role-based permissions
|
||||
- Specializations
|
||||
- Availability schedules
|
||||
|
||||
### Booking Flow
|
||||
- Service selection
|
||||
- Staff selection (optional)
|
||||
- Date/time selection
|
||||
- Customer details
|
||||
- Confirmation
|
||||
- Payment (optional)
|
||||
|
||||
### Scheduling
|
||||
- Working hours
|
||||
- Break times
|
||||
- Days off
|
||||
- Multi-location support
|
||||
- Timezone handling
|
||||
|
||||
### Notifications
|
||||
- Email confirmations
|
||||
- SMS reminders
|
||||
- Calendar sync (Google, iCal)
|
||||
- Push notifications
|
||||
|
||||
### Admin Features
|
||||
- Booking management
|
||||
- Availability editor
|
||||
- Reports and analytics
|
||||
- Customer management
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Services
|
||||
|
||||
```sql
|
||||
CREATE TABLE services (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category_id INTEGER,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
duration INTEGER NOT NULL, -- minutes
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
buffer_time INTEGER DEFAULT 0, -- minutes between appointments
|
||||
max_per_slot INTEGER DEFAULT 1,
|
||||
is_active BOOLEAN DEFAULT 1,
|
||||
image_url TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (category_id) REFERENCES service_categories(id)
|
||||
);
|
||||
|
||||
CREATE TABLE service_categories (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
image_url TEXT,
|
||||
sort_order INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE service_staff (
|
||||
service_id INTEGER NOT NULL,
|
||||
staff_id INTEGER NOT NULL,
|
||||
custom_price DECIMAL(10, 2),
|
||||
custom_duration INTEGER,
|
||||
PRIMARY KEY (service_id, staff_id),
|
||||
FOREIGN KEY (service_id) REFERENCES services(id),
|
||||
FOREIGN KEY (staff_id) REFERENCES staff(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Staff
|
||||
|
||||
```sql
|
||||
CREATE TABLE staff (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
phone TEXT,
|
||||
avatar_url TEXT,
|
||||
bio TEXT,
|
||||
role TEXT DEFAULT 'staff', -- 'admin', 'manager', 'staff'
|
||||
is_active BOOLEAN DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE staff_schedules (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
staff_id INTEGER NOT NULL,
|
||||
day_of_week INTEGER NOT NULL, -- 0=Sunday, 6=Saturday
|
||||
start_time TEXT NOT NULL, -- '09:00'
|
||||
end_time TEXT NOT NULL, -- '17:00'
|
||||
break_start TEXT, -- '12:00'
|
||||
break_end TEXT, -- '13:00'
|
||||
is_working BOOLEAN DEFAULT 1,
|
||||
FOREIGN KEY (staff_id) REFERENCES staff(id)
|
||||
);
|
||||
|
||||
CREATE TABLE staff_time_off (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
staff_id INTEGER NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
reason TEXT,
|
||||
FOREIGN KEY (staff_id) REFERENCES staff(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Bookings
|
||||
|
||||
```sql
|
||||
CREATE TABLE bookings (
|
||||
id TEXT PRIMARY KEY, -- UUID
|
||||
booking_number TEXT UNIQUE NOT NULL,
|
||||
service_id INTEGER NOT NULL,
|
||||
staff_id INTEGER,
|
||||
customer_id INTEGER,
|
||||
|
||||
-- Customer info (guest bookings allowed)
|
||||
customer_name TEXT NOT NULL,
|
||||
customer_email TEXT NOT NULL,
|
||||
customer_phone TEXT,
|
||||
customer_notes TEXT,
|
||||
|
||||
-- Appointment details
|
||||
booking_date DATE NOT NULL,
|
||||
start_time TEXT NOT NULL,
|
||||
end_time TEXT NOT NULL,
|
||||
|
||||
-- Status
|
||||
status TEXT DEFAULT 'pending', -- 'pending', 'confirmed', 'completed', 'cancelled', 'no_show'
|
||||
|
||||
-- Pricing
|
||||
service_price DECIMAL(10, 2) NOT NULL,
|
||||
addons_total DECIMAL(10, 2) DEFAULT 0,
|
||||
discount DECIMAL(10, 2) DEFAULT 0,
|
||||
total DECIMAL(10, 2) NOT NULL,
|
||||
payment_method TEXT, -- 'cash', 'card', 'online'
|
||||
payment_status TEXT DEFAULT 'pending', -- 'pending', 'paid', 'refunded'
|
||||
|
||||
-- Metadata
|
||||
source TEXT DEFAULT 'website', -- 'website', 'phone', 'walk_in'
|
||||
notes TEXT,
|
||||
internal_notes TEXT,
|
||||
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (service_id) REFERENCES services(id),
|
||||
FOREIGN KEY (staff_id) REFERENCES staff(id),
|
||||
FOREIGN KEY (customer_id) REFERENCES customers(id)
|
||||
);
|
||||
|
||||
CREATE TABLE booking_addons (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
booking_id TEXT NOT NULL,
|
||||
addon_id INTEGER NOT NULL,
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
FOREIGN KEY (booking_id) REFERENCES bookings(id),
|
||||
FOREIGN KEY (addon_id) REFERENCES service_addons(id)
|
||||
);
|
||||
|
||||
-- Availability cache for fast queries
|
||||
CREATE TABLE availability_slots (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
service_id INTEGER NOT NULL,
|
||||
staff_id INTEGER,
|
||||
date DATE NOT NULL,
|
||||
start_time TEXT NOT NULL,
|
||||
end_time TEXT NOT NULL,
|
||||
available BOOLEAN DEFAULT 1,
|
||||
booking_id TEXT,
|
||||
FOREIGN KEY (service_id) REFERENCES services(id),
|
||||
FOREIGN KEY (staff_id) REFERENCES staff(id),
|
||||
FOREIGN KEY (booking_id) REFERENCES bookings(id)
|
||||
);
|
||||
```
|
||||
|
||||
### Customers
|
||||
|
||||
```sql
|
||||
CREATE TABLE customers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
phone TEXT,
|
||||
date_of_birth DATE,
|
||||
notes TEXT,
|
||||
total_visits INTEGER DEFAULT 0,
|
||||
total_spent DECIMAL(10, 2) DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_customers_email ON customers(email);
|
||||
CREATE INDEX idx_customers_phone ON customers(phone);
|
||||
```
|
||||
|
||||
### Settings
|
||||
|
||||
```sql
|
||||
CREATE TABLE booking_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT DEFAULT 'string' -- 'string', 'number', 'boolean', 'json'
|
||||
);
|
||||
|
||||
-- Default settings
|
||||
INSERT INTO booking_settings (key, value, type) VALUES
|
||||
('timezone', 'Europe/Moscow', 'string'),
|
||||
('currency', 'RUB', 'string'),
|
||||
('booking_interval', '30', 'number'),
|
||||
('min_booking_notice', '60', 'number'), -- minutes
|
||||
('max_booking_advance', '30', 'number'), -- days
|
||||
('require_phone', '1', 'boolean'),
|
||||
('require_payment', '0', 'boolean'),
|
||||
('deposit_percentage', '20', 'number'),
|
||||
('cancellation_hours', '24', 'number'),
|
||||
('reminder_hours', '2', 'number'),
|
||||
('confirmation_sms', '0', 'boolean'),
|
||||
('confirmation_email', '1', 'boolean');
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Public API
|
||||
|
||||
```yaml
|
||||
# Services
|
||||
GET /api/services # List all active services
|
||||
GET /api/services/:id # Get service details
|
||||
GET /api/categories # List service categories
|
||||
|
||||
# Staff
|
||||
GET /api/staff # List active staff
|
||||
GET /api/staff/:id # Get staff details
|
||||
GET /api/staff/:id/availability # Get staff availability
|
||||
|
||||
# Availability
|
||||
GET /api/availability # Get available slots
|
||||
POST /api/availability/check # Check specific slot availability
|
||||
|
||||
# Booking
|
||||
POST /api/bookings # Create booking (guest)
|
||||
GET /api/bookings/:id # Get booking details
|
||||
POST /api/bookings/:id/cancel # Cancel booking
|
||||
POST /api/bookings/:id/reschedule # Reschedule booking
|
||||
|
||||
# Customer
|
||||
GET /api/customer/bookings # Get customer's bookings (auth)
|
||||
POST /api/customer/register # Register customer
|
||||
```
|
||||
|
||||
### Admin API
|
||||
|
||||
```yaml
|
||||
# Services
|
||||
GET /api/admin/services # List all services
|
||||
POST /api/admin/services # Create service
|
||||
PUT /api/admin/services/:id # Update service
|
||||
DELETE /api/admin/services/:id # Delete service
|
||||
|
||||
# Categories
|
||||
GET /api/admin/categories # List categories
|
||||
POST /api/admin/categories # Create category
|
||||
PUT /api/admin/categories/:id # Update category
|
||||
DELETE /api/admin/categories/:id # Delete category
|
||||
|
||||
# Staff
|
||||
GET /api/admin/staff # List all staff
|
||||
POST /api/admin/staff # Add staff member
|
||||
PUT /api/admin/staff/:id # Update staff
|
||||
DELETE /api/admin/staff/:id # Remove staff
|
||||
PUT /api/admin/staff/:id/schedule # Update schedule
|
||||
POST /api/admin/staff/:id/time-off # Add time off
|
||||
|
||||
# Bookings
|
||||
GET /api/admin/bookings # List bookings (filters)
|
||||
GET /api/admin/bookings/:id # Get booking details
|
||||
PUT /api/admin/bookings/:id/confirm # Confirm booking
|
||||
PUT /api/admin/bookings/:id/complete # Mark complete
|
||||
PUT /api/admin/bookings/:id/cancel # Cancel booking
|
||||
PUT /api/admin/bookings/:id/no-show # Mark no-show
|
||||
|
||||
# Calendar
|
||||
GET /api/admin/calendar # Calendar view
|
||||
GET /api/admin/calendar/staff/:id # Staff calendar
|
||||
|
||||
# Customers
|
||||
GET /api/admin/customers # List customers
|
||||
GET /api/admin/customers/:id # Customer details
|
||||
GET /api/admin/customers/:id/history # Booking history
|
||||
|
||||
# Reports
|
||||
GET /api/admin/reports/revenue # Revenue report
|
||||
GET /api/admin/reports/services # Service popularity
|
||||
GET /api/admin/reports/staff # Staff utilization
|
||||
GET /api/admin/reports/trends # Booking trends
|
||||
|
||||
# Settings
|
||||
GET /api/admin/settings # Get all settings
|
||||
PUT /api/admin/settings # Update settings
|
||||
```
|
||||
|
||||
## Availability Logic
|
||||
|
||||
### Get Available Slots
|
||||
|
||||
```javascript
|
||||
// services/availability.js
|
||||
async function getAvailableSlots(serviceId, staffId, date) {
|
||||
const service = await db.services.findById(serviceId);
|
||||
const dayOfWeek = new Date(date).getDay();
|
||||
|
||||
// Get staff schedule
|
||||
const schedule = await db.staffSchedules.findOne({
|
||||
staff_id: staffId,
|
||||
day_of_week: dayOfWeek,
|
||||
is_working: true
|
||||
});
|
||||
|
||||
if (!schedule) return [];
|
||||
|
||||
// Get existing bookings
|
||||
const bookings = await db.bookings.find({
|
||||
staff_id: staffId,
|
||||
booking_date: date,
|
||||
status: { $in: ['pending', 'confirmed'] }
|
||||
});
|
||||
|
||||
// Generate slots
|
||||
const slots = [];
|
||||
let currentTime = parseTime(schedule.start_time);
|
||||
const endTime = parseTime(schedule.end_time);
|
||||
const bufferTime = service.buffer_time || 0;
|
||||
|
||||
while (addMinutes(currentTime, service.duration + bufferTime) <= endTime) {
|
||||
const slotStart = formatTime(currentTime);
|
||||
const slotEnd = formatTime(addMinutes(currentTime, service.duration));
|
||||
|
||||
// Check if slot is available
|
||||
const isBooked = bookings.some(b =>
|
||||
b.start_time <= slotStart && b.end_time >= slotStart ||
|
||||
b.start_time <= slotEnd && b.end_time >= slotEnd ||
|
||||
b.start_time >= slotStart && b.end_time <= slotEnd
|
||||
);
|
||||
|
||||
// Check break time
|
||||
const isBreak = schedule.break_start && (
|
||||
slotStart >= schedule.break_start && slotStart < schedule.break_end ||
|
||||
slotEnd > schedule.break_start && slotEnd <= schedule.break_end
|
||||
);
|
||||
|
||||
// Check advance booking limit
|
||||
const slotDateTime = new Date(`${date}T${slotStart}`);
|
||||
const isTooSoon = slotDateTime < addMinutes(new Date(), settings.min_booking_notice);
|
||||
|
||||
// Check past
|
||||
const isPast = slotDateTime < new Date();
|
||||
|
||||
if (!isBooked && !isBreak && !isTooSoon && !isPast) {
|
||||
slots.push({
|
||||
start_time: slotStart,
|
||||
end_time: slotEnd,
|
||||
available: true
|
||||
});
|
||||
}
|
||||
|
||||
currentTime = addMinutes(currentTime, service.duration);
|
||||
}
|
||||
|
||||
return slots;
|
||||
}
|
||||
```
|
||||
|
||||
### Create Booking
|
||||
|
||||
```javascript
|
||||
// services/booking.js
|
||||
async function createBooking(bookingData) {
|
||||
const { service_id, staff_id, date, time, customer_name, customer_email, customer_phone } = bookingData;
|
||||
|
||||
// Validate service exists
|
||||
const service = await db.services.findById(service_id);
|
||||
if (!service || !service.is_active) {
|
||||
throw new Error('Service not available');
|
||||
}
|
||||
|
||||
// Get staff or auto-assign
|
||||
let staff = staff_id ?
|
||||
await db.staff.findById(staff_id) :
|
||||
await autoAssignStaff(service_id, date, time);
|
||||
|
||||
if (!staff) {
|
||||
throw new Error('No staff available for this slot');
|
||||
}
|
||||
|
||||
// Check availability
|
||||
const available = await checkAvailability(service_id, staff.id, date, time);
|
||||
if (!available) {
|
||||
throw new Error('Slot already booked');
|
||||
}
|
||||
|
||||
// Calculate end time
|
||||
const endTime = addMinutes(parseTime(time), service.duration);
|
||||
|
||||
// Create booking number
|
||||
const bookingNumber = generateBookingNumber();
|
||||
|
||||
// Create booking
|
||||
const booking = await db.bookings.create({
|
||||
id: generateUUID(),
|
||||
booking_number: bookingNumber,
|
||||
service_id,
|
||||
staff_id: staff.id,
|
||||
customer_name,
|
||||
customer_email,
|
||||
customer_phone,
|
||||
booking_date: date,
|
||||
start_time: time,
|
||||
end_time: formatTime(endTime),
|
||||
status: 'pending',
|
||||
service_price: service.price,
|
||||
total: service.price
|
||||
});
|
||||
|
||||
// Create availability slot
|
||||
await db.availabilitySlots.create({
|
||||
service_id,
|
||||
staff_id: staff.id,
|
||||
date,
|
||||
start_time: time,
|
||||
end_time: formatTime(endTime),
|
||||
available: false,
|
||||
booking_id: booking.id
|
||||
});
|
||||
|
||||
// Send confirmation
|
||||
await sendConfirmation(booking);
|
||||
|
||||
return booking;
|
||||
}
|
||||
```
|
||||
|
||||
## Booking Status Flow
|
||||
|
||||
```
|
||||
pending → confirmed → completed
|
||||
↓ ↓ ↓
|
||||
cancelled cancelled cancelled → refunded
|
||||
↓
|
||||
no_show
|
||||
```
|
||||
|
||||
### Status Transitions
|
||||
|
||||
```javascript
|
||||
const STATUS_FLOW = {
|
||||
pending: ['confirmed', 'cancelled'],
|
||||
confirmed: ['completed', 'cancelled', 'no_show'],
|
||||
completed: ['refunded'],
|
||||
cancelled: ['refunded'],
|
||||
no_show: [],
|
||||
refunded: []
|
||||
};
|
||||
|
||||
function canTransition(currentStatus, newStatus) {
|
||||
return STATUS_FLOW[currentStatus]?.includes(newStatus) || false;
|
||||
}
|
||||
```
|
||||
|
||||
## Notifications
|
||||
|
||||
### Email Templates
|
||||
|
||||
```javascript
|
||||
// services/notifications/email.js
|
||||
const bookingConfirmation = (booking, service, staff) => ({
|
||||
subject: `Booking Confirmed - ${service.name}`,
|
||||
html: `
|
||||
<h1>Your Booking is Confirmed!</h1>
|
||||
<p>Booking #${booking.booking_number}</p>
|
||||
|
||||
<h2>Details</h2>
|
||||
<ul>
|
||||
<li>Service: ${service.name}</li>
|
||||
<li>Staff: ${staff.name}</li>
|
||||
<li>Date: ${formatDate(booking.booking_date)}</li>
|
||||
<li>Time: ${booking.start_time} - ${booking.end_time}</li>
|
||||
<li>Price: ${formatCurrency(booking.total)}</li>
|
||||
</ul>
|
||||
|
||||
<p>Location: Your Business Name</p>
|
||||
|
||||
<a href="${config.siteUrl}/booking/${booking.id}">Manage Booking</a>
|
||||
|
||||
<p>Need to cancel? Please give us ${settings.cancellation_hours} hours notice.</p>
|
||||
`
|
||||
});
|
||||
|
||||
const bookingReminder = (booking, service, staff) => ({
|
||||
subject: `Reminder: ${service.name} in 2 hours`,
|
||||
html: `
|
||||
<h2>Upcoming Appointment Reminder</h2>
|
||||
<p>Your appointment is in 2 hours!</p>
|
||||
|
||||
<ul>
|
||||
<li>Service: ${service.name}</li>
|
||||
<li>Staff: ${staff.name}</li>
|
||||
<li>Time: ${booking.start_time}</li>
|
||||
</ul>
|
||||
`
|
||||
});
|
||||
```
|
||||
|
||||
### SMS Templates
|
||||
|
||||
```javascript
|
||||
// services/notifications/sms.js
|
||||
const templates = {
|
||||
confirmation: (booking, service) =>
|
||||
`Your ${service.name} booking is confirmed for ${formatDate(booking.booking_date)} at ${booking.start_time}. Booking #${booking.booking_number}`,
|
||||
|
||||
reminder: (booking, service) =>
|
||||
`Reminder: ${service.name} appointment in 2 hours at ${booking.start_time}. Reply C to cancel.`,
|
||||
|
||||
cancellation: (booking) =>
|
||||
`Your booking #${booking.booking_number} has been cancelled.`
|
||||
};
|
||||
```
|
||||
|
||||
## Calendar Integration
|
||||
|
||||
### iCal Export
|
||||
|
||||
```javascript
|
||||
function generateICal(booking, service, staff) {
|
||||
return `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Business Name//Booking System//EN
|
||||
BEGIN:VEVENT
|
||||
DTSTART:${formatICalDate(booking.booking_date, booking.start_time)}
|
||||
DTEND:${formatICalDate(booking.booking_date, booking.end_time)}
|
||||
SUMMARY:${service.name} with ${staff.name}
|
||||
DESCRIPTION:Booking #${booking.booking_number}
|
||||
LOCATION:Business Address
|
||||
STATUS:CONFIRMED
|
||||
END:VEVENT
|
||||
END:VCALENDAR`;
|
||||
}
|
||||
```
|
||||
|
||||
## Reports
|
||||
|
||||
### Revenue Report
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
DATE(booking_date) as date,
|
||||
COUNT(*) as bookings,
|
||||
SUM(total) as revenue,
|
||||
AVG(total) as avg_booking
|
||||
FROM bookings
|
||||
WHERE status IN ('completed', 'confirmed')
|
||||
AND booking_date BETWEEN ? AND ?
|
||||
GROUP BY DATE(booking_date)
|
||||
ORDER BY date;
|
||||
```
|
||||
|
||||
### Staff Utilization
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
s.id,
|
||||
s.name,
|
||||
COUNT(b.id) as bookings,
|
||||
SUM(TIMESTAMPDIFF(MINUTE,
|
||||
CONCAT(b.booking_date, ' ', b.start_time),
|
||||
CONCAT(b.booking_date, ' ', b.end_time)
|
||||
)) / 60 as hours_booked,
|
||||
COUNT(DISTINCT b.booking_date) as days_worked
|
||||
FROM staff s
|
||||
LEFT JOIN bookings b ON b.staff_id = s.id
|
||||
AND b.status IN ('completed', 'confirmed')
|
||||
AND b.booking_date BETWEEN ? AND ?
|
||||
GROUP BY s.id
|
||||
ORDER BY bookings DESC;
|
||||
```
|
||||
|
||||
### Service Popularity
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
s.id,
|
||||
s.name,
|
||||
s.category_id,
|
||||
COUNT(b.id) as bookings,
|
||||
SUM(b.total) as revenue
|
||||
FROM services s
|
||||
LEFT JOIN bookings b ON b.service_id = s.id
|
||||
AND b.status IN ('completed', 'confirmed')
|
||||
AND b.booking_date BETWEEN ? AND ?
|
||||
GROUP BY s.id
|
||||
ORDER BY bookings DESC;
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
|
||||
- Payment: Stripe, YooKassa
|
||||
- Calendar: Google Calendar, iCal
|
||||
- SMS: Twilio, SMS.ru
|
||||
- Email: SendGrid, Mailgun
|
||||
- Analytics: Google Analytics, Yandex Metrika
|
||||
- CRM: Integration API for customer data
|
||||
Reference in New Issue
Block a user