diff --git a/SHOPEE_MATCH_AND_MAP_LEDGER.md b/SHOPEE_MATCH_AND_MAP_LEDGER.md new file mode 100644 index 0000000..a8002fc --- /dev/null +++ b/SHOPEE_MATCH_AND_MAP_LEDGER.md @@ -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 รายการ)* | ... | diff --git a/scripts/match_and_map_shopee_products.ts b/scripts/match_and_map_shopee_products.ts new file mode 100644 index 0000000..8c21c60 --- /dev/null +++ b/scripts/match_and_map_shopee_products.ts @@ -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 = { + // 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(); + 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);