Bỏ qua

SMP KPI & Metrics Definition

Audience: BA, PM, Ops, Data team · Updated: v3.3


1. Mục đích

Tài liệu này định nghĩa chính xác các metrics SMP track ở dashboard. Mỗi metric có: - Definition: nói rõ đo cái gì - Formula: công thức tính (SQL pseudo) - Dimensions: filter/group by gì - Frequency: tính real-time hay batch - Owner: ai quan tâm metric này

Nguyên tắc: nếu BA + Dev + Data đồng ý formula khác nhau → conflict. Phải resolve ở doc này trước khi build dashboard.


2. Business metrics (PM, Founder)

2.1 GMV (Gross Merchandise Value)

Định nghĩa: Tổng giá trị giao dịch thành công, gồm labor + material + VAT (trước bất kỳ discount/refund nào).

Formula:

SELECT SUM(total_amount + discount_amount) AS gmv
FROM orders
WHERE current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

Dimensions: by period, by partner_id, by service_id, by city/district Frequency: Daily snapshot (cron 00:05 hàng ngày) Unit: VND

2.2 NMV (Net Merchandise Value)

Định nghĩa: GMV sau khi trừ discount và refund.

Formula:

SELECT SUM(total_amount) - SUM(refund_amount)
FROM orders
WHERE current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

2.3 SMP Revenue

Định nghĩa: Phần SMP giữ lại (commission từ thợ + SaaS fee + material margin).

Formula:

-- Commission từ order labor
SELECT SUM(order_steps.labor_price * commission_rate)
FROM order_steps
JOIN orders ON ...
WHERE orders.current_stage IN ('09_completed', '10_rated')
 AND orders.created_at BETWEEN <from> AND <to>

UNION ALL

-- Material margin
SELECT SUM(material_variants.sell_price - material_variants.cost_price_avg) * quantity
FROM order_step_materials
WHERE source = 'reserved'
 AND order step is completed

UNION ALL

-- SaaS fee
SELECT SUM(partners.supplier_config.saas_fee_monthly)
FROM partners
WHERE supplier_config.model IN ('saas_only', 'hybrid')
 AND active in period

Note: tính 3 nguồn revenue tách biệt, sau đó cộng để có total SMP revenue.

2.4 Order count

Định nghĩa: Số đơn theo từng status trong period.

Formula:

SELECT current_stage, COUNT(*)
FROM orders
WHERE created_at BETWEEN <from> AND <to>
GROUP BY current_stage;

Dimensions: by source (customer_direct, partner_customer, contract), by city Frequency: Real-time (Redis counter, sync to MySQL hourly)

2.5 Active customers

Định nghĩa: Khách có ≥ 1 đơn hoàn thành trong period.

Formula:

SELECT COUNT(DISTINCT customer_id)
FROM orders
WHERE current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

2.6 Repeat customer rate

Định nghĩa: % khách có ≥ 2 đơn hoàn thành.

Formula:

WITH customer_orders AS (
 SELECT customer_id, COUNT(*) AS cnt
 FROM orders
 WHERE current_stage IN ('09_completed', '10_rated')
 GROUP BY customer_id
)
SELECT 
 SUM(CASE WHEN cnt >= 2 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS repeat_rate_pct
FROM customer_orders;

2.7 AOV (Average Order Value)

Formula:

SELECT AVG(total_amount)
FROM orders
WHERE current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

3. Operational metrics (Ops Manager)

3.1 Order completion rate

Định nghĩa: % đơn hoàn thành (vs tổng tạo).

Formula:

SELECT 
 SUM(CASE WHEN current_stage IN ('09_completed', '10_rated') THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM orders
WHERE created_at BETWEEN <from> AND <to>;

Target: ≥ 90%

3.2 Cancellation rate

Formula:

SELECT 
 SUM(CASE WHEN current_stage = 'cancelled' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM orders
WHERE created_at BETWEEN <from> AND <to>;

Sub-dimension: cancellation reason (customer_cancel, no_agent_available, quote_rejected, payment_failed)

Target: ≤ 5%

3.3 Dispatch SLA

Định nghĩa: % đơn assigned to agent trong N phút sau khi tạo.

Formula:

SELECT 
 SUM(CASE 
 WHEN TIMESTAMPDIFF(MINUTE, created_at, accept_at) <= 5 
 THEN 1 ELSE 0 END
 ) * 100.0 / COUNT(*)
FROM (
 SELECT o.created_at, MIN(log.created_at) AS accept_at
 FROM orders o
 JOIN order_stage_log log ON log.order_id = o.id
 WHERE log.to_stage = '03_survey_accepted'
 GROUP BY o.id
) t;

Target: 95% within 5 phút

3.4 Average time to assign

Formula:

SELECT AVG(TIMESTAMPDIFF(SECOND, created_at, accept_at)) AS avg_seconds
FROM ...

3.5 Average order duration (created → completed)

Formula:

SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, completed_at))
FROM orders
WHERE current_stage IN ('09_completed', '10_rated');

3.6 Stuck orders count (alert)

Định nghĩa: Đơn ở 1 stage quá lâu so với threshold.

Stages + threshold: | Stage | Max time | If exceeded | |---|---|---| | 01_created | 5 min | Dispatch issue | | 02_dispatched_survey | 15 min | No agent accepting | | 03_survey_accepted → 04_arrived_survey | 60 min | Agent late | | 05_surveyed → 06_quote_approved | 24h | Customer not responding | | 07_dispatched_execution | 15 min | No agent accepting | | 08_in_progress | service duration × 2 | Step too long |

SELECT id, current_stage, TIMESTAMPDIFF(MINUTE, last_stage_change, NOW) AS stuck_min
FROM orders
WHERE current_stage NOT IN ('09_completed', '10_rated', 'cancelled')
 AND TIMESTAMPDIFF(MINUTE, last_stage_change, NOW) > <threshold>;

Frequency: Real-time alert (every 5 min cron)

3.7 Manual dispatch ratio

Định nghĩa: % đơn cần ops dispatch tay (sau 3 rounds fail).

Formula:

SELECT 
 SUM(CASE WHEN manual_dispatched = TRUE THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM orders
WHERE current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

Target: ≤ 3%

4. Agent metrics (Agent Squad)

4.1 Agent utilization

Định nghĩa: % thời gian agent đang trong job (vs online time).

Formula:

WITH agent_time AS (
 SELECT 
 agent_id,
 SUM(TIMESTAMPDIFF(MINUTE, online_start, online_end)) AS online_minutes
 FROM agent_sessions
 WHERE date BETWEEN <from> AND <to>
 GROUP BY agent_id
),
agent_busy AS (
 SELECT 
 execution_agent_id AS agent_id,
 SUM(TIMESTAMPDIFF(MINUTE, started_at, completed_at)) AS busy_minutes
 FROM order_steps
 WHERE status = 'completed'
 AND started_at BETWEEN <from> AND <to>
 GROUP BY execution_agent_id
)
SELECT t.agent_id, b.busy_minutes * 100.0 / t.online_minutes AS utilization_pct
FROM agent_time t
LEFT JOIN agent_busy b ON t.agent_id = b.agent_id;

Target: 50-70% (cao quá → quá tải, thấp quá → lãng phí)

4.2 Active agents (DAU, MAU)

Định nghĩa: Số agent có ≥ 1 đơn completed trong period.

Daily: WHERE completed_at >= today_start Monthly: WHERE completed_at >= month_start

4.3 Agent rating average

Formula:

SELECT agent_id, AVG(rating) AS avg_rating, COUNT(*) AS cnt
FROM order_ratings
WHERE rated_at BETWEEN <from> AND <to>
GROUP BY agent_id;

4.4 Agent earnings

Per period:

SELECT 
 os.execution_agent_id,
 SUM(os.labor_price * (1 - commission_rate)) AS earnings
FROM order_steps os
WHERE os.status = 'completed'
 AND os.completed_at BETWEEN <from> AND <to>
GROUP BY os.execution_agent_id;

4.5 Agent acceptance rate

Định nghĩa: % đơn agent accept khi nhận invitation (vs decline/timeout).

Formula:

SELECT 
 agent_id,
 SUM(CASE WHEN response = 'accept' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM dispatch_invitations
WHERE created_at BETWEEN <from> AND <to>
GROUP BY agent_id;

Note: agent có acceptance rate < 30% → flag for ops review (low engagement).

4.6 No-show rate

Định nghĩa: % đơn agent accept nhưng không đến.

Formula:

SELECT 
 agent_id,
 SUM(CASE WHEN no_show = TRUE THEN 1 ELSE 0 END) * 100.0 / total_assigned
FROM agent_performance
WHERE date BETWEEN <from> AND <to>;

Target: ≤ 1%

5. Partner metrics (Partner Squad)

5.1 Partner GMV

Định nghĩa: GMV của các đơn có source = partner_customer cho partner cụ thể.

Formula:

SELECT SUM(total_amount)
FROM orders
WHERE source = 'partner_customer'
 AND partner_id = <partner>
 AND current_stage IN ('09_completed', '10_rated')
 AND created_at BETWEEN <from> AND <to>;

5.2 Partner active rate

Định nghĩa: % partner có ≥ 1 đơn trong period (vs tổng active partners).

SELECT 
 SUM(CASE WHEN order_count > 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM (
 SELECT p.id, COUNT(o.id) AS order_count
 FROM partners p
 LEFT JOIN orders o ON o.partner_id = p.id AND o.created_at BETWEEN <from> AND <to>
 WHERE p.status = 'active'
 GROUP BY p.id
) t;

5.3 Partner wallet health

Định nghĩa: % partner có balance < 7 ngày spending average (alert).

WITH partner_daily_spend AS (
 SELECT partner_id, AVG(daily_amount) AS avg_daily
 FROM (
 SELECT partner_id, DATE(created_at) AS d, SUM(total_amount) AS daily_amount
 FROM orders
 WHERE source = 'partner_customer'
 AND created_at >= NOW - INTERVAL 30 DAY
 GROUP BY partner_id, d
 ) t
 GROUP BY partner_id
)
SELECT 
 p.id, p.business_name, p.customer_config->>'$.wallet_balance' AS balance, 
 ds.avg_daily * 7 AS reserve_needed,
 CASE WHEN balance < ds.avg_daily * 7 THEN 'LOW' ELSE 'OK' END AS status
FROM partners p
JOIN partner_daily_spend ds ON ds.partner_id = p.id;

Trigger: nếu LOW → email partner_finance role với "Cần nạp ví"

5.4 Partner outstanding invoice

Formula:

SELECT partner_id, SUM(total_amount) AS outstanding
FROM partner_invoices
WHERE status IN ('sent', 'overdue')
GROUP BY partner_id;

5.5 Partner agent count

Formula:

SELECT 
 partner_id,
 SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) AS active_count,
 COUNT(*) AS total_count
FROM agents
WHERE partner_id IS NOT NULL
GROUP BY partner_id;

6. Quality metrics (Quality Squad)

6.1 Average rating

Formula:

SELECT AVG(rating)
FROM order_ratings
WHERE rated_at BETWEEN <from> AND <to>;

Target: ≥ 4.5

6.2 Rating distribution

Formula:

SELECT rating, COUNT(*) 
FROM order_ratings
WHERE rated_at BETWEEN <from> AND <to>
GROUP BY rating;

6.3 NPS (Net Promoter Score)

Định nghĩa: % promoters (9-10) - % detractors (0-6) (giả định scale 0-10).

Note: SMP đang dùng scale 1-5 sao, chuyển đổi: - Promoters: 5 sao - Passives: 4 sao - Detractors: 1-3 sao

WITH t AS (
 SELECT 
 SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) AS promoters,
 SUM(CASE WHEN rating <= 3 THEN 1 ELSE 0 END) AS detractors,
 COUNT(*) AS total
 FROM order_ratings
)
SELECT (promoters - detractors) * 100.0 / total AS nps FROM t;

Target: ≥ 50

6.4 Dispute rate

Formula:

SELECT COUNT(disputes) * 100.0 / total_orders
FROM ...

Target: ≤ 2%

6.5 Average dispute resolution time

Formula:

SELECT AVG(TIMESTAMPDIFF(HOUR, opened_at, resolved_at))
FROM disputes
WHERE resolved_at IS NOT NULL
 AND resolved_at BETWEEN <from> AND <to>;

Target: ≤ 48h

6.6 Free-form material rate

Định nghĩa: % step có free-form material (vs reserved).

Formula:

SELECT 
 SUM(CASE WHEN source = 'free_form' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM order_step_materials
WHERE created_at BETWEEN <from> AND <to>;

Target: ≤ 10% (cao quá → BOM coverage kém)

6.7 Variance review rate

Định nghĩa: % BOM có actual khác > 5% expected.

Formula: tính từ comparison BOM expected vs actual per step.

7. Financial metrics (Finance Squad)

7.1 Cash collected vs receivable

Formula:

SELECT 
 SUM(CASE WHEN payment_status = 'paid' THEN total_amount ELSE 0 END) AS collected,
 SUM(CASE WHEN payment_status = 'pending' THEN total_amount ELSE 0 END) AS receivable
FROM orders
WHERE created_at BETWEEN <from> AND <to>;

7.2 Refund rate

Formula:

SELECT SUM(refund_amount) / SUM(total_amount) * 100.0
FROM orders WHERE ...;

7.3 Pending payouts to agents/partners

Formula:

SELECT SUM(net_amount) FROM partner_payouts WHERE status = 'pending';
SELECT SUM(amount) FROM agent_payouts WHERE status = 'pending';

7.4 Material margin

Formula:

SELECT 
 SUM((mv.sell_price - mv.cost_price_avg) * osm.quantity) AS total_margin
FROM order_step_materials osm
JOIN material_variants mv ON osm.material_variant_id = mv.id
WHERE osm.source = 'reserved'
 AND osm.step is completed
 AND period;

8. Technical metrics (Engineering)

8.1 API latency (p50, p95, p99)

Source: Prometheus Dashboard: Grafana

histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

SLO targets: - Read endpoints: p95 < 300ms - Write endpoints: p95 < 500ms - Dispatch: p95 < 2s

8.2 Error rate (5xx)

sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

SLO: < 0.1%

8.3 Webhook delivery success rate

SELECT 
 SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)
FROM webhook_delivery_log
WHERE sent_at BETWEEN <from> AND <to>;

8.4 Event log DLQ depth

Source: MongoDB count Alert: > 100 entries

8.5 Cache hit ratio

Source: Redis INFO Target: > 80% for cached endpoints

9. Dashboard mapping

Dashboard 1 · Executive overview (PM, Founder)

  • GMV (this month + last month + YoY if available)
  • Order count
  • Active customers
  • Top services by revenue
  • Top partners by GMV
  • Rating average + NPS

Dashboard 2 · Operations live (Ops Manager)

  • Orders in progress (by stage breakdown)
  • Stuck orders count + list
  • Active agents (online now)
  • Dispatch queue depth
  • Manual dispatch tasks
  • Pending material verifies

Dashboard 3 · Agent performance (Agent Squad)

  • Total agents · active vs pending KYC
  • DAU / MAU
  • Utilization distribution
  • Top performers (orders + rating)
  • Bottom performers (acceptance rate)

Dashboard 4 · Partner overview (Partner Squad)

  • Total partners · active
  • Partner GMV ranking
  • Wallet health alerts
  • Outstanding invoices
  • Recent KYC pending

Dashboard 5 · Quality (Quality Squad)

  • Rating average trend
  • Dispute count + resolution time
  • Free-form rate trend
  • BOM variance trend
  • Recent dispute list (open)

Dashboard 6 · Finance (Finance Squad)

  • Cash collected
  • Pending payouts
  • Material margin
  • Refund rate
  • Outstanding invoices aging

Dashboard 7 · Tech health (Engineering)

  • API latency
  • Error rate
  • Webhook delivery
  • DLQ depth
  • Pod CPU/RAM

10. Data pipeline

10.1 Real-time (Redis counters)

  • Orders count by status (TTL 24h)
  • Active agents (sets)
  • Dispatch queue depth

10.2 Near real-time (5-min aggregations)

  • Stuck orders alert
  • Webhook delivery success
  • Error rate

10.3 Hourly batch

  • Order completion rate
  • Average duration
  • Cancellation reasons breakdown

10.4 Daily batch (00:05)

  • GMV, NMV, SMP Revenue
  • Partner GMV, wallet health
  • Agent utilization, earnings
  • Material margin

10.5 Tooling

  • Prometheus for tech metrics
  • Custom Go cron jobs for business metrics → write to smp_analytics MySQL DB
  • Grafana queries smp_analytics for dashboards
  • Phase 2: dbt + ClickHouse for heavy analytics

10.6 · v4.0 architecture · CQRS Read Model

Quan trọng: từ v4.0, hầu hết dashboard queries chuyển sang Elasticsearch read model thay vì query MySQL primary trực tiếp. Lý do: scale + decouple workload analytic khỏi transactional.

10.6.1 Architecture diagram

┌─────────────────────────────────────────────────────────────────┐
│ WRITE SIDE (Commands) │
│ │
│ API → service → MySQL primary │
│ │ │
│ │ binlog │
│ ▼ │
│ ┌──────────┐ │
│ │ Debezium │ CDC connector │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Kafka topics │ │
│ └────────┬───────┘ │
└───────────────────────┼─────────────────────────────────────────┘
┌───────────────────────┼─────────────────────────────────────────┐
│ ▼ READ SIDE (Queries) │
│ ┌────────────────┐ │
│ │ ES sink │ (Kafka Connect) │
│ │ Connector │ │
│ └────────┬───────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Elasticsearch │ ◄── read model │
│ │ indices: │ │
│ │ orders-YYYY-MM│ (daily, denormalized) │
│ │ agents │ │
│ │ partners │ │
│ │ dispatch-runs │ │
│ └────────┬───────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │Grafana │ │Admin UI │ │Reports │ │
│ │dashboard│ │analytics │ │CSV/Excel │ │
│ └─────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘

10.6.2 Query routing matrix

Cập nhật cách truy vấn từng metric:

Metric category v3.x source v4.0 source Latency Fallback
Real-time counts (Section 3.6 Stuck orders) MySQL primary Redis (unchanged) <100ms n/a
Hourly aggs (Section 3.1 Completion rate) MySQL replica Elasticsearch 1-5s lag MySQL replica
Daily aggs (Section 2.1 GMV) MySQL replica + cron Elasticsearch + scheduled refresh 1-5s lag MySQL replica
Search queries (Customer support find order) MySQL LIKE Elasticsearch full-text 50-200ms MySQL LIKE (slow)
Map visualizations (heatmap orders by district) MySQL geospatial Elasticsearch geo_point <500ms n/a
Financial reports (Section 7.x) MySQL primary (auditable) MySQL primary (unchanged) n/a n/a

Note: Financial reports KHÔNG migrate sang ES vì cần exact consistency cho audit. Eventual consistency 1-5s không acceptable cho accounting close.

10.6.3 Sample queries · before/after

Example 1: GMV by district (Section 3.x)

-- ❌ v3.x: SQL on MySQL replica
SELECT
 district,
 SUM(total_amount) AS gmv,
 COUNT(*) AS order_count
FROM orders
WHERE completed_at_utc >= DATE_SUB(NOW, INTERVAL 1 DAY)
 AND status = 'paid'
GROUP BY district
ORDER BY gmv DESC;
-- ~3-5 seconds with 1M orders
// ✅ v4.0: Elasticsearch aggregation
POST /orders-*/_search
{
 "size": 0,
 "query": {
 "bool": {
 "filter": [
 {"range": {"completed_at_utc": {"gte": "now-1d/d"}}},
 {"term": {"status": "paid"}}
 ]
 }
 },
 "aggs": {
 "by_district": {
 "terms": {"field": "district", "size": 100, "order": {"gmv": "desc"}},
 "aggs": {
 "gmv": {"sum": {"field": "total_amount"}}
 }
 }
 }
}
// ~200-500ms với same data

Example 2: Order full-text search (admin support)

-- ❌ v3.x: MySQL LIKE on multiple columns
SELECT * FROM orders
WHERE customer_name LIKE '%nguyen%'
 OR address_line LIKE '%nguyen%'
 OR notes LIKE '%nguyen%'
ORDER BY created_at DESC LIMIT 50;
-- 2-5 seconds, no index usage
// ✅ v4.0: Elasticsearch multi_match
POST /orders-*/_search
{
 "query": {
 "multi_match": {
 "query": "nguyen",
 "fields": ["customer_name^3", "address_line", "notes"],
 "fuzziness": "AUTO"
 }
 },
 "sort": [{"created_at_utc": "desc"}],
 "size": 50
}
// 50-100ms với analyzer + inverted index

10.6.4 Eventual consistency · UX guidance

Dashboard hiển thị data lag indicator: - Lag < 5s: "Real-time" badge (green) - Lag 5-60s: "Updated X seconds ago" (yellow) - Lag > 60s: Banner "Data may be stale - last sync: HH:MM" + fallback to MySQL replica - Lag > 5min: Critical alert, page on-call

Critical UX rule: không hiển thị dashboard mà không có lag indicator — user phải biết data tươi/cũ thế nào.

10.6.5 Elasticsearch index design

Daily rolling indices cho orders:

orders-2026-05-28 ← hot (today, write + read)
orders-2026-05-27 ← hot (yesterday)
orders-2026-05-26 ← warm (older, read-only)
...
orders-2026-04-* ← warm → cold tier

Index template (Elasticsearch):

PUT /_index_template/orders-template
{
 "index_patterns": ["orders-*"],
 "template": {
 "settings": {
 "number_of_shards": 3,
 "number_of_replicas": 1,
 "refresh_interval": "5s"
 },
 "mappings": {
 "properties": {
 "order_id": {"type": "keyword"},
 "customer_id": {"type": "keyword"},
 "customer_name": {
 "type": "text",
 "fields": {"keyword": {"type": "keyword"}}
 },
 "country_code": {"type": "keyword"},
 "currency": {"type": "keyword"},
 "total_amount": {"type": "long"},
 "status": {"type": "keyword"},
 "district": {"type": "keyword"},
 "city": {"type": "keyword"},
 "location": {"type": "geo_point"},
 "created_at_utc": {"type": "date"},
 "completed_at_utc": {"type": "date"}
 }
 }
 }
}

ILM (Index Lifecycle Management): - Hot phase: 7 days, fast SSD, write enabled - Warm phase: 60 days, slower disks, read-only, force_merge - Cold phase: 1 year, archive, very slow access - Delete phase: > 1 year, auto-delete

10.6.6 Failover

Primary path: Service → ES query
 ▼ if ES down
Fallback path: Service → MySQL replica
 ▼ if also down
Emergency: Service → return cached snapshot (1h old) + alert

Circuit breaker pattern: nếu ES timeout > 3 lần trong 1 phút, mở circuit, route 100% traffic sang MySQL fallback trong 30s.

11. Validation

Cuối tháng, Finance đối chiếu: - SMP Revenue formula (Section 2.3) vs accounting books - Pending payouts (Section 7.3) vs bank statements - GMV vs invoice totals

Bất kỳ discrepancy > 1% → investigate + fix formula or data pipeline.

12. Change management

Khi đổi formula 1 metric: 1. Tạo PR với new formula + old formula side-by-side comparison 2. Test trên historical data 30 ngày 3. PM + Finance approve 4. Deploy + recalculate historical metrics 5. Notify all dashboard users 6. Document change in this doc + changelog