Chạy on-demand. Job dài (>25s) sẽ vẫn tiếp tục chạy nền sau khi response trả về — kiểm tra Discord / log Heroku.
/mapping.html cho team thêm/sửa/xoá exo_app_mapping qua web (full CRUD). Bảo vệ bằng Basic Auth (env MAPPING_ADMIN_USER + MAPPING_ADMIN_PASS). Có search box, modal form, toast feedback.GET/POST/DELETE /api/mapping + GET /api/mapping/:app_key. Logic chia sẻ qua module mappingService.js (dùng chung với CLI manage_mapping.js).campaign trong cohort blend thành 2: campaign_canonical (lowercase, JOIN nội bộ) + campaign_name (raw, preserve case từ Adjust > AM > cost source). Dashboard query đang dùng campaign cần chuyển sang campaign_canonical hoặc campaign_name.exo_account_ads_config (multi-row history với effective_from). Seed 50 accounts FB+GG theo rule "name có HK → tax 0, còn lại → 10% FCT VN".fb_spend_with_tax, gg_cost_with_tax, total_cost_with_tax. Apply markup = (1+tax)(1+agency)(1+payment) per row trước khi aggregate.profit = total_revenue - total_cost_with_tax (gross, after tax/agency/payment) — phản ánh real bottom line thay vì rev - net cost.run_hourly_chain.js): 13 fetch jobs chạy parallel với concurrency limit 5 (tránh OOM Heroku Standard-1X). Discord summary với label friendly thay vì filename./api/jobs render dynamic theo category, button per job (today/yesterday hoặc default Run). Long jobs (>25s) chạy nền sau khi response trả về.exo_data_cutoff_blend_partition (cost + AdMob/Apple revenue, grain app/date/country) và exo_data_cohort_blend_partition (cost + MMP, grain thêm network/campaign). Run via node run_cost_revenue_blend.js today.SUM(developer_proceeds × units) thay vì SUM(developer_proceeds).exo_data_facebook_partition (with country) — không còn rows NULL country.run_blend_data.js (trước đó cộng raw VND+USD → số sai ~12,000×).run_blend_data_new.js (duplicate 99% với run_blend_data.js).convertCampaignNametoAppKey + convertAppIdtoAppKey đọc cache từ BQ exo_app_mapping thay vì hardcode switch (~200 cases trong constants.js).admob_app_id vào exo_app_mapping — team thêm app mới qua node manage_mapping.js set … không cần deploy code.fetchAdjustData.js, run_adjust.js, table exo_data_adjust_partition.run_mmp_blend.js → exo_data_mmp_blend_partition (AppMetrica + Adjust với network/campaign canonical).exo_app_mapping thay thế app.csv làm source-of-truth cho mapping app_key.database_test + run_test.sh + bootstrap_test.js.