CYBERSOFT
Đăng nhập

Thực chiến: đồng bộ tồn kho omnichannel, hoàn/đổi & tích điểm loyalty trong bán lẻ

Thực chiến doanh nghiệpBán lẻAPIPlaywrightThực tế
🗓 1 tháng trước22 phút đọc·👁 969 lượt xem👤 422 người đọc

Bài sâu: bối cảnh, kiến trúc omnichannel đa kho, bất biến tồn kho/loyalty, test plan, ma trận ca, automation, race condition, đối soát, CI, AI, phỏng vấn.

1. Bối cảnh doanh nghiệp & phạm vi

Một chuỗi bán lẻ thời trang - gia dụng có 85 cửa hàng vật lý trên toàn quốc và một kênh online (app + website) vận hành song song, tổng cộng phục vụ khoảng 40.000 giao dịch/ngày, trong đó 65% diễn ra tại cửa hàng qua POS và 35% qua kênh online. Mỗi cửa hàng có kho riêng, ngoài ra còn 2 trung tâm phân phối lớn (WH-HN, WH-HCM) giữ vai trò kho dự phòng cho các đơn online khi cửa hàng gần nhất hết hàng. Bài toán cốt lõi là đồng bộ tồn kho thời gian thực giữa hàng chục điểm bán để tránh tình trạng bán vượt tồn kho (oversell) — một lỗi từng khiến chuỗi bán lẻ tương tự phải hoàn tiền và xin lỗi hơn 1.200 khách hàng trong một đợt sale cuối năm do hệ thống hiển thị 'còn hàng' tại 12 cửa hàng dù kho thực tế đã hết.

Phạm vi bài viết bao trùm 3 luồng nghiệp vụ chính: đồng bộ tồn kho đa kênh (online, POS, kho dự phòng), quy trình hoàn/đổi hàng theo mô hình omnichannel (mua online trả tại cửa hàng — BOPIS return, mua tại cửa hàng đổi online), và hệ thống tích điểm loyalty gắn liền với giá trị giao dịch thực (sau khi trừ hàng trả). Ràng buộc nghiệp vụ quan trọng: tồn kho hiển thị cho khách phải trễ tối đa 5 giây so với tồn kho thực tại quầy, và điểm loyalty phải được tính lại chính xác trong vòng 24 giờ nếu có hoàn/đổi xảy ra sau khi điểm đã cộng.

Luồng tồn kho Omnichannel · Omnichannel inventory flow App/Web (Online) POS cửa hàng Inventory Hub Order/OMS Loyalty Ledger Kho A (WH-HN) Kho B (WH-HCM) Cửa hàng #12 Bất biến: Tồn kho tổng ≥ 0 tại mọi kênh, mọi thời điểm
Kiến trúc luồng tồn kho omnichannel giữa Online/POS/kho/loyalty

Phạm vi tự động hoá

  • Kiểm thử API cho Inventory Hub: trừ/cộng tồn kho, khoá tạm thời (reservation)
  • Kiểm thử E2E Playwright cho luồng bán hàng và hoàn/đổi liên kênh
  • Kiểm thử tính điểm loyalty và cập nhật hạng thành viên
Bài này giả định Inventory Hub expose test-only endpoint để seed tồn kho theo từng kho/cửa hàng và reset điểm loyalty giữa các lần chạy.

2. Kiến trúc & luồng nghiệp vụ

Kiến trúc trung tâm là Inventory Hub — một service độc lập giữ bảng tồn kho theo (SKU, locationId) và phát sự kiện StockChanged mỗi khi có giao dịch tại bất kỳ kênh nào. POS tại cửa hàng và app online đều gọi Inventory Hub qua API đồng bộ khi cần kiểm tra tồn kho khả dụng (available-to-sell = tồn vật lý − đã giữ chỗ), và gọi bất đồng bộ (qua message queue) khi ghi nhận thay đổi để tránh nghẽn tại quầy thu ngân lúc cao điểm. OMS (Order Management System) đóng vai trò điều phối: khi kho gần nhất hết hàng, OMS tự động chọn kho dự phòng theo thứ tự ưu tiên khoảng cách địa lý và tồn kho khả dụng.

Điểm khó khi kiểm thử

Điểm khó nhất là bán đồng thời trên 2 kênh cho cùng 1 SKU chỉ còn 1 đơn vị tồn kho — đây là race condition kinh điển của bài toán omnichannel, đòi hỏi Inventory Hub phải dùng cơ chế khoá (row lock hoặc optimistic lock với version number) để đảm bảo chỉ 1 giao dịch thắng. Khó khăn thứ hai là độ trễ đồng bộ giữa các kho khi mạng cửa hàng gián đoạn: POS phải hoạt động offline-first (ghi log giao dịch cục bộ) rồi đồng bộ bù khi mạng phục hồi, nhưng thứ tự đồng bộ bù sai có thể khiến tồn kho tính sai tạm thời. Khó khăn thứ ba là hoàn hàng ngược kênh (BOPIS return): khách mua online nhưng trả tại cửa hàng bất kỳ, đòi hỏi tồn kho phải cộng đúng vào kho của cửa hàng nhận trả chứ không phải kho gốc đã xuất bán, nếu không sẽ gây lệch tồn kho giữa các kho theo thời gian.

🎯 Sự cố thực tế

Trong đợt khai trương bộ sưu tập giới hạn, một mẫu túi xách chỉ có 3 chiếc tại kho trung tâm nhưng hệ thống đã bán được 5 đơn hàng online trong vòng 90 giây đầu tiên. Điều tra cho thấy cache tồn kho ở tầng CDN của app có TTL 30 giây, khiến 2 request cuối vẫn thấy 'còn hàng' dù kho đã về 0, và Inventory Hub không có cơ chế publish invalidate cache ngay khi tồn kho chạm ngưỡng thấp.

3. Mô hình dữ liệu & bất biến (oracle)

Mô hình dữ liệu cốt lõi gồm bảng StockLevel (sku, locationId, onHand, reserved, version), StockTransfer (fromLocation, toLocation, sku, qty, status), ReturnExchange (originalOrderId, returnLocationId, sku, qty, refundAmount, type: RETURN|EXCHANGE), và LoyaltyLedger (customerId, points, tier, txnRef, effectiveAt). Available-to-sell của một SKU tại một địa điểm luôn được tính là onHand − reserved, và giá trị này là số hiển thị cho khách chứ không phải onHand thô.

Bất biến nghiệp vụ bắt buộc (oracle)

  • Bất biến 1: tồn kho tổng toàn hệ thống của 1 SKU (Σ onHand mọi location) không bao giờ âm và không đổi ngoài các giao dịch hợp lệ (bán, nhập, chuyển kho, trả hàng)
  • Bất biến 2: available-to-sell = onHand − reserved luôn ≥ 0 tại mọi location, mọi thời điểm
  • Bất biến 3: mọi StockTransfer hoàn tất phải làm tổng tồn kho hai đầu (from+to) không đổi (bảo toàn số lượng)
  • Bất biến 4: điểm loyalty cộng cho 1 đơn hàng phải bị điều chỉnh giảm tương ứng khi có hoàn/đổi làm giảm giá trị giao dịch thực
  • Bất biến 5: hạng thành viên (tier) chỉ tăng khi điểm tích luỹ hợp lệ vượt ngưỡng, không giảm ngược khi có hoàn hàng xảy ra sau khi đã lên hạng trong kỳ trước
🔒

Nội dung bị khoá

Bạn đang xem bản xem trước. Đăng nhập để mở khoá toàn bộ bài viết và tất cả tài liệu Tester/QA.

Đã xem 24% nội dung
  • Đọc trọn vẹn mọi bài viết
  • Lưu bài & ghi chú cá nhân
  • Theo dõi tiến độ đã đọc
  • Luyện phỏng vấn, ISTQB, Mock
Đăng nhập để đọc tiếp

Chưa có mã? Lấy mã qua Fanpage / Zalo CyberSoft.

0

💬 Bình luận (0)

Bạn có thể đọc mọi bình luận. Đăng nhập để bình luận

Chưa có bình luận nào. Hãy là người đầu tiên!