拡張機能を使用した税統合のデータ フィールドの追加

この記事では、X++ 拡張機能を使用して税統合のデータ フィールドを追加する方法について説明します。 これらのフィールドは、税サービスの税データ モデルに拡張し、税コードを決定するために使用できます。 詳細については、税コンフィギュレーションにデータ フィールドを追加するを参照してください。

データ モデル

データ モデルのデータはオブジェクトによって転送され、クラスによって実装されます。

主なオブジェクトの一覧を次に示します。

  • AxClass/TaxIntegrationDocumentObject
  • AxClass/TaxIntegrationLineObject
  • AxClass/TaxIntegrationTaxLineObject

次の図に、これらのオブジェクトを関連付ける方法を示します。

データ モデル オブジェクト リレーションシップ。 ]

Document オブジェクトには 多数の Document オブジェクトを含めることができます。 各オブジェクトには、税サービスのメタデータが含まれます。

  • TaxIntegrationDocumentObject には、ソース アドレスに関する情報を含む includingTax メタデータと、明細行に売上税が含まれているかどうかを示す originAddress メタデータがあります。
  • TaxIntegrationLineObject には、itemIdquantitycategoryId メタデータが含まれます。

メモ

TaxIntegrationLineObject には、Charge オブジェクトも実装されます。

統合フロー

フロー内のデータは活動によって操作されます。

主な活動

  • AxClass/TaxIntegrationCalculationActivityOnDocument
  • AxClass/TaxIntegrationCurrencyExchangeActivityOnDocument
  • AxClass/TaxIntegrationDataPersistenceActivityOnDocument
  • AxClass/TaxIntegrationDataRetrievalActivityOnDocument
  • AxClass/TaxIntegrationSettingRetrievalActivityOnDocument

活動は次の順序で実行されます。

  1. 取得の設定
  2. データ取得
  3. 計算サービス
  4. 通貨為替
  5. データ持続性

たとえば、計算サービスの前にデータ取得を拡張します。

データ取得活動

データ取得活動は、データベースからデータを取得します。 さまざまなトランザクションに対するアダプタにより、次の異なるトランザクション テーブルからデータを取得できます。

  • AxClass/TaxIntegrationPurchTableDataRetrieval
  • AxClass/TaxIntegrationPurchParmTableDataRetrieval
  • AxClass/TaxIntegrationPurchREQTableDataRetrieval
  • AxClass/TaxIntegrationPurchRFQTableDataRetrieval
  • AxClass/TaxIntegrationVendInvoiceInfoTableDataRetrieval
  • AxClass/TaxIntegrationSalesTableDataRetrieval
  • AxClass/TaxIntegrationSalesParmDataRetrieval

これらのデータ取得活動では、データベースから TaxIntegrationDocumentObjectTaxIntegrationLineObject にデータがコピーされます。 これらの活動はすべて同じ抽象テンプレート クラスを拡張するので、メソッドは共通です。

計算サービス活動

税計算活動は、税サービスと税統合の間のリンクです。 この活動は、次の機能を担当します。

  1. 要求を作成する。
  2. 税務サービスへ要求を転記する。
  3. 税サービスから応答を取得する。
  4. 応答を解析する。

要求に追加するデータ フィールドは、他のメタデータと共に転記されます。

拡張機能の実装

ここでは、拡張機能の実装方法を説明する詳細な手順を示します。 例としてコスト センタープロジェクトの財務分析コードが使用されます。

手順 1 オブジェクト クラスにデータ変数を追加する

オブジェクト クラスには、データ変数およびデータの getter/setter メソッドが含まれています。 フィールドのレベルに応じて、TaxIntegrationDocumentObject または TaxIntegrationLineObject にデータ フィールドを追加します。 次の例では行レベルを使用し、ファイル名は TaxIntegrationLineObject_Extension.xpp です。

メモ

追加するデータ フィールドがドキュメント レベルの場合は、ファイル名を TaxIntegrationDocumentObject_Extension.xpp に変更します。

[ExtensionOf(classStr(TaxIntegrationLineObject))]
final class TaxIntegrationLineObject_Extension
{
    private OMOperatingUnitNumber costCenter;
    private ProjId projectId;

    /// <summary>
    /// Gets a costCenter.
    /// </summary>
    /// <returns>The cost center.</returns>
    public final OMOperatingUnitNumber getCostCenter()
    {
        return this.costCenter;
    }

    /// <summary>
    /// Sets the cost center.
    /// </summary>
    /// <param name = "_value">The cost center.</param>
    public final void setCostCenter(OMOperatingUnitNumber _value)
    {
        this.costCenter = _value;
    }

    /// <summary>
    /// Gets a project ID.
    /// </summary>
    /// <returns>The project ID.</returns>
    public final ProjId getProjectId()
    {
        return this.projectId;
    }

    /// <summary>
    /// Sets the project ID.
    /// </summary>
    /// <param name = "_value">The project ID.</param>
    public final void setProjectId(ProjId _value)
    {
        this.projectId = _value;
    }
}

コスト センタープロジェクトが、プライベート変数として追加されます。 これらのデータ フィールドでデータを操作するための getter メソッドおよび Setter メソッドを作成します。

手順 2 データベースからデータを取得する

トランザクションを指定し、適切なアダプタ クラスを拡張してデータを取得します。 たとえば、発注書トランザクションを使用する場合は、TaxIntegrationPurchTableDataRetrieval および TaxIntegrationVendInvoiceInfoTableDataRetrieval を拡張する必要があります。

メモ

TaxIntegrationPurchParmTableDataRetrieval は、TaxIntegrationPurchTableDataRetrieval から継承されています。 purchTablepurchParmTable テーブルのロジックが異なる場合以外、変更することはできません。

すべてのトランザクションにデータ フィールドを追加する必要がある場合は、すべての DataRetrieval クラスを拡張します。

すべてのデータ取得活動は同じテンプレート クラスを拡張するため、クラス構造、変数、およびメソッドは類似しています。

protected TaxIntegrationDocumentObject document;

/// <summary>
/// Copies to the document.
/// </summary>
protected abstract void copyToDocument()
{
    // It is recommended to implement as:
    //
    // this.copyToDocumentByDefault();
    // this.copyToDocumentFromHeaderTable();
    // this.copyAddressToDocument();
}
 
/// <summary>
/// Copies to the current line of the document.
/// </summary>
/// <param name = "_line">The current line of the document.</param>
protected abstract void copyToLine(TaxIntegrationLineObject _line)
{
    // It is recommended to implement as:
    //
    // this.copyToLineByDefault(_line);
    // this.copyToLineFromLineTable(_line);
    // this.copyQuantityAndTransactionAmountToLine(_line);
    // this.copyAddressToLine(_line);
    // this.copyToLineFromHeaderTable(_line);
}

次の例では、PurchTable テーブルを使用する場合の基本的な構造を示します。

public class TaxIntegrationPurchTableDataRetrieval extends TaxIntegrationAbstractDataRetrievalTemplate
{
    protected PurchTable purchTable;
    protected PurchLine purchLine;

    // Query builder methods
    [Replaceable]
    protected SysDaQueryObject getDocumentQueryObject()
    [Replaceable]
    protected SysDaQueryObject getLineQueryObject()
    [Replaceable]
    protected SysDaQueryObject getDocumentChargeQueryObject()
    [Replaceable]
    protected SysDaQueryObject getLineChargeQueryObject()

    // Data retrieval methods
    protected void copyToDocument()
    protected void copyToDocumentFromHeaderTable()
    protected void copyToLine(TaxIntegrationLineObject _line)
    protected void copyToLineFromLineTable(TaxIntegrationLineObject _line)
    ...
}

CopyToDocument メソッドが呼び出されたとき、this.purchTable バッファは既に存在します。 このメソッドの目的は、DocumentObject クラスで作成された Setter メソッドを使用して、必要なすべてのデータを this.purchTable から document オブジェクトにコピーすることです。

同様に、CopyToLine メソッドには this.purchLine バッファが既に存在します。 このメソッドの目的は、LineObject クラスで作成された Setter メソッドを使用して、必要なすべてのデータを this.purchLine から _line オブジェクトにコピーすることです。

最も単純な方法は、CopyToDocumentCopyToLine メソッドを拡張する方法です。 ただし、最初に copyToDocumentFromHeaderTablecopyToLineFromLineTable メソッドを試することをお勧めします。 希望通りに機能しない場合、独自のメソッドを実装し、CopyToDocumentCopyToLine で呼び出 します 。 このメソッドを使用できる一般的な状況には次の 3 種類があります。

  • 必須フィールドは、PurchTable または PurchLine にあります。 この場合、copyToDocumentFromHeaderTablecopyToLineFromLineTable を拡張できます。 サンプル コードを次に示します。

    /// <summary>
    /// Copies to the current line of the document from.
    /// </summary>
    /// <param name = "_line">The current line of the document.</param>
    protected void copyToLineFromLineTable(TaxIntegrationLineObject _line)
    {
        next copyToLineFromLineTable(_line);
        // if we already added XXX in TaxIntegrationLineObject
        _line.setXXX(this.purchLine.XXX);
    }
    
  • 必要なデータは、トランザクションの既定テーブルにはありません。 ただし、既定のテーブルとの結合リレーションシップがいくつかあるため、フィールドはほとんどの行で必須です。 この場合は、getDocumentQueryObject または getLineObject を置換して、結合リレーションシップによってテーブルをクエリします。 次の例では、今すぐ配送フィールドが行レベルで販売注文と統合されています。

    public class TaxIntegrationSalesTableDataRetrieval
    {
        protected MCRSalesLineDropShipment mcrSalesLineDropShipment;
    
        /// <summary>
        /// Gets the query for the lines of the document.
        /// </summary>
        /// <returns>The query for the lines of the document</returns>
        [Replaceable]
        protected SysDaQueryObject getLineQueryObject()
        {
            return SysDaQueryObjectBuilder::from(this.salesLine)
                .where(this.salesLine, fieldStr(SalesLine, SalesId)).isEqualToLiteral(this.salesTable.SalesId)
                .outerJoin(this.mcrSalesLineDropShipment)
                .where(this.mcrSalesLineDropShipment, fieldStr(MCRSalesLineDropShipment, SalesLine)).isEqualTo(this.salesLine, fieldStr(SalesLine, RecId))
                .toSysDaQueryObject();
        }
    }
    

    この例では、mcrSalesLineDropShipment バッファが申告され、クエリが getLineQueryObject で定義されています。 クエリは、リレーションシップ MCRSalesLineDropShipment.SalesLine == SalesLine.RecId を使用します。 この状況を拡張している間、getLineQueryObject を独自に作成したクエリ オブジェクトで置換できます。 ただし、次の点に留意してください。

    • getLineQueryObject メソッドの戻り値は SysDaQueryObject なので、SysDa法を使用してこの オブジェクトを作成する必要があります。
    • 存在するテーブルは削除できません。
  • 必須データがトランザクション テーブルに複雑な結合リレーションシップで関連付けられている、またはリレーションシップが 1 対 1 (1:1) ではなく 1 対多 (1:N) です。 そのような状況では、少し複雑になります。 このような状況は、財務分析コードの例に適用されます。

    この場合、独自のメソッドを実装してデータを取得できます。 TaxIntegrationPurchTableDataRetrieval_Extension.xpp ファイル内のサンプル コードを次に示します。

    [ExtensionOf(classStr(TaxIntegrationPurchTableDataRetrieval))]
    final class TaxIntegrationPurchTableDataRetrieval_Extension
    {
        private const str CostCenterKey = 'CostCenter';
        private const str ProjectKey = 'Project';
    
        /// <summary>
        /// Copies to the current line of the document from.
        /// </summary>
        /// <param name = "_line">The current line of the document.</param>
        protected void copyToLineFromLineTable(TaxIntegrationLineObject _line)
        {
            Map dimensionAttributeMap = this.getDimensionAttributeMapByDefaultDimension(this.purchline.DefaultDimension);
            if (dimensionAttributeMap.exists(CostCenterKey))
            {
                _line.setCostCenter(dimensionAttributeMap.lookup(CostCenterKey));
            }
            if (dimensionAttributeMap.exists(ProjectKey))
            {
                _line.setProjectId(dimensionAttributeMap.lookup(ProjectKey));
            }
            next copyToLineFromLineTable(_line);
        }
        private Map getDimensionAttributeMapByDefaultDimension(RefRecId _defaultDimension)
        {
            DimensionAttribute dimensionAttribute;
            DimensionAttributeValue dimensionAttributeValue;
            DimensionAttributeValueSetItem dimensionAttributeValueSetItem;
            Map ret = new Map(Types::String, Types::String);
    
            select Name, RecId from dimensionAttribute
                join dimensionAttributeValue
                    where dimensionAttributeValue.dimensionAttribute == dimensionAttribute.RecId
                join DimensionAttributeValueSetItem
                    where DimensionAttributeValueSetItem.DimensionAttributeValue == DimensionAttributeValue.RecId
                        && DimensionAttributeValueSetItem.DimensionAttributeValueSet == _defaultDimension;
    
            while(dimensionAttribute.RecId)
            {
                ret.insert(dimensionAttribute.Name, dimensionAttributeValue.DisplayValue);
                next dimensionAttribute;
            }
            return ret;
        }
    }
    

手順 3 要求へデータを追加する

copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject または copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine メソッドを拡張して、データを要求に追加します。 TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension.xpp ファイル内のサンプル コードを次に示します。

[ExtensionOf(classStr(TaxIntegrationCalculationActivityOnDocument_CalculationService))]
final static class TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension
{
    // Define the field name in the request
    private const str IOCostCenter = 'Cost Center';
    private const str IOProject = 'Project';
    // private const str IOEnumExample = 'Enum Example';

    /// <summary>
    /// Copies to <c>TaxableDocumentLineWrapper</c> from <c>TaxIntegrationLineObject</c> by line.
    /// </summary>
    /// <param name = "_destination"><c>TaxableDocumentLineWrapper</c>.</param>
    /// <param name = "_source"><c>TaxIntegrationLineObject</c>.</param>
    protected static void copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine(Microsoft.Dynamics.TaxCalculation.ApiContracts.TaxableDocumentLineWrapper _destination, TaxIntegrationLineObject _source)
    {
        next copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine(_destination, _source);
        // Set the field we need to integrated for tax service
        _destination.SetField(IOCostCenter, _source.getCostCenter());
        _destination.SetField(IOProject, _source.getProjectId());

        // If the field to be extended is an enum type, use enum2Symbol to convert an enum variable exampleEnum of ExampleEnumType to a string
        // _destination.SetField(IOEnumExample, enum2Symbol(enumNum(ExampleEnumType), _source.getExampleEnum()));
    }
}

このコードでは _destination が要求の生成に使用されるラッパー オブジェクトであり、_sourceTaxIntegrationLineObject オブジェクトになります。

メモ

要求内で private const str として使用されるフィールド名を定義します。 文字列は、税コンフィギュレーションへのデータ フィールドの追加の記事で追加したノード名 (ラベルではない) と同じである必要があります。

SetField メソッドを使用して、copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine メソッドのフィールドを設定します。 2 番目のパラメータのデータ型は、文字列である必要があります。 データ型が文字列でない場合、文字列に変換します。 データ型が X++ 列挙型 の場合、enum2Symbol メソッドを使用して列挙値を文字列に変換することをお勧めします。 税コンフィギュレーションで追加する列挙値は、列挙名と完全に同じにしてください。 次に、列挙値、ラベル、および名前の違いのリストを示します。

  • 列挙型の名前は、コード内のシンボル名です。 enum2Symbol() は、列挙値を名前に変換することができます。
  • 列挙値は整数です。
  • 列挙のラベルは、優先する言語で異なる場合があります。 enum2Str() は、列挙値をラベルに変換することができます。

モデルの依存関係

プロジェクトを正常に構築するには、次の参照モデルをモデルの依存関係に追加します。

  • ApplicationPlatform
  • ApplicationSuite
  • 税エンジン
  • 分析コード (財務分析コードが使用されている場合)
  • コード内で参照されるその他の必要なモデル

検証

前の手順を完了したら、プロジェクトをビルドして変更を検証できます。

  1. 財務で、買掛金管理に移動し、&debug=vs%2CconfirmExit& を URL に追加します。 たとえば、https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&。 最後の は必須です。
  2. 発注書ページを開いて、新規を選択して発注書を作成します。
  3. カスタマイズされたフィールドの値を設定し、売上税を選択します。 接頭語が付けられているトラブルシューティング ファイルである TaxServiceTroubleshootingLog は自動的にダウンロードされます。 このファイルには、税務計算サービスに転記されたトランザクション情報が含まれます。
  4. カスタマイズされた追加フィールドが 税計算入力 JSON セクションに存在し、値が正しいことを確認します。 値の変更が修正しない場合は、このドキュメントの手順をダブルクリックします。

ファイルの例:

===Tax service calculation input JSON:===
{
  "TaxableDocument": {
    "Header": [
      {
        "Lines": [
          {
            "Line Type": "Normal",
            "Item Code": "",
            "Item Type": "Item",
            "Quantity": 0.0,
            "Amount": 1000.0,
            "Currency": "EUR",
            "Transaction Date": "2022-1-26T00:00:00",
            ...
            /// The new fields added at line level
            "Cost Center": "003",
            "Project": "Proj-123"
          }
        ],
        "Amount include tax": true,
        "Business Process": "Journal",
        "Currency": "",
        "Vendor Account": "DE-001",
        "Vendor Invoice Account": "DE-001",
        ...
        // The new fields added at header level, no new fields in this example
        ...
      }
    ]
  },
}
...

付録

この付録では、財務分析コード (コスト センターおよびプロジェクト) を明細行レベルで統合する完全なサンプル コードを示しています。

TaxIntegrationLineObject_Extension.xpp

[ExtensionOf(classStr(TaxIntegrationLineObject))]
final class TaxIntegrationLineObject_Extension
{
    private OMOperatingUnitNumber costCenter;
    private ProjId projectId;

    /// <summary>
    /// Gets a costCenter.
    /// </summary>
    /// <returns>The cost center.</returns>
    public final OMOperatingUnitNumber getCostCenter()
    {
        return this.costCenter;
    }

    /// <summary>
    /// Sets the cost center.
    /// </summary>
    /// <param name = "_value">The cost center.</param>
    public final void setCostCenter(OMOperatingUnitNumber _value)
    {
        this.costCenter = _value;
    }

    /// <summary>
    /// Gets a project ID.
    /// </summary>
    /// <returns>The project ID.</returns>
    public final ProjId getProjectId()
    {
        return this.projectId;
    }

    /// <summary>
    /// Sets the project ID.
    /// </summary>
    /// <param name = "_value">The project ID.</param>
    public final void setProjectId(ProjId _value)
    {
        this.projectId = _value;
    }
}

TaxIntegrationPurchTableDataRetrieval_Extension.xpp

[ExtensionOf(classStr(TaxIntegrationPurchTableDataRetrieval))]
final class TaxIntegrationPurchTableDataRetrieval_Extension
{
    private const str CostCenterKey = 'CostCenter';
    private const str ProjectKey = 'Project';

    /// <summary>
    /// Copies to the current line of the document from.
    /// </summary>
    /// <param name = "_line">The current line of the document.</param>
    protected void copyToLineFromLineTable(TaxIntegrationLineObject _line)
    {
        Map dimensionAttributeMap = this.getDimensionAttributeMapByDefaultDimension(this.purchline.DefaultDimension);
        if (dimensionAttributeMap.exists(CostCenterKey))
        {
            _line.setCostCenter(dimensionAttributeMap.lookup(CostCenterKey));
        }
        if (dimensionAttributeMap.exists(ProjectKey))
        {
            _line.setProjectId(dimensionAttributeMap.lookup(ProjectKey));
        }
        next copyToLineFromLineTable(_line);
    }
    private Map getDimensionAttributeMapByDefaultDimension(RefRecId _defaultDimension)
    {
        DimensionAttribute dimensionAttribute;
        DimensionAttributeValue dimensionAttributeValue;
        DimensionAttributeValueSetItem dimensionAttributeValueSetItem;
        Map ret = new Map(Types::String, Types::String);
        select Name, RecId from dimensionAttribute
            join dimensionAttributeValue
                where dimensionAttributeValue.dimensionAttribute == dimensionAttribute.RecId
            join DimensionAttributeValueSetItem
                where DimensionAttributeValueSetItem.DimensionAttributeValue == DimensionAttributeValue.RecId
                    && DimensionAttributeValueSetItem.DimensionAttributeValueSet == _defaultDimension;
        while(dimensionAttribute.RecId)
        {
            ret.insert(dimensionAttribute.Name, dimensionAttributeValue.DisplayValue);
            next dimensionAttribute;
        }
        return ret;
    }
}

TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension.xpp

[ExtensionOf(classStr(TaxIntegrationCalculationActivityOnDocument_CalculationService))]
final static class TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension
{
    // Define the field name in the request
    private const str IOCostCenter = 'Cost Center';
    private const str IOProject = 'Project';

    /// <summary>
    /// Copies to <c>TaxableDocumentLineWrapper</c> from <c>TaxIntegrationLineObject</c> by line.
    /// </summary>
    /// <param name = "_destination"><c>TaxableDocumentLineWrapper</c>.</param>
    /// <param name = "_source"><c>TaxIntegrationLineObject</c>.</param>
    protected static void copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine(Microsoft.Dynamics.TaxCalculation.ApiContracts.TaxableDocumentLineWrapper _destination, TaxIntegrationLineObject _source)
    {
        next copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine(_destination, _source);
        // Set the field we need to integrated for tax service
        _destination.SetField(IOCostCenter, _source.getCostCenter());
        _destination.SetField(IOProject, _source.getProjectId());
    }
}