ข้ามไปยังเนื้อหาหลัก

ใบจองราคา

เมนู ใบจองราคา ใช้จองราคา/จองสินค้าให้ลูกค้าก่อนแปลงเป็นใบสั่งขายจริง โครงโดยรวมคล้าย ใบสั่งขาย เพราะใช้ลูกค้า, สินค้า, คลัง, step price, น้ำหนัก, และยอดรวมแบบเดียวกัน แต่ต่างตรงที่ใบจองราคายังไม่ใช่ order ที่เดินต่อไป invoice/payment จนกว่าจะกดแปลงเป็นใบสั่งขาย

จุดพิเศษของเมนูนี้คือ field currentStepPrice ในระดับรายการสินค้า ระบบเก็บข้อมูล step price ณ ตอนที่ทำใบจองราคาไว้กับ item เพื่อให้ตอนแปลงเป็นใบสั่งขายหรือกลับมาแก้ไข item ระบบยังรู้ราคาขั้นบันไดที่เคยใช้กับรายการนั้น

ดู workflow หลักดู currentStepPriceดูการแปลงเป็นใบสั่งขาย

ทางเข้าในแอป

แอปทางเข้าไฟล์/จุดทำงานหลัก
MobileHome -> BookingPriceList -> CustomerList mode booking -> BookingPriceDetailbookingPriceListScreen, bookingPriceDetailScreen
POS TerminalHomeScreen split-pane -> menu bookingPrice -> customer picker/detail panelHomeScreen/index.tsx, BookingPriceDetailForm.tsx, BookingProductPicker.tsx

Service และ endpoint

งานEndpoint / service
โหลดรายการใบจองราคาGET /booking-price ผ่าน getBookingPrices(...)
โหลดรายละเอียดGET /booking-price/{bookingPriceId}
สร้าง headerPOST /booking-price
อัปเดต headerPATCH /booking-price/{bookingPriceId}
ลบเอกสารDELETE /booking-price/{bookingPriceId}
เพิ่ม itemPOST /booking-price/{bookingPriceId}/items
แก้ itemPATCH /booking-price/{bookingPriceId}/items/{itemId}
ลบ itemDELETE /booking-price/{bookingPriceId}/items/{itemId}
แปลงเป็นใบสั่งขายPOST /booking-price/{bookingPriceId}/convert
โหลด step priceproduct step-price API ผ่าน getProductStepPrice(...)

Workflow หลัก

รายการใบจองราคา

รายการโหลดด้วย getBookingPrices(...) โดยใช้ context ของสาขาที่เลือก:

  • branchId จาก selected branch
  • whGrpCode จาก selected warehouse group
  • page, pageLimit, search
  • bookingPriceStatus
  • userCode
  • sort เป็น DESC ตาม CreatedAt

พฤติกรรมของ list:

  • dedupe ด้วย bookingPriceId, bookingPriceStorageId, หรือ bookingPriceNo
  • มี search และ pagination
  • สีของ card สื่อสถานะเอกสาร
  • Mobile ใช้ floating plus เพื่อเริ่ม CustomerList mode booking
  • Terminal ใช้ split-pane list และปุ่มเพิ่มเพื่อเปิด customer picker
  • วันที่สำหรับตัดสินว่าเอกสารแก้ได้หรือไม่ใช้ server business date จาก GET /api/healthz เมื่อมี และ fallback เป็นวันที่เครื่อง

สร้าง draft จากลูกค้า

การสร้างใบจองราคาเริ่มจากเลือกลูกค้าเหมือนใบสั่งขาย แต่ mode คือ booking

field ที่ seed เข้ารายละเอียด:

Fieldแหล่งข้อมูล
customerCode, customerName, phone1, phone2customer detail
shippingTypecustomer / branch rule
routeCode, routeNameaddress ของลูกค้า
U_BFP_Latitude, U_BFP_Longitudeaddress ลูกค้า
branchId, whGrpCode, whGrpNamebranch storage
userCode, userResponsibleuser ปัจจุบัน
bookingPriceStatusเริ่มเป็น DRAFT

Terminal มี parity note สำคัญ: draft จาก customer ใช้ address แรกของลูกค้าสำหรับ route/coordinate ตาม Mobile booking-price initialization ปัจจุบัน

เพิ่มและแก้สินค้า

การเพิ่มสินค้าในใบจองราคาคล้ายใบสั่งขาย:

  1. เปิด product picker
  2. ถ้าเลือกสินค้าที่มีอยู่แล้ว จะเข้า edit overlay แทนการเพิ่มซ้ำ
  3. validate จำนวนเป็นตัวเลขเต็มและมากกว่า 0
  4. เช็ค available stock จาก onHand - bookingQty
  5. ถ้าเป็นสินค้าน้ำหนัก ใช้ qty * price * weight
  6. ถ้าเป็นสินค้าปกติ ใช้ qty * price
  7. ถ้า product มี stepPriceFlag และจำนวนเข้าเงื่อนไข จะโหลด step price ก่อนคำนวณราคา
  8. 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, UOMEntryUOM
whsCode, whGrpCode, whGrpNameคลังและกลุ่มคลัง
companyCode, companyName, taxNo, vatGroupบริษัท/ภาษี
weightBaseFlagสินค้าน้ำหนัก
stepPriceFlagสินค้า step price
currentStepPricesnapshot/fallback ของ step price ที่ใช้กับ item นี้
editPriceByuser ที่แก้ราคา

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
ลบใบจองจาก listowner ของ 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 หรือ editPrice
  • DONE, CANCELED, DELETED: ถือว่า locked
  • เอกสารที่เกินวันสร้างจะปิด add/edit/delete item, แก้ remark/header, และ save

ความต่างจากใบสั่งขาย

เรื่องใบจองราคาใบสั่งขาย
จุดประสงค์จองราคาและสินค้าไว้ก่อนสร้าง order เพื่อออก invoice/payment
เอกสารปลายทางconvert เป็น orderสร้าง invoice แล้วไป payment
status หลักDRAFT, NEW, DONE, CANCELED, DELETEDDRAFT, 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

เรื่องMobilePOS Terminal
Navigationstack: BookingPriceList -> CustomerList -> BookingPriceDetailsplit-pane: menu, list/customer picker, detail
Product pickerscreen/modal ตาม Mobilefull-screen modal host ใน BookingProductPicker
Add/edit itemoverlay ตาม product picker flowoverlay เดียวใน modal host เพื่อเลี่ยง nested native modal บน iOS
created-day guardใช้ server date เมื่อโหลดได้ใช้ GET /api/healthz และ fallback device date
convertreplace ไป OrderDetailเปิด order detail ใน panel และ refresh list

Developer Handoff Map

งานMobilePOS Terminal
listapps/mobile/src/screens/bookingPriceStack/bookingPriceListScreen/index.tsxHomeScreen/index.tsx, ListPane.tsx
detailapps/mobile/src/screens/bookingPriceStack/bookingPriceDetailScreen/index.tsxBookingPriceDetailForm.tsx
product pickerlogic ใน detail screenBookingProductPicker.tsx
payload helpersdetail screen helpersHomeScreen/helpers.ts
serviceapps/mobile/src/services/bookingPrice.tsapps/pos-terminal/src/services/bookingPrice.ts
shared typesapps/mobile/src/types/bookingPrice.tspackages/types/src/bookingPrice.ts
order ที่รับ booking ต่อorderDetailScreen/index.tsxOrderDetailForm.tsx

เอกสารที่เกี่ยวข้อง