Přidání datových polí do daňové integrace pomocí rozšíření
Tento článek vysvětluje, jak používat rozšíření X++ k přidání datových polí do daňové integrace. Tato pole lze rozšířit na daňový datový model daňové služby a použít k určení daňových kódů. Další informace najdete v tématu Přidání datových polí do daňových konfigurací.
Datový model
Data v datovém modelu jsou přenášena objekty a implementována třídami.
Zde je seznam hlavních objektů:
- AxClass/TaxIntegrationDocumentObject
- AxClass/TaxIntegrationLineObject
- AxClass/TaxIntegrationTaxLineObject
Následující obrázek ukazuje, jak tyto objekty souvisejí.
]
Objekt Document může obsahovat mnoho objektů Line. Každý objekt obsahuje metadata pro daňovou službu.
-
TaxIntegrationDocumentObject
má metadataoriginAddress
, která obsahují informace o zdrojové adrese, a metadataincludingTax
, která označují, zda částka zahrnuje daň z obratu. -
TaxIntegrationLineObject
má metadataitemId
,quantity
acategoryId
.
Poznámka
TaxIntegrationLineObject
také implementuje objekty Charge.
Tok integrace
Údaje v toku jsou manipulovány aktivitami.
Klíčové aktivity
- AxClass/TaxIntegrationCalculationActivityOnDocument
- AxClass/TaxIntegrationCurrencyExchangeActivityOnDocument
- AxClass/TaxIntegrationDataPersistenceActivityOnDocument
- AxClass/TaxIntegrationDataRetrievalActivityOnDocument
- AxClass/TaxIntegrationSettingRetrievalActivityOnDocument
Aktivity probíhají v následujícím pořadí:
- Nastavení načítání
- Načítání dat
- Výpočetní služba
- Směnný kurz měny
- Perzistence dat
Rozšiřte například fázi Načítání dat před Výpočetní službu.
Aktivity načítání dat
Aktivity Načítání dat načítají data z databáze. K načítání dat z různých transakčních tabulek jsou k dispozici adaptéry pro různé transakce:
- AxClass/TaxIntegrationPurchTableDataRetrieval
- AxClass/TaxIntegrationPurchParmTableDataRetrieval
- AxClass/TaxIntegrationPurchREQTableDataRetrieval
- AxClass/TaxIntegrationPurchRFQTableDataRetrieval
- AxClass/TaxIntegrationVendInvoiceInfoTableDataRetrieval
- AxClass/TaxIntegrationSalesTableDataRetrieval
- AxClass/TaxIntegrationSalesParmDataRetrieval
V těchto aktivitách Načítání dat se data zkopírují z databáze do objektů TaxIntegrationDocumentObject
a TaxIntegrationLineObject
. Protože všechny tyto aktivity rozšiřují stejnou abstraktní třídu šablony, mají společné metody.
Aktivity výpočetní služby
Aktivita Výpočetní služba je odkazem mezi daňovou službou a daňovou integrací. Tato aktivita je zodpovědná za následující funkce:
- Sestavení požadavku.
- Zaslání požadavku daňové službě.
- Získání odpovědi od daňové služby.
- Syntaktickou analýzu odpovědi.
Datové pole, které přidáte k požadavku, bude zasláno společně s dalšími metadaty.
Implementace rozšíření
Tato část poskytuje podrobné kroky, které vysvětlují, jak implementovat rozšíření. Jako příklady využívá finanční dimenze Nákladové středisko a Projekt.
Krok 1. Přidejte datovou proměnnou do třídy objektu
Třída objektu obsahuje datovou proměnnou a metody getter/setter pro načítání/zápis dat. Přidejte datové pole buď do objektu TaxIntegrationDocumentObject
, nebo do objektu TaxIntegrationLineObject
, v závislosti na úrovni pole. Následující příklad používá úroveň řádku a název souboru je TaxIntegrationLineObject_Extension.xpp
.
Poznámka
Pokud je datové pole, které přidáváte, na úrovni dokumentu, změňte název souboru na 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;
}
}
Nákladové středisko a Projekt jsou přidány jako soukromé proměnné. Vytvořte metody getter a setter pro tato datová pole, které zajistí manipulaci s daty.
Krok 2. Načtěte data z databáze
Určete transakci a rozšiřte příslušné třídy adaptérů tak, aby se data načetla. Například pokud používáte transakci Nákupní objednávka, musíte rozšířit třídy TaxIntegrationPurchTableDataRetrieval
a TaxIntegrationVendInvoiceInfoTableDataRetrieval
.
Poznámka
Třída TaxIntegrationPurchParmTableDataRetrieval
dědí ze třídy TaxIntegrationPurchTableDataRetrieval
. Nemělo by se to měnit, pokud se liší logika tabulek purchTable
a purchParmTable
.
Pokud má být datové pole přidáno pro všechny transakce, rozšiřte všechny třídy DataRetrieval
.
Protože všechny aktivity Načítání dat rozšiřují stejnou třídu šablony, jsou struktury tříd, proměnné a metody podobné.
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);
}
Následující příklad ukazuje základní strukturu, když se používá tabulka 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)
...
}
Když je volána metoda CopyToDocument
, vyrovnávací paměť this.purchTable
již existuje. Účelem této metody je zkopírovat všechna požadovaná data z objektu this.purchTable
do objektu document
pomocí metody setter, která byla vytvořena ve třídě DocumentObject
.
Obdobně již existuje vyrovnávací paměť this.purchLine
v metodě CopyToLine
. Účelem této metody je zkopírovat všechna požadovaná data z objektu this.purchLine
do objektu _line
pomocí metody setter, která byla vytvořena ve třídě LineObject
.
Nejpřímějším přístupem je rozšíření metod CopyToDocument
a CopyToLine
. Doporučujeme však vyzkoušet nejprve metody copyToDocumentFromHeaderTable
a copyToLineFromLineTable
. Pokud nefungují tak, jak požadujete, implementujte vlastní metodu a volejte ji v metodách CopyToDocument
a CopyToLine
. Existují tři běžné situace, kdy můžete použít tento přístup:
Povinné pole je ve třídě
PurchTable
neboPurchLine
. V této situaci můžete rozšířit metodycopyToDocumentFromHeaderTable
acopyToLineFromLineTable
. Zde je ukázkový kód./// <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); }
Požadovaná data nejsou ve výchozí tabulce transakce. Existují však některé relace spojení s výchozí tabulkou a toto pole je u většiny řádků povinné. V této situaci nahraďte metody
getDocumentQueryObject
nebogetLineObject
a dotazujte se v tabulce podle relace spojení. V následujícím příkladu je pole Doručit ihned integrováno s prodejní objednávkou na úrovni řádku.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(); } }
V tomto příkladu je deklarována vyrovnávací paměť
mcrSalesLineDropShipment
a dotaz je definován v metoděgetLineQueryObject
. Dotaz používá relaciMCRSalesLineDropShipment.SalesLine == SalesLine.RecId
. Když v této situaci rozšiřujete třídu, můžete nahraditgetLineQueryObject
vlastním vytvořeným objektem dotazu. Upozorňujeme však na tyto aspekty:- Protože návratová hodnota metody
getLineQueryObject
je objektSysDaQueryObject
, musíte tento objekt sestavit pomocí přístupu SysDa. - Existující tabulku nelze odstranit.
- Protože návratová hodnota metody
Požadovaná data souvisí s transakční tabulkou komplikovanou relací spojení, nebo relace není jedna ku jedné (1:1), ale jedna k mnoha (1:N). V tomto případě se věci trochu komplikují. Tato situace platí pro příklad finančních dimenzí.
V tomto případě můžete implementovat vlastní metodu k načtení dat. Zde je ukázkový kód v souboru
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; } }
Krok 3. Přidejte k požadavku data
Rozšiřte metodu copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject
nebo copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine
, aby přidávala data k požadavku. Zde je ukázkový kód v souboru 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()));
}
}
V tomto kódu je _destination
objektem obálky, který se používá ke generování požadavku, a _source
je objekt třídy TaxIntegrationLineObject
.
Poznámka
Definujte název pole, které se používá ve formuláři žádosti, jako private const str. Řetězec by měl být přesně stejný jako název uzlu (nikoli popisek) přidaný v článku Přidání datových polí v daňových konfiguracích.
Nastavte pole v metodě copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine pomocí metody SetField. Datový typ druhého parametru by měl být string. Pokud datový typ není string, převeďte ho na řetězec. Pokud je datový typ X++ typ výčtu, doporučujeme použít metodu enum2Symbol pro převod hodnoty enum na řetězec. Přidaná hodnota výčtu v konfiguraci daně by měla být přesně stejná jako název výčtu. Následuje seznam rozdílů mezi hodnotou výčtu, štítkem a názvem.
- Název enum je symbolický název v kódu. enum2Symbol() lze použít k převodu hodnoty výčtu na jeho název.
- Hodnota výčtu je typu integer.
- Popisek výčtu se může v různých preferovaných jazycích lišit. enum2Str() lze použít k převodu hodnoty výčtu na jeho popisek.
Závislost na modelu
Chcete-li úspěšně sestavit projekt, přidejte do závislostí modelu následující referenční modely:
- ApplicationPlatform
- ApplicationSuite
- Daňový modul
- Dimenze, pokud je použita finanční dimenze
- Další potřebné modely uvedené v kódu
Ověření
Po dokončení předchozích kroků můžete potvrdit své změny.
- V části Finance přejděte na Závazky a přidejte k adrese URL část &debug=vs%2CconfirmExit&. Například
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&
. Závěrečný znak & je zásadní. - Otevřete stránku Nákupní objednávka a výběrem položky Nová vytvořte nákupní objednávku.
- Nastavte hodnotu pro přizpůsobené pole a poté vyberte Prodejní daň. Soubor pro odstraňování problémů s předponou TaxServiceTroubleshootingLog se stáhne automaticky. Tento soubor obsahuje informace o transakci odeslané do služby pro výpočet daně.
- Zkontrolujte, zda je přidané přizpůsobené pole přítomno v sekci Vstupní JSON služby pro výpočet daně a zda je jeho hodnota správná. Pokud hodnota není správná, znovu zkontrolujte kroky v tomto dokumentu.
Příklad souboru:
===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
...
}
]
},
}
...
Dodatek
Tato příloha ukazuje kompletní ukázkový kód pro integraci finančních dimenzí Nákladové středisko a Projekt na úrovni řádku.
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());
}
}