Làm sao tránh tính trùng ngày công và chi trùng trong EWA?
Để tránh tính trùng và chi trùng trong EWA, hệ thống phải kiểm soát ở hai tầng: dữ liệu công không được cộng hai lần và giao dịch tiền không được gửi hai lần. Điều này cần khóa định danh ổn định, quy tắc phát hiện bản ghi trùng, đánh dấu phần công đã dùng, mã giao dịch duy nhất, khóa xử lý đồng thời và cơ chế fail-closed khi trạng thái thanh toán chưa rõ.
Có hai loại “trùng” hoàn toàn khác nhau
Trùng dữ liệu công
Một ngày hoặc ca làm xuất hiện nhiều lần trong dữ liệu.
Nguyên nhân có thể là:
- đồng bộ từ hai nguồn;
- import lại file;
- cùng một ca có hai mã;
- dữ liệu khách hàng bị nhân bản;
- người lao động làm nhiều nơi nhưng khóa nối sai.
Trùng giao dịch tiền
Một yêu cầu được gửi thanh toán nhiều lần.
Nguyên nhân có thể là:
- người dùng bấm lại;
- hệ thống retry không đúng;
- phản hồi ngân hàng bị timeout;
- hai tiến trình xử lý cùng lúc;
- mã giao dịch không ổn định.
Nếu chỉ chống một loại, hệ thống vẫn có thể mất tiền hoặc tính sai.
Lớp 1: Dùng khóa định danh ổn định
Hệ thống cần xác định duy nhất:
- người lao động;
- khách hàng;
- ngày;
- ca;
- nguồn dữ liệu;
- kỳ.
Khóa định danh phải đủ mạnh để biết hai bản ghi có thực sự là một hay không.
Nếu chỉ dùng tên người lao động, rủi ro trùng hoặc ghép nhầm rất cao.
Trong mô hình Lương Ngày, CCCD là khóa định danh người lao động; khách hàng có company_id và mã chấm công theo từng nơi làm việc.
Lớp 2: Kiểm tra bản ghi trùng trước khi tính tiền
Trước khi một ngày công tham gia công thức, hệ thống nên kiểm tra:
- cùng người;
- cùng ngày;
- cùng ca;
- cùng nơi làm việc;
- cùng nguồn hoặc nguồn tương đương;
- có bản ghi nào đã được chọn làm bản chính hay chưa.
Nếu có hai nguồn cùng cung cấp một ngày công, cần quy định rõ nguồn ưu tiên hoặc cơ chế hợp nhất.
Không nên cộng cả hai chỉ vì chúng đến từ hai hệ thống khác nhau.
Lớp 3: Chỉ công đã duyệt mới sinh giá trị
Ngay cả khi dữ liệu không trùng, công chưa duyệt vẫn chưa nên tạo số tiền có thể nhận.
Nguyên tắc:
Số tiền có thể nhận = (số công đã duyệt × đơn giá một ngày công) − số đã nhận trong kỳ − phần giữ lại theo quy định của doanh nghiệp.
Việc dùng công đã duyệt giúp giảm khả năng một bản ghi lỗi hoặc trùng đi thẳng vào dòng tiền.
Lớp 4: Đánh dấu phần giá trị đã được sử dụng
Sau khi một giao dịch thành công, hệ thống cần nhớ phần công nào đã tạo ra giá trị đã chi.
Điều này giúp ngăn:
- cùng ngày công bị tính lại như mới;
- phần đã nhận bị cộng sang kỳ tiếp theo;
- payroll cuối kỳ trả lại toàn bộ như chưa nhận.
Đây là lớp nối giữa dữ liệu công và sổ giao dịch.
Lớp 5: Mỗi yêu cầu phải có mã giao dịch duy nhất
Khi người lao động gửi yêu cầu, hệ thống phải tạo một mã giao dịch bất biến.
Nếu cùng yêu cầu được gửi lại vì lỗi mạng, hệ thống nhận ra đây vẫn là cùng một giao dịch.
Cơ chế này thường được gọi là idempotency.
Mục tiêu là:
> cùng một yêu cầu nghiệp vụ, dù được gửi lại nhiều lần, cũng chỉ tạo một kết quả tài chính hợp lệ.
Lớp 6: Khóa xử lý đồng thời
Một rủi ro khác là hai tiến trình cùng lúc đọc cùng một số khả dụng rồi đều quyết định rằng người lao động đủ điều kiện.
Ví dụ:
- số khả dụng: 1.000.000 đồng;
- hai yêu cầu 800.000 đồng chạy gần như cùng lúc;
- nếu không khóa, cả hai đều có thể thấy 1.000.000 đồng và cùng chi.
Hệ thống cần khóa hoặc kiểm soát đồng thời để chỉ một tiến trình cập nhật số dư tại một thời điểm.
Lớp 7: Timeout phải fail-closed
Đây là một trong những lớp quan trọng nhất.
Nếu lệnh đã gửi ngân hàng nhưng hệ thống không nhận được phản hồi rõ:
- không được tự kết luận thất bại;
- không nên mở lại số tiền ngay;
- không cho gửi lệnh khác trên cùng phần giá trị;
- cần giữ giao dịch ở trạng thái chờ;
- tra soát đến khi có bằng chứng.
Nguyên tắc là: chậm xác nhận tốt hơn chi hai lần.
Vì sao retry tự động có thể nguy hiểm?
Retry tốt cho nhiều loại API, nhưng với chuyển tiền phải có điều kiện.
Nếu lần đầu thực tế đã thành công nhưng phản hồi bị mất, retry có thể tạo lệnh thứ hai.
Vì vậy trước khi retry cần biết:
- ngân hàng có hỗ trợ mã idempotency không;
- mã tham chiếu có giữ nguyên không;
- trạng thái cũ đã được tra soát chưa;
- có bằng chứng lệnh đầu thất bại không.
Không được retry “mù”.
Đối soát giúp phát hiện trùng như thế nào?
Cuối ngày hoặc theo chu kỳ, cần so sánh:
- sổ giao dịch EWA;
- sao kê ngân hàng;
- payroll/khoản đã nhận.
Các dấu hiệu bất thường:
- hai dòng sao kê cùng người, cùng số tiền, gần cùng thời điểm;
- một mã giao dịch xuất hiện nhiều lần;
- sao kê có giao dịch nhưng hệ thống không có;
- hệ thống báo thành công nhưng sao kê không thấy;
- cùng ngày công liên kết với số tiền vượt giá trị đủ điều kiện.
Các ngoại lệ phải được giữ riêng và có chủ xử lý.
Một dashboard chống trùng nên có gì?
- số bản ghi công nghi trùng;
- số bản ghi công bị loại;
- số yêu cầu lặp;
- số giao dịch bị idempotency chặn;
- số giao dịch chờ;
- số retry;
- số trường hợp chi trùng xác nhận;
- số chênh lệch EWA–ngân hàng;
- số ngày công bị dùng vượt giá trị;
- thời gian xử lý ngoại lệ.
Nếu không đo, doanh nghiệp khó biết kiểm soát có thực sự hiệu quả.
Khi phát hiện chi trùng thì làm gì?
Không nên xóa một giao dịch để làm báo cáo “khớp”.
Cần:
- giữ nguyên cả hai giao dịch;
- xác định giao dịch gốc và giao dịch trùng;
- đối chiếu ngân hàng;
- xác định nguyên nhân kỹ thuật;
- xử lý thu hồi/bù trừ theo chính sách được phê duyệt;
- ghi audit trail;
- sửa lỗi hệ thống;
- rà các giao dịch tương tự.
Mục tiêu là khắc phục nguyên nhân, không chỉ sửa số cuối.
Khi phát hiện công trùng thì làm gì?
Cần:
- xác định bản ghi gốc;
- khóa bản ghi nghi trùng khỏi phép tính;
- kiểm tra đã có giao dịch liên quan chưa;
- nếu chưa có tiền, sửa dữ liệu và tính lại;
- nếu đã có tiền, đưa vào đối soát;
- giữ lịch sử trước/sau.
Không nên xóa dấu vết một cách thủ công.
Kết luận
Chống trùng trong EWA là một chuỗi kiểm soát nhiều lớp: khóa định danh đúng, loại công trùng, chỉ dùng công đã duyệt, đánh dấu phần giá trị đã dùng, mã giao dịch bất biến, khóa xử lý đồng thời và fail-closed khi trạng thái chưa rõ. Khi những lớp này kết hợp với đối soát, hệ thống có thể giảm đáng kể rủi ro cùng một ngày công hoặc cùng một yêu cầu tạo ra tiền nhiều lần.
[TÊN TÁC GIẢ] — [Chức vụ]
Câu hỏi thường gặp
Idempotency có đủ để chống chi trùng không?
Không. Còn cần khóa đồng thời, fail-closed, đối soát và kiểm soát số khả dụng.
Một người làm nhiều khách hàng có bị coi là trùng không?
Không nếu dữ liệu đúng và khóa khách hàng/nơi làm việc được tách rõ. Trùng phải được xác định theo toàn bộ ngữ cảnh.
Giao dịch timeout có nên gửi lại sau vài phút không?
Không nên nếu chưa xác định giao dịch đầu đã thất bại. Cần tra soát trước.
Cuối kỳ payroll có thể phát hiện hết chi trùng không?
Không nên phụ thuộc vào cuối kỳ. Chống trùng cần bắt đầu từ thời điểm dữ liệu và giao dịch phát sinh.