Looker & Tableau Dashboards
Built a suite of interactive dashboards giving leadership real-time visibility into sales performance, payment collection, call activity, manager productivity, and marketing ROI — all powered by BigQuery as the single source of truth.
Sales & Event Performance Dashboard
Tracks total sales volume, received payments, leftover balances, seat utilization, revenue per seat, and payment status breakdown across three upcoming events (Miami, Los Angeles, New York). Includes daily sales/received trends, monthly comparisons, and package distribution analysis.
Financial Inflow Summary
Comprehensive payment tracking dashboard with upcoming payment schedules by event and month, daily payment forecast, client-level outstanding balances with installment plan visibility, received payment log, and overdue payment tracking ($10.7K total overdue).
Call Activity Report
Real-time call monitoring with geographic distribution by state, weekly/daily activity trends, per-manager performance breakdown (call duration buckets, missed calls, total time), and a detailed activity log with contact names, outcomes, stages, and call durations. Auto-refreshes every 15 minutes.
Manager Stats & Marketing Analytics
Two dashboards tracking per-manager sales performance (average sale amount, monthly trends, sales count by package type, client list with payment status) and end-to-end marketing funnel (6,641 leads generated at $6.39/lead, 5.16% conversion, ad set performance ranking, daily spend vs. lead generation trends).
Bitrix24 → BigQuery Pipeline Suite
Production-grade cloud pipelines extracting CRM, HR, financial, and operational data from Bitrix24 into BigQuery — with type coercion, quarantine tables, incremental sync, and Terraform-managed infrastructure.
CRM Deals Sync
Full & incremental sync of CRM deals with atomic staging-to-production swaps. State tracking prevents partial data overwrites.
crm.deal.list → dealsCall History Sync
Hourly incremental extraction of VoIP call records from Voximplant API. Powers the real-time call activity dashboard.
voximplant.statistic.get → callsPayment Schedule Sync
Syncs Smart Process payment schedule data, enabling the financial inflow dashboard with upcoming/overdue tracking.
crm.item.list → payment_scheduleCRM Change Tracker
Webhook-driven, append-only change log. Stores entity snapshots and writes field-level diffs — fully idempotent via dedup keys.
webhooks → crm_change_logModel Measurements
Extracts height, chest, waist, hips from CRM contacts/deals via dynamic field discovery. Normalizes messy input formats.
crm.contact.fields → model_measurementsHR Users Sync
Incremental employee directory sync with ARRAY<INT64> handling for department fields and custom UF_ field support.
user.get → hr_usersBigQuery Views & Analytical Queries
Production SQL views in BigQuery that transform raw CRM extracts into clean, analytics-ready data layers — spanning two projects: an event production CRM (Young Fashion Show) and a cleaning services booking system (Wow Now Cleaning).
Canonical Deal Fact Table
Central view that joins raw deals with 6 dimension tables (users, contacts, CRM statuses, userfield enums, payment schedule) to produce one clean row per deal with resolved names, normalized UTM sources, multi-value language fields, and the first payment date.
Key Techniques
- Resolves coded IDs to display names via LEFT JOINs to
crm_status,users,contacts, anduserfield_enums(3 separate enum lookups for state, location, and service package) - Normalizes inconsistent UTM sources (
ig/instagram,fb/facebook) and detects ad language from UTM medium patterns - Unnests multi-value bracket-encoded fields (
[760, 762]) with correlated subquery +STRING_AGGfor client language resolution - Computes first payment date via subquery aggregation on
payment_schedulelinked byparentId2orxmlId - All timestamps converted from UTC/Bitrix format to
America/New_YorkusingPARSE_TIMESTAMP+ timezone cast
SELECT
CAST(d.ID AS INT64) AS ID,
DATE(PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%S%Ez', d.DATE_CREATE),
'America/New_York') AS create_date,
-- Stage group: semantic stage (S/P/F)
CASE
WHEN d.STAGE_SEMANTIC_ID = "S" THEN "Successful"
WHEN d.STAGE_SEMANTIC_ID = "P" THEN "Progress"
WHEN d.STAGE_SEMANTIC_ID = "F" THEN "Failure"
END AS stage_group,
COALESCE(stage_status.name, 'Unknown / Other') AS stage,
COALESCE(assignee.full_name, CONCAT('ID: ', d.ASSIGNED_BY_ID))
AS responsible_person,
-- Normalize traffic source
CASE
WHEN d.UTM_SOURCE IN ('instagram','ig') THEN 'instagram'
WHEN d.UTM_SOURCE IN ('facebook','fb') THEN 'facebook'
WHEN d.UTM_SOURCE = 'telegram' THEN 'telegram'
ELSE 'unknown'
END AS source,
-- Multi-value language field: [760, 762] → "English, Ukrainian"
COALESCE(
(SELECT STRING_AGG(le.option_value, ', ')
FROM UNNEST(SPLIT(
REGEXP_REPLACE(d.UF_CRM_1744805668224, r"[\[\]\s']",''),','
)) AS lang_id
JOIN userfield_enums le
ON le.field_id = 'UF_CRM_1744805668224'
AND TRIM(lang_id) = le.option_id),
d.UF_CRM_1744805668224
) AS language,
-- First payment date (earliest from payment_schedule)
DATE(SAFE.PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%S%Ez',
ps.first_payment_time), 'America/New_York')
AS first_payment_date
FROM deals d
LEFT JOIN users assignee ON d.ASSIGNED_BY_ID = assignee.id
LEFT JOIN contacts ON CAST(d.CONTACT_ID AS STRING) = contacts.id
LEFT JOIN crm_status ON d.STAGE_ID = stage_status.status_id
LEFT JOIN userfield_enums ON ... -- state, location, service_package
LEFT JOIN (SELECT COALESCE(parentId2, xmlId) AS deal_id,
MIN(createdTime) AS first_payment_time
FROM payment_schedule GROUP BY 1) ps
ON CAST(d.ID AS STRING) = CAST(ps.deal_id AS STRING)
Call Analytics
Joins raw VoIP call records with users, contacts, and deal context to produce a fully enriched call activity view — with deduplication, duration bucketing, missed-call detection, and geographic attribution from linked deals.
Key Techniques
- Deduplicates calls, users, and contacts with
QUALIFY ROW_NUMBER() OVER (PARTITION BY ...)to prevent join multiplication from incremental syncs - Polymorphic entity resolution: calls link to either CONTACT or DEAL via
CRM_ENTITY_TYPE— a UNION ALL CTE maps both entity types to deal attributes (location, state, stage) - Handles mixed timestamp formats (ISO string vs Unix micros) with
CASE+SAFE_CAST+TIMESTAMP_MICROS - Computes call outcome (
Missed/No answer/Success), duration buckets (0-2 min,2-5 min,5+ min), and threshold flags for conversion analysis
WITH
-- Deduplicate calls from incremental sync
calls_dedup AS (
SELECT *
FROM bitrix24.calls
QUALIFY ROW_NUMBER() OVER (
PARTITION BY COALESCE(
NULLIF(TRIM(CALL_ID), ''),
CONCAT(CALL_START_DATE,'|',PORTAL_USER_ID,'|',
CRM_ENTITY_ID,'|',PHONE_NUMBER))
ORDER BY CREATED_AT DESC NULLS LAST
) = 1
),
-- Map both CONTACT and DEAL entity types to deal attributes
deals_for_calls AS (
SELECT 'DEAL' AS entity_type, CAST(d.ID AS STRING) AS entity_id,
dv.location_event, dv.state, dv.stage
FROM deals d LEFT JOIN deals_view dv ON d.ID = dv.ID
UNION ALL
SELECT 'CONTACT', CAST(d.CONTACT_ID AS STRING),
dv.location_event, dv.state, dv.stage
FROM deals d LEFT JOIN deals_view dv ON d.ID = dv.ID
WHERE d.CONTACT_ID IS NOT NULL
QUALIFY ROW_NUMBER() OVER (
PARTITION BY d.CONTACT_ID ORDER BY d.DATE_MODIFY DESC
) = 1
)
SELECT
-- Mixed timestamp formats → normalized datetime
DATETIME(
CASE
WHEN CALL_START_DATE LIKE '%-%'
THEN SAFE_CAST(CALL_START_DATE AS TIMESTAMP)
ELSE SAFE.TIMESTAMP_MICROS(
DIV(SAFE_CAST(CALL_START_DATE AS INT64), 1000))
END, 'America/New_York') AS timestamp,
COALESCE(u.full_name, CONCAT('ID: ', PORTAL_USER_ID)) AS manager,
-- Call classification
CASE WHEN call_type = 2 AND duration = 0 THEN 'Missed'
WHEN duration < 10 THEN 'No answer'
ELSE 'Success' END AS call_outcome,
duration > 120 AS call_longer_2min,
duration > 300 AS call_longer_5min,
deals.state, deals.location_event
FROM calls_dedup
LEFT JOIN users_dedup u ON PORTAL_USER_ID = u.id
LEFT JOIN deals_for_calls ON CRM_ENTITY_TYPE = entity_type
AND CRM_ENTITY_ID = entity_id
Cleaning Schedule Sync
A 240-line, 10-CTE production view that transforms raw Bitrix24 CRM booking data into a clean, denormalized table powering an automated Google Calendar sync. Parses nested JSON time slots, resolves cleaner IDs to names and emails, handles multiple date formats, and deduplicates across incremental loads — producing one row per (deal, time slot) for a cleaning services company.
Key Techniques
- Parses nested JSON arrays (
booking_time_json) with three date format fallbacks using chainedCOALESCE(SAFE.PARSE_TIMESTAMP(...))— handlesYYYY-MM-DD,MM/DD/YYYY, andDD/MM/YYYY - Polymorphic cleaner JSON: detects whether
assigned_cleaner_jsonis an array or single object usingJSON_TYPE(SAFE.PARSE_JSON(...)), then normalizes to array before unnesting - Two-pass name resolution: extracts cleaner IDs from JSON, resolves to full names via
hr_usersLEFT JOIN — falls back to raw JSON text when HR record is missing - Address parsing: extracts from
{VALUE},{ADDRESS}, or plain string JSON, then splits on|delimiter to get the first address component - Multi-level deduplication:
ROW_NUMBER()for latest booking per deal,GROUP BYfor unique time slots, finalANY_VALUEaggregation to collapse join fan-out - Smart booking end time: if
hours_totalis set, computesbooking_to = booking_from + hours_totalviaTIMESTAMP_ADD; otherwise falls back to Bitrix raw end time
WITH latest_drb AS (
-- Latest booking state per deal
SELECT * EXCEPT(rn) FROM (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY deal_id ORDER BY ingested_at DESC) AS rn
FROM deal_resourcebookings
WHERE booking_time_json IS NOT NULL
AND booking_time_json != '[]'
) WHERE rn = 1
),
-- Parse nested JSON time slots with 3 date format fallbacks
time_slots_raw AS (
SELECT drb.deal_id, drb.hours_total,
drb.cleaners_instruction, drb.client_name,
slot.from_ts, slot.to_ts
FROM latest_drb drb
CROSS JOIN UNNEST(ARRAY(
SELECT AS STRUCT
COALESCE(
SAFE.PARSE_TIMESTAMP('%Y-%m-%d %H:%M:%S',
JSON_VALUE(el, '$.from'), 'America/New_York'),
SAFE.PARSE_TIMESTAMP('%m/%d/%Y %H:%M:%S',
JSON_VALUE(el, '$.from'), 'America/New_York'),
SAFE.PARSE_TIMESTAMP('%d/%m/%Y %H:%M:%S',
JSON_VALUE(el, '$.from'), 'America/New_York')
) AS from_ts,
-- (same pattern for $.to)
...
FROM UNNEST(JSON_EXTRACT_ARRAY(
drb.booking_time_json)) AS el
)) AS slot
),
-- Polymorphic cleaner JSON → unnested IDs
cleaner_unnested AS (
SELECT pa.*, cu.vid, cu.name_raw
FROM parsed_address pa
JOIN time_slots ts USING (deal_id, booking_from)
CROSS JOIN UNNEST(
CASE
WHEN JSON_TYPE(SAFE.PARSE_JSON(
ts.assigned_cleaner_json)) = 'array'
THEN JSON_EXTRACT_ARRAY(ts.assigned_cleaner_json)
WHEN JSON_TYPE(...) = 'object'
THEN JSON_EXTRACT_ARRAY(
CONCAT('[', ts.assigned_cleaner_json, ']'))
ELSE []
END
) AS el
),
-- Resolve IDs → names via hr_users
cleaner_with_names AS (
SELECT cu.*,
COALESCE(
NULLIF(TRIM(CONCAT(
COALESCE(h.NAME,''),' ',COALESCE(h.LAST_NAME,'')
)), ''),
cu.name_raw
) AS cleaner_name
FROM cleaner_unnested cu
LEFT JOIN hr_users h ON h.ID = cu.vid
)
-- Final output: one row per (deal, time slot)
SELECT deal_id, booking_from,
-- Smart end time: hours_total or Bitrix raw
CASE WHEN hours_total IS NOT NULL
THEN TIMESTAMP_ADD(booking_from,
INTERVAL CAST(hours_total * 3600 AS INT64) SECOND)
ELSE booking_to_old
END AS booking_to,
ANY_VALUE(all_cleaners) AS all_cleaners,
ANY_VALUE(all_emails) AS all_emails,
ANY_VALUE(customer_name) AS customer_name,
ANY_VALUE(CASE stage_id
WHEN 'C1:EXECUTING' THEN 'Cleaning'
WHEN 'FINAL_INVOICE' THEN 'Updates'
WHEN 'WON' THEN 'Close deal'
END) AS stage_name
FROM joined GROUP BY deal_id, booking_from, booking_to_old
Multi-Source Integrations
Additional pipelines and automations connecting QuickBooks, Google Calendar, and Google Sheets to BigQuery — all deployed as Cloud Functions with Terraform.
QuickBooks → BigQuery
Extracts Invoices, Customers, and Purchases with OAuth2 credentials stored in Google Secret Manager. No hardcoded keys.
QuickBooks API → GCS → BigQueryGoogle Calendar Sync
Reads a BigQuery booking report view and creates/updates per-cleaner calendar events with deterministic IDs to prevent duplicates.
booking_report → Calendar APIGoogle Sheets → BigQuery
Automated external table refresh that lets marketing data in Google Sheets be queried live from BigQuery.
Sheets → external tableInfrastructure as Code
All Cloud Functions provisioned with Terraform and shell deploy scripts. Cloud Scheduler, Pub/Sub triggers, repeatable zero-downtime releases.
Terraform + gcloud CLITechnologies Used
The full technology stack behind the dashboards, pipelines, and integrations.