iDempiere 割引スキーマの使い方|在庫管理 操作マニュアル・技術仕様
📖 在庫管理の全体像: 在庫管理の業務フロー全体図 も合わせてご覧ください。
割引スキーマは、取引割引率(Trade Discount)の計算方法を定義するマスタです。単純な定率割引のほか、数量や金額の分岐点(Break)ごとに割引率を変える段階割引を設定でき、価格表や取引先マスタから参照されて伝票明細の価格計算に使われます。
📌 ポイント: 割引タイプは「定率(Flat Percent)」と「分岐(Breaks)」が実装されており、「数式(Formula)」「価格表(Pricelist)」は iDempiere 13 のコードでも未サポート(割引 0 を返却)です。
割引スキーマでできること
Section titled “割引スキーマでできること”- 定率割引(Flat Percent)の設定
- 数量ベースまたは金額ベースの段階割引(分岐条件)の設定
- 品目単位・品目カテゴリ単位での分岐条件の指定
- 取引先マスタに設定された割引率の優先適用
- 分岐ごとの固定価格(Fixed Price)の指定
- 累積レベル(Accumulation Level)による集計単位の指定
- 「再計算」ボタンによる分岐のシーケンス番号の振り直し
- 適用開始日(Valid from)による有効期間の管理
割引スキーマは 2 タブ構成です。
| タブ名 | テーブル | 項目数 | 役割 |
|---|---|---|---|
| 割引スキーマ | M_DiscountSchema | 12項目 | 割引タイプ・定率割引・累積レベル |
| 割引条件設定 | M_DiscountSchemaBreak | 11項目 | 分岐値ごとの割引率・固定価格 |
💡 ヒント: 割引タイプが「定率」の場合、割引条件設定タブは使用しません。段階割引を使う場合のみ分岐を登録してください。
基本操作手順
Section titled “基本操作手順”graph TD
A["🚀 メニューから開く<br/>在庫管理 > 品目マスタ管理 > 割引スキーマ"] --> B["➕ 新規で名称を入力"]
B --> C{割引タイプ}
C -->|定率割引| D["💯 割引率を入力<br/>(FlatDiscount)"]
C -->|段階割引| E["📊 数量ベースか金額ベースかを選択"]
D --> F{取引先の割引率を<br/>優先する?}
E --> G["📋 割引条件設定タブで<br/>分岐値・分岐割引率を登録"]
G --> H["🎯 必要なら品目 or 品目カテゴリで<br/>適用範囲を絞る"]
H --> I["🔁 再計算ボタンで<br/>シーケンスNoを振り直し"]
F -->|する| J["☑ 取引先割引率優先にチェック"]
F -->|しない| K["💾 保存"]
I --> K
J --> K
K --> L["🔗 価格表・取引先マスタから参照<br/>→ 伝票明細の割引に反映"]
アクセス方法(メニューパス)
Section titled “アクセス方法(メニューパス)”メニューから「在庫管理 > 品目マスタ管理 > 割引スキーマ」を開きます。
新規登録(定率割引)
Section titled “新規登録(定率割引)”- ツールバーの「新規」ボタンをクリック
- 必須項目を入力:
- 名称: 割引スキーマ名(60文字以内)
- 割引タイプ:
定率(Flat Percent)を選択 - 数量ベース: 定率の場合は判定に使用されません(既定値 Y)
- 適用開始日: 空欄で保存すると当日日付が自動セットされます
- 割引率 に定率の割引パーセントを入力
- 取引先マスタ側の割引率を優先する場合は「取引先割引率優先」にチェック
- 「保存」をクリック
段階割引(分岐)の設定
Section titled “段階割引(分岐)の設定”- 割引スキーマタブで 割引タイプ を分岐(Breaks)系に設定し、判定軸を選択:
- 数量ベース にチェック → 数量で分岐
- チェックを外す → 金額(単価 × 数量)で分岐
- 「割引条件設定」タブに移動
- 各分岐を登録:
- 分岐値: この値以上で当該行が適用される下限値
- 分岐割引率: 適用する割引率(%)
- 品目 または 品目カテゴリ: 適用範囲を絞る場合に指定
- Fixed Price: 固定価格を適用する場合に入力(既定値 0)
- 「保存」をクリック
- 割引スキーマタブに戻り「再計算」ボタンでシーケンス番号を 10 刻みに整列
⚠️ 注意: 分岐行で「品目」と「品目カテゴリ」の両方を指定して保存すると、
MDiscountSchemaBreak.beforeSave()が品目カテゴリを自動的に 0(クリア) します。品目指定が優先される仕様です。
項目リファレンス
Section titled “項目リファレンス”割引スキーマタブ
Section titled “割引スキーマタブ”| 項目名 | 必須 | 型 | 説明 |
|---|---|---|---|
| 名称 | 必須 | 文字列(60) | 割引スキーマ名 |
| 説明 | - | 文字列(255) | 補足説明 |
| 割引タイプ | 必須 | リスト(1) | 定率/分岐/数式/価格表 |
| 割引率 | - | 数値 | 定率割引のパーセント |
| 取引先割引率優先 | 必須 | チェック | 取引先マスタの割引率を使用 |
| 数量ベース | 必須 | チェック | 分岐判定を数量で行う(既定値 Y) |
| 累積レベル | - | リスト(1) | 集計単位(既定値 L) |
| スクリプト | - | テキスト(2000) | 動的スクリプト(数式タイプ用) |
| 適用開始日 | 必須 | 日付 | 未入力時は保存日が自動セット |
| 再計算 | - | ボタン | 分岐のシーケンスNoを振り直す |
| 有効 | - | チェック | レコードが有効か(既定値 Y) |
割引条件設定タブ
Section titled “割引条件設定タブ”| 項目名 | 必須 | 型 | 説明 |
|---|---|---|---|
| 割引スキーマ | 必須 | 選択 | 親レコードへの参照 |
| シーケンスNo | 必須 | 整数 | 評価順(既定値は最大値+10) |
| 分岐値 | 必須 | 数値 | 適用の下限値(数量または金額) |
| 分岐割引率 | 必須 | 数値 | 当該分岐の割引率(%) |
| 品目 | - | 検索 | 適用対象の品目 |
| 品目カテゴリ | - | 選択 | 適用対象の品目カテゴリ |
| 取引先割引率優先 | 必須 | チェック | 取引先の割引率を使用(既定値 N) |
| Fixed Price | - | 金額 | 固定価格(既定値 0) |
| 有効 | 必須 | チェック | レコードが有効か(既定値 Y) |
よくある質問(FAQ)
Section titled “よくある質問(FAQ)”Q. 割引タイプに「数式」や「価格表」を選ぶとどうなりますか?
Section titled “Q. 割引タイプに「数式」や「価格表」を選ぶとどうなりますか?”割引率 0 が返されます。MDiscountSchema.calculateDiscount() は DISCOUNTTYPE_Formula および DISCOUNTTYPE_Pricelist に対して「Not supported (yet)」とログ出力し、Env.ZERO を返す実装です(iDempiere 13 時点)。実運用では「定率」または分岐(Breaks)を使用してください。
Q. 分岐はどの順番で評価されますか?
Section titled “Q. 分岐はどの順番で評価されますか?”「割引条件設定」タブのシーケンスNo 順(昇順)です。getBreaks() で取得した配列を順に走査し、MDiscountSchemaBreak.applies() が真になる行を採用します。評価順を整理したい場合は割引スキーマタブの「再計算」ボタンを実行すると、reSeq() が 10 / 20 / 30 … と 10 刻みに振り直します。
Q. 分岐行の適用条件はどのように判定されますか?
Section titled “Q. 分岐行の適用条件はどのように判定されますか?”MDiscountSchemaBreak.applies(Value, M_Product_ID, M_Product_Category_ID) が以下の順で判定します。
- 行が無効(
IsActive=N)なら不適用 - 判定値が分岐値(
BreakValue)未満なら不適用 - 品目・品目カテゴリがどちらも未指定なら適用(全品目対象)
- 行の品目が対象品目と一致すれば適用
- 対象の品目カテゴリが指定されていれば、行の品目カテゴリと一致するかを判定
- それ以外は
MProductCategory.isCategory()で品目のカテゴリを照会して判定
Q. 「数量ベース」のチェックを外すと何が変わりますか?
Section titled “Q. 「数量ベース」のチェックを外すと何が変わりますか?”分岐の判定値が数量から金額(Price × Qty)に変わります。「10 個以上で 5% 引き」ではなく「10 万円以上で 5% 引き」といった金額帯での段階割引を設定できます。
Q. 「取引先割引率優先」にチェックすると何が起きますか?
Section titled “Q. 「取引先割引率優先」にチェックすると何が起きますか?”割引タイプが定率の場合、calculateDiscount() はスキーマの FlatDiscount ではなく、引数で渡された取引先の割引率(BPartnerFlatDiscount)を返します。取引先マスタ側で個別の割引率を管理している場合に使用します。
Q. 適用開始日を入力し忘れました
Section titled “Q. 適用開始日を入力し忘れました”保存時に自動補完されます。MDiscountSchema.beforeSave() が ValidFrom の未設定を検出すると TimeUtil.getDay(null)(当日)をセットします。
🛠 技術仕様(開発者向け)
割引スキーマはマスタデータ型のウィンドウ(AD_Window_ID: 233)で、M_DiscountSchema テーブル(アクセスレベル 3)と子テーブル M_DiscountSchemaBreak で構成されます。MDiscountSchema クラス(488行)が価格・割引の計算エンジンを、MDiscountSchemaBreak(183行)が分岐の適用判定を担います。MDiscountSchema は markImmutable() を実装し、キャッシュ経由の不変オブジェクト参照に対応しています。Document 型ではありません。
なお、価格表の割引に使われる M_DiscountSchemaLine(MDiscountSchemaLine クラス)も同じ親テーブルに紐づきますが、このウィンドウのタブとしては公開されていません。
アーキテクチャ概要
Section titled “アーキテクチャ概要”classDiagram
class MDiscountSchema {
+get(M_DiscountSchema_ID) MDiscountSchema
+getBreaks(reload) MDiscountSchemaBreak[]
+getLines(reload) MDiscountSchemaLine[]
+calculatePrice(Qty, Price, ...) BigDecimal
+calculateDiscount(Qty, Price, ...) BigDecimal
+calculateDiscountedPrice(price, discount) BigDecimal
+beforeSave(newRecord) boolean
+reSeq() int
}
class MDiscountSchemaBreak {
+applies(Value, M_Product_ID, M_Product_Category_ID) boolean
+beforeSave(newRecord) boolean
}
class X_M_DiscountSchema {
<<generated>>
}
class PO {
<<abstract>>
}
MDiscountSchema --|> X_M_DiscountSchema
X_M_DiscountSchema --|> PO
MDiscountSchema --> MDiscountSchemaBreak : has many
MDiscountSchema --> MDiscountSchemaLine : has many
MDiscountSchemaBreak --> MProductCategory : category check
パッケージ: org.compiere.model
ソースファイル: org.adempiere.base/src/org/compiere/model/MDiscountSchema.java、MDiscountSchemaBreak.java
関連DBテーブル
Section titled “関連DBテーブル”M_DiscountSchema(割引スキーマ)
Section titled “M_DiscountSchema(割引スキーマ)”| カラム名 | 型 | 必須 | 説明 | 備考 |
|---|---|---|---|---|
| M_DiscountSchema_ID | ID | PK | 割引スキーマID | 主キー |
| AD_Client_ID | Table Direct | Y | クライアント | 既定値 @#AD_Client_ID@ |
| AD_Org_ID | Table Direct | Y | 組織 | 既定値 @#AD_Org_ID@ |
| Name | String(60) | Y | 名称 | 識別子カラム |
| Description | String(255) | N | 説明 | |
| DiscountType | List(1) | Y | 割引タイプ | 定率/分岐/数式/価格表 |
| FlatDiscount | Number | N | 割引率 | 定率割引用 |
| IsBPartnerFlatDiscount | Yes-No | Y | 取引先割引率優先 | |
| IsQuantityBased | Yes-No | Y | 数量ベース | 既定値 Y |
| CumulativeLevel | List(1) | N | 累積レベル | 既定値 L |
| Script | Text(2000) | N | スクリプト | 数式タイプ用 |
| ValidFrom | Date | Y | 適用開始日 | 未設定時は beforeSave で当日補完 |
| Processing | Button | N | 再計算 | reSeq() を起動 |
| IsActive | Yes-No | Y | 有効 | 既定値 Y |
M_DiscountSchemaBreak(割引条件設定)
Section titled “M_DiscountSchemaBreak(割引条件設定)”| カラム名 | 型 | 必須 | 説明 | 備考 |
|---|---|---|---|---|
| M_DiscountSchemaBreak_ID | ID | PK | 分岐ID | 主キー |
| M_DiscountSchema_ID | Table Direct | Y | 割引スキーマ | IsParent=Y |
| SeqNo | Integer | Y | シーケンスNo | 既定値 MAX(SeqNo)+10・識別子 |
| BreakValue | Number | Y | 分岐値 | 適用の下限値 |
| BreakDiscount | Number | Y | 分岐割引率 | |
| M_Product_ID | Search | N | 品目 | 指定時はカテゴリをクリア |
| M_Product_Category_ID | Table Direct | N | 品目カテゴリ | |
| IsBPartnerFlatDiscount | Yes-No | Y | 取引先割引率優先 | 既定値 N |
| FixedPrice | Costs+Prices | N | 固定価格 | 既定値 0 |
| IsActive | Yes-No | Y | 有効 | 既定値 Y |
erDiagram
M_DiscountSchema ||--o{ M_DiscountSchemaBreak : "breaks"
M_DiscountSchema ||--o{ M_DiscountSchemaLine : "price list lines"
M_DiscountSchemaBreak }o--o| M_Product : "product filter"
M_DiscountSchemaBreak }o--o| M_Product_Category : "category filter"
M_PriceList }o--o| M_DiscountSchema : "price list schema"
C_BPartner }o--o| M_DiscountSchema : "partner schema"
ビジネスロジック
Section titled “ビジネスロジック”calculateDiscount() の分岐
Section titled “calculateDiscount() の分岐”flowchart TD
A["calculateDiscount(Qty, Price,<br/>M_Product_ID, M_Product_Category_ID,<br/>BPartnerFlatDiscount)"] --> B["BPartnerFlatDiscount が null なら<br/>ZERO に補完"]
B --> C{DiscountType}
C -->|FlatPercent| D{IsBPartnerFlatDiscount?}
D -->|Yes| E["取引先の割引率を返却"]
D -->|No| F["FlatDiscount を返却"]
C -->|Formula / Pricelist| G["未サポート<br/>ログ出力して ZERO を返却"]
C -->|Breaks| H["getBreaks(false) で分岐を取得"]
H --> I["判定値を決定<br/>数量ベース: Qty / それ以外: Price × Qty"]
I --> J["分岐を順に走査し<br/>applies() が真の行を採用"]
applies() の判定順序
Section titled “applies() の判定順序”MDiscountSchemaBreak.applies(BigDecimal Value, int M_Product_ID, int M_Product_Category_ID):
| 順 | 条件 | 結果 |
|---|---|---|
| 1 | !isActive() | false |
| 2 | Value < BreakValue | false |
| 3 | 行の品目・品目カテゴリがどちらも 0 | true(全品目対象) |
| 4 | 行の品目 == 対象品目 | true |
| 5 | 対象の品目カテゴリ != 0 | 行の品目カテゴリと一致するか |
| 6 | 上記以外 | MProductCategory.isCategory(行のカテゴリ, 対象品目) の結果 |
beforeSave()
Section titled “beforeSave()”| クラス | 処理 |
|---|---|
MDiscountSchema | ValidFrom が null なら TimeUtil.getDay(null)(当日)をセット |
MDiscountSchemaBreak | M_Product_ID > 0 かつ M_Product_Category_ID > 0 の場合、M_Product_Category_ID を 0 にリセット |
reSeq() — シーケンスの振り直し
Section titled “reSeq() — シーケンスの振り直し”「再計算」ボタン(Processing カラム)から起動される reSeq() は次の処理を行います。
getLines(true)でM_DiscountSchemaLineを再取得し、(i+1) * 10の値にSeqNoを更新getBreaks(true)でM_DiscountSchemaBreakを再取得し、同様に 10 刻みへ更新- 更新できた件数を返却し、内部キャッシュ(
m_lines/m_breaks)をクリア
割引後価格の算出
Section titled “割引後価格の算出”calculateDiscountedPrice(BigDecimal price, BigDecimal discount) は static メソッドとして提供され、価格と割引率から割引後価格を求めます。伝票明細の価格計算(calculatePrice())から利用されます。
拡張ポイント(カスタマイズ箇所)
Section titled “拡張ポイント(カスタマイズ箇所)”既存 Callout
Section titled “既存 Callout”M_DiscountSchema / M_DiscountSchemaBreak の各カラムには標準の Callout は定義されていません。SeqNo の既定値は AD の @SQL=SELECT NVL(MAX(SeqNo),0)+10 ... で解決されます。
OSGi Model Validator(推奨)
Section titled “OSGi Model Validator(推奨)”public class CustomDiscountSchemaValidator implements ModelValidator { @Override public int modelChange(PO po, int type) throws Exception { if (po instanceof MDiscountSchemaBreak && (type == TYPE_BEFORE_NEW || type == TYPE_BEFORE_CHANGE)) { MDiscountSchemaBreak br = (MDiscountSchemaBreak) po; // 例: 割引率の上限を 30% に制限 if (br.getBreakDiscount() != null && br.getBreakDiscount().compareTo(new BigDecimal("30")) > 0) { throw new AdempiereException("分岐割引率は30%以内にしてください"); } } return null; }}IPricingRule による価格計算の拡張
Section titled “IPricingRule による価格計算の拡張”「数式(Formula)」タイプがコアで未実装であるため、複雑な割引ロジックが必要な場合は IPricingRule インターフェースを実装し、OSGi サービスとして登録する方式が推奨されます。コアの価格計算エンジンを改変せずに、得意先グループ別・期間限定・累積購入額連動などのルールを追加できます。
関連プロセス
Section titled “関連プロセス”| プロセス名 | 起動場所 | 説明 |
|---|---|---|
| 再計算(Renumber Discount entries) | 割引スキーマタブの Processing ボタン | reSeq() により明細・分岐のシーケンスNoを 10 刻みに振り直す |
関連ドキュメント
Section titled “関連ドキュメント”iDempiereカスタマイズのご相談
Section titled “iDempiereカスタマイズのご相談”数量帯別割引や得意先別価格は、売上と粗利に直結する重要な設定です。標準の割引スキーマで足りない部分は IPricingRule の追加実装で、コア改変なしに柔軟な価格戦略を実現できます。
As-Link株式会社では、OSGiプラグインによる安全なカスタマイズを提供しています。