สินค้า
เมนูสินค้าเป็น inventory lookup ตามสาขา ใช้ร่วมกับใบสั่งขาย ใบจองราคา งานโอนสินค้า รับสินค้าเข้า และคืนสินค้า เมื่อเปิดจากเมนูสินค้า ทั้ง Mobile และ POS Terminal เป็นหน้า lookup แบบ read-only เหมือนกัน Mobile จะแสดงฟอร์มเพิ่มเข้าใบสั่งขายเฉพาะเมื่อ ProductList/ProductDetail ถูกเปิดจาก flow ใบสั่งขายพร้อม orderId หรือ draftOrder; ส่วน POS Terminal เมนูสินค้าปกติอ้างอิงพฤติกรรม standalone ของ Mobile และไม่แสดงฟอร์มเพิ่มสินค้า
สำหรับ developer หน้านี้สำคัญเพราะแสดง shape ของสินค้าแบบเดียวกับที่เอกสารอื่นใช้ต่อ เช่น รหัสสินค้า ชื่อสินค้า คลัง สต๊อก SAP สต๊อกจอง สต๊อกขายได้ ราคา UOM การคุมน้ำหนัก บริษัท สี และ step price
ทางเข้าในแอป
| App | Target |
|---|---|
| Mobile | ProductList -> ProductDetail |
| POS Terminal | product -> ProductDetailForm |
Workflow ผู้ใช้
Data และ API Flow
| เรื่อง | รายละเอียด |
|---|---|
| Branch context | ทั้งสองแอปเรียก getProductBranchContext() และสาขาที่เลือกต้องมี whsGrpCode; ถ้าไม่มีจะขึ้น error ไม่พบข้อมูลสาขาสำหรับสินค้า |
| Inventory refresh | syncProductInventory(context, page) สั่ง refresh inventory จาก MST และยิง promotion sync ไป Firebase แบบ fire-and-forget |
| API สาขาหลัก | ถ้า branchCode === whsGrpCode จะเรียก /products?...&whGrpCode={whsGrpCode} |
| API สาขาย่อย/non-main | ถ้า branch และ warehouse group ไม่ตรงกัน จะเรียก /products/sub-inventories?...&whGrpCode={branchCode}&whsCode={whsGrpCode} |
| Firebase collection | อ่านสินค้า inventory จาก ${whsGrpCode}-inventories |
| Warehouse codes | ทั้งสองแอปใช้ whsGrpCode, PC-{whsGrpCode}, และ NF-{whsGrpCode} |
| Filter รายการ | ตัด row ที่ price <= 0, กัน itemCode ซ้ำ และ list/detail ต้องมี itemCode ที่ใช้ได้ |
| Step price | Detail โหลด getProductStepPrice({ itemCode, uomCode }) เฉพาะเมื่อ stepPriceFlag เป็นจริง และ flow ขาย/จองราคานำแนวคิดเดียวกันไปใช้เมื่อจำนวนเปลี่ยน |
| Dummy products | บางเอกสารรวม dummy products สำหรับงานเฉพาะทาง แต่เมนูสินค้าหลักนี้เป็น inventory-product lookup |
พฤติกรรมหน้า List
| เรื่อง | Mobile | POS Terminal |
|---|---|---|
| วิธีโหลด | เรียก syncProductInventory() ครั้งแรก แล้ว subscribe Firestore ผ่าน subscribeProductInventory() | เรียก getProductSplitList({ page, search, bookingOnly }) และ append page โดย merge id ซ้ำ |
| Search | ค้นใน memory ด้วย itemName และ itemCode | service ค้นเหมือน Mobile คือใช้เฉพาะ itemName และ itemCode |
| Tab | ทั้งหมด และ ติดจอง; tab ติดจองคงไว้เฉพาะสินค้า booking > 0 | มี tab เดียวกัน; ติดจอง ส่ง bookingOnly: true เข้า split list service |
| Summary | header แสดงจำนวนสินค้าทั้งหมดและจำนวนสินค้าที่ติดจอง | split list ใช้ count ของ pane กลาง และรองรับ load more |
| Refresh | pull-to-refresh เรียก syncProductInventory(branchContext) | pull-to-refresh และ reload เมนูเรียก split list แบบแบ่งหน้าอีกครั้ง |
| Empty state | แสดงไม่พบรายการหลังได้ snapshot แรกแล้ว | ใช้ loading/error/empty state กลางของ split-pane |
พฤติกรรม Detail และ Action
| Action | Mobile | POS Terminal |
|---|---|---|
| เปิด detail | navigate ไป ProductDetail พร้อม productId, itemCode, itemName, stepPriceFlag, uomCode และ sales-order context ถ้ามี | HomeScreen เรียก getProductSplitDetail(item), โหลด detail ด้วย itemCode, แล้วส่ง detail.raw เข้า ProductDetailForm |
| Refresh detail | pull-to-refresh โหลด getProductInventoryDetail(context, productId) และ step price ถ้าจำเป็น | detail refresh จากการเลือก/reload pane; step price ถูกโหลดก่อนส่ง raw record เข้า ProductDetailForm |
| เพิ่มเข้าใบสั่งขาย | แสดงเฉพาะเมื่อ ProductList/ProductDetail ถูกเปิดมาพร้อม orderId หรือ draftOrder; validate จำนวน, สร้าง order ก่อนถ้ามีแค่ draftOrder, แล้วเรียก createOrderItem() | ไม่แสดงจากเมนูสินค้าปกติ เพื่อให้ตรงกับ standalone Mobile behavior การเพิ่มสินค้าใน terminal อยู่ใน product picker ของ flow ใบสั่งขาย |
| เงื่อนไขจำนวน | จำนวนต้องมากกว่า 0 | จำนวนต้องมากกว่า 0 |
| เงื่อนไขน้ำหนัก | สินค้า weight-base เพิ่มหรือแก้ในใบสั่งขายโดยมีน้ำหนัก 0 ได้; ระบบจะตรวจน้ำหนักอีกครั้งเมื่อสร้างใบแจ้งหนี้ | เงื่อนไขเดียวกัน: สินค้า weight-base บันทึกน้ำหนัก 0 ได้ แต่สร้างใบแจ้งหนี้ไม่ได้จนกว่าน้ำหนักจะไม่เท่ากับ 0 |
| ราคา | ผู้ใช้แก้ราคาในฟอร์มเพิ่มสินค้าได้ โดย default จาก price ของสินค้า | ผู้ใช้แก้ราคาในฟอร์มเพิ่มสินค้าได้ โดย default จาก price ของสินค้า |
| Preview ยอด | weight-base: qty * price * weight; ไม่ใช่ weight-base: qty * price | คำนวณเหมือนกัน |
| ขอบเขต mutation | เมนูสินค้าปกติไม่แก้ product master, จำนวน stock หรือ item ใบสั่งขาย ส่วน add action ของ Mobile เป็นบริบทของ route ใบสั่งขาย ไม่ใช่เมนูสินค้าปกติ | ขอบเขต standalone เดียวกัน |
Field สำคัญสำหรับ Developer
| Field | ใช้ทำอะไร |
|---|---|
id | Firestore document ID ที่ mobile ใช้เปิด detail; terminal detail โหลดข้อมูลล่าสุดด้วย itemCode |
itemCode / itemName | identity หลักของสินค้าใน list, detail และ payload item ทุกเอกสาร |
whsCode / warehouseCode | จำเป็นเมื่อเพิ่มสินค้าในใบสั่งขาย ใบจองราคา โอนสินค้า และรับสินค้าเข้า ถ้าขาดมักทำให้ downstream screen บันทึกไม่ได้ |
price, salUomCode, salUomEntry | ใช้สร้างราคาและ UOM ของ line item |
onHand, booking, available stock | UI แสดง stock SAP, stock จอง และ stock ขายได้จาก onHand - booking; เอกสารอื่นใช้ประกอบ validation stock/booking |
weight, weightBaseFlag | กำหนดว่าสินค้าขายเป็นหน่วยนับหรือคุมน้ำหนัก สินค้า weight-base มีน้ำหนัก 0 ในใบสั่งขายได้ แต่ต้องระบุน้ำหนักไม่เท่ากับ 0 ก่อนสร้างใบแจ้งหนี้ |
stepPriceFlag / step price rows | ใช้แสดง tiered price และใช้เลือกราคาใน sales/booking เมื่อจำนวนเปลี่ยน |
color | ใช้ทำสี card/list/detail เพื่อแยกกลุ่มสินค้า ถ้า hex ไม่ถูกต้องจะ fallback เป็น accent ของ theme |
| company fields | เอกสารบางหน้าอาจใช้ field บริษัทเพื่อกรองสินค้าที่เข้ากัน แต่ search ของเมนูสินค้าตรงกับ Mobile คือใช้ชื่อ/รหัสสินค้า |
สิทธิ์และ Guard
| เรื่อง | รายละเอียด |
|---|---|
| Mobile menu permission | home menu ใช้ชุด deptCode เดียวกับ terminal: -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 20, 22, 23, 27, 28, 13, 16 |
| Terminal menu key | product |
| Terminal menu permission | MENU_PRODUCT ใช้ -2, 1, 2, 3, 4, 7, 29, 21, 8, 9, 12, 20, 22, 23, 27, 28, 13, 16 |
| เงื่อนไขสาขา | เมนูสินค้าไม่ได้บังคับเฉพาะสาขาหลัก แต่ต้องมี selected branch product context และ whsGrpCode |
| สิทธิ์เพิ่มเข้าใบสั่งขาย | Mobile แสดงฟอร์มเพิ่มสินค้าเฉพาะเมื่อเปิดมาจาก flow ใบสั่งขายพร้อม orderId หรือ draftOrder ส่วน POS Terminal ไม่เปิด action นี้จากเมนูสินค้าปกติ การเพิ่ม item อยู่ใน flow ใบสั่งขาย |
| Product mutation | เมนูนี้ไม่มี create/update/delete product master การเปลี่ยนจำนวนสินค้าเกิดผ่าน document flow อื่นเท่านั้น |
Mobile vs POS Terminal
| เรื่อง | Mobile | POS Terminal |
|---|---|---|
| Navigation | product stack เฉพาะเมนู | HomeScreen split-pane menu key product |
| List | list จาก snapshot พร้อม search, tab, summary count, pull refresh | split list แบบแบ่งหน้า พร้อม search, tab, load more, pull refresh |
| Detail | detail เต็มจอ มี pull refresh และ bottom add form เมื่อมีบริบท | detail ด้านขวา มี add card เมื่อมีสิทธิ์ |
| Add action | เฉพาะเมื่อเปิดจาก Mobile sales-order route context | ไม่เปิดจากเมนูสินค้าปกติ |
| Reuse | product picker components ใน document stacks | shared product picker/detail patterns ใน HomeScreen |
Developer Handoff Map
| Area | Code |
|---|---|
| Mobile list/detail | apps/mobile/src/screens/productStack/productListScreen, productDetailScreen |
| Mobile home permission | apps/mobile/src/screens/homeScreen/index.tsx -> MENU_PRODUCT |
| Terminal detail | apps/pos-terminal/src/screens/HomeScreen/ProductDetailForm.tsx |
| Terminal home wiring | apps/pos-terminal/src/screens/HomeScreen/index.tsx, DetailPane.tsx, ListPane.tsx |
| Terminal split data | apps/pos-terminal/src/services/splitMenuData.ts -> getProductSplitList, getProductSplitDetail |
| Product service | apps/*/src/services/products.ts |
| Picker reuse | OrderFlowShared, BookingProductPicker, inventory transfer forms |