Fibretrace Monet docs/Business logic/Business logic (VI)

Business logic của FibreTrace

Tài liệu này mô tả sản phẩm như đang chạy trên BETA FibreTrace, project Lovable nơi Malcolm và team thực hiện mọi thay đổi về Dashboard và business logic từ nay về sau. Nội dung được đọc từ code và database live của BETA ngày 2026-09-22 (commit 39ae45a9) và kiểm lại ngày 2026-09-23. BETA chưa có dữ liệu nghiệp vụ nào, nên mọi hành vi dưới đây đều đọc từ code và rule của database, chưa được quan sát khi chạy thật.

Bản tiếng Anh là bản gốc; nếu hai bản lệch nhau thì theo bản tiếng Anh. Thuật ngữ sản phẩm (Claim Unit, purchase order, Fibre Verification Record…) và tên trạng thái được giữ nguyên tiếng Anh, vì đó là chữ hiển thị trên màn hình. Tài liệu viết “program” theo đúng màn hình; riêng tên bảng trong database vẫn là “programme”.

1. FibreTrace chứng minh điều gì

FibreTrace bán khả năng truy xuất vật lý. Một chất đánh dấu phát quang (pigment) được trộn vào xơ thô tại nhà máy cán bông (gin). Máy scan cầm tay Bluetooth, cùng thiết bị SDU gắn trên dây chuyền tại gin, phát hiện pigment đó ở các khâu sau của chuỗi cung ứng. Nền tảng biến các lần scan đó thành claim về nguồn gốc và khối lượng xơ, có thể được kiểm toán (audit) và chia sẻ.

Đây là cân bằng khối lượng (mass balance), không phải truy vết từng sản phẩm. Nền tảng chứng minh xơ đã đánh dấu đi qua các cơ sở, và đối chiếu khối lượng claim với khối lượng đã scan và đã sản xuất. Nó không bao giờ khẳng định “kiện bông này thành chiếc áo này”.

2. Tier và persona

Tier được đếm ngược từ kệ hàng:

Tier Là ai Ví dụ
Tier 0 Nhà bán lẻ / thương hiệu (brand) Target
Tier 1 Nhà sản xuất cuối (cắt-may-hoàn thiện) ACME Apparel
Tier 2 Vải và phụ liệu ACME Fabrics
Tier 3 Chế biến nguyên liệu (kéo sợi) ACME Spinning
Tier 4 Nhà sản xuất xơ thô (gin) Sundown Gin

Công ty tự khai tier trong Settings, và tier chỉ là nhãn. Một công ty làm được gì là do dữ liệu của nó quyết định, không bao giờ do tier:

  • Fibre producer: đã đăng ký làm producer trong một fibre program.
  • Manufacturer: đã nhận ít nhất một purchase order. Mục “Link Purchase Orders” chỉ hiện trong menu sau khi nhận được PO đầu tiên.
  • Retailer / brand: loại công ty là brand hoặc retailer, có tham gia một program, và không phải producer.
  • Program owner: sở hữu một program; trang Home của nó là owner dashboard.
  • Công ty Tier 2/3: scan hàng vào và hàng ra để dựng chain of custody, và không bao giờ claim.
  • Auditor: vai trò hiện trường, chỉ scan. Auditor đăng nhập vào FibreTrace Scanning App, không vào Dashboard.
  • FibreTrace admin: nhân viên nền tảng. Họ tạo program, đăng ký producer và participant, cấp reservation, cấu hình product category, cấp license và máy scan, duyệt đăng ký.

3. Thuật ngữ

Dùng đúng các từ sau, vì đó là chữ trên màn hình của BETA.

Thuật ngữ Nghĩa
Fibre program Đơn vị chứa thương mại: một loại xơ, một pigment, các producer đã đăng ký (mỗi producer có trần sản lượng), các brand tham gia, và một công ty owner.
Claim Unit (CU) Đơn vị xơ được claim. 1 CU = 1 kg, 1000 CU = 1 MT. Từ “credit unit” không còn xuất hiện ở đâu trên BETA.
Reservation Dung lượng trong program mà FibreTrace cấp cho brand. Mặc định hết hạn 18 tháng sau khi ghi nhận. Một dòng âm nghĩa là giải phóng (release).
Purchase order (PO) Cam kết thương mại của brand với một nhà sản xuất Tier 1, gồm các product line. Một số màn hình gọi là nomination.
Verification / scan session Một lần scan tại một cơ sở, gồm nhiều scan. Danh sách của nó là “Scan Records” / “History”.
Claim Tuyên bố của manufacturer, được brand xác nhận, rằng xơ đã truy vết được dùng cho một PO. Một số màn hình gọi cùng thứ này là “verification claim” hoặc “Fibre Claim”.
Fibre Verification Record Kết quả của một claim đã xác nhận, sẵn sàng để chia sẻ. Danh sách claim có tiêu đề “Verification Records”.
Share Một công ty cho đối tác xem một scan của mình. Mỗi share được đánh dấu exclusive hoặc non-exclusive.

4. Cách tính Claim Unit

Với mỗi PO line: CU cho một sản phẩm = khối lượng tịnh thực tế (kg) x % blend x một hệ số hao hụt nội bộ; CU của cả line = CU cho một sản phẩm x số lượng. % blend lấy từ product category do FibreTrace admin cấu hình, không bao giờ hard-code. Giá trị của category được điền sẵn vào mỗi line. Line nào lệch khỏi category sẽ bị gắn cờ và hiện trong báo cáo override của admin. Database luôn giữ CU và MT khớp nhau (CU = MT x 1000) trên purchase order, claim và reservation. Tổng CU của một PO là tổng các line không bị loại trừ, tính lại mỗi khi line thay đổi.

5. Dung lượng của program

Chỉ số Là gì
Ceiling Tổng trần sản lượng của các producer đã đăng ký.
Activated Khối lượng producer đã thực sự sản xuất. Đây là nguồn mà các claim rút từ đó.
Reserved Dung lượng đã cấp cho các brand. Tổng reservation chưa hết hạn không được vượt ceiling của program.
Pending Reserved trừ activated, không nhỏ hơn 0.
Available to claim Với một brand, lấy số nhỏ hơn: reservation của brand trừ phần brand đã claim, hoặc tổng khối lượng đã sản xuất của program trừ tổng đã claim.

Các rule phía sản xuất:

  • Trên màn hình, sản lượng chỉ được tính sau khi admin duyệt. Mọi màn hình chỉ cộng sản lượng nhập tay vào khối lượng program sau khi FibreTrace admin duyệt; các record ở trạng thái draft, submitted, pending, rejected và cancelled không được tính ở đó. Phép kiểm tra trần trong database bỏ qua trạng thái duyệt, nên sản lượng chưa duyệt vẫn được tính ở đó (xem mục 12).
  • Khi admin duyệt, ceiling chỉ là cảnh báo, không chặn. Admin thấy cảnh báo “Over ceiling” và vẫn có thể tiếp tục, vì giới hạn thật là lượng pigment thực tế đã trộn vào xơ, được kiểm soát ngoài hệ thống.
  • Sản lượng của mỗi producer bị giới hạn ở trần của nó.
  • Số lần scan cần cho một production record: 1 nếu dữ liệu đến từ SDU hoặc import EWR. Với record nhập tay: 5% số lượng làm tròn lên, tối thiểu 1.
  • Record bị khoá khoảng 30 ngày sau khi tạo.
  • Một pigment cho mỗi cơ sở trong mỗi khoảng thời gian. Một cơ sở không được phục vụ hai program đang hoạt động dùng cùng pigment trong các khoảng thời gian chồng nhau.

Cả các con số dung lượng trên màn hình lẫn trần trong database chỉ tính các claim không ở trạng thái proposed, rejected hay revoked. Claim đang proposed chưa chiếm dung lượng cho tới khi brand xác nhận.

6. Luồng chính: từ purchase order đến Fibre Verification Record

brand tạo PO ──► shared ──► manufacturer link scan ──► linked
                                                         │
                          manufacturer đề xuất claim ──► proposed
                                                         │
         brand xác nhận ──► partially_claimed ⟲ ──► fully_claimed ──► closed
         brand từ chối  ──► gỡ link, quay về shared
  1. Brand tạo PO cho một đối tác Tier 1. PO bắt đầu ở shared; bước draft không bao giờ được dùng. Mã PO là duy nhất theo cặp brand + đối tác khi PO chưa bị archive. PO chỉ gửi được cho đối tác, và một PO không bao giờ gồm hai nhà sản xuất.
  2. Manufacturer link scan vào PO. Một scan link được nếu cơ sở của nó thuộc manufacturer, hoặc nó đã được share cho manufacturer. Scan do manufacturer sở hữu vẫn link được dù được gửi cho bên khác (trường hợp nhập hàng từ tier dưới).
  3. Manufacturer đề xuất claim và tự khai số lượng, vì chỉ manufacturer biết số liệu sản xuất thực tế. Giới hạn duy nhất ở bước này là phần còn lại của PO. BETA chỉ hiển thị số dư program để tham khảo và không còn chặn theo nó.
  4. Brand xác nhận hoặc từ chối. Mọi trần theo reservation và program được database kiểm tra ở bước này.
  5. Xác nhận tạo ra Fibre Verification Record. Bước này cũng ghi khối lượng đã claim lên PO, chốt số lượng của từng line, chuyển PO sang partially_claimed hoặc fully_claimed, tạo link chia sẻ công khai cho record (mặc định là bật), và khoá các scan đã link đồng thời hiện verification ID đang bị ẩn của chúng. Database không giới hạn khối lượng claim theo phần còn lại của PO: claim lớn hơn sẽ nâng khối lượng của PO lên cho vừa. Giới hạn duy nhất nằm ở màn hình đề xuất.
  6. Claim từng phần lặp lại cho đến khi mọi line hoàn tất. Một PO có thể có nhiều claim, mỗi claim có bộ bằng chứng riêng.

Các rule chi phối luồng:

  • Mỗi scan thuộc tối đa một PO, mãi mãi.
  • Scan chỉ làm bằng chứng cho PO cùng fibre program. Pigment của program này không thể làm bằng chứng cho claim của program khác.
  • Bằng chứng chỉ dùng một lần trên toàn bộ các claim, bất kể công ty nào tạo claim. Mỗi claim chỉ dùng scan đã link của chính nó, không bao giờ thừa hưởng scan của claim khác.
  • Không đề xuất được claim khi chưa link scan nào.
  • Chỉ chặn link khi PO đã closed. PO fully_claimed vẫn nhận thêm link.
  • Claim thuộc về brand. Công ty của claim là công ty đã phát hành PO, vì reservation mà claim tiêu thụ là của brand.
  • Sau khi xác nhận, không bên nào sửa được. Từ chối thì lưu lý do, gỡ các link scan của PO và đưa PO về shared để manufacturer chọn scan lại. Trên thực tế, các scan của chính claim bị từ chối vẫn bị chặn (xem mục 12).
  • Retirement theo tier. Chỉ các scan đã link của Tier 1 bị “retire” (đánh dấu đã dùng) khi xác nhận. Scan của tier dưới không bao giờ bị retire và không bao giờ xuất hiện trên claim.
  • Admin revoke khác admin void. Revoke vô hiệu hoá claim và chứng nhận, và giữ scan đã link; màn hình admin nói revoke không trả lại Claim Unit nào, nhưng các phép kiểm tra dung lượng không còn tính claim đã revoke (xem mục 12). Void xoá claim, bằng chứng và các liên kết, đồng thời trả lại CU và scan.
  • Duyệt hàng loạt: đề xuất không thay đổi được chọn sẵn; dòng có chênh lệch phải chọn tay. Nếu một nhóm vượt dung lượng, “Deselect to fit” bỏ các mục nhỏ nhất trước. Xác nhận chạy lần lượt từng PO, không có rollback cho cả lô.
  • Scan tự link vào PO khi mã PO dạng text của một scan session mới khớp đúng một PO còn mở, chưa fully claimed, thuộc công ty của cơ sở đó. Việc này diễn ra trong database, không qua màn hình nào.

7. Share, exclusivity và việc ẩn verification ID

  • Mỗi share phải khai exclusivity. Khi share, người dùng phải trả lời “Is this scan exclusive?”: “Yes — exclusive to one partner” hoặc “Non-exclusive — also supplied to others”. Exclusive nghĩa là đúng một người nhận: khi scan đã có share exclusive, ô chọn đối tác bị đóng.
  • Evidence request cũng hỏi câu này. Supplier khi trả lời một evidence request (yêu cầu gửi scan, trong mục “Requests”) giờ chọn exclusive hoặc non-exclusive, và non-exclusive được chọn sẵn. Đây là điểm mới trên BETA.
  • Khi nào verification ID được hiện: verification ID và blockchain ID mặc định bị ẩn. Chúng chỉ hiện khi scan đã link vào một claim không có share non-exclusive nào trên scan đó. Chỉ một share non-exclusive là đủ để ID bị ẩn với mọi bên phía sau. Help centre mô tả trạng thái bị ẩn này là khó gỡ; nhưng trong code, xoá share non-exclusive đó thì trạng thái được đặt lại.
  • Gỡ share được thiết kế để có hiệu ứng dây chuyền. Rule của Malcolm: gỡ một share thì scan đó cũng bị gỡ khỏi những gì bên nhận đã dựng trên nó (liên kết claim và link PO). Trên thực tế, gỡ share chỉ tách scan khỏi các record thuộc sở hữu của công ty nhận share; vì claim và PO thuộc về brand, trường hợp thường gặp bị bỏ sót (xem mục 12). Chấm dứt quan hệ đối tác thì các share hiện có vẫn giữ nguyên.
  • Bản đồ Supply Chain: chỉ hiện các công ty nằm trên đường đi của hàng về brand, được nối với nhau bằng mã vận chuyển trên các scan. Đối tác hiện tên và địa điểm. Công ty trên đường đi mà chưa là đối tác hiện dòng “Identity hidden for commercial confidentiality”, kèm lựa chọn mời làm đối tác.
  • Truy cập công khai: công chúng chỉ xem được claim qua link công khai đang bật. Claim bị revoke hiện banner Revoked. Thứ mà sản phẩm gọi là “webhook” thực chất là một trang JSON để bên nhận tự lấy về; nền tảng không bao giờ đẩy dữ liệu ra ngoài.

8. Đăng nhập, role và quyền truy cập (mới trên BETA)

  • Đăng nhập thật. MVP cũ không có đăng nhập: nó lấy user từ trình duyệt, có một tài khoản mặc định. BETA dùng Supabase Auth với session thật.
  • Nhân viên đăng nhập ở /admin qua cổng riêng: mật khẩu được hash, khoá theo quốc gia (hiện là GB và VN), và giới hạn số lần thử. Khi đăng nhập thành công, nhân viên cũng được tạo session tài khoản bình thường và role platform-admin.
  • Hai hệ role tồn tại song song. Tài khoản khách hàng dùng role theo công ty (admin, auditor, pending). Nhân viên và các rule truy cập phía server dùng một bảng role riêng (platform_admin, company_admin, member). Hai hệ không trùng nhau và chưa có gì đối chiếu chúng.
  • Đăng nhập máy scan: dịch vụ đăng nhập máy scan của BETA (sat-auth) chỉ nhận tài khoản khách hàng có role công ty là admin hoặc auditor và không bị block hay xoá. Nó chỉ kiểm tra role công ty, nên tài khoản nhân viên bị từ chối.
  • Quyền truy cập dữ liệu hiện nay: truy cập ẩn danh đã bị đóng. Việc tách dữ liệu theo từng công ty như kế hoạch chưa áp dụng cho phần nghiệp vụ lõi. Purchase order, claim và scan session đang mở cho mọi user đã đăng nhập; chính migration của database mô tả đây là mức tối thiểu tạm thời có chủ ý. Vì BETA chưa có dữ liệu khách hàng, đây là câu hỏi thiết kế về thứ sẽ ship, không phải một lỗ hổng đang bị lộ.
  • Pigment ID ai cũng đọc được, và Scanning App cần điều đó. Chỉ nhân viên được sửa.

9. Email thông báo (mới trên BETA)

Sự kiện Gửi cho Tắt được không
PO được share manufacturer không
Scan được link vào PO brand không
Nhắc PO manufacturer
Claim được đề xuất brand
Claim được xác nhận manufacturer
Mời đối tác công ty được mời

10. BETA thay đổi gì so với MVP cũ

Mọi function trong database mang business logic đều giống hệt MVP, và gần như toàn bộ logic frontend cũng vậy. Danh sách đầy đủ các thay đổi về rule:

  1. Bước đề xuất của manufacturer chỉ bị giới hạn bởi số dư PO; trần theo số dư program đã bị bỏ.
  2. Thêm sáu email thông báo, trong đó hai cái không tắt được.
  3. Evidence request có thêm lựa chọn exclusivity, non-exclusive được chọn sẵn.
  4. Đăng nhập thật thay cho đăng nhập demo, và mỗi persona giờ dùng công ty riêng của nó.

Câu chữ sản phẩm đã chuyển vào admin CMS (/admin/cms), nên sửa câu chữ giờ là thao tác của admin, không phải sửa code. Tiếng Việt không được chuyển sang: BETA chỉ có 330 chuỗi tiếng Việt, chủ yếu ở màn hình admin.

11. Còn mở

  • Mô hình claim nào là chuẩn. BETA chạy luồng hai bước propose/confirm; API backend của Fibretrace chỉ có claim một bước.
  • Tách dữ liệu theo công ty cho PO, claim và session: vẫn trong kế hoạch, hay đã bỏ?
  • Hệ role nào là chuẩn cho tài khoản khách hàng và cho Scanning App.
  • Có nên để non-exclusive làm mặc định trên evidence request không, và màn hình có nên cảnh báo rằng lựa chọn này làm ID bị ẩn không.
  • Reservation có phải giới hạn cứng không? Malcolm cho rằng đó là lượng vật lý hữu hạn. Cách hiểu hiện tại cho phép PO vượt reservation như tín hiệu cần mua thêm.
  • Nhãn tier. Tên tier trong form đăng ký (“Tier 3 — Fibre producer”) khác với định nghĩa ở trên.

12. Những lỗ hổng người vận hành cần biết

Phát hiện khi đọc code của BETA ngày 2026-09-23 dưới góc nhìn người vận hành business. Bằng chứng đầy đủ: operator-review-20260923.m3max.md. Chưa trường hợp nào được thấy xảy ra, vì BETA chưa có dữ liệu.

  • Từ chối một claim làm các scan của nó bị chặn vĩnh viễn. Claim bị từ chối vẫn giữ liên kết với scan, và bước kiểm tra khi đề xuất từ chối mọi scan đã từng gắn với một claim. Manufacturer không đề xuất lại được với chính các scan đó, ngược với mục đích của việc từ chối.
  • Từ chối một đề xuất gỡ toàn bộ link scan khỏi PO. Nó gỡ mọi link scan trên PO và đưa PO về shared, kể cả khi PO đã có một claim được xác nhận.
  • Revoke giải phóng dung lượng dù màn hình nói không. Claim bị revoke không còn bị tính vào reservation của brand và khối lượng đã sản xuất của program, nên cùng dung lượng đó có thể dùng cho một claim mới.
  • Database tính cả sản lượng chưa được duyệt. Màn hình chỉ tính sản lượng sau khi admin duyệt, nhưng trần claim trong database tính mọi production record không bị loại trừ, bất kể trạng thái duyệt, và còn tính thêm khối lượng đã scan của producer.
  • Xác nhận không phải là một thao tác nguyên khối. Đó là nhiều bước riêng lẻ. Lỗi giữa chừng có thể để lại một claim đã xác nhận và PO đã chuyển trạng thái mà các bước còn lại chưa chạy.
  • Gỡ share tách scan khỏi nhầm record. Nó tìm claim và PO thuộc sở hữu của bên nhận share, trong khi claim và PO thuộc về brand. Scan gỡ share khỏi manufacturer vẫn nằm trong claim của brand; scan gỡ share khỏi brand thì bị gỡ cả khỏi claim đã xác nhận.
  • Record đã xác nhận công khai ngay lập tức. Link công khai được bật cho mọi claim đã xác nhận; brand phải tự tắt.
  • Phần lớn rule của luồng chỉ nằm ở màn hình. Vì dữ liệu đang mở cho mọi user đã đăng nhập, khi gọi thẳng API chỉ các rule trong database còn giữ: trần khi xác nhận, một PO cho mỗi scan, không có claim khi chưa có scan, không link vào PO đã đóng, CU luôn bằng MT x 1000, và reservation nằm trong ceiling của program. Giới hạn khi đề xuất, việc chỉ link cùng program, bằng chứng dùng một lần, exclusivity một người nhận, và các bước reject/revoke/void chỉ nằm ở màn hình.