kiotviet-client-sdk - v0.3.4
    Preparing search index...

    kiotviet-client-sdk - v0.3.4

    KiotViet Retail SDK

    Một SDK được viết bằng TypeScript/JavaScript dùng để tương tác với nền tảng Public API của KiotViet. SDK này cung cấp giao diện dễ sử dụng để quản lý khách hàng, sản phẩm, đơn hàng và các tài nguyên khác trong cửa hàng bán lẻ của bạn trên hệ thống KiotViet.

    npm version
    license


    • 🔐 Quản lý token tự động với cơ chế xác thực bằng thông tin khách hàng (client credentials)
    • 📦 Hỗ trợ đầy đủ TypeScript với định nghĩa kiểu dữ liệu chi tiết
    • 🚀 API bất đồng bộ dựa trên Promise
    • 🛡️ Xử lý lỗi tích hợp với các loại lỗi cụ thể
    • 📝 Tài liệu đầy đủ và có ví dụ minh họa

    Tài liệu API KiotViet SDK

    Tài liệu này cung cấp hướng dẫn chi tiết về cách sử dụng các API có sẵn trong KiotViet Client SDK.

    Mỗi tài liệu API sẽ bao gồm:

    • Danh sách đầy đủ các phương thức có sẵn
    • Ví dụ chi tiết về cách sử dụng
    • Cấu trúc dữ liệu và các tham số
    • Ghi chú và lưu ý quan trọng

    Để bắt đầu sử dụng SDK, trước tiên bạn cần khởi tạo client:

    import { KiotVietClient } from "kiotviet-client-sdk";

    const client = new KiotVietClient({
    clientId: "your_client_id",
    clientSecret: "your_client_secret",
    retailerName: "your_retailer_name"
    });

    Sau đó bạn có thể truy cập các API tương ứng thông qua client này. Xem chi tiết trong từng tài liệu API cụ thể.

    SDK này sử dụng chuẩn OAuth 2.0 để xác thực. Bạn cần cung cấp clientId, clientSecretretailerName khi khởi tạo đối tượng client.

    • retailerName là tên cửa hàng của bạn trên hệ thống KiotViet.
    • clientIdclientSecret được lấy từ cổng dành cho nhà phát triển (developer portal) của KiotViet.

    SDK sẽ tự động xử lý việc lấy và lưu trữ access token cho bạn.
    Access token được lưu trong bộ nhớ và sẽ được sử dụng cho tất cả các yêu cầu API.

    • Token có hiệu lực trong vòng 1 giờ và SDK sẽ tự động làm mới khi token hết hạn.
    • Nếu không thể làm mới token, SDK sẽ phát sinh lỗi và bạn cần xử lý tình huống này trong ứng dụng của mình.

    Sản phẩm (Products)

    API Sản phẩm (Products)

    1. list(params) - Lấy danh sách sản phẩm
    2. getById(productId) - Lấy thông tin sản phẩm theo ID
    3. create(productData) - Tạo sản phẩm mới
    4. update(productId, productData) - Cập nhật sản phẩm
    5. delete(productId) - Xóa sản phẩm
    6. getByCategory(categoryId, params) - Lấy sản phẩm theo danh mục
    7. search(query, params) - Tìm kiếm sản phẩm
    8. getByCode(code) - Lấy sản phẩm theo mã
    9. getByBarcode(barcode) - Lấy sản phẩm theo mã vạch
    10. getAttributes() - Lấy tất cả thuộc tính sản phẩm
    11. bulkCreate(products) - Tạo nhiều sản phẩm cùng lúc
    12. bulkUpdate(products) - Cập nhật nhiều sản phẩm cùng lúc
    13. getInventoryLevels(params) - Lấy thông tin tồn kho
    // Lấy danh sách với phân trang
    const products = await client.products.list({
    pageSize: 20,
    currentItem: 0,
    includeInventory: true
    });

    // Lấy sản phẩm có lọc theo danh mục
    const products = await client.products.list({
    categoryId: 123,
    pageSize: 20
    });

    // Lọc theo trạng thái
    const products = await client.products.list({
    status: "Active",
    pageSize: 20
    });
    const newProduct = await client.products.create({
    code: "SP001",
    name: "Tên sản phẩm",
    categoryId: 1,
    basePrice: 100000,
    retailPrice: 150000,
    weight: 1.5,
    unit: "Cái",
    allowsSale: true,
    description: "Mô tả sản phẩm",
    attributes: [
    {
    attributeName: "Màu sắc",
    attributeValue: "Đỏ"
    }
    ]
    });
    const updatedProduct = await client.products.update(123, {
    name: "Tên sản phẩm mới",
    retailPrice: 200000,
    description: "Mô tả mới"
    });
    // Tìm theo tên hoặc mã
    const searchResults = await client.products.search("keyword", {
    pageSize: 20
    });

    // Tìm theo mã vạch
    const product = await client.products.getByBarcode("8938505040059");

    // Tìm theo mã sản phẩm
    const product = await client.products.getByCode("SP001");
    // Tạo nhiều sản phẩm
    await client.products.bulkCreate([
    {
    code: "SP001",
    name: "Sản phẩm 1",
    retailPrice: 100000
    },
    {
    code: "SP002",
    name: "Sản phẩm 2",
    retailPrice: 200000
    }
    ]);

    // Cập nhật nhiều sản phẩm
    await client.products.bulkUpdate([
    {
    id: 1,
    retailPrice: 150000
    },
    {
    id: 2,
    retailPrice: 250000
    }
    ]);
    interface Product {
    id: number; // ID sản phẩm
    code: string; // Mã sản phẩm
    name: string; // Tên sản phẩm
    categoryId: number; // ID danh mục
    basePrice: number; // Giá gốc
    retailPrice: number; // Giá bán lẻ
    weight?: number; // Khối lượng
    unit?: string; // Đơn vị tính
    allowsSale: boolean; // Cho phép bán
    status: "Active" | "Inactive"; // Trạng thái
    description?: string; // Mô tả
    attributes?: ProductAttribute[]; // Thuộc tính
    inventories?: ProductInventory[]; // Thông tin tồn kho
    modifiedDate: string; // Ngày cập nhật
    createdDate: string; // Ngày tạo
    }

    interface ProductAttribute {
    attributeName: string; // Tên thuộc tính
    attributeValue: string; // Giá trị thuộc tính
    }

    interface ProductInventory {
    branchId: number; // ID chi nhánh
    branchName: string; // Tên chi nhánh
    onHand: number; // Tồn kho thực tế
    reserved: number; // Số lượng đã đặt
    available: number; // Số lượng có thể bán
    }
    interface ProductCreateParams {
    code: string; // Mã sản phẩm (bắt buộc)
    name: string; // Tên sản phẩm (bắt buộc)
    categoryId: number; // ID danh mục (bắt buộc)
    basePrice?: number; // Giá gốc
    retailPrice: number; // Giá bán lẻ (bắt buộc)
    weight?: number; // Khối lượng
    unit?: string; // Đơn vị tính
    allowsSale?: boolean; // Cho phép bán
    description?: string; // Mô tả
    attributes?: ProductAttribute[]; // Thuộc tính
    }
    interface ProductUpdateParams extends Partial<ProductCreateParams> {
    id: number; // ID sản phẩm cần cập nhật
    }
    1. Khi sử dụng phương thức list():

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Có thể bổ sung các tham số lọc khác như categoryId, status, modifiedFrom, v.v.
    2. Khi tạo sản phẩm:

      • code phải là duy nhất trong hệ thống
      • retailPrice không được nhỏ hơn basePrice
      • attributes là tùy chọn nhưng nên tuân theo cấu trúc chuẩn
    3. Quản lý tồn kho:

      • Sử dụng getInventoryLevels() để lấy thông tin tồn kho theo chi nhánh
      • Thông tin tồn kho tự động cập nhật khi có giao dịch
      • Có thể lọc theo branchIds để lấy tồn kho của một số chi nhánh cụ thể

    Khách hàng (Customers)

    API Khách hàng (Customers)

    1. list(params) - Lấy danh sách khách hàng
    2. getById(customerId) - Lấy thông tin khách hàng theo ID
    3. create(customerData) - Tạo khách hàng mới
    4. search(query, params) - Tìm kiếm khách hàng
    5. getByGroup(groupId, params) - Lấy khách hàng theo nhóm
    6. getByContactNumber(contactNumber) - Tìm khách hàng theo số điện thoại
    // Lấy danh sách với phân trang
    const customers = await client.customers.list({
    pageSize: 20,
    currentItem: 0,
    includeCustomerGroup: true
    });

    // Lọc theo thời gian cập nhật
    const customers = await client.customers.list({
    lastModifiedFrom: "2024-01-01",
    pageSize: 20
    });

    // Sắp xếp kết quả
    const customers = await client.customers.list({
    orderBy: "name",
    orderDirection: "ASC",
    pageSize: 20
    });
    const newCustomer = await client.customers.create({
    name: "Nguyễn Văn A", // Bắt buộc
    contactNumber: "0901234567",
    email: "nguyenvana@email.com",
    gender: true, // true: Nam, false: Nữ
    birthDate: "1990-01-01",
    address: "123 Đường ABC",
    locationName: "Phường XYZ",
    wardName: "Quận 1",
    organization: "Công ty ABC",
    taxCode: "0123456789",
    groupIds: [1, 2] // ID các nhóm khách hàng
    });
    // Tìm theo từ khóa (tên, số điện thoại, mã)
    const searchResults = await client.customers.search("0901234567", {
    pageSize: 20
    });

    // Tìm theo số điện thoại chính xác
    const customer = await client.customers.getByContactNumber("0901234567");

    // Tìm theo nhóm khách hàng
    const groupCustomers = await client.customers.getByGroup(1, {
    pageSize: 20
    });
    interface Customer {
    id: number; // ID khách hàng
    code: string; // Mã khách hàng
    name: string; // Tên khách hàng
    type?: number; // Loại khách hàng (0: Cá nhân, 1: Doanh nghiệp)
    gender?: boolean; // Giới tính (true: Nam, false: Nữ)
    birthDate?: string; // Ngày sinh
    contactNumber?: string; // Số điện thoại
    address?: string; // Địa chỉ
    locationName?: string; // Tên địa điểm (phường/xã)
    wardName?: string; // Tên quận/huyện
    email?: string; // Email
    organization?: string; // Tên tổ chức/công ty
    comments?: string; // Ghi chú
    taxCode?: string; // Mã số thuế
    debt: number; // Công nợ
    totalInvoiced?: number; // Tổng số hóa đơn
    totalPoint?: number; // Tổng điểm tích lũy
    totalRevenue?: number; // Tổng doanh thu
    retailerId: number; // ID nhà bán lẻ
    modifiedDate?: string; // Ngày cập nhật
    createdDate: string; // Ngày tạo
    rewardPoint?: number; // Điểm thưởng
    psidFacebook?: number; // ID Facebook
    groups?: string; // Chuỗi nhóm khách hàng
    branchId?: number; // ID chi nhánh
    createdBy?: string; // Người tạo
    isActive?: boolean; // Trạng thái hoạt động
    customerGroupDetails?: CustomerGroupDetails[]; // Chi tiết nhóm khách hàng
    }

    interface CustomerGroupDetails {
    id: number; // ID chi tiết nhóm
    customerId: number; // ID khách hàng
    groupId: number; // ID nhóm
    }
    interface CustomerCreateParams {
    code?: string; // Mã khách hàng (tự động nếu không nhập)
    name: string; // Tên khách hàng (bắt buộc)
    gender?: boolean; // Giới tính
    birthDate?: string; // Ngày sinh
    contactNumber?: string; // Số điện thoại
    address?: string; // Địa chỉ
    locationName?: string; // Tên địa điểm
    wardName?: string; // Tên quận/huyện
    email?: string; // Email
    comments?: string; // Ghi chú
    organization?: string; // Tên tổ chức
    taxCode?: string; // Mã số thuế
    groupIds?: number[]; // Danh sách ID nhóm
    branchId?: number; // ID chi nhánh
    }
    interface CustomerListParams {
    code?: string; // Lọc theo mã
    name?: string; // Lọc theo tên
    contactNumber?: string; // Lọc theo SĐT
    lastModifiedFrom?: string; // Từ ngày cập nhật
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    includeTotal?: boolean; // Bao gồm tổng số
    includeCustomerGroup?: boolean; // Bao gồm nhóm
    birthDate?: string; // Lọc theo ngày sinh
    groupId?: number; // Lọc theo nhóm
    includeCustomerSocial?: boolean; // Bao gồm MXH
    }
    1. Khi tạo khách hàng:

      • Trường name là bắt buộc
      • code sẽ được tự động tạo nếu không cung cấp
      • Nên cung cấp contactNumber hoặc email để dễ dàng tìm kiếm sau này
    2. Khi tìm kiếm:

      • Có thể tìm theo tên, số điện thoại, mã khách hàng
      • Tìm kiếm không phân biệt chữ hoa/thường
      • Hỗ trợ tìm kiếm một phần của từ khóa
    3. Nhóm khách hàng:

      • Một khách hàng có thể thuộc nhiều nhóm
      • Sử dụng groupIds khi tạo/cập nhật để quản lý nhóm
      • Có thể lấy danh sách theo nhóm với getByGroup()
    4. Phân trang và sắp xếp:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Có thể sắp xếp theo nhiều trường khác nhau
      • Hướng sắp xếp: 'ASC' (tăng dần) hoặc 'DESC' (giảm dần)

    Đơn hàng (Orders)

    API Đơn hàng (Orders)

    1. list(params) - Lấy danh sách đơn hàng
    2. getById(orderId) - Lấy thông tin đơn hàng theo ID
    3. create(orderData) - Tạo đơn hàng mới
    4. update(orderId, orderData) - Cập nhật đơn hàng
    5. cancel(orderId, reason) - Hủy đơn hàng
    6. getByCode(code) - Lấy đơn hàng theo mã
    7. delete(orderId, isVoidPayment) - Xóa đơn hàng
    8. getByDateRange(fromDate, toDate, params) - Lấy đơn hàng theo khoảng thời gian
    9. getByCustomer(customerIdentifier, params) - Lấy đơn hàng theo khách hàng
    const newOrder = await client.orders.create({
    branchId: 1, // ID chi nhánh (bắt buộc)
    customerId: 123, // ID khách hàng (tùy chọn)
    purchaseDate: "2024-04-05", // Ngày mua hàng
    discount: 10000, // Giảm giá
    makeInvoice: true, // Tạo hóa đơn
    description: "Ghi chú đơn hàng",
    orderDetails: [ // Chi tiết đơn hàng (bắt buộc)
    {
    productId: 1,
    productCode: "SP001",
    productName: "Sản phẩm 1",
    quantity: 2,
    price: 100000,
    discount: 5000,
    isMaster: true
    }
    ],
    orderDelivery: { // Thông tin giao hàng (tùy chọn)
    receiver: "Nguyễn Văn A",
    contactNumber: "0901234567",
    address: "123 Đường ABC",
    locationName: "Phường XYZ",
    wardName: "Quận 1"
    }
    });
    const updatedOrder = await client.orders.update(123, {
    discount: 20000,
    description: "Ghi chú mới",
    orderDetails: [
    {
    productId: 1,
    quantity: 3,
    price: 100000
    }
    ]
    });
    // Lấy danh sách với phân trang
    const orders = await client.orders.list({
    pageSize: 20,
    currentItem: 0,
    includePayment: true,
    includeOrderDelivery: true
    });

    // Lọc theo trạng thái
    const orders = await client.orders.list({
    status: [OrderStatus.Processing, OrderStatus.Completed],
    pageSize: 20
    });

    // Lọc theo chi nhánh
    const orders = await client.orders.list({
    branchIds: [1, 2],
    pageSize: 20
    });
    // Theo khoảng thời gian
    const orders = await client.orders.getByDateRange(
    "2024-01-01",
    "2024-01-31",
    { pageSize: 20 }
    );

    // Theo khách hàng
    const orders = await client.orders.getByCustomer(
    "0901234567", // Có thể là số điện thoại hoặc mã khách hàng
    { pageSize: 20 }
    );

    // Theo mã đơn hàng
    const order = await client.orders.getByCode("DH001");
    // Hủy đơn hàng
    await client.orders.cancel(123, "Lý do hủy đơn");

    // Xóa đơn hàng
    await client.orders.delete(123, true); // true: hủy thanh toán liên quan
    interface Order {
    id: number; // ID đơn hàng
    code: string; // Mã đơn hàng
    purchaseDate: string; // Ngày mua hàng
    branchId: number; // ID chi nhánh
    branchName: string; // Tên chi nhánh
    customerId?: number; // ID khách hàng
    customerCode?: string; // Mã khách hàng
    customerName?: string; // Tên khách hàng
    total: number; // Tổng tiền
    totalPayment: number; // Tổng thanh toán
    discount?: number; // Giảm giá
    discountRatio?: number; // Tỷ lệ giảm giá
    description?: string; // Ghi chú
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    usingCod: boolean; // Sử dụng COD
    orderDetails: OrderProduct[]; // Chi tiết đơn hàng
    orderDelivery?: OrderDelivery; // Thông tin giao hàng
    payments?: OrderPayment[]; // Thông tin thanh toán
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface OrderProduct {
    productId: number; // ID sản phẩm
    productCode: string; // Mã sản phẩm
    productName: string; // Tên sản phẩm
    quantity: number; // Số lượng
    price: number; // Giá
    discount?: number; // Giảm giá
    discountRatio?: number; // Tỷ lệ giảm giá
    note?: string; // Ghi chú
    isMaster: boolean; // Là sản phẩm chính
    }
    interface OrderListParams {
    branchIds?: number[]; // Lọc theo chi nhánh
    customerIds?: number[]; // Lọc theo khách hàng
    customerCode?: string; // Lọc theo mã khách hàng
    status?: number[]; // Lọc theo trạng thái
    includePayment?: boolean; // Bao gồm thông tin thanh toán
    includeOrderDelivery?: boolean; // Bao gồm thông tin giao hàng
    lastModifiedFrom?: string; // Từ ngày cập nhật
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    saleChannelId?: number; // ID kênh bán hàng
    }
    enum OrderStatus {
    Draft = 1, // Nháp
    Processing = 2, // Đang xử lý
    Completed = 3, // Hoàn thành
    Cancelled = 4, // Đã hủy
    }
    1. Khi tạo đơn hàng:

      • branchIdorderDetails là bắt buộc
      • Nếu có customerId, hệ thống sẽ lấy thông tin khách hàng tự động
      • Có thể tạo khách hàng mới đồng thời với đơn hàng bằng cách cung cấp customer
    2. Thanh toán và giao hàng:

      • Sử dụng orderDelivery để thêm thông tin giao hàng
      • Có thể kiểm tra thông tin thanh toán qua payments
      • Hỗ trợ thanh toán COD (usingCod)
    3. Trạng thái đơn hàng:

      • Mặc định là Draft khi tạo mới
      • Chỉ có thể hủy đơn hàng ở trạng thái Draft hoặc Processing
      • Xem OrderStatus để biết các trạng thái có thể
    4. Phân trang và sắp xếp:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Có thể sắp xếp theo nhiều trường khác nhau

    Đơn đặt hàng (Purchase Orders)

    API Đơn đặt hàng (Purchase Orders)

    1. list(params) - Lấy danh sách đơn đặt hàng
    2. getById(purchaseOrderId) - Lấy thông tin đơn đặt hàng theo ID
    3. create(purchaseOrderData) - Tạo đơn đặt hàng mới
    4. update(purchaseOrderId, purchaseOrderData) - Cập nhật đơn đặt hàng
    5. cancel(purchaseOrderId, reason) - Hủy đơn đặt hàng
    6. getByDateRange(fromDate, toDate, params) - Lấy đơn đặt hàng theo khoảng thời gian
    7. getBySupplier(supplierCode, params) - Lấy đơn đặt hàng theo nhà cung cấp
    const newPurchaseOrder = await client.purchaseOrders.create({
    branchId: 1, // ID chi nhánh (bắt buộc)
    supplierId: 123, // ID nhà cung cấp
    purchaseDate: "2024-04-05", // Ngày đặt hàng
    expectedDeliveryDate: "2024-04-10", // Ngày dự kiến nhận
    description: "Đơn hàng tháng 4",
    discount: 50000, // Giảm giá
    purchaseOrderDetails: [ // Chi tiết đơn hàng (bắt buộc)
    {
    productId: 1,
    productCode: "SP001",
    quantity: 10,
    price: 100000,
    discount: 5000,
    note: "Hàng mới",
    batches: [ // Thông tin lô hàng
    {
    batchName: "L001",
    quantity: 5,
    expiredDate: "2024-12-31"
    }
    ]
    }
    ],
    supplier: { // Thông tin nhà cung cấp mới (nếu chưa có)
    name: "Nhà cung cấp ABC",
    contactNumber: "0901234567",
    email: "abc@supplier.com"
    },
    payments: [ // Thanh toán
    {
    amount: 950000,
    method: "Cash"
    }
    ]
    });
    // Lấy danh sách với phân trang
    const orders = await client.purchaseOrders.list({
    pageSize: 20,
    currentItem: 0,
    includePayment: true
    });

    // Lọc theo chi nhánh và trạng thái
    const orders = await client.purchaseOrders.list({
    branchIds: [1, 2],
    status: [PurchaseOrderStatus.Processing],
    pageSize: 20
    });

    // Lọc theo khoảng thời gian
    const orders = await client.purchaseOrders.getByDateRange(
    "2024-01-01",
    "2024-01-31",
    { pageSize: 20 }
    );

    // Lọc theo nhà cung cấp
    const orders = await client.purchaseOrders.getBySupplier(
    "NCC001",
    { pageSize: 20 }
    );
    const updatedOrder = await client.purchaseOrders.update(123, {
    expectedDeliveryDate: "2024-04-15",
    description: "Cập nhật ngày giao",
    purchaseOrderDetails: [
    {
    productId: 1,
    quantity: 15,
    price: 100000
    }
    ]
    });
    await client.purchaseOrders.cancel(123, "Nhà cung cấp hết hàng");
    
    interface PurchaseOrder {
    id: number; // ID đơn hàng
    code: string; // Mã đơn hàng
    documentCode?: string; // Mã chứng từ
    purchaseDate: string; // Ngày đặt hàng
    expectedDeliveryDate?: string; // Ngày dự kiến nhận
    deliveryDate?: string; // Ngày nhận thực tế
    branchId: number; // ID chi nhánh
    branchName: string; // Tên chi nhánh
    supplierId?: number; // ID nhà cung cấp
    supplierCode?: string; // Mã nhà cung cấp
    supplierName?: string; // Tên nhà cung cấp
    total: number; // Tổng tiền
    totalPayment: number; // Tổng thanh toán
    discount?: number; // Giảm giá
    discountRatio?: number; // Tỷ lệ giảm
    description?: string; // Ghi chú
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    purchaseOrderDetails: PurchaseOrderProduct[]; // Chi tiết đơn hàng
    payments?: PaymentDetail[]; // Thanh toán
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface PurchaseOrderProduct {
    productId: number; // ID sản phẩm
    productCode: string; // Mã sản phẩm
    productName: string; // Tên sản phẩm
    quantity: number; // Số lượng
    price: number; // Giá
    discount?: number; // Giảm giá
    discountRatio?: number; // Tỷ lệ giảm
    note?: string; // Ghi chú
    receivedQuantity?: number; // Số lượng đã nhận
    serialNumbers?: string[]; // Số serial
    batches?: { // Thông tin lô
    batchName: string; // Tên lô
    quantity: number; // Số lượng
    expiredDate: string; // Ngày hết hạn
    }[];
    }
    enum PurchaseOrderStatus {
    Draft = 1, // Nháp
    Processing = 2, // Đang xử lý
    Completed = 3, // Hoàn thành
    Cancelled = 4 // Đã hủy
    }
    1. Khi tạo đơn hàng:

      • branchIdpurchaseOrderDetails là bắt buộc
      • Có thể tạo nhà cung cấp mới đồng thời bằng cách cung cấp thông tin trong supplier
      • Hỗ trợ quản lý lô hàng và số serial cho sản phẩm
    2. Quản lý lô hàng:

      • Mỗi sản phẩm có thể có nhiều lô khác nhau
      • Mỗi lô có thể có số lượng và ngày hết hạn riêng
      • Theo dõi số lượng đã nhận qua receivedQuantity
    3. Thanh toán:

      • Hỗ trợ nhiều phương thức thanh toán
      • Có thể thanh toán một phần hoặc toàn bộ
      • Theo dõi lịch sử thanh toán qua payments
    4. Tìm kiếm và lọc:

      • Hỗ trợ tìm kiếm theo nhiều tiêu chí
      • Lọc theo trạng thái, chi nhánh, nhà cung cấp
      • Tìm kiếm theo khoảng thời gian

    Danh mục (Categories)

    API Danh mục (Categories)

    1. list(params) - Lấy danh sách danh mục
    2. getById(categoryId) - Lấy thông tin danh mục theo ID
    3. create(categoryData) - Tạo danh mục mới
    4. update(categoryId, categoryData) - Cập nhật danh mục
    5. delete(categoryId) - Xóa danh mục
    6. getHierarchical(params) - Lấy cấu trúc danh mục dạng cây
    // Lấy danh sách với phân trang
    const categories = await client.categories.list({
    pageSize: 20,
    currentItem: 0
    });

    // Sắp xếp kết quả
    const categories = await client.categories.list({
    orderBy: "categoryName",
    orderDirection: "ASC"
    });

    // Lấy cấu trúc danh mục dạng cây
    const hierarchicalCategories = await client.categories.getHierarchical({
    pageSize: 100
    });
    // Tạo danh mục gốc
    const newCategory = await client.categories.create({
    categoryName: "Điện thoại di động",
    description: "Danh mục các loại điện thoại di động",
    rank: 1
    });

    // Tạo danh mục con
    const newSubCategory = await client.categories.create({
    categoryName: "iPhone",
    parentId: 1, // ID của danh mục cha
    description: "Danh mục điện thoại iPhone",
    rank: 1
    });
    const updatedCategory = await client.categories.update(123, {
    categoryName: "Tên danh mục mới",
    description: "Mô tả mới",
    rank: 2
    });
    await client.categories.delete(123);
    
    interface Category {
    categoryId: number; // ID danh mục
    categoryName: string; // Tên danh mục
    parentId: number | null; // ID danh mục cha (null nếu là danh mục gốc)
    hasChild?: boolean; // Có danh mục con hay không
    description?: string; // Mô tả
    retailerId: number; // ID nhà bán lẻ
    rank?: number; // Thứ tự sắp xếp
    isDeleted?: boolean; // Đã xóa chưa
    modifiedDate: string; // Ngày cập nhật
    createdDate: string; // Ngày tạo
    }
    interface CategoryCreateParams {
    categoryName: string; // Tên danh mục (bắt buộc)
    parentId?: number; // ID danh mục cha
    description?: string; // Mô tả
    rank?: number; // Thứ tự sắp xếp
    }
    interface CategoryListParams {
    lastModifiedFrom?: string; // Từ ngày cập nhật
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    hierarchicalData?: boolean; // Lấy dạng cây
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    }
    1. Cấu trúc phân cấp:

      • Danh mục có thể được tổ chức theo cấu trúc cha-con
      • Sử dụng parentId để xác định mối quan hệ
      • getHierarchical() trả về cấu trúc dạng cây đầy đủ
    2. Thứ tự sắp xếp:

      • Sử dụng trường rank để sắp xếp danh mục
      • Giá trị càng nhỏ thì hiển thị càng cao
      • Có thể cập nhật rank để thay đổi thứ tự
    3. Xóa danh mục:

      • Khi xóa danh mục, tất cả sản phẩm trong danh mục sẽ không hiển thị
      • Nên đảm bảo không có sản phẩm trong danh mục trước khi xóa
      • Có thể lấy danh mục đã xóa bằng includeRemoveIds: true
    4. Phân trang và sắp xếp:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Có thể sắp xếp theo categoryName, createdDate, modifiedDate

    Hóa đơn (Invoices)

    API Hóa đơn (Invoices)

    1. list(params) - Lấy danh sách hóa đơn
    2. getById(invoiceId) - Lấy thông tin hóa đơn theo ID
    3. create(invoiceData) - Tạo hóa đơn mới
    4. update(invoiceId, invoiceData) - Cập nhật hóa đơn
    5. cancel(invoiceId, reason) - Hủy hóa đơn
    6. delete(invoiceId, isVoidPayment) - Xóa/hủy bỏ hóa đơn
    7. getByDateRange(fromDate, toDate, params) - Lấy hóa đơn theo khoảng thời gian
    8. getByCustomer(customerIdentifier, params) - Lấy hóa đơn theo khách hàng
    9. getByOrder(orderId, params) - Lấy hóa đơn theo đơn hàng
    10. getByCode(code) - Lấy hóa đơn theo mã
    const newInvoice = await client.invoices.create({
    branchId: 1, // ID chi nhánh (bắt buộc)
    purchaseDate: "2024-04-05", // Ngày mua hàng
    customerId: 123, // ID khách hàng
    discount: 10000, // Giảm giá
    totalPayment: 990000, // Tổng thanh toán (bắt buộc)
    method: "Cash", // Phương thức thanh toán
    usingCod: false, // Sử dụng COD
    invoiceDetails: [ // Chi tiết hóa đơn (bắt buộc)
    {
    productId: 1,
    productCode: "SP001",
    productName: "Sản phẩm 1",
    quantity: 2,
    price: 500000,
    discount: 5000
    }
    ],
    deliveryDetail: { // Thông tin giao hàng (tùy chọn)
    receiver: "Nguyễn Văn A",
    contactNumber: "0901234567",
    address: "123 Đường ABC",
    locationName: "Phường XYZ",
    wardName: "Quận 1"
    }
    });
    // Lấy danh sách với phân trang
    const invoices = await client.invoices.list({
    pageSize: 20,
    currentItem: 0,
    includePayment: true,
    includeInvoiceDelivery: true
    });

    // Lọc theo chi nhánh và trạng thái
    const invoices = await client.invoices.list({
    branchIds: [1, 2],
    status: [InvoiceStatus.Completed],
    pageSize: 20
    });

    // Lọc theo khoảng thời gian
    const invoices = await client.invoices.getByDateRange(
    "2024-01-01",
    "2024-01-31",
    { pageSize: 20 }
    );
    // Theo khách hàng
    const invoices = await client.invoices.getByCustomer(
    "0901234567", // Số điện thoại hoặc mã khách hàng
    { pageSize: 20 }
    );

    // Theo đơn hàng
    const invoices = await client.invoices.getByOrder(123);

    // Theo mã hóa đơn
    const invoice = await client.invoices.getByCode("HD001");
    // Hủy hóa đơn
    await client.invoices.cancel(123, "Lý do hủy hóa đơn");

    // Xóa hóa đơn và hủy thanh toán liên quan
    await client.invoices.delete(123, true);
    interface Invoice {
    id: number; // ID hóa đơn
    code: string; // Mã hóa đơn
    orderCode?: string; // Mã đơn hàng
    purchaseDate: string; // Ngày mua
    branchId: number; // ID chi nhánh
    branchName: string; // Tên chi nhánh
    customerId?: number; // ID khách hàng
    customerCode?: string; // Mã khách hàng
    customerName?: string; // Tên khách hàng
    total: number; // Tổng tiền
    totalPayment: number; // Tổng thanh toán
    discount?: number; // Giảm giá
    discountRatio?: number; // Tỷ lệ giảm
    description?: string; // Ghi chú
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    usingCod: boolean; // Sử dụng COD
    invoiceDetails: InvoiceDetail[]; // Chi tiết hóa đơn
    payments: InvoicePayment[]; // Thanh toán
    invoiceDelivery?: InvoiceDelivery; // Thông tin giao hàng
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    enum PaymentMethod {
    Cash = 1, // Tiền mặt
    Card = 2, // Thẻ
    BankTransfer = 3, // Chuyển khoản
    MobilePayment = 4, // Thanh toán di động
    Mixed = 5 // Kết hợp
    }
    enum InvoiceStatus {
    Draft = 1, // Nháp
    Processing = 2, // Đang xử lý
    Completed = 3, // Hoàn thành
    Cancelled = 4 // Đã hủy
    }
    1. Khi tạo hóa đơn:

      • branchId, totalPaymentinvoiceDetails là bắt buộc
      • Có thể tạo khách hàng mới đồng thời bằng cách cung cấp thông tin trong customer
      • Tổng tiền được tính tự động dựa trên chi tiết hóa đơn
    2. Thanh toán:

      • Hỗ trợ nhiều phương thức thanh toán
      • Có thể thanh toán một phần hoặc toàn bộ
      • Thanh toán COD được quản lý riêng qua usingCod
    3. Giao hàng:

      • Thông tin giao hàng là tùy chọn
      • Hỗ trợ tích hợp với đối tác giao hàng qua partnerDelivery
      • Có thể theo dõi trạng thái giao hàng
    4. Phân trang và tìm kiếm:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Hỗ trợ nhiều tiêu chí tìm kiếm và lọc
      • Có thể sắp xếp theo nhiều trường khác nhau

    Chi nhánh (Branches)

    API Chi nhánh (Branches)

    1. list(params) - Lấy danh sách chi nhánh
    2. getById(branchId) - Lấy thông tin chi nhánh theo ID
    3. create(branchData) - Tạo chi nhánh mới
    4. update(branchId, branchData) - Cập nhật chi nhánh
    5. delete(branchId) - Xóa chi nhánh
    // Lấy danh sách với phân trang
    const branches = await client.branches.list({
    pageSize: 20,
    currentItem: 0
    });

    // Lọc theo trạng thái
    const activeBranches = await client.branches.list({
    isActive: true,
    pageSize: 20
    });

    // Lọc chi nhánh chính
    const mainBranches = await client.branches.list({
    isMain: true,
    pageSize: 20
    });

    // Tìm kiếm theo tên hoặc mã
    const searchBranches = await client.branches.list({
    code: "CN001",
    name: "Chi nhánh"
    });
    const newBranch = await client.branches.create({
    name: "Chi nhánh ABC", // Tên chi nhánh (bắt buộc)
    address: "123 Đường XYZ", // Địa chỉ (bắt buộc)
    code: "CN001", // Mã chi nhánh
    wardName: "Phường 1", // Phường/Xã
    districtName: "Quận 1", // Quận/Huyện
    cityName: "TP.HCM", // Tỉnh/Thành phố
    phoneNumber: "028.1234567", // Số điện thoại cố định
    contactNumber: "0901234567", // Số điện thoại di động
    email: "chinhanh@email.com", // Email
    isActive: true, // Trạng thái hoạt động
    locationName: "Khu vực Nam", // Tên khu vực
    latLng: "10.7756587,106.7004238", // Tọa độ
    parentId: 1, // ID chi nhánh cha
    level: 1 // Cấp độ phân cấp
    });
    const updatedBranch = await client.branches.update(123, {
    name: "Chi nhánh ABC (Mới)",
    address: "456 Đường XYZ",
    phoneNumber: "028.9876543",
    isActive: false
    });
    await client.branches.delete(123);
    
    interface Branch {
    id: number; // ID chi nhánh
    branchId: string; // Mã định danh
    name: string; // Tên chi nhánh
    address: string; // Địa chỉ
    wardName?: string; // Phường/Xã
    districtName?: string; // Quận/Huyện
    cityName?: string; // Tỉnh/Thành phố
    phoneNumber?: string; // Số điện thoại cố định
    email?: string; // Email
    isActive: boolean; // Trạng thái hoạt động
    isMain: boolean; // Là chi nhánh chính
    retailerId: number; // ID nhà bán lẻ
    locationId?: number; // ID khu vực
    locationName?: string; // Tên khu vực
    contactNumber?: string; // Số điện thoại di động
    latLng?: string; // Tọa độ địa lý
    code?: string; // Mã chi nhánh
    parentId?: number; // ID chi nhánh cha
    level?: number; // Cấp độ phân cấp
    hasChild?: boolean; // Có chi nhánh con
    createdBy?: string; // Người tạo
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface BranchCreateParams {
    name: string; // Tên chi nhánh (bắt buộc)
    address: string; // Địa chỉ (bắt buộc)
    code?: string; // Mã chi nhánh
    branchId?: string; // Mã định danh
    wardName?: string; // Phường/Xã
    districtName?: string; // Quận/Huyện
    cityName?: string; // Tỉnh/Thành phố
    phoneNumber?: string; // Số điện thoại cố định
    contactNumber?: string; // Số điện thoại di động
    email?: string; // Email
    isActive?: boolean; // Trạng thái hoạt động
    locationId?: number; // ID khu vực
    locationName?: string; // Tên khu vực
    latLng?: string; // Tọa độ địa lý
    parentId?: number; // ID chi nhánh cha
    level?: number; // Cấp độ phân cấp
    }
    interface BranchListParams {
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    lastModifiedFrom?: string; // Từ ngày cập nhật
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    isActive?: boolean; // Lọc theo trạng thái
    isMain?: boolean; // Lọc chi nhánh chính
    code?: string; // Lọc theo mã
    name?: string; // Lọc theo tên
    parentId?: number; // Lọc theo chi nhánh cha
    level?: number; // Lọc theo cấp độ
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    }
    1. Phân cấp chi nhánh:

      • Chi nhánh có thể được tổ chức theo cấu trúc cha-con
      • Sử dụng parentId để xác định mối quan hệ
      • level chỉ định cấp độ trong hệ thống phân cấp
    2. Chi nhánh chính:

      • Mỗi cửa hàng có một chi nhánh chính (isMain: true)
      • Chi nhánh chính thường được sử dụng làm mặc định cho các hoạt động
    3. Vị trí địa lý:

      • Có thể lưu tọa độ trong latLng (định dạng "latitude,longitude")
      • Hỗ trợ phân chia theo khu vực địa lý qua locationIdlocationName
    4. Phân trang và tìm kiếm:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Hỗ trợ tìm kiếm theo tên và mã chi nhánh
      • Có thể lọc theo trạng thái hoạt động và loại chi nhánh

    Nhà cung cấp (Suppliers)

    API Nhà cung cấp (Suppliers)

    1. list(params) - Lấy danh sách nhà cung cấp
    2. getById(id) - Lấy thông tin nhà cung cấp theo ID
    3. getByCode(code) - Lấy nhà cung cấp theo mã
    // Lấy danh sách với phân trang
    const suppliers = await client.suppliers.list({
    pageSize: 20,
    currentItem: 0
    });

    // Lọc theo trạng thái và nhóm
    const suppliers = await client.suppliers.list({
    isActive: true,
    supplierGroupId: 1,
    includeSupplierGroup: true
    });

    // Tìm kiếm theo tên hoặc mã
    const suppliers = await client.suppliers.list({
    code: "NCC001",
    name: "Công ty",
    contactNumber: "0901234567"
    });

    // Sắp xếp kết quả
    const suppliers = await client.suppliers.list({
    orderBy: "name",
    orderDirection: "ASC",
    pageSize: 20
    });
    // Theo ID
    const supplier = await client.suppliers.getById(123);

    // Theo mã
    const supplier = await client.suppliers.getByCode("NCC001");
    interface Supplier {
    id: number; // ID nhà cung cấp
    code: string; // Mã nhà cung cấp
    name: string; // Tên nhà cung cấp
    contactNumber?: string; // Số điện thoại
    email?: string; // Email
    address?: string; // Địa chỉ
    locationName?: string; // Tên địa điểm
    wardName?: string; // Phường/Xã
    organization?: string; // Tên tổ chức
    taxCode?: string; // Mã số thuế
    comments?: string; // Ghi chú
    description?: string; // Mô tả
    isActive: boolean; // Trạng thái hoạt động
    retailerId: number; // ID nhà bán lẻ
    branchId?: number; // ID chi nhánh
    debt?: number; // Công nợ
    totalInvoiced?: number; // Tổng hóa đơn
    totalInvoicedWithoutReturn?: number; // Tổng hóa đơn (không tính trả hàng)
    supplierGroupId?: number; // ID nhóm nhà cung cấp
    supplierGroupIds?: number[]; // Danh sách ID nhóm
    supplierGroup?: { // Thông tin nhóm
    id: number; // ID nhóm
    name: string; // Tên nhóm
    description?: string; // Mô tả nhóm
    retailerId: number; // ID nhà bán lẻ
    isActive: boolean; // Trạng thái nhóm
    };
    createdBy?: string; // Người tạo
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface SupplierListParams {
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    code?: string; // Lọc theo mã
    name?: string; // Lọc theo tên
    contactNumber?: string; // Lọc theo SĐT
    lastModifiedFrom?: string; // Từ ngày cập nhật
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    includeTotal?: boolean; // Bao gồm tổng số
    includeSupplierGroup?: boolean; // Bao gồm thông tin nhóm
    isActive?: boolean; // Lọc theo trạng thái
    supplierGroupId?: number; // Lọc theo nhóm
    }
    interface SupplierCreateParams {
    code?: string; // Mã nhà cung cấp
    name: string; // Tên nhà cung cấp (bắt buộc)
    contactNumber?: string; // Số điện thoại
    email?: string; // Email
    address?: string; // Địa chỉ
    locationName?: string; // Tên địa điểm
    wardName?: string; // Phường/Xã
    organization?: string; // Tên tổ chức
    taxCode?: string; // Mã số thuế
    comments?: string; // Ghi chú
    description?: string; // Mô tả
    isActive?: boolean; // Trạng thái hoạt động
    branchId?: number; // ID chi nhánh
    supplierGroupIds?: number[]; // Danh sách ID nhóm
    }
    1. Quản lý nhà cung cấp:

      • Có thể phân nhóm nhà cung cấp để dễ quản lý
      • Theo dõi công nợ và tổng giá trị hóa đơn
      • Hỗ trợ ghi chú và mô tả chi tiết
    2. Tìm kiếm và lọc:

      • Hỗ trợ tìm kiếm theo mã, tên, số điện thoại
      • Lọc theo trạng thái hoạt động và nhóm
      • Có thể sắp xếp theo nhiều tiêu chí
    3. Thông tin bổ sung:

      • Có thể bao gồm thông tin nhóm nhà cung cấp
      • Theo dõi người tạo và thời gian cập nhật
      • Lưu trữ thông tin địa chỉ và thuế
    4. Phân trang và hiệu suất:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Hỗ trợ lấy tổng số bản ghi với includeTotal

    Vouchers

    API Voucher (Vouchers)

    1. listCampaigns(params) - Lấy danh sách chiến dịch voucher
    2. getCampaign(id) - Lấy thông tin chiến dịch theo ID
    3. createCampaign(data) - Tạo chiến dịch mới
    4. updateCampaign(data) - Cập nhật chiến dịch
    5. deleteCampaign(id) - Xóa chiến dịch
    1. list(params) - Lấy danh sách voucher
    2. get(id) - Lấy thông tin voucher theo ID
    3. getByCode(code) - Lấy voucher theo mã
    // Tạo chiến dịch voucher mới
    const newCampaign = await client.vouchers.createCampaign({
    code: "SUMMER2024", // Mã chiến dịch (bắt buộc)
    name: "Khuyến mãi hè 2024", // Tên chiến dịch (bắt buộc)
    description: "Giảm giá hè 2024", // Mô tả
    startDate: "2024-06-01", // Ngày bắt đầu (bắt buộc)
    endDate: "2024-08-31", // Ngày kết thúc (bắt buộc)
    branchId: 1, // ID chi nhánh (bắt buộc)
    branchIds: [1, 2, 3], // Danh sách chi nhánh áp dụng
    customerGroupIds: [1, 2], // Nhóm khách hàng được áp dụng
    discountType: VoucherDiscountType.Percentage, // Loại giảm giá
    discountValue: 10, // Giá trị giảm (10%)
    minOrderValue: 1000000, // Giá trị đơn hàng tối thiểu
    maxDiscountValue: 200000, // Giảm giá tối đa
    quantity: 1000, // Số lượng voucher
    isAutoGenerate: true, // Tự động tạo mã
    isUnlimited: false // Không giới hạn số lượng
    });

    // Lấy danh sách chiến dịch
    const campaigns = await client.vouchers.listCampaigns({
    status: [VoucherStatus.Active],
    fromDate: "2024-01-01",
    toDate: "2024-12-31",
    pageSize: 20
    });

    // Cập nhật chiến dịch
    const updatedCampaign = await client.vouchers.updateCampaign({
    id: 123,
    endDate: "2024-09-30",
    maxDiscountValue: 300000
    });

    // Xóa chiến dịch
    await client.vouchers.deleteCampaign(123);
    // Lấy danh sách voucher của một chiến dịch
    const vouchers = await client.vouchers.list({
    campaignId: 123,
    status: [VoucherStatus.Active],
    pageSize: 20
    });

    // Tìm voucher theo mã
    const voucher = await client.vouchers.getByCode("SUMMER2024ABC");

    // Lấy thông tin voucher
    const voucher = await client.vouchers.get(456);
    interface VoucherCampaign {
    id: number; // ID chiến dịch
    code: string; // Mã chiến dịch
    name: string; // Tên chiến dịch
    description?: string; // Mô tả
    startDate: string; // Ngày bắt đầu
    endDate: string; // Ngày kết thúc
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    isActive: boolean; // Đang hoạt động
    branchId: number; // ID chi nhánh
    branchIds?: number[]; // Danh sách chi nhánh
    customerGroupIds?: number[]; // Nhóm khách hàng
    discountType: number; // Loại giảm giá
    discountValue: number; // Giá trị giảm
    minOrderValue?: number; // Đơn hàng tối thiểu
    maxDiscountValue?: number; // Giảm tối đa
    quantity: number; // Số lượng
    usedQuantity: number; // Đã sử dụng
    remainingQuantity: number; // Còn lại
    isAutoGenerate: boolean; // Tự động tạo mã
    isUnlimited: boolean; // Không giới hạn
    voucherProducts?: VoucherProduct[]; // Sản phẩm áp dụng
    }
    interface Voucher {
    id: number; // ID voucher
    code: string; // Mã voucher
    campaignId: number; // ID chiến dịch
    campaignCode: string; // Mã chiến dịch
    campaignName: string; // Tên chiến dịch
    startDate: string; // Ngày bắt đầu
    endDate: string; // Ngày kết thúc
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    isUsed: boolean; // Đã sử dụng
    usedDate?: string; // Ngày sử dụng
    customerId?: number; // ID khách hàng
    customerCode?: string; // Mã khách hàng
    customerName?: string; // Tên khách hàng
    orderId?: number; // ID đơn hàng
    orderCode?: string; // Mã đơn hàng
    discountValue: number; // Giá trị giảm
    }
    enum VoucherStatus {
    Active = 1, // Đang hoạt động
    Inactive = 0, // Không hoạt động
    Used = 2, // Đã sử dụng
    Expired = 3 // Hết hạn
    }
    enum VoucherDiscountType {
    FixedAmount = 1, // Giảm số tiền cố định
    Percentage = 2 // Giảm theo phần trăm
    }
    1. Chiến dịch voucher:

      • Có thể giới hạn theo chi nhánh và nhóm khách hàng
      • Hỗ trợ hai loại giảm giá: cố định và phần trăm
      • Có thể tự động tạo mã voucher hoặc nhập thủ công
    2. Giới hạn giảm giá:

      • minOrderValue: Giá trị đơn hàng tối thiểu để áp dụng
      • maxDiscountValue: Giới hạn số tiền giảm tối đa
      • Đối với giảm giá theo phần trăm, luôn kiểm tra maxDiscountValue
    3. Quản lý số lượng:

      • Theo dõi số lượng đã sử dụng và còn lại
      • Có thể tạo không giới hạn số lượng với isUnlimited
      • Hệ thống tự động cập nhật số lượng khi voucher được sử dụng
    4. Tìm kiếm và lọc:

      • Hỗ trợ tìm kiếm theo từ khóa (mã, tên)
      • Lọc theo trạng thái và khoảng thời gian
      • Phân trang với pageSizecurrentItem

    Người dùng (Users)

    API Người dùng (Users)

    1. list(params) - Lấy danh sách người dùng
    2. getById(userId) - Lấy thông tin người dùng theo ID
    3. getActive(params) - Lấy danh sách người dùng đang hoạt động
    4. getByBranch(branchId, params) - Lấy người dùng theo chi nhánh
    5. search(query, params) - Tìm kiếm người dùng
    // Lấy danh sách với phân trang
    const users = await client.users.list({
    pageSize: 20,
    currentItem: 0
    });

    // Sắp xếp kết quả
    const users = await client.users.list({
    orderBy: "userName",
    orderDirection: "ASC"
    });

    // Lấy người dùng đang hoạt động
    const activeUsers = await client.users.getActive({
    pageSize: 20
    });
    // Tìm theo tên hoặc mã
    const searchResults = await client.users.search("John", {
    pageSize: 20
    });

    // Tìm theo chi nhánh
    const branchUsers = await client.users.getByBranch(1, {
    pageSize: 20
    });
    // Lấy theo ID
    const user = await client.users.getById(123);
    interface User {
    id: number; // ID người dùng
    userName: string; // Tên đăng nhập
    givenName: string; // Tên hiển thị
    address?: string; // Địa chỉ
    mobilePhone?: string; // Số điện thoại
    email?: string; // Email
    description?: string; // Mô tả
    retailerId: number; // ID nhà bán lẻ
    birthDate?: string; // Ngày sinh
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface UserListParams {
    lastModifiedFrom?: string; // Từ ngày cập nhật
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    }
    1. Tìm kiếm người dùng:

      • Hỗ trợ tìm kiếm theo tên hoặc mã người dùng
      • Tìm kiếm không phân biệt chữ hoa/thường
      • API sẽ tự động tìm kiếm cả trong tên đăng nhập và tên hiển thị
    2. Phân quyền:

      • Người dùng có thể được gán cho một hoặc nhiều chi nhánh
      • Sử dụng getByBranch() để lấy danh sách người dùng theo chi nhánh
      • Quản lý trạng thái hoạt động thông qua getActive()
    3. Thông tin chi tiết:

      • Mỗi người dùng có một tên đăng nhập duy nhất
      • Có thể quản lý thông tin cá nhân như địa chỉ, số điện thoại, email
      • Theo dõi thời gian tạo và cập nhật thông tin
    4. Phân trang và sắp xếp:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Có thể sắp xếp theo nhiều trường khác nhau
      • Hướng sắp xếp: 'ASC' (tăng dần) hoặc 'DESC' (giảm dần)

    Báo cáo dòng tiền (Cash Flow)

    API Dòng tiền (Cash Flow)

    1. list(params) - Lấy danh sách dòng tiền
    2. processPayment(data) - Xử lý thanh toán cho hóa đơn
    // Lấy danh sách với phân trang
    const cashFlows = await client.cashFlow.list({
    pageSize: 20,
    currentItem: 0
    });

    // Lọc theo chi nhánh và thời gian
    const cashFlows = await client.cashFlow.list({
    branchIds: [1, 2],
    startDate: "2024-01-01",
    endDate: "2024-01-31"
    });

    // Lọc theo phương thức thanh toán
    const cashFlows = await client.cashFlow.list({
    method: ["Cash", "Card", "Transfer"],
    includeAccount: true
    });

    // Tìm kiếm theo đối tác
    const cashFlows = await client.cashFlow.list({
    partnerName: "Công ty ABC",
    contactNumber: "0901234567"
    });
    // Thanh toán tiền mặt
    const payment = await client.cashFlow.processPayment({
    invoiceId: 123,
    amount: 1000000,
    method: "Cash"
    });

    // Thanh toán qua thẻ/chuyển khoản
    const payment = await client.cashFlow.processPayment({
    invoiceId: 123,
    amount: 1000000,
    method: "Card",
    accountId: 1 // ID tài khoản ngân hàng
    });
    interface CashFlow {
    id: number; // ID dòng tiền
    code: string; // Mã dòng tiền
    branchId: number; // ID chi nhánh
    address?: string; // Địa chỉ
    wardName?: string; // Phường/Xã
    contactNumber?: string; // Số điện thoại
    createdBy: number; // Người tạo
    usedForFinancialReporting: number; // Dùng cho báo cáo tài chính
    cashFlowGroupId?: number; // ID nhóm dòng tiền
    method: string; // Phương thức thanh toán
    partnerType: string; // Loại đối tác
    partnerId?: number; // ID đối tác
    status: number; // Mã trạng thái
    statusValue: string; // Tên trạng thái
    transDate: string; // Ngày giao dịch
    amount: number; // Số tiền
    partnerName: string; // Tên đối tác
    user: string; // Người dùng
    accountId?: number; // ID tài khoản
    description?: string; // Ghi chú
    }
    interface PaymentRequest {
    amount: number; // Số tiền thanh toán
    method: string; // Phương thức (Cash, Card, Transfer)
    accountId?: number; // ID tài khoản (bắt buộc với Card/Transfer)
    invoiceId: number; // ID hóa đơn
    }
    interface PaymentResponse {
    paymentId: number; // ID thanh toán
    paymentCode: string; // Mã thanh toán
    amount: number; // Số tiền
    method: string; // Phương thức
    accountId?: number; // ID tài khoản
    invoiceId: number; // ID hóa đơn
    documentCode: number; // Mã chứng từ
    }
    interface CashFlowListParams {
    branchIds?: number[]; // Lọc theo chi nhánh
    code?: string[]; // Lọc theo mã
    userId?: number; // Lọc theo người dùng
    accountId?: number; // Lọc theo tài khoản
    partnerType?: string; // Lọc theo loại đối tác
    method?: string[]; // Lọc theo phương thức
    cashFlowGroupId?: number[]; // Lọc theo nhóm
    usedForFinancialReporting?: number; // Dùng cho báo cáo
    partnerName?: string; // Tìm theo tên đối tác
    contactNumber?: string; // Tìm theo SĐT
    isReceipt?: boolean; // Là phiếu thu
    includeAccount?: boolean; // Bao gồm thông tin tài khoản
    includeBranch?: boolean; // Bao gồm thông tin chi nhánh
    includeUser?: boolean; // Bao gồm thông tin người dùng
    startDate?: string; // Từ ngày
    endDate?: string; // Đến ngày
    status?: number; // Lọc theo trạng thái
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    }
    1. Phương thức thanh toán:

      • Hỗ trợ nhiều phương thức: Tiền mặt (Cash), Thẻ (Card), Chuyển khoản (Transfer)
      • Với thanh toán qua thẻ hoặc chuyển khoản, bắt buộc cung cấp accountId
      • Mỗi giao dịch được ghi nhận với mã duy nhất
    2. Báo cáo tài chính:

      • Dùng trường usedForFinancialReporting để đánh dấu giao dịch cho báo cáo
      • Có thể nhóm các giao dịch theo cashFlowGroupId
      • Hỗ trợ xuất báo cáo theo nhiều tiêu chí
    3. Tìm kiếm và lọc:

      • Có thể lọc theo nhiều tiêu chí: chi nhánh, thời gian, phương thức, đối tác
      • Hỗ trợ tìm kiếm theo tên đối tác và số điện thoại
      • Phân trang với pageSizecurrentItem
    4. Thông tin bổ sung:

      • Có thể bao gồm thông tin tài khoản, chi nhánh, người dùng
      • Mỗi giao dịch đều có thông tin đối tác và người thực hiện
      • Ghi chú có thể được thêm vào để theo dõi chi tiết

    Phụ thu (Surcharges)

    API Phụ thu (Surcharges)

    1. list(params) - Lấy danh sách phụ thu
    2. getById(surchargeId) - Lấy thông tin phụ thu theo ID
    3. create(surchargeData) - Tạo phụ thu mới
    4. update(surchargeId, surchargeData) - Cập nhật phụ thu
    5. delete(surchargeId) - Xóa phụ thu
    const newSurcharge = await client.surcharges.create({
    name: "Phí vận chuyển", // Tên phụ thu (bắt buộc)
    code: "SHIP", // Mã phụ thu
    value: 30000, // Giá trị phụ thu (bắt buộc)
    isPercent: false, // Tính theo phần trăm (bắt buộc)
    isAutoAdd: true, // Tự động thêm vào đơn hàng
    isRequired: false, // Bắt buộc áp dụng
    description: "Phí giao hàng", // Mô tả
    branchIds: [1, 2], // Áp dụng cho chi nhánh
    isActive: true // Trạng thái hoạt động
    });

    // Tạo phụ thu theo phần trăm
    const percentSurcharge = await client.surcharges.create({
    name: "Phí dịch vụ",
    code: "SERVICE",
    value: 10, // 10%
    isPercent: true,
    isAutoAdd: true,
    isRequired: true
    });
    // Lấy danh sách với phân trang
    const surcharges = await client.surcharges.list({
    pageSize: 20,
    currentItem: 0
    });

    // Lọc theo chi nhánh và trạng thái
    const surcharges = await client.surcharges.list({
    branchId: 1,
    isActive: true
    });

    // Tìm kiếm theo tên hoặc mã
    const surcharges = await client.surcharges.list({
    code: "SHIP",
    name: "vận chuyển"
    });
    const updatedSurcharge = await client.surcharges.update(123, {
    value: 35000,
    description: "Phí vận chuyển mới",
    isActive: true
    });
    await client.surcharges.delete(123);
    
    interface Surcharge {
    id: number; // ID phụ thu
    code: string; // Mã phụ thu
    name: string; // Tên phụ thu
    value: number; // Giá trị phụ thu
    isPercent: boolean; // Tính theo phần trăm
    isAutoAdd: boolean; // Tự động thêm
    isRequired: boolean; // Bắt buộc áp dụng
    description?: string; // Mô tả
    isActive: boolean; // Trạng thái hoạt động
    retailerId: number; // ID nhà bán lẻ
    branchId?: number; // ID chi nhánh
    branchIds?: number[]; // Danh sách chi nhánh
    createdBy?: string; // Người tạo
    createdDate: string; // Ngày tạo
    modifiedDate?: string; // Ngày cập nhật
    }
    interface SurchargeCreateParams {
    code?: string; // Mã phụ thu
    name: string; // Tên phụ thu (bắt buộc)
    value: number; // Giá trị phụ thu (bắt buộc)
    isPercent: boolean; // Tính theo phần trăm (bắt buộc)
    isAutoAdd?: boolean; // Tự động thêm
    isRequired?: boolean; // Bắt buộc áp dụng
    description?: string; // Mô tả
    branchIds?: number[]; // Danh sách chi nhánh
    isActive?: boolean; // Trạng thái hoạt động
    }
    interface SurchargeListParams {
    pageSize?: number; // Số lượng trên trang
    currentItem?: number; // Vị trí bắt đầu
    lastModifiedFrom?: string; // Từ ngày cập nhật
    orderBy?: string; // Sắp xếp theo trường
    orderDirection?: 'ASC' | 'DESC'; // Hướng sắp xếp
    isActive?: boolean; // Lọc theo trạng thái
    branchId?: number; // Lọc theo chi nhánh
    includeRemoveIds?: boolean; // Bao gồm đã xóa
    code?: string; // Lọc theo mã
    name?: string; // Lọc theo tên
    }
    1. Loại phụ thu:

      • Có thể tính theo số tiền cố định hoặc phần trăm
      • Phụ thu theo phần trăm sẽ được tính dựa trên tổng giá trị đơn hàng
      • Có thể thiết lập tự động thêm vào đơn hàng (isAutoAdd)
    2. Áp dụng phụ thu:

      • Có thể áp dụng cho toàn bộ cửa hàng hoặc chỉ một số chi nhánh
      • Phụ thu bắt buộc (isRequired) không thể bỏ qua khi tạo đơn hàng
      • Có thể tắt/bật phụ thu thông qua trạng thái isActive
    3. Tìm kiếm và lọc:

      • Hỗ trợ tìm kiếm theo mã và tên phụ thu
      • Có thể lọc theo chi nhánh và trạng thái
      • Theo dõi thời gian cập nhật qua lastModifiedFrom
    4. Phân trang và hiệu suất:

      • Mặc định pageSize là 20
      • currentItem bắt đầu từ 0
      • Hỗ trợ sắp xếp theo nhiều tiêu chí

    Webhooks

    API Webhook (Webhooks)

    1. list(params) - Lấy danh sách webhook
    2. getById(webhookId) - Lấy thông tin webhook theo ID
    3. create(webhookData) - Tạo webhook mới
    4. update(webhookId, webhookData) - Cập nhật webhook
    5. delete(webhookId) - Xóa webhook
    6. enable(webhookId) - Kích hoạt webhook
    7. disable(webhookId) - Vô hiệu hóa webhook
    8. verifySignature(payload, signature, secret) - Xác thực chữ ký webhook
    9. parseWebhookPayload(payload, signature, secret) - Phân tích và xác thực dữ liệu webhook
    const newWebhook = await client.webhooks.create({
    url: "https://your-domain.com/webhook", // URL nhận webhook (bắt buộc)
    secret: "your-secret-key", // Khóa bí mật (bắt buộc)
    events: [ // Danh sách sự kiện (bắt buộc)
    WebhookEvent.OrderCreated,
    WebhookEvent.OrderUpdated
    ],
    isActive: true // Trạng thái kích hoạt
    });
    // Lấy tất cả webhook
    const webhooks = await client.webhooks.list({
    pageSize: 20,
    currentItem: 0
    });

    // Lọc theo trạng thái
    const activeWebhooks = await client.webhooks.list({
    isActive: true
    });
    // Kích hoạt webhook
    await client.webhooks.enable(123);

    // Vô hiệu hóa webhook
    await client.webhooks.disable(123);
    // Middleware để lấy raw body cho việc tạo chữ ký
    app.use(bodyParser.json({
    verify: (req, res, buf) => {
    req.rawBody = buf;
    }
    }));

    // Xử lý webhook trong Express.js
    app.post('/webhook', (req, res) => {
    const signature = req.headers['x-hub-signature'];
    const payload = req.rawBody.toString();
    const secret = 'your-webhook-secret';

    try {
    // Xác thực và phân tích payload
    const webhookData = client.webhooks.parseWebhookPayload(
    payload,
    signature,
    secret
    );

    // Xử lý dữ liệu dựa trên loại sự kiện
    switch (webhookData.event) {
    // ===== CUSTOMER EVENTS =====
    case WebhookEvent.CustomerUpdated:
    // Xử lý cập nhật khách hàng
    const customerData = webhookData.data as CustomerUpdateWebhookPayload;

    // Truy cập dữ liệu khách hàng đã cập nhật
    for (const notification of customerData.Notifications) {
    for (const customer of notification.Data) {
    console.log(`Khách hàng cập nhật: ${customer.Name} (${customer.Code})`);
    // Xử lý cập nhật khách hàng
    }
    }
    break;

    case WebhookEvent.CustomerDeleted:
    const customerDeleteData = webhookData.data as { RemoveId: number[] };
    console.log('ID khách hàng bị xóa:', customerDeleteData.RemoveId);
    break;

    // ===== PRODUCT EVENTS =====
    case WebhookEvent.ProductUpdated:
    const productData = webhookData.data as ProductUpdateWebhookPayload;
    // Xử lý cập nhật sản phẩm
    break;

    case WebhookEvent.ProductDeleted:
    const productDeleteData = webhookData.data as { RemoveId: number[] };
    console.log('ID sản phẩm bị xóa:', productDeleteData.RemoveId);
    break;

    // ===== ORDER EVENTS =====
    case WebhookEvent.OrderCreated:
    case WebhookEvent.OrderUpdated:
    const orderData = webhookData.data as OrderUpdateWebhookPayload;
    // Xử lý đơn hàng
    break;

    // ===== INVOICE EVENTS =====
    case WebhookEvent.InvoiceUpdated:
    const invoiceData = webhookData.data as InvoiceUpdateWebhookPayload;
    // Xử lý hóa đơn
    break;

    // ===== STOCK EVENTS =====
    case WebhookEvent.StockUpdated:
    const stockData = webhookData.data as StockUpdateWebhookPayload;
    // Xử lý cập nhật tồn kho
    break;

    // ===== PRICEBOOK EVENTS =====
    case WebhookEvent.PriceBookUpdated:
    case WebhookEvent.PriceBookDetailUpdated:
    // Xử lý cập nhật bảng giá
    break;
    }

    res.sendStatus(200);
    } catch (error) {
    console.error('Lỗi webhook:', error);
    res.sendStatus(400);
    }
    });
    interface Webhook {
    id: number; // ID webhook
    url: string; // URL nhận webhook
    secret: string; // Khóa bí mật
    events: WebhookEvent[]; // Danh sách sự kiện
    isActive: boolean; // Trạng thái kích hoạt
    createdDate: string; // Ngày tạo
    modifiedDate: string; // Ngày cập nhật
    retailerId: number; // ID nhà bán lẻ
    }
    enum WebhookEvent {
    // Sự kiện sản phẩm (Product)
    ProductCreated = 'product.created',
    ProductUpdated = 'product.updated',
    ProductDeleted = 'product.deleted',

    // Sự kiện danh mục (Category)
    CategoryCreated = 'category.created',
    CategoryUpdated = 'category.updated',
    CategoryDeleted = 'category.deleted',

    // Sự kiện khách hàng (Customer)
    CustomerCreated = 'customer.created',
    CustomerUpdated = 'customer.updated',
    CustomerDeleted = 'customer.deleted',

    // Sự kiện đơn hàng (Order)
    OrderCreated = 'order.created',
    OrderUpdated = 'order.updated',
    OrderDeleted = 'order.deleted',

    // Sự kiện hóa đơn (Invoice)
    InvoiceCreated = 'invoice.created',
    InvoiceUpdated = 'invoice.updated',
    InvoiceDeleted = 'invoice.deleted',

    // Sự kiện tồn kho (Stock)
    StockUpdated = 'stock.update',

    // Sự kiện bảng giá (PriceBook)
    PriceBookUpdated = 'pricebook.update',
    PriceBookDeleted = 'pricebook.delete',
    PriceBookDetailUpdated = 'pricebookdetail.update',
    PriceBookDetailDeleted = 'pricebookdetail.delete',

    // Sự kiện chi nhánh (Branch)
    BranchUpdated = 'branch.update',
    BranchDeleted = 'branch.delete'
    }

    SDK cung cấp các interface để giúp bạn xử lý dữ liệu webhook một cách dễ dàng với TypeScript. Dưới đây là các cấu trúc dữ liệu chính:

    export interface WebhookPayload<T = any> {
    event: WebhookEvent; // Loại sự kiện
    data: T; // Dữ liệu sự kiện
    timestamp: string; // Thời gian xảy ra
    retailerId: number; // ID nhà bán lẻ
    signature: string; // Chữ ký
    }

    // Cấu trúc chung cho các webhook sự kiện update
    export interface GenericUpdateWebhookPayload<T> {
    Id: string; // ID của webhook
    Attempt: number; // Số lần thử gửi
    Notifications: [{
    Action: string; // Hành động (customer.update, product.update, ...)
    Data: T[]; // Mảng dữ liệu
    }]
    }

    // Cấu trúc chung cho các webhook sự kiện delete
    export interface GenericDeleteWebhookPayload {
    RemoveId: number[]; // Mảng ID các mục bị xóa
    }

    // Kiểu dữ liệu cho webhook customer.update
    export interface CustomerUpdateWebhookPayload extends GenericUpdateWebhookPayload<CustomerWebhookData> {}

    export interface CustomerWebhookData {
    Id: number; // ID khách hàng
    Code: string; // Mã khách hàng
    Name: string; // Tên khách hàng
    Gender?: boolean; // Giới tính
    BirthDate?: string; // Ngày sinh
    ContactNumber?: string; // Số điện thoại
    Address?: string; // Địa chỉ
    LocationName?: string; // Tên địa điểm
    Email?: string; // Email
    ModifiedDate: string; // Ngày cập nhật
    Type?: number; // Loại khách hàng
    Organization?: string; // Tổ chức
    TaxCode?: string; // Mã số thuế
    Comments?: string; // Ghi chú
    }
    1. Bảo mật:

      • Mỗi webhook có một khóa bí mật riêng (secret)
      • Sử dụng chữ ký HMAC SHA-256 để xác thực payload
      • Luôn xác thực chữ ký trước khi xử lý dữ liệu
    2. Xử lý sự kiện:

      • Webhook được gửi bất đồng bộ
      • Nên xử lý nhanh và trả về response ngay
      • Đưa xử lý phức tạp vào hàng đợi hoặc worker
    3. Độ tin cậy:

      • KiotViet sẽ thử gửi lại webhook nếu không nhận được phản hồi 2xx
      • Xử lý webhook phải bảo đảm idempotent (có thể thực hiện nhiều lần)
      • Lưu ID sự kiện để tránh xử lý trùng lặp
    4. Quản lý:

      • Có thể kích hoạt/vô hiệu hóa webhook bất kỳ lúc nào
      • Một webhook có thể đăng ký nhiều loại sự kiện
      • Nên giám sát webhook để đảm bảo hoạt động ổn định

    Cài đặt (Settings)

    API Cài đặt (Settings)

    1. get() - Lấy thông tin cài đặt hiện tại
    // Lấy cài đặt hiện tại
    const settings = await client.settings.get();

    // Kiểm tra các cài đặt cụ thể
    if (settings.allowSellWhenOutStock) {
    // Cho phép bán khi hết hàng
    }

    if (settings.managerCustomerByBranch) {
    // Quản lý khách hàng theo chi nhánh
    }
    interface Setting {
    managerCustomerByBranch: boolean; // Quản lý khách hàng theo chi nhánh
    allowOrderWhenOutStock: boolean; // Cho phép đặt hàng khi hết hàng
    allowSellWhenOrderOutStock: boolean; // Cho phép bán khi đơn đặt hàng hết hàng
    allowSellWhenOutStock: boolean; // Cho phép bán khi hết hàng tồn kho
    }
    1. Cài đặt quản lý tồn kho:

      • allowSellWhenOutStock: Cho phép bán hàng khi sản phẩm hết tồn kho
      • allowOrderWhenOutStock: Cho phép đặt hàng khi sản phẩm hết tồn kho
      • allowSellWhenOrderOutStock: Cho phép bán khi đơn đặt hàng vượt quá số lượng tồn kho
    2. Cài đặt quản lý khách hàng:

      • managerCustomerByBranch: Khi bật tính năng này, mỗi chi nhánh sẽ quản lý danh sách khách hàng riêng
      • Ảnh hưởng đến việc hiển thị và tìm kiếm khách hàng trong từng chi nhánh
    3. Tính năng này chỉ hỗ trợ đọc cài đặt:

      • Không thể thay đổi cài đặt thông qua API
      • Các thay đổi cài đặt phải được thực hiện trên giao diện KiotViet
    4. Ảnh hưởng đến quy trình bán hàng:

      • Các cài đặt này ảnh hưởng trực tiếp đến logic xử lý đơn hàng và tồn kho
      • Nên kiểm tra các cài đặt này trước khi thực hiện các thao tác liên quan đến bán hàng và đặt hàng
      • Điều chỉnh logic ứng dụng dựa trên các cài đặt này để đảm bảo tuân thủ quy tắc kinh doanh