This commit is contained in:
Your Name committed 2026-10-18 14:25:29 +07:00
1 parent f49776f9cb
commit fdbbaea021
2 files changed
+468

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
# Shopee Marketplace Product Matching & Mapping Ledger
* **Generated At:** 2026-10-18T07:17:25.420Z
* **Target Server:** `https://api.homaroma.shop/api` (Production)
* **Total Shopee Products:** 253
* **Successfully Mapped:** 217 (85.8%)
* **Held / Requires Decision:** 36 (14.2%)
## 1. Held / Unmapped Items (ต้องตัดสินใจเพิ่มเติม)
| ID | ชื่อสินค้า Shopee | Variation | ราคาเฉลี่ย | สาเหตุที่ Hold |
|:---|:---|:---|:---|:---|
| 619 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น | ฿77.13 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
| 649 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น,30 ml | ฿89.00 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
| 651 | น้ำมันหอมระเหย สำหรับ เครื่องพ่นไอน้ำ เครื่องพ่นอโรม่า ขนาด 30 ml | เมลอน ญี่ปุ่น,120 ml | ฿190.00 | ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ) |
| 804 | น้ำมันหอมระเหย กลิ่นดอกโมก สำหรับเครื่องพ่นไอน้ำ ช่วยปรับอากาศ | - | ฿180.00 | ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation |
| 664 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ไทยสปา | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 698 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ซากุระ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 767 | HOM AROMA กลิ่นลาเวนเดอร์ lavender น้ำมันหอมระหย ออยโอโซน สำหรับเครื่องพ่น เครื่องพ่นไอน้ำ | - | ฿88.00 | ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation |
| 692 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ยูคาลิปตัส | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 687 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แคนตาลูป | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 730 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลาเวนเดอร์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 705 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แซนด้าวูด | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 704 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เฟรชลาเวนเดอร์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 691 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | โอเชี่ยน | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 693 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | โรสเมรี่ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 686 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เมล่อนญี่ปุ่น | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 761 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แอปเปิ้ลแดง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 741 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เปเปอร์มิ้นต์ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 760 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะกรูด | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 756 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะลิ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 755 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ตะไตร้ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 759 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | เซเว่นซี | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 754 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | กุหลาบชมพู | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 753 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกปีบ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 752 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | มะนาว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 751 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ส้ม | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 758 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกแก้ว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 757 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกอัญชัน | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 750 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลิลลี่ | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 749 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | วนิลลา | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 747 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | กุหลาบแดง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 748 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ผลไม้รวม | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 746 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | แอปเปิ้ลเขียว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 745 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | หมากฝรั่ง | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 744 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกโมก | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 743 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ลีลาวดี | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
| 742 | oil ozone ขนาด 10 ml น้ำมันหอมระเหย น้ำมันใส่เครื่องพ่นไอน้ำ ออยโอโซน aroma oil อโรม่าออย | ดอกบัว | ฿25.00 | ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml) |
## 2. Sample of Successfully Mapped Items (ตัวอย่างที่เชื่อมโยงสำเร็จ)
| ID | Shopee SKU | กลิ่น | ขนาด | Master SKU | Master Variant | มี BOM หรือยัง |
|:---|:---|:---|:---|:---|:---|:---|
| 563 | M1015 | เปเปอร์มิ้น | 120ml | `8-1-120ML` | เปเปอร์มิ้น Oil Ozone (120ml) [8-1-120ML] | ✓ มี BOM แล้ว |
| 575 | M1002 | ตะไคร้ | 120ml | `1-1-120ML` | ตะไคร้ Oil Ozone (120ml) [1-1-120ML] | ✓ มี BOM แล้ว |
| 601 | 30-ml- | ลาเวนเดอร์ | 30ml | `2-1-30ML` | ลาเวนเดอร์ Oil Ozone (30ml) [2-1-30ML] | ✓ มี BOM แล้ว |
| 589 | 30-ml- | ยูคาลิปตัส | 30ml | `6-1-30ML` | ยูคาลิปตัส Oil Ozone (30ml) [6-1-30ML] | ✓ มี BOM แล้ว |
| 572 | M1015 | เปเปอร์มิ้น | 30ml | `8-1-30ML` | เปเปอร์มิ้น Oil Ozone (30ml) [8-1-30ML] | ✓ มี BOM แล้ว |
| 591 | 30-ml- | โอเชี่ยน | 30ml | `9-1-30ML` | โอเชี่ยน Oil Ozone (30ml) [9-1-30ML] | ✓ มี BOM แล้ว |
| 608 | 30-ml- | มะลิ | 30ml | `5-1-30ML` | มะลิ Oil Ozone (30ml) [5-1-30ML] | ✓ มี BOM แล้ว |
| 627 | M1001 | ลาเวนเดอร์ | 120ml | `2-1-120ML` | ลาเวนเดอร์ Oil Ozone (120ml) [2-1-120ML] | ✓ มี BOM แล้ว |
| 590 | 30-ml- | ซากุระ | 30ml | `4-1-30ML` | ซากุระ Oil Ozone (30ml) [4-1-30ML] | ✓ มี BOM แล้ว |
| 559 | M1005 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
| 611 | 30-ml- | ตะไคร้ | 30ml | `1-1-30ML` | ตะไคร้ Oil Ozone (30ml) [1-1-30ML] | ✓ มี BOM แล้ว |
| 557 | M1015 | เปเปอร์มิ้น | 60ml | `8-1-60ML` | เปเปอร์มิ้น Oil Ozone (60ml) [8-1-60ML] | ✓ มี BOM แล้ว |
| 604 | 30-ml- | เปเปอร์มิ้น | 30ml | `8-1-30ML` | เปเปอร์มิ้น Oil Ozone (30ml) [8-1-30ML] | ✓ มี BOM แล้ว |
| 607 | 30-ml- | ลาเวนเดอร์ | 30ml | `2-1-30ML` | ลาเวนเดอร์ Oil Ozone (30ml) [2-1-30ML] | ✓ มี BOM แล้ว |
| 606 | 30-ml- | กุหลาบแดง | 30ml | `22-1-30ML` | กุหลาบแดง Oil Ozone (30ml) [22-1-30ML] | ✓ มี BOM แล้ว |
| 602 | 30-ml- | ดอกโมก | 30ml | `12-1-30ML` | ดอกโมก Oil Ozone (30ml) [12-1-30ML] | ✓ มี BOM แล้ว |
| 636 | M1015 | เปเปอร์มิ้น | 85ml | `8-1-85ML` | เปเปอร์มิ้น Oil Ozone (85ml) [8-1-85ML] | ✓ มี BOM แล้ว |
| 612 | 30-ml- | ผลไม้รวม | 30ml | `24-1-30ML` | ผลไม้รวม Oil Ozone (30ml) [24-1-30ML] | ✓ มี BOM แล้ว |
| 620 | 30-ml- | แคนตาลูป | 30ml | `16-1-30ML` | แคนตาลูป Oil Ozone (30ml) [16-1-30ML] | ✓ มี BOM แล้ว |
| 603 | M1004 | มะลิ | 120ml | `5-1-120ML` | มะลิ Oil Ozone (120ml) [5-1-120ML] | ✓ มี BOM แล้ว |
| 249 | M1001 | ลาเวนเดอร์ | 120ml | `2-1-120ML` | ลาเวนเดอร์ Oil Ozone (120ml) [2-1-120ML] | ✓ มี BOM แล้ว |
| 615 | 30-ml- | วานิลลา | 30ml | `3-1-30ML` | วานิลลา Oil Ozone (30ml) [3-1-30ML] | ✓ มี BOM แล้ว |
| 558 | M1003 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
| 616 | 30-ml- | ดอกแก้ว | 30ml | `11-1-30ML` | ดอกแก้ว Oil Ozone (30ml) [11-1-30ML] | ✓ มี BOM แล้ว |
| 678 | 1652052234 | โอโซน | 120ml | `29-1-120ML` | โอโซน Oil Ozone (120ml) [29-1-120ML] | ✓ มี BOM แล้ว |
| 625 | 30-ml- | ดอกราตรี | 30ml | `39-1-30ML` | ดอกราตรี Oil Ozone (30ml) [39-1-30ML] | ✓ มี BOM แล้ว |
| 626 | 30-ml- | กลิ่นองุ่น | 30ml | `45-1-30ML` | กลิ่นองุ่น Oil Ozone (30ml) [45-1-30ML] | ✓ มี BOM แล้ว |
| 734 | 1652058083 | โอโซน | 60ml | `29-1-60ML` | โอโซน Oil Ozone (60ml) [29-1-60ML] | ✓ มี BOM แล้ว |
| 622 | 30-ml- | แอปเปิ้ลเขียว | 30ml | `18-1-30ML` | แอปเปิ้ลเขียว Oil Ozone (30ml) [18-1-30ML] | ✓ มี BOM แล้ว |
| 610 | 30-ml- | มะนาว | 30ml | `15-1-30ML` | มะนาว Oil Ozone (30ml) [15-1-30ML] | ✓ มี BOM แล้ว |
| ... | ... | ... | ... | ... | *(แสดง 30 จากทั้งหมด 217 รายการ)* | ... |
+384
View File
@@ -0,0 +1,384 @@
import * as fs from "fs";
import * as path from "path";
const PROD_API = "https://api.homaroma.shop/api";
interface MasterVariant {
id: number;
sku: string;
barcode: string;
price: number;
stock: number;
optionsStr: string;
productName: string;
categoryName: string;
brandName: string;
bomCost: number;
displayName: string;
}
interface MarketplaceProduct {
id: number;
channel: string;
shop: string;
marketplaceSku: string;
productName: string;
variation: string;
avgPrice: number;
totalSoldQty: number;
totalRevenue: number;
firstSoldAt: string;
lastSoldAt: string;
masterVariantId: number | null;
}
// Scent aliases dictionary mapping to Master Catalog Product Names (without "Oil Ozone")
const SCENT_ALIASES: Record<string, string> = {
// Peppermint
"เปปเปอร์มินต์": "เปเปอร์มิ้น",
"เปปเปอร์มิ้นท์": "เปเปอร์มิ้น",
"เปปเปอร์มิ้น": "เปเปอร์มิ้น",
"เปเปอร์มินต์": "เปเปอร์มิ้น",
"เปเปอร์​มิ้น​": "เปเปอร์มิ้น",
"เปเปอร์มิ้น": "เปเปอร์มิ้น",
"pepermint": "เปเปอร์มิ้น",
"peppermint": "เปเปอร์มิ้น",
// Lemongrass
"ตะไคร้บ้าน": "ตะไคร้",
"ตะไคร้": "ตะไคร้",
"ตะไตร้": "ตะไคร้",
"lemongrass": "ตะไคร้",
// Lavender
"ลา​เวนเดอร์​": "ลาเวนเดอร์",
"ลาเวนเดอร์": "ลาเวนเดอร์",
"lavender": "ลาเวนเดอร์",
"เฟรซ ลาเวนเดอร์": "เฟรชลาเวนเดอร์",
"เฟรชลาเวนเดอร์": "เฟรชลาเวนเดอร์",
"เฟรช ลาเวนเดอร์": "เฟรชลาเวนเดอร์",
"fresh lavender": "เฟรชลาเวนเดอร์",
// Sandalwood
"แซนดัลวูด": "แซนด้าวูส",
"แซนด้าวูด": "แซนด้าวูส",
"แซนดัลวู้ด": "แซนด้าวูส",
"แซนด้าวูส": "แซนด้าวูส",
"sandalwood": "แซนด้าวูส",
// Rose
"กุหลาบแดง": "กุหลาบแดง",
"กุหลาบ​แดง": "กุหลาบแดง",
"กุหลาบ": "กุหลาบแดง",
"red rose": "กุหลาบแดง",
// Jasmine
"มะลิซ้อน": "มะลิ",
"ดอกมะลิ": "มะลิ",
"มะลิ": "มะลิ",
"jasmine": "มะลิ",
// Fruits
"ลูกพีช": "กลิ่นลูกพีช",
"พีช": "กลิ่นลูกพีช",
"peach": "กลิ่นลูกพีช",
"องุ่น": "กลิ่นองุ่น",
"grape": "กลิ่นองุ่น",
"น้ำนม": "กลิ่นน้ำนม",
"นม": "กลิ่นน้ำนม",
"milk": "กลิ่นน้ำนม",
"แคนตาลูป​": "แคนตาลูป",
"แคนตาลูป": "แคนตาลูป",
"cantaloupe": "แคนตาลูป",
"ส้ม": "ส้ม",
"orange": "ส้ม",
"มะกรูด": "มะกรูด",
"bergamot": "มะกรูด",
"ผลไม้รวม": "ผลไม้รวม",
"mixed fruit": "ผลไม้รวม",
"แอปเปิ้ล​เขียว": "แอปเปิ้ลเขียว",
"แอปเปิ้ลเขียว": "แอปเปิ้ลเขียว",
"green apple": "แอปเปิ้ลเขียว",
"แอปเปื้ลแดง": "แอปเปิ้ลแดง",
"แอปเปิ้ลแดง": "แอปเปิ้ลแดง",
"red apple": "แอปเปิ้ลแดง",
"มะนาว": "มะนาว",
"lemon": "มะนาว",
"lime": "มะนาว",
"ทับทิม": "ทับทิม",
"pomegranate": "ทับทิม",
"มะละกอสุก": "มะละกอสุก",
"มะละกอ": "มะละกอสุก",
"papaya": "มะละกอสุก",
"กล้วย": "กล้วย",
"banana": "กล้วย",
// Ocean / Ozone
"โอเชี่ยน": "โอเชี่ยน",
"มหาสมุทร": "โอเชี่ยน",
"ocean": "โอเชี่ยน",
"โอโซน": "โอโซน",
"ozone": "โอโซน",
// Thai Flowers
"ดอกราตรี": "ดอกราตรี",
"ราตรี": "ดอกราตรี",
"การเวก": "ดอกการะเวก",
"ดอกการะเวก": "ดอกการะเวก",
"ดอกอัญชัน": "อัญชัน",
"อัญชัน": "อัญชัน",
"butterfly pea": "อัญชัน",
"ดอกบัวหลวง": "ดอกบัว",
"ดอกบัว": "ดอกบัว",
"บัว": "ดอกบัว",
"lotus": "ดอกบัว",
"ดอกปีบ": "ดอกปีบ",
"ปีบ": "ดอกปีบ",
"ดอกโมก": "ดอกโมก",
"โมก": "ดอกโมก",
"ดอกแก้ว": "ดอกแก้ว",
"แก้ว": "ดอกแก้ว",
"ดอกจำปา": "ดอกจำปา",
"จำปา": "ดอกจำปา",
"ดอกพิกุล": "ดอกพิกุล",
"พิกุล": "ดอกพิกุล",
"ดอกพุด": "ดอกพุด",
"พุด": "ดอกพุด",
"ดอกชมนาด": "ดอกชมนาด",
"ชมนาด": "ดอกชมนาด",
"ดอกราชาวดี": "ดอกราชาวดี",
"ราชาวดี": "ดอกราชาวดี",
"ดอกลำดวน": "ดอกลำดวน",
"ลำดวน": "ดอกลำดวน",
"ลีลาวดี": "ลีลาวดี",
"frangipani": "ลีลาวดี",
"ลิลลี่": "ดอกลิลลี่",
"ดอกลิลลี่": "ดอกลิลลี่",
"lilly": "ดอกลิลลี่",
"lily": "ดอกลิลลี่",
// Herbs & Others
"ยูคาลิปตัส": "ยูคาลิปตัส",
"ยูคา": "ยูคาลิปตัส",
"eucalyptus": "ยูคาลิปตัส",
"วนิลา": "วานิลลา",
"วนิลลา": "วานิลลา",
"วานิลลา": "วานิลลา",
"vanilla": "วานิลลา",
"ซากุระ": "ซากุระ",
"sakura": "ซากุระ",
"เซเว่นซี": "เซเว่นซี",
"seven sea": "เซเว่นซี",
"7 sea": "เซเว่นซี",
"หมากฝรั่ง": "หมากฝรั่ง",
"bubble gum": "หมากฝรั่ง",
"ไทยสปา": "ไทยสปา",
"thai spa": "ไทยสปา",
"กาแฟ": "กาแฟ",
"coffee": "กาแฟ",
"โรสเมรี่": "โรสเมรี่",
"โรสแมรี่": "โรสเมรี่",
"rosemary": "โรสเมรี่",
};
function cleanSize(str: string): string | null {
if (!str) return null;
const s = str.toLowerCase().replace(/\s+/g, "");
if (s.includes("120ml") || s === "120ml") return "120ml";
if (s.includes("85ml") || s === "85ml") return "85ml";
if (s.includes("60ml") || s === "60ml") return "60ml";
if (s.includes("30ml") || s === "30ml") return "30ml";
if (s.includes("25ml") || s === "25ml") return "25ml";
return null;
}
function findScent(text: string): string | null {
if (!text) return null;
const lower = text.toLowerCase();
for (const [alias, canonical] of Object.entries(SCENT_ALIASES)) {
if (lower.includes(alias.toLowerCase())) {
return canonical;
}
}
return null;
}
async function main() {
console.log("== Shopee Product Matcher & Mapper ==");
// 1. Fetch Master Variants
console.log("Fetching Master Product Variants...");
const varRes = await fetch(`${PROD_API}/tools/marketplace-products/master-variants`);
const varData = (await varRes.json()) as { variants: MasterVariant[] };
const variants = varData.variants || [];
console.log(`Loaded ${variants.length} master variants.`);
// Lookup map: `${canonicalScent}:::${normalizedSize}` -> MasterVariant
const masterLookup = new Map<string, MasterVariant>();
for (const v of variants) {
const cleanName = v.productName.replace(/Oil Ozone/gi, "").trim();
const size = v.optionsStr;
const key = `${cleanName}:::${size}`.toLowerCase();
masterLookup.set(key, v);
}
// 3. Fetch Shopee Marketplace Products
console.log("Fetching Shopee Marketplace Products...");
const pRes = await fetch(`${PROD_API}/tools/marketplace-products?channel=shopee&status=all&limit=400`);
const pData = (await pRes.json()) as { items: MarketplaceProduct[]; total: number };
const items = pData.items || [];
console.log(`Loaded ${items.length} Shopee products.`);
const matchedMappings: { marketplaceProductId: number; masterVariantId: number; meta: any }[] = [];
const heldItems: { id: number; name: string; variation: string; price: number; reason: string; detectedScent?: string | null; detectedSize?: string | null }[] = [];
for (const item of items) {
// If it's a 10ml product, hold it (Master Catalog does not support 10ml)
if (item.productName.toLowerCase().includes("10 ml") || (item.variation || "").includes("10 ml")) {
heldItems.push({
id: item.id,
name: item.productName,
variation: item.variation,
price: item.avgPrice,
reason: "ขนาด 10 ml (Master Catalog ปัจจุบันยังไม่มีขนาด 10ml)",
detectedSize: "10ml",
});
continue;
}
const vStr = item.variation || "";
const pStr = item.productName || "";
let scent: string | null = null;
let size: string | null = null;
// Handle comma-separated variation e.g. 'ตะไคร้บ้าน,120 ml'
if (vStr.includes(",")) {
const parts = vStr.split(",");
for (const part of parts) {
if (!size) size = cleanSize(part);
if (!scent) scent = findScent(part);
}
}
if (!size) size = cleanSize(vStr);
if (!scent) scent = findScent(vStr);
if (!size) size = cleanSize(pStr);
if (!scent) scent = findScent(pStr);
if (!size) {
heldItems.push({
id: item.id,
name: item.productName,
variation: item.variation,
price: item.avgPrice,
reason: "ไม่สามารถระบุขนาดบรรจุ (Size) ได้ชัดเจนจากชื่อสินค้าหรือ Variation",
detectedScent: scent,
detectedSize: null,
});
continue;
}
if (!scent) {
heldItems.push({
id: item.id,
name: item.productName,
variation: item.variation,
price: item.avgPrice,
reason: "ไม่พบชื่อกลิ่นที่ตรงกับ Master Catalog (เช่น เมลอน หรือกลิ่นที่ยังไม่มีในระบบ)",
detectedScent: null,
detectedSize: size,
});
continue;
}
const key = `${scent}:::${size}`.toLowerCase();
const master = masterLookup.get(key);
if (!master) {
heldItems.push({
id: item.id,
name: item.productName,
variation: item.variation,
price: item.avgPrice,
reason: `พบกลิ่น '${scent}' ขนาด '${size}' แต่ไม่มี Master Variant ขนาดนี้ในแคตตาล็อก`,
detectedScent: scent,
detectedSize: size,
});
continue;
}
matchedMappings.push({
marketplaceProductId: item.id,
masterVariantId: master.id,
meta: {
shopeeSku: item.marketplaceSku,
shopeeName: item.productName,
shopeeVar: item.variation,
masterSku: master.sku,
masterName: master.displayName,
scent,
size,
hasBom: master.bomCost > 0,
},
});
}
console.log(`\n========================================`);
console.log(`Total Shopee Products : ${items.length}`);
console.log(`High Confidence Match : ${matchedMappings.length} (${((matchedMappings.length / items.length) * 100).toFixed(1)}%)`);
console.log(`Held / Needs Decision : ${heldItems.length} (${((heldItems.length / items.length) * 100).toFixed(1)}%)`);
console.log(`========================================\n`);
// 4. Execute Bulk Mapping on Production
if (matchedMappings.length > 0) {
console.log(`Applying ${matchedMappings.length} bulk mappings on production...`);
const bulkPayload = {
mappings: matchedMappings.map((m) => ({
marketplaceProductId: m.marketplaceProductId,
masterVariantId: m.masterVariantId,
})),
};
const mapRes = await fetch(`${PROD_API}/tools/marketplace-products/bulk-map`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(bulkPayload),
});
const mapResult = await mapRes.json();
console.log("Bulk Map API Response:", mapResult);
}
// 5. Generate Markdown Report
let report = `# Shopee Marketplace Product Matching & Mapping Ledger\n\n`;
report += `* **Generated At:** ${new Date().toISOString()}\n`;
report += `* **Target Server:** \`${PROD_API}\` (Production)\n`;
report += `* **Total Shopee Products:** ${items.length}\n`;
report += `* **Successfully Mapped:** ${matchedMappings.length} (${((matchedMappings.length / items.length) * 100).toFixed(1)}%)\n`;
report += `* **Held / Requires Decision:** ${heldItems.length} (${((heldItems.length / items.length) * 100).toFixed(1)}%)\n\n`;
report += `## 1. Held / Unmapped Items (ต้องตัดสินใจเพิ่มเติม)\n\n`;
report += `| ID | ชื่อสินค้า Shopee | Variation | ราคาเฉลี่ย | สาเหตุที่ Hold |\n`;
report += `|:---|:---|:---|:---|:---|\n`;
for (const h of heldItems) {
report += `| ${h.id} | ${h.name.replace(/\|/g, "-")} | ${h.variation || "-"} | ฿${h.price.toFixed(2)} | ${h.reason} |\n`;
}
report += `\n## 2. Sample of Successfully Mapped Items (ตัวอย่างที่เชื่อมโยงสำเร็จ)\n\n`;
report += `| ID | Shopee SKU | กลิ่น | ขนาด | Master SKU | Master Variant | มี BOM หรือยัง |\n`;
report += `|:---|:---|:---|:---|:---|:---|:---|\n`;
for (const m of matchedMappings.slice(0, 30)) {
report += `| ${m.marketplaceProductId} | ${m.meta.shopeeSku || "-"} | ${m.meta.scent} | ${m.meta.size} | \`${m.meta.masterSku}\` | ${m.meta.masterName} | ${m.meta.hasBom ? "✓ มี BOM แล้ว" : "รอสร้าง BOM"} |\n`;
}
if (matchedMappings.length > 30) {
report += `| ... | ... | ... | ... | ... | *(แสดง 30 จากทั้งหมด ${matchedMappings.length} รายการ)* | ... |\n`;
}
const outPath = path.resolve(process.cwd(), "SHOPEE_MATCH_AND_MAP_LEDGER.md");
fs.writeFileSync(outPath, report, "utf8");
console.log(`\nSaved ledger report to: ${outPath}`);
}
main().catch(console.error);