API 整合

iDempiere REST API 概述

iDempiere Mobile 透過 idempiere-rest 外掛的 OData REST API 與 iDempiere ERP 伺服器通訊。所有資料存取都使用標準的 HTTP 方法操作 REST 端點。

┌─────────────────────┐       HTTPS/JWT        ┌──────────────────────┐
│   Flutter App        │ <──────────────────►   │  iDempiere ERP       │
│   (iOS/Android/Web/  │   idempiere-rest API   │  + idempiere-rest    │
│    macOS)            │   /api/v1/...          │    plugin            │
└─────────────────────┘                         └──────────────────────┘

認證流程(兩步驟)

認證分為兩個步驟:先登入取得臨時 token 和可用角色列表,再選擇角色取得正式 session token。

步驟 1:登入

POST /api/v1/auth/tokens
Content-Type: application/json

{
  "userName": "your-username",
  "password": "your-password"
}

# 回應:
{
  "token": "jwt-temporary-token...",
  "clients": [
    {
      "id": 11,
      "name": "GardenWorld",
      "roles": [
        { "id": 102, "name": "GardenAdmin" }
      ]
    }
  ]
}

步驟 2:選擇角色

PUT /api/v1/auth/tokens
Authorization: Bearer jwt-temporary-token
Content-Type: application/json

{
  "clientId": 11,
  "roleId": 102,
  "organizationId": 11,
  "language": "zh_TW"
}

# 回應:
{
  "token": "jwt-session-token..."
}
  • 步驟 1 回傳臨時 token 和可用的 client/role 列表
  • 步驟 2 選擇工作的 client/role/org 並回傳正式 session token
  • Token 儲存在 FlutterSecureStorage(iOS Keychain / Android EncryptedSharedPreferences)
  • 後續所有請求由 AuthInterceptor 自動附加 Authorization: Bearer <token>

CRUD 操作

所有資料存取使用 GET/POST/PUT/DELETE 操作 /api/v1/models/{tableName}

HTTP 方法 端點 用途
GET /api/v1/models/{tableName} 查詢記錄列表
GET /api/v1/models/{tableName}/{id} 取得單筆記錄
POST /api/v1/models/{tableName} 新增記錄
PUT /api/v1/models/{tableName}/{id} 更新記錄
DELETE /api/v1/models/{tableName}/{id} 刪除記錄

Dart 程式碼範例

// ApiClient 封裝了所有 CRUD 操作
final api = ref.watch(apiClientProvider);

// 查詢列表(含篩選、排序、展開外鍵)
final data = await api.getRecords('C_Order',
  filter: "IsSOTrx eq 'Y' and DocStatus eq 'DR'",
  orderBy: 'DateOrdered desc',
  expand: 'C_BPartner_ID,C_DocType_ID',
  top: 20,
  skip: 0,
);

// 取得單筆記錄(含展開欄位)
final record = await api.getRecord('C_Order', 12345,
  expand: 'C_BPartner_ID,C_DocType_ID,M_Warehouse_ID',
);

// 新增記錄
final newRecord = await api.createRecord('C_Order', {
  'C_DocTypeTarget_ID': 132,
  'C_BPartner_ID': 118,
  'DateOrdered': '2026-02-17',
});

// 更新記錄
await api.updateRecord('C_Order', 12345, {
  'Description': 'Updated description',
});

// 刪除記錄
await api.deleteRecord('C_Order', 12345);

OData 查詢

idempiere-rest 支援 OData 風格的查詢參數:

參數 用途 範例
$filter 篩選條件 IsSOTrx eq 'Y' and DocStatus eq 'DR'
$expand 展開外鍵欄位 C_BPartner_ID,C_DocType_ID
$orderby 排序 DateOrdered desc
$top 取得筆數 20
$skip 跳過筆數(分頁) 40

OData 篩選注意事項

這是最常見的 bug 來源。Boolean 欄位和 Char 欄位的篩選語法不同:

欄位類型 正確語法 錯誤語法
Boolean 欄位 IsActive eq true IsActive eq 'Y'(悄悄回傳錯誤資料)
Boolean 欄位 IsSummary eq false IsSummary eq 'N'
Char 欄位 IsSOTrx eq 'Y'
Char 欄位 DocStatus eq 'CO'

Document Framework 透過 StatusFilter.isBoolean 自動處理這個差異。

文件工作流程

iDempiere 文件(如銷售訂單、付款等)遵循三步驟工作流程:

# 1. 建立文件標頭
POST /api/v1/models/C_Order
{
  "C_DocTypeTarget_ID": 132,
  "C_BPartner_ID": 118,
  "DateOrdered": "2026-02-17"
}
# 回傳: { "id": 12345, ... }

# 2. 建立明細行
POST /api/v1/models/C_OrderLine
{
  "C_Order_ID": 12345,
  "M_Product_ID": 456,
  "QtyOrdered": 10
}

# 3. 完成文件(doc-action)
PUT /api/v1/models/C_Order/12345
{
  "doc-action": "CO"
}

常用 doc-action 代碼

代碼 動作 說明
CO Complete 完成文件
VO Void 作廢文件
CL Close 關閉文件
RC Reverse – Correct 沖銷更正
PR Prepare 準備文件

攔截器

API 客戶端使用 Dio 攔截器處理橫切關注點:

攔截器 用途 說明
AuthInterceptor JWT 認證 自動附加 Bearer token,401 時自動刷新
ContextInterceptor 上下文標頭 自動附加 AD_Language 和上下文標頭
RetryInterceptor 網路重試 網路錯誤時自動重試(3 次,指數退避)

認證攔截器流程

HTTP 請求
  │
  ▼
AuthInterceptor 附加 Bearer token
  │
  ▼
伺服器回應
  │
  ├── 200 OK → 正常回傳
  │
  └── 401 Unauthorized
       │
       ▼
  自動刷新 token(PUT /api/v1/auth/tokens)
       │
       ▼
  使用新 token 重新發送原始請求

附件上傳/下載

// 上傳附件
final attachmentApi = AttachmentApi(api.dio);
await attachmentApi.upload(
  tableName: 'C_Order',
  recordId: 12345,
  file: selectedFile,
);

// 下載附件
final bytes = await attachmentApi.download(
  tableName: 'C_Order',
  recordId: 12345,
  attachmentId: 67890,
);

選單樹 API

// 取得角色的選單樹
final menuTree = await api.getMenuTree(treeId);
// 回傳巢狀選單項目,每個項目含有:
// - action: 'W' (Window), 'P' (Process), 'R' (Report), 'X' (Form)
// - targetId: AD_Window_ID / AD_Process_ID / AD_Form_ID
// - name, description

表格對應參考

應用概念 iDempiere 表格 關鍵欄位
銷售訂單 C_Order DocumentNo, DateOrdered, C_BPartner_ID, GrandTotal, DocStatus
採購訂單 C_Order 同上(以 IsSOTrx=’N’ 區分)
發票 C_Invoice DocumentNo, DateInvoiced, C_BPartner_ID, GrandTotal, DocStatus
付款/收款 C_Payment DocumentNo, DateTrx, C_BPartner_ID, PayAmt, DocStatus
產品 M_Product Value, Name, M_Product_Category_ID, C_UOM_ID
商業夥伴 C_BPartner Value, Name, IsCustomer, IsVendor
庫存移動 M_Movement DocumentNo, MovementDate, Description, DocStatus
庫存移動明細 M_MovementLine M_Movement_ID, M_Product_ID, M_Locator_ID, M_LocatorTo_ID, MovementQty
總帳分錄 GL_Journal DocumentNo, DateAcct, Description, TotalDr, TotalCr
資產 A_Asset Value, Name, A_Asset_Group_ID, DateAcct
🌐 English Version

iDempiere REST API Overview

iDempiere Mobile communicates with iDempiere ERP via the idempiere-rest plugin’s OData REST API.

Authentication Flow (Two Steps)

  1. POST /api/v1/auth/tokens with username/password → returns temporary token + client/role list
  2. PUT /api/v1/auth/tokens with clientId/roleId/orgId → returns session JWT token

The token is stored in FlutterSecureStorage and attached to all subsequent requests via AuthInterceptor.

CRUD Operations

All data access uses GET/POST/PUT/DELETE on /api/v1/models/{tableName}.

OData Queries

Supported parameters: $filter, $expand, $orderby, $top, $skip.

OData Filter Gotcha

Boolean columns use unquoted true/false. Char columns use quoted values like 'Y'. Using IsActive eq 'Y' silently returns wrong data.

Document Workflow

  1. POST to create header
  2. POST to create line items
  3. PUT with {"doc-action": "CO"} to complete

Interceptors

Interceptor Purpose
AuthInterceptor JWT Bearer token + auto-refresh on 401
ContextInterceptor AD_Language and context headers
RetryInterceptor Automatic retry on network errors (3 attempts, exponential backoff)

Attachments

Use AttachmentApi for file upload/download operations on any record.

Table Mapping

Concept Table Key Fields
Sales Order C_Order DocumentNo, DateOrdered, C_BPartner_ID, GrandTotal
Invoice C_Invoice DocumentNo, DateInvoiced, C_BPartner_ID, GrandTotal
Payment C_Payment DocumentNo, DateTrx, C_BPartner_ID, PayAmt
Product M_Product Value, Name, M_Product_Category_ID
Business Partner C_BPartner Value, Name, IsCustomer, IsVendor
Movement M_Movement DocumentNo, MovementDate, DocStatus
GL Journal GL_Journal DocumentNo, DateAcct, TotalDr, TotalCr