autopilot-core-sandbox 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/dist/Provider.d.ts +21 -0
- package/dist/Provider.d.ts.map +1 -0
- package/dist/Provider.js +61 -0
- package/dist/Provider.js.map +1 -0
- package/dist/dist/Provider.d.ts +21 -0
- package/dist/dist/Provider.d.ts.map +1 -0
- package/dist/dist/Provider.js +61 -0
- package/dist/dist/Provider.js.map +1 -0
- package/dist/dist/index.d.ts +45 -0
- package/dist/dist/index.d.ts.map +1 -0
- package/dist/dist/index.js +61 -0
- package/dist/dist/index.js.map +1 -0
- package/dist/dist/interfaces/index.d.ts +1238 -0
- package/dist/dist/interfaces/index.d.ts.map +1 -0
- package/dist/dist/interfaces/index.js +3 -0
- package/dist/dist/interfaces/index.js.map +1 -0
- package/dist/dist/migrations/20260608222757_remote_schema.sql +5666 -0
- package/dist/dist/migrations/20260611174826_add_job_item_recurring_id_to_job_item.sql +7 -0
- package/dist/dist/migrations/20260625224612_add_dashboard_guided_tour_dismissed_to_settings_user.sql +5 -0
- package/dist/dist/migrations/20260627031433_support_chat_tables.sql +115 -0
- package/dist/dist/migrations/20260627031434_support_chat_rls.sql +37 -0
- package/dist/dist/migrations/20260704170209_add_estimate_scheduling.sql +121 -0
- package/dist/dist/migrations/20260704170210_add_estimate_scheduling_rls.sql +42 -0
- package/dist/dist/migrations/20260704235034_add_estimate_reminder_settings.sql +11 -0
- package/dist/dist/migrations/20260707003825_blocked_number_table.sql +32 -0
- package/dist/dist/migrations/20260707003826_blocked_number_rls.sql +22 -0
- package/dist/dist/migrations/20260709191055_sms_opt_out_table.sql +38 -0
- package/dist/dist/migrations/20260709191056_sms_opt_out_rls.sql +21 -0
- package/dist/dist/migrations/20260709214507_job_time_entry_table.sql +70 -0
- package/dist/dist/migrations/20260709214508_job_time_entry_rls.sql +21 -0
- package/dist/dist/migrations/20260711185956_phone_call_table.sql +99 -0
- package/dist/dist/migrations/20260711185957_phone_call_rls.sql +25 -0
- package/dist/dist/migrations/20260712014705_ai_voice_assistant_settings.sql +71 -0
- package/dist/dist/migrations/20260712014706_ai_voice_assistant_settings_rls.sql +14 -0
- package/dist/dist/migrations/20260712014707_ai_prompt_templates.sql +28 -0
- package/dist/dist/migrations/20260712014708_ai_prompt_templates_rls.sql +5 -0
- package/dist/dist/migrations/20260712014709_ai_prompt_templates_seed.sql +77 -0
- package/dist/dist/migrations/20260715011550_reminder_dispatch_table.sql +47 -0
- package/dist/dist/migrations/20260715011551_reminder_dispatch_rls.sql +22 -0
- package/dist/dist/migrations/20260715192923_dedup_sequence_contact_add_unique_constraint.sql +27 -0
- package/dist/dist/migrations/20260716194420_add_employee_location_tracking.sql +146 -0
- package/dist/dist/migrations/20260716194421_add_employee_location_tracking_rls.sql +154 -0
- package/dist/dist/migrations/20260716194422_get_client_map_last_completed_jobs.sql +28 -0
- package/dist/dist/migrations/20260717144535_add_blast_email_invalid_skipped_count.sql +2 -0
- package/dist/dist/migrations/20260717162900_create_blast_email_chunk.sql +36 -0
- package/dist/dist/migrations/20260717162901_add_blast_email_chunk_rls.sql +5 -0
- package/dist/dist/migrations/20260717162902_add_blast_email_chunk_progress_columns.sql +3 -0
- package/dist/dist/migrations/20260717162903_add_blast_email_bounce_tracking_columns.sql +4 -0
- package/dist/dist/migrations/20260717162904_create_email_hard_bounced.sql +23 -0
- package/dist/dist/migrations/20260717162905_add_email_hard_bounced_rls.sql +5 -0
- package/dist/dist/migrations/20260717164837_add_sequence_contact_sent_at.sql +19 -0
- package/dist/dist/migrations/20260717170920_add_sequence_contact_claim_index.sql +12 -0
- package/dist/dist/migrations/20260719215139_create_outbound_event.sql +59 -0
- package/dist/dist/migrations/20260719215140_create_outbound_event_rls.sql +16 -0
- package/dist/dist/migrations/20260719215141_create_webhook_delivery.sql +99 -0
- package/dist/dist/migrations/20260719215142_create_webhook_delivery_rls.sql +11 -0
- package/dist/dist/migrations/20260719215143_outbound_event_triggers.sql +219 -0
- package/dist/dist/migrations/20260719215144_create_api_key.sql +34 -0
- package/dist/dist/migrations/20260719215146_create_api_key_rls.sql +12 -0
- package/dist/dist/migrations/20260719215147_webhook_subscription_add_secret_active.sql +28 -0
- package/dist/dist/migrations/20260719215148_api_aggregate_create_functions.sql +479 -0
- package/dist/dist/migrations/20260721174837_webhook_subscription_generic_endpoints.sql +41 -0
- package/dist/dist/migrations/20260721185631_webhook_outbox_retention.sql +87 -0
- package/dist/dist/migrations/20260721190304_create_qb_sync_task.sql +120 -0
- package/dist/dist/migrations/20260721190306_qb_sync_task_rls.sql +8 -0
- package/dist/dist/migrations/20260721190307_qb_mapping_orphan_fks.sql +29 -0
- package/dist/dist/migrations/20260721190308_qb_task_retention.sql +90 -0
- package/dist/dist/migrations/20260721234401_create_permission_role.sql +83 -0
- package/dist/dist/migrations/20260721234402_permission_role_rls.sql +22 -0
- package/dist/dist/migrations/20260721234403_user_role_add_permission_role_id.sql +12 -0
- package/dist/dist/migrations/20260721234404_seed_permission_roles.sql +100 -0
- package/dist/dist/migrations/20260721234405_backfill_user_role_permission_role_id.sql +20 -0
- package/dist/dist/migrations/20260722193800_permission_role_add_manage_employees.sql +56 -0
- package/dist/dist/migrations/20260723025606_staging_new_employee_company_read_rls.sql +10 -0
- package/dist/dist/migrations/20260723134328_create_google_lsa_integration.sql +1016 -0
- package/dist/dist/migrations/20260723134329_google_lsa_integration_rls.sql +205 -0
- package/dist/dist/migrations/20260723150640_google_lsa_hardening.sql +169 -0
- package/dist/dist/migrations/20260723150641_google_lsa_hardening_rls.sql +12 -0
- package/dist/dist/migrations/20260724230721_google_lsa_facebook_exclusivity_attach.sql +104 -0
- package/dist/dist/migrations/20260724230722_google_lsa_facebook_exclusivity_attach_rls.sql +20 -0
- package/dist/dist/migrations/20260725172630_add_message_pagination_and_search.sql +265 -0
- package/dist/dist/migrations/20260725172631_add_message_pagination_and_search_rls.sql +72 -0
- package/dist/dist/migrations/20260725172632_optimize_message_thread_pagination_search.sql +245 -0
- package/dist/dist/migrations/20260725172633_harden_message_search_and_photo_pagination.sql +256 -0
- package/dist/dist/migrations/20260725172634_fix_message_thread_line_filter.sql +250 -0
- package/dist/dist/migrations/20260725174948_google_lsa_attach_bing_exclusivity_guard.sql +23 -0
- package/dist/dist/migrations/20260725200430_client_portal_settings.sql +41 -0
- package/dist/dist/migrations/20260725200431_client_portal_settings_rls.sql +21 -0
- package/dist/dist/migrations/20260725200433_client_portal_auth_tables.sql +81 -0
- package/dist/dist/migrations/20260725200434_client_portal_auth_rls.sql +18 -0
- package/dist/dist/migrations/20260725200435_portal_claim_login_attempt.sql +36 -0
- package/dist/dist/migrations/20260725200436_portal_appointment_request.sql +39 -0
- package/dist/dist/migrations/20260725200437_portal_appointment_request_rls.sql +10 -0
- package/dist/dist/migrations/20260725210000_google_reserve_integration.sql +85 -0
- package/dist/dist/migrations/20260725210001_google_reserve_integration_rls.sql +10 -0
- package/dist/dist/migrations/20260725220000_add_analytics_query_indexes.sql +47 -0
- package/dist/dist/migrations/20260725230000_onboarding_preload_data.sql +1074 -0
- package/dist/dist/migrations/20260725230001_onboarding_preload_data_rls.sql +40 -0
- package/dist/dist/migrations/20260725230002_add_pricebook_query_indexes.sql +13 -0
- package/dist/dist/migrations/20260726000000_facebook_ads_integration.sql +1538 -0
- package/dist/dist/migrations/20260726000001_facebook_ads_integration_rls.sql +356 -0
- package/dist/dist/migrations/20260726000002_facebook_ads_attach_lsa_exclusivity_guard.sql +25 -0
- package/dist/dist/migrations/20260726000003_facebook_ads_attach_bing_exclusivity_guard.sql +22 -0
- package/dist/dist/migrations/20260726010000_create_bing_ads_integration.sql +1112 -0
- package/dist/dist/migrations/20260726010001_bing_ads_integration_rls.sql +227 -0
- package/dist/dist/migrations/20260726220000_add_document_theme_settings.sql +11 -0
- package/dist/dist/migrations/20260726220001_update_document_theme_comments.sql +7 -0
- package/dist/dist/migrations/20260727210319_evolve_tier_for_pricing.sql +68 -0
- package/dist/dist/migrations/20260727210320_create_billing_addon.sql +85 -0
- package/dist/dist/migrations/20260727210321_create_billing_addon_rls.sql +21 -0
- package/dist/dist/migrations/20260728213930_create_usage_metering.sql +189 -0
- package/dist/dist/migrations/20260728213931_create_usage_metering_rls.sql +32 -0
- package/dist/dist/migrations/20260729004305_marketing_charge_txn_functions.sql +242 -0
- package/dist/dist/migrations/20260729004306_marketing_charge_txn_functions_rls.sql +37 -0
- package/dist/dist/migrations/20260729005524_update_deduct_from_balance_errcode.sql +42 -0
- package/dist/dist/migrations/20260730193601_fix_deduct_from_balance_lost_update.sql +46 -0
- package/dist/dist/migrations/20260730193602_add_blast_sms_chunk_reaper_columns.sql +27 -0
- package/dist/dist/migrations/20260731011415_create_google_calendar_connection.sql +84 -0
- package/dist/dist/migrations/20260731011416_google_calendar_connection_rls.sql +23 -0
- package/dist/dist/migrations/20260731011417_create_google_calendar_sync.sql +157 -0
- package/dist/dist/migrations/20260731011418_google_calendar_sync_rls.sql +26 -0
- package/dist/dist/migrations/20260731011419_event_outbound_triggers.sql +28 -0
- package/dist/dist/migrations/20260731011420_add_google_calendar_addon_key.sql +24 -0
- package/dist/dist/migrations/20260731011421_gcal_task_retention.sql +109 -0
- package/dist/dist/migrations/20260731234245_create_estimate_proposal.sql +157 -0
- package/dist/dist/migrations/20260731234247_estimate_proposal_rls.sql +46 -0
- package/dist/dist/migrations/20260731234248_add_proposal_estimate_settings.sql +17 -0
- package/dist/dist/migrations/20260731234249_add_estimate_proposals_addon_key.sql +25 -0
- package/dist/dist/migrations/20260731234250_approve_estimate_proposal_function.sql +133 -0
- package/dist/dist/migrations/20260801010609_save_estimate_proposal_function.sql +154 -0
- package/dist/dist/migrations/20260801173554_add_hide_autopilot_branding_setting.sql +6 -0
- package/dist/dist/migrations/20260806230917_add_client_billing_address_columns.sql +9 -0
- package/dist/dist/migrations/20260806230944_api_create_client_billing_address.sql +93 -0
- package/dist/dist/migrations/20260807014702_create_parent_id_company_id_keys.sql +20 -0
- package/dist/dist/migrations/20260807014703_create_contract_template.sql +30 -0
- package/dist/dist/migrations/20260807014704_create_contract.sql +233 -0
- package/dist/dist/migrations/20260807014705_create_contract_signature.sql +39 -0
- package/dist/dist/migrations/20260807014706_create_item_contract_requirement.sql +38 -0
- package/dist/dist/migrations/20260807014707_create_settings_contract.sql +23 -0
- package/dist/dist/migrations/20260807014708_contract_rls.sql +74 -0
- package/dist/dist/migrations/20260807014709_sign_contract_function.sql +92 -0
- package/dist/dist/migrations/20260807014710_create_settings_rental.sql +34 -0
- package/dist/dist/migrations/20260807014711_create_saved_location.sql +56 -0
- package/dist/dist/migrations/20260807014712_create_rental_task_option.sql +52 -0
- package/dist/dist/migrations/20260807014713_create_asset.sql +90 -0
- package/dist/dist/migrations/20260807014714_add_job_schedule_asset_fkeys.sql +39 -0
- package/dist/dist/migrations/20260807014715_rental_rls.sql +48 -0
- package/dist/dist/migrations/20260807014716_add_job_schedule_any_time.sql +7 -0
- package/dist/dist/migrations/20260807014717_add_job_schedule_target_location.sql +48 -0
- package/dist/dist/migrations/20260807014718_job_schedule_completion_guard.sql +53 -0
- package/dist/dist/migrations/20260807014719_add_job_schedule_list_sort_key.sql +7 -0
- package/dist/dist/migrations/20260807014720_add_item_duration.sql +11 -0
- package/dist/dist/migrations/20260807014721_add_settings_booking_enforce_rental_availability.sql +9 -0
- package/dist/dist/migrations/20260807014722_add_job_unsigned_rental_agreement.sql +10 -0
- package/dist/dist/migrations/20260807014723_add_item_size.sql +12 -0
- package/dist/dist/migrations/20260809193533_add_booking_path_button_labels.sql +11 -0
- package/dist/dist/migrations/20260809231721_add_route_distance_cache.sql +37 -0
- package/dist/dist/migrations/20260809231722_route_distance_cache_rls.sql +15 -0
- package/dist/dist/migrations/20260809231723_apply_rental_route_plan_function.sql +275 -0
- package/dist/dist/migrations/20260810171550_add_settings_job_default_job_types.sql +9 -0
- package/dist/dist/migrations/20260810212430_backfill_rental_task_option_sort_order.sql +61 -0
- package/dist/dist/migrations/20260811224317_add_settings_user_dnd_hint_dismissed.sql +9 -0
- package/dist/dist/migrations/20260813174503_repair_user_role_email_drift.sql +59 -0
- package/dist/dist/migrations/20260813174504_sync_user_role_email_trigger.sql +30 -0
- package/dist/dist/migrations/20260813175210_add_event_list_sort_key.sql +5 -0
- package/dist/dist/migrations/20260814172222_add_client_auto_bill_on_completion.sql +6 -0
- package/dist/dist/migrations/20260816160252_create_automation_rule.sql +99 -0
- package/dist/dist/migrations/20260816160253_automation_rule_rls.sql +19 -0
- package/dist/dist/migrations/20260816160254_create_settings_automation.sql +36 -0
- package/dist/dist/migrations/20260816160255_settings_automation_rls.sql +18 -0
- package/dist/dist/migrations/20260816160256_create_automation_event.sql +71 -0
- package/dist/dist/migrations/20260816160257_automation_event_rls.sql +12 -0
- package/dist/dist/migrations/20260816160258_create_automation_run.sql +129 -0
- package/dist/dist/migrations/20260816160259_automation_run_rls.sql +19 -0
- package/dist/dist/migrations/20260816160300_add_client_created_via.sql +25 -0
- package/dist/dist/migrations/20260816160301_add_estimate_sent_at.sql +39 -0
- package/dist/dist/migrations/20260816160302_add_message_automation_ref.sql +10 -0
- package/dist/dist/migrations/20260816160303_automation_event_triggers.sql +236 -0
- package/dist/dist/migrations/20260816160304_automation_retention.sql +142 -0
- package/dist/dist/migrations/20260816202404_add_automation_center_addon_key.sql +26 -0
- package/dist/dist/migrations/20260816225501_widen_automation_rule_trigger_action_checks.sql +25 -0
- package/dist/dist/migrations/20260816225502_widen_automation_event_type_check.sql +18 -0
- package/dist/dist/migrations/20260816225503_add_invoice_id_to_automation_tables.sql +18 -0
- package/dist/dist/migrations/20260816225504_add_job_rescheduled_capture_trigger.sql +51 -0
- package/dist/dist/migrations/20260816225505_add_lead_converted_capture_trigger.sql +26 -0
- package/dist/dist/migrations/20260816225506_fix_booking_request_import_guard.sql +27 -0
- package/dist/dist/migrations/20260816225507_widen_outbound_event_for_automation.sql +38 -0
- package/dist/dist/migrations/20260817002626_add_user_last_login_platform_columns.sql +14 -0
- package/dist/dist/migrations/20260817221703_create_deletion.sql +34 -0
- package/dist/dist/migrations/20260817221707_deletion_rls.sql +21 -0
- package/dist/dist/migrations/20260817221708_add_soft_delete_columns.sql +82 -0
- package/dist/dist/migrations/20260817221709_trash_functions.sql +434 -0
- package/dist/dist/migrations/20260817221710_outbound_event_soft_delete.sql +55 -0
- package/dist/dist/migrations/20260817221711_automation_soft_delete_events.sql +77 -0
- package/dist/dist/migrations/20260817221712_fix_estimate_address_cascade.sql +14 -0
- package/dist/dist/migrations/20260817232630_trash_batch_function.sql +67 -0
- package/dist/dist/migrations/20260819170204_create_email_spam_complaint.sql +23 -0
- package/dist/dist/migrations/20260819170205_add_email_spam_complaint_rls.sql +5 -0
- package/dist/dist/migrations/20260819170206_email_unsubscribed_upsert_constraints.sql +14 -0
- package/dist/dist/migrations/20260819180151_get_suppressed_emails_function.sql +23 -0
- package/dist/dist/migrations/20260819191143_is_deleted_backfill_guard.sql +29 -0
- package/dist/dist/migrations/20260819191144_trash_functions_no_dual_write.sql +319 -0
- package/dist/dist/migrations/20260819191145_client_map_jobs_deleted_at.sql +33 -0
- package/dist/dist/migrations/20260819191147_booking_declined_deleted_at.sql +28 -0
- package/dist/dist/migrations/20260819191148_onboarding_preload_no_address_is_deleted.sql +1019 -0
- package/dist/dist/migrations/20260820222756_add_settings_job_hide_task_timer.sql +2 -0
- package/dist/dist/migrations/20260824234151_add_blast_chunk_recipients.sql +13 -0
- package/dist/dist/migrations/20260824234152_add_blast_pause_cancel_columns.sql +10 -0
- package/dist/dist/migrations/20260825014725_get_job_ids_in_range.sql +42 -0
- package/dist/dist/migrations/20260825194452_add_failed_job_status.sql +15 -0
- package/dist/dist/migrations/20260825194457_remove_take_to_rental_task_options.sql +24 -0
- package/dist/dist/migrations/20260825194458_remove_take_to_job_schedules.sql +12 -0
- package/dist/dist/migrations/20260825194459_rework_apply_rental_route_plan_no_take_to.sql +168 -0
- package/dist/dist/migrations/20260825194500_drop_job_schedule_target_location.sql +24 -0
- package/dist/dist/migrations/20260825194501_add_job_schedule_size.sql +17 -0
- package/dist/dist/migrations/20260825233001_add_job_schedule_id_company_id_key.sql +11 -0
- package/dist/dist/migrations/20260825233002_create_route_plan.sql +52 -0
- package/dist/dist/migrations/20260825233003_create_route_plan_stop.sql +159 -0
- package/dist/dist/migrations/20260825233004_route_plan_rls.sql +32 -0
- package/dist/dist/migrations/20260825233005_apply_rental_route_plan_v2.sql +351 -0
- package/dist/dist/migrations/20260826150934_facebook_ads_lead_ingestion.sql +1711 -0
- package/dist/dist/migrations/20260826151141_facebook_ads_lead_ingestion_rls_v2.sql +43 -0
- package/dist/dist/migrations/20260826234722_facebook_messenger_ingestion.sql +703 -0
- package/dist/dist/migrations/20260826234723_facebook_messenger_ingestion_rls.sql +80 -0
- package/dist/dist/migrations/20260826234724_widen_automations_messenger.sql +46 -0
- package/dist/dist/migrations/20260827100001_add_route_plan_minutes.sql +10 -0
- package/dist/dist/migrations/20260827100002_add_route_plan_stop_leg_minutes.sql +9 -0
- package/dist/dist/migrations/20260827100003_apply_rental_route_plan_v3.sql +341 -0
- package/dist/dist/migrations/20260827230346_apply_rental_route_plan_v4.sql +366 -0
- package/dist/dist/migrations/20260829090911_create_fleet_schema.sql +3698 -0
- package/dist/dist/migrations/20260829090912_fleet_rls.sql +1011 -0
- package/dist/dist/migrations/20260831102212_add_online_booking_address_display.sql +8 -0
- package/dist/dist/providers/AddressProvider.d.ts +9 -0
- package/dist/dist/providers/AddressProvider.d.ts.map +1 -0
- package/dist/dist/providers/AddressProvider.js +207 -0
- package/dist/dist/providers/AddressProvider.js.map +1 -0
- package/dist/dist/providers/AutomationsProvider.d.ts +11 -0
- package/dist/dist/providers/AutomationsProvider.d.ts.map +1 -0
- package/dist/dist/providers/AutomationsProvider.js +170 -0
- package/dist/dist/providers/AutomationsProvider.js.map +1 -0
- package/dist/dist/providers/BlockedNumbersProvider.d.ts +13 -0
- package/dist/dist/providers/BlockedNumbersProvider.d.ts.map +1 -0
- package/dist/dist/providers/BlockedNumbersProvider.js +71 -0
- package/dist/dist/providers/BlockedNumbersProvider.js.map +1 -0
- package/dist/dist/providers/CategoryProvider.d.ts +8 -0
- package/dist/dist/providers/CategoryProvider.d.ts.map +1 -0
- package/dist/dist/providers/CategoryProvider.js +95 -0
- package/dist/dist/providers/CategoryProvider.js.map +1 -0
- package/dist/dist/providers/ClientProvider.d.ts +39 -0
- package/dist/dist/providers/ClientProvider.d.ts.map +1 -0
- package/dist/dist/providers/ClientProvider.js +253 -0
- package/dist/dist/providers/ClientProvider.js.map +1 -0
- package/dist/dist/providers/CompanyProvider.d.ts +7 -0
- package/dist/dist/providers/CompanyProvider.d.ts.map +1 -0
- package/dist/dist/providers/CompanyProvider.js +114 -0
- package/dist/dist/providers/CompanyProvider.js.map +1 -0
- package/dist/dist/providers/DashboardProvider.d.ts +78 -0
- package/dist/dist/providers/DashboardProvider.d.ts.map +1 -0
- package/dist/dist/providers/DashboardProvider.js +568 -0
- package/dist/dist/providers/DashboardProvider.js.map +1 -0
- package/dist/dist/providers/DiscountProvider.d.ts +8 -0
- package/dist/dist/providers/DiscountProvider.d.ts.map +1 -0
- package/dist/dist/providers/DiscountProvider.js +92 -0
- package/dist/dist/providers/DiscountProvider.js.map +1 -0
- package/dist/dist/providers/EmployeesProvider.d.ts +6 -0
- package/dist/dist/providers/EmployeesProvider.d.ts.map +1 -0
- package/dist/dist/providers/EmployeesProvider.js +185 -0
- package/dist/dist/providers/EmployeesProvider.js.map +1 -0
- package/dist/dist/providers/EstimatesProvider.d.ts +16 -0
- package/dist/dist/providers/EstimatesProvider.d.ts.map +1 -0
- package/dist/dist/providers/EstimatesProvider.js +251 -0
- package/dist/dist/providers/EstimatesProvider.js.map +1 -0
- package/dist/dist/providers/EventsProvider.d.ts +4 -0
- package/dist/dist/providers/EventsProvider.d.ts.map +1 -0
- package/dist/dist/providers/EventsProvider.js +24 -0
- package/dist/dist/providers/EventsProvider.js.map +1 -0
- package/dist/dist/providers/FilesProvider.d.ts +17 -0
- package/dist/dist/providers/FilesProvider.d.ts.map +1 -0
- package/dist/dist/providers/FilesProvider.js +201 -0
- package/dist/dist/providers/FilesProvider.js.map +1 -0
- package/dist/dist/providers/InvoicesProvider.d.ts +13 -0
- package/dist/dist/providers/InvoicesProvider.d.ts.map +1 -0
- package/dist/dist/providers/InvoicesProvider.js +207 -0
- package/dist/dist/providers/InvoicesProvider.js.map +1 -0
- package/dist/dist/providers/ItemProvider.d.ts +9 -0
- package/dist/dist/providers/ItemProvider.d.ts.map +1 -0
- package/dist/dist/providers/ItemProvider.js +201 -0
- package/dist/dist/providers/ItemProvider.js.map +1 -0
- package/dist/dist/providers/JobSourceProvider.d.ts +8 -0
- package/dist/dist/providers/JobSourceProvider.d.ts.map +1 -0
- package/dist/dist/providers/JobSourceProvider.js +75 -0
- package/dist/dist/providers/JobSourceProvider.js.map +1 -0
- package/dist/dist/providers/JobTypeProvider.d.ts +8 -0
- package/dist/dist/providers/JobTypeProvider.d.ts.map +1 -0
- package/dist/dist/providers/JobTypeProvider.js +75 -0
- package/dist/dist/providers/JobTypeProvider.js.map +1 -0
- package/dist/dist/providers/JobsProvider.d.ts +35 -0
- package/dist/dist/providers/JobsProvider.d.ts.map +1 -0
- package/dist/dist/providers/JobsProvider.js +863 -0
- package/dist/dist/providers/JobsProvider.js.map +1 -0
- package/dist/dist/providers/MessagesProvider.d.ts +33 -0
- package/dist/dist/providers/MessagesProvider.d.ts.map +1 -0
- package/dist/dist/providers/MessagesProvider.js +415 -0
- package/dist/dist/providers/MessagesProvider.js.map +1 -0
- package/dist/dist/providers/PaymentProvider.d.ts +9 -0
- package/dist/dist/providers/PaymentProvider.d.ts.map +1 -0
- package/dist/dist/providers/PaymentProvider.js +48 -0
- package/dist/dist/providers/PaymentProvider.js.map +1 -0
- package/dist/dist/providers/PermissionRolesProvider.d.ts +5 -0
- package/dist/dist/providers/PermissionRolesProvider.d.ts.map +1 -0
- package/dist/dist/providers/PermissionRolesProvider.js +30 -0
- package/dist/dist/providers/PermissionRolesProvider.js.map +1 -0
- package/dist/dist/providers/PhoneNumberProvider.d.ts +12 -0
- package/dist/dist/providers/PhoneNumberProvider.d.ts.map +1 -0
- package/dist/dist/providers/PhoneNumberProvider.js +184 -0
- package/dist/dist/providers/PhoneNumberProvider.js.map +1 -0
- package/dist/dist/providers/ProfileProvider.d.ts +6 -0
- package/dist/dist/providers/ProfileProvider.d.ts.map +1 -0
- package/dist/dist/providers/ProfileProvider.js +71 -0
- package/dist/dist/providers/ProfileProvider.js.map +1 -0
- package/dist/dist/providers/RentalsProvider.d.ts +28 -0
- package/dist/dist/providers/RentalsProvider.d.ts.map +1 -0
- package/dist/dist/providers/RentalsProvider.js +438 -0
- package/dist/dist/providers/RentalsProvider.js.map +1 -0
- package/dist/dist/providers/SettingsBookingProvider.d.ts +9 -0
- package/dist/dist/providers/SettingsBookingProvider.d.ts.map +1 -0
- package/dist/dist/providers/SettingsBookingProvider.js +163 -0
- package/dist/dist/providers/SettingsBookingProvider.js.map +1 -0
- package/dist/dist/providers/SettingsProvider.d.ts +8 -0
- package/dist/dist/providers/SettingsProvider.d.ts.map +1 -0
- package/dist/dist/providers/SettingsProvider.js +86 -0
- package/dist/dist/providers/SettingsProvider.js.map +1 -0
- package/dist/dist/providers/TagsProvider.d.ts +12 -0
- package/dist/dist/providers/TagsProvider.d.ts.map +1 -0
- package/dist/dist/providers/TagsProvider.js +210 -0
- package/dist/dist/providers/TagsProvider.js.map +1 -0
- package/dist/dist/providers/TimeCardProvider.d.ts +8 -0
- package/dist/dist/providers/TimeCardProvider.d.ts.map +1 -0
- package/dist/dist/providers/TimeCardProvider.js +93 -0
- package/dist/dist/providers/TimeCardProvider.js.map +1 -0
- package/dist/dist/providers/TrashProvider.d.ts +13 -0
- package/dist/dist/providers/TrashProvider.d.ts.map +1 -0
- package/dist/dist/providers/TrashProvider.js +98 -0
- package/dist/dist/providers/TrashProvider.js.map +1 -0
- package/dist/dist/providers/dashboardAnalytics.d.ts +9 -0
- package/dist/dist/providers/dashboardAnalytics.d.ts.map +1 -0
- package/dist/dist/providers/dashboardAnalytics.js +41 -0
- package/dist/dist/providers/dashboardAnalytics.js.map +1 -0
- package/dist/dist/providers/dashboardAnalytics.test.d.ts +2 -0
- package/dist/dist/providers/dashboardAnalytics.test.d.ts.map +1 -0
- package/dist/dist/providers/dashboardAnalytics.test.js +111 -0
- package/dist/dist/providers/dashboardAnalytics.test.js.map +1 -0
- package/dist/dist/server-safe/BlockedNumberFunctions.d.ts +11 -0
- package/dist/dist/server-safe/BlockedNumberFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/BlockedNumberFunctions.js +16 -0
- package/dist/dist/server-safe/BlockedNumberFunctions.js.map +1 -0
- package/dist/dist/server-safe/EmployeeFunctions.d.ts +14 -0
- package/dist/dist/server-safe/EmployeeFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/EmployeeFunctions.js +70 -0
- package/dist/dist/server-safe/EmployeeFunctions.js.map +1 -0
- package/dist/dist/server-safe/EmployeeLocationFunctions.d.ts +7 -0
- package/dist/dist/server-safe/EmployeeLocationFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/EmployeeLocationFunctions.js +47 -0
- package/dist/dist/server-safe/EmployeeLocationFunctions.js.map +1 -0
- package/dist/dist/server-safe/EstimateFunctions.d.ts +15 -0
- package/dist/dist/server-safe/EstimateFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/EstimateFunctions.js +34 -0
- package/dist/dist/server-safe/EstimateFunctions.js.map +1 -0
- package/dist/dist/server-safe/InvoiceFunctions.d.ts +21 -0
- package/dist/dist/server-safe/InvoiceFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/InvoiceFunctions.js +124 -0
- package/dist/dist/server-safe/InvoiceFunctions.js.map +1 -0
- package/dist/dist/server-safe/JobFunctions.d.ts +15 -0
- package/dist/dist/server-safe/JobFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/JobFunctions.js +56 -0
- package/dist/dist/server-safe/JobFunctions.js.map +1 -0
- package/dist/dist/server-safe/PaymentFunctions.d.ts +16 -0
- package/dist/dist/server-safe/PaymentFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/PaymentFunctions.js +80 -0
- package/dist/dist/server-safe/PaymentFunctions.js.map +1 -0
- package/dist/dist/server-safe/PermissionFunctions.d.ts +7 -0
- package/dist/dist/server-safe/PermissionFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/PermissionFunctions.js +39 -0
- package/dist/dist/server-safe/PermissionFunctions.js.map +1 -0
- package/dist/dist/server-safe/TagFunctions.d.ts +18 -0
- package/dist/dist/server-safe/TagFunctions.d.ts.map +1 -0
- package/dist/dist/server-safe/TagFunctions.js +52 -0
- package/dist/dist/server-safe/TagFunctions.js.map +1 -0
- package/dist/dist/server-safe/autoJobType.d.ts +16 -0
- package/dist/dist/server-safe/autoJobType.d.ts.map +1 -0
- package/dist/dist/server-safe/autoJobType.js +39 -0
- package/dist/dist/server-safe/autoJobType.js.map +1 -0
- package/dist/dist/server-safe/autoJobType.test.d.ts +2 -0
- package/dist/dist/server-safe/autoJobType.test.d.ts.map +1 -0
- package/dist/dist/server-safe/autoJobType.test.js +83 -0
- package/dist/dist/server-safe/autoJobType.test.js.map +1 -0
- package/dist/dist/server-safe/automations.d.ts +197 -0
- package/dist/dist/server-safe/automations.d.ts.map +1 -0
- package/dist/dist/server-safe/automations.js +757 -0
- package/dist/dist/server-safe/automations.js.map +1 -0
- package/dist/dist/server-safe/automations.test.d.ts +2 -0
- package/dist/dist/server-safe/automations.test.d.ts.map +1 -0
- package/dist/dist/server-safe/automations.test.js +441 -0
- package/dist/dist/server-safe/automations.test.js.map +1 -0
- package/dist/dist/server-safe/constants.d.ts +156 -0
- package/dist/dist/server-safe/constants.d.ts.map +1 -0
- package/dist/dist/server-safe/constants.js +186 -0
- package/dist/dist/server-safe/constants.js.map +1 -0
- package/dist/dist/server-safe/contracts.d.ts +3 -0
- package/dist/dist/server-safe/contracts.d.ts.map +1 -0
- package/dist/dist/server-safe/contracts.js +15 -0
- package/dist/dist/server-safe/contracts.js.map +1 -0
- package/dist/dist/server-safe/database.types.d.ts +9510 -0
- package/dist/dist/server-safe/database.types.d.ts.map +1 -0
- package/dist/dist/server-safe/database.types.js +40 -0
- package/dist/dist/server-safe/database.types.js.map +1 -0
- package/dist/dist/server-safe/entitlements.d.ts +99 -0
- package/dist/dist/server-safe/entitlements.d.ts.map +1 -0
- package/dist/dist/server-safe/entitlements.js +487 -0
- package/dist/dist/server-safe/entitlements.js.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeads.d.ts +225 -0
- package/dist/dist/server-safe/facebookAdsLeads.d.ts.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeads.js +376 -0
- package/dist/dist/server-safe/facebookAdsLeads.js.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeads.test.d.ts +2 -0
- package/dist/dist/server-safe/facebookAdsLeads.test.d.ts.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeads.test.js +189 -0
- package/dist/dist/server-safe/facebookAdsLeads.test.js.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeadsSql.test.d.ts +2 -0
- package/dist/dist/server-safe/facebookAdsLeadsSql.test.d.ts.map +1 -0
- package/dist/dist/server-safe/facebookAdsLeadsSql.test.js +32 -0
- package/dist/dist/server-safe/facebookAdsLeadsSql.test.js.map +1 -0
- package/dist/dist/server-safe/facebookMessenger.d.ts +20 -0
- package/dist/dist/server-safe/facebookMessenger.d.ts.map +1 -0
- package/dist/dist/server-safe/facebookMessenger.js +63 -0
- package/dist/dist/server-safe/facebookMessenger.js.map +1 -0
- package/dist/dist/server-safe/facebookMessenger.test.d.ts +2 -0
- package/dist/dist/server-safe/facebookMessenger.test.d.ts.map +1 -0
- package/dist/dist/server-safe/facebookMessenger.test.js +51 -0
- package/dist/dist/server-safe/facebookMessenger.test.js.map +1 -0
- package/dist/dist/server-safe/helpers.d.ts +78 -0
- package/dist/dist/server-safe/helpers.d.ts.map +1 -0
- package/dist/dist/server-safe/helpers.js +569 -0
- package/dist/dist/server-safe/helpers.js.map +1 -0
- package/dist/dist/server-safe/index.d.ts +30 -0
- package/dist/dist/server-safe/index.d.ts.map +1 -0
- package/dist/dist/server-safe/index.js +46 -0
- package/dist/dist/server-safe/index.js.map +1 -0
- package/dist/dist/server-safe/permissions.d.ts +29 -0
- package/dist/dist/server-safe/permissions.d.ts.map +1 -0
- package/dist/dist/server-safe/permissions.js +180 -0
- package/dist/dist/server-safe/permissions.js.map +1 -0
- package/dist/dist/server-safe/proposals.d.ts +6 -0
- package/dist/dist/server-safe/proposals.d.ts.map +1 -0
- package/dist/dist/server-safe/proposals.js +58 -0
- package/dist/dist/server-safe/proposals.js.map +1 -0
- package/dist/dist/server-safe/rentalAvailability.d.ts +56 -0
- package/dist/dist/server-safe/rentalAvailability.d.ts.map +1 -0
- package/dist/dist/server-safe/rentalAvailability.js +124 -0
- package/dist/dist/server-safe/rentalAvailability.js.map +1 -0
- package/dist/dist/server-safe/rentalAvailability.test.d.ts +2 -0
- package/dist/dist/server-safe/rentalAvailability.test.d.ts.map +1 -0
- package/dist/dist/server-safe/rentalAvailability.test.js +290 -0
- package/dist/dist/server-safe/rentalAvailability.test.js.map +1 -0
- package/dist/dist/server-safe/rentals.d.ts +95 -0
- package/dist/dist/server-safe/rentals.d.ts.map +1 -0
- package/dist/dist/server-safe/rentals.js +432 -0
- package/dist/dist/server-safe/rentals.js.map +1 -0
- package/dist/dist/server-safe/rentals.test.d.ts +2 -0
- package/dist/dist/server-safe/rentals.test.d.ts.map +1 -0
- package/dist/dist/server-safe/rentals.test.js +1034 -0
- package/dist/dist/server-safe/rentals.test.js.map +1 -0
- package/dist/dist/server-safe/routeOptimization.d.ts +155 -0
- package/dist/dist/server-safe/routeOptimization.d.ts.map +1 -0
- package/dist/dist/server-safe/routeOptimization.js +120 -0
- package/dist/dist/server-safe/routeOptimization.js.map +1 -0
- package/dist/dist/server-safe/routeOptimization.test.d.ts +2 -0
- package/dist/dist/server-safe/routeOptimization.test.d.ts.map +1 -0
- package/dist/dist/server-safe/routeOptimization.test.js +234 -0
- package/dist/dist/server-safe/routeOptimization.test.js.map +1 -0
- package/dist/dist/server-safe/routePlanFingerprint.d.ts +13 -0
- package/dist/dist/server-safe/routePlanFingerprint.d.ts.map +1 -0
- package/dist/dist/server-safe/routePlanFingerprint.js +49 -0
- package/dist/dist/server-safe/routePlanFingerprint.js.map +1 -0
- package/dist/dist/server-safe/routePlanFingerprint.test.d.ts +2 -0
- package/dist/dist/server-safe/routePlanFingerprint.test.d.ts.map +1 -0
- package/dist/dist/server-safe/routePlanFingerprint.test.js +68 -0
- package/dist/dist/server-safe/routePlanFingerprint.test.js.map +1 -0
- package/dist/dist/server-safe/scheduleListOrder.d.ts +18 -0
- package/dist/dist/server-safe/scheduleListOrder.d.ts.map +1 -0
- package/dist/dist/server-safe/scheduleListOrder.js +92 -0
- package/dist/dist/server-safe/scheduleListOrder.js.map +1 -0
- package/dist/dist/server-safe/scheduleListOrder.test.d.ts +2 -0
- package/dist/dist/server-safe/scheduleListOrder.test.d.ts.map +1 -0
- package/dist/dist/server-safe/scheduleListOrder.test.js +260 -0
- package/dist/dist/server-safe/scheduleListOrder.test.js.map +1 -0
- package/dist/dist/server-safe/serviceMinutes.d.ts +13 -0
- package/dist/dist/server-safe/serviceMinutes.d.ts.map +1 -0
- package/dist/dist/server-safe/serviceMinutes.js +38 -0
- package/dist/dist/server-safe/serviceMinutes.js.map +1 -0
- package/dist/dist/server-safe/serviceMinutes.test.d.ts +2 -0
- package/dist/dist/server-safe/serviceMinutes.test.d.ts.map +1 -0
- package/dist/dist/server-safe/serviceMinutes.test.js +86 -0
- package/dist/dist/server-safe/serviceMinutes.test.js.map +1 -0
- package/dist/dist/server-safe/settingsSearch.d.ts +24 -0
- package/dist/dist/server-safe/settingsSearch.d.ts.map +1 -0
- package/dist/dist/server-safe/settingsSearch.js +8 -0
- package/dist/dist/server-safe/settingsSearch.js.map +1 -0
- package/dist/dist/server-safe/supportChat.d.ts +59 -0
- package/dist/dist/server-safe/supportChat.d.ts.map +1 -0
- package/dist/dist/server-safe/supportChat.js +56 -0
- package/dist/dist/server-safe/supportChat.js.map +1 -0
- package/dist/dist/server-safe/templates.d.ts +15 -0
- package/dist/dist/server-safe/templates.d.ts.map +1 -0
- package/dist/dist/server-safe/templates.js +91 -0
- package/dist/dist/server-safe/templates.js.map +1 -0
- package/dist/dist/server-safe/templates.test.d.ts +2 -0
- package/dist/dist/server-safe/templates.test.d.ts.map +1 -0
- package/dist/dist/server-safe/templates.test.js +62 -0
- package/dist/dist/server-safe/templates.test.js.map +1 -0
- package/dist/dist/server-safe/trash.d.ts +50 -0
- package/dist/dist/server-safe/trash.d.ts.map +1 -0
- package/dist/dist/server-safe/trash.js +100 -0
- package/dist/dist/server-safe/trash.js.map +1 -0
- package/dist/dist/utils/LogHandler.d.ts +3 -0
- package/dist/dist/utils/LogHandler.d.ts.map +1 -0
- package/dist/dist/utils/LogHandler.js +16 -0
- package/dist/dist/utils/LogHandler.js.map +1 -0
- package/dist/dist/utils/convertSupabaseImagesToAssets.d.ts +28 -0
- package/dist/dist/utils/convertSupabaseImagesToAssets.d.ts.map +1 -0
- package/dist/dist/utils/convertSupabaseImagesToAssets.js +14 -0
- package/dist/dist/utils/convertSupabaseImagesToAssets.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.d.ts +1238 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +3 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/providers/AddressProvider.d.ts +9 -0
- package/dist/providers/AddressProvider.d.ts.map +1 -0
- package/dist/providers/AddressProvider.js +207 -0
- package/dist/providers/AddressProvider.js.map +1 -0
- package/dist/providers/AutomationsProvider.d.ts +11 -0
- package/dist/providers/AutomationsProvider.d.ts.map +1 -0
- package/dist/providers/AutomationsProvider.js +170 -0
- package/dist/providers/AutomationsProvider.js.map +1 -0
- package/dist/providers/BlockedNumbersProvider.d.ts +7 -0
- package/dist/providers/BlockedNumbersProvider.d.ts.map +1 -0
- package/dist/providers/BlockedNumbersProvider.js +71 -0
- package/dist/providers/BlockedNumbersProvider.js.map +1 -0
- package/dist/providers/CategoryProvider.d.ts +8 -0
- package/dist/providers/CategoryProvider.d.ts.map +1 -0
- package/dist/providers/CategoryProvider.js +95 -0
- package/dist/providers/CategoryProvider.js.map +1 -0
- package/dist/providers/ClientProvider.d.ts +39 -0
- package/dist/providers/ClientProvider.d.ts.map +1 -0
- package/dist/providers/ClientProvider.js +253 -0
- package/dist/providers/ClientProvider.js.map +1 -0
- package/dist/providers/CompanyProvider.d.ts +7 -0
- package/dist/providers/CompanyProvider.d.ts.map +1 -0
- package/dist/providers/CompanyProvider.js +114 -0
- package/dist/providers/CompanyProvider.js.map +1 -0
- package/dist/providers/DashboardProvider.d.ts +78 -0
- package/dist/providers/DashboardProvider.d.ts.map +1 -0
- package/dist/providers/DashboardProvider.js +568 -0
- package/dist/providers/DashboardProvider.js.map +1 -0
- package/dist/providers/DiscountProvider.d.ts +8 -0
- package/dist/providers/DiscountProvider.d.ts.map +1 -0
- package/dist/providers/DiscountProvider.js +92 -0
- package/dist/providers/DiscountProvider.js.map +1 -0
- package/dist/providers/EmployeesProvider.d.ts +6 -0
- package/dist/providers/EmployeesProvider.d.ts.map +1 -0
- package/dist/providers/EmployeesProvider.js +185 -0
- package/dist/providers/EmployeesProvider.js.map +1 -0
- package/dist/providers/EstimatesProvider.d.ts +16 -0
- package/dist/providers/EstimatesProvider.d.ts.map +1 -0
- package/dist/providers/EstimatesProvider.js +251 -0
- package/dist/providers/EstimatesProvider.js.map +1 -0
- package/dist/providers/EventsProvider.d.ts +4 -0
- package/dist/providers/EventsProvider.d.ts.map +1 -0
- package/dist/providers/EventsProvider.js +24 -0
- package/dist/providers/EventsProvider.js.map +1 -0
- package/dist/providers/FilesProvider.d.ts +17 -0
- package/dist/providers/FilesProvider.d.ts.map +1 -0
- package/dist/providers/FilesProvider.js +201 -0
- package/dist/providers/FilesProvider.js.map +1 -0
- package/dist/providers/InvoicesProvider.d.ts +13 -0
- package/dist/providers/InvoicesProvider.d.ts.map +1 -0
- package/dist/providers/InvoicesProvider.js +207 -0
- package/dist/providers/InvoicesProvider.js.map +1 -0
- package/dist/providers/ItemProvider.d.ts +9 -0
- package/dist/providers/ItemProvider.d.ts.map +1 -0
- package/dist/providers/ItemProvider.js +201 -0
- package/dist/providers/ItemProvider.js.map +1 -0
- package/dist/providers/JobSourceProvider.d.ts +8 -0
- package/dist/providers/JobSourceProvider.d.ts.map +1 -0
- package/dist/providers/JobSourceProvider.js +75 -0
- package/dist/providers/JobSourceProvider.js.map +1 -0
- package/dist/providers/JobTypeProvider.d.ts +8 -0
- package/dist/providers/JobTypeProvider.d.ts.map +1 -0
- package/dist/providers/JobTypeProvider.js +75 -0
- package/dist/providers/JobTypeProvider.js.map +1 -0
- package/dist/providers/JobsProvider.d.ts +20 -0
- package/dist/providers/JobsProvider.d.ts.map +1 -0
- package/dist/providers/JobsProvider.js +863 -0
- package/dist/providers/JobsProvider.js.map +1 -0
- package/dist/providers/MessagesProvider.d.ts +14 -0
- package/dist/providers/MessagesProvider.d.ts.map +1 -0
- package/dist/providers/MessagesProvider.js +415 -0
- package/dist/providers/MessagesProvider.js.map +1 -0
- package/dist/providers/PaymentProvider.d.ts +9 -0
- package/dist/providers/PaymentProvider.d.ts.map +1 -0
- package/dist/providers/PaymentProvider.js +48 -0
- package/dist/providers/PaymentProvider.js.map +1 -0
- package/dist/providers/PermissionRolesProvider.d.ts +5 -0
- package/dist/providers/PermissionRolesProvider.d.ts.map +1 -0
- package/dist/providers/PermissionRolesProvider.js +30 -0
- package/dist/providers/PermissionRolesProvider.js.map +1 -0
- package/dist/providers/PhoneNumberProvider.d.ts +12 -0
- package/dist/providers/PhoneNumberProvider.d.ts.map +1 -0
- package/dist/providers/PhoneNumberProvider.js +184 -0
- package/dist/providers/PhoneNumberProvider.js.map +1 -0
- package/dist/providers/ProfileProvider.d.ts +6 -0
- package/dist/providers/ProfileProvider.d.ts.map +1 -0
- package/dist/providers/ProfileProvider.js +71 -0
- package/dist/providers/ProfileProvider.js.map +1 -0
- package/dist/providers/RentalsProvider.d.ts +28 -0
- package/dist/providers/RentalsProvider.d.ts.map +1 -0
- package/dist/providers/RentalsProvider.js +438 -0
- package/dist/providers/RentalsProvider.js.map +1 -0
- package/dist/providers/SettingsBookingProvider.d.ts +9 -0
- package/dist/providers/SettingsBookingProvider.d.ts.map +1 -0
- package/dist/providers/SettingsBookingProvider.js +163 -0
- package/dist/providers/SettingsBookingProvider.js.map +1 -0
- package/dist/providers/SettingsProvider.d.ts +8 -0
- package/dist/providers/SettingsProvider.d.ts.map +1 -0
- package/dist/providers/SettingsProvider.js +86 -0
- package/dist/providers/SettingsProvider.js.map +1 -0
- package/dist/providers/TagsProvider.d.ts +12 -0
- package/dist/providers/TagsProvider.d.ts.map +1 -0
- package/dist/providers/TagsProvider.js +210 -0
- package/dist/providers/TagsProvider.js.map +1 -0
- package/dist/providers/TimeCardProvider.d.ts +8 -0
- package/dist/providers/TimeCardProvider.d.ts.map +1 -0
- package/dist/providers/TimeCardProvider.js +93 -0
- package/dist/providers/TimeCardProvider.js.map +1 -0
- package/dist/providers/TrashProvider.d.ts +13 -0
- package/dist/providers/TrashProvider.d.ts.map +1 -0
- package/dist/providers/TrashProvider.js +98 -0
- package/dist/providers/TrashProvider.js.map +1 -0
- package/dist/providers/dashboardAnalytics.d.ts +9 -0
- package/dist/providers/dashboardAnalytics.d.ts.map +1 -0
- package/dist/providers/dashboardAnalytics.js +41 -0
- package/dist/providers/dashboardAnalytics.js.map +1 -0
- package/dist/providers/dashboardAnalytics.test.d.ts +2 -0
- package/dist/providers/dashboardAnalytics.test.d.ts.map +1 -0
- package/dist/providers/dashboardAnalytics.test.js +111 -0
- package/dist/providers/dashboardAnalytics.test.js.map +1 -0
- package/dist/server-safe/BlockedNumberFunctions.d.ts +11 -0
- package/dist/server-safe/BlockedNumberFunctions.d.ts.map +1 -0
- package/dist/server-safe/BlockedNumberFunctions.js +16 -0
- package/dist/server-safe/BlockedNumberFunctions.js.map +1 -0
- package/dist/server-safe/EmployeeFunctions.d.ts +14 -0
- package/dist/server-safe/EmployeeFunctions.d.ts.map +1 -0
- package/dist/server-safe/EmployeeFunctions.js +70 -0
- package/dist/server-safe/EmployeeFunctions.js.map +1 -0
- package/dist/server-safe/EmployeeLocationFunctions.d.ts +7 -0
- package/dist/server-safe/EmployeeLocationFunctions.d.ts.map +1 -0
- package/dist/server-safe/EmployeeLocationFunctions.js +47 -0
- package/dist/server-safe/EmployeeLocationFunctions.js.map +1 -0
- package/dist/server-safe/EstimateFunctions.d.ts +15 -0
- package/dist/server-safe/EstimateFunctions.d.ts.map +1 -0
- package/dist/server-safe/EstimateFunctions.js +34 -0
- package/dist/server-safe/EstimateFunctions.js.map +1 -0
- package/dist/server-safe/InvoiceFunctions.d.ts +21 -0
- package/dist/server-safe/InvoiceFunctions.d.ts.map +1 -0
- package/dist/server-safe/InvoiceFunctions.js +124 -0
- package/dist/server-safe/InvoiceFunctions.js.map +1 -0
- package/dist/server-safe/JobFunctions.d.ts +15 -0
- package/dist/server-safe/JobFunctions.d.ts.map +1 -0
- package/dist/server-safe/JobFunctions.js +56 -0
- package/dist/server-safe/JobFunctions.js.map +1 -0
- package/dist/server-safe/PaymentFunctions.d.ts +16 -0
- package/dist/server-safe/PaymentFunctions.d.ts.map +1 -0
- package/dist/server-safe/PaymentFunctions.js +80 -0
- package/dist/server-safe/PaymentFunctions.js.map +1 -0
- package/dist/server-safe/PermissionFunctions.d.ts +7 -0
- package/dist/server-safe/PermissionFunctions.d.ts.map +1 -0
- package/dist/server-safe/PermissionFunctions.js +39 -0
- package/dist/server-safe/PermissionFunctions.js.map +1 -0
- package/dist/server-safe/TagFunctions.d.ts +18 -0
- package/dist/server-safe/TagFunctions.d.ts.map +1 -0
- package/dist/server-safe/TagFunctions.js +52 -0
- package/dist/server-safe/TagFunctions.js.map +1 -0
- package/dist/server-safe/autoJobType.d.ts +16 -0
- package/dist/server-safe/autoJobType.d.ts.map +1 -0
- package/dist/server-safe/autoJobType.js +39 -0
- package/dist/server-safe/autoJobType.js.map +1 -0
- package/dist/server-safe/autoJobType.test.d.ts +2 -0
- package/dist/server-safe/autoJobType.test.d.ts.map +1 -0
- package/dist/server-safe/autoJobType.test.js +83 -0
- package/dist/server-safe/autoJobType.test.js.map +1 -0
- package/dist/server-safe/automations.d.ts +197 -0
- package/dist/server-safe/automations.d.ts.map +1 -0
- package/dist/server-safe/automations.js +757 -0
- package/dist/server-safe/automations.js.map +1 -0
- package/dist/server-safe/automations.test.d.ts +2 -0
- package/dist/server-safe/automations.test.d.ts.map +1 -0
- package/dist/server-safe/automations.test.js +441 -0
- package/dist/server-safe/automations.test.js.map +1 -0
- package/dist/server-safe/constants.d.ts +156 -0
- package/dist/server-safe/constants.d.ts.map +1 -0
- package/dist/server-safe/constants.js +186 -0
- package/dist/server-safe/constants.js.map +1 -0
- package/dist/server-safe/contracts.d.ts +3 -0
- package/dist/server-safe/contracts.d.ts.map +1 -0
- package/dist/server-safe/contracts.js +15 -0
- package/dist/server-safe/contracts.js.map +1 -0
- package/dist/server-safe/database.types.d.ts +9510 -0
- package/dist/server-safe/database.types.d.ts.map +1 -0
- package/dist/server-safe/database.types.js +40 -0
- package/dist/server-safe/database.types.js.map +1 -0
- package/dist/server-safe/entitlements.d.ts +99 -0
- package/dist/server-safe/entitlements.d.ts.map +1 -0
- package/dist/server-safe/entitlements.js +487 -0
- package/dist/server-safe/entitlements.js.map +1 -0
- package/dist/server-safe/helpers.d.ts +78 -0
- package/dist/server-safe/helpers.d.ts.map +1 -0
- package/dist/server-safe/helpers.js +569 -0
- package/dist/server-safe/helpers.js.map +1 -0
- package/dist/server-safe/index.d.ts +30 -0
- package/dist/server-safe/index.d.ts.map +1 -0
- package/dist/server-safe/index.js +46 -0
- package/dist/server-safe/index.js.map +1 -0
- package/dist/server-safe/permissions.d.ts +29 -0
- package/dist/server-safe/permissions.d.ts.map +1 -0
- package/dist/server-safe/permissions.js +180 -0
- package/dist/server-safe/permissions.js.map +1 -0
- package/dist/server-safe/proposals.d.ts +6 -0
- package/dist/server-safe/proposals.d.ts.map +1 -0
- package/dist/server-safe/proposals.js +58 -0
- package/dist/server-safe/proposals.js.map +1 -0
- package/dist/server-safe/rentalAvailability.d.ts +56 -0
- package/dist/server-safe/rentalAvailability.d.ts.map +1 -0
- package/dist/server-safe/rentalAvailability.js +124 -0
- package/dist/server-safe/rentalAvailability.js.map +1 -0
- package/dist/server-safe/rentalAvailability.test.d.ts +2 -0
- package/dist/server-safe/rentalAvailability.test.d.ts.map +1 -0
- package/dist/server-safe/rentalAvailability.test.js +290 -0
- package/dist/server-safe/rentalAvailability.test.js.map +1 -0
- package/dist/server-safe/rentals.d.ts +95 -0
- package/dist/server-safe/rentals.d.ts.map +1 -0
- package/dist/server-safe/rentals.js +432 -0
- package/dist/server-safe/rentals.js.map +1 -0
- package/dist/server-safe/rentals.test.d.ts +2 -0
- package/dist/server-safe/rentals.test.d.ts.map +1 -0
- package/dist/server-safe/rentals.test.js +1034 -0
- package/dist/server-safe/rentals.test.js.map +1 -0
- package/dist/server-safe/routeOptimization.d.ts +155 -0
- package/dist/server-safe/routeOptimization.d.ts.map +1 -0
- package/dist/server-safe/routeOptimization.js +120 -0
- package/dist/server-safe/routeOptimization.js.map +1 -0
- package/dist/server-safe/routeOptimization.test.d.ts +2 -0
- package/dist/server-safe/routeOptimization.test.d.ts.map +1 -0
- package/dist/server-safe/routeOptimization.test.js +234 -0
- package/dist/server-safe/routeOptimization.test.js.map +1 -0
- package/dist/server-safe/routePlanFingerprint.d.ts +13 -0
- package/dist/server-safe/routePlanFingerprint.d.ts.map +1 -0
- package/dist/server-safe/routePlanFingerprint.js +49 -0
- package/dist/server-safe/routePlanFingerprint.js.map +1 -0
- package/dist/server-safe/routePlanFingerprint.test.d.ts +2 -0
- package/dist/server-safe/routePlanFingerprint.test.d.ts.map +1 -0
- package/dist/server-safe/routePlanFingerprint.test.js +68 -0
- package/dist/server-safe/routePlanFingerprint.test.js.map +1 -0
- package/dist/server-safe/scheduleListOrder.d.ts +18 -0
- package/dist/server-safe/scheduleListOrder.d.ts.map +1 -0
- package/dist/server-safe/scheduleListOrder.js +92 -0
- package/dist/server-safe/scheduleListOrder.js.map +1 -0
- package/dist/server-safe/scheduleListOrder.test.d.ts +2 -0
- package/dist/server-safe/scheduleListOrder.test.d.ts.map +1 -0
- package/dist/server-safe/scheduleListOrder.test.js +260 -0
- package/dist/server-safe/scheduleListOrder.test.js.map +1 -0
- package/dist/server-safe/serviceMinutes.d.ts +13 -0
- package/dist/server-safe/serviceMinutes.d.ts.map +1 -0
- package/dist/server-safe/serviceMinutes.js +38 -0
- package/dist/server-safe/serviceMinutes.js.map +1 -0
- package/dist/server-safe/serviceMinutes.test.d.ts +2 -0
- package/dist/server-safe/serviceMinutes.test.d.ts.map +1 -0
- package/dist/server-safe/serviceMinutes.test.js +86 -0
- package/dist/server-safe/serviceMinutes.test.js.map +1 -0
- package/dist/server-safe/settingsSearch.d.ts +24 -0
- package/dist/server-safe/settingsSearch.d.ts.map +1 -0
- package/dist/server-safe/settingsSearch.js +8 -0
- package/dist/server-safe/settingsSearch.js.map +1 -0
- package/dist/server-safe/supportChat.d.ts +59 -0
- package/dist/server-safe/supportChat.d.ts.map +1 -0
- package/dist/server-safe/supportChat.js +56 -0
- package/dist/server-safe/supportChat.js.map +1 -0
- package/dist/server-safe/templates.d.ts +15 -0
- package/dist/server-safe/templates.d.ts.map +1 -0
- package/dist/server-safe/templates.js +91 -0
- package/dist/server-safe/templates.js.map +1 -0
- package/dist/server-safe/templates.test.d.ts +2 -0
- package/dist/server-safe/templates.test.d.ts.map +1 -0
- package/dist/server-safe/templates.test.js +62 -0
- package/dist/server-safe/templates.test.js.map +1 -0
- package/dist/server-safe/trash.d.ts +50 -0
- package/dist/server-safe/trash.d.ts.map +1 -0
- package/dist/server-safe/trash.js +100 -0
- package/dist/server-safe/trash.js.map +1 -0
- package/dist/utils/LogHandler.d.ts +3 -0
- package/dist/utils/LogHandler.d.ts.map +1 -0
- package/dist/utils/LogHandler.js +16 -0
- package/dist/utils/LogHandler.js.map +1 -0
- package/dist/utils/convertSupabaseImagesToAssets.d.ts +28 -0
- package/dist/utils/convertSupabaseImagesToAssets.d.ts.map +1 -0
- package/dist/utils/convertSupabaseImagesToAssets.js +14 -0
- package/dist/utils/convertSupabaseImagesToAssets.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,1711 @@
|
|
|
1
|
+
-- Meta Lead Ads ingestion: Page/Form configuration, durable webhook receipt,
|
|
2
|
+
-- consent evidence, atomic lead creation, and agent-first call attempts.
|
|
3
|
+
--
|
|
4
|
+
-- Everything in this migration is additive and fail-closed. Lead sync and
|
|
5
|
+
-- auto-dial are disabled for every existing company until a server route
|
|
6
|
+
-- validates and enables the configuration through
|
|
7
|
+
-- set_facebook_ads_lead_settings(). The companion RLS migration keeps every
|
|
8
|
+
-- credential, PII, evidence, and queue table service-role only.
|
|
9
|
+
|
|
10
|
+
-- ============================================================
|
|
11
|
+
-- Shared normalized-array checks
|
|
12
|
+
-- ============================================================
|
|
13
|
+
|
|
14
|
+
create or replace function "public"."facebook_ads_text_array_is_normalized"(
|
|
15
|
+
"p_values" text[]
|
|
16
|
+
)
|
|
17
|
+
returns boolean
|
|
18
|
+
language sql
|
|
19
|
+
immutable
|
|
20
|
+
parallel safe
|
|
21
|
+
set search_path = ''
|
|
22
|
+
as $$
|
|
23
|
+
select case
|
|
24
|
+
when cardinality("p_values") = 0 then true
|
|
25
|
+
when coalesce(array_ndims("p_values"), 0) <> 1 then false
|
|
26
|
+
when exists (
|
|
27
|
+
select 1
|
|
28
|
+
from unnest("p_values") as "item"("value")
|
|
29
|
+
where "item"."value" is null
|
|
30
|
+
or nullif(btrim("item"."value"), '') is null
|
|
31
|
+
) then false
|
|
32
|
+
else "p_values" = array(
|
|
33
|
+
select distinct "item"."value"
|
|
34
|
+
from unnest("p_values") as "item"("value")
|
|
35
|
+
order by "item"."value"
|
|
36
|
+
)
|
|
37
|
+
end;
|
|
38
|
+
$$;
|
|
39
|
+
|
|
40
|
+
alter function "public"."facebook_ads_text_array_is_normalized"(text[])
|
|
41
|
+
owner to "postgres";
|
|
42
|
+
|
|
43
|
+
create or replace function "public"."facebook_ads_call_days_are_normalized"(
|
|
44
|
+
"p_days" smallint[]
|
|
45
|
+
)
|
|
46
|
+
returns boolean
|
|
47
|
+
language sql
|
|
48
|
+
immutable
|
|
49
|
+
parallel safe
|
|
50
|
+
set search_path = ''
|
|
51
|
+
as $$
|
|
52
|
+
select case
|
|
53
|
+
when cardinality("p_days") = 0 then false
|
|
54
|
+
when coalesce(array_ndims("p_days"), 0) <> 1 then false
|
|
55
|
+
when exists (
|
|
56
|
+
select 1
|
|
57
|
+
from unnest("p_days") as "day_row"("value")
|
|
58
|
+
where "day_row"."value" is null
|
|
59
|
+
or "day_row"."value" not between 1 and 7
|
|
60
|
+
) then false
|
|
61
|
+
else "p_days" = array(
|
|
62
|
+
select distinct "day_row"."value"
|
|
63
|
+
from unnest("p_days") as "day_row"("value")
|
|
64
|
+
order by "day_row"."value"
|
|
65
|
+
)::smallint[]
|
|
66
|
+
end;
|
|
67
|
+
$$;
|
|
68
|
+
|
|
69
|
+
alter function "public"."facebook_ads_call_days_are_normalized"(smallint[])
|
|
70
|
+
owner to "postgres";
|
|
71
|
+
|
|
72
|
+
-- ============================================================
|
|
73
|
+
-- Existing-table additions
|
|
74
|
+
-- ============================================================
|
|
75
|
+
|
|
76
|
+
alter table "public"."client"
|
|
77
|
+
add column if not exists "is_do_not_marketing_email" boolean
|
|
78
|
+
default false not null;
|
|
79
|
+
|
|
80
|
+
alter table "public"."facebook_ads_connection"
|
|
81
|
+
add column if not exists "granted_scopes" text[]
|
|
82
|
+
default '{}'::text[] not null;
|
|
83
|
+
|
|
84
|
+
alter table "public"."facebook_ads_connection"
|
|
85
|
+
add constraint "facebook_ads_connection_granted_scopes_check"
|
|
86
|
+
check (
|
|
87
|
+
"public"."facebook_ads_text_array_is_normalized"("granted_scopes")
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
alter table "public"."settings_facebook_ads"
|
|
91
|
+
add column if not exists "lead_sync_enabled" boolean default false not null,
|
|
92
|
+
add column if not exists "lead_tag_id" uuid,
|
|
93
|
+
add column if not exists "lead_sms_rule_id" uuid,
|
|
94
|
+
add column if not exists "lead_email_rule_id" uuid,
|
|
95
|
+
-- The customer attests that the company service timezone governs calling
|
|
96
|
+
-- every lead delivered by its selected Meta Forms. This is deliberately
|
|
97
|
+
-- separate from enabling the feature so the legal assumption is explicit.
|
|
98
|
+
add column if not exists "lead_auto_dial_timezone_attested" boolean
|
|
99
|
+
default false not null,
|
|
100
|
+
add column if not exists "lead_auto_dial_enabled" boolean default false not null,
|
|
101
|
+
add column if not exists "lead_auto_dial_phone_number_id" uuid,
|
|
102
|
+
add column if not exists "lead_auto_dial_user_id" uuid,
|
|
103
|
+
add column if not exists "lead_auto_dial_delay_seconds" integer default 0 not null,
|
|
104
|
+
add column if not exists "lead_auto_dial_window_start" time without time zone
|
|
105
|
+
default '08:00:00'::time without time zone not null,
|
|
106
|
+
add column if not exists "lead_auto_dial_window_end" time without time zone
|
|
107
|
+
default '20:00:00'::time without time zone not null,
|
|
108
|
+
-- ISO weekdays: Monday=1 through Sunday=7.
|
|
109
|
+
add column if not exists "lead_auto_dial_window_days" smallint[]
|
|
110
|
+
default array[1, 2, 3, 4, 5, 6, 7]::smallint[] not null;
|
|
111
|
+
|
|
112
|
+
alter table "public"."settings_facebook_ads"
|
|
113
|
+
add constraint "settings_facebook_ads_lead_tag_id_fkey"
|
|
114
|
+
foreign key ("lead_tag_id") references "public"."client_tag"("id")
|
|
115
|
+
on delete set null,
|
|
116
|
+
add constraint "settings_facebook_ads_lead_sms_rule_id_fkey"
|
|
117
|
+
foreign key ("lead_sms_rule_id") references "public"."automation_rule"("id")
|
|
118
|
+
on delete set null,
|
|
119
|
+
add constraint "settings_facebook_ads_lead_email_rule_id_fkey"
|
|
120
|
+
foreign key ("lead_email_rule_id") references "public"."automation_rule"("id")
|
|
121
|
+
on delete set null,
|
|
122
|
+
add constraint "settings_facebook_ads_lead_auto_dial_phone_number_id_fkey"
|
|
123
|
+
foreign key ("lead_auto_dial_phone_number_id")
|
|
124
|
+
references "public"."phone_number"("id") on delete set null,
|
|
125
|
+
add constraint "settings_facebook_ads_lead_auto_dial_user_id_fkey"
|
|
126
|
+
foreign key ("lead_auto_dial_user_id") references "public"."user"("id")
|
|
127
|
+
on delete set null,
|
|
128
|
+
add constraint "settings_facebook_ads_lead_sync_source_check"
|
|
129
|
+
check (not "lead_sync_enabled" or "source_id" is not null),
|
|
130
|
+
add constraint "settings_facebook_ads_lead_rule_distinct_check"
|
|
131
|
+
check (
|
|
132
|
+
"lead_sms_rule_id" is null
|
|
133
|
+
or "lead_email_rule_id" is null
|
|
134
|
+
or "lead_sms_rule_id" <> "lead_email_rule_id"
|
|
135
|
+
),
|
|
136
|
+
add constraint "settings_facebook_ads_lead_auto_dial_config_check"
|
|
137
|
+
check (
|
|
138
|
+
not "lead_auto_dial_enabled"
|
|
139
|
+
or (
|
|
140
|
+
"lead_auto_dial_timezone_attested"
|
|
141
|
+
and
|
|
142
|
+
"lead_auto_dial_phone_number_id" is not null
|
|
143
|
+
and "lead_auto_dial_user_id" is not null
|
|
144
|
+
)
|
|
145
|
+
),
|
|
146
|
+
add constraint "settings_facebook_ads_lead_auto_dial_delay_check"
|
|
147
|
+
check ("lead_auto_dial_delay_seconds" between 0 and 86400),
|
|
148
|
+
add constraint "settings_facebook_ads_lead_auto_dial_window_check"
|
|
149
|
+
check ("lead_auto_dial_window_start" < "lead_auto_dial_window_end"),
|
|
150
|
+
add constraint "settings_facebook_ads_lead_auto_dial_days_check"
|
|
151
|
+
check (
|
|
152
|
+
"public"."facebook_ads_call_days_are_normalized"(
|
|
153
|
+
"lead_auto_dial_window_days"
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
-- Facebook lead response rules are selected explicitly in settings rather
|
|
158
|
+
-- than discovered from every company-wide lead_created rule. Snapshot that
|
|
159
|
+
-- selection on the automation event created by the existing client trigger:
|
|
160
|
+
-- downstream fanout can then require both provenance and rule-id inclusion.
|
|
161
|
+
-- The non-Facebook branch deliberately preserves the old INSERT shape, so its
|
|
162
|
+
-- payload remains the automation_event default '{}'::jsonb.
|
|
163
|
+
create or replace function "public"."capture_automation_lead_created"()
|
|
164
|
+
returns trigger
|
|
165
|
+
language plpgsql
|
|
166
|
+
security definer
|
|
167
|
+
set search_path = ''
|
|
168
|
+
as $$
|
|
169
|
+
declare
|
|
170
|
+
"v_rule_ids" jsonb;
|
|
171
|
+
begin
|
|
172
|
+
if new."created_via" = 'facebook_lead_ads' then
|
|
173
|
+
select to_jsonb(array_remove(array[
|
|
174
|
+
"settings"."lead_sms_rule_id",
|
|
175
|
+
"settings"."lead_email_rule_id"
|
|
176
|
+
], null::uuid))
|
|
177
|
+
into "v_rule_ids"
|
|
178
|
+
from "public"."settings_facebook_ads" as "settings"
|
|
179
|
+
where "settings"."company_id" = new."company_id";
|
|
180
|
+
|
|
181
|
+
insert into "public"."automation_event" (
|
|
182
|
+
"company_id", "event_type", "client_id", "payload"
|
|
183
|
+
) values (
|
|
184
|
+
new."company_id",
|
|
185
|
+
'lead_created',
|
|
186
|
+
new."id",
|
|
187
|
+
jsonb_build_object(
|
|
188
|
+
'created_via', 'facebook_lead_ads',
|
|
189
|
+
'rule_ids', coalesce("v_rule_ids", '[]'::jsonb)
|
|
190
|
+
)
|
|
191
|
+
);
|
|
192
|
+
else
|
|
193
|
+
insert into "public"."automation_event" (
|
|
194
|
+
"company_id", "event_type", "client_id"
|
|
195
|
+
) values (new."company_id", 'lead_created', new."id");
|
|
196
|
+
end if;
|
|
197
|
+
|
|
198
|
+
return null;
|
|
199
|
+
end;
|
|
200
|
+
$$;
|
|
201
|
+
|
|
202
|
+
alter function "public"."capture_automation_lead_created"()
|
|
203
|
+
owner to "postgres";
|
|
204
|
+
|
|
205
|
+
-- ============================================================
|
|
206
|
+
-- Page and Form configuration
|
|
207
|
+
-- ============================================================
|
|
208
|
+
|
|
209
|
+
create table if not exists "public"."facebook_ads_lead_page" (
|
|
210
|
+
"id" uuid default gen_random_uuid() not null,
|
|
211
|
+
"created_at" timestamp with time zone default now() not null,
|
|
212
|
+
"updated_at" timestamp with time zone default now() not null,
|
|
213
|
+
"company_id" uuid not null,
|
|
214
|
+
"connection_id" uuid not null,
|
|
215
|
+
"meta_page_id" text not null,
|
|
216
|
+
"page_name" text not null,
|
|
217
|
+
-- Ciphertext produced by autopilot-web's Facebook token encryption.
|
|
218
|
+
"encrypted_page_access_token" text not null,
|
|
219
|
+
"token_expires_at" timestamp with time zone,
|
|
220
|
+
"is_enabled" boolean default false not null,
|
|
221
|
+
"subscription_status" text default 'pending' not null,
|
|
222
|
+
"subscribed_at" timestamp with time zone,
|
|
223
|
+
"last_subscription_attempt_at" timestamp with time zone,
|
|
224
|
+
"last_subscription_error" text,
|
|
225
|
+
-- selected is fail-closed: no Form is accepted until explicitly saved.
|
|
226
|
+
"form_selection_mode" text default 'selected' not null,
|
|
227
|
+
constraint "facebook_ads_lead_page_pkey" primary key ("id"),
|
|
228
|
+
constraint "facebook_ads_lead_page_company_id_id_key"
|
|
229
|
+
unique ("company_id", "id"),
|
|
230
|
+
constraint "facebook_ads_lead_page_meta_page_id_key" unique ("meta_page_id"),
|
|
231
|
+
constraint "facebook_ads_lead_page_company_id_fkey"
|
|
232
|
+
foreign key ("company_id") references "public"."company"("id")
|
|
233
|
+
on delete cascade,
|
|
234
|
+
constraint "facebook_ads_lead_page_company_connection_fkey"
|
|
235
|
+
foreign key ("company_id", "connection_id")
|
|
236
|
+
references "public"."facebook_ads_connection"("company_id", "id")
|
|
237
|
+
on delete cascade,
|
|
238
|
+
constraint "facebook_ads_lead_page_meta_page_id_check"
|
|
239
|
+
check ("meta_page_id" ~ '^[0-9]+$'),
|
|
240
|
+
constraint "facebook_ads_lead_page_page_name_check"
|
|
241
|
+
check (nullif(btrim("page_name"), '') is not null),
|
|
242
|
+
constraint "facebook_ads_lead_page_token_check"
|
|
243
|
+
check (nullif(btrim("encrypted_page_access_token"), '') is not null),
|
|
244
|
+
constraint "facebook_ads_lead_page_subscription_status_check"
|
|
245
|
+
check (
|
|
246
|
+
"subscription_status" in (
|
|
247
|
+
'pending', 'subscribed', 'error', 'disconnected'
|
|
248
|
+
)
|
|
249
|
+
),
|
|
250
|
+
constraint "facebook_ads_lead_page_form_selection_mode_check"
|
|
251
|
+
check ("form_selection_mode" in ('all', 'selected')),
|
|
252
|
+
constraint "facebook_ads_lead_page_enabled_subscription_check"
|
|
253
|
+
check (not "is_enabled" or "subscription_status" = 'subscribed')
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
alter table "public"."facebook_ads_lead_page" owner to "postgres";
|
|
257
|
+
|
|
258
|
+
create index if not exists "facebook_ads_lead_page_company_enabled_idx"
|
|
259
|
+
on "public"."facebook_ads_lead_page" ("company_id", "is_enabled");
|
|
260
|
+
|
|
261
|
+
create table if not exists "public"."facebook_ads_lead_form" (
|
|
262
|
+
"id" uuid default gen_random_uuid() not null,
|
|
263
|
+
"created_at" timestamp with time zone default now() not null,
|
|
264
|
+
"updated_at" timestamp with time zone default now() not null,
|
|
265
|
+
"company_id" uuid not null,
|
|
266
|
+
"page_id" uuid not null,
|
|
267
|
+
"meta_form_id" text not null,
|
|
268
|
+
"form_name" text not null,
|
|
269
|
+
"is_enabled" boolean default false not null,
|
|
270
|
+
-- Successful reconciliation advances this cursor. Workers overlap their
|
|
271
|
+
-- next Graph scan slightly so delayed Meta writes are still observed.
|
|
272
|
+
"last_reconciled_at" timestamp with time zone,
|
|
273
|
+
-- Maps Meta field names to first_name/last_name/email/phone. The exact
|
|
274
|
+
-- JSON shape is validated by the server-safe module added with this PR.
|
|
275
|
+
"field_mapping" jsonb default '{}'::jsonb not null,
|
|
276
|
+
-- A company must explicitly attest each channel and save the disclosure
|
|
277
|
+
-- shown on the Meta Form. A missing attestation/disclosure can never be
|
|
278
|
+
-- normalized to granted consent during finalization.
|
|
279
|
+
"sms_consent_attested" boolean default false not null,
|
|
280
|
+
"sms_consent_disclosure" text,
|
|
281
|
+
"email_consent_attested" boolean default false not null,
|
|
282
|
+
"email_consent_disclosure" text,
|
|
283
|
+
"voice_consent_attested" boolean default false not null,
|
|
284
|
+
"voice_consent_disclosure" text,
|
|
285
|
+
constraint "facebook_ads_lead_form_pkey" primary key ("id"),
|
|
286
|
+
constraint "facebook_ads_lead_form_company_id_id_key"
|
|
287
|
+
unique ("company_id", "id"),
|
|
288
|
+
constraint "facebook_ads_lead_form_page_form_key"
|
|
289
|
+
unique ("page_id", "meta_form_id"),
|
|
290
|
+
constraint "facebook_ads_lead_form_company_id_fkey"
|
|
291
|
+
foreign key ("company_id") references "public"."company"("id")
|
|
292
|
+
on delete cascade,
|
|
293
|
+
constraint "facebook_ads_lead_form_company_page_fkey"
|
|
294
|
+
foreign key ("company_id", "page_id")
|
|
295
|
+
references "public"."facebook_ads_lead_page"("company_id", "id")
|
|
296
|
+
on delete cascade,
|
|
297
|
+
constraint "facebook_ads_lead_form_meta_form_id_check"
|
|
298
|
+
check ("meta_form_id" ~ '^[0-9]+$'),
|
|
299
|
+
constraint "facebook_ads_lead_form_name_check"
|
|
300
|
+
check (nullif(btrim("form_name"), '') is not null),
|
|
301
|
+
constraint "facebook_ads_lead_form_field_mapping_check"
|
|
302
|
+
check (jsonb_typeof("field_mapping") = 'object'),
|
|
303
|
+
constraint "facebook_ads_lead_form_sms_consent_check"
|
|
304
|
+
check (
|
|
305
|
+
not "sms_consent_attested"
|
|
306
|
+
or nullif(btrim("sms_consent_disclosure"), '') is not null
|
|
307
|
+
),
|
|
308
|
+
constraint "facebook_ads_lead_form_email_consent_check"
|
|
309
|
+
check (
|
|
310
|
+
not "email_consent_attested"
|
|
311
|
+
or nullif(btrim("email_consent_disclosure"), '') is not null
|
|
312
|
+
),
|
|
313
|
+
constraint "facebook_ads_lead_form_voice_consent_check"
|
|
314
|
+
check (
|
|
315
|
+
not "voice_consent_attested"
|
|
316
|
+
or nullif(btrim("voice_consent_disclosure"), '') is not null
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
alter table "public"."facebook_ads_lead_form" owner to "postgres";
|
|
321
|
+
|
|
322
|
+
create index if not exists "facebook_ads_lead_form_company_page_enabled_idx"
|
|
323
|
+
on "public"."facebook_ads_lead_form" (
|
|
324
|
+
"company_id", "page_id", "is_enabled"
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
create or replace trigger "set_facebook_ads_lead_page_updated_at_trigger"
|
|
328
|
+
before update on "public"."facebook_ads_lead_page"
|
|
329
|
+
for each row execute function "public"."set_facebook_ads_updated_at"();
|
|
330
|
+
|
|
331
|
+
create or replace trigger "set_facebook_ads_lead_form_updated_at_trigger"
|
|
332
|
+
before update on "public"."facebook_ads_lead_form"
|
|
333
|
+
for each row execute function "public"."set_facebook_ads_updated_at"();
|
|
334
|
+
|
|
335
|
+
-- ============================================================
|
|
336
|
+
-- Durable receipt and consent evidence
|
|
337
|
+
-- ============================================================
|
|
338
|
+
|
|
339
|
+
create table if not exists "public"."facebook_ads_lead_event" (
|
|
340
|
+
"id" uuid default gen_random_uuid() not null,
|
|
341
|
+
"created_at" timestamp with time zone default now() not null,
|
|
342
|
+
"updated_at" timestamp with time zone default now() not null,
|
|
343
|
+
"company_id" uuid not null,
|
|
344
|
+
-- Nullable specifically so disconnecting a Page can erase its token and
|
|
345
|
+
-- configuration without erasing historical submission/consent evidence.
|
|
346
|
+
"page_id" uuid,
|
|
347
|
+
"meta_page_id" text not null,
|
|
348
|
+
"meta_leadgen_id" text not null,
|
|
349
|
+
"meta_form_id" text not null,
|
|
350
|
+
"meta_created_at" timestamp with time zone not null,
|
|
351
|
+
"meta_ad_id" text,
|
|
352
|
+
"meta_adset_id" text,
|
|
353
|
+
"meta_campaign_id" text,
|
|
354
|
+
"source_id" uuid,
|
|
355
|
+
"client_id" uuid,
|
|
356
|
+
"webhook_payload" jsonb not null,
|
|
357
|
+
"lead_payload" jsonb,
|
|
358
|
+
-- Immutable snapshot of mapping + channel attestations/disclosures at
|
|
359
|
+
-- receipt time. Form/Page deletion cannot destroy compliance evidence.
|
|
360
|
+
"form_configuration_snapshot" jsonb default '{}'::jsonb not null,
|
|
361
|
+
"status" text default 'pending' not null,
|
|
362
|
+
"skip_reason" text,
|
|
363
|
+
"received_count" integer default 1 not null,
|
|
364
|
+
"replay_count" integer default 0 not null,
|
|
365
|
+
"last_received_at" timestamp with time zone default now() not null,
|
|
366
|
+
"attempt_count" integer default 0 not null,
|
|
367
|
+
"next_attempt_at" timestamp with time zone default now() not null,
|
|
368
|
+
"last_attempt_at" timestamp with time zone,
|
|
369
|
+
"fetched_at" timestamp with time zone,
|
|
370
|
+
"processed_at" timestamp with time zone,
|
|
371
|
+
"last_error" text,
|
|
372
|
+
constraint "facebook_ads_lead_event_pkey" primary key ("id"),
|
|
373
|
+
constraint "facebook_ads_lead_event_company_id_id_key"
|
|
374
|
+
unique ("company_id", "id"),
|
|
375
|
+
constraint "facebook_ads_lead_event_meta_submission_key"
|
|
376
|
+
unique ("meta_page_id", "meta_leadgen_id"),
|
|
377
|
+
constraint "facebook_ads_lead_event_company_id_fkey"
|
|
378
|
+
foreign key ("company_id") references "public"."company"("id")
|
|
379
|
+
on delete cascade,
|
|
380
|
+
constraint "facebook_ads_lead_event_company_page_fkey"
|
|
381
|
+
foreign key ("company_id", "page_id")
|
|
382
|
+
references "public"."facebook_ads_lead_page"("company_id", "id")
|
|
383
|
+
on delete set null ("page_id"),
|
|
384
|
+
constraint "facebook_ads_lead_event_source_id_fkey"
|
|
385
|
+
foreign key ("source_id") references "public"."job_source"("id")
|
|
386
|
+
on delete set null,
|
|
387
|
+
constraint "facebook_ads_lead_event_client_id_fkey"
|
|
388
|
+
foreign key ("client_id") references "public"."client"("id")
|
|
389
|
+
on delete set null,
|
|
390
|
+
constraint "facebook_ads_lead_event_meta_page_id_check"
|
|
391
|
+
check ("meta_page_id" ~ '^[0-9]+$'),
|
|
392
|
+
constraint "facebook_ads_lead_event_meta_leadgen_id_check"
|
|
393
|
+
check ("meta_leadgen_id" ~ '^[0-9]+$'),
|
|
394
|
+
constraint "facebook_ads_lead_event_meta_form_id_check"
|
|
395
|
+
check ("meta_form_id" ~ '^[0-9]+$'),
|
|
396
|
+
constraint "facebook_ads_lead_event_meta_ad_id_check"
|
|
397
|
+
check ("meta_ad_id" is null or "meta_ad_id" ~ '^[0-9]+$'),
|
|
398
|
+
constraint "facebook_ads_lead_event_meta_adset_id_check"
|
|
399
|
+
check ("meta_adset_id" is null or "meta_adset_id" ~ '^[0-9]+$'),
|
|
400
|
+
constraint "facebook_ads_lead_event_meta_campaign_id_check"
|
|
401
|
+
check ("meta_campaign_id" is null or "meta_campaign_id" ~ '^[0-9]+$'),
|
|
402
|
+
constraint "facebook_ads_lead_event_webhook_payload_check"
|
|
403
|
+
check (jsonb_typeof("webhook_payload") = 'object'),
|
|
404
|
+
constraint "facebook_ads_lead_event_lead_payload_check"
|
|
405
|
+
check ("lead_payload" is null or jsonb_typeof("lead_payload") = 'object'),
|
|
406
|
+
constraint "facebook_ads_lead_event_form_snapshot_check"
|
|
407
|
+
check (jsonb_typeof("form_configuration_snapshot") = 'object'),
|
|
408
|
+
constraint "facebook_ads_lead_event_status_check"
|
|
409
|
+
check ("status" in ('pending', 'processing', 'processed', 'skipped', 'dead')),
|
|
410
|
+
constraint "facebook_ads_lead_event_received_count_check"
|
|
411
|
+
check ("received_count" > 0),
|
|
412
|
+
constraint "facebook_ads_lead_event_replay_count_check"
|
|
413
|
+
check ("replay_count" >= 0),
|
|
414
|
+
constraint "facebook_ads_lead_event_attempt_count_check"
|
|
415
|
+
check ("attempt_count" >= 0),
|
|
416
|
+
constraint "facebook_ads_lead_event_processed_at_check"
|
|
417
|
+
check ("status" <> 'processed' or "processed_at" is not null)
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
alter table "public"."facebook_ads_lead_event" owner to "postgres";
|
|
421
|
+
|
|
422
|
+
create index if not exists "facebook_ads_lead_event_due_idx"
|
|
423
|
+
on "public"."facebook_ads_lead_event" ("next_attempt_at", "created_at")
|
|
424
|
+
where "status" = 'pending';
|
|
425
|
+
|
|
426
|
+
create index if not exists "facebook_ads_lead_event_company_history_idx"
|
|
427
|
+
on "public"."facebook_ads_lead_event" ("company_id", "created_at" desc);
|
|
428
|
+
|
|
429
|
+
create index if not exists "facebook_ads_lead_event_client_idx"
|
|
430
|
+
on "public"."facebook_ads_lead_event" ("client_id")
|
|
431
|
+
where "client_id" is not null;
|
|
432
|
+
|
|
433
|
+
create table if not exists "public"."facebook_ads_lead_consent" (
|
|
434
|
+
"id" uuid default gen_random_uuid() not null,
|
|
435
|
+
"created_at" timestamp with time zone default now() not null,
|
|
436
|
+
"company_id" uuid not null,
|
|
437
|
+
"lead_event_id" uuid not null,
|
|
438
|
+
"channel" text not null,
|
|
439
|
+
"status" text default 'unknown' not null,
|
|
440
|
+
"captured_at" timestamp with time zone,
|
|
441
|
+
"disclosure_text" text,
|
|
442
|
+
"response_value" text,
|
|
443
|
+
"evidence" jsonb default '{}'::jsonb not null,
|
|
444
|
+
constraint "facebook_ads_lead_consent_pkey" primary key ("id"),
|
|
445
|
+
constraint "facebook_ads_lead_consent_company_id_id_key"
|
|
446
|
+
unique ("company_id", "id"),
|
|
447
|
+
constraint "facebook_ads_lead_consent_event_channel_key"
|
|
448
|
+
unique ("lead_event_id", "channel"),
|
|
449
|
+
constraint "facebook_ads_lead_consent_company_id_fkey"
|
|
450
|
+
foreign key ("company_id") references "public"."company"("id")
|
|
451
|
+
on delete cascade,
|
|
452
|
+
constraint "facebook_ads_lead_consent_company_event_fkey"
|
|
453
|
+
foreign key ("company_id", "lead_event_id")
|
|
454
|
+
references "public"."facebook_ads_lead_event"("company_id", "id")
|
|
455
|
+
on delete cascade,
|
|
456
|
+
constraint "facebook_ads_lead_consent_channel_check"
|
|
457
|
+
check ("channel" in ('email', 'sms', 'voice')),
|
|
458
|
+
constraint "facebook_ads_lead_consent_status_check"
|
|
459
|
+
check ("status" in ('granted', 'denied', 'unknown')),
|
|
460
|
+
constraint "facebook_ads_lead_consent_evidence_check"
|
|
461
|
+
check (jsonb_typeof("evidence") = 'object'),
|
|
462
|
+
constraint "facebook_ads_lead_consent_granted_evidence_check"
|
|
463
|
+
check (
|
|
464
|
+
"status" <> 'granted'
|
|
465
|
+
or (
|
|
466
|
+
"captured_at" is not null
|
|
467
|
+
and nullif(btrim("disclosure_text"), '') is not null
|
|
468
|
+
)
|
|
469
|
+
)
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
alter table "public"."facebook_ads_lead_consent" owner to "postgres";
|
|
473
|
+
|
|
474
|
+
create index if not exists "facebook_ads_lead_consent_company_event_idx"
|
|
475
|
+
on "public"."facebook_ads_lead_consent" ("company_id", "lead_event_id");
|
|
476
|
+
|
|
477
|
+
-- ============================================================
|
|
478
|
+
-- Agent-first auto-dial queue
|
|
479
|
+
-- ============================================================
|
|
480
|
+
|
|
481
|
+
create table if not exists "public"."facebook_ads_lead_call_attempt" (
|
|
482
|
+
"id" uuid default gen_random_uuid() not null,
|
|
483
|
+
"created_at" timestamp with time zone default now() not null,
|
|
484
|
+
"updated_at" timestamp with time zone default now() not null,
|
|
485
|
+
"company_id" uuid not null,
|
|
486
|
+
"lead_event_id" uuid not null,
|
|
487
|
+
"client_id" uuid,
|
|
488
|
+
"consent_id" uuid not null,
|
|
489
|
+
"from_phone_number_id" uuid,
|
|
490
|
+
"target_user_id" uuid,
|
|
491
|
+
"answered_by_user_id" uuid,
|
|
492
|
+
-- Immutable snapshots keep the audit useful after a number/user changes.
|
|
493
|
+
"from_phone_number" text not null,
|
|
494
|
+
"destination_phone_number" text not null,
|
|
495
|
+
"routing_snapshot" jsonb default '{}'::jsonb not null,
|
|
496
|
+
"status" text default 'pending' not null,
|
|
497
|
+
-- quiet-hours/business-window holds return a claimed row to pending with
|
|
498
|
+
-- a future next_attempt_at; workers never need a direct table update.
|
|
499
|
+
"held_reason" text,
|
|
500
|
+
"attempt_count" integer default 0 not null,
|
|
501
|
+
"next_attempt_at" timestamp with time zone default now() not null,
|
|
502
|
+
"last_attempt_at" timestamp with time zone,
|
|
503
|
+
"twilio_agent_call_sid" text,
|
|
504
|
+
"twilio_lead_call_sid" text,
|
|
505
|
+
"twilio_conference_sid" text,
|
|
506
|
+
"started_at" timestamp with time zone,
|
|
507
|
+
"connected_at" timestamp with time zone,
|
|
508
|
+
"completed_at" timestamp with time zone,
|
|
509
|
+
"skip_reason" text,
|
|
510
|
+
"last_error" text,
|
|
511
|
+
constraint "facebook_ads_lead_call_attempt_pkey" primary key ("id"),
|
|
512
|
+
constraint "facebook_ads_lead_call_attempt_company_id_id_key"
|
|
513
|
+
unique ("company_id", "id"),
|
|
514
|
+
constraint "facebook_ads_lead_call_attempt_lead_event_id_key"
|
|
515
|
+
unique ("lead_event_id"),
|
|
516
|
+
constraint "facebook_ads_lead_call_attempt_company_id_fkey"
|
|
517
|
+
foreign key ("company_id") references "public"."company"("id")
|
|
518
|
+
on delete cascade,
|
|
519
|
+
constraint "facebook_ads_lead_call_attempt_company_event_fkey"
|
|
520
|
+
foreign key ("company_id", "lead_event_id")
|
|
521
|
+
references "public"."facebook_ads_lead_event"("company_id", "id")
|
|
522
|
+
on delete cascade,
|
|
523
|
+
constraint "facebook_ads_lead_call_attempt_client_id_fkey"
|
|
524
|
+
foreign key ("client_id") references "public"."client"("id")
|
|
525
|
+
on delete set null,
|
|
526
|
+
constraint "facebook_ads_lead_call_attempt_company_consent_fkey"
|
|
527
|
+
foreign key ("company_id", "consent_id")
|
|
528
|
+
references "public"."facebook_ads_lead_consent"("company_id", "id")
|
|
529
|
+
on delete cascade,
|
|
530
|
+
constraint "facebook_ads_lead_call_attempt_from_phone_number_id_fkey"
|
|
531
|
+
foreign key ("from_phone_number_id")
|
|
532
|
+
references "public"."phone_number"("id") on delete set null,
|
|
533
|
+
constraint "facebook_ads_lead_call_attempt_target_user_id_fkey"
|
|
534
|
+
foreign key ("target_user_id") references "public"."user"("id")
|
|
535
|
+
on delete set null,
|
|
536
|
+
constraint "facebook_ads_lead_call_attempt_answered_by_user_id_fkey"
|
|
537
|
+
foreign key ("answered_by_user_id") references "public"."user"("id")
|
|
538
|
+
on delete set null,
|
|
539
|
+
constraint "facebook_ads_lead_call_attempt_status_check"
|
|
540
|
+
check (
|
|
541
|
+
"status" in (
|
|
542
|
+
'pending', 'processing', 'agent_ringing', 'lead_ringing',
|
|
543
|
+
'connected', 'completed', 'skipped', 'failed', 'dead'
|
|
544
|
+
)
|
|
545
|
+
),
|
|
546
|
+
constraint "facebook_ads_lead_call_attempt_count_check"
|
|
547
|
+
check ("attempt_count" >= 0),
|
|
548
|
+
constraint "facebook_ads_lead_call_attempt_from_phone_check"
|
|
549
|
+
check (nullif(btrim("from_phone_number"), '') is not null),
|
|
550
|
+
constraint "facebook_ads_lead_call_attempt_destination_phone_check"
|
|
551
|
+
check (nullif(btrim("destination_phone_number"), '') is not null),
|
|
552
|
+
constraint "facebook_ads_lead_call_attempt_routing_snapshot_check"
|
|
553
|
+
check (jsonb_typeof("routing_snapshot") = 'object')
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
alter table "public"."facebook_ads_lead_call_attempt" owner to "postgres";
|
|
557
|
+
|
|
558
|
+
create index if not exists "facebook_ads_lead_call_attempt_due_idx"
|
|
559
|
+
on "public"."facebook_ads_lead_call_attempt" (
|
|
560
|
+
"next_attempt_at", "created_at"
|
|
561
|
+
) where "status" = 'pending';
|
|
562
|
+
|
|
563
|
+
create index if not exists "facebook_ads_lead_call_attempt_company_history_idx"
|
|
564
|
+
on "public"."facebook_ads_lead_call_attempt" (
|
|
565
|
+
"company_id", "created_at" desc
|
|
566
|
+
);
|
|
567
|
+
|
|
568
|
+
create unique index if not exists "facebook_ads_lead_call_attempt_agent_sid_key"
|
|
569
|
+
on "public"."facebook_ads_lead_call_attempt" ("twilio_agent_call_sid")
|
|
570
|
+
where "twilio_agent_call_sid" is not null;
|
|
571
|
+
|
|
572
|
+
create unique index if not exists "facebook_ads_lead_call_attempt_lead_sid_key"
|
|
573
|
+
on "public"."facebook_ads_lead_call_attempt" ("twilio_lead_call_sid")
|
|
574
|
+
where "twilio_lead_call_sid" is not null;
|
|
575
|
+
|
|
576
|
+
create unique index if not exists "facebook_ads_lead_call_attempt_conference_sid_key"
|
|
577
|
+
on "public"."facebook_ads_lead_call_attempt" ("twilio_conference_sid")
|
|
578
|
+
where "twilio_conference_sid" is not null;
|
|
579
|
+
|
|
580
|
+
create or replace trigger "set_facebook_ads_lead_event_updated_at_trigger"
|
|
581
|
+
before update on "public"."facebook_ads_lead_event"
|
|
582
|
+
for each row execute function "public"."set_facebook_ads_updated_at"();
|
|
583
|
+
|
|
584
|
+
create or replace trigger "set_facebook_ads_lead_call_attempt_updated_at_trigger"
|
|
585
|
+
before update on "public"."facebook_ads_lead_call_attempt"
|
|
586
|
+
for each row execute function "public"."set_facebook_ads_updated_at"();
|
|
587
|
+
|
|
588
|
+
-- ============================================================
|
|
589
|
+
-- Lead settings RPC
|
|
590
|
+
-- ============================================================
|
|
591
|
+
|
|
592
|
+
create or replace function "public"."set_facebook_ads_lead_settings"("p" jsonb)
|
|
593
|
+
returns void
|
|
594
|
+
language plpgsql
|
|
595
|
+
security definer
|
|
596
|
+
set search_path = ''
|
|
597
|
+
as $$
|
|
598
|
+
declare
|
|
599
|
+
"v_company_id" uuid := ("p" ->> 'company_id')::uuid;
|
|
600
|
+
"v_current" "public"."settings_facebook_ads"%rowtype;
|
|
601
|
+
"v_sync_enabled" boolean;
|
|
602
|
+
"v_tag_id" uuid;
|
|
603
|
+
"v_sms_rule_id" uuid;
|
|
604
|
+
"v_email_rule_id" uuid;
|
|
605
|
+
"v_timezone_attested" boolean;
|
|
606
|
+
"v_auto_dial_enabled" boolean;
|
|
607
|
+
"v_phone_number_id" uuid;
|
|
608
|
+
"v_user_id" uuid;
|
|
609
|
+
"v_delay_seconds" integer;
|
|
610
|
+
"v_window_start" time without time zone;
|
|
611
|
+
"v_window_end" time without time zone;
|
|
612
|
+
"v_window_days" smallint[];
|
|
613
|
+
begin
|
|
614
|
+
if jsonb_typeof("p") <> 'object' or "v_company_id" is null then
|
|
615
|
+
raise exception 'company_id and an object payload are required'
|
|
616
|
+
using errcode = '22023';
|
|
617
|
+
end if;
|
|
618
|
+
|
|
619
|
+
if not exists (
|
|
620
|
+
select 1 from "public"."company" where "id" = "v_company_id"
|
|
621
|
+
) then
|
|
622
|
+
raise exception 'Company does not exist' using errcode = '23503';
|
|
623
|
+
end if;
|
|
624
|
+
|
|
625
|
+
insert into "public"."settings_facebook_ads" ("company_id")
|
|
626
|
+
values ("v_company_id")
|
|
627
|
+
on conflict ("company_id") do nothing;
|
|
628
|
+
|
|
629
|
+
select * into "v_current"
|
|
630
|
+
from "public"."settings_facebook_ads"
|
|
631
|
+
where "company_id" = "v_company_id"
|
|
632
|
+
for update;
|
|
633
|
+
|
|
634
|
+
"v_sync_enabled" := case when "p" ? 'lead_sync_enabled'
|
|
635
|
+
then coalesce(("p" ->> 'lead_sync_enabled')::boolean, false)
|
|
636
|
+
else "v_current"."lead_sync_enabled" end;
|
|
637
|
+
"v_tag_id" := case when "p" ? 'lead_tag_id'
|
|
638
|
+
then ("p" ->> 'lead_tag_id')::uuid
|
|
639
|
+
else "v_current"."lead_tag_id" end;
|
|
640
|
+
"v_sms_rule_id" := case when "p" ? 'lead_sms_rule_id'
|
|
641
|
+
then ("p" ->> 'lead_sms_rule_id')::uuid
|
|
642
|
+
else "v_current"."lead_sms_rule_id" end;
|
|
643
|
+
"v_email_rule_id" := case when "p" ? 'lead_email_rule_id'
|
|
644
|
+
then ("p" ->> 'lead_email_rule_id')::uuid
|
|
645
|
+
else "v_current"."lead_email_rule_id" end;
|
|
646
|
+
"v_timezone_attested" := case
|
|
647
|
+
when "p" ? 'lead_auto_dial_timezone_attested'
|
|
648
|
+
then coalesce(
|
|
649
|
+
("p" ->> 'lead_auto_dial_timezone_attested')::boolean,
|
|
650
|
+
false
|
|
651
|
+
)
|
|
652
|
+
else "v_current"."lead_auto_dial_timezone_attested" end;
|
|
653
|
+
"v_auto_dial_enabled" := case when "p" ? 'lead_auto_dial_enabled'
|
|
654
|
+
then coalesce(("p" ->> 'lead_auto_dial_enabled')::boolean, false)
|
|
655
|
+
else "v_current"."lead_auto_dial_enabled" end;
|
|
656
|
+
"v_phone_number_id" := case when "p" ? 'lead_auto_dial_phone_number_id'
|
|
657
|
+
then ("p" ->> 'lead_auto_dial_phone_number_id')::uuid
|
|
658
|
+
else "v_current"."lead_auto_dial_phone_number_id" end;
|
|
659
|
+
"v_user_id" := case when "p" ? 'lead_auto_dial_user_id'
|
|
660
|
+
then ("p" ->> 'lead_auto_dial_user_id')::uuid
|
|
661
|
+
else "v_current"."lead_auto_dial_user_id" end;
|
|
662
|
+
"v_delay_seconds" := case when "p" ? 'lead_auto_dial_delay_seconds'
|
|
663
|
+
then coalesce(("p" ->> 'lead_auto_dial_delay_seconds')::integer, 0)
|
|
664
|
+
else "v_current"."lead_auto_dial_delay_seconds" end;
|
|
665
|
+
"v_window_start" := case when "p" ? 'lead_auto_dial_window_start'
|
|
666
|
+
then ("p" ->> 'lead_auto_dial_window_start')::time without time zone
|
|
667
|
+
else "v_current"."lead_auto_dial_window_start" end;
|
|
668
|
+
"v_window_end" := case when "p" ? 'lead_auto_dial_window_end'
|
|
669
|
+
then ("p" ->> 'lead_auto_dial_window_end')::time without time zone
|
|
670
|
+
else "v_current"."lead_auto_dial_window_end" end;
|
|
671
|
+
|
|
672
|
+
if "p" ? 'lead_auto_dial_window_days' then
|
|
673
|
+
if jsonb_typeof("p" -> 'lead_auto_dial_window_days') <> 'array' then
|
|
674
|
+
raise exception 'lead_auto_dial_window_days must be an array'
|
|
675
|
+
using errcode = '22023';
|
|
676
|
+
end if;
|
|
677
|
+
select coalesce(
|
|
678
|
+
array_agg("day_row"."value"::smallint order by "day_row"."value"::smallint),
|
|
679
|
+
'{}'::smallint[]
|
|
680
|
+
) into "v_window_days"
|
|
681
|
+
from jsonb_array_elements_text(
|
|
682
|
+
"p" -> 'lead_auto_dial_window_days'
|
|
683
|
+
) as "day_row"("value");
|
|
684
|
+
else
|
|
685
|
+
"v_window_days" := "v_current"."lead_auto_dial_window_days";
|
|
686
|
+
end if;
|
|
687
|
+
|
|
688
|
+
if "v_sync_enabled" and "v_current"."source_id" is null then
|
|
689
|
+
raise exception 'A Facebook Ads lead source is required before enabling lead sync'
|
|
690
|
+
using errcode = '23514';
|
|
691
|
+
end if;
|
|
692
|
+
|
|
693
|
+
if "v_tag_id" is not null and not exists (
|
|
694
|
+
select 1 from "public"."client_tag"
|
|
695
|
+
where "id" = "v_tag_id" and "company_id" = "v_company_id"
|
|
696
|
+
) then
|
|
697
|
+
raise exception 'Lead tag does not belong to this company'
|
|
698
|
+
using errcode = '23503';
|
|
699
|
+
end if;
|
|
700
|
+
|
|
701
|
+
-- Facebook follow-up is marketing by definition. Requiring both trusted
|
|
702
|
+
-- Facebook provenance and explicit marketing classification prevents a
|
|
703
|
+
-- generic lead rule from being repurposed and keeps the executor's
|
|
704
|
+
-- per-channel DNM gates in the path for every selected rule.
|
|
705
|
+
if "v_sms_rule_id" is not null and not exists (
|
|
706
|
+
select 1 from "public"."automation_rule"
|
|
707
|
+
where "id" = "v_sms_rule_id"
|
|
708
|
+
and "company_id" = "v_company_id"
|
|
709
|
+
and "trigger_type" = 'lead_created'
|
|
710
|
+
and "trigger_config" ->> 'created_via' = 'facebook_lead_ads'
|
|
711
|
+
and "action_type" = 'sms_client'
|
|
712
|
+
and "action_config" ->> 'communication_type' = 'marketing'
|
|
713
|
+
) then
|
|
714
|
+
raise exception 'SMS rule must be this company''s Facebook lead-created marketing SMS rule'
|
|
715
|
+
using errcode = '23503';
|
|
716
|
+
end if;
|
|
717
|
+
|
|
718
|
+
if "v_email_rule_id" is not null and not exists (
|
|
719
|
+
select 1 from "public"."automation_rule"
|
|
720
|
+
where "id" = "v_email_rule_id"
|
|
721
|
+
and "company_id" = "v_company_id"
|
|
722
|
+
and "trigger_type" = 'lead_created'
|
|
723
|
+
and "trigger_config" ->> 'created_via' = 'facebook_lead_ads'
|
|
724
|
+
and "action_type" = 'email_client'
|
|
725
|
+
and "action_config" ->> 'communication_type' = 'marketing'
|
|
726
|
+
) then
|
|
727
|
+
raise exception 'Email rule must be this company''s Facebook lead-created marketing email rule'
|
|
728
|
+
using errcode = '23503';
|
|
729
|
+
end if;
|
|
730
|
+
|
|
731
|
+
if "v_sms_rule_id" is not null
|
|
732
|
+
and "v_sms_rule_id" = "v_email_rule_id" then
|
|
733
|
+
raise exception 'SMS and email rules must be different'
|
|
734
|
+
using errcode = '23514';
|
|
735
|
+
end if;
|
|
736
|
+
|
|
737
|
+
if "v_delay_seconds" not between 0 and 86400
|
|
738
|
+
or "v_window_start" is null
|
|
739
|
+
or "v_window_end" is null
|
|
740
|
+
or "v_window_start" >= "v_window_end"
|
|
741
|
+
or not "public"."facebook_ads_call_days_are_normalized"("v_window_days")
|
|
742
|
+
then
|
|
743
|
+
raise exception 'Auto-dial delay or call window is invalid'
|
|
744
|
+
using errcode = '23514';
|
|
745
|
+
end if;
|
|
746
|
+
|
|
747
|
+
if "v_auto_dial_enabled" then
|
|
748
|
+
if not "v_timezone_attested" then
|
|
749
|
+
raise exception 'Auto-dial requires company service timezone attestation'
|
|
750
|
+
using errcode = '23514';
|
|
751
|
+
end if;
|
|
752
|
+
|
|
753
|
+
if "v_phone_number_id" is null or "v_user_id" is null then
|
|
754
|
+
raise exception 'Auto-dial requires a phone number and user'
|
|
755
|
+
using errcode = '23514';
|
|
756
|
+
end if;
|
|
757
|
+
|
|
758
|
+
if not exists (
|
|
759
|
+
select 1 from "public"."phone_number"
|
|
760
|
+
where "id" = "v_phone_number_id"
|
|
761
|
+
and "company_id" = "v_company_id"
|
|
762
|
+
and "twilio" = true
|
|
763
|
+
and "client_id" is null
|
|
764
|
+
and "phone_sid" is not null
|
|
765
|
+
) then
|
|
766
|
+
raise exception 'Auto-dial phone number is not an active company Twilio number'
|
|
767
|
+
using errcode = '23503';
|
|
768
|
+
end if;
|
|
769
|
+
|
|
770
|
+
if not exists (
|
|
771
|
+
select 1 from "public"."user"
|
|
772
|
+
where "id" = "v_user_id"
|
|
773
|
+
and "company_id" = "v_company_id"
|
|
774
|
+
and "is_current_employee" = true
|
|
775
|
+
) then
|
|
776
|
+
raise exception 'Auto-dial user is not a current company employee'
|
|
777
|
+
using errcode = '23503';
|
|
778
|
+
end if;
|
|
779
|
+
|
|
780
|
+
if not exists (
|
|
781
|
+
select 1 from "public"."phone_number_user_link"
|
|
782
|
+
where "company_id" = "v_company_id"
|
|
783
|
+
and "phone_number_id" = "v_phone_number_id"
|
|
784
|
+
and "user_id" = "v_user_id"
|
|
785
|
+
) then
|
|
786
|
+
raise exception 'Auto-dial user is not linked to the selected number'
|
|
787
|
+
using errcode = '23514';
|
|
788
|
+
end if;
|
|
789
|
+
end if;
|
|
790
|
+
|
|
791
|
+
update "public"."settings_facebook_ads"
|
|
792
|
+
set
|
|
793
|
+
"lead_sync_enabled" = "v_sync_enabled",
|
|
794
|
+
"lead_tag_id" = "v_tag_id",
|
|
795
|
+
"lead_sms_rule_id" = "v_sms_rule_id",
|
|
796
|
+
"lead_email_rule_id" = "v_email_rule_id",
|
|
797
|
+
"lead_auto_dial_timezone_attested" = "v_timezone_attested",
|
|
798
|
+
"lead_auto_dial_enabled" = "v_auto_dial_enabled",
|
|
799
|
+
"lead_auto_dial_phone_number_id" = "v_phone_number_id",
|
|
800
|
+
"lead_auto_dial_user_id" = "v_user_id",
|
|
801
|
+
"lead_auto_dial_delay_seconds" = "v_delay_seconds",
|
|
802
|
+
"lead_auto_dial_window_start" = "v_window_start",
|
|
803
|
+
"lead_auto_dial_window_end" = "v_window_end",
|
|
804
|
+
"lead_auto_dial_window_days" = "v_window_days"
|
|
805
|
+
where "company_id" = "v_company_id";
|
|
806
|
+
end;
|
|
807
|
+
$$;
|
|
808
|
+
|
|
809
|
+
alter function "public"."set_facebook_ads_lead_settings"(jsonb)
|
|
810
|
+
owner to "postgres";
|
|
811
|
+
|
|
812
|
+
-- ============================================================
|
|
813
|
+
-- Receipt, claim, replay, and finalization RPCs
|
|
814
|
+
-- ============================================================
|
|
815
|
+
|
|
816
|
+
create or replace function "public"."enqueue_facebook_ads_lead_event"("p" jsonb)
|
|
817
|
+
returns jsonb
|
|
818
|
+
language plpgsql
|
|
819
|
+
security definer
|
|
820
|
+
set search_path = ''
|
|
821
|
+
as $$
|
|
822
|
+
declare
|
|
823
|
+
"v_page" "public"."facebook_ads_lead_page"%rowtype;
|
|
824
|
+
"v_form" "public"."facebook_ads_lead_form"%rowtype;
|
|
825
|
+
"v_settings" "public"."settings_facebook_ads"%rowtype;
|
|
826
|
+
"v_form_found" boolean := false;
|
|
827
|
+
"v_form_selected" boolean := false;
|
|
828
|
+
"v_accepted" boolean := false;
|
|
829
|
+
"v_reason" text;
|
|
830
|
+
"v_snapshot" jsonb := '{}'::jsonb;
|
|
831
|
+
"v_event_id" uuid;
|
|
832
|
+
"v_received_count" integer;
|
|
833
|
+
"v_status" text;
|
|
834
|
+
"v_return_reason" text;
|
|
835
|
+
"v_webhook_payload" jsonb := coalesce("p" -> 'webhook_payload', '{}'::jsonb);
|
|
836
|
+
begin
|
|
837
|
+
if jsonb_typeof("p") <> 'object'
|
|
838
|
+
or jsonb_typeof("v_webhook_payload") <> 'object'
|
|
839
|
+
or coalesce("p" ->> 'meta_page_id', '') !~ '^[0-9]+$'
|
|
840
|
+
or coalesce("p" ->> 'meta_leadgen_id', '') !~ '^[0-9]+$'
|
|
841
|
+
or coalesce("p" ->> 'meta_form_id', '') !~ '^[0-9]+$'
|
|
842
|
+
or "p" ->> 'meta_created_at' is null
|
|
843
|
+
then
|
|
844
|
+
raise exception 'Valid Meta Page, lead, Form, created time, and payload are required'
|
|
845
|
+
using errcode = '22023';
|
|
846
|
+
end if;
|
|
847
|
+
|
|
848
|
+
select * into "v_page"
|
|
849
|
+
from "public"."facebook_ads_lead_page"
|
|
850
|
+
where "meta_page_id" = "p" ->> 'meta_page_id';
|
|
851
|
+
|
|
852
|
+
if not found then
|
|
853
|
+
return jsonb_build_object(
|
|
854
|
+
'event_id', null,
|
|
855
|
+
'accepted', false,
|
|
856
|
+
'duplicate', false,
|
|
857
|
+
'reason', 'page_not_configured'
|
|
858
|
+
);
|
|
859
|
+
end if;
|
|
860
|
+
|
|
861
|
+
select * into "v_settings"
|
|
862
|
+
from "public"."settings_facebook_ads"
|
|
863
|
+
where "company_id" = "v_page"."company_id";
|
|
864
|
+
|
|
865
|
+
select * into "v_form"
|
|
866
|
+
from "public"."facebook_ads_lead_form"
|
|
867
|
+
where "company_id" = "v_page"."company_id"
|
|
868
|
+
and "page_id" = "v_page"."id"
|
|
869
|
+
and "meta_form_id" = "p" ->> 'meta_form_id';
|
|
870
|
+
"v_form_found" := found;
|
|
871
|
+
|
|
872
|
+
if "v_form_found" then
|
|
873
|
+
"v_snapshot" := jsonb_build_object(
|
|
874
|
+
'form_id', "v_form"."id",
|
|
875
|
+
'form_name', "v_form"."form_name",
|
|
876
|
+
'is_enabled', "v_form"."is_enabled",
|
|
877
|
+
'field_mapping', "v_form"."field_mapping",
|
|
878
|
+
'sms', jsonb_build_object(
|
|
879
|
+
'attested', "v_form"."sms_consent_attested",
|
|
880
|
+
'disclosure', "v_form"."sms_consent_disclosure"
|
|
881
|
+
),
|
|
882
|
+
'email', jsonb_build_object(
|
|
883
|
+
'attested', "v_form"."email_consent_attested",
|
|
884
|
+
'disclosure', "v_form"."email_consent_disclosure"
|
|
885
|
+
),
|
|
886
|
+
'voice', jsonb_build_object(
|
|
887
|
+
'attested', "v_form"."voice_consent_attested",
|
|
888
|
+
'disclosure', "v_form"."voice_consent_disclosure"
|
|
889
|
+
)
|
|
890
|
+
);
|
|
891
|
+
end if;
|
|
892
|
+
|
|
893
|
+
"v_form_selected" := "v_page"."form_selection_mode" = 'all'
|
|
894
|
+
or ("v_form_found" and "v_form"."is_enabled");
|
|
895
|
+
"v_accepted" := "v_page"."is_enabled"
|
|
896
|
+
and "v_page"."subscription_status" = 'subscribed'
|
|
897
|
+
and coalesce("v_settings"."lead_sync_enabled", false)
|
|
898
|
+
and "v_settings"."source_id" is not null
|
|
899
|
+
and "v_form_selected";
|
|
900
|
+
|
|
901
|
+
"v_reason" := case
|
|
902
|
+
when not "v_page"."is_enabled"
|
|
903
|
+
and "v_page"."subscription_status" = 'pending'
|
|
904
|
+
then 'page_activation_pending'
|
|
905
|
+
when not "v_page"."is_enabled" then 'page_disabled'
|
|
906
|
+
when "v_page"."subscription_status" <> 'subscribed' then 'page_not_subscribed'
|
|
907
|
+
when not coalesce("v_settings"."lead_sync_enabled", false) then 'lead_sync_disabled'
|
|
908
|
+
when "v_settings"."source_id" is null then 'source_not_configured'
|
|
909
|
+
when not "v_form_selected" then 'form_not_selected'
|
|
910
|
+
else null
|
|
911
|
+
end;
|
|
912
|
+
|
|
913
|
+
insert into "public"."facebook_ads_lead_event" (
|
|
914
|
+
"company_id", "page_id", "meta_page_id", "meta_leadgen_id",
|
|
915
|
+
"meta_form_id", "meta_created_at", "source_id", "webhook_payload",
|
|
916
|
+
"form_configuration_snapshot", "status", "skip_reason"
|
|
917
|
+
) values (
|
|
918
|
+
"v_page"."company_id",
|
|
919
|
+
"v_page"."id",
|
|
920
|
+
"p" ->> 'meta_page_id',
|
|
921
|
+
"p" ->> 'meta_leadgen_id',
|
|
922
|
+
"p" ->> 'meta_form_id',
|
|
923
|
+
("p" ->> 'meta_created_at')::timestamp with time zone,
|
|
924
|
+
"v_settings"."source_id",
|
|
925
|
+
"v_webhook_payload",
|
|
926
|
+
"v_snapshot",
|
|
927
|
+
case when "v_accepted" then 'pending' else 'skipped' end,
|
|
928
|
+
"v_reason"
|
|
929
|
+
)
|
|
930
|
+
on conflict ("meta_page_id", "meta_leadgen_id") do update
|
|
931
|
+
set
|
|
932
|
+
"received_count" = "facebook_ads_lead_event"."received_count" + 1,
|
|
933
|
+
"last_received_at" = now()
|
|
934
|
+
returning "id", "received_count", "status", "skip_reason"
|
|
935
|
+
into "v_event_id", "v_received_count", "v_status", "v_return_reason";
|
|
936
|
+
|
|
937
|
+
return jsonb_build_object(
|
|
938
|
+
'event_id', "v_event_id",
|
|
939
|
+
'accepted', "v_status" <> 'skipped',
|
|
940
|
+
'duplicate', "v_received_count" > 1,
|
|
941
|
+
'reason', "v_return_reason"
|
|
942
|
+
);
|
|
943
|
+
end;
|
|
944
|
+
$$;
|
|
945
|
+
|
|
946
|
+
alter function "public"."enqueue_facebook_ads_lead_event"(jsonb)
|
|
947
|
+
owner to "postgres";
|
|
948
|
+
|
|
949
|
+
create or replace function "public"."claim_facebook_ads_lead_events"(
|
|
950
|
+
"batch_size" integer
|
|
951
|
+
)
|
|
952
|
+
returns setof "public"."facebook_ads_lead_event"
|
|
953
|
+
language sql
|
|
954
|
+
security definer
|
|
955
|
+
set search_path = ''
|
|
956
|
+
as $$
|
|
957
|
+
update "public"."facebook_ads_lead_event"
|
|
958
|
+
set
|
|
959
|
+
"status" = 'processing',
|
|
960
|
+
"attempt_count" = "attempt_count" + 1,
|
|
961
|
+
"last_attempt_at" = now()
|
|
962
|
+
where "id" in (
|
|
963
|
+
select "id"
|
|
964
|
+
from "public"."facebook_ads_lead_event"
|
|
965
|
+
where "status" = 'pending' and "next_attempt_at" <= now()
|
|
966
|
+
order by "next_attempt_at", "created_at"
|
|
967
|
+
limit least(greatest(coalesce("batch_size", 0), 0), 100)
|
|
968
|
+
for update skip locked
|
|
969
|
+
)
|
|
970
|
+
returning *;
|
|
971
|
+
$$;
|
|
972
|
+
|
|
973
|
+
alter function "public"."claim_facebook_ads_lead_events"(integer)
|
|
974
|
+
owner to "postgres";
|
|
975
|
+
|
|
976
|
+
create or replace function "public"."replay_facebook_ads_lead_event"(
|
|
977
|
+
"p_company_id" uuid,
|
|
978
|
+
"p_event_id" uuid
|
|
979
|
+
)
|
|
980
|
+
returns boolean
|
|
981
|
+
language plpgsql
|
|
982
|
+
security definer
|
|
983
|
+
set search_path = ''
|
|
984
|
+
as $$
|
|
985
|
+
declare
|
|
986
|
+
"v_updated" uuid;
|
|
987
|
+
begin
|
|
988
|
+
update "public"."facebook_ads_lead_event" as "event_row"
|
|
989
|
+
set
|
|
990
|
+
"status" = 'pending',
|
|
991
|
+
"skip_reason" = null,
|
|
992
|
+
"attempt_count" = 0,
|
|
993
|
+
"replay_count" = "event_row"."replay_count" + 1,
|
|
994
|
+
"next_attempt_at" = now(),
|
|
995
|
+
"last_attempt_at" = null,
|
|
996
|
+
"processed_at" = null,
|
|
997
|
+
"last_error" = null
|
|
998
|
+
from "public"."settings_facebook_ads" as "settings",
|
|
999
|
+
"public"."facebook_ads_lead_page" as "page"
|
|
1000
|
+
where "event_row"."id" = "p_event_id"
|
|
1001
|
+
and "event_row"."company_id" = "p_company_id"
|
|
1002
|
+
and "event_row"."status" in ('skipped', 'dead')
|
|
1003
|
+
and "event_row"."client_id" is null
|
|
1004
|
+
and "event_row"."page_id" = "page"."id"
|
|
1005
|
+
and "page"."company_id" = "p_company_id"
|
|
1006
|
+
and "page"."is_enabled"
|
|
1007
|
+
and "page"."subscription_status" = 'subscribed'
|
|
1008
|
+
-- Replay must respect the Page's current Form-selection policy. The
|
|
1009
|
+
-- receipt-time form_configuration_snapshot remains untouched below so
|
|
1010
|
+
-- consent evidence cannot be upgraded by later Form edits.
|
|
1011
|
+
and (
|
|
1012
|
+
"page"."form_selection_mode" = 'all'
|
|
1013
|
+
or (
|
|
1014
|
+
"page"."form_selection_mode" = 'selected'
|
|
1015
|
+
and exists (
|
|
1016
|
+
select 1
|
|
1017
|
+
from "public"."facebook_ads_lead_form" as "form"
|
|
1018
|
+
where "form"."company_id" = "p_company_id"
|
|
1019
|
+
and "form"."page_id" = "page"."id"
|
|
1020
|
+
and "form"."meta_form_id" = "event_row"."meta_form_id"
|
|
1021
|
+
and "form"."is_enabled"
|
|
1022
|
+
)
|
|
1023
|
+
)
|
|
1024
|
+
)
|
|
1025
|
+
and "settings"."company_id" = "p_company_id"
|
|
1026
|
+
and "settings"."lead_sync_enabled"
|
|
1027
|
+
and "settings"."source_id" is not null
|
|
1028
|
+
returning "event_row"."id" into "v_updated";
|
|
1029
|
+
|
|
1030
|
+
return "v_updated" is not null;
|
|
1031
|
+
end;
|
|
1032
|
+
$$;
|
|
1033
|
+
|
|
1034
|
+
alter function "public"."replay_facebook_ads_lead_event"(uuid, uuid)
|
|
1035
|
+
owner to "postgres";
|
|
1036
|
+
|
|
1037
|
+
create or replace function "public"."promote_facebook_ads_activation_gap_events"(
|
|
1038
|
+
"p_company_id" uuid,
|
|
1039
|
+
"p_page_id" uuid
|
|
1040
|
+
)
|
|
1041
|
+
returns table ("event_id" uuid)
|
|
1042
|
+
language plpgsql
|
|
1043
|
+
security definer
|
|
1044
|
+
set search_path = ''
|
|
1045
|
+
as $$
|
|
1046
|
+
begin
|
|
1047
|
+
if "p_company_id" is null or "p_page_id" is null then
|
|
1048
|
+
raise exception 'company_id and page_id are required'
|
|
1049
|
+
using errcode = '22023';
|
|
1050
|
+
end if;
|
|
1051
|
+
|
|
1052
|
+
return query
|
|
1053
|
+
update "public"."facebook_ads_lead_event" as "event_row"
|
|
1054
|
+
set
|
|
1055
|
+
"status" = 'pending',
|
|
1056
|
+
"skip_reason" = null,
|
|
1057
|
+
"attempt_count" = 0,
|
|
1058
|
+
"replay_count" = "event_row"."replay_count" + 1,
|
|
1059
|
+
"next_attempt_at" = now(),
|
|
1060
|
+
"last_attempt_at" = null,
|
|
1061
|
+
"processed_at" = null,
|
|
1062
|
+
"last_error" = null
|
|
1063
|
+
from "public"."settings_facebook_ads" as "settings",
|
|
1064
|
+
"public"."facebook_ads_lead_page" as "page"
|
|
1065
|
+
where "event_row"."company_id" = "p_company_id"
|
|
1066
|
+
and "event_row"."page_id" = "p_page_id"
|
|
1067
|
+
and "event_row"."status" = 'skipped'
|
|
1068
|
+
and "event_row"."skip_reason" = 'page_activation_pending'
|
|
1069
|
+
and "event_row"."client_id" is null
|
|
1070
|
+
and "page"."company_id" = "p_company_id"
|
|
1071
|
+
and "page"."id" = "p_page_id"
|
|
1072
|
+
and "page"."is_enabled"
|
|
1073
|
+
and "page"."subscription_status" = 'subscribed'
|
|
1074
|
+
and "settings"."company_id" = "p_company_id"
|
|
1075
|
+
and "settings"."lead_sync_enabled"
|
|
1076
|
+
and "settings"."source_id" is not null
|
|
1077
|
+
and nullif(btrim(
|
|
1078
|
+
"event_row"."form_configuration_snapshot" ->> 'form_id'
|
|
1079
|
+
), '') is not null
|
|
1080
|
+
and "event_row"."form_configuration_snapshot" -> 'is_enabled'
|
|
1081
|
+
= 'true'::jsonb
|
|
1082
|
+
and exists (
|
|
1083
|
+
select 1
|
|
1084
|
+
from (values ('sms'), ('email'), ('voice')) as "channel_row"("name")
|
|
1085
|
+
where "event_row"."form_configuration_snapshot"
|
|
1086
|
+
-> "channel_row"."name" -> 'attested' = 'true'::jsonb
|
|
1087
|
+
and nullif(btrim(
|
|
1088
|
+
"event_row"."form_configuration_snapshot"
|
|
1089
|
+
-> "channel_row"."name" ->> 'disclosure'
|
|
1090
|
+
), '') is not null
|
|
1091
|
+
)
|
|
1092
|
+
returning "event_row"."id";
|
|
1093
|
+
end;
|
|
1094
|
+
$$;
|
|
1095
|
+
|
|
1096
|
+
alter function "public"."promote_facebook_ads_activation_gap_events"(uuid, uuid)
|
|
1097
|
+
owner to "postgres";
|
|
1098
|
+
|
|
1099
|
+
create or replace function "public"."finalize_facebook_ads_lead_event"("p" jsonb)
|
|
1100
|
+
returns table (
|
|
1101
|
+
"event_id" uuid,
|
|
1102
|
+
"client_id" uuid,
|
|
1103
|
+
"client_created" boolean,
|
|
1104
|
+
"call_attempt_id" uuid
|
|
1105
|
+
)
|
|
1106
|
+
language plpgsql
|
|
1107
|
+
security definer
|
|
1108
|
+
set search_path = ''
|
|
1109
|
+
as $$
|
|
1110
|
+
declare
|
|
1111
|
+
"v_event" "public"."facebook_ads_lead_event"%rowtype;
|
|
1112
|
+
"v_page" "public"."facebook_ads_lead_page"%rowtype;
|
|
1113
|
+
"v_settings" "public"."settings_facebook_ads"%rowtype;
|
|
1114
|
+
"v_client_id" uuid;
|
|
1115
|
+
"v_call_attempt_id" uuid;
|
|
1116
|
+
"v_voice_consent_id" uuid;
|
|
1117
|
+
"v_inserted_consent_id" uuid;
|
|
1118
|
+
"v_phone" text := nullif(btrim("p" ->> 'primary_phone_number'), '');
|
|
1119
|
+
"v_email" text := nullif(btrim("p" ->> 'email'), '');
|
|
1120
|
+
"v_consents" jsonb := coalesce("p" -> 'consents', '[]'::jsonb);
|
|
1121
|
+
"v_consent" jsonb;
|
|
1122
|
+
"v_channel" text;
|
|
1123
|
+
"v_status" text;
|
|
1124
|
+
"v_disclosure" text;
|
|
1125
|
+
"v_attested" boolean;
|
|
1126
|
+
"v_sms_granted" boolean := false;
|
|
1127
|
+
"v_email_granted" boolean := false;
|
|
1128
|
+
"v_voice_granted" boolean := false;
|
|
1129
|
+
"v_from_phone" text;
|
|
1130
|
+
"v_company_timezone" text;
|
|
1131
|
+
begin
|
|
1132
|
+
if jsonb_typeof("p") <> 'object'
|
|
1133
|
+
or "p" ->> 'event_id' is null
|
|
1134
|
+
or jsonb_typeof(coalesce("p" -> 'lead_payload', '{}'::jsonb)) <> 'object'
|
|
1135
|
+
or jsonb_typeof("v_consents") <> 'array'
|
|
1136
|
+
then
|
|
1137
|
+
raise exception 'event_id, lead payload, and a consent array are required'
|
|
1138
|
+
using errcode = '22023';
|
|
1139
|
+
end if;
|
|
1140
|
+
|
|
1141
|
+
select * into "v_event"
|
|
1142
|
+
from "public"."facebook_ads_lead_event"
|
|
1143
|
+
where "id" = ("p" ->> 'event_id')::uuid
|
|
1144
|
+
for update;
|
|
1145
|
+
|
|
1146
|
+
if not found then
|
|
1147
|
+
raise exception 'Facebook lead event does not exist'
|
|
1148
|
+
using errcode = 'P0002';
|
|
1149
|
+
end if;
|
|
1150
|
+
|
|
1151
|
+
if "v_event"."status" = 'processed' and "v_event"."client_id" is not null then
|
|
1152
|
+
return query select "v_event"."id", "v_event"."client_id", false, (
|
|
1153
|
+
select "id" from "public"."facebook_ads_lead_call_attempt"
|
|
1154
|
+
where "lead_event_id" = "v_event"."id"
|
|
1155
|
+
);
|
|
1156
|
+
return;
|
|
1157
|
+
end if;
|
|
1158
|
+
|
|
1159
|
+
if "v_event"."status" <> 'processing' or "v_event"."client_id" is not null then
|
|
1160
|
+
raise exception 'Facebook lead event is not claimable for finalization'
|
|
1161
|
+
using errcode = '55000';
|
|
1162
|
+
end if;
|
|
1163
|
+
|
|
1164
|
+
select * into "v_settings"
|
|
1165
|
+
from "public"."settings_facebook_ads"
|
|
1166
|
+
where "company_id" = "v_event"."company_id"
|
|
1167
|
+
for share;
|
|
1168
|
+
|
|
1169
|
+
if not found
|
|
1170
|
+
or not "v_settings"."lead_sync_enabled"
|
|
1171
|
+
or "v_settings"."source_id" is null
|
|
1172
|
+
then
|
|
1173
|
+
update "public"."facebook_ads_lead_event"
|
|
1174
|
+
set "status" = 'skipped', "skip_reason" = 'lead_sync_disabled'
|
|
1175
|
+
where "id" = "v_event"."id";
|
|
1176
|
+
return query select "v_event"."id", null::uuid, false, null::uuid;
|
|
1177
|
+
return;
|
|
1178
|
+
end if;
|
|
1179
|
+
|
|
1180
|
+
-- Graph retrieval happens outside this transaction. Re-lock the current
|
|
1181
|
+
-- Page before any client/automation side effect so a disconnect that
|
|
1182
|
+
-- completed in that interval fails closed. Holding the Page row through
|
|
1183
|
+
-- commit also prevents it from being disabled after this check but before
|
|
1184
|
+
-- the client INSERT below.
|
|
1185
|
+
select * into "v_page"
|
|
1186
|
+
from "public"."facebook_ads_lead_page"
|
|
1187
|
+
where "id" = "v_event"."page_id"
|
|
1188
|
+
and "company_id" = "v_event"."company_id"
|
|
1189
|
+
for share;
|
|
1190
|
+
|
|
1191
|
+
if not found or not "v_page"."is_enabled" then
|
|
1192
|
+
update "public"."facebook_ads_lead_event"
|
|
1193
|
+
set "status" = 'skipped', "skip_reason" = 'page_disabled'
|
|
1194
|
+
where "id" = "v_event"."id";
|
|
1195
|
+
return query select "v_event"."id", null::uuid, false, null::uuid;
|
|
1196
|
+
return;
|
|
1197
|
+
end if;
|
|
1198
|
+
|
|
1199
|
+
if "v_page"."subscription_status" <> 'subscribed' then
|
|
1200
|
+
update "public"."facebook_ads_lead_event"
|
|
1201
|
+
set "status" = 'skipped', "skip_reason" = 'page_not_subscribed'
|
|
1202
|
+
where "id" = "v_event"."id";
|
|
1203
|
+
return query select "v_event"."id", null::uuid, false, null::uuid;
|
|
1204
|
+
return;
|
|
1205
|
+
end if;
|
|
1206
|
+
|
|
1207
|
+
if not exists (
|
|
1208
|
+
select 1 from "public"."job_source"
|
|
1209
|
+
where "id" = "v_settings"."source_id"
|
|
1210
|
+
and "company_id" = "v_event"."company_id"
|
|
1211
|
+
) then
|
|
1212
|
+
raise exception 'Configured Facebook lead source is invalid'
|
|
1213
|
+
using errcode = '23503';
|
|
1214
|
+
end if;
|
|
1215
|
+
|
|
1216
|
+
if nullif(btrim("p" ->> 'first_name'), '') is null
|
|
1217
|
+
and nullif(btrim("p" ->> 'last_name'), '') is null
|
|
1218
|
+
and "v_email" is null
|
|
1219
|
+
and "v_phone" is null
|
|
1220
|
+
then
|
|
1221
|
+
raise exception 'A lead must include at least one contact field'
|
|
1222
|
+
using errcode = '23514';
|
|
1223
|
+
end if;
|
|
1224
|
+
|
|
1225
|
+
if (
|
|
1226
|
+
select count(*) <> count(distinct "consent_row"."value" ->> 'channel')
|
|
1227
|
+
from jsonb_array_elements("v_consents") as "consent_row"("value")
|
|
1228
|
+
) then
|
|
1229
|
+
raise exception 'Consent channels must be unique'
|
|
1230
|
+
using errcode = '23505';
|
|
1231
|
+
end if;
|
|
1232
|
+
|
|
1233
|
+
-- Normalize consent before inserting the client. This is a critical
|
|
1234
|
+
-- fail-closed invariant: the lead_created automation trigger fires on
|
|
1235
|
+
-- the client INSERT, so its marketing DND flags must already reflect
|
|
1236
|
+
-- explicit channel consent at that exact statement.
|
|
1237
|
+
for "v_consent" in
|
|
1238
|
+
select "consent_row"."value"
|
|
1239
|
+
from jsonb_array_elements("v_consents") as "consent_row"("value")
|
|
1240
|
+
loop
|
|
1241
|
+
"v_channel" := "v_consent" ->> 'channel';
|
|
1242
|
+
"v_status" := "v_consent" ->> 'status';
|
|
1243
|
+
|
|
1244
|
+
if "v_channel" is null
|
|
1245
|
+
or "v_channel" not in ('email', 'sms', 'voice')
|
|
1246
|
+
or "v_status" is null
|
|
1247
|
+
or "v_status" not in ('granted', 'denied', 'unknown')
|
|
1248
|
+
or jsonb_typeof(coalesce("v_consent" -> 'evidence', '{}'::jsonb)) <> 'object'
|
|
1249
|
+
then
|
|
1250
|
+
raise exception 'Consent channel, status, or evidence is invalid'
|
|
1251
|
+
using errcode = '22023';
|
|
1252
|
+
end if;
|
|
1253
|
+
|
|
1254
|
+
"v_attested" := coalesce((
|
|
1255
|
+
"v_event"."form_configuration_snapshot"
|
|
1256
|
+
-> "v_channel" ->> 'attested'
|
|
1257
|
+
)::boolean, false);
|
|
1258
|
+
"v_disclosure" := nullif(btrim(
|
|
1259
|
+
"v_event"."form_configuration_snapshot"
|
|
1260
|
+
-> "v_channel" ->> 'disclosure'
|
|
1261
|
+
), '');
|
|
1262
|
+
|
|
1263
|
+
if "v_status" = 'granted' and (
|
|
1264
|
+
not "v_attested"
|
|
1265
|
+
or "v_disclosure" is null
|
|
1266
|
+
or "v_consent" ->> 'captured_at' is null
|
|
1267
|
+
) then
|
|
1268
|
+
raise exception 'Granted consent requires an attested Form disclosure and capture time'
|
|
1269
|
+
using errcode = '23514';
|
|
1270
|
+
end if;
|
|
1271
|
+
|
|
1272
|
+
if "v_status" = 'granted' and "v_channel" = 'sms' then
|
|
1273
|
+
"v_sms_granted" := true;
|
|
1274
|
+
elsif "v_status" = 'granted' and "v_channel" = 'email' then
|
|
1275
|
+
"v_email_granted" := true;
|
|
1276
|
+
elsif "v_status" = 'granted' and "v_channel" = 'voice' then
|
|
1277
|
+
"v_voice_granted" := true;
|
|
1278
|
+
end if;
|
|
1279
|
+
end loop;
|
|
1280
|
+
|
|
1281
|
+
insert into "public"."client" (
|
|
1282
|
+
"company_id", "email", "first_name", "last_name",
|
|
1283
|
+
"client_or_lead", "primary_phone_number", "source_id",
|
|
1284
|
+
"created_via", "is_do_not_marketing_sms",
|
|
1285
|
+
"is_do_not_marketing_email"
|
|
1286
|
+
) values (
|
|
1287
|
+
"v_event"."company_id",
|
|
1288
|
+
"v_email",
|
|
1289
|
+
nullif(btrim("p" ->> 'first_name'), ''),
|
|
1290
|
+
nullif(btrim("p" ->> 'last_name'), ''),
|
|
1291
|
+
'lead'::"public"."client_or_lead_enum",
|
|
1292
|
+
"v_phone",
|
|
1293
|
+
"v_settings"."source_id",
|
|
1294
|
+
'facebook_lead_ads',
|
|
1295
|
+
not "v_sms_granted",
|
|
1296
|
+
not "v_email_granted"
|
|
1297
|
+
) returning "id" into "v_client_id";
|
|
1298
|
+
|
|
1299
|
+
if "v_phone" is not null then
|
|
1300
|
+
insert into "public"."phone_number" (
|
|
1301
|
+
"company_id", "client_id", "twilio", "phone_number", "label"
|
|
1302
|
+
) values (
|
|
1303
|
+
"v_event"."company_id", "v_client_id", false, "v_phone", 'Primary'
|
|
1304
|
+
);
|
|
1305
|
+
end if;
|
|
1306
|
+
|
|
1307
|
+
if "v_settings"."lead_tag_id" is not null then
|
|
1308
|
+
if not exists (
|
|
1309
|
+
select 1 from "public"."client_tag"
|
|
1310
|
+
where "id" = "v_settings"."lead_tag_id"
|
|
1311
|
+
and "company_id" = "v_event"."company_id"
|
|
1312
|
+
) then
|
|
1313
|
+
raise exception 'Configured Facebook lead tag is invalid'
|
|
1314
|
+
using errcode = '23503';
|
|
1315
|
+
end if;
|
|
1316
|
+
insert into "public"."client_tag_link" (
|
|
1317
|
+
"company_id", "client_id", "client_tag_id"
|
|
1318
|
+
) values (
|
|
1319
|
+
"v_event"."company_id", "v_client_id", "v_settings"."lead_tag_id"
|
|
1320
|
+
);
|
|
1321
|
+
end if;
|
|
1322
|
+
|
|
1323
|
+
for "v_consent" in
|
|
1324
|
+
select "consent_row"."value"
|
|
1325
|
+
from jsonb_array_elements("v_consents") as "consent_row"("value")
|
|
1326
|
+
loop
|
|
1327
|
+
"v_channel" := "v_consent" ->> 'channel';
|
|
1328
|
+
"v_status" := "v_consent" ->> 'status';
|
|
1329
|
+
"v_disclosure" := nullif(btrim(
|
|
1330
|
+
"v_event"."form_configuration_snapshot"
|
|
1331
|
+
-> "v_channel" ->> 'disclosure'
|
|
1332
|
+
), '');
|
|
1333
|
+
|
|
1334
|
+
insert into "public"."facebook_ads_lead_consent" (
|
|
1335
|
+
"company_id", "lead_event_id", "channel", "status",
|
|
1336
|
+
"captured_at", "disclosure_text", "response_value", "evidence"
|
|
1337
|
+
) values (
|
|
1338
|
+
"v_event"."company_id",
|
|
1339
|
+
"v_event"."id",
|
|
1340
|
+
"v_channel",
|
|
1341
|
+
"v_status",
|
|
1342
|
+
("v_consent" ->> 'captured_at')::timestamp with time zone,
|
|
1343
|
+
"v_disclosure",
|
|
1344
|
+
"v_consent" ->> 'response_value',
|
|
1345
|
+
coalesce("v_consent" -> 'evidence', '{}'::jsonb)
|
|
1346
|
+
) returning "id" into "v_inserted_consent_id";
|
|
1347
|
+
|
|
1348
|
+
if "v_channel" = 'voice' and "v_status" = 'granted' then
|
|
1349
|
+
"v_voice_consent_id" := "v_inserted_consent_id";
|
|
1350
|
+
end if;
|
|
1351
|
+
end loop;
|
|
1352
|
+
|
|
1353
|
+
update "public"."facebook_ads_lead_event"
|
|
1354
|
+
set
|
|
1355
|
+
"source_id" = "v_settings"."source_id",
|
|
1356
|
+
"client_id" = "v_client_id",
|
|
1357
|
+
"lead_payload" = coalesce("p" -> 'lead_payload', '{}'::jsonb),
|
|
1358
|
+
"meta_ad_id" = nullif("p" ->> 'meta_ad_id', ''),
|
|
1359
|
+
"meta_adset_id" = nullif("p" ->> 'meta_adset_id', ''),
|
|
1360
|
+
"meta_campaign_id" = nullif("p" ->> 'meta_campaign_id', ''),
|
|
1361
|
+
"fetched_at" = now(),
|
|
1362
|
+
"processed_at" = now(),
|
|
1363
|
+
"status" = 'processed',
|
|
1364
|
+
"skip_reason" = null,
|
|
1365
|
+
"last_error" = null
|
|
1366
|
+
where "id" = "v_event"."id";
|
|
1367
|
+
|
|
1368
|
+
-- Voice is stricter than record creation: no explicit, attested voice
|
|
1369
|
+
-- grant means no call-attempt row exists for a worker to accidentally
|
|
1370
|
+
-- claim. Settings and current number/user linkage are revalidated here.
|
|
1371
|
+
if "v_voice_granted"
|
|
1372
|
+
and "v_voice_consent_id" is not null
|
|
1373
|
+
and "v_phone" is not null
|
|
1374
|
+
and "v_settings"."lead_auto_dial_enabled"
|
|
1375
|
+
and "v_settings"."lead_auto_dial_phone_number_id" is not null
|
|
1376
|
+
and "v_settings"."lead_auto_dial_user_id" is not null
|
|
1377
|
+
then
|
|
1378
|
+
select "phone"."phone_number", "company"."time_zone"
|
|
1379
|
+
into "v_from_phone", "v_company_timezone"
|
|
1380
|
+
from "public"."phone_number" as "phone"
|
|
1381
|
+
inner join "public"."company" as "company"
|
|
1382
|
+
on "company"."id" = "phone"."company_id"
|
|
1383
|
+
inner join "public"."user" as "user_row"
|
|
1384
|
+
on "user_row"."id" = "v_settings"."lead_auto_dial_user_id"
|
|
1385
|
+
and "user_row"."company_id" = "phone"."company_id"
|
|
1386
|
+
and "user_row"."is_current_employee" = true
|
|
1387
|
+
inner join "public"."phone_number_user_link" as "link"
|
|
1388
|
+
on "link"."company_id" = "phone"."company_id"
|
|
1389
|
+
and "link"."phone_number_id" = "phone"."id"
|
|
1390
|
+
and "link"."user_id" = "user_row"."id"
|
|
1391
|
+
where "phone"."id" = "v_settings"."lead_auto_dial_phone_number_id"
|
|
1392
|
+
and "phone"."company_id" = "v_event"."company_id"
|
|
1393
|
+
and "phone"."twilio" = true
|
|
1394
|
+
and "phone"."client_id" is null
|
|
1395
|
+
and "phone"."phone_sid" is not null;
|
|
1396
|
+
|
|
1397
|
+
if found then
|
|
1398
|
+
insert into "public"."facebook_ads_lead_call_attempt" (
|
|
1399
|
+
"company_id", "lead_event_id", "client_id", "consent_id",
|
|
1400
|
+
"from_phone_number_id", "target_user_id",
|
|
1401
|
+
"from_phone_number", "destination_phone_number",
|
|
1402
|
+
"routing_snapshot", "next_attempt_at"
|
|
1403
|
+
) values (
|
|
1404
|
+
"v_event"."company_id",
|
|
1405
|
+
"v_event"."id",
|
|
1406
|
+
"v_client_id",
|
|
1407
|
+
"v_voice_consent_id",
|
|
1408
|
+
"v_settings"."lead_auto_dial_phone_number_id",
|
|
1409
|
+
"v_settings"."lead_auto_dial_user_id",
|
|
1410
|
+
"v_from_phone",
|
|
1411
|
+
"v_phone",
|
|
1412
|
+
jsonb_build_object(
|
|
1413
|
+
'time_zone', "v_company_timezone",
|
|
1414
|
+
'window_start', "v_settings"."lead_auto_dial_window_start",
|
|
1415
|
+
'window_end', "v_settings"."lead_auto_dial_window_end",
|
|
1416
|
+
'window_days', "v_settings"."lead_auto_dial_window_days",
|
|
1417
|
+
'delay_seconds', "v_settings"."lead_auto_dial_delay_seconds"
|
|
1418
|
+
),
|
|
1419
|
+
now() + make_interval(
|
|
1420
|
+
secs => "v_settings"."lead_auto_dial_delay_seconds"
|
|
1421
|
+
)
|
|
1422
|
+
) returning "id" into "v_call_attempt_id";
|
|
1423
|
+
end if;
|
|
1424
|
+
end if;
|
|
1425
|
+
|
|
1426
|
+
return query select "v_event"."id", "v_client_id", true, "v_call_attempt_id";
|
|
1427
|
+
end;
|
|
1428
|
+
$$;
|
|
1429
|
+
|
|
1430
|
+
alter function "public"."finalize_facebook_ads_lead_event"(jsonb)
|
|
1431
|
+
owner to "postgres";
|
|
1432
|
+
|
|
1433
|
+
-- ============================================================
|
|
1434
|
+
-- Call claim and state-transition RPCs
|
|
1435
|
+
-- ============================================================
|
|
1436
|
+
|
|
1437
|
+
create or replace function "public"."claim_facebook_ads_lead_call_attempts"(
|
|
1438
|
+
"batch_size" integer
|
|
1439
|
+
)
|
|
1440
|
+
returns setof "public"."facebook_ads_lead_call_attempt"
|
|
1441
|
+
language sql
|
|
1442
|
+
security definer
|
|
1443
|
+
set search_path = ''
|
|
1444
|
+
as $$
|
|
1445
|
+
update "public"."facebook_ads_lead_call_attempt"
|
|
1446
|
+
set
|
|
1447
|
+
"status" = 'processing',
|
|
1448
|
+
"held_reason" = null,
|
|
1449
|
+
"attempt_count" = "attempt_count" + 1,
|
|
1450
|
+
"last_attempt_at" = now(),
|
|
1451
|
+
"started_at" = coalesce("started_at", now())
|
|
1452
|
+
where "id" in (
|
|
1453
|
+
select "id"
|
|
1454
|
+
from "public"."facebook_ads_lead_call_attempt"
|
|
1455
|
+
where "status" = 'pending' and "next_attempt_at" <= now()
|
|
1456
|
+
order by "next_attempt_at", "created_at"
|
|
1457
|
+
limit least(greatest(coalesce("batch_size", 0), 0), 100)
|
|
1458
|
+
for update skip locked
|
|
1459
|
+
)
|
|
1460
|
+
returning *;
|
|
1461
|
+
$$;
|
|
1462
|
+
|
|
1463
|
+
alter function "public"."claim_facebook_ads_lead_call_attempts"(integer)
|
|
1464
|
+
owner to "postgres";
|
|
1465
|
+
|
|
1466
|
+
create or replace function "public"."update_facebook_ads_lead_call_state"(
|
|
1467
|
+
"p" jsonb
|
|
1468
|
+
)
|
|
1469
|
+
returns boolean
|
|
1470
|
+
language plpgsql
|
|
1471
|
+
security definer
|
|
1472
|
+
set search_path = ''
|
|
1473
|
+
as $$
|
|
1474
|
+
declare
|
|
1475
|
+
"v_company_id" uuid := ("p" ->> 'company_id')::uuid;
|
|
1476
|
+
"v_attempt_id" uuid := ("p" ->> 'attempt_id')::uuid;
|
|
1477
|
+
"v_new_status" text := "p" ->> 'status';
|
|
1478
|
+
"v_expected_status" text := "p" ->> 'expected_status';
|
|
1479
|
+
"v_attempt" "public"."facebook_ads_lead_call_attempt"%rowtype;
|
|
1480
|
+
"v_old_rank" integer;
|
|
1481
|
+
"v_new_rank" integer;
|
|
1482
|
+
"v_terminal" boolean;
|
|
1483
|
+
"v_answered_by" uuid := ("p" ->> 'answered_by_user_id')::uuid;
|
|
1484
|
+
"v_next_attempt_at" timestamp with time zone :=
|
|
1485
|
+
("p" ->> 'next_attempt_at')::timestamp with time zone;
|
|
1486
|
+
"v_expected_updated_at" timestamp with time zone :=
|
|
1487
|
+
("p" ->> 'expected_updated_at')::timestamp with time zone;
|
|
1488
|
+
"v_is_retry_hold" boolean;
|
|
1489
|
+
begin
|
|
1490
|
+
if jsonb_typeof("p") <> 'object'
|
|
1491
|
+
or "v_company_id" is null
|
|
1492
|
+
or "v_attempt_id" is null
|
|
1493
|
+
or "v_new_status" is null
|
|
1494
|
+
or ("p" ? 'next_attempt_at' and "v_next_attempt_at" is null)
|
|
1495
|
+
or (
|
|
1496
|
+
"p" ? 'expected_updated_at'
|
|
1497
|
+
and "v_expected_updated_at" is null
|
|
1498
|
+
)
|
|
1499
|
+
or (
|
|
1500
|
+
"p" ? 'expected_status'
|
|
1501
|
+
and (
|
|
1502
|
+
"v_expected_status" is null
|
|
1503
|
+
or "v_expected_status" not in (
|
|
1504
|
+
'pending', 'processing', 'agent_ringing', 'lead_ringing',
|
|
1505
|
+
'connected', 'completed', 'skipped', 'failed', 'dead'
|
|
1506
|
+
)
|
|
1507
|
+
)
|
|
1508
|
+
)
|
|
1509
|
+
or "v_new_status" not in (
|
|
1510
|
+
'pending', 'processing', 'agent_ringing', 'lead_ringing',
|
|
1511
|
+
'connected', 'completed', 'skipped', 'failed', 'dead'
|
|
1512
|
+
)
|
|
1513
|
+
then
|
|
1514
|
+
raise exception 'Valid company, attempt, and call status are required'
|
|
1515
|
+
using errcode = '22023';
|
|
1516
|
+
end if;
|
|
1517
|
+
|
|
1518
|
+
select * into "v_attempt"
|
|
1519
|
+
from "public"."facebook_ads_lead_call_attempt"
|
|
1520
|
+
where "id" = "v_attempt_id" and "company_id" = "v_company_id"
|
|
1521
|
+
for update;
|
|
1522
|
+
|
|
1523
|
+
if not found then
|
|
1524
|
+
return false;
|
|
1525
|
+
end if;
|
|
1526
|
+
|
|
1527
|
+
-- Optional compare-and-set guards let sweepers inspect provider state,
|
|
1528
|
+
-- then safely dead-letter only if no callback advanced the row while the
|
|
1529
|
+
-- provider request was in flight. Existing callers can omit both keys.
|
|
1530
|
+
if ("p" ? 'expected_status'
|
|
1531
|
+
and "v_attempt"."status" <> "v_expected_status")
|
|
1532
|
+
or ("p" ? 'expected_updated_at'
|
|
1533
|
+
and "v_attempt"."updated_at" is distinct from "v_expected_updated_at")
|
|
1534
|
+
then
|
|
1535
|
+
return false;
|
|
1536
|
+
end if;
|
|
1537
|
+
|
|
1538
|
+
"v_terminal" := "v_attempt"."status" in (
|
|
1539
|
+
'completed', 'skipped', 'failed', 'dead'
|
|
1540
|
+
);
|
|
1541
|
+
"v_old_rank" := case "v_attempt"."status"
|
|
1542
|
+
when 'pending' then 0
|
|
1543
|
+
when 'processing' then 1
|
|
1544
|
+
when 'agent_ringing' then 2
|
|
1545
|
+
when 'lead_ringing' then 3
|
|
1546
|
+
when 'connected' then 4
|
|
1547
|
+
else 5
|
|
1548
|
+
end;
|
|
1549
|
+
"v_new_rank" := case "v_new_status"
|
|
1550
|
+
when 'pending' then 0
|
|
1551
|
+
when 'processing' then 1
|
|
1552
|
+
when 'agent_ringing' then 2
|
|
1553
|
+
when 'lead_ringing' then 3
|
|
1554
|
+
when 'connected' then 4
|
|
1555
|
+
else 5
|
|
1556
|
+
end;
|
|
1557
|
+
-- A hold is the sole permitted backward transition. Before provider work
|
|
1558
|
+
-- it releases a processing claim; after the agent answers it lets a
|
|
1559
|
+
-- transient entitlement/configuration check defer safely once the web
|
|
1560
|
+
-- worker has ended the current conference. CAS guards remain available so
|
|
1561
|
+
-- a concurrent callback always wins.
|
|
1562
|
+
"v_is_retry_hold" := "v_attempt"."status" in ('processing', 'agent_ringing')
|
|
1563
|
+
and "v_new_status" = 'pending'
|
|
1564
|
+
and "v_next_attempt_at" is not null
|
|
1565
|
+
and "v_next_attempt_at" > now()
|
|
1566
|
+
and nullif(btrim("p" ->> 'held_reason'), '') is not null;
|
|
1567
|
+
|
|
1568
|
+
if ("v_terminal" and "v_new_status" <> "v_attempt"."status")
|
|
1569
|
+
or (
|
|
1570
|
+
not "v_terminal"
|
|
1571
|
+
and "v_new_rank" < "v_old_rank"
|
|
1572
|
+
and not "v_is_retry_hold"
|
|
1573
|
+
)
|
|
1574
|
+
then
|
|
1575
|
+
raise exception 'Call state cannot transition backward or leave a terminal state'
|
|
1576
|
+
using errcode = '23514';
|
|
1577
|
+
end if;
|
|
1578
|
+
|
|
1579
|
+
if "v_answered_by" is not null and not exists (
|
|
1580
|
+
select 1 from "public"."user"
|
|
1581
|
+
where "id" = "v_answered_by" and "company_id" = "v_company_id"
|
|
1582
|
+
) then
|
|
1583
|
+
raise exception 'Answered-by user does not belong to this company'
|
|
1584
|
+
using errcode = '23503';
|
|
1585
|
+
end if;
|
|
1586
|
+
|
|
1587
|
+
update "public"."facebook_ads_lead_call_attempt"
|
|
1588
|
+
set
|
|
1589
|
+
"status" = "v_new_status",
|
|
1590
|
+
"held_reason" = case
|
|
1591
|
+
when "p" ? 'held_reason' then "p" ->> 'held_reason'
|
|
1592
|
+
when "v_new_status" <> 'pending' then null
|
|
1593
|
+
else "v_attempt"."held_reason" end,
|
|
1594
|
+
"next_attempt_at" = case
|
|
1595
|
+
when "p" ? 'next_attempt_at' then "v_next_attempt_at"
|
|
1596
|
+
else "v_attempt"."next_attempt_at" end,
|
|
1597
|
+
"twilio_agent_call_sid" = case
|
|
1598
|
+
when "p" ? 'twilio_agent_call_sid'
|
|
1599
|
+
then nullif("p" ->> 'twilio_agent_call_sid', '')
|
|
1600
|
+
else "v_attempt"."twilio_agent_call_sid" end,
|
|
1601
|
+
"twilio_lead_call_sid" = case
|
|
1602
|
+
when "p" ? 'twilio_lead_call_sid'
|
|
1603
|
+
then nullif("p" ->> 'twilio_lead_call_sid', '')
|
|
1604
|
+
else "v_attempt"."twilio_lead_call_sid" end,
|
|
1605
|
+
"twilio_conference_sid" = case
|
|
1606
|
+
when "p" ? 'twilio_conference_sid'
|
|
1607
|
+
then nullif("p" ->> 'twilio_conference_sid', '')
|
|
1608
|
+
else "v_attempt"."twilio_conference_sid" end,
|
|
1609
|
+
"answered_by_user_id" = case
|
|
1610
|
+
when "p" ? 'answered_by_user_id' then "v_answered_by"
|
|
1611
|
+
else "v_attempt"."answered_by_user_id" end,
|
|
1612
|
+
"started_at" = case
|
|
1613
|
+
when "p" ? 'started_at'
|
|
1614
|
+
then ("p" ->> 'started_at')::timestamp with time zone
|
|
1615
|
+
else "v_attempt"."started_at" end,
|
|
1616
|
+
"connected_at" = case
|
|
1617
|
+
when "p" ? 'connected_at'
|
|
1618
|
+
then ("p" ->> 'connected_at')::timestamp with time zone
|
|
1619
|
+
when "v_new_status" = 'connected'
|
|
1620
|
+
then coalesce("v_attempt"."connected_at", now())
|
|
1621
|
+
else "v_attempt"."connected_at" end,
|
|
1622
|
+
"completed_at" = case
|
|
1623
|
+
when "p" ? 'completed_at'
|
|
1624
|
+
then ("p" ->> 'completed_at')::timestamp with time zone
|
|
1625
|
+
when "v_new_status" in ('completed', 'skipped', 'failed', 'dead')
|
|
1626
|
+
then coalesce("v_attempt"."completed_at", now())
|
|
1627
|
+
else "v_attempt"."completed_at" end,
|
|
1628
|
+
"skip_reason" = case
|
|
1629
|
+
when "p" ? 'skip_reason' then "p" ->> 'skip_reason'
|
|
1630
|
+
else "v_attempt"."skip_reason" end,
|
|
1631
|
+
"last_error" = case
|
|
1632
|
+
when "p" ? 'last_error' then "p" ->> 'last_error'
|
|
1633
|
+
else "v_attempt"."last_error" end
|
|
1634
|
+
where "id" = "v_attempt_id";
|
|
1635
|
+
|
|
1636
|
+
return true;
|
|
1637
|
+
end;
|
|
1638
|
+
$$;
|
|
1639
|
+
|
|
1640
|
+
alter function "public"."update_facebook_ads_lead_call_state"(jsonb)
|
|
1641
|
+
owner to "postgres";
|
|
1642
|
+
|
|
1643
|
+
-- ============================================================
|
|
1644
|
+
-- Function permissions (tables are handled in the RLS migration)
|
|
1645
|
+
-- ============================================================
|
|
1646
|
+
|
|
1647
|
+
revoke all on function "public"."facebook_ads_text_array_is_normalized"(text[])
|
|
1648
|
+
from public;
|
|
1649
|
+
revoke all on function "public"."facebook_ads_text_array_is_normalized"(text[])
|
|
1650
|
+
from "anon";
|
|
1651
|
+
revoke all on function "public"."facebook_ads_text_array_is_normalized"(text[])
|
|
1652
|
+
from "authenticated";
|
|
1653
|
+
grant execute on function "public"."facebook_ads_text_array_is_normalized"(text[])
|
|
1654
|
+
to "service_role";
|
|
1655
|
+
|
|
1656
|
+
revoke all on function "public"."facebook_ads_call_days_are_normalized"(smallint[])
|
|
1657
|
+
from public;
|
|
1658
|
+
revoke all on function "public"."facebook_ads_call_days_are_normalized"(smallint[])
|
|
1659
|
+
from "anon";
|
|
1660
|
+
revoke all on function "public"."facebook_ads_call_days_are_normalized"(smallint[])
|
|
1661
|
+
from "authenticated";
|
|
1662
|
+
grant execute on function "public"."facebook_ads_call_days_are_normalized"(smallint[])
|
|
1663
|
+
to "service_role";
|
|
1664
|
+
|
|
1665
|
+
revoke all on function "public"."set_facebook_ads_lead_settings"(jsonb) from public;
|
|
1666
|
+
revoke all on function "public"."set_facebook_ads_lead_settings"(jsonb) from "anon";
|
|
1667
|
+
revoke all on function "public"."set_facebook_ads_lead_settings"(jsonb) from "authenticated";
|
|
1668
|
+
grant execute on function "public"."set_facebook_ads_lead_settings"(jsonb) to "service_role";
|
|
1669
|
+
|
|
1670
|
+
revoke all on function "public"."enqueue_facebook_ads_lead_event"(jsonb) from public;
|
|
1671
|
+
revoke all on function "public"."enqueue_facebook_ads_lead_event"(jsonb) from "anon";
|
|
1672
|
+
revoke all on function "public"."enqueue_facebook_ads_lead_event"(jsonb) from "authenticated";
|
|
1673
|
+
grant execute on function "public"."enqueue_facebook_ads_lead_event"(jsonb) to "service_role";
|
|
1674
|
+
|
|
1675
|
+
revoke all on function "public"."claim_facebook_ads_lead_events"(integer) from public;
|
|
1676
|
+
revoke all on function "public"."claim_facebook_ads_lead_events"(integer) from "anon";
|
|
1677
|
+
revoke all on function "public"."claim_facebook_ads_lead_events"(integer) from "authenticated";
|
|
1678
|
+
grant execute on function "public"."claim_facebook_ads_lead_events"(integer) to "service_role";
|
|
1679
|
+
|
|
1680
|
+
revoke all on function "public"."replay_facebook_ads_lead_event"(uuid, uuid) from public;
|
|
1681
|
+
revoke all on function "public"."replay_facebook_ads_lead_event"(uuid, uuid) from "anon";
|
|
1682
|
+
revoke all on function "public"."replay_facebook_ads_lead_event"(uuid, uuid) from "authenticated";
|
|
1683
|
+
grant execute on function "public"."replay_facebook_ads_lead_event"(uuid, uuid) to "service_role";
|
|
1684
|
+
|
|
1685
|
+
revoke all on function "public"."promote_facebook_ads_activation_gap_events"(uuid, uuid) from public;
|
|
1686
|
+
revoke all on function "public"."promote_facebook_ads_activation_gap_events"(uuid, uuid) from "anon";
|
|
1687
|
+
revoke all on function "public"."promote_facebook_ads_activation_gap_events"(uuid, uuid) from "authenticated";
|
|
1688
|
+
grant execute on function "public"."promote_facebook_ads_activation_gap_events"(uuid, uuid) to "service_role";
|
|
1689
|
+
|
|
1690
|
+
revoke all on function "public"."finalize_facebook_ads_lead_event"(jsonb) from public;
|
|
1691
|
+
revoke all on function "public"."finalize_facebook_ads_lead_event"(jsonb) from "anon";
|
|
1692
|
+
revoke all on function "public"."finalize_facebook_ads_lead_event"(jsonb) from "authenticated";
|
|
1693
|
+
grant execute on function "public"."finalize_facebook_ads_lead_event"(jsonb) to "service_role";
|
|
1694
|
+
|
|
1695
|
+
revoke all on function "public"."claim_facebook_ads_lead_call_attempts"(integer)
|
|
1696
|
+
from public;
|
|
1697
|
+
revoke all on function "public"."claim_facebook_ads_lead_call_attempts"(integer)
|
|
1698
|
+
from "anon";
|
|
1699
|
+
revoke all on function "public"."claim_facebook_ads_lead_call_attempts"(integer)
|
|
1700
|
+
from "authenticated";
|
|
1701
|
+
grant execute on function "public"."claim_facebook_ads_lead_call_attempts"(integer)
|
|
1702
|
+
to "service_role";
|
|
1703
|
+
|
|
1704
|
+
revoke all on function "public"."update_facebook_ads_lead_call_state"(jsonb)
|
|
1705
|
+
from public;
|
|
1706
|
+
revoke all on function "public"."update_facebook_ads_lead_call_state"(jsonb)
|
|
1707
|
+
from "anon";
|
|
1708
|
+
revoke all on function "public"."update_facebook_ads_lead_call_state"(jsonb)
|
|
1709
|
+
from "authenticated";
|
|
1710
|
+
grant execute on function "public"."update_facebook_ads_lead_call_state"(jsonb)
|
|
1711
|
+
to "service_role";
|