-- 12-email-sent-and-more.sql -- Add email_sent column to sales_orders, update notify_lead_qualified() to include -- rental_type and email_sent, update qualify_lead() to set email_sent=0, -- add sales_order_update_email_sent and sales_order_get_email_details RPCs. -- Idempotent. -- ============================================================================= -- A. Add email_sent to sales_orders -- ============================================================================= alter table public.sales_orders add column if not exists email_sent integer not null default 0; create index if not exists sales_orders_email_sent_idx on public.sales_orders (email_sent); -- ============================================================================= -- B. Update notify_lead_qualified() trigger function -- (defined in 10-mietvertrag-workflow.sql, overridden by 11-consolidate-km-rental.sql) -- Since migration 12 runs after 11, this is the final version. -- ============================================================================= create or replace function public.notify_lead_qualified() returns trigger language plpgsql security definer as $$ begin -- Skip notification for 'individuell' rental type if NEW.rental_type = 'individuell' then return NEW; end if; perform pg_notify('lead_qualified', json_build_object( 'sales_order_id', NEW.id, 'customer_id', NEW.customer_id, 'lead_id', NEW.lead_id, 'order_number', NEW.order_number, 'total_eur', NEW.total_eur, 'deposit_eur', NEW.deposit_eur, 'date_from', NEW.date_from, 'date_to', NEW.date_to, 'vehicle_label', NEW.vehicle_label, 'rental_type', NEW.rental_type, 'email_sent', NEW.email_sent )::text); return NEW; end; $$; -- ============================================================================= -- C. Update qualify_lead() RPC -- Add email_sent = 0 to the sales_orders insert. -- ============================================================================= create or replace function public.qualify_lead(p_lead_id uuid, p_notes text default '') returns public.customers language plpgsql security invoker as $$ declare v_lead public.leads; v_customer public.customers; v_sales_order public.sales_orders; v_user uuid := auth.uid(); v_order_num text; v_year integer; v_count integer; begin select * into v_lead from public.leads where id = p_lead_id for update; if not found then raise exception 'lead % not found', p_lead_id; end if; if v_lead.status = 'qualified' then select * into v_customer from public.customers where lower(email) = lower(v_lead.email) limit 1; return v_customer; end if; update public.leads set status = 'qualified', is_active = false, qualified_at = now(), qualified_by = v_user, admin_notes = coalesce(nullif(p_notes, ''), admin_notes) where id = v_lead.id; insert into public.customers (lead_id, name, email, phone, notes, created_by) values (v_lead.id, v_lead.name, v_lead.email, v_lead.phone, coalesce(p_notes,''), v_user) on conflict ((lower(email))) do update set name = excluded.name, phone = excluded.phone, notes = case when excluded.notes <> '' then excluded.notes else public.customers.notes end, updated_at = now() returning * into v_customer; v_year := extract(year from now())::integer; select coalesce(count(*), 0) + 1 into v_count from public.sales_orders where extract(year from created_at)::integer = v_year; v_order_num := 'SO-' || v_year || '-' || lpad(v_count::text, 4, '0'); insert into public.sales_orders ( customer_id, lead_id, order_number, private_notes, daily_subtotal, weekend_subtotal, subtotal_eur, vat_eur, total_eur, deposit_eur, total_days, weekday_count, weekend_day_count, date_from, date_to, vehicle_label, rental_type, email_sent ) values ( v_customer.id, v_lead.id, v_order_num, coalesce(v_lead.admin_notes, ''), coalesce(v_lead.daily_subtotal, 0), coalesce(v_lead.weekend_subtotal, 0), coalesce(v_lead.subtotal_eur, 0), coalesce(v_lead.vat_eur, 0), coalesce(v_lead.total_eur, 0), coalesce(v_lead.deposit_eur, 0), coalesce(v_lead.total_days, 0), coalesce(v_lead.weekday_count, 0), coalesce(v_lead.weekend_day_count, 0), v_lead.date_from, v_lead.date_to, v_lead.vehicle_label, v_lead.rental_type, 0 ) returning * into v_sales_order; insert into public.customer_attachments (customer_id, lead_id, sales_order_id, bucket, file_path, file_name, mime_type, kind, created_at) select v_customer.id, la.lead_id, v_sales_order.id, la.bucket, la.file_path, la.file_name, la.mime_type, la.kind, la.created_at from public.lead_attachments la where la.lead_id = v_lead.id and not exists ( select 1 from public.customer_attachments ca where ca.customer_id = v_customer.id and ca.file_path = la.file_path ); insert into public.sales_order_attachments (sales_order_id, bucket, file_path, file_name, mime_type, kind, created_at) select v_sales_order.id, la.bucket, la.file_path, la.file_name, la.mime_type, la.kind, la.created_at from public.lead_attachments la where la.lead_id = v_lead.id; return v_customer; end; $$; -- ============================================================================= -- D. New RPC: sales_order_update_email_sent -- ============================================================================= create or replace function public.sales_order_update_email_sent(p_so_id uuid, p_status integer) returns void language plpgsql security invoker as $$ declare v_so public.sales_orders; begin select * into v_so from public.sales_orders where id = p_so_id for update; if not found then raise exception 'sales order % not found', p_so_id; end if; if p_status not in (0, 1, 2) then raise exception 'invalid email_sent status: %', p_status; end if; update public.sales_orders set email_sent = p_status, updated_at = now() where id = p_so_id; end; $$; grant execute on function public.sales_order_update_email_sent(uuid, integer) to authenticated; -- ============================================================================= -- E. New RPC: sales_order_get_email_details -- ============================================================================= create or replace function public.sales_order_get_email_details(p_so_id uuid) returns jsonb language plpgsql security definer as $$ declare v_result jsonb; begin select jsonb_build_object( 'order_number', so.order_number, 'total_eur', so.total_eur, 'deposit_eur', so.deposit_eur, 'date_from', so.date_from, 'date_to', so.date_to, 'vehicle_label', so.vehicle_label, 'customer_name', c.name, 'customer_email', c.email, 'customer_phone', c.phone, 'daily_subtotal', so.daily_subtotal, 'weekend_subtotal', so.weekend_subtotal, 'subtotal_eur', so.subtotal_eur, 'vat_eur', so.vat_eur, 'total_days', so.total_days, 'weekday_count', so.weekday_count, 'weekend_day_count', so.weekend_day_count ) into v_result from public.sales_orders so join public.customers c on c.id = so.customer_id where so.id = p_so_id; if v_result is null then raise exception 'sales order % not found', p_so_id; end if; return v_result; end; $$; grant execute on function public.sales_order_get_email_details(uuid) to authenticated; -- ============================================================================= -- F. Final schema reload -- ============================================================================= notify pgrst, 'reload schema';