- Add TimeBlock and Holiday models with recurrence support (one-time, weekly, monthly, yearly, holiday) - Implement business-level and resource-level blocking with hard/soft block types - Add multi-select holiday picker for bulk holiday blocking - Add calendar overlay visualization with distinct colors: - Business blocks: Red (hard) / Yellow (soft) - Resource blocks: Purple (hard) / Cyan (soft) - Add month view resource indicators showing 1/n width per resource - Add yearly calendar view for block overview - Add My Availability page for staff self-service - Add contracts module with templates, signing flow, and PDF generation - Update scheduler with click-to-day navigation in week view 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
64 lines
5.3 KiB
Python
64 lines
5.3 KiB
Python
# Generated by Django 5.2.8 on 2025-12-04 19:27
|
|
|
|
import django.db.models.deletion
|
|
from django.conf import settings
|
|
from django.db import migrations, models
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
('schedule', '0027_add_deposit_percent_back'),
|
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
]
|
|
|
|
operations = [
|
|
migrations.CreateModel(
|
|
name='Holiday',
|
|
fields=[
|
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
('code', models.CharField(help_text="Unique identifier (e.g., 'thanksgiving_us', 'christmas')", max_length=50, unique=True)),
|
|
('name', models.CharField(max_length=100)),
|
|
('country', models.CharField(db_index=True, default='US', help_text='ISO 3166-1 alpha-2 country code', max_length=2)),
|
|
('holiday_type', models.CharField(choices=[('FIXED', 'Fixed date'), ('FLOATING', 'Floating (Nth weekday of month)'), ('CALCULATED', 'Calculated (algorithm-based)')], default='FIXED', max_length=20)),
|
|
('month', models.PositiveSmallIntegerField(blank=True, help_text='Month (1-12)', null=True)),
|
|
('day', models.PositiveSmallIntegerField(blank=True, help_text='Day of month (1-31)', null=True)),
|
|
('week_of_month', models.PositiveSmallIntegerField(blank=True, help_text="Week of month (1-4, or 5 for 'last')", null=True)),
|
|
('day_of_week', models.PositiveSmallIntegerField(blank=True, help_text='Day of week (0=Monday, 6=Sunday)', null=True)),
|
|
('calculation_rule', models.CharField(blank=True, help_text="Calculation rule (e.g., 'easter', 'easter-2' for Good Friday)", max_length=50)),
|
|
('is_active', models.BooleanField(default=True)),
|
|
],
|
|
options={
|
|
'ordering': ['country', 'name'],
|
|
'indexes': [models.Index(fields=['country', 'is_active'], name='schedule_ho_country_b41340_idx')],
|
|
},
|
|
),
|
|
migrations.CreateModel(
|
|
name='TimeBlock',
|
|
fields=[
|
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
('title', models.CharField(help_text="Display title (e.g., 'Christmas Day', 'Lunch Break')", max_length=200)),
|
|
('description', models.TextField(blank=True, help_text='Optional description or reason for block')),
|
|
('block_type', models.CharField(choices=[('HARD', 'Hard Block (prevents booking)'), ('SOFT', 'Soft Block (warning only, override allowed)')], default='HARD', help_text='HARD prevents booking; SOFT shows warning but allows override', max_length=10)),
|
|
('recurrence_type', models.CharField(choices=[('NONE', 'No recurrence (specific date/range)'), ('WEEKLY', 'Weekly (specific days of week)'), ('MONTHLY', 'Monthly (specific days of month)'), ('YEARLY', 'Yearly (specific days of year)'), ('HOLIDAY', 'Holiday (floating dates)')], db_index=True, default='NONE', max_length=20)),
|
|
('start_date', models.DateField(blank=True, help_text='Start date for one-time blocks', null=True)),
|
|
('end_date', models.DateField(blank=True, help_text='End date for one-time blocks (same as start for single day)', null=True)),
|
|
('all_day', models.BooleanField(default=True, help_text='If true, blocks entire day; if false, uses start/end time')),
|
|
('start_time', models.TimeField(blank=True, help_text='Start time (if not all-day)', null=True)),
|
|
('end_time', models.TimeField(blank=True, help_text='End time (if not all-day)', null=True)),
|
|
('recurrence_pattern', models.JSONField(blank=True, default=dict, help_text='\n Recurrence configuration:\n - WEEKLY: {"days_of_week": [0,1,2]} (0=Mon, 6=Sun)\n - MONTHLY: {"days_of_month": [1, 15]}\n - YEARLY: {"month": 7, "day": 4} or {"month": 12, "day": 25}\n - HOLIDAY: {"holiday_code": "thanksgiving_us"}\n ')),
|
|
('recurrence_start', models.DateField(blank=True, help_text='When this recurring block becomes active', null=True)),
|
|
('recurrence_end', models.DateField(blank=True, help_text='When this recurring block ends (null = forever)', null=True)),
|
|
('is_active', models.BooleanField(db_index=True, default=True)),
|
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
('updated_at', models.DateTimeField(auto_now=True)),
|
|
('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_time_blocks', to=settings.AUTH_USER_MODEL)),
|
|
('resource', models.ForeignKey(blank=True, help_text='Specific resource (null = business-level block)', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='time_blocks', to='schedule.resource')),
|
|
],
|
|
options={
|
|
'ordering': ['-created_at'],
|
|
'indexes': [models.Index(fields=['resource', 'is_active'], name='schedule_ti_resourc_1e9a5d_idx'), models.Index(fields=['recurrence_type', 'is_active'], name='schedule_ti_recurre_b7b096_idx'), models.Index(fields=['start_date', 'end_date'], name='schedule_ti_start_d_bba9d9_idx')],
|
|
},
|
|
),
|
|
]
|