Skip to main content

SOS Scoring Engine

Scoring Engine tính toán điểm SOS cho mỗi Seller theo 5 thành phần, áp dụng trọng số (weight), xác định tier, và xử lý grace period cho seller mới.


Tổng Quan Quy Trình

1. Load configs (weights, tiers, grace period, exceptions)
2. Calculate 5 components in parallel:
├── calcPScore() → Planning & Forecast
├── calcOScore() → Order Operations
├── calcTScore() → Ticket SLA
├── calcFScore() → Finance/Payment
└── calcIScore() → Inventory Health
3. Apply exceptions (score overrides)
4. Apply weights → weighted scores
5. Sum → Total SOS
6. Grace period check (min floor for new sellers)
7. Determine tier from sos_tier_config
8. Return ScoreResult

P-Score: Planning & Forecast (Default 25%)

Dữ liệu nguồn: sos_planning_raw (aggregated từ forecast submissions)

Logic tính điểm:

  • Lấy tất cả planning records cho seller trong period
  • Mỗi record có on_time_points (0–50) và accuracy_points (0–50)
  • P-Score = Average(on_time + accuracy) across all campaigns

Config (sos_p_score_config):

FieldDescriptionDefault
campaign_typeLoại campaign (major/mini)
required_lead_daysSố ngày phải submit trước campaign
on_time_pointsĐiểm max cho submit đúng hạn50
late_penalty_per_dayTrừ điểm mỗi ngày trễ
accuracy_base_pointsĐiểm base cho forecast accuracy50
accuracy_good_range_min/maxNgưỡng % dự báo chính xác
accuracy_penalty_per_10pctTrừ điểm mỗi 10% sai lệch

O-Score: Order Operations (Default 20%)

Dữ liệu nguồn: sos_order_operation_raw (từ OMS sync POST /api/sync/orders)

Logic tính điểm:

late_pct = orders_late / total_orders × 100

if (late_pct <= acceptable_late_pct):
O-Score = base_points (100)
else:
over = late_pct - acceptable_late_pct
penalty = floor(over) × penalty_per_1pct_over
O-Score = clamp(base_points - penalty, 0, 100)

Config (sos_o_score_config):

FieldDescriptionDefault
base_pointsĐiểm khởi đầu100
acceptable_late_pct% trễ được phép (không bị trừ)3%
penalty_per_1pct_overTrừ điểm mỗi 1% vượt ngưỡng5
max_delay_minutesNgưỡng coi là "trễ" (phút)30

T-Score: Ticket SLA (Default 20%)

Dữ liệu nguồn: sos_ticket_sla_raw (từ Frappe Helpdesk sync POST /api/sync/tickets)

Logic tính điểm:

  • Lấy avg_response_time_hours từ raw data
  • So khớp với tier config theo thứ tự tier_index:
    • Nếu avg_hours >= min AND (max IS NULL OR avg_hours < max) → trả về points
  • Fallback: tier cuối cùng (worst)

Config (sos_t_score_config):

tier_indexresponse_hours_minresponse_hours_maxpoints
104100
24880
381660
4162440
524NULL20

F-Score: Finance & Payment (Default 20%)

Dữ liệu nguồn: sos_payment_raw (từ Accounting sync POST /api/sync/payments)

Logic tính điểm:

  • Lấy worst_days_late (số ngày trễ thanh toán tệ nhất)
  • So khớp với tier config theo tier_index:
    • Nếu worst_late >= min AND (max IS NULL OR worst_late <= max) → trả về points
  • Fallback: tier cuối cùng (worst = 0 điểm)

Config (sos_f_score_config):

tier_indexdays_late_mindays_late_maxpoints
100100
21780
381560
4162030
521NULL0

I-Score: Inventory Health (Default 15%)

Dữ liệu nguồn: sos_inventory_health_raw (từ WMS sync POST /api/sync/inventory)

Logic tính điểm:

aging_pct = MAX(aging_pct_by_cbm, aging_pct_by_qty)

if (aging_pct <= base_good_pct):
I-Score = base_points (100)
else:
over = aging_pct - base_good_pct
penalty_steps = floor(over / 5)
score = base_points - (penalty_steps × penalty_per_5pct)

# Severe aging multiplier
if (aging_over_180d_pct > severe_threshold):
score = round(score / severe_multiplier)

I-Score = clamp(score, 0, 100)

Config (sos_i_score_config):

FieldDescriptionDefault
base_pointsĐiểm khởi đầu100
base_good_pct% tồn kho aging được phép5%
penalty_per_5pctTrừ điểm mỗi 5% vượt ngưỡng10
severe_aging_daysNgưỡng aging nghiêm trọng (ngày)180
severe_aging_pct_threshold% tồn kho > 180 ngày kích hoạt multiplier30%
severe_storage_multiplierHệ số nhân phạt nặng1.5

Weighted Scoring & Total SOS

Total SOS = P_raw × W_p + O_raw × W_o + T_raw × W_t + F_raw × W_f + I_raw × W_i

Ví dụ:

ComponentRaw ScoreWeightWeighted
P-Score8525%21.25
O-Score9020%18.00
T-Score8020%16.00
F-Score10020%20.00
I-Score7015%10.50
Total SOS85.75

Weights có thể cấu hình qua sos_score_weight_config. Default weights: P=25%, O=20%, T=20%, F=20%, I=15%.


Tier Determination

Sau khi tính Total SOS, hệ thống so khớp với sos_tier_config:

TierMin ScoreMax ScoreÝ nghĩa
Platinum90100Seller xuất sắc
Gold8089Seller tốt
Silver7079Seller trung bình
Bronze5069Cần cải thiện
Warning049Cảnh báo nghiêm trọng

Grace Period (Seller Mới)

Seller mới ký hợp đồng được hưởng grace period (ưu đãi điểm tối thiểu):

Điều kiện áp dụng:
1. months_since_contract <= grace_period_months (default: 2)
2. cumulative_orders < min_orders_threshold (default: 30)

Nếu đủ điều kiện VÀ total_sos < min_score_floor (default: 70):
→ total_sos = min_score_floor
→ grace_floor_applied = true
→ original_sos_before_floor = [giá trị gốc]

Config qua seller_grace_period_config.


Score Exceptions

Quản trị viên có thể tạo exception để override điểm cho một seller cụ thể:

  • Áp dụng theo component: p_score, o_score, t_score, f_score, i_score, hoặc all
  • Rule format: { "set_score": 100 } — set điểm raw thành giá trị cố định
  • Thời hạn hiệu lực: effective_from / effective_to

Exceptions được kiểm tra tự động khi chạy scoring engine.


Monthly Scoring Flow

# 1. Trigger monthly scoring
POST /api/sos/monthly/calculate
Body: { "period": "2026-02" }
→ Tính điểm cho TẤT CẢ active sellers

# 2. Review monthly scores
GET /api/sos/monthly?period=2026-02
→ Danh sách scores (status: draft)

# 3. Finalize individual score
PATCH /api/sos/monthly/:id/finalize
Body: { "reviewed_by": "admin", "notes": "OK" }
→ status: draft → final
→ Webhook: seller.score_finalized
→ Webhook: seller.tier_changed (nếu tier thay đổi)