Đặc tả nghiệp vụ quản lý tồn kho

Phiên bản: 1.0
Ngày cập nhật: 12/08/2026
Phạm vi: Nhập kho, xuất kho, kiểm kho, điều chỉnh tồn, số dư tồn kho và nhật ký kho.

1. Mục tiêu và nguyên tắc chung

Hệ thống quản lý tồn kho phải đáp ứng các yêu cầu sau:

  • Số dư hiện tại được truy vấn nhanh nhưng vẫn có thể tái dựng từ lịch sử giao dịch.
  • Không phát sinh tồn âm trong quy trình thông thường.
  • Mọi thay đổi tồn kho đều truy vết được tới chứng từ nguồn, người thao tác và lý do.
  • Chứng từ đã hạch toán không được sửa hoặc xóa trực tiếp.
  • Sai dữ liệu phải được sửa bằng giao dịch bù hoặc phiếu hoàn tác.
  • Các thao tác cập nhật số dư, reservation và ledger phải có tính nguyên tử.
  • Một request gửi lại không được hạch toán trùng.

Hiện tại hệ thống chỉ có một kho. UI không cần yêu cầu người dùng chọn kho, nhưng backend vẫn phải lưu:

warehouse_id = DEFAULT_WAREHOUSE

Việc này giúp dữ liệu nhất quán và cho phép mở rộng nhiều kho trong tương lai.

2. Các đại lượng tồn kho

Hệ thống quản lý bốn đại lượng độc lập cho từng cặp warehouse_id + product_id/SKU.

Đại lượng Tên hiển thị trên UI Ý nghĩa Cách xác định
on_hand Tồn thực tế Số lượng hàng hiện đang có trong kho và đã được hạch toán Tổng delta_on_hand từ stock ledger
reserved Đang giữ hàng Số lượng vẫn nằm trong kho nhưng đã được dành cho các phiếu xuất ở trạng thái chuẩn bị hàng Tổng reservation còn hiệu lực
available Có thể xuất Số lượng còn có thể tiếp tục giữ cho phiếu xuất mới hoặc xuất khỏi kho on_hand - reserved
incoming Sắp về Số lượng thuộc các phiếu nhập đã xác nhận nhưng chưa được nhập thực tế vào kho Tổng số lượng còn lại của các phiếu nhập đã xác nhận

2.1. Giải thích tên hiển thị trên giao diện

Tồn thực tế (on_hand)

Đây là số lượng đã được ghi nhận chính thức trong kho. Chỉ các nghiệp vụ đã hạch toán như hoàn tất nhập kho, hoàn tất xuất kho, hoàn thành kiểm kho, điều chỉnh tồn hoặc hoàn tác mới làm thay đổi số này.

on_hand không bao gồm hàng mới đặt mua hoặc phiếu nhập chưa hoàn thành.

Đang giữ hàng (reserved)

Đây là hàng vẫn còn trong kho nhưng đã được phân bổ cho phiếu xuất đang chuẩn bị. User khác không được tiếp tục sử dụng phần hàng này để tạo reservation mới.

Khi phiếu xuất hoàn tất, số lượng được giảm đồng thời khỏi on_handreserved. Khi phiếu xuất bị hủy trước khi hoàn tất, hệ thống chỉ giải phóng reserved.

Có thể xuất (available)

Đây là số lượng thực sự còn có thể cấp cho phiếu xuất mới:

available = on_hand - reserved

Mọi kiểm tra đủ hàng khi chuyển phiếu xuất sang Chuẩn bị hàng phải sử dụng available, không sử dụng riêng on_hand.

Sắp về (incoming)

incoming tương đương với nhãn Sắp về trên giao diện.

Số này biểu thị hàng dự kiến sẽ vào kho từ các phiếu nhập đã được xác nhận, nhưng chưa được nhận và chưa hạch toán vào tồn thực tế. Vì vậy:

  • Sắp về không thuộc Tồn thực tế.
  • Sắp về không được cộng vào Có thể xuất.
  • Khi phiếu nhập được xác nhận, Sắp về tăng.
  • Khi hàng được nhập kho, Sắp về giảm và Tồn thực tế tăng tương ứng.
  • Khi phiếu nhập đã xác nhận bị hủy, phần số lượng còn lại được loại khỏi Sắp về.

Tooltip đề xuất trên UI:

Số lượng từ các phiếu nhập đã xác nhận nhưng chưa nhập kho.

Khi nhấn vào giá trị Sắp về, UI nên mở danh sách các phiếu nhập liên quan, gồm mã phiếu, số lượng còn lại, ngày dự kiến nhận và trạng thái. Phiếu quá ngày dự kiến vẫn được tính trong Sắp về, đồng thời hiển thị cảnh báo Trễ dự kiến.

2.2. Công thức chuẩn

available = on_hand - reserved

incoming = Σ(
  confirmed_quantity
  - received_quantity
  - cancelled_quantity
)

incoming không được cộng vào available vì hàng chưa có trong kho.

Nếu UI cần hiển thị số dự báo, sử dụng tên riêng để tránh nhầm với tồn hiện tại:

projected_on_hand  = on_hand + incoming
projected_available = available + incoming

2.3. Bất biến dữ liệu

Tại mọi thời điểm, hệ thống phải đảm bảo:

on_hand >= 0
reserved >= 0
reserved <= on_hand
incoming >= 0
available = on_hand - reserved

Backend không được dùng max(0, on_hand - reserved) để che giấu dữ liệu sai.

3. Ma trận ảnh hưởng số dư

Trong bảng dưới đây, q là số lượng trên dòng chứng từ và d là chênh lệch kiểm kho hoặc điều chỉnh.

Nghiệp vụ Sự kiện on_hand reserved incoming available
Nhập kho Tạo phiếu mới 0 0 0 0
Nhập kho Xác nhận phiếu 0 0 +q 0
Nhập kho Nhập hàng +q 0 -q +q
Nhập kho Hủy sau xác nhận 0 0 -q 0
Hoàn tác nhập Hoàn tác phiếu đã nhập -q 0 0 -q
Xuất kho Tạo phiếu mới 0 0 0 0
Xuất kho Chuẩn bị hàng 0 +q 0 -q
Xuất kho Hoàn tất -q -q 0 0
Xuất kho Hủy khi chuẩn bị 0 -q 0 +q
Hoàn tác xuất Hoàn tác phiếu đã xuất +q 0 0 +q
Kiểm kho Bắt đầu kiểm 0 0 0 0
Kiểm kho Hoàn thành +d 0 0 +d
Kiểm kho Hủy 0 0 0 0
Điều chỉnh tồn Hoàn thành +d 0 0 +d
Hoàn tác điều chỉnh Hoàn tác phiếu đã hoàn thành -d 0 0 -d

4. Nghiệp vụ nhập kho

4.1. Trạng thái

Mới → Đã xác nhận → Đã nhập hàng
  └──────────────→ Đã hủy
Trạng thái Ảnh hưởng số dư Khả năng sửa
Mới Không ảnh hưởng Được sửa toàn bộ
Đã xác nhận Tăng incoming; chưa tăng on_hand Hạn chế; không đổi sản phẩm sau khi đã duyệt
Đã nhập hàng Tăng on_hand, giảm incoming Không được sửa
Đã hủy Không ảnh hưởng hoặc giải phóng incoming Không được sửa
Đã hoàn tác Đã có giao dịch bù ngược dấu Không được sửa

4.2. Quy tắc hạch toán

  • Chỉ trạng thái Đã nhập hàng mới cộng on_hand.
  • Phiếu Đã xác nhận chỉ làm tăng incoming.
  • Chuyển sang Đã nhập hàng phải hạch toán toàn bộ dòng sản phẩm trong một database transaction.
  • Nếu một dòng thất bại, rollback toàn bộ phiếu.
  • Mỗi phiếu chỉ được hạch toán một lần.
  • Giá nhập tại thời điểm hoàn tất phải được lưu thành snapshot.
  • Giá snapshot không thay đổi khi giá sản phẩm thay đổi sau này.

4.3. Nhập hàng một phần

Phiên bản đầu có thể chỉ hỗ trợ nhập toàn bộ. Nếu hỗ trợ nhập một phần, mỗi lần nhận r sản phẩm:

on_hand  += r
incoming -= r

Phiếu chỉ chuyển sang Đã nhập hàng khi toàn bộ số lượng được nhận; nếu chưa đủ có thể sử dụng trạng thái Đã nhập một phần.

Phiếu đã quá ngày dự kiến nhưng chưa nhận đủ vẫn được tính trong incoming và phải được đánh dấu Trễ dự kiến.

4.4. Hủy và hoàn tác nhập kho

  • Phiếu mới có thể sửa hoặc hủy.
  • Phiếu đã xác nhận có thể hủy nếu chưa hạch toán; hệ thống phải giảm phần incoming còn lại.
  • Phiếu đã nhập hàng không được hủy trực tiếp.
  • Muốn sửa phiếu đã nhập phải hoàn tác phiếu cũ, sau đó sao chép thành phiếu mới.
  • Không cho hoàn tác nếu kết quả làm on_hand < reserved.

Điều kiện hoàn tác một dòng nhập:

quantity_to_reverse <= available

5. Nghiệp vụ xuất kho và giữ hàng

5.1. Trạng thái

Mới → Chuẩn bị hàng → Hoàn tất
  └───────────────→ Đã hủy
Trạng thái Ảnh hưởng số dư
Mới Không ảnh hưởng
Chuẩn bị hàng Tăng reserved, giảm available
Hoàn tất Giảm on_hand, đồng thời giải phóng reserved
Hủy khi chuẩn bị Giải phóng reserved
Hoàn tác sau hoàn tất Cộng lại on_hand bằng giao dịch bù

5.2. Tạo reservation

Phiếu mới không giữ hàng. Reservation chỉ được tạo khi chuyển sang Chuẩn bị hàng.

Điều kiện:

requested_quantity <= available

Khi thành công:

reserved  += requested_quantity
available -= requested_quantity

Reservation phải gắn với từng dòng phiếu xuất để xác định hàng đang được giữ cho phiếu nào.

5.3. Hoàn tất xuất kho

Khi hoàn tất, hệ thống phải xác nhận reservation của chính dòng phiếu vẫn còn đủ:

reservation_quantity >= requested_quantity
on_hand >= requested_quantity

Sau đó thực hiện trong cùng transaction:

on_hand  -= requested_quantity
reserved -= requested_quantity

available không thay đổi tại bước hoàn tất vì lượng hàng đó đã được trừ khỏi available khi tạo reservation.

Ví dụ:

Trước chuẩn bị: on_hand = 10, reserved = 0, available = 10
Sau chuẩn bị 6: on_hand = 10, reserved = 6, available = 4
Sau hoàn tất:   on_hand = 4,  reserved = 0, available = 4

5.4. Hủy và sửa phiếu đang chuẩn bị

Khi hủy phiếu đang chuẩn bị:

reserved  -= reserved_quantity
available += reserved_quantity

Nếu cần sửa sản phẩm hoặc số lượng của phiếu đang chuẩn bị, quy trình an toàn là:

  1. Giải phóng toàn bộ reservation của phiếu.
  2. Chuyển phiếu về trạng thái được phép sửa.
  3. Sửa dòng sản phẩm.
  4. Chuyển lại sang Chuẩn bị hàng.
  5. Kiểm tra available và tạo reservation mới.

Không được tự động hoàn tất một phần phiếu nếu một dòng không đủ hàng.

5.5. Hoàn tác xuất kho

Phiếu đã hoàn tất không được đổi trạng thái ngược về Chuẩn bị hàng. Hệ thống tạo giao dịch hoàn tác:

original_delta = -q
reversal_delta = +q

Phiếu gốc chuyển sang Đã hoàn tác nhưng không bị xóa.

6. Nghiệp vụ kiểm kho

6.1. Mục tiêu

Kiểm kho dùng để đối chiếu số lượng vật lý với số lượng hệ thống, phù hợp cho kiểm kê định kỳ, kiểm nhiều SKU hoặc kiểm theo đợt.

Kiểm đếm vật lý phải so với on_hand, không so với available. Hàng đang reserved vẫn còn nằm trong kho cho đến khi phiếu xuất hoàn tất.

6.2. Dữ liệu khi bắt đầu

Khi tạo phiếu, hệ thống lưu snapshot:

  • on_hand_at_start.
  • Thời điểm bắt đầu.
  • Danh sách sản phẩm/SKU.
  • Người tạo và người kiểm.

Nhân viên chỉ nhập số lượng kiểm đếm thực tế. Hệ thống tự tính tồn kỳ vọng, chênh lệch và tồn sau điều chỉnh.

6.3. Công thức khi hoàn thành

expected_on_hand =
  on_hand_at_start
  + Σ(delta_on_hand phát sinh từ lúc bắt đầu đến lúc hoàn thành)

variance = counted_quantity - expected_on_hand

adjusted_on_hand = expected_on_hand + variance
                 = counted_quantity

Tổng biến động trong thời gian kiểm phải bao gồm nhập, xuất, hoàn tác và các điều chỉnh khác.

6.4. Trạng thái

Trạng thái Ảnh hưởng tồn
Đang kiểm hàng Không ảnh hưởng
Hoàn thành Ghi giao dịch điều chỉnh bằng variance
Hủy Không ảnh hưởng
Đã hoàn tác Ghi giao dịch bù ngược dấu

Thời gian hoàn thành phải để trống khi tạo và chỉ được hệ thống ghi khi phiếu chuyển sang Hoàn thành.

6.5. Kiểm tra reservation

Trước khi hoàn thành phải đảm bảo:

adjusted_on_hand >= reserved

Nếu số lượng kiểm đếm thấp hơn lượng đang giữ, hệ thống phải chặn và yêu cầu người có quyền xử lý các phiếu xuất liên quan. Không tự động giải phóng hoặc hủy reservation.

Nên chỉ cho phép một phiếu kiểm kho đang hoạt động trên cùng SKU và kho.

7. Điều chỉnh tồn trực tiếp từ quản lý sản phẩm

7.1. Nguyên tắc

Cho phép user thao tác nhanh tại Product Detail, nhưng không cho sửa trực tiếp trường on_hand.

Mỗi lần thay đổi phải tự động tạo một Phiếu điều chỉnh tồn kho. Không ngầm tạo phiếu nhập hoặc phiếu xuất thông thường vì sẽ làm sai bản chất và báo cáo nghiệp vụ.

7.2. Lựa chọn chứng từ

Tình huống Chứng từ cần dùng
Nhận hàng từ nhà cung cấp hoặc khách trả hàng Phiếu nhập kho
Giao hàng, xuất dùng hoặc trả nhà cung cấp Phiếu xuất kho
Phát hiện thừa/thiếu, hư hỏng, mất hàng hoặc sửa dữ liệu Phiếu điều chỉnh tồn
Kiểm đếm định kỳ hoặc nhiều SKU Phiếu kiểm kho
Khai báo số dư ban đầu Phiếu điều chỉnh – Tồn đầu kỳ

7.3. Trạng thái phiếu điều chỉnh

Trạng thái Ảnh hưởng tồn
Nháp Không ảnh hưởng
Chờ duyệt Không ảnh hưởng
Hoàn thành Cộng hoặc trừ on_hand
Từ chối/Hủy Không ảnh hưởng
Đã hoàn tác Ghi giao dịch bù ngược dấu

Nếu user có cả quyền tạo và duyệt, hệ thống có thể tạo rồi hoàn thành phiếu ngay. Nếu chỉ có quyền đề xuất, phiếu phải ở trạng thái Chờ duyệt.

Điều chỉnh vượt ngưỡng số lượng hoặc giá trị cấu hình phải được quản lý phê duyệt.

7.4. Cách nhập số lượng

Modal điều chỉnh hỗ trợ một trong hai cách:

  1. Nhập Số lượng thực tế mới; hoặc
  2. Nhập Số lượng tăng/giảm.

Không cho nhập đồng thời cả hai.

Giả sử:

OH = on_hand hiện tại
R  = reserved
T  = số lượng thực tế mới
D  = T - OH

Kết quả:

on_hand'   = OH + D
reserved'  = R
available' = on_hand' - reserved'
incoming'  = incoming

Điều chỉnh tồn không làm thay đổi reserved hoặc incoming.

7.5. Điều kiện giảm tồn

Bắt buộc:

T >= reserved

Tương đương:

abs(negative_delta) <= available

Nếu không đạt, hệ thống phải chặn và hiển thị các phiếu xuất đang giữ hàng. Không tự động giải phóng reservation.

7.6. Lý do điều chỉnh

Bắt buộc chọn một lý do chuẩn hóa:

  • Tồn đầu kỳ.
  • Phát hiện thừa.
  • Phát hiện thiếu.
  • Hàng hư hỏng.
  • Hàng thất thoát.
  • Sửa sai dữ liệu.
  • Dùng nội bộ.
  • Khác — bắt buộc nhập giải thích.

Nếu tăng tồn và hệ thống quản lý giá vốn, cần lưu giá trị hoặc đơn giá điều chỉnh. Giá có thể mặc định theo giá vốn hiện tại nhưng thay đổi thủ công phải có quyền riêng.

8. Hủy, hoàn tác và mở lại chứng từ

8.1. Trước khi hạch toán

  • Phiếu mới có thể sửa hoặc hủy.
  • Phiếu nhập đã xác nhận có thể hủy nếu chưa nhập hàng; giải phóng incoming.
  • Phiếu xuất đang chuẩn bị có thể hủy; giải phóng reserved.
  • Phiếu kiểm đang kiểm có thể hủy; không tạo giao dịch điều chỉnh.
  • Phiếu điều chỉnh nháp hoặc chờ duyệt có thể hủy; không ảnh hưởng tồn.

8.2. Sau khi hạch toán

Không cho phép:

  • Sửa sản phẩm, số lượng hoặc giá.
  • Xóa chứng từ.
  • Đổi trực tiếp trạng thái về trước.
  • Ghi đè ledger cũ.
  • Mở lại chứng từ đã hoàn thành.

Thay vào đó, cung cấp thao tác Yêu cầu hoàn tác.

Chứng từ gốc Giao dịch hoàn tác
Nhập kho +10 -10
Xuất kho -10 +10
Kiểm kho điều chỉnh -3 +3
Điều chỉnh tồn +5 -5

8.3. Quy trình hoàn tác

  1. User chọn Yêu cầu hoàn tác.
  2. User bắt buộc nhập lý do.
  3. Người có quyền quản lý kho phê duyệt.
  4. Hệ thống tạo chứng từ hoàn tác mới, tham chiếu chứng từ gốc.
  5. Hệ thống ghi giao dịch tồn ngược dấu.
  6. Chứng từ gốc chuyển sang Đã hoàn tác và không bị xóa.

Một chứng từ chỉ được hoàn tác toàn bộ một lần. Nếu hỗ trợ hoàn tác một phần, phải tạo chứng từ mới ghi rõ từng dòng và số lượng hoàn tác.

9. Chống xuất âm và xử lý đồng thời

9.1. Kiểm tra tồn

Không cho phép:

requested_quantity > available

Kiểm tra diễn ra hai lần:

  • Trên UI khi user chọn sản phẩm, nhằm cảnh báo sớm.
  • Tại backend khi chuyển sang Chuẩn bị hàng, đây là kiểm tra có hiệu lực cuối cùng.

Khi hoàn tất xuất, backend kiểm tra reservation của chính phiếu thay vì trừ thêm từ available.

Thông báo lỗi phải cung cấp đủ ngữ cảnh, ví dụ:

Sản phẩm A yêu cầu 8; tồn thực tế 10;
đang giữ 6; có thể xuất 4.

9.2. Nhiều người thao tác đồng thời

Ví dụ available ban đầu bằng 10:

  1. User A yêu cầu giữ 7.
  2. Gần đồng thời, user B yêu cầu giữ 5.
  3. A thành công trước, available còn 3.
  4. B thất bại do không đủ tồn.

Backend phải:

  • Khóa bản ghi stock balance của từng sản phẩm khi kiểm tra và cập nhật.
  • Kiểm tra và tạo reservation trong cùng database transaction.
  • Khóa SKU theo thứ tự cố định khi chứng từ có nhiều dòng để tránh deadlock.
  • Rollback toàn bộ chứng từ nếu bất kỳ dòng nào thất bại.
  • Sử dụng idempotency_key để request gửi lại không tạo giao dịch trùng.
  • Không cho override xuất âm trong quy trình thông thường.

Nếu tương lai cần ngoại lệ xuất âm, đây phải là quyền quản lý riêng và tạo cảnh báo audit mức cao.

10. Stock balance, reservation và incoming

10.1. Stock balance

Stock balance là dữ liệu tổng hợp hiện tại để truy vấn nhanh. Tối thiểu gồm:

warehouse_id
product_id / sku_id
on_hand
reserved
version
updated_at

available nên được tính từ on_hand - reserved, không lưu thành nguồn dữ liệu độc lập nếu không cần tối ưu đặc biệt.

10.2. Reservation

Reservation tối thiểu gồm:

reservation_id
warehouse_id
product_id / sku_id
source_document_type
source_document_id
source_line_id
quantity
status
created_at
released_at

reserved trong stock balance phải bằng tổng reservation đang hiệu lực.

10.3. Incoming

incoming có thể tính từ các dòng phiếu nhập đã xác nhận. Nếu cần cache để truy vấn nhanh, cache phải được cập nhật trong cùng transaction với thay đổi trạng thái phiếu nhập.

11. Stock ledger bất biến

11.1. Mục đích

Cần tách hai lớp dữ liệu:

  • Stock balance: số dư hiện tại để truy vấn nhanh.
  • Stock ledger: sổ cái bất biến để kiểm tra, audit và tái dựng số dư.

Mọi thay đổi on_hand hoặc reserved phải tạo một dòng ledger. Sự kiện chỉ thay đổi reservation vẫn phải được ghi với delta_on_hand = 0.

11.2. Trường dữ liệu tối thiểu

Trường Ý nghĩa
Mã giao dịch ID duy nhất
Sản phẩm/SKU Sản phẩm bị ảnh hưởng
Kho DEFAULT_WAREHOUSE trong phiên bản hiện tại
Loại giao dịch Nhập, xuất, giữ hàng, giải phóng, kiểm kho, điều chỉnh, hoàn tác
Nguồn Loại chứng từ và mã chứng từ
Dòng nguồn ID dòng sản phẩm trong chứng từ
delta_on_hand Số lượng thay đổi tồn thực tế
on_hand_before/after Tồn thực tế trước và sau
reserved_before/after Giữ hàng trước và sau
Người thực hiện Tài khoản thao tác
Thời điểm nghiệp vụ Khi nghiệp vụ có hiệu lực
Thời điểm ghi hệ thống Khi dữ liệu được lưu
Lý do Bắt buộc với kiểm kho, điều chỉnh và hoàn tác
Giao dịch gốc Bắt buộc với giao dịch hoàn tác
idempotency_key Chống ghi trùng

11.3. Quy tắc ledger

  • Không được update hoặc delete ledger.
  • Sai dữ liệu phải sửa bằng giao dịch bù.
  • Cập nhật balance, reservation và ghi ledger nằm trong cùng transaction.
  • Từ ledger phải tái dựng được cả on_handreserved.
  • Mỗi thay đổi trạng thái chứng từ phải có audit log riêng, kể cả trạng thái không ảnh hưởng số dư.

12. Quyền và phê duyệt

Nên tách các quyền:

inventory.view
inventory.receive.create
inventory.receive.complete
inventory.issue.create
inventory.issue.reserve
inventory.issue.complete
inventory.count.create
inventory.count.complete
inventory.adjust.create
inventory.adjust.approve
inventory.reverse.request
inventory.reverse.approve

Các trường hợp cần phê duyệt quản lý:

  • Điều chỉnh vượt ngưỡng số lượng hoặc giá trị.
  • Hoàn tác chứng từ đã hạch toán.
  • Thay đổi giá trị tồn khi tăng điều chỉnh.
  • Ngoại lệ xuất âm nếu được hỗ trợ trong tương lai.

13. Yêu cầu UI tại Product Detail

13.1. Khối kho hàng

Hiển thị rõ bốn chỉ số:

Tồn thực tế       123
Đang giữ hàng       8
Có thể xuất        115
Sắp về              50
  • on_hand, reserved, available, incoming đều là read-only.
  • Đang giữ hàng có link tới các phiếu xuất đang chuẩn bị.
  • Sắp về có link tới các phiếu nhập đã xác nhận.
  • Cung cấp nút Điều chỉnh tồn cho user có quyền.

13.2. Modal điều chỉnh tồn

Hiển thị:

  • SKU và tên sản phẩm.
  • Tồn thực tế hiện tại.
  • Đang giữ hàng.
  • Có thể xuất.
  • Số lượng thực tế mới hoặc số lượng tăng/giảm.
  • Bản xem trước tồn sau điều chỉnh.
  • Lý do bắt buộc.
  • Ghi chú và chứng từ đính kèm.
  • Giá/giá trị điều chỉnh khi tăng tồn nếu quản lý giá vốn.

13.3. Lịch sử kho

Lịch sử mặc định ở trạng thái collapsed. Khi mở rộng, bảng tối thiểu có:

  • Thời gian nghiệp vụ.
  • Loại giao dịch.
  • Mã chứng từ.
  • Số lượng thay đổi.
  • Tồn trước.
  • Tồn sau.
  • Giữ hàng trước/sau khi cần.
  • Người thực hiện.
  • Lý do.

Mã phiếu nhập, xuất, kiểm kho và điều chỉnh phải là link mở trực tiếp chứng từ nguồn.

14. Ví dụ tổng hợp

Trạng thái ban đầu:

on_hand  = 100
reserved = 10
available = 90
incoming = 50

Bước 1 — Xác nhận thêm phiếu nhập 20

on_hand  = 100
reserved = 10
available = 90
incoming = 70

Bước 2 — Chuẩn bị phiếu xuất 15

on_hand  = 100
reserved = 25
available = 75
incoming = 70

Bước 3 — Hoàn tất phiếu xuất 15

on_hand  = 85
reserved = 10
available = 75
incoming = 70

Bước 4 — Nhập hàng 20

on_hand  = 105
reserved = 10
available = 95
incoming = 50

Bước 5 — Điều chỉnh giảm 5 do hư hỏng

on_hand  = 100
reserved = 10
available = 90
incoming = 50

Hệ thống tạo phiếu điều chỉnh, ghi ledger delta_on_hand = -5 và bắt buộc lưu lý do Hàng hư hỏng.

15. Tiêu chí nghiệm thu cốt lõi

  • Chỉ phiếu nhập đã hạch toán mới tăng on_hand.
  • Phiếu nhập đã xác nhận tăng incoming, không tăng available.
  • Chuẩn bị xuất tăng reserved và giảm available.
  • Hoàn tất xuất giảm đồng thời on_handreserved.
  • Kiểm kho dùng on_hand, không dùng available, làm số đối chiếu.
  • Điều chỉnh trực tiếp luôn tạo phiếu điều chỉnh và ledger; không ghi đè số dư.
  • Không thao tác nào được làm reserved > on_hand hoặc available < 0.
  • Không hoàn tác nhập/điều chỉnh giảm nếu ảnh hưởng hàng đang giữ.
  • Một request lặp lại không được hạch toán lần thứ hai.
  • Chứng từ nhiều dòng được hạch toán toàn bộ hoặc rollback toàn bộ.
  • Ledger không thể sửa hoặc xóa và tái dựng được số dư.
  • Mọi thay đổi trạng thái chứng từ có audit log.