From 1371fec036e2501903b638bfa65fa706923b89f7 Mon Sep 17 00:00:00 2001 From: Stephen Adamson Date: Fri, 17 Oct 2025 22:39:31 +0100 Subject: [PATCH] formatting --- frontend/src/routes/book/+page.svelte | 21 +++++++++++++++ init-scripts/init-script.sql | 39 +++++++++++++++++++-------- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/frontend/src/routes/book/+page.svelte b/frontend/src/routes/book/+page.svelte index eb90d35..762aa23 100644 --- a/frontend/src/routes/book/+page.svelte +++ b/frontend/src/routes/book/+page.svelte @@ -636,6 +636,24 @@ } } + function getDayWithOrdinal(date: CalendarDate): string { + const monthName = new Date(date.year, date.month - 1, date.day).toLocaleDateString('en-GB', { + month: 'long' + }); + const day = date.day; + if (day > 3 && day < 21) return monthName + ' ' + day + 'th'; + switch (day % 10) { + case 1: + return monthName + ' ' + day + 'st'; + case 2: + return monthName + ' ' + day + 'nd'; + case 3: + return monthName + ' ' + day + 'rd'; + default: + return monthName + ' ' + day + 'th'; + } + } + // Use the formatted duration everywhere const formattedTotalDuration = $derived(formatDuration(getTotalDuration())); @@ -823,6 +841,9 @@ {#if (loadingWorkingHours || loadingAvailableHours) && selectedDate}
Loading available times...
{:else if groupedTimeSlots.length > 0} + {#if selectedDate} +
{getDayWithOrdinal(selectedDate)}
+ {/if}
{#each groupedTimeSlots as slot (slot.startTime)} diff --git a/init-scripts/init-script.sql b/init-scripts/init-script.sql index efbb328..20f95d2 100644 --- a/init-scripts/init-script.sql +++ b/init-scripts/init-script.sql @@ -327,14 +327,19 @@ INSERT INTO business_settings ( 'Crussell Nail Art Studio', 'Address', '+44 131 123 4567', - 'Email', - NULL, -- Set this when you register for VAT - FALSE, -- Set to TRUE when you register for VAT + 'crussellnails@gmail.com', + NULL, -- Set this when we register for VAT + FALSE, -- Set to TRUE when we register for VAT 20.00, 'GBP', 'https://www.website.co.uk' ); +CREATE INDEX idx_payments_booking_id_status ON payments(booking_id, status); +CREATE INDEX idx_bookings_start_time_status ON bookings(start_time, status); +CREATE INDEX idx_users_created_at ON users(created_at); +CREATE INDEX idx_payments_created_at_status ON payments(created_at, status); + -- ======================================= -- GDPR COMPLIANCE FUNCTIONS -- ======================================= @@ -425,7 +430,7 @@ BEGIN 'account_role', account_role, 'loyalty_stamps', loyalty_stamps, 'data_retention_consent', data_retention_consent, - 'data_consent_updated_at', consent_updated_at, + 'data_consent_updated_at', data_consent_updated_at, 'created_at', created_at, 'updated_at', updated_at ) @@ -528,14 +533,26 @@ BEGIN COALESCE(SUM(p.amount), 0) as total_sales, COALESCE(SUM( CASE - WHEN p.vat_rate IS NOT NULL THEN p.vat_amount - ELSE ROUND(p.amount - (p.amount / (1 + COALESCE(p.vat_rate, 20.00)/100)), 2) + -- If VAT is explicitly stored, use it + WHEN p.vat_amount IS NOT NULL THEN p.vat_amount + -- If business is VAT registered but no VAT breakdown, calculate it + WHEN (SELECT is_vat_registered FROM business_settings WHERE id = 1) THEN + ROUND(p.amount - (p.amount / (1 + COALESCE(p.vat_rate, + (SELECT default_vat_rate FROM business_settings WHERE id = 1))/100)), 2) + -- Business not VAT registered = no VAT charged + ELSE 0 END ), 0) as total_vat_charged, COALESCE(SUM( CASE + -- If net amount is explicitly stored, use it WHEN p.net_amount IS NOT NULL THEN p.net_amount - ELSE ROUND(p.amount / 1.20, 2) -- Calculate net if not stored + -- If business is VAT registered but no net amount, calculate it + WHEN (SELECT is_vat_registered FROM business_settings WHERE id = 1) THEN + ROUND(p.amount / (1 + COALESCE(p.vat_rate, + (SELECT default_vat_rate FROM business_settings WHERE id = 1))/100), 2) + -- Business not VAT registered = gross amount is net amount + ELSE p.amount END ), 0) as net_sales, COUNT(*) as transaction_count @@ -543,7 +560,7 @@ BEGIN JOIN bookings b ON p.booking_id = b.id WHERE p.status = 'completed' AND p.created_at::date BETWEEN start_date AND end_date - AND p.payment_type IN ('full', 'partial', 'balance'); -- Exclude deposits and tips for main sales + AND p.payment_type IN ('full', 'partial', 'balance'); END; $$ LANGUAGE plpgsql; @@ -690,7 +707,7 @@ $$ LANGUAGE plpgsql; -- WHY: When crossing £90k threshold, must register for VAT and backfill existing data -- WHEN: One-time use when VAT registration becomes mandatory -- OUTPUT: Updates all historical payments with VAT breakdown + sets new defaults --- USE: Call once when you register for VAT - transforms business from non-VAT to VAT +-- USE: Call once when we register for VAT - transforms business from non-VAT to VAT -- ======================================= -- UTILITY FUNCTIONS -- ======================================= @@ -699,7 +716,7 @@ $$ LANGUAGE plpgsql; -- WHY: When crossing £90k threshold, must register for VAT and backfill existing data -- WHEN: One-time use when VAT registration becomes mandatory -- OUTPUT: Updates all historical payments with VAT breakdown + sets new defaults --- USE: Call once when you register for VAT - transforms business from non-VAT to VAT +-- USE: Call once when we register for VAT - transforms business from non-VAT to VAT CREATE OR REPLACE FUNCTION enable_vat_registration( registration_date DATE DEFAULT CURRENT_DATE, vat_rate NUMERIC(5,2) DEFAULT 20.00, @@ -1022,7 +1039,7 @@ $$ LANGUAGE plpgsql; -- ======================================= /* This summary organizes all database functions by their primary purpose, legal basis, and expected usage frequency— -providing a clear guide for integration into your application, compliance workflows, and business operations. +providing a clear guide for integration into our application, compliance workflows, and business operations. -------------------------------------------------------------------------------- 1. FINANCIAL & TAX COMPLIANCE