本指南涵蓋三種新增功能的場景:
- 設定驅動文件 — 標準列表/詳情畫面(~50 行,10 分鐘)
- 自訂功能 — 具有自訂畫面的複雜 UI(完整實作)
- 企業自訂視窗 — 不修改核心程式碼即可新增設定
場景一:設定驅動文件(推薦)
大多數 iDempiere 視窗遵循 列表 → 詳情 → 動作 的模式。這些只需一個設定檔即可新增。
步驟 1:辨識視窗
在 iDempiere 中找到 AD_Window_ID 和表格名稱:
SELECT AD_Window_ID, Name FROM AD_Window WHERE Name LIKE '%Your Window%';
-- 範例: AD_Window_ID=143, Name='Sales Order'
-- 找到主表格:
SELECT t.TableName FROM AD_Tab tab
JOIN AD_Table t ON tab.AD_Table_ID = t.AD_Table_ID
WHERE tab.AD_Window_ID = 143 AND tab.SeqNo = 10;
-- 範例: C_Order
步驟 2:了解資料結構
開啟 iDempiere REST API explorer 或查看表格結構:
- 關鍵欄位 — 列表卡片要顯示什麼(DocumentNo、Name 等)
- 外鍵 — 以
_ID結尾、需要$expand的欄位(C_BPartner_ID 等) - 狀態欄位 — 文件用
DocStatus,主資料用IsActive - 明細行 — 子表格名稱和父外鍵欄位
- 排序欄位 — 預設排序方式
步驟 3:建立設定檔
建立 lib/features/<feature_name>/<feature_name>_config.dart:
範例 A:交易文件(銷售訂單模式)
import 'package:flutter/material.dart';
import '../../core/document_framework/config/document_config.dart';
import '../../core/document_framework/config/field_config.dart';
import '../../core/document_framework/config/filter_config.dart';
final myFeatureConfig = DocumentConfig(
// --- 識別 ---
targetId: 143, // AD_Window_ID
tableName: 'C_Order', // 主表格
title: (l10n) => l10n.myFeatureTitle, // 本地化標題
icon: Icons.shopping_cart,
color: Colors.blue,
category: 'sales', // 儀表板循環頁籤
// --- 列表畫面 ---
baseFilter: "IsSOTrx eq true", // 永久篩選(選用)
defaultOrderBy: 'DateOrdered desc',
expandFields: 'C_BPartner_ID,C_DocType_ID', // 列表的外鍵展開
searchFields: const ['DocumentNo', 'Description'],
// 狀態篩選標籤
statusFilters: StatusFilter.docStatusWithIP,
// 卡片佈局
cardConfig: const CardConfig(
titleField: 'DocumentNo',
subtitleFields: [
DisplayField.lookup('C_BPartner_ID'), // 顯示展開外鍵的 "identifier"
],
trailingField: DisplayField.currency('GrandTotal'),
statusField: 'DocStatus',
dateField: 'DateOrdered',
),
// --- 詳情畫面 ---
detailConfig: const DetailConfig(
detailExpand: 'C_BPartner_ID,C_DocType_ID,M_Warehouse_ID',
headerFields: [
DetailField.lookup('C_BPartner_ID', label: 'Business Partner'),
DetailField.date('DateOrdered', label: 'Date Ordered'),
DetailField.lookup('C_DocType_ID', label: 'Doc Type'),
DetailField.currency('GrandTotal', label: 'Grand Total'),
],
// 明細行(選用)
linesTable: 'C_OrderLine',
linesParentField: 'C_Order_ID',
linesExpand: 'M_Product_ID',
linesFields: [
LineField.lookup('M_Product_ID'),
LineField.number('QtyOrdered', label: 'Qty'),
LineField.currency('LineNetAmt', label: 'Amount'),
],
// 彙總合計(選用)
summaryFields: [
SummaryField.currency('GrandTotal', label: 'Grand Total'),
],
),
// --- 文件動作 ---
actions: const [DocAction.complete, DocAction.void_],
actionCondition: (record) => record['DocStatus'] == 'DR',
// --- 路由 ---
listRoute: '/dashboard/my-feature',
detailRoute: '/dashboard/my-feature/:id',
);
範例 B:主資料(產品模式)
final myMasterConfig = DocumentConfig(
targetId: 140,
tableName: 'M_Product',
title: (l10n) => l10n.productTitle,
icon: Icons.inventory_2,
color: Colors.blue,
category: 'master',
baseFilter: 'IsSummary eq false', // Boolean 篩選(不加引號!)
defaultOrderBy: 'Value',
searchFields: const ['Value', 'Name', 'Description'],
// 使用 IsActive 篩選(而非 DocStatus)
statusFilterField: 'IsActive',
statusFilters: StatusFilter.isActiveFilters,
cardConfig: const CardConfig(
titleField: 'Value',
subtitleFields: [DisplayField.raw('Name')],
// 無 statusField, trailingField, dateField
),
detailConfig: const DetailConfig(
detailExpand: 'M_Product_Category_ID,C_UOM_ID',
headerFields: [
DetailField('Value', label: 'Value'),
DetailField('Name', label: 'Name'),
DetailField.lookup('M_Product_Category_ID', label: 'Category'),
DetailField.lookup('C_UOM_ID', label: 'UOM'),
],
// 無明細行、無彙總、無動作
),
listRoute: '/dashboard/product',
detailRoute: '/dashboard/product/:id',
);
範例 C:含額外篩選的文件(付款模式)
final paymentConfig = DocumentConfig(
targetId: 195,
tableName: 'C_Payment',
title: (l10n) => l10n.paymentTitle,
icon: Icons.payments,
color: Colors.green,
category: 'financing',
defaultOrderBy: 'DateTrx desc',
expandFields: 'C_BPartner_ID,C_DocType_ID,C_Currency_ID',
searchFields: const ['DocumentNo', 'Description'],
statusFilters: StatusFilter.docStatusFilters,
// 額外篩選:收款 vs 付款切換
extraFilters: const [
ExtraFilter(
field: 'IsReceipt',
label: 'Type',
options: [
FilterOption("'Y'", 'Receipt'),
FilterOption("'N'", 'Payment'),
],
),
],
cardConfig: const CardConfig(
titleField: 'DocumentNo',
subtitleFields: [DisplayField.lookup('C_BPartner_ID')],
trailingField: DisplayField.currency('PayAmt'),
statusField: 'DocStatus',
dateField: 'DateTrx',
),
// ... detailConfig, actions, routes
);
步驟 4:新增本地化 Key
新增到 lib/l10n/app_zh_TW.arb(範本):
"myFeatureTitle": "我的功能"
新增到 lib/l10n/app_en.arb:
"myFeatureTitle": "My Feature"
執行:
flutter gen-l10n
步驟 5:註冊設定
在 lib/core/document_framework/config/standard_configs.dart 中:
import '../../../features/my_feature/my_feature_config.dart';
void registerStandardConfigs() {
DocumentRegistry.registerAll([
// ... 現有的 configs ...
myFeatureConfig, // 在此新增
]);
}
完成!
框架會自動:
- 透過
generateDocumentRoutes()建立列表和詳情路由 - 透過
DocumentRegistry.find(action, targetId)對應選單項目 - 渲染含搜尋、篩選標籤、分頁的列表畫面
- 渲染含標頭欄位、明細行、文件動作的詳情畫面
- 自動處理 OData 篩選建構、Boolean vs String 欄位類型
驗證
flutter analyze # 無錯誤
flutter run # 在裝置上測試 — 找到選單項目
欄位類型參考
DisplayField 建構子
| 建構子 | 用途 | 渲染結果 |
|---|---|---|
DisplayField.raw('Name') |
純文字 | record['Name'].toString() |
DisplayField.lookup('C_BPartner_ID') |
外鍵 | record['C_BPartner_ID']['identifier'] |
DisplayField.currency('GrandTotal') |
金額 | "1234.56"(2 位小數) |
DisplayField.date('DateOrdered') |
日期 | "2026-02-15"(去除時間) |
DisplayField.boolean('IsActive') |
是/否 | "Yes" 或 "No" |
DisplayField.custom('Field', formatter: ...) |
自訂邏輯 | 自訂函式的回傳值 |
欄位類別階層
DisplayField ← 基礎類別(用於 CardConfig.subtitleFields)
├── DetailField ← 用於 DetailConfig.headerFields
├── LineField ← 用於 DetailConfig.linesFields
└── SummaryField ← 用於 DetailConfig.summaryFields
所有子類別共用相同的建構子(.raw()、.lookup()、.currency()、.date())。請根據上下文使用正確的類別。
CardConfig 欄位
CardConfig(
titleField: 'DocumentNo', // 主標題(從記錄取得的原始字串)
subtitleFields: [...], // 標題下方(DisplayField 列表)
trailingField: DisplayField..., // 右側(通常為金額)
statusField: 'DocStatus', // 狀態標籤顏色 + 文字
dateField: 'DateOrdered', // 狀態列中顯示的日期
)
篩選預設值
| 預設值 | 適用場景 | 選項 |
|---|---|---|
StatusFilter.docStatusFilters |
標準文件 | 全部、草稿、已完成、已作廢 |
StatusFilter.docStatusWithIP |
含進行中的文件 | 全部、草稿、進行中、已完成、已作廢 |
StatusFilter.isActiveFilters |
主資料 | 全部、啟用中、已停用 |
自訂篩選
extraFilters: const [
ExtraFilter(
field: 'IsSOTrx', // OData 欄位名稱
label: 'Transaction Type',
options: [
FilterOption("'Y'", 'Sales'), // 值必須是有效的 OData
FilterOption("'N'", 'Purchase'),
],
),
],
文件動作
actions: const [DocAction.complete, DocAction.void_],
actionCondition: (record) => record['DocStatus'] == 'DR',
| 動作 | 代碼 | 顯示條件 |
|---|---|---|
DocAction.complete |
CO | actionCondition 回傳 true |
DocAction.void_ |
VO | actionCondition 回傳 true |
DocAction.close |
CL | actionCondition 回傳 true |
DocAction.reverse |
RC | actionCondition 回傳 true |
DocAction.prepare |
PR | actionCondition 回傳 true |
場景二:自訂功能(複雜 UI)
當功能需要超越列表/詳情的互動 — 表單、日曆、儀表板、多步驟工作流程 — 請建立完整的自訂功能。
目錄結構
lib/features/my_feature/
├── data/
│ └── my_feature_repository.dart # API 呼叫
├── domain/
│ └── my_feature_notifier.dart # 狀態管理
├── presentation/
│ ├── my_feature_screen.dart # 主畫面
│ ├── my_feature_form_screen.dart # 表單畫面(選用)
│ └── widgets/ # 功能專屬小工具
└── my_feature_routes.dart # GoRoute 定義
Repository 模式
final myFeatureRepositoryProvider = Provider<MyFeatureRepository>((ref) {
return MyFeatureRepository(ref.watch(apiClientProvider));
});
class MyFeatureRepository {
final ApiClient _api;
MyFeatureRepository(this._api);
Future<List<Map<String, dynamic>>> getRecords() async {
final data = await _api.getRecords('My_Table',
filter: 'IsActive eq true',
orderBy: 'Name',
expand: 'C_BPartner_ID',
);
return (data['records'] as List?)?.cast<Map<String, dynamic>>() ?? [];
}
}
註冊路由
在 lib/core/router/app_router.dart 中新增:
import '../../features/my_feature/my_feature_routes.dart';
// 在 dashboard ShellRoute 的 routes 列表中:
...myFeatureRoutes,
註冊到 Enhanced Module Registry(如果對應到 iDempiere 選單)
在 lib/core/config/enhanced_module_registry.dart 中:
// 對於 AD_Window,使用 action 'W':
'W:YOUR_WINDOW_ID': const EnhancedModule(
action: 'W',
targetId: YOUR_WINDOW_ID,
route: '/dashboard/my-feature',
icon: Icons.your_icon,
color: Colors.yourColor,
category: 'your_cycle',
),
// 對於 AD_Form,使用 action 'X':
'X:YOUR_FORM_ID': const EnhancedModule(
action: 'X',
targetId: YOUR_FORM_ID,
route: '/dashboard/my-feature',
...
),
場景三:企業自訂視窗
企業部署可以在不修改核心程式碼的情況下新增自訂 DocumentConfig。
建立自訂設定目錄
lib/custom/
└── custom_configs.dart
註冊自訂設定
// lib/custom/custom_configs.dart
import '../core/document_framework/config/document_config.dart';
import '../core/document_framework/config/document_registry.dart';
import '../core/document_framework/config/field_config.dart';
import '../core/document_framework/config/filter_config.dart';
import 'package:flutter/material.dart';
/// 註冊企業專屬的增強畫面。
/// 在 main.dart 的 registerStandardConfigs() 之後呼叫。
void registerCustomConfigs() {
DocumentRegistry.registerAll([
// 在此放入您的自訂 iDempiere 視窗
DocumentConfig(
targetId: 1000001, // 您的自訂 AD_Window_ID
tableName: 'XX_MyTable',
title: (l10n) => 'My Custom Window',
icon: Icons.extension,
color: Colors.purple,
category: 'custom',
// ... card, detail, filters, routes
cardConfig: const CardConfig(titleField: 'Name'),
listRoute: '/dashboard/xx-my-table',
detailRoute: '/dashboard/xx-my-table/:id',
),
]);
}
整合到 main.dart
import 'custom/custom_configs.dart';
void main() async {
// ...
registerStandardConfigs();
registerCustomConfigs(); // 在標準設定之後新增
// ...
}
重要事項
- 企業自訂設定在標準設定之後註冊
- 無需修改核心程式碼 — 只需在
lib/custom/下新增檔案 - 權限仍由伺服器驅動(使用者必須有該視窗的 AD_Window_Access)
- 建置並發布包含自訂設定的專屬 App 版本
提交前檢查清單
| 項目 | 說明 |
|---|---|
| 設定檔已建立 | lib/features/<name>/<name>_config.dart |
| 本地化 Key 已新增 | app_zh_TW.arb 和 app_en.arb |
| 設定已註冊 | standard_configs.dart(或 custom_configs.dart) |
| 本地化已產生 | flutter gen-l10n 已執行 |
| 靜態分析通過 | flutter analyze 無錯誤 |
| 裝置測試通過 | 列表載入、詳情開啟、動作運作正常 |
多人協作指南
本節說明如何在此專案中新增功能模組,以及多人協作時應遵守的規範。
專案架構
lib/
├── core/ ← 共用基礎設施(不隨功能增減而變動)
│ ├── api/ ← ApiClient、TokenStorage
│ ├── config/ ← AppConfig、DashboardModule 註冊
│ ├── models/ ← 跨功能共用的 Model(≥2 個 feature 使用)
│ ├── router/ ← app_router.dart(組裝各 feature 的 routes)
│ ├── services/ ← WarehouseService、MovementService
│ ├── theme/ ← ThemeNotifier
│ └── widgets/ ← RetryErrorWidget 等通用元件
├── features/ ← 每個功能模組獨立一個目錄
│ ├── inbound/
│ ├── outbound/
│ ├── inventory/
│ ├── approval/
│ ├── scanner/
│ └── dashboard/
└── main.dart
新增一個功能模組(Step by Step)
以「盤點功能 stocktake」為例:
Step 1:建立目錄結構
lib/features/stocktake/
├── data/
│ └── stocktake_repository.dart ← API 呼叫
├── domain/
│ ├── stocktake_notifier.dart ← 業務邏輯 + 狀態管理
│ └── models/ ← 此功能專用的 Model
│ └── stocktake_item.dart
├── presentation/
│ └── stocktake_screen.dart ← UI 畫面
└── stocktake_routes.dart ← 路由定義
Step 2:定義路由
// lib/features/stocktake/stocktake_routes.dart
import 'package:go_router/go_router.dart';
import 'presentation/stocktake_screen.dart';
final stocktakeRoutes = [
GoRoute(path: 'stocktake', builder: (_, __) => const StocktakeScreen()),
];
Step 3:註冊到 Router(1 行)
// lib/core/router/app_router.dart — 在 routes 中加一行
import '../../features/stocktake/stocktake_routes.dart';
routes: [
...inboundRoutes,
...outboundRoutes,
...stocktakeRoutes, // ← 新增這行
],
Step 4:註冊到儀表板
儀表板有兩種模組來源,依功能性質選擇:
A. ERP 增強模組(對應 iDempiere AD_Menu 項目)→ 加到 enhanced_module_registry.dart
// lib/core/config/enhanced_module_registry.dart — 加入 action:targetId entry
'W:12345': EnhancedModule(
action: 'W', targetId: 12345, // AD_Window_ID
route: '/dashboard/stocktake',
icon: Icons.fact_check, color: Colors.teal,
),
當使用者登入後,AD_Menu 中匹配此 targetId 的項目會自動提升為儀表板磚塊。
B. 非 ERP 功能模組(App 獨立功能,不對應 AD_Menu)→ 加到 app_modules.dart
// lib/core/config/app_modules.dart — 在 appModules 清單中加入
DashboardModule(
titleBuilder: (l) => l.stocktakeTitle,
subtitleBuilder: (l) => l.stocktakeSub,
icon: Icons.fact_check,
color: Colors.teal,
route: '/dashboard/stocktake',
category: 'inventory',
),
Step 5:使用共用服務
// data/stocktake_repository.dart — 直接注入 WarehouseService
import '../../../core/services/warehouse_service.dart';
final stocktakeRepositoryProvider = Provider<StocktakeRepository>((ref) {
return StocktakeRepository(ref.watch(warehouseServiceProvider));
});
Step 6:撰寫測試
test/features/stocktake/
└── stocktake_notifier_test.dart
分工規範
目錄所有權
| 目錄 | 負責人 | 規則 |
|---|---|---|
lib/core/ |
架構負責人 | 修改需 Code Review |
lib/features/{your_feature}/ |
功能負責人 | 可自主開發 |
test/ |
對應功能負責人 | PR 必須附測試 |
Git 分支策略
main ← 穩定版本,僅透過 PR 合併
├── feature/stocktake ← 功能開發分支
├── feature/barcode-print ← 功能開發分支
└── fix/approval-creator ← Bug 修復分支
PR Checklist
flutter analyze0 errorsflutter test全部通過- 新功能有對應的 notifier 測試
- 只修改自己功能目錄 + 必要的 router/dashboard 註冊
- 不直接修改其他功能的程式碼
共用服務一覽
| 服務 | 路徑 | 用途 |
|---|---|---|
WarehouseService |
core/services/warehouse_service.dart |
庫存查詢、儲位查詢 |
MovementService |
core/services/movement_service.dart |
建立並完成 M_Movement |
ApiClient |
core/api/api_client.dart |
iDempiere REST API 呼叫 |
TokenStorage |
core/api/token_storage.dart |
Token / 生物辨識憑證存取 |
CacheManager |
core/cache/cache_manager.dart |
離線快取 |
共用 Model 規則
放在 core/models/ |
放在 features/{name}/domain/models/ |
|---|---|
| ≥2 個 feature 使用 | 只有 1 個 feature 使用 |
StorageOnHand、Locator、Product |
WfActivity(僅 approval 使用) |
測試結構
test/
├── core/ ← 共用模組測試
│ ├── api/token_storage_test.dart
│ └── theme/theme_notifier_test.dart
├── features/ ← 功能模組測試(鏡像 lib/features/)
│ └── stocktake/
│ └── stocktake_notifier_test.dart
└── helpers/
└── test_helpers.dart ← 共用 mock 和 factory
常見問題
| 問題 | 解答 |
|---|---|
| 需要查詢庫存或儲位? | 注入 warehouseServiceProvider,呼叫 getStock() 或 getLocators()。不需要自己寫 OData filter。 |
| 需要建立移動單(M_Movement)? | 注入 movementServiceProvider,呼叫 createAndComplete()。 |
| Model 要放哪裡? | 只有你的功能使用 → features/{name}/domain/models/。多個功能共用 → 提 PR 到 core/models/。 |
| 想在儀表板加一張卡片? | 若功能對應 iDempiere 視窗/流程,在 enhanced_module_registry.dart 加 EnhancedModule entry。若為 App 獨立功能,在 app_modules.dart 加 DashboardModule entry。 |
國際化(i18n)指南
重要:新增任何使用者可見的文字時,必須使用本地化系統。除了開發日誌之外,禁止在 Dart 檔案中硬編碼字串。
支援語言
| 語言 | ARB 檔案路徑 | 說明 |
|---|---|---|
| English | lib/l10n/app_en.arb |
基礎範本 |
| 繁體中文 | lib/l10n/app_zh_TW.arb |
|
| 日本語 | lib/l10n/app_ja.arb |
新增字串步驟
以新增「查看全部」按鈕為例:
Step 1:在所有 ARB 檔案中新增 Key
app_en.arb:
"viewAll": "View All"
app_zh_TW.arb:
"viewAll": "查看全部"
app_ja.arb:
"viewAll": "すべて見る"
⚠️ 若遺漏任一檔案的 key,建置可能失敗或顯示 fallback 警告。
Step 2:重新產生本地化類別
flutter gen-l10n
VS Code 通常會在儲存時自動執行,但手動執行可確保正確性。
Step 3:在程式碼中使用
import 'package:tw_idempiere_flutter/l10n/app_localizations.dart';
// ...
Text(S.of(context).viewAll)
Agent Checklist(AI 編碼助手檢查清單)
| 檢查項目 | 說明 |
|---|---|
| 辨識使用者可見文字 | 是否有需要本地化的字串? |
新增到 app_en.arb |
英文翻譯已加入 |
新增到 app_zh_TW.arb |
繁體中文翻譯已加入 |
新增到 app_ja.arb |
日文翻譯已加入 |
執行 flutter gen-l10n |
本地化類別已重新產生 |
使用 S.of(context).keyName |
不使用字串常數 |
🌐 English Version
Three Scenarios for Adding Features
- Config-driven document — standard list/detail screen (~50 lines, 10 minutes)
- Custom feature — complex UI with its own screens (full implementation)
- Enterprise custom window — adding a config without modifying core code
Scenario 1: Config-Driven Document (Recommended)
Most iDempiere windows follow a list → detail → action pattern. These can be added with a single config file.
Steps
- Find the
AD_Window_IDand table name in iDempiere - Understand the data structure (key columns, FKs, status field, line items)
- Create
lib/features/<name>/<name>_config.dartwith aDocumentConfig - Add localization keys to ARB files and run
flutter gen-l10n - Register in
standard_configs.dart
The framework automatically creates routes, maps menu items, renders list/detail screens with search, filters, pagination, and doc actions.
Field Types
| Constructor | Use For | Renders As |
|---|---|---|
DisplayField.raw() |
Plain text | record[field].toString() |
DisplayField.lookup() |
Foreign key | record[field]['identifier'] |
DisplayField.currency() |
Money | 2-decimal formatted number |
DisplayField.date() |
Date | Date without time |
DisplayField.boolean() |
Yes/No | “Yes” or “No” |
Filter Presets
StatusFilter.docStatusFilters— All, Draft, Complete, VoidedStatusFilter.docStatusWithIP— All, Draft, In Progress, Complete, VoidedStatusFilter.isActiveFilters— All, Active, Inactive
Scenario 2: Custom Feature (Complex UI)
Use the full data/domain/presentation/ structure when you need forms, calendars, dashboards, or multi-step workflows.
- Create directory under
lib/features/my_feature/ - Implement repository → notifier → screens → routes
- Register routes in
app_router.dart - Register in
enhanced_module_registry.dartif mapped to iDempiere menu
Scenario 3: Enterprise Custom Window
Add custom configs in lib/custom/custom_configs.dart without modifying core code. Register after registerStandardConfigs() in main.dart.
Checklist
- Config file created
- Localization keys added
- Config registered
flutter gen-l10nrunflutter analyzepasses- Tested on device
🇯🇵 日本語版
機能追加の3つのシナリオ
- 設定駆動ドキュメント — 標準的なリスト/詳細画面(約50行、10分)
- カスタム機能 — 独自の画面を持つ複雑な UI(フル実装)
- エンタープライズカスタムウィンドウ — コアコードを変更せずに設定を追加
シナリオ 1:設定駆動ドキュメント(推奨)
ほとんどの iDempiere ウィンドウは、リスト → 詳細 → アクションのパターンに従います。これらは1つの設定ファイルだけで追加できます。
手順
- iDempiere で
AD_Window_IDとテーブル名を確認 - データ構造を把握(キーカラム、外部キー、ステータスフィールド、明細行)
lib/features/<name>/<name>_config.dartにDocumentConfigを作成- ARB ファイルにローカライゼーションキーを追加し、
flutter gen-l10nを実行 standard_configs.dartに登録
フレームワークがルートの作成、メニュー項目のマッピング、検索・フィルター・ページネーション・ドキュメントアクション付きのリスト/詳細画面のレンダリングを自動的に行います。
フィールドタイプ
| コンストラクタ | 用途 | 表示形式 |
|---|---|---|
DisplayField.raw() |
プレーンテキスト | record[field].toString() |
DisplayField.lookup() |
外部キー | record[field]['identifier'] |
DisplayField.currency() |
金額 | 小数点以下2桁の数値 |
DisplayField.date() |
日付 | 時刻を除いた日付 |
DisplayField.boolean() |
はい/いいえ | "Yes" または "No" |
フィルタープリセット
StatusFilter.docStatusFilters— すべて、下書き、完了、無効StatusFilter.docStatusWithIP— すべて、下書き、進行中、完了、無効StatusFilter.isActiveFilters— すべて、有効、無効
シナリオ 2:カスタム機能(複雑な UI)
フォーム、カレンダー、ダッシュボード、マルチステップワークフローが必要な場合は、完全な data/domain/presentation/ 構造を使用してください。
lib/features/my_feature/配下にディレクトリを作成- リポジトリ → ノティファイアー → 画面 → ルートを実装
app_router.dartにルートを登録- iDempiere メニューにマッピングする場合は
enhanced_module_registry.dartに登録
シナリオ 3:エンタープライズカスタムウィンドウ
コアコードを変更せずに、lib/custom/custom_configs.dart にカスタム設定を追加します。main.dart の registerStandardConfigs() の後に登録してください。
チェックリスト
- 設定ファイルの作成完了
- ローカライゼーションキーの追加完了
- 設定の登録完了
flutter gen-l10nの実行完了flutter analyzeのパス確認- デバイスでのテスト完了