feat(api): complete M5 swap and dispatch backend slice

This commit is contained in:
zouantchaw
2026-03-18 10:40:04 +01:00
parent 32f6cd55c8
commit 26a853184f
18 changed files with 2170 additions and 109 deletions

View File

@@ -0,0 +1,76 @@
ALTER TABLE assignments
DROP CONSTRAINT IF EXISTS assignments_status_check;
ALTER TABLE assignments
ADD CONSTRAINT assignments_status_check
CHECK (status IN ('ASSIGNED', 'ACCEPTED', 'SWAP_REQUESTED', 'SWAPPED_OUT', 'CHECKED_IN', 'CHECKED_OUT', 'COMPLETED', 'CANCELLED', 'NO_SHOW'));
CREATE TABLE IF NOT EXISTS shift_swap_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
business_id UUID NOT NULL REFERENCES businesses(id) ON DELETE CASCADE,
vendor_id UUID REFERENCES vendors(id) ON DELETE SET NULL,
shift_id UUID NOT NULL REFERENCES shifts(id) ON DELETE CASCADE,
shift_role_id UUID NOT NULL REFERENCES shift_roles(id) ON DELETE CASCADE,
original_assignment_id UUID NOT NULL REFERENCES assignments(id) ON DELETE CASCADE,
original_staff_id UUID NOT NULL REFERENCES staffs(id) ON DELETE RESTRICT,
requested_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
status TEXT NOT NULL DEFAULT 'OPEN'
CHECK (status IN ('OPEN', 'RESOLVED', 'CANCELLED', 'EXPIRED', 'AUTO_CANCELLED')),
reason TEXT,
expires_at TIMESTAMPTZ NOT NULL,
resolved_at TIMESTAMPTZ,
resolved_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
selected_application_id UUID REFERENCES applications(id) ON DELETE SET NULL,
replacement_assignment_id UUID REFERENCES assignments(id) ON DELETE SET NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_shift_swap_requests_open_original
ON shift_swap_requests (original_assignment_id)
WHERE status = 'OPEN';
CREATE INDEX IF NOT EXISTS idx_shift_swap_requests_status_expiry
ON shift_swap_requests (status, expires_at ASC);
CREATE INDEX IF NOT EXISTS idx_shift_swap_requests_shift_role
ON shift_swap_requests (shift_role_id, created_at DESC);
CREATE TABLE IF NOT EXISTS dispatch_team_memberships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
business_id UUID NOT NULL REFERENCES businesses(id) ON DELETE CASCADE,
hub_id UUID REFERENCES clock_points(id) ON DELETE CASCADE,
staff_id UUID NOT NULL REFERENCES staffs(id) ON DELETE CASCADE,
team_type TEXT NOT NULL
CHECK (team_type IN ('CORE', 'CERTIFIED_LOCATION', 'MARKETPLACE')),
source TEXT NOT NULL DEFAULT 'MANUAL'
CHECK (source IN ('MANUAL', 'AUTOMATED', 'SYSTEM')),
status TEXT NOT NULL DEFAULT 'ACTIVE'
CHECK (status IN ('ACTIVE', 'INACTIVE')),
reason TEXT,
effective_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
created_by_user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_dispatch_team_certified_scope
CHECK (team_type <> 'CERTIFIED_LOCATION' OR hub_id IS NOT NULL)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_dispatch_team_memberships_active_global
ON dispatch_team_memberships (business_id, staff_id, team_type)
WHERE status = 'ACTIVE' AND hub_id IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_dispatch_team_memberships_active_hub
ON dispatch_team_memberships (business_id, hub_id, staff_id, team_type)
WHERE status = 'ACTIVE' AND hub_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_dispatch_team_memberships_staff
ON dispatch_team_memberships (staff_id, status, effective_at DESC);
CREATE INDEX IF NOT EXISTS idx_dispatch_team_memberships_business_hub
ON dispatch_team_memberships (business_id, hub_id, status, effective_at DESC);