ใบจองราคา
เมนู ใบจองราคา ใช้จองราคา/จองสินค้าให้ลูกค้าก่อนแปลงเป็นใบสั่งขายจริง โครงโดยรวมคล้าย ใบสั่งขาย เพราะใช้ลูกค้า, สินค้า, คลัง, step price, น้ำหนัก, และยอดรวมแบบเดียวกัน แต่ต่างตรงที่ใบจองราคายังไม่ใช่ order ที่เดินต่อไป invoice/payment จนกว่าจะกดแปลงเป็นใบสั่งขาย
จุดพิเศษของเมนูนี้คือ field currentStepPrice ในระดับรายการสินค้า ระบบเก็บข้อมูล step price ณ ตอนที่ทำใบจองราคาไว้กับ item เพื่อให้ตอนแปลงเป็นใบสั่งขายหรือกลับมาแก้ไข item ระบบยังรู้ราคาขั้นบันไดที่เคยใช้กับรายการนั้น
ดู workflow หลักดู currentStepPriceดูการแปลงเป็นใบสั่งขาย
ทางเข้าในแอป
| แอป | ทางเข้า | ไฟล์/จุดทำงานหลัก |
|---|---|---|
| Mobile | Home -> BookingPriceList -> CustomerList mode booking -> BookingPriceDetail | bookingPriceListScreen, bookingPriceDetailScreen |
| POS Terminal | HomeScreen split-pane -> menu bookingPrice -> customer picker/detail panel | HomeScreen/index.tsx, BookingPriceDetailForm.tsx, BookingProductPicker.tsx |
Service และ endpoint
| งาน | Endpoint / service |
|---|---|
| โหลดรายการใบจองราคา | GET /booking-price ผ่าน getBookingPrices(...) |
| โหลดรายละเอียด | GET /booking-price/{bookingPriceId} |
| สร้าง header | POST /booking-price |
| อัปเดต header | PATCH /booking-price/{bookingPriceId} |
| ลบเอกสาร | DELETE /booking-price/{bookingPriceId} |
| เพิ่ม item | POST /booking-price/{bookingPriceId}/items |
| แก้ item | PATCH /booking-price/{bookingPriceId}/items/{itemId} |
| ลบ item | DELETE /booking-price/{bookingPriceId}/items/{itemId} |
| แปลงเป็นใบสั่งขาย | POST /booking-price/{bookingPriceId}/convert |
| โหลด step price | product step-price API ผ่าน getProductStepPrice(...) |
Workflow หลัก
รายการใบจองราคา
รายการโหลดด้วย getBookingPrices(...) โดยใช้ context ของสาขาที่เลือก:
branchIdจาก selected branchwhGrpCodeจาก selected warehouse grouppage,pageLimit,searchbookingPriceStatususerCode- sort เป็น
DESCตามCreatedAt
พฤติกรรมของ list:
- dedupe ด้วย
bookingPriceId,bookingPriceStorageId, หรือbookingPriceNo - มี search และ pagination
- สีของ card สื่อสถานะเอกสาร
- Mobile ใช้ floating plus เพื่อเริ่ม
CustomerListmodebooking - Terminal ใช้ split-pane list และปุ่มเพิ่มเพื่อเปิด customer picker
- วันที่สำหรับตัดสินว่าเอกสารแก้ได้หรือไม่ใช้ server business date จาก
GET /api/healthzเมื่อมี และ fallback เป็นวันที่เครื่อง
สร้าง draft จากลูกค้า
การสร้างใบจองราคาเริ่มจากเลือกลูกค้าเหมือนใบสั่งขาย แต่ mode คือ booking
field ที่ seed เข้ารายละเอียด:
| Field | แหล่งข้อมูล |
|---|---|
customerCode, customerName, phone1, phone2 | customer detail |
shippingType | customer / branch rule |
routeCode, routeName | address ของลูกค้า |
U_BFP_Latitude, U_BFP_Longitude | address ลูกค้า |
branchId, whGrpCode, whGrpName | branch storage |
userCode, userResponsible | user ปัจจุบัน |
bookingPriceStatus | เริ่มเป็น DRAFT |
Terminal มี parity note สำคัญ: draft จาก customer ใช้ address แรกของลูกค้าสำหรับ route/coordinate ตาม Mobile booking-price initialization ปัจจุบัน
เพิ่มและแก้สินค้า
การเพิ่มสินค้าในใบจองราคาคล้ายใบสั่งขาย:
- เปิด product picker
- ถ้าเลือกสินค้าที่มีอยู่แล้ว จะเข้า edit overlay แทนการเพิ่มซ้ำ
- validate จำนวนเป็นตัวเลขเต็มและมากกว่า
0 - เช็ค available stock จาก
onHand - bookingQty - ถ้าเป็นสินค้าน้ำหนัก ใช้
qty * price * weight - ถ้าเป็นสินค้าปกติ ใช้
qty * price - ถ้า product มี
stepPriceFlagและจำนวนเข้าเงื่อนไข จะโหลด step price ก่อนคำนวณราคา - persist ตามลำดับ: ensure/create booking header -> create/update item -> update totals -> reload detail
payload item สำคัญ:
| Field | ความหมาย |
|---|---|
productCode, productName | สินค้า |
qty, price, amount, weight, limitWeight | จำนวน ราคา ยอด และน้ำหนัก |
onHand, bookingQty | ใช้ validate stock |
uomCode, uomName, UOMCode, UOMName, UOMEntry | UOM |
whsCode, whGrpCode, whGrpName | คลังและกลุ่มคลัง |
companyCode, companyName, taxNo, vatGroup | บริษัท/ภาษี |
weightBaseFlag | สินค้าน้ำหนัก |
stepPriceFlag | สินค้า step price |
currentStepPrice | snapshot/fallback ของ step price ที่ใช้กับ item นี้ |
editPriceBy | user ที่แก้ราคา |
currentStepPrice
currentStepPrice เป็น field ระดับ item ที่ใช้เก็บข้อมูล step price ณ เวลาจองราคา โดยปกติจะเป็น JSON string ของ step price rows หรือ fallback JSON เมื่อไม่มีข้อมูล step price จาก API
รูปแบบ fallback ที่ระบบสร้าง:
[
{
From: 0,
Price: item.price,
BPLName: item.whGrpName || item.whsName || ''
}
]
พฤติกรรมสำคัญ:
- ตอนเพิ่ม item ใหม่ ระบบสร้าง
currentStepPriceจากbuildDefaultCurrentStepPrice(product)ถ้ายังไม่มี step price rows - ถ้าสินค้าเป็น
stepPriceFlagและจำนวนมากพอ ระบบโหลด step price จาก API แล้วเก็บ response เป็น JSON ในcurrentStepPrice - ตอนแก้ item เดิม ระบบ refresh inventory จาก Firestore ด้วย
productCodeก่อน เพื่อเอา stock/booking ล่าสุด - ถ้าเป็น step price item ระบบ reload step price จาก MST API อีกครั้ง แต่
currentStepPriceยังถูกเก็บเป็น fallback data ไม่ใช่ source ราคาเดียวเสมอไป - เมื่อแปลงเป็นใบสั่งขาย item ใน order จะมี
currentStepPriceต่อไปด้วย เพื่อให้หน้าใบสั่งขายรู้ว่า item นี้มาจาก booking/step price context
แปลงเป็นใบสั่งขาย
การแปลงเกิดจากปุ่ม แปลงเป็นใบสั่งขาย ใน detail:
หลัง convert:
- booking price เปลี่ยนเป็นสถานะ
DONE - detail แสดงว่าเอกสารนี้แปลงเป็นใบสั่งขายแล้ว
- Mobile ใช้
navigation.replace('OrderDetail', { orderId }) - Terminal สร้าง/open order detail ใน split-pane และ refresh booking list
- order detail จะถือว่า order มี source จาก
bookingPriceIdหรือbookingPriceStorageId
สิทธิ์และเงื่อนไข
| ส่วน | เงื่อนไข |
|---|---|
| เห็นเมนูใบจองราคา | MENU_BOOKING.has(deptCode) หรือ admin/full non-main warehouse access |
| dept ที่เห็นเมนู | -2, 1, 2, 3, 4, 7, 8, 9, 13, 16, 20, 21, 22, 23, 27, 29 |
| shortcut สร้างจาก Home | ใช้ชุดเดียวกับ create order shortcut (CAN_CREATE_ORDER) หรือ full non-main warehouse access |
| สร้างจาก list/detail | ต้องผ่าน canCreateSalesOrder |
| ลบใบจองจาก list | owner ของ DRAFT, หรือ DRAFT/NEW ที่ user เป็น admin, override user, dept 8, dept 22 |
| override user ลบใบจอง | 008045, 012521, 012508, 008051, 012388, 012428, 008060 |
| แก้ item ใน detail | ต้องเป็นวันเดียวกับวันที่สร้างเอกสาร และผ่าน owner/override ตามสถานะ |
| แปลงเป็นใบสั่งขาย | booking price มี id, status DRAFT, owner เป็น user ปัจจุบัน, และมีสินค้าอย่างน้อย 1 รายการ |
กฎแก้ไขตามสถานะ:
DRAFT: owner แก้ item ได้ในวันสร้างเอกสารNEW: ต้องมีสิทธิ์ override เช่นfillWeightหรือeditPriceDONE,CANCELED,DELETED: ถือว่า locked- เอกสารที่เกินวันสร้างจะปิด add/edit/delete item, แก้ remark/header, และ save
ความต่างจากใบสั่งขาย
| เรื่อง | ใบจองราคา | ใบสั่งขาย |
|---|---|---|
| จุดประสงค์ | จองราคาและสินค้าไว้ก่อน | สร้าง order เพื่อออก invoice/payment |
| เอกสารปลายทาง | convert เป็น order | สร้าง invoice แล้วไป payment |
| status หลัก | DRAFT, NEW, DONE, CANCELED, DELETED | DRAFT, NEW, WAITING_PAYMENT, CONFIRMED, ... |
| item step price | เก็บ currentStepPrice เป็น snapshot/fallback | รับ currentStepPrice ต่อจาก booking หรือ product picker |
| การแก้ item จองราคา | แก้ในใบจองราคาก่อน convert | หลัง convert แก้ลดจำนวนได้ โดยใช้ currentStepPrice หา step price และ fallback ราคาตั้งต้น |
| การแก้ย้อนหลัง | จำกัดวันสร้างเอกสารเข้มกว่า | ขึ้นกับ order action state/status |
| stock check | ใช้ onHand - bookingQty | ใช้ available stock และ order item rules |
Mobile vs POS Terminal
| เรื่อง | Mobile | POS Terminal |
|---|---|---|
| Navigation | stack: BookingPriceList -> CustomerList -> BookingPriceDetail | split-pane: menu, list/customer picker, detail |
| Product picker | screen/modal ตาม Mobile | full-screen modal host ใน BookingProductPicker |
| Add/edit item | overlay ตาม product picker flow | overlay เดียวใน modal host เพื่อเลี่ยง nested native modal บน iOS |
| created-day guard | ใช้ server date เมื่อโหลดได้ | ใช้ GET /api/healthz และ fallback device date |
| convert | replace ไป OrderDetail | เปิด order detail ใน panel และ refresh list |
Developer Handoff Map
| งาน | Mobile | POS Terminal |
|---|---|---|
| list | apps/mobile/src/screens/bookingPriceStack/bookingPriceListScreen/index.tsx | HomeScreen/index.tsx, ListPane.tsx |
| detail | apps/mobile/src/screens/bookingPriceStack/bookingPriceDetailScreen/index.tsx | BookingPriceDetailForm.tsx |
| product picker | logic ใน detail screen | BookingProductPicker.tsx |
| payload helpers | detail screen helpers | HomeScreen/helpers.ts |
| service | apps/mobile/src/services/bookingPrice.ts | apps/pos-terminal/src/services/bookingPrice.ts |
| shared types | apps/mobile/src/types/bookingPrice.ts | packages/types/src/bookingPrice.ts |
| order ที่รับ booking ต่อ | orderDetailScreen/index.tsx | OrderDetailForm.tsx |