diff --git a/init-scripts/init-script.sql b/init-scripts/init-script.sql index b39ef13..1bd917b 100644 --- a/init-scripts/init-script.sql +++ b/init-scripts/init-script.sql @@ -126,14 +126,6 @@ CREATE TABLE user_social_logins ( CREATE INDEX idx_users_email_lower ON users (LOWER(email)); CREATE INDEX idx_users_account_role ON users (account_role); -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, - referred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - claimed_booking_id CHAR(12) REFERENCES bookings(id) ON DELETE SET NULL, - PRIMARY KEY (referrer_id, referred_id) -); - -- ======================================= -- SERVICES TABLE -- ======================================= @@ -196,6 +188,14 @@ CREATE TABLE booking_services ( PRIMARY KEY (booking_id, service_id) ); +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, + referred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + claimed_booking_id CHAR(12) REFERENCES bookings(id) ON DELETE SET NULL, + PRIMARY KEY (referrer_id, referred_id) +); + -- ======================================= -- DEFAULT WORKING HOURS TABLE -- ======================================= @@ -484,8 +484,8 @@ BEGIN 'updated_by', p.updated_by ) ), '[]'::json) - FROM payments p - JOIN bookings b ON p.booking_id = b.id + FROM payments p + JOIN bookings b ON p.booking_id = b.id WHERE b.user_id = target_user_id ), 'export_metadata', json_build_object( @@ -495,7 +495,7 @@ BEGIN 'format_version', '1.0' ) ) INTO result; - + RETURN result; END; $$ LANGUAGE plpgsql; @@ -531,7 +531,7 @@ RETURNS TABLE ( ) AS $$ BEGIN RETURN QUERY - SELECT + SELECT start_date as period_start, end_date as period_end, COALESCE(SUM(p.amount), 0) as total_sales, @@ -540,20 +540,20 @@ BEGIN -- 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) + 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 + CASE -- If net amount is explicitly stored, use it WHEN p.net_amount IS NOT NULL THEN p.net_amount -- 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, + 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 @@ -595,7 +595,7 @@ RETURNS TABLE ( ) AS $$ BEGIN RETURN QUERY - SELECT + SELECT p.created_at::date as transaction_date, p.invoice_number::text AS invoice_number, COALESCE(u.fn, 'Walk-in Customer') as customer_name, @@ -603,17 +603,17 @@ BEGIN string_agg(s.name, ', ') as service_description, p.payment_method::text as payment_method, p.amount as gross_amount, - CASE + CASE WHEN include_vat AND p.net_amount IS NOT NULL THEN p.net_amount WHEN include_vat THEN ROUND(p.amount / 1.20, 2) ELSE p.amount END as net_amount, - CASE + CASE WHEN include_vat AND p.vat_amount IS NOT NULL THEN p.vat_amount WHEN include_vat THEN ROUND(p.amount - (p.amount / 1.20), 2) ELSE 0 END as vat_amount, - CASE + CASE WHEN include_vat THEN COALESCE(p.vat_rate, 20.00) ELSE NULL END as vat_rate, @@ -653,7 +653,7 @@ RETURNS TABLE ( ) AS $$ BEGIN RETURN QUERY - SELECT + SELECT TO_CHAR(p.created_at, 'YYYY-MM') as month_year, COUNT(DISTINCT b.id) as total_bookings, SUM(p.amount) as total_revenue, @@ -689,7 +689,7 @@ RETURNS TABLE ( ) AS $$ BEGIN RETURN QUERY - SELECT + SELECT COALESCE(SUM(p.amount), 0) as total_sales, COUNT(*) as total_transactions, COALESCE(SUM(CASE WHEN p.payment_method = 'cash' THEN p.amount ELSE 0 END), 0) as cash_total, @@ -740,8 +740,8 @@ DECLARE settings_ok BOOLEAN := FALSE; BEGIN -- Update all existing completed payments with VAT breakdown - UPDATE payments - SET + UPDATE payments + SET vat_rate = enable_vat_registration.vat_rate, vat_amount = ROUND(amount - (amount / (1 + enable_vat_registration.vat_rate/100)), 2), net_amount = ROUND(amount / (1 + enable_vat_registration.vat_rate/100), 2), @@ -750,33 +750,33 @@ BEGIN WHERE status = 'completed' AND created_at::date >= registration_date AND vat_amount IS NULL; - + GET DIAGNOSTICS updated_count = ROW_COUNT; - + -- Calculate totals - SELECT + SELECT COALESCE(SUM(vat_amount), 0), COALESCE(SUM(net_amount), 0) INTO total_vat, total_net - FROM payments - WHERE status = 'completed' + FROM payments + WHERE status = 'completed' AND created_at::date >= registration_date AND vat_amount IS NOT NULL; - + -- Update business_settings to reflect VAT registration UPDATE business_settings - SET + SET is_vat_registered = TRUE, vat_registration_number = COALESCE(vat_reg_number, vat_registration_number), default_vat_rate = enable_vat_registration.vat_rate, updated_at = NOW() WHERE id = 1; - + IF FOUND THEN settings_ok := TRUE; END IF; - RETURN QUERY SELECT + RETURN QUERY SELECT updated_count, total_vat, total_net, @@ -821,7 +821,7 @@ BEGIN -- Update payment with VAT amounts UPDATE payments - SET + SET vat_rate = effective_rate, vat_amount = ROUND(amount - (amount / (1 + effective_rate/100)), 2), net_amount = ROUND(amount / (1 + effective_rate/100), 2), @@ -865,7 +865,7 @@ BEGIN -- Return net and VAT RETURN QUERY - SELECT + SELECT ROUND(gross_amount / (1 + effective_rate/100), 2) AS net, gross_amount - ROUND(gross_amount / (1 + effective_rate/100), 2) AS vat; END; @@ -909,36 +909,36 @@ RETURNS TABLE ( business_email TEXT, vat_registration_number TEXT, is_vat_registered BOOLEAN, - + -- Payment Information payment_id CHAR(12), invoice_number TEXT, transaction_date TIMESTAMPTZ, payment_method TEXT, payment_status TEXT, - + -- Customer Information customer_id CHAR(12), customer_name TEXT, customer_email TEXT, customer_phone TEXT, - + -- Booking Information booking_id CHAR(12), appointment_date TIMESTAMPTZ, booking_status TEXT, - + -- Service Details services JSON, total_duration_minutes INT, - + -- Financial Information gross_amount NUMERIC(10,2), net_amount NUMERIC(10,2), vat_amount NUMERIC(10,2), vat_rate NUMERIC(5,2), is_vat_applicable BOOLEAN, - + -- Receipt Metadata receipt_generated_at TIMESTAMPTZ, currency_code TEXT @@ -959,7 +959,7 @@ BEGIN WHERE id = 1; RETURN QUERY - SELECT + SELECT -- Business Info bs.business_name, bs.business_address, @@ -967,34 +967,34 @@ BEGIN bs.business_email, bs.vat_registration_number, bs.is_vat_registered, - + -- Payment Info p.id as payment_id, COALESCE('INV-' || p.invoice_number::text, 'INV-' || p.id) as invoice_number, p.created_at as transaction_date, p.payment_method::text as payment_method, p.status::text as payment_status, - + -- Customer Info u.id as customer_id, - CASE + CASE WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN 'Walk-in Customer' ELSE u.fn END as customer_name, - CASE + CASE WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN NULL ELSE u.email END as customer_email, - CASE + CASE WHEN u.account_role = 'guest' OR u.fn = 'Deleted User' THEN NULL ELSE u.phone END as customer_phone, - + -- Booking Info b.id as booking_id, b.start_time as appointment_date, b.status::text as booking_status, - + -- Service Details COALESCE(( SELECT json_agg( @@ -1011,7 +1011,7 @@ BEGIN JOIN services s ON bs_inner.service_id = s.id WHERE bs_inner.booking_id = b.id ), '[]'::json) as services, - + -- Total service duration COALESCE(( SELECT SUM(s.duration_minutes) @@ -1019,18 +1019,18 @@ BEGIN JOIN services s ON bs_inner.service_id = s.id WHERE bs_inner.booking_id = b.id ), 0) as total_duration_minutes, - + -- Financial Info p.amount as gross_amount, COALESCE(p.net_amount, p.amount) as net_amount, COALESCE(p.vat_amount, 0.00) as vat_amount, p.vat_rate as vat_rate, p.is_vat_applicable as is_vat_applicable, - + -- Receipt Metadata NOW() as receipt_generated_at, bs.currency_code as currency_code - + FROM payments p JOIN bookings b ON p.booking_id = b.id LEFT JOIN users u ON b.user_id = u.id @@ -1111,4 +1111,4 @@ INTEGRATION QUICK REFERENCE • Monthly close → export_sales_transactions(..., include_vat := [true/false]) • VAT registration day → enable_vat_registration() once, then apply_vat_to_payment() • Dashboard load → get_sales_totals() + get_monthly_business_summary() -*/ \ No newline at end of file +*/