新增功能指南

本指南涵蓋三種新增功能的場景:

  1. 設定驅動文件 — 標準列表/詳情畫面(~50 行,10 分鐘)
  2. 自訂功能 — 具有自訂畫面的複雜 UI(完整實作)
  3. 企業自訂視窗 — 不修改核心程式碼即可新增設定

場景一:設定驅動文件(推薦)

大多數 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.arbapp_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 analyze 0 errors
  • flutter 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 使用
StorageOnHandLocatorProduct 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.dartEnhancedModule entry。若為 App 獨立功能,在 app_modules.dartDashboardModule 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

  1. Config-driven document — standard list/detail screen (~50 lines, 10 minutes)
  2. Custom feature — complex UI with its own screens (full implementation)
  3. 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

  1. Find the AD_Window_ID and table name in iDempiere
  2. Understand the data structure (key columns, FKs, status field, line items)
  3. Create lib/features/<name>/<name>_config.dart with a DocumentConfig
  4. Add localization keys to ARB files and run flutter gen-l10n
  5. 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, Voided
  • StatusFilter.docStatusWithIP — All, Draft, In Progress, Complete, Voided
  • StatusFilter.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.

  1. Create directory under lib/features/my_feature/
  2. Implement repository → notifier → screens → routes
  3. Register routes in app_router.dart
  4. Register in enhanced_module_registry.dart if 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-l10n run
  • flutter analyze passes
  • Tested on device
🇯🇵 日本語版

機能追加の3つのシナリオ

  1. 設定駆動ドキュメント — 標準的なリスト/詳細画面(約50行、10分)
  2. カスタム機能 — 独自の画面を持つ複雑な UI(フル実装)
  3. エンタープライズカスタムウィンドウ — コアコードを変更せずに設定を追加

シナリオ 1:設定駆動ドキュメント(推奨)

ほとんどの iDempiere ウィンドウは、リスト → 詳細 → アクションのパターンに従います。これらは1つの設定ファイルだけで追加できます。

手順

  1. iDempiere で AD_Window_ID とテーブル名を確認
  2. データ構造を把握(キーカラム、外部キー、ステータスフィールド、明細行)
  3. lib/features/<name>/<name>_config.dartDocumentConfig を作成
  4. ARB ファイルにローカライゼーションキーを追加し、flutter gen-l10n を実行
  5. 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/ 構造を使用してください。

  1. lib/features/my_feature/ 配下にディレクトリを作成
  2. リポジトリ → ノティファイアー → 画面 → ルートを実装
  3. app_router.dart にルートを登録
  4. iDempiere メニューにマッピングする場合は enhanced_module_registry.dart に登録

シナリオ 3:エンタープライズカスタムウィンドウ

コアコードを変更せずに、lib/custom/custom_configs.dart にカスタム設定を追加します。main.dartregisterStandardConfigs() の後に登録してください。

チェックリスト

  • 設定ファイルの作成完了
  • ローカライゼーションキーの追加完了
  • 設定の登録完了
  • flutter gen-l10n の実行完了
  • flutter analyze のパス確認
  • デバイスでのテスト完了

按 Enter 搜尋,ESC 關閉