EzyFix 2.0 — Functional Flow Spec
0) Phạm vi & Mục tiêu
Phạm vi : Flows “Khách đặt lịch → Thợ xử lý → Thanh toán → Ví thợ → Rút tiền”, kèm flow đặt lịch qua AI và các flow phụ trợ (GPS, Review/Rating, Dispute, Voucher, Escrow, Payout).
Không phạm vi (ngoài bản này): CMS nội dung marketing, quản trị người dùng cấp hệ thống phức tạp, kế toán thuế chi tiết, tích hợp KYC nâng cao (chỉ nêu pre-check cơ bản), tính năng cộng tác nội bộ nâng cao.
Mục tiêu :
Diễn giải chi tiết từng bước cho Mobile/Web.
Ràng buộc trạng thái bám enums đã khai báo.
Xác định rules (giá/hoa hồng/escrow/voucher) và edge cases .
Chuẩn hoá thông báo (push/in-app), logs , AC để QA test.
1) Actors & Vai trò
Customer (Khách hàng) : tạo yêu cầu, nhận báo giá, xác nhận, thanh toán, đánh giá.
Technician (Thợ) : nhận đơn, báo giá, di chuyển/kiểm tra, sửa chữa, hoàn tất, rút tiền.
Admin/Operator : xử lý payout, xử lý tranh chấp (Dispute), kiểm soát gian lận, cấu hình phí/hoa hồng, phê duyệt ngoại lệ.
AI Service : gợi ý khoảng giá, chẩn đoán ban đầu, kiểm tra sai số báo giá.
2) Giả định & Ràng buộc chung
Đơn vị tiền tệ : VND (số nguyên).
Múi giờ : Asia/Ho_Chi_Minh.
Khoảng giá AI : minPrice, maxPrice; ngưỡng vượt X% cấu hình theo ServiceCategory.
Commission : cấu hình theo Service hoặc ServiceCategory hoặc toàn hệ thống (ưu tiên theo mức cụ thể nhất).
Escrow : chỉ áp dụng Online ; Cash không escrow (ghi nợ hoa hồng nếu có).
Hình ảnh/video : yêu cầu ở mốc trạng thái quan trọng (Before/After/Exceed/Payment).
GPS : ghi SystemLog khi thợ cập nhật EN_ROUTE/ARRIVED.
Voucher : áp dụng theo ràng buộc VoucherCategory/VoucherService/VoucherPaymentMethod và thời gian hiệu lực.
3) Khái niệm lõi (Mapping ERD)
Job = ServiceRequest + ServiceDeliveryOffer + ServiceAppointment + Payment (+ AI_Diagnosis, Media, Review, ActivityLog).
Quote : ServiceDeliveryOffer (estimatedCost/finalCost) ↔ trạng thái PENDING/ACCEPTED/REJECTED/EXPIRED/CHECKING_AWAIT .
Appointment : lịch làm việc của thợ sau khi quote được ACCEPTED .
Payment : theo PaymentMethod (Online/Cash), bám các trạng thái PENDING → PAYMENT_SUCCESS → (ESCROW) → COMPLETE | FAILED .
Wallet : WalletAccount (per Technician) + WalletTransaction (Credit/Debit; reference tới Payment/Appointment/... ).
Payout : WalletPayoutRequest (yêu cầu rút tiền) + WalletTransaction (Debit/Withdrawal).
4) Mô hình trạng thái (ASCII)
4.1 ServiceRequest
States : PENDING, QUOTED, QUOTE_ACCEPTED, QUOTE_REJECTED, COMPLETED, CANCELLED
Default : PENDING
Allowed transitions
PENDING → QUOTED — khi có ≥ 1 offer hợp lệ.
QUOTED → QUOTE_ACCEPTED — khi khách chấp nhận 1 offer.
QUOTED → QUOTE_REJECTED — khi khách từ chối offer (và không còn offer khác).
QUOTE_ACCEPTED → COMPLETED — khi Appointment=REPAIRED và Payment=COMPLETE.
QUOTE_ACCEPTED → CANCELLED — theo policy huỷ.
QUOTE_REJECTED → CANCELLED — khi tất cả offer bị từ chối/hết hạn.
Guards & notes
QUOTED nghĩa là request đã có ít nhất một offer hợp lệ.
COMPLETED chỉ khi job đã REPAIRED và thanh toán COMPLETE.
4.2 ServiceDeliveryOffer
States : PENDING, ACCEPTED, REJECTED, EXPIRED, CHECKING_AWAIT
Default : PENDING
Allowed transitions
PENDING → ACCEPTED — khi customer chấp nhận báo giá (tạo Appointment).
PENDING → REJECTED — khi customer từ chối báo giá.
PENDING → EXPIRED — offer quá hạn offerTtl.
PENDING → CHECKING_AWAIT — thợ cần kiểm tra tại chỗ/giá vượt ngưỡng AI.
CHECKING_AWAIT → ACCEPTED — khi khách xác nhận đặc biệt.
Terminal : ACCEPTED, REJECTED, EXPIRED.
4.3 ServiceAppointment
States : SCHEDULED, EN_ROUTE, ARRIVED, CHECKING, REPAIRING, REPAIRED, CANCELLED, DISPUTE, ABSENT
Default : SCHEDULED
Nominal path
SCHEDULED → EN_ROUTE → ARRIVED → CHECKING → REPAIRING → REPAIRED
Other transitions
CHECKING → CANCELLED — nếu khách từ chối sau kiểm tra.
Any of (SCHEDULED..REPAIRING) → ABSENT — nếu không gặp được khách.
Any of (SCHEDULED..REPAIRED) → DISPUTE — khi mở tranh chấp.
Guards & notes
Cập nhật EN_ROUTE/ARRIVED phải ghi SystemLog (GPS).
Không cho “nhảy cóc” trạng thái trong nominal path.
4.4 Payment
States : PENDING, PAYMENT_SUCCESS, ESCROW, COMPLETE, FAILED
Online (ưu tiên)
PENDING → PAYMENT_SUCCESS → ESCROW → COMPLETE
PENDING → FAILED
Tiền mặt (CASH)
PAYMENT_SUCCESS → COMPLETE (gần như đồng thời)
Guards & notes
Online chỉ cộng Ví thợ khi COMPLETE (sau escrow).
Cash tạo ghi nợ hoa hồng nếu có: WalletTransaction(Debit, Commission).
4.5 Dispute
States : OPEN, IN_REVIEW, RESOLVED
Allowed transitions
OPEN → IN_REVIEW
IN_REVIEW → RESOLVED (refund/partial/adjust commission)
Notes
Có thể mở từ SCHEDULED đến sau REPAIRED (trước khi đóng job).
5) End-to-end Flows (Chi tiết + AC)
5.1 Đăng nhập & xác định vai trò (theo Docs)
Steps
App hiển thị màn chào.
Hỏi vai trò Thợ/Khách (có nhớ lựa chọn).
Điều hướng tới trang chủ tương ứng.
Rules
Lưu lựa chọn vai trò vào profile hoặc local (nếu đã xác thực).
Nếu user là cả hai (được cấp quyền), cho phép switch role trong Profile.
AC
Given user mới mở app, When chọn “Thợ”, Then vào Home thợ và không hỏi lại ở lần sau nếu bật “Nhớ”.
5.2 Customer – Đặt lịch (theo Docs §1)
Steps
Vào Home khách → chọn Dịch vụ → Đặt lịch .
Nhập: họ tên, số ĐT, địa chỉ (chọn Address đã lưu hoặc nhập mới), thời gian (ngay/hẹn), mô tả + ảnh/video.
Submit → tạo ServiceRequest(PENDING) + Media(INITIAL) nếu có.
Validation
Địa chỉ: city/province/lat/lng hợp lệ.
Thời gian: không < now, cách now ≥ minLeadTime.
Tối thiểu 1 mô tả (text hoặc media).
Rate limit tạo yêu cầu (anti-spam).
AC
Given form hợp lệ, When submit, Then tạo ServiceRequest (PENDING) và push thông báo tới thợ phù hợp.
5.3 Thợ – Tiếp nhận đơn & Báo giá (theo Docs §2)
Steps
Home Thợ → Hàng chờ lọc theo Service/Skill/Location.
Mở chi tiết request (ảnh/video, mô tả, nếu có AI_Diagnosis).
Nhập báo giá: Giá chốt hoặc Giá dự kiến estimatedCost.
Nếu vượt ngưỡng AI (X% ngoài khoảng), buộc nhập lý do + media minh chứng → Media(EXCEED).
Tạo ServiceDeliveryOffer(PENDING); hệ thống set ServiceRequest=QUOTED nếu là offer đầu tiên.
Rules
Một thợ có thể tạo 1 offer/1 request (tránh spam).
Offer hết hạn sau offerTtl (→ EXPIRED).
AI gợi ý khoảng giá hiển thị cho cả thợ & khách.
AC
Given thợ nhập giá nằm trong khoảng AI, When submit, Then offer=PENDING và request=QUOTED.
5.4 Customer – Nhận báo giá (theo Docs §3)
Steps
Khách nhận push: “Có báo giá từ thợ X”.
Màn hình hiển thị giá + khoảng AI + badge cảnh báo nếu vượt ngưỡng.
Khách chọn Chấp nhận hoặc Từ chối .
Rules
Khi Chấp nhận : ServiceDeliveryOffer=ACCEPTED, ServiceRequest=QUOTE_ACCEPTED, tạo ServiceAppointment(SCHEDULED) với scheduledDate (theo thời gian khách chọn).
Khi Từ chối : ServiceDeliveryOffer=REJECTED. Nếu tất cả offer bị từ chối → ServiceRequest=CANCELLED (hoặc vẫn QUOTED nếu còn offer khác/đang chờ).
AC
Given khách bấm Chấp nhận, When confirm, Then auto tạo Appointment và cố định thợ.
5.5 Thợ – Đến & Kiểm tra (theo Docs §4)
Steps
Thợ cập nhật: EN_ROUTE → ARRIVED (ghi SystemLog + GPS).
Gửi ảnh/video ban đầu Media(INITIAL) nếu thiếu.
Nếu trước đó là giá dự kiến : vào CHECKING , nhập finalCost .
Nếu finalCost vượt ngưỡng AI/estimatedCost theo rule → ServiceDeliveryOffer=CHECKING_AWAIT và yêu cầu khách xác nhận đặc biệt.
Rules
Trạng thái Appointment phải theo chuỗi hợp lệ (không nhảy cóc).
Mọi cập nhật giá cần log vào ActivityLog (type=UPDATED, field=cost).
AC
Given thợ đặt ARRIVED, When app gửi, Then hiển thị mốc đến nơi cho khách theo thời gian thực (GPS tracking).
5.6 Customer – Xác nhận lần cuối (theo Docs §5)
Steps
Khách xem finalCost + cảnh báo AI (nếu có).
Chọn: Chấp nhận hoặc Từ chối (SOS) .
Rules
Nếu Chấp nhận : Appointment chuyển REPAIRING .
Nếu Từ chối (SOS) : mở luồng DISPUTE hoặc CANCELLED theo lý do.
AC
Given finalCost thay đổi, When khách chấp nhận, Then cho phép thợ bắt đầu REPAIRING.
5.7 Thợ – Sửa chữa & Hoàn tất (theo Docs §6)
Steps
Thợ cập nhật REPAIRING .
Sau khi xong: REPAIRED , gửi ảnh/video sau sửa Media(FINAL) + ghi chú.
Chuyển sang Thanh toán .
AC
Given thợ set REPAIRED và upload Media(FINAL), When lưu, Then cho phép mở màn thanh toán.
5.8 Thanh toán (theo Docs §7)
5.8.1 Online (ưu tiên, có Escrow)
Steps
Khách chọn phương thức (MOMO/ZALOPAY/VNPAY/BANK_QR/CREDIT_CARD/BANK_TRANSFER).
Tạo Payment(PENDING) và gọi cổng.
Thành công: Payment=PAYMENT_SUCCESS, set Payment=ESCROW.
Khi khách xác nhận hoàn tất hoặc hệ thống auto-complete sau T+N: Payment=COMPLETE → ghi thu nhập ròng cho thợ.
Hạch toán Ví thợ
NetEarning = Gross - Commission.
Ghi WalletTransaction(Credit, Reason='Earning', reference=Payment); cập nhật WalletAccount.Balance += NetEarning.
AC
Given payment success, When Appointment=REPAIRED & customer confirm, Then Payment=COMPLETE và Ví thợ +NetEarning.
5.8.2 Tiền mặt
Steps
Thợ thu tiền trực tiếp, chụp ảnh biên nhận Media(PAYMENT).
Tạo Payment(PAYMENT_SUCCESS → COMPLETE) với method=CASH (không escrow).
Hệ thống ghi hoa hồng phải thu : WalletTransaction(Debit, Reason='Commission'); WalletAccount.Balance -= Commission.
Nếu Balance < 0 : hiển thị nợ hoa hồng (bù khi có thu nhập mới hoặc lúc rút tiền).
AC
Given method=CASH, When mark paid, Then ghi nợ hoa hồng (nếu có) và hiển thị công nợ trên Ví thợ.
5.9 Hoàn tất đơn (theo Docs §8)
Steps
Payment COMPLETE → thông báo cho cả hai.
Khách đánh giá Review(ratingOverall, reviewDate).
Rules
Sau COMPLETE: ServiceRequest=COMPLETED (nếu không ở trạng thái DISPUTE).
Review chỉ mở trong reviewWindow ngày.
AC
Given Payment=COMPLETE, When success, Then request chuyển COMPLETED và mở form Review.
5.10 Rút tiền từ Ví (theo Docs §9)
Steps
Thợ mở Ví của tôi → xem số dư khả dụng + lịch sử.
Tạo WalletPayoutRequest(amount ≤ balance, payoutChannel).
Admin xử lý: duyệt/chuyển khoản .
Thành công: ghi WalletTransaction(Debit, Reason='Withdrawal'); WalletAccount.Balance -= amount.
Gợi ý Status (nên bổ sung enum)
WalletPayoutRequest.status: PENDING → APPROVED → PAID | REJECTED | FAILED .
AC
Given số dư đủ, When tạo payout, Then trạng thái = PENDING và khoá số dư tương ứng (nếu bật cơ chế hold).
6) Quy tắc chi tiết & Ràng buộc
6.1 Quy tắc báo giá & AI
Khoảng AI hiển thị cho cả hai phía.
Ngưỡng vượt X% phải: (a) nhập lý do, (b) đính kèm media minh chứng, (c) hiển thị cảnh báo cho khách, (d) cần xác nhận đặc biệt từ khách.
Ghi nhận vào AI_Diagnosis (predictedIssue, confidenceScore, isLatest).
6.2 Thay đổi giá tại chỗ
Từ estimatedCost → finalCost: nếu thay đổi vượt deltaLimit hoặc ngoài khoảng AI → chuyển ServiceDeliveryOffer=CHECKING_AWAIT và chặn tiến trình cho tới khi khách xác nhận.
6.3 Voucher
Áp dụng khi thanh toán Online trừ khi VoucherPaymentMethod cho phép phương thức khác.
Kiểm tra: thời gian hiệu lực, isActive, ràng buộc theo ServiceCategory/Service.
Voucher.discountType:
PERCENTAGE: giảm theo %.
FIXED_AMOUNT: giảm số tiền cố định.
FREE_CHECKING: miễn phí bước kiểm tra (áp dụng vào công kiểm tra).
Ghi VoucherUsage (link tới serviceRequestID & paymentID).
6.4 Commission
Commission = rule(service/category/global); có thể có minCommission .
Tính trên Gross trước voucher hay sau? → Quy ước : tính trên giá sau voucher (có thể cấu hình).
Log chi tiết vào ActivityLog khi rule thay đổi.
6.5 Wallet Invariants
WalletAccount.Balance = Σ(Credits) - Σ(Debits) (không tính các khoản hold nếu chưa trừ).
Không cho rút > balance khả dụng (balance - holds).
Cho phép âm sau Cash Commission → hiển thị công nợ.
6.6 Cancellation Policies
Trước EN_ROUTE : khách huỷ miễn phí.
Sau EN_ROUTE, trước ARRIVED : phí huỷ nhỏ (có thể cấu hình).
Sau ARRIVED : tính phí kiểm tra (nếu có).
Thợ huỷ nhiều lần → ảnh hưởng rating/điểm kỷ luật .
6.7 Dispute Rules
Mở Dispute(OPEN) với lý do + media.
Admin chuyển IN_REVIEW, yêu cầu thêm chứng cứ.
Kết quả RESOLVED: refund/tách phần việc/điều chỉnh commission.
6.8 Logging & Audit
Mọi thay đổi quan trọng ghi ActivityLog (CREATED/UPDATED/APPROVED/CANCELLED/ASSIGNED/COMPLETED/DISPUTED).
SystemLog dùng cho check-in GPS & hành vi thực địa (arrival, movement).
7) Flow đặt lịch qua AI (Phụ) (theo Docs)
Steps
Khách vào Chẩn đoán lỗi → chụp/quay + mô tả.
AI phân tích → trả về chẩn đoán , bước sơ cứu , khoảng giá .
Khách bấm Đặt lịch từ màn hình AI → tạo ServiceRequest kèm AI_Diagnosis & Media.
Rules
Kết quả AI không thay thế kiểm tra của thợ, chỉ định hướng.
Báo giá thợ vẫn phải bám khoảng AI (nếu vượt → quy tắc vượt ngưỡng).
AC
Given AI trả về kết quả, When khách đặt lịch từ đó, Then form auto điền dữ liệu AI và tạo request PENDING.
8) Ma trận Thông báo (Push/In-app)
Sự kiện Người nhận Nội dung chính Kênh Request CREATED Thợ phù hợp Yêu cầu mới gần bạn Push + In-app Offer RECEIVED Khách Thợ X đã báo giá Push + In-app Offer EXPIRED Thợ Báo giá hết hạn In-app Offer ACCEPTED Thợ Khách đã chấp nhận, lịch SCHEDULED Push + In-app EN_ROUTE Khách Thợ đang di chuyển In-app + Map ARRIVED Khách Thợ đã đến In-app FinalCost UPDATE Khách Giá cuối cần xác nhận Push + In-app Appointment REPAIRED Khách Sẵn sàng thanh toán In-app Payment SUCCESS/ESCROW Thợ Đã thanh toán (escrow) In-app Payment COMPLETE Cả hai Đơn hoàn tất Push + In-app Payout PAID Thợ Rút tiền thành công Push + In-app Dispute OPEN/RESOLVED Cả hai Trạng thái tranh chấp In-app
9) Màn hình, Empty/Loading/Error States (gợi ý UX)
Hàng chờ thợ : empty ↔ gợi ý bật định vị / mở filter.
Chi tiết request : lỗi media → retry upload; hiển thị badge “AI suggested”.
Thanh toán online : timeout/FAILED → hiển thị retry & đổi phương thức.
Ví thợ : nếu âm → hiển thị “công nợ hoa hồng”.
Payout : đang PENDING → banner nhắc thời gian xử lý.
10) Bản đồ quyền hạn (Permission)
Tác vụ Customer Technician Admin Tạo/huỷ ServiceRequest ✔️ ❌ ✔️ Tạo Offer ❌ ✔️ ❌ Nhận/Chuyển trạng thái Appointment ❌ ✔️ ✔️ Cập nhật giá (finalCost) ❌ ✔️ ✔️ Thanh toán ✔️ ❌ ✔️ (refund/adjust) Tạo Dispute ✔️ ✔️ ✔️ Duyệt Payout ❌ ❌ ✔️
11) Mapping sang ERD & ENUMs (đồng bộ thuật ngữ)
ServiceRequest.status : PENDING → QUOTED → (QUOTE_ACCEPTED|QUOTE_REJECTED) → (COMPLETED|CANCELLED)
Note : COMPLETED chỉ khi Payment=COMPLETE và Appointment=REPAIRED.
ServiceDeliveryOffer.status : dùng CHECKING_AWAIT khi chờ xác nhận finalCost vượt ngưỡng.
ServiceAppointment.status : dùng đúng chuỗi, ABSENT khi không gặp được khách.
Payment.status : Online = PENDING → PAYMENT_SUCCESS → ESCROW → COMPLETE; Cash = PAYMENT_SUCCESS → COMPLETE.
Media.mediaType : INITIAL/FINAL/EXCEED/PAYMENT/OTHER khớp các mốc upload.
ActivityLog.action : ghi đủ CREATED/UPDATED/APPROVED/CANCELLED/ASSIGNED/COMPLETED/DISPUTED.
Khoảng trống đề xuất bổ sung
WalletTransaction.type & reason (EARNING, COMMISSION, WITHDRAWAL, ADJUSTMENT).
WalletPayoutRequest.status: PENDING/APPROVED/PAID/REJECTED/FAILED .
Dispute.reason (enum mô tả: overcharge, poor quality, no show...).
12) Analytics cần theo dõi
Funnel : PENDING → QUOTED → ACCEPTED → ARRIVED → REPAIRED → COMPLETE.
Tỉ lệ vượt ngưỡng AI , tỉ lệ bị từ chối , tỉ lệ huỷ theo mốc .
ARPU thợ , Commission/day , Voucher burn rate .
Thời gian EN_ROUTE , tỉ lệ ABSENT .
13) Bảo mật & Tuân thủ
Ẩn PII khỏi logs; mã hoá token cổng thanh toán.
Chống spam/giả mạo ảnh (hash, EXIF basic).
Chống định vị giả (basic checks, server-side distance validation).
Role-based access theo bảng Permission.
14) Open Questions & TODOs
Ngưỡng X% theo ServiceCategory hay Service ?
Commission tính trước/ sau voucher (hiện quy ước “sau”, cần flag cấu hình).
offerTtl, reviewWindow, autoCompleteDays giá trị mặc định?
Có hold số dư khi tạo WalletPayoutRequest? (đề xuất có )
Kịch bản refund online (1 phần/ toàn phần) và điều chỉnh WalletTransaction.