Tham chiếu

Manifest (pos-app.json)

Khai báo app: quyền truy cập dữ liệu, domain được gọi và danh sách extension.

Xem Markdown

pos-app.json nằm ở thư mục gốc của dự án. POS đọc manifest khi upload để biết app cần quyền gì, được gọi domain nào và gắn vào những vị trí nào.

Ví dụ đầy đủ

JSON
{
  "handle": "doi-soat-cong-no",
  "name": "Đối soát công nợ",
  "description": "Kiểm tra công nợ khi lên đơn và đối soát cuối ngày.",
  "version": "1.3.0",
  "api_version": "2026-01",
  "access": {
    "methods": ["GET", "POST", "PUT"],
    "warehouse_ids": null,
    "hidden_order_fields": ["bill_phone_number"]
  },
  "network": {
    "allowed_origins": ["https://erp.khachhang.vn"]
  },
  "extensions": [
    { "target": "nav.item", "id": "nav", "label": "Đối soát", "page": "home" },
    { "target": "app.page", "id": "home", "title": "Đối soát công nợ", "render": "iframe", "entry": "page/index.html" },
    {
      "target": "order.details.action",
      "id": "push-erp",
      "label": "Đẩy sang ERP",
      "presentation": "headless",
      "module": "extensions/push-erp.ts",
      "conditions": { "status": [1, 2] }
    },
    {
      "target": "order.details.action",
      "id": "warranty",
      "label": "Tạo phiếu bảo hành",
      "presentation": "modal",
      "title": "Phiếu bảo hành",
      "module": "extensions/warranty-modal.tsx",
      "should_render": "extensions/warranty-should-render.ts"
    },
    { "target": "order.details.block", "id": "debt", "module": "extensions/debt-block.tsx" },
    {
      "target": "purchase.index.action",
      "id": "push-purchase-erp",
      "label": "Đẩy sang ERP",
      "presentation": "headless",
      "module": "extensions/push-purchase-erp.ts",
      "conditions": { "status": [1] }
    },
    {
      "target": "order.before_submit",
      "id": "check-debt",
      "module": "extensions/check-debt.ts",
      "timeout_ms": 2000,
      "on_error": "allow"
    }
  ]
}

Trường cấp gốc

Trường Kiểu Bắt buộc Mô tả
handle string Có Định danh app trong shop, khớp ^[a-z0-9][a-z0-9-]{1,39}$. Dùng trong URL trang của app. Đổi handle thì lần upload sau tạo một app mới, tách khỏi app cũ.
name string Có Tên hiển thị, tối đa 60 ký tự.
description string Không Mô tả ngắn, tối đa 300 ký tự. Hiển thị cho admin khi kích hoạt.
version string Có Semver, ví dụ 1.3.0. Mỗi lần upload phải dùng version chưa tồn tại.
api_version string Có Phiên bản hợp đồng của POS (YYYY-MM), quyết định danh sách target, component và schema dữ liệu ngữ cảnh.
access object Không Giới hạn dữ liệu app được dùng. Xem access.
network object Không Domain bên ngoài app được gọi. Xem network.
extensions array Có Danh sách extension, tối đa 30. Xem extensions.

access

Giới hạn thêm trên quyền của người đang dùng. App token không bao giờ có quyền vượt quá người dùng; access chỉ thu hẹp thêm.

Trường Kiểu Mặc định Mô tả
methods string[] ["GET"] HTTP method được dùng: GET, POST, PUT, DELETE. Không khai thì app chỉ đọc.
warehouse_ids uuid[] không giới hạn Chỉ thao tác trên các kho trong danh sách. Áp dụng cho đơn hàng, phiếu nhập, phiếu xuất, phiếu chuyển kho và tồn kho theo kho.
order_sources string[] không giới hạn Chỉ thấy đơn có nguồn trong danh sách.
hidden_order_sources string[] không ẩn Ẩn đơn có nguồn trong danh sách.
order_statuses int[] không giới hạn Chỉ thấy đơn có trạng thái trong danh sách.
order_since datetime (ISO 8601) không giới hạn Chỉ thấy đơn tạo từ thời điểm này.
hidden_order_fields string[] không ẩn Bỏ các trường này khỏi dữ liệu đơn trả về cho app, kể cả dữ liệu ngữ cảnh.
product_categories int[] không giới hạn Chỉ thấy sản phẩm thuộc các danh mục này.
product_brands int[] không giới hạn Chỉ thấy sản phẩm thuộc các thương hiệu này.

Giá trị null hoặc không khai nghĩa là không giới hạn thêm (trừ methods). Giới hạn hiệu lực là access của version đang kích hoạt.

network

Trường Kiểu Mô tả
allowed_origins string[] Origin https:// app được gọi bằng fetch, tối đa 10, không dùng wildcard. Open API của POS luôn được phép, không cần khai. Cũng áp dụng cho ảnh của pos-image.

Request tới origin không có trong danh sách bị trình duyệt chặn. Server bên ngoài phải bật CORS cho domain sandbox của app (xem Truy cập dữ liệu).

extensions

Trường chung

Trường Kiểu Bắt buộc Mô tả
target string Có Vị trí gắn extension. Xem Extension point.
id string Có Duy nhất trong app, khớp ^[a-z0-9-]{1,40}$.
module string Tuỳ target Đường dẫn file nguồn (.ts, .tsx, .js, .jsx). CLI build thành một ES module độc lập. Bắt buộc với target Worker UI và headless.

Trường cho action

Áp dụng cho *.details.action, *.index.action và *.index.selection-action của mọi resource.

Trường Kiểu Bắt buộc Mô tả
label string Có Nhãn mục menu, tối đa 40 ký tự.
presentation string Có headless, modal, fullscreen hoặc navigate. Xem kiểu action.
title string Không Tiêu đề modal (modal, fullscreen). Mặc định bằng label.
page string Khi navigate id của extension app.page cần mở.
path string Không Đường dẫn bên trong trang khi navigate, ví dụ /returns.
conditions object Không Điều kiện hiện mục menu, POS tự kiểm tra. Xem conditions.
should_render string Không Module trả về true/false để quyết định có hiện mục hay không. Dùng khi conditions không đủ.

Trường cho block

Áp dụng cho *.details.block.

Trường Kiểu Bắt buộc Mô tả
conditions object Không Điều kiện hiện block.
should_render string Không Như action.

Trường cho trang và menu

Target Trường Mô tả
app.page title (bắt buộc) Tiêu đề trang.
app.page render (bắt buộc) iframe hoặc worker (trang Worker UI dự kiến ở giai đoạn 2).
app.page entry File HTML của trang trong gói, khi render: "iframe". Khai entry hoặc url.
app.page url Trang tự host: địa chỉ https trên máy chủ của bạn, POS nhúng thẳng thay cho file trong gói. Không chứa tài khoản, query, #; không dùng domain của Pancake, localhost hay địa chỉ IP. Đổi url phải deploy version mới. Xem Trang tự host.
app.page module Bắt buộc khi render: "worker".
nav.item label (bắt buộc) Nhãn trên menu POS, tối đa 30 ký tự.
nav.item page (bắt buộc) id của extension app.page.

Trường cho hook

Áp dụng cho *.before_submit và *.after_save.

Trường Kiểu Mặc định Mô tả
timeout_ms int 2000 Chỉ cho before_submit, tối đa 5000.
on_error string allow Chỉ cho before_submit: allow cho lưu tiếp khi hook lỗi/timeout, block chặn lưu.

conditions

Các khoá kết hợp theo AND; giá trị mảng trong một khoá kết hợp theo OR. POS kiểm tra ngay khi hiển thị nên không làm chậm menu.

Khoá Kiểu Resource Đúng khi
mode string[] Tất cả (target trên form chi tiết) create (bản ghi mới chưa lưu) hoặc edit (bản ghi đã có).
status int[] order, purchase, export, warehouse_transfer Trạng thái bản ghi nằm trong danh sách (mã trạng thái theo Open API).
warehouse_ids uuid[] order, purchase, export, warehouse_transfer Kho của bản ghi nằm trong danh sách; với phiếu chuyển kho là kho đi hoặc kho đến.
tags_any (int | string)[] order, product, purchase, export Bản ghi có ít nhất một thẻ trong danh sách (ID thẻ).
type int[] export Loại phiếu xuất nằm trong danh sách.
category_ids int[] product Sản phẩm thuộc ít nhất một danh mục trong danh sách.
order_sources string[] order Nguồn đơn nằm trong danh sách.
has_customer boolean order Đơn đã/chưa gắn khách hàng.

Dùng khoá không áp dụng cho resource của target là lỗi kiểm tra (invalid_condition).

Ví dụ: chỉ hiện cho đơn đã có, trạng thái 1 hoặc 2, có thẻ VIP (ID 7):

JSON
{ "mode": ["edit"], "status": [1, 2], "tags_any": [7] }

Ví dụ: chỉ hiện cho phiếu nhập đã nhập hàng (trạng thái 1) vào một kho cụ thể:

JSON
{ "status": [1], "warehouse_ids": ["a52f…"] }

Lỗi kiểm tra manifest

Mã Nguyên nhân
invalid_handle handle sai định dạng.
invalid_field Giá trị của trường không hợp lệ, ví dụ quá độ dài cho phép, khoá lạ trong access hoặc access.methods sai.
duplicate_version version đã tồn tại.
unsupported_api_version api_version không còn hỗ trợ hoặc không tồn tại.
unknown_target target không có trong api_version đã khai.
missing_field Thiếu trường bắt buộc theo target.
module_not_found Không tìm thấy file module/entry/should_render.
bundle_too_large Module hoặc bundle vượt giới hạn kích thước.
storage_quota_exceeded Tổng dung lượng app của shop sẽ vượt 500 MB.
invalid_bundle File upload không phải gói .zip hợp lệ, hoặc có đường dẫn file không an toàn.
upload_failed Không lưu được bundle; thử upload lại.
invalid_origin allowed_origins không phải https:// hoặc có wildcard.
invalid_url url của app.page không phải https theo tên miền, có tài khoản, query, #, hoặc thuộc domain của Pancake.
invalid_condition Khoá trong conditions không áp dụng cho resource của target.