Mục lục
- metrics-agoda/ — tách khỏi Booking, gộp sau
- Khoá tự nhiên từng bảng
- Bản đồ endpoint — dò ngày 24/09/2026
- Agoda dễ hơn Booking ở hai chỗ
- Và khó hơn ở hai chỗ
- Số đầu tiên — mốc 24/09/2026
- Điều kiện chạy
- Ngày bắt đầu active — dò 25/09/2026
- Ba bẫy đã trả giá — đọc trước khi sửa script
- Chưa làm
- agoda_ranking — thứ hạng trên trang tìm kiếm CÔNG KHAI (thêm 26/09/2026)
metrics-agoda/ — tách khỏi Booking, gộp sau
Cùng cơ chế với ../metrics/: bảng chiều + bảng sự kiện, chỉ thêm
không sửa, payload gốc giữ trong _raw/.
🔴 Tách kho là có chủ ý. Hai kênh đo những thứ không quy về nhau được: cửa sổ khác nhau, định nghĩa "booking" khác nhau, hoa hồng khác nhau. Trộn sớm là mất khả năng truy một con số về đúng nguồn của nó. Gộp là việc của lớp trình bày, làm sau, và làm tường minh.
metrics-agoda/
├── dim_property.csv 7 căn × kênh agoda (external_id = Agoda hotel id)
├── fact_agoda_summary.csv 84 · mốc × căn × chỉ số × KỲ
├── fact_agoda_daily.csv 178 · mốc × căn × NGÀY × trục (book|stay)
├── fact_agoda_transactions.csv 28 · 1 GIAO DỊCH tiền
├── fact_agoda_reservations.csv 14 · 1 ĐƠN đặt phòng
├── fact_agoda_reviews.csv 8 · 1 REVIEW
├── agoda_ranking.csv 56 · 1 căn × 1 lượt tìm kiếm công khai (thứ hạng Best match)
├── _raw/<ngày>-agoda/ payload gốc, mỗi căn một file JSON
└── README.md file này
Cào rồi nạp:
node scripts/ota-agoda-metrics-crawl.mjs # → _raw/<ngày>-agoda/
python3 scripts/ota-agoda-metrics-extract.py # → 5 bảng fact_agoda_*
python3 scripts/build-agoda-page.py # → tab Agoda của báo cáo
python3 scripts/build-agoda-data-page.py # → tab Data_Agoda
Khoá tự nhiên từng bảng
| Bảng | Một dòng là gì | Khoá |
|---|---|---|
dim_property |
1 chỗ nghỉ × kênh agoda | (slug, channel) |
fact_agoda_summary |
1 mốc × căn × chỉ số × kỳ | (snapshot_date, channel, slug, metric, period_type) |
fact_agoda_daily |
1 mốc × căn × ngày × trục | (snapshot_date, channel, slug, date, basis) |
fact_agoda_transactions |
1 giao dịch tiền | (channel, slug, transaction_id) |
fact_agoda_reservations |
1 đơn | (channel, slug, booking_id) |
fact_agoda_reviews |
1 review | (channel, slug, review_id) |
agoda_ranking |
1 căn × 1 tìm kiếm × 1 lượt đo | (snapshot_date, search_id, pass_no, slug) |
Nối đơn ↔ tiền: fact_agoda_reservations.booking_id = fact_agoda_transactions.reference_number
(kiểm 2/2 trên 181 THT). Danh sách đơn không kèm tiền, nên muốn doanh thu từng đơn thì
bắt buộc phải nối. booking_value = 0 nghĩa là đơn đã huỷ — 9/28 giao dịch đang như vậy,
giống hệt cách Booking xoá giá đơn huỷ.
Bản đồ endpoint — dò ngày 24/09/2026
Cây menu đầy đủ không cần bấm: GET /mldc/en-us/api/layout/<id>/PrivateLayout/Main.
| Việc | Trang | API |
|---|---|---|
| Tổng quan | /app/reporting/dashboard/<id> |
reporting/PerformanceDashboard/GetPerformanceSummaries/<id> |
| Hạng so đối thủ | cùng trên | reporting/MarketInsights/RankingAgainstCompetitors/<id> |
| Phân tích | /app/reporting/analyticscenter/<id> |
reporting/BookingInsights/GetTimeFrameMetrics/<id> · …/GetTopCompetitors/<id> |
| Đơn | /app/postbook/booking/<id> |
postbook/Booking/list/<id> — POST, ngày dạng /Date(ms)/ |
| Review | /app/setting/review/<id> |
setting/Review/searchreviews/<id> |
| Tiền từng đơn | /app/finance/transactions/<id> |
finance/LegacyTransactions/<id>/SearchTransactions |
Agoda dễ hơn Booking ở hai chỗ
① Ngày là tham số thật. RankingAgainstCompetitors?startDate=YYYY-MM-DD&dayRange=N nhận
ngày tuỳ ý — thử startDate=2025-09-01 và dayRange=365 đều được phục vụ, không bị ép
như date_from của Booking. Cũng không có chuyện token nonce hay trần 3 tháng mỗi lần hỏi.
② Phân trang tử tế. Booking/list trả pagedBookingList{totalCount, pageSize, hasNextPage,
totalPages} — không phải đoán như hasNextPage rỗng của Booking.
Và khó hơn ở hai chỗ
① GetTimeFrameMetrics chỉ có 4 kỳ cứng, lùi tối đa 30 ngày.
timeFrameType |
1 = theo ngày ĐẶT · 2 = theo ngày Ở |
|---|---|
period |
1 = 14 ngày qua · 2 = 30 ngày qua · 3 = 14 ngày tới · 4 = 30 ngày tới |
Bù lại, nó trả daily[] — chuỗi theo NGÀY, thứ Ranking Dashboard của Booking không hề có.
② periodType chưa giải mã hết. GetPerformanceSummaries trả periodSummary[] với
periodType 0 · 1 · 2 · 3, và API không khai nghĩa.
- ✅
periodType 1= this month — xác minh bằng cách đối chiếu với con số hiện trên dashboard của 181 Tô Hiến Thành (Revenue 8.235.938 ₫). - ⬜
0·2·3chưa chốt. Nhìn độ lớn thì3là kỳ dài nhất,2ở giữa — nhưng đó là suy đoán. Script ghi nguyên sốperiodType, không tự đặt tên. Muốn chốt thì đối chiếu từng giá trị với nhãn trên dashboard như đã làm vớiperiodType 1.
performanceType: 0 = đêm phòng · 1 = doanh thu · 3 = lượt đặt (suy từ đơn vị và độ
lớn, chưa đối chiếu nhãn). 2 và 4..7 chưa dùng.
Số đầu tiên — mốc 24/09/2026
| Căn | Doanh thu p3 |
Lượt đặt p3 |
|---|---|---|
| 181 Tô Hiến Thành | 111.993.840 ₫ | 10 |
| 15 Nước Mặn 5 | 36.520.736 ₫ | 3 |
| A La Carte 502 | 6.240.000 ₫ | 1 |
| 305 · 404 · 41 LHC · 25 AT11 | 0 | 0 |
🔴 Đây là doanh thu mà báo cáo Booking hoàn toàn không thấy. 181 Tô Hiến Thành — căn mà bên Booking chỉ có 45,3 tr doanh thu 12 tháng và bị 70% huỷ — bán được 112 triệu bên Agoda. Bất kỳ kết luận nào về "căn này yếu" mà chỉ nhìn Booking đều sai.
⚠️ Chưa biết p3 là cửa sổ nào, nên chưa được so thẳng với con số 12 tháng của Booking.
Đó là việc phải làm trước khi gộp hai kênh.
Điều kiện chạy
Giống hệt Booking — xem /ota-metrics-crawl
§0: đúng profile Chrome (Huy), tab con phải mở bằng window.open từ tab đã đăng nhập,
daemon CDP còn sống và chưa đầy bộ đệm. Cần một tab portal.agoda.com đang mở.
Ngày bắt đầu active — dò 25/09/2026
Agoda không có trường "ngày lên sóng". Đã dò cả 27 mục menu YCS: hợp đồng, compliance,
property details, payouts, remittances, calendar, property switcher — không trang nào khai.
Nên ngày phải suy từ dấu vết, bằng node scripts/ota-agoda-active-from.mjs.
| Căn | active_from |
Căn cứ | Hợp đồng sớm nhất |
|---|---|---|---|
181tohienthanh |
2026-01-23 | 📧 khách hỏi phòng (sớm hơn đơn đầu 19/02 gần một tháng) | 2026-01-19 |
alacarte305 |
2025-03-06 | đơn đặt sớm nhất (khách hỏi cùng buổi sáng, 10:08) | 2024-12-25 |
15nuocman5 |
2026-06-19 | đơn đặt sớm nhất (ở 26/06/2026) | 2026-02-02 |
alacarte502 |
2026-06-22 | đơn đặt sớm nhất (ở 16/07/2026) | 2026-02-02 |
25anthuong11 |
2026-09-18 | 📧 Agoda tự khai — thư "Your property has been published!" 19:41 | 2026-09-18 |
alacarte404 |
— | chưa bán được đêm nào · không thư Agoda nào từng nhắc tên căn này | 2026-02-02 |
41lehycat |
— | chưa bán được đêm nào · thư sớm nhất nhắc tên là 08/07/2026, nhưng chỉ là thư marketing | 2026-02-02 |
active_from là cận trên quan sát được: listing chắc chắn đã active trước hoặc đúng
ngày đó, không có gì bảo đảm nó active đúng ngày đó. Cùng quy ước với kho Booking.
Cột contract_from là lần chấp thuận điều khoản sớm nhất của chính căn đó — trần trên
của ngày tạo hồ sơ. Để riêng, không thế vào active_from: ký giấy xong vẫn có thể chưa
bật bán. Khoảng cách giữa hai cột nói lên điều đó: 305 ký 25/12/2024 mà mãi 06/03/2025 mới
có đơn; bốn căn cùng ký 02/02/2026 nhưng 15 NM5 tới 19/06 mới bán được, còn 404 và 41 LHC
tới nay vẫn chưa.
🔴 Ba căn để trống là "chưa quan sát được", KHÔNG phải "chưa active". Ba bằng chứng:
PropertyRecStatus = 1— giống hệt bốn căn đang bán được. Không có căn nào ở trạng thái nháp hay bị tạm ngưng.- Lịch đang mở và đang có giá: 404 48 ngày · 41 LHC 90 · 25 AT11 88.
totalPages: 0trênSearchTransactionsxác nhận không có đơn thật, không phải lỗi phân trang (phân biệt với zero-rows-may-mean-logged-out).
Nên chỗ trống không phải lỗi đọc, cũng không phải listing hỏng — nó là hệ quả của cách suy ngày. Mọi dấu vết Agoda giữ lại đều là dấu vết khách để lại (đơn, review, giao dịch, payout). Căn chưa có khách thì không để lại gì để neo.
Thứ suy được là một KHOẢNG, không phải một ngày:
| Căn | Sớm nhất có thể | Muộn nhất có thể | Rộng |
|---|---|---|---|
alacarte404 |
02/02/2026 (ký hợp đồng) | hôm nay | ~8 tháng |
41lehycat |
02/02/2026 (ký hợp đồng) | hôm nay | ~8 tháng |
25anthuong11 |
18/09/2026 (ký hợp đồng) | hôm nay | ~1 tuần |
25 An Thượng 11 hẹp tới mức coi như đã có đáp số: property tạo bằng Fast-track import và chỉ chấp thuận đúng bản điều khoản hiện hành, nên 18/09/2026 vừa là ngày ký vừa là ngày tạo hồ sơ. Hai căn còn lại thì khoảng quá rộng để gọi là một ngày.
Cách duy nhất làm hẹp thêm: đợi đơn đầu tiên, hoặc hỏi Agoda support (họ giữ lịch sử trạng thái property, portal thì không).
Bên trong hợp đồng có gì — mở đọc 25/09/2026
Nút View contract mở modal trong trang (không phải PDF, không mở tab mới). Nội dung là văn bản chung của Agoda, không có tên căn và không có ngày ký riêng. Thứ duy nhất có ngày là dòng đầu — ngày hiệu lực của phiên bản điều khoản, giống hệt nhau ở mọi căn:
| id | Phiên bản | Ngày hiệu lực ghi trong văn bản |
|---|---|---|
88 |
General T&C 6.0 | 08/12/2023 |
89 |
General T&C 7.0 | 21/02/2025 |
130 |
General T&C 8.0 | 08/06/2026 |
🔴 Đừng lấy ngày trong văn bản làm ngày của căn. Ngày per-property nằm ở lastModify của
API, tức lúc căn đó chấp thuận, không phải lúc văn bản ra đời. Ví dụ 41 Lê Hy Cát chấp
thuận bản 7.0 ngày 02/02/2026 — 11 tháng sau khi bản đó hiệu lực.
Hợp đồng riêng của từng căn (APPA — Accommodation Property Participation Agreement) là
thứ đáng đọc nhất thì nút View và Download đều disabled: chỉ 305 (1371548) và 181 THT
(1705775) có APPA, và cả hai đều không mở được. Bốn căn còn lại không có APPA nào.
Nhưng danh sách phiên bản đã chấp thuận lại khoanh được vùng ngày tạo hồ sơ. Một căn chỉ phải chấp thuận những bản còn hiệu lực trong đời nó, nên bản CŨ NHẤT nó có nói lên nó ra đời lúc nào:
| Căn | Bản cũ nhất đã chấp thuận | ⇒ hồ sơ tạo trong khoảng |
|---|---|---|
alacarte305 |
6.0 (nhận 25/12/2024) | trước 21/02/2025 — căn duy nhất có mặt từ thời 6.0 |
181tohienthanh |
7.0 (nhận 19/01/2026) | 21/02/2025 → 19/01/2026 |
alacarte404 · alacarte502 · 15nuocman5 · 41lehycat |
7.0 (nhận 02/02/2026) | 21/02/2025 → 02/02/2026 |
25anthuong11 |
8.0 (nhận 18/09/2026) | sau 08/06/2026 → 18/09/2026 |
25anthuong11 không có bản 7.0 — nếu hồ sơ đã tồn tại thời 7.0 thì bắt buộc phải có. Đó
là bằng chứng độc lập cho thấy căn này sinh ra sau 08/06/2026, khớp với hotel id 96450982 cao
vọt so với sáu căn kia.
⚠️ Bản 130 được cả sáu căn cũ nhận đúng ngày 08/06/2026 (305 lúc 13:12, năm căn kia
13:56) — trùng ngày hiệu lực, nên nhiều khả năng là đóng dấu hàng loạt tự động, không phải
host bấm. Vì vậy ngày của bản 130 chỉ chứng minh "căn đã tồn tại ngày 08/06/2026", không nói
gì thêm. Ngược lại 88 và 89 được nhận muộn hơn ngày hiệu lực cả năm → đó là hành vi thật.
Nguồn thứ năm: hộp thư Gmail — mạnh hơn cả portal
node scripts/gmail-read.mjs --search "<truy vấn Gmail>" đọc Gmail qua tab Chrome đã đăng
nhập, không cần OAuth, không cần 2FA. Hộp thư giữ thứ portal đã xoá.
Hai mốc mà portal không cho, Gmail cho:
- 📧
25anthuong11= 18/09/2026 19:41 — thưno-reply@notifications.agoda-messaging.comtiêu đề "Your property has been published! Let's get booking.", mở ra ghi đích danh "Dear 4BR Villa in An Thuong 500m to MyKhe Beach". Đây là Agoda tự khai ngày lên sóng, mạnh hơn mọi suy luận — nênactive_from_basisghideclared, không phảiobserved. Chỉ căn này có thư đó (tìmfrom:agoda "has been published"trả đúng 1 kết quả); sáu căn kia lên sóng trước khi Agoda có loại thông báo này. - 📧
181tohienthanh= 23/01/2026 02:01 — thư "Inquiry by YOUNGJAE YOON". Khách hỏi được thì listing đã hiển thị, mà mốc này sớm hơn đơn đầu tiên gần một tháng (19/02). Đã kiểm không có thư khách nào sớm hơn.
Đối chiếu chéo: 305 có thư hỏi phòng lúc 06/03/2025 10:08 và yêu cầu đặt lúc 10:20
cùng sáng — khớp createdDate 06/03/2025 của giao dịch, tức hai nguồn độc lập cùng ra một
ngày. Thư "Welcome to Agoda" 25/12/2024 cũng khớp đúng contract_from của 305, xác nhận
cách đọc cột hợp đồng là đúng.
⚠️ Thư marketing nhắc tên căn KHÔNG phải bằng chứng lên sóng — Agoda gửi cho cả property
chưa bán được gì. 41lehycat được nhắc tên từ 08/07/2026 trong thư mời Mega Sale nhưng vẫn
chưa có dấu hiệu nào cho thấy nó từng bán. alacarte404 thì không thư nào nhắc tên.
🔴 Lịch KHÔNG phải kho lịch sử — đã thử và đã bác bỏ
Calendar/GetAvailability và GetRate nhận ngày quá khứ tuỳ ý và trả về đủ 365 ô, nên
rất dễ tưởng là lịch sử tồn kho. Không phải.
Phép thử bác bỏ: A La Carte 305 có đơn tạo 06/03/2025, khách ở 18/03/2025 — vậy mà quét
trọn năm 2025 trả 365 ngày đều remainingRoomNightsAvailable = 0, basePrice = null,
netRoomNightsBooked = 0. Quá khứ bị dọn sạch, không phải "chưa từng mở".
Dấu hiệu nhận ra cái bẫy mà không cần phép thử: sáu căn khác nhau cùng trả basePrice
bắt đầu đúng một ngày — 26/08/2026. Sáu listing mở bán ở sáu thời điểm khác nhau không thể
cùng có giá từ một ngày; đó là ngày cấu hình giá hiện hành, không phải ngày mở bán.
Script vẫn quét lịch nhưng in dưới nhãn chẩn đoán và không cho nó tính active_from.
period_type — đã chốt thêm một giá trị
p3 khớp tuyệt đối số đơn trong fact_agoda_reservations cho cả bốn căn có đơn
(181 THT 10 · 15 NM5 3 · 502 1 · 305 0). Tức p3 đếm đúng tập mà Booking/list trả về.
Đối chiếu theo ngày nhận phòng thì ba cửa sổ 12 tháng · từ đầu năm · trọn đời cho cùng kết
quả nên chưa tách được ba khả năng đó — nhưng biết chắc p3 không phải "trọn lịch sử
giao dịch", vì 305 có 6 giao dịch từ 2025 mà p3 = 0.
p0 (181 THT = 2) và p2 (181 THT = 5, 15 NM5 = 2) vẫn chưa khớp cửa sổ nào đã thử —
không khớp 7/30/90 ngày, 6/12 tháng, tháng này, từ đầu năm, theo cả ngày đặt lẫn ngày ở.
Ba bẫy đã trả giá — đọc trước khi sửa script
① SearchTransactions mặc định phân trang MỘT DÒNG mỗi trang. Thiếu PageSize thì API
trả 1 dòng với totalPages: 17 và không báo lỗi gì. Dấu hiệu bắt được nó: nới rộng
khoảng ngày mà số dòng lại giảm. Luôn truyền PageSize và đối chiếu với totalPages.
② Tên trường của Agoda khác Booking. checkinDate (i thường, không phải checkInDate),
roomNights cho sẵn (không phải tự tính từ hai ngày), không có trường tiền trong đơn. Đoán
theo thói quen Booking thì bảng vẫn ra đủ dòng — nhưng mọi cột đều rỗng trừ booking_id.
③ Review gọi trần trả rỗng. Phải để trang tự gọi rồi bắt ở tầng mạng qua chrome-grab.
Trang 181 THT ghi "5 reviews" mà API trả 4 — chênh lệch này chưa giải thích được.
Chưa làm
period_type0 · 2 · 3 chưa giải mã. Cả ba chỉ số (bookings·revenue·room_nights) đều có đủ 4 kỳ, tức 84 dòng thì 63 dòng chưa biết cửa sổ. Cách chốt: đối chiếu từng giá trị với nhãn trên dashboard, đúng như đã làm với1.- Chuỗi theo ngày mới có một mốc. Lùi tối đa 30 ngày nên lịch sử dài chỉ tích luỹ được bằng cách cào hằng tuần — bỏ một tuần là đứt đoạn vĩnh viễn, không cào bù được.
- Không có phễu hiển thị / CTR. Đã dò cả 27 trang trong
PrivateLayout/Main: không trang nào trả lượt hiện ra, lượt bấm hay CTR. Nên không so được khả năng thu hút khách giữa hai kênh — chỉ so được kết quả bán. GetTopCompetitorstrả comp set rỗng cho mọi căn (isCompetitorsTracked: false) — chưa rõ phải bật ở đâu.- Chưa dò Calendar (occupancy · RevPAR) và Campaigns / Growth.
agoda_ranking — thứ hạng trên trang tìm kiếm CÔNG KHAI (thêm 26/09/2026)
Tên bảng do user đặt (không theo tiền tố fact_). Nguồn: node scripts/ota-agoda-search-rank.mjs --anon (phát lại GraphQL citySearch của agoda.com) + trạng thái mở bán từ ari/Calendar/GetAvailability + sức chứa từ ChildRate/getroombypropertyId. Chỉ thêm, không sửa. Chi tiết lượt đầu: ../audits/2026-09-26-agoda-search-rank.md.
| Cột | Nghĩa |
|---|---|
search_id · pass_no |
mã tìm kiếm trong lượt đo (A–D) · lượt 1/2 — Agoda xếp lại mỗi trang nên phải đo ≥2 lượt |
city_id · checkin · checkout · nights · adults · rooms |
bộ lọc khách nhập |
sort · personalization |
best_match · anon = đã xoá searchHistory (vẫn đăng nhập) |
total_filtered · rows_scanned · unique_ids |
tổng Agoda báo · số dòng đã duyệt · số id khác nhau (rows_scanned > unique_ids = trùng do xếp lại) |
rank · page_approx · pct_from_top |
vị trí đầu tiên gặp · trang (~49 dòng/trang) · % tính từ đầu — có thể >100 % vì dòng trùng |
status |
ranked · not_found (mở bán, đủ sức chứa mà không thấy) · not_open (lịch không mở đủ các đêm) · over_capacity (khách > sức chứa) · grouped_under_84463577 (404 không có dòng riêng, hiện chung nhóm với 502) |
agoda_bookings_confirmed |
số đơn xác nhận trong postbook/Booking/list lúc đo (305: danh sách chỉ trả 1 dù có giao dịch từ 03/2025) |
note |
ghi chú dòng |
⚠️ Dòng alacarte502 có status = ranked là thứ hạng của cả nhóm 502 + 404 (An Hai Bac Houses), không riêng căn 502.