Files
smoothschedule/smoothschedule/schedule/migrations/0028_add_timeblock_and_holiday.py
poduck 8d0cc1e90a feat(time-blocks): Add comprehensive time blocking system with contracts
- 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>
2025-12-04 17:19:12 -05:00

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')],
},
),
]