-- ============================================================================ -- Massage vertical - data migration -- ============================================================================ -- Studio Beve, 2026-08-10. -- -- NOTE: there is NO DDL here. The massage vertical needed no schema change: -- the vertical is derived from workers.service_category_id, and -- services.service_type is already a VARCHAR. No ALTER, no backfill, -- no UPDATE of any existing row. -- -- ORDER IS LOAD-BEARING. Run the whole file in ONE session (it uses @vars): -- -- 1. service_categories FIRST. getAllServiceByServiceType() and -- getWorkersByCategorySlug() both INNER JOIN service_categories, so a -- dangling category_id makes rows silently INVISIBLE rather than erroring. -- 2. The worker row, carrying that category id. Must exist before any -- massage service is enabled, or the public booking page renders an -- empty step 2. -- 3. worker_schedule rows for every bookable weekday. NOT OPTIONAL, and -- worker_schedule_overrides alone are NOT sufficient - getAvailableTimes() -- returns [] without a schedule row, and booking_process() rejects with -- 'worker_unavailable'. -- 4. Services inserted with is_enabled = '0'. Flipping that to '1' is the -- go-live switch (step 9), after the smoke test. -- -- DEMO DATA: the therapist ("Nora Lindqvist"), the four services and their -- prices are placeholders. Replace with real values before production. -- ============================================================================ SET NAMES utf8mb4; -- ---------------------------------------------------------------- 1. category INSERT INTO service_categories (serv_cat_slug, serv_cat_name, is_deleted) VALUES ('massage', 'Masszázs', 0); SET @cat := LAST_INSERT_ID(); SELECT @cat AS new_service_category_id; -- ---------------------------------------------------------------- 2. worker -- is_beauty / is_barber are the legacy columns; nothing reads them any more -- (the vertical is derived from service_category_id). They are NOT NULL, so -- they are written as 0 here to stay consistent with the derived value. INSERT INTO workers ( worker_name, worker_profile_img, worker_info, service_category_id, is_barber, is_beauty, is_active, is_deleted, google_calendar_id, ntfy_topic, lunch_window_start, lunch_window_end, lunch_preferred_time ) VALUES ( 'Nora Lindqvist', 'worker_Nora_Lindqvist.jpg', '', @cat, 0, 0, 1, 0, NULL, NULL, '12:00:00', '14:00:00', '12:30:00' ); SET @worker := LAST_INSERT_ID(); SELECT @worker AS new_worker_id; -- ---------------------------------------------------------------- 3. schedule -- weekday follows PHP date('w'): 0 = Sunday .. 6 = Saturday. -- Mon-Fri 10:00-18:00, Sat 10:00-16:00, Sun closed (no row). INSERT INTO worker_schedule (worker_id, weekday, start_time, end_time, is_alternate_week) VALUES (@worker, 1, '10:00:00', '18:00:00', 0), (@worker, 2, '10:00:00', '18:00:00', 0), (@worker, 3, '10:00:00', '18:00:00', 0), (@worker, 4, '10:00:00', '18:00:00', 0), (@worker, 5, '10:00:00', '18:00:00', 0), (@worker, 6, '10:00:00', '16:00:00', 0); -- ---------------------------------------------------------------- 4. services -- Inserted DISABLED. service_time drives the slot length, so it also decides -- how many slots the therapist appears to have against the 18:00 close: -- a 90-minute service has its last possible start at 16:30. INSERT INTO services ( service_type, service_category_no, service_category_en, service_category_hu, service_name_no, service_name_en, service_name_hu, service_description_no, service_description_en, service_description_hu, service_price, service_time, service_category_id, is_enabled, is_deleted ) VALUES ('massage', 'Massasje', 'Massage', 'Masszázs', 'Klassisk massasje 60 min', 'Classic massage 60 min', 'Klasszikus masszázs 60 perc', 'Helkropp, avslappende', 'Full body, relaxing', 'Teljes test, lazító', 900, '01:00:00', @cat, 0, 0), ('massage', 'Massasje', 'Massage', 'Masszázs', 'Klassisk massasje 90 min', 'Classic massage 90 min', 'Klasszikus masszázs 90 perc', 'Helkropp, utvidet behandling', 'Full body, extended treatment', 'Teljes test, hosszabb kezelés', 1300, '01:30:00', @cat, 0, 0), ('massage', 'Massasje', 'Massage', 'Masszázs', 'Dypvevsmassasje 60 min', 'Deep tissue massage 60 min', 'Mélyszöveti masszázs 60 perc', 'Mot spenninger i nakke, skuldre og korsrygg', 'For tension in neck, shoulders and lower back', 'Nyaki, vállövi és deréktáji feszülésre', 1000, '01:00:00', @cat, 0, 0), ('massage', 'Massasje', 'Massage', 'Masszázs', 'Nakke og skuldre 30 min', 'Neck and shoulders 30 min', 'Nyak és váll 30 perc', 'Kort behandling for stillesittende arbeid', 'Short treatment for desk-bound days', 'Rövid kezelés ülőmunkához', 550, '00:30:00', @cat, 0, 0); -- ---------------------------------------------------------------- verification SELECT 'category' AS step, service_category_id AS id, serv_cat_slug, serv_cat_name FROM service_categories WHERE service_category_id = @cat; SELECT 'worker' AS step, worker_id, worker_name, service_category_id, is_active FROM workers WHERE worker_id = @worker; SELECT 'schedule' AS step, weekday, start_time, end_time FROM worker_schedule WHERE worker_id = @worker ORDER BY weekday; SELECT 'services' AS step, service_id, service_name_no, service_price, service_time, is_enabled FROM services WHERE service_category_id = @cat ORDER BY service_id; -- The derived-vertical check: this is what getActiveWorkers('massage') resolves. SELECT 'derived' AS step, w.worker_id, w.worker_name, (SELECT GROUP_CONCAT(DISTINCT s.service_type ORDER BY s.service_type) FROM services s WHERE s.service_category_id = w.service_category_id AND s.is_deleted != '1') AS derived_verticals FROM workers w WHERE w.worker_id = @worker;