partial edit fixes

This commit is contained in:
2026-02-26 20:03:11 +00:00
parent 4fc84e6d39
commit 72124bac98
3 changed files with 438 additions and 103 deletions
+113 -17
View File
@@ -232,6 +232,47 @@ CREATE TABLE booking_services (
PRIMARY KEY (booking_id, service_id)
);
-- =======================================
-- BOOKING EDIT REQUESTS TABLE
-- =======================================
CREATE TABLE booking_edit_requests (
id CHAR(12) PRIMARY KEY DEFAULT generate_booking_id(),
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
requested_by CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
new_start_time TIMESTAMPTZ,
new_services JSONB DEFAULT '[]'::jsonb, -- Array of service IDs to replace booking_services
notes TEXT,
has_overrides BOOLEAN NOT NULL DEFAULT FALSE, -- If TRUE, cannot change services, use existing overrides for duration
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_at_least_one_field CHECK (
new_start_time IS NOT NULL OR
new_services IS NOT NULL OR
notes IS NOT NULL
)
);
CREATE INDEX idx_booking_edit_requests_booking ON booking_edit_requests(booking_id);
id CHAR(12) PRIMARY KEY DEFAULT generate_booking_id(),
booking_id CHAR(12) NOT NULL REFERENCES bookings(id) ON DELETE CASCADE,
requested_by CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
new_start_time TIMESTAMPTZ,
new_services JSONB DEFAULT '[]'::jsonb,
new_notes TEXT,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
admin_notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT chk_at_least_one_field CHECK (
new_start_time IS NOT NULL OR
new_services IS NOT NULL OR
new_notes IS NOT NULL
)
);
CREATE INDEX idx_booking_edit_requests_booking ON booking_edit_requests(booking_id);
CREATE INDEX idx_booking_edit_requests_status ON booking_edit_requests(status);
CREATE TABLE user_referrals (
referrer_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
referred_id CHAR(12) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -384,7 +425,7 @@ INSERT INTO business_settings (
'https://www.website.co.uk'
);
CREATE TYPE admin_notification_reason AS ENUM ('pending_booking', 'cancelled_booking', 'rescheduled_booking', '1_week_no_pay', '1_month_no_pay', 'affiliate_claim', 'late_cancellation', 'no_deposit', 'deposit_paid');
CREATE TYPE admin_notification_reason AS ENUM ('pending_booking', 'cancelled_booking', 'rescheduled_booking', '1_week_no_pay', '1_month_no_pay', 'affiliate_claim', 'late_cancellation', 'no_deposit', 'deposit_paid', 'edit_request');
CREATE TABLE admin_notifications (
id SERIAL PRIMARY KEY,
@@ -504,21 +545,26 @@ BEGIN
SELECT json_build_object(
'user_profile', (
SELECT json_build_object(
'id', id,
'first_name', n_first_name,
'last_name', n_last_name,
'full_name', fn,
'email', email,
'phone', phone,
'date_of_birth', date_of_birth,
'profile_pic_url', profile_pic_url,
'account_role', account_role,
'loyalty_stamps', loyalty_stamps,
'referral_code', referral_code,
'data_retention_consent', data_retention_consent,
'data_consent_updated_at', data_consent_updated_at,
'created_at', created_at,
'updated_at', updated_at
'id', id,
'first_name', n_first_name,
'last_name', n_last_name,
'full_name', fn,
'email', email,
'phone', phone,
'date_of_birth', date_of_birth,
'profile_pic_url', profile_pic_url,
'account_role', account_role,
'account_type', account_type,
'last_login_at', last_login_at,
'loyalty_stamps', loyalty_stamps,
'deposits_required', deposits_required,
'referral_code', referral_code,
'privacy_policy_and_terms_consent', privacy_policy_and_terms_consent,
'policy_consent_updated_at', policy_consent_updated_at,
'data_retention_consent', data_retention_consent,
'data_consent_updated_at', data_consent_updated_at,
'created_at', created_at,
'updated_at', updated_at
)
FROM users WHERE id = target_user_id
),
@@ -529,6 +575,7 @@ BEGIN
'start_time', b.start_time,
'status', b.status,
'notes', b.notes,
'created_by', b.created_by,
'created_at', b.created_at,
'updated_at', b.updated_at,
'services', (
@@ -556,6 +603,7 @@ BEGIN
'booking_id', p.booking_id,
'payment_type', p.payment_type,
'payment_method', p.payment_method,
'vendor_code', p.vendor_code,
'invoice_number', p.invoice_number,
'status', p.status,
'amount', p.amount,
@@ -582,11 +630,59 @@ BEGIN
JOIN patch_tests pt ON upt.patch_test_id = pt.id
WHERE upt.user_id = target_user_id
),
'referrals', (
SELECT json_build_object(
'referred_by', (
-- The user who referred this user (if any)
-- First name only: referrer's name is their own personal data,
-- included here only to make the SAR meaningful to the recipient.
SELECT json_build_object(
'referrer_id', ur.referrer_id,
'referrer_first_name', u.n_first_name,
'referred_at', ur.referred_at,
'claimed_booking_id', ur.claimed_booking_id
)
FROM user_referrals ur
JOIN users u ON u.id = ur.referrer_id
WHERE ur.referred_id = target_user_id
LIMIT 1
),
'referred_users', (
-- Users this person has referred
-- First name only: same reasoning as above.
SELECT COALESCE(json_agg(
json_build_object(
'referred_id', ur.referred_id,
'referred_first_name', u.n_first_name,
'referred_at', ur.referred_at,
'claimed_booking_id', ur.claimed_booking_id
)
ORDER BY ur.referred_at DESC), '[]'::json)
FROM user_referrals ur
JOIN users u ON u.id = ur.referred_id
WHERE ur.referrer_id = target_user_id
)
)
),
'notification_preferences', (
SELECT COALESCE(json_agg(
json_build_object(
'notification_type', unp.notification_type,
'email_enabled', unp.email_enabled,
'sms_enabled', unp.sms_enabled,
'push_enabled', unp.push_enabled,
'created_at', unp.created_at,
'updated_at', unp.updated_at
)
ORDER BY unp.notification_type), '[]'::json)
FROM user_notification_preferences unp
WHERE unp.user_id = target_user_id
),
'export_metadata', json_build_object(
'exported_at', NOW(),
'exported_by', 'system',
'user_id', target_user_id,
'format_version', '1.0'
'format_version', '1.1'
)
) INTO result;