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

Login ไป Branch ไป Home

หน้านี้อธิบาย mobile startup flow ปัจจุบันจาก implementation จริงใน apps/mobile

อ้างอิงจากโค้ด​

  • apps/mobile/App.tsx
  • apps/mobile/src/navigation/RootNavigator.tsx
  • apps/mobile/src/screens/loginScreen/index.tsx
  • apps/mobile/src/screens/branchScreen/index.tsx
  • apps/mobile/src/screens/homeScreen/index.tsx

ลำดับการตัดสินใจตอนเริ่มแอป​

ขั้นที่ 1 Login​

การตรวจเวอร์ชั่นที่บังคับอัปเดท​

  • หน้า Login อ่าน native app versionCode ผ่าน AppVersionModule

  • JavaScript อ่าน native version ผ่าน boundary ของแต่ละแอป: apps/mobile/src/native/appVersion.ts และ apps/pos-terminal/src/native/appVersion.ts

  • หน้า Login อ่าน Firestore ที่ version/vapp

  • required-update logic อยู่ที่ apps/mobile/src/services/appUpdate.ts และ apps/pos-terminal/src/services/appUpdate.ts

  • การเช็คปัจจุบันแปลงค่าทั้งสองฝั่งด้วย Number(...) ก่อนเปรียบเทียบ

  • field หลักจาก Firestore คือ versionNumber; implementation ยังรองรับรูปแบบแยก platform ถ้าเพิ่มภายหลัง:

    • androidVersionNumber / androidVersion
    • iosVersionNumber / iosVersion
    • nested android.versionNumber / android.version
    • nested ios.versionNumber / ios.version
  • ถ้า version number จาก Firestore มากกว่า native versionCode แอปจะแสดง modal บังคับว่า แอปของคุณจำเป็นต้องอัปเดทเป็นเวอร์ชั่น ...

  • เลขเวอร์ชั่นใน modal ใช้ค่าตัวเลขจาก Firestore versionNumber

  • modal นี้ไม่มีปุ่มปิด และกด back ของ Android เพื่อปิดไม่ได้

  • POS Terminal ใช้ check เดียวกันบน UserSelectionScreen และแสดง modal บล็อกก่อนใช้งาน login ต่อ

  • ถ้า username หรือ password ไม่ครบ จะไม่ submit และแสดง alert

  • เมื่อ login สำเร็จ แอปจะ:

    • เก็บ auth token
    • เก็บ user object
    • เก็บ login date ในรูปแบบ DD-MM-YYYY
    • refresh FCM token ถ้าทำได้
    • patch user flags กลับไป backend ถ้ามี userId
  • ก่อนจบ login แอปจะเรียก verifyAlcoholCheckBeforeLogin

  • ถ้า API ตอบกลับว่า required values ไม่ครบ หน้าจอจะเปลี่ยนไปเป็น password reset mode

ขั้นที่ 2 เลือก Branch​

  • ถ้ายังไม่มี branch ใน storage, RootNavigator จะ render branch-selection flow
  • หน้าจอจะโหลด branch list สองชุด:
    • allBranches จาก GET /api/branches?userCode=999999 สำหรับแสดงรายการสาขาทั้งหมด
    • userBranches จาก GET /api/branches?userCode={loggedInUserCode} สำหรับตรวจสิทธิ์ของผู้ใช้ที่ Login
  • step แรกคือเลือก main branch จาก allBranches
  • แอปเทียบรหัสสาขาที่เลือกกับ userBranches ด้วย hasMainBranchAccess โดยอ่านรหัสจาก whsGrpCode, whGrpCode หรือ BranchCode และตัดช่องว่างหัวท้ายก่อนเทียบ
  • เมื่อเลือกสาขา แอปยังเรียก getWarehouses() (GET /api/warehouses?pageLimit=40&page=1) ทุกครั้ง
  • เรียก getWarehousesSub(branchCode) (GET /api/warehouses/{whGrpCode}/sub) เสมอ แม้สาขาที่เลือกจะไม่อยู่ใน userBranches
  • ตัวเลือกคลังจะตัดรหัสที่มี NF- หรือ DR- ออก ส่วนรายการคลังย่อยจะตัดรหัสสาขาที่เลือกและรหัสที่ซ้ำกับคลังหลักของสาขานั้นออกด้วย
  • คลังหลักแสดงก่อนคลังย่อยและแสดงเฉพาะสาขาที่ผู้ใช้มีสิทธิ์ หากสาขาที่มีสิทธิ์ไม่มีคลังหลักในผล API หลังกรอง จะใช้รหัสและชื่อสาขาที่เลือกเป็นตัวเลือกคลังหลักแทน
  • หากสาขาที่เลือกไม่อยู่ในรายการของผู้ใช้ จะยังซ่อนคลังหลัก แต่แสดงคลังย่อยที่ API ส่งกลับมาหลังผ่านการกรอง
  • ปุ่ม ซิ้งค์ข้อมูลคลังใหม่ เรียก syncWarehouses() รอสองวินาที แล้วโหลดรายการคลังทั้งสองชุดใหม่ โดยใช้ userBranches ใน state ปัจจุบันควบคุมเฉพาะการแสดงคลังหลัก ไม่ใช้เป็นเงื่อนไขในการโหลดคลังย่อย
  • step ที่สองคือเลือก warehouse branch ที่จะเก็บลง storage
  • เมื่อเลือก warehouse แล้ว แอปจะ:
    • ถ้าเป็นคลังหลัก จะโหลดสาขาของผู้ใช้ที่ Login ใหม่และตรวจสิทธิ์ซ้ำก่อนบันทึก หากสิทธิ์ถูกถอด จะกลับไป step แรกพร้อมข้อความ ไม่มีสิทธิเลือกคลังหลักของสาขานี้
    • ล้าง tax cache
    • บันทึก branch ลง storage
    • แจ้ง navigator ว่า branch selection เสร็จแล้ว
    • พยายาม fetch tax data ต่อแบบ best effort

ตรวจ Firestore inventory index​

  • กดค้างคลังใน step ที่ 2 เพื่อรัน real-time Firestore inventory query เดียวกับที่ product picker ใช้สำหรับคลังนั้น โดยจะไม่เลือกหรือเปลี่ยนคลังจากการกดค้าง
  • listener สำหรับตรวจสอบจะ log snapshot ที่ได้รับและหยุดเองภายใน 5 วินาที หากเริ่มตรวจคลังใหม่ ระบบจะหยุด listener เดิมก่อน
  • ถ้า Firestore ปฏิเสธ query เพราะยังไม่มี composite index, console จะแสดง [FIRESTORE CREATE INDEX] และ Firebase Console URL เป็นรายการแยก ทำให้ React Native DevTools เปิดเป็นลิงก์ที่กดได้

payload ที่บันทึกลง storage​

branch ที่เลือกจะถูกเก็บเป็น JSON object เดียวใต้ key branch

shape ของข้อมูลคือ:

{
BranchId: string | number;
BranchCode: string;
whsGrpName: string;
whsGrpCode: string;
isMain: boolean;
}

ความหมายคือ:

  • BranchId คือ id ของ main branch ที่เลือก
  • BranchCode คือ code ของ main branch ที่เลือก
  • whsGrpCode คือ code ของคลังที่เลือกจริงใน step 2
  • whsGrpName คือชื่อคลังที่เลือก
  • isMain บอกว่าคลังที่เลือกเป็นคลังหลักของสาขานั้นหรือไม่

หมายเหตุเรื่องการใช้งานต่อ​

  • หน้าจอและ service ที่ต้องใช้คลังที่เลือก มักอ่านจาก whsGrpCode
  • หน้าจอที่ต้องรู้ main branch owner code สามารถอ่านจาก BranchCode
  • TransferWarehouseDetail ตอนนี้ใช้ BranchCode ที่เก็บไว้เพื่อโหลด warehouse options ผ่าน getWarehousesSub(BranchCode) แต่ยังใช้ whsGrpCode ที่เก็บไว้เป็น source warehouse ของ flow สินค้าและ batch
  • ใน TransferWarehouseDetail ค่า company สำหรับโหลด Batch/Lot ต้องมาจากสินค้าที่เลือก (companyCode หรือ company) โดยตรง ไม่ใช้ company จากคลังปลายทาง; เมื่อเลือกสินค้าตัวแรก ระบบจะเติม companyCode และ companyName ของ header จาก company ของสินค้านั้น

ขั้นที่ 3 Home​

  • เมื่อ auth และ branch ครบแล้ว RootNavigator จะ render Home
  • Home จะโหลด user และ branch จาก local storage
  • Home จะเช็ค login date ทุกครั้งที่ได้ focus; ถ้า login_date ที่เก็บไว้ไม่ใช่วันปัจจุบัน จะใช้ logout behavior เดิมของ mobile
  • Header ของ Home จะเช็ค required app update แบบเดียวกับหน้า Login รวมถึงตอนกลับมา focus ที่ Home
  • POS Terminal Home เช็ค required app update ซ้ำเมื่อกลับมา focus เช่นกัน
  • เมนูที่มองเห็นขึ้นกับ:
    • deptCode ของ user
    • ค่า isMain ของ branch ที่เลือก
  • เมนูจะพาผู้ใช้ไปยัง flow อื่นของ POS เช่น order, booking, invoice, shipping, stock, customer และ employee
  • การ logout จาก header จะล้าง session storage ทั้งหมดและพากลับไปสู่ login state

หมายเหตุ​

  • session นี้มีอายุอิงตามวันที่ login ที่เก็บไว้
  • มี screen Branch อยู่ใน authenticated stack ด้วย ดังนั้นผู้ใช้สามารถกลับมาเลือก branch ใหม่หลังเข้า Home ได้
  • Home ทำหน้าที่ทั้งเป็น landing page และ permission gate ของ flow อื่นๆ