Gegevensvelden in de belastingintegratie toevoegen met extensies
In dit artikel wordt uitgelegd hoe u X++-extensies gebruikt om gegevensvelden toe te voegen in de belastingintegratie. Deze velden kunnen worden uitgebreid tot het belastinggegevensmodel van de belastingservice en worden gebruikt om belastingcodes te bepalen. Zie Gegevensvelden toevoegen in belastingconfiguraties voor meer informatie.
Gegevensmodel
De gegevens in het gegevensmodel worden vervoerd door objecten en geïmplementeerd door klassen.
Hieronder volgt een overzicht van de belangrijke objecten:
- AxClass/TaxIntegrationDocumentObject
- AxClass/TaxIntegrationLineObject
- AxClass/TaxIntegrationTaxLineObject
In de volgende afbeelding ziet u de relaties tussen deze objecten.
]
Een object Document kan veel objecten van het type Line bevatten. Elk object bevat metagegevens voor de belastingservice.
-
Het
TaxIntegrationDocumentObject
beschikt overoriginAddress
metagegevens, die informatie bevaten over het bronadres, enincludingTax
-metagegevens die aangeven of het regelbedrag inclusief btw is. -
TaxIntegrationLineObject
bevatitemId
-,quantity
- encategoryId
-metagegevens.
Notitie
TaxIntegrationLineObject
implementeert ook Charge-objecten.
Integratiestroom
De gegevens in de stroom worden gemanipuleerd door activiteiten.
Sleutelactiviteiten
- AxClass/TaxIntegrationCalculationActivityOnDocument
- AxClass/TaxIntegrationCurrencyExchangeActivityOnDocument
- AxClass/TaxIntegrationDataPersistenceActivityOnDocument
- AxClass/TaxIntegrationDataRetrievalActivityOnDocument
- AxClass/TaxIntegrationSettingRetrievalActivityOnDocument
Activiteiten worden uitgevoerd in de volgende volgorde:
- SettingRetrieval
- DataRetrieval
- Calculation
- CurrencyExchange
- DataPersistence
Breid DataRetrieval bijvoorbeeld eerder uit dan Calculation.
DataRetrieval-activiteiten
Met DataRetrieval-activiteiten worden gegevens opgehaald uit de database. Er zijn adapters voor verschillende transacties beschikbaar om gegevens uit verschillende transactietabellen op te halen:
- AxClass/TaxIntegrationPurchTableDataRetrieval
- AxClass/TaxIntegrationPurchParmTableDataRetrieval
- AxClass/TaxIntegrationPurchREQTableDataRetrieval
- AxClass/TaxIntegrationPurchRFQTableDataRetrieval
- AxClass/TaxIntegrationVendInvoiceInfoTableDataRetrieval
- AxClass/TaxIntegrationSalesTableDataRetrieval
- AxClass/TaxIntegrationSalesParmDataRetrieval
In deze DataRetrieval-activiteiten worden gegevens van de database naar TaxIntegrationDocumentObject
en TaxIntegrationLineObject
gekopieerd. Omdat al deze activiteiten dezelfde abstracte sjabloonklasse uitbreiden, hebben ze gemeenschappelijke methoden.
Calculation-activiteiten
De Calculation-activiteit is de koppeling tussen de belastingservice en de belastingintegratie. Deze activiteit is verantwoordelijk voor de volgende functies:
- De aanvraag maken.
- De aanvraag naar de belastingservice boeken.
- Het antwoord van de belastingservice ontvangen.
- Het antwoord parseren.
Een gegevensveld dat u aan de aanvraag toevoegt, wordt samen met andere metagegevens geboekt.
Extensie-implementatie
In deze sectie vindt u gedetailleerde stappen waarin wordt uitgelegd hoe u de extensie implementeert. De financiële dimensies Kostenplaats en Project worden als voorbeelden gebruikt.
Stap 1. De gegevensvariabele toevoegen in de objectklasse
De objectklasse bevat de gegevensvariabele en getter/setter-methoden voor de gegevens. Voeg het gegevensveld toe aan TaxIntegrationDocumentObject
of TaxIntegrationLineObject
, afhankelijk van het niveau van het veld. In het volgende voorbeeld wordt het regelniveau gebruikt en de bestandsnaam is TaxIntegrationLineObject_Extension.xpp
.
Notitie
Als het gegevensveld dat u toevoegt zich op documentniveau bevindt, wijzigt u de bestandsnaam in 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;
}
}
Kostenplaats en Project worden toegevoegd als privévariabelen. Maak getter- en settermethoden voor deze gegevensvelden om de gegevens te bewerken.
Stap 2. Gegevens uit de database ophalen
Geef de transactie op en breid de juiste adapterklassen uit om de gegevens op te halen. Voor een transactie Inkooporder moet u bijvoorbeeld TaxIntegrationPurchTableDataRetrieval
en TaxIntegrationVendInvoiceInfoTableDataRetrieval
uitbreiden.
Notitie
TaxIntegrationPurchParmTableDataRetrieval
wordt overgenomen van TaxIntegrationPurchTableDataRetrieval
. Wijzig dit alleen als de logica van de tabellen purchTable
en purchParmTable
verschilt.
Als het gegevensveld voor alle transacties moet worden toegevoegd, breidt u alle DataRetrieval
-klassen uit.
Omdat alle DataRetrieval-activiteiten dezelfde sjabloonklasse uitbreiden, zijn de klassestructuren, variabelen en methoden vergelijkbaar.
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);
}
In het volgende voorbeeld ziet u de basisstructuur wanneer de PurchTable
-tabel wordt gebruikt.
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)
...
}
Wanneer de CopyToDocument
-methode wordt aangeroepen, bestaat de this.purchTable
-buffer al. Het doel van deze methode is om alle vereiste gegevens van this.purchTable
naar het document
-object te kopiëren met de setter-methode die in de DocumentObject
-klasse is gemaakt.
Evenzo bestaat er al een this.purchLine
-buffer in de CopyToLine
-methode. Het doel van deze methode is om alle vereiste gegevens van this.purchLine
naar het _line
-object te kopiëren met de setter-methode die in de LineObject
-klasse is gemaakt.
De meest directe aanpak is om de CopyToDocument
- en CopyToLine
-methoden uit te breiden. We raden u echter aan om eerst de copyToDocumentFromHeaderTable
- en copyToLineFromLineTable
-methoden uit te proberen. Als deze niet werken zoals u wilt, implementeert u uw eigen methode en roept u deze aan in CopyToDocument
en CopyToLine
. Er zijn drie veelvoorkomende situaties waarin u deze aanpak kunt gebruiken:
Het vereiste veld bevindt zich in
PurchTable
ofPurchLine
. In dit geval kunt ucopyToDocumentFromHeaderTable
encopyToLineFromLineTable
uitbreiden. Hier is de voorbeeldcode./// <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); }
De vereiste gegevens bevinden zich niet in de standaardtabel van de transactie. Er zijn echter enkele join-relaties met de standaardtabel en het veld is vereist voor de meeste regels. In dit geval vervangt u
getDocumentQueryObject
ofgetLineObject
om een query op basis van de join-relatie uit te voeren op de tabel. In het volgende voorbeeld is het veld Nu leveren geïntegreerd met de verkooporder op het regelniveau.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(); } }
In dit voorbeeld wordt een
mcrSalesLineDropShipment
-buffer gedeclareerd en wordt de query gedefinieerd ingetLineQueryObject
. De query gebruikt de relatieMCRSalesLineDropShipment.SalesLine == SalesLine.RecId
. Terwijl u in deze situatie uitbreidt, kunt ugetLineQueryObject
vervangen door uw eigen geconstrueerde queryobject. Houd echter rekening met het volgende:- Omdat de retourwaarde van de
getLineQueryObject
-methodeSysDaQueryObject
is, moet u dit object construeren met de SysDa-benadering. - Kan bestaande tabel niet verwijderen.
- Omdat de retourwaarde van de
De vereiste gegevens zijn op basis van een gecompliceerde join-relatie aan de transactietabel gekoppeld of de relatie is niet één-op-één (1:1), maar één-op-veel (1:N). In deze situatie wordt het een beetje ingewikkeld. Deze situatie is van toepassing op het voorbeeld van financiële dimensies.
In dit geval kunt u uw eigen methode implementeren om de gegevens op te halen. Hier is de voorbeeldcode in het
TaxIntegrationPurchTableDataRetrieval_Extension.xpp
-bestand.[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; } }
Stap 3. Gegevens aan de aanvraag toevoegen
Breid de copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject
- of copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine
-methode uit om gegevens aan de aanvraag toe te voegen. Hier is de voorbeeldcode in het TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension.xpp
-bestand.
[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()));
}
}
In deze code is _destination
het wrapper-object dat wordt gebruikt om de aanvraag te genereren en is _source
het TaxIntegrationLineObject
-object.
Notitie
Definieer de veldnaam die in de aanvraag wordt gebruikt als private const str. De tekenreeks moet exact gelijk zijn aan de knooppuntnaam (niet het label) die is toegevoegd in het artikel Gegevensvelden toevoegen in belastingconfiguraties.
Stel het veld in de methode copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine in op basis van de methode SetField. Het gegevenstype van de tweede parameter moet string zijn. Als het gegevenstype niet string is, converteert u het naar een tekenreeks. Als het gegevenstype X++ enumtype is, raden we u aan om de methode enum2Symbol te gebruiken om de enumwaarde naar een tekenreeks te converteren. De opsommingswaarde die in de belastingconfiguratie is toegevoegd, moet exact gelijk zijn aan opsommingsnaam. Hieronder volgt een lijst met verschillen tussen de opsommingswaarde, het label en de naam.
- De naam van de opsomming is een symbolische naam in code. enum2Symbol() kan de opsommingswaarde converteren naar de naam.
- De waarde van de opsomming is een geheel getal.
- Het label van de opsomming kan in verschillende voorkeurstalen verschillend zijn. enum2Str() kan de opsommingswaarde converteren naar het label.
Modelafhankelijkheid
Om het project te bouwen, voegt u de volgende referentiemodellen toe aan de modelafhankelijkheden:
- ApplicationPlatform
- ApplicationSuite
- Belastingengine
- Dimensies, als financiële dimensies worden gebruikt
- Andere vereiste modellen waarnaar in de code wordt verwezen
Validatie
Nadat u de vorige stappen hebt voltooid, kunt u de wijzigingen valideren.
- Ga in Finance naar Leveranciers en voeg &debug=vs%2CconfirmExit& toe aan de URL. Bijvoorbeeld
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&
. De laatste & is essentieel. - Open de pagina Inkooporder en selecteer Nieuw om een inkooporder te maken.
- Stel de waarde voor het aangepaste veld in en selecteer vervolgens Btw. Een probleemoplossingsbestand met het voorvoegsel TaxServiceTroubleshootingLog wordt automatisch gedownload. Dit bestand bevat de transactiegegevens die zijn geboekt naar de btw-berekeningsservice.
- Controleer of het toegevoegde aangepaste veld aanwezig is in de sectie JSON voor invoer voor berekening van btw-service en of de waarde juist is. Als de waarde niet correct is, controleert u de stappen in dit document nogmaals.
Voorbeeld van bestand:
===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
...
}
]
},
}
...
Bijlage
In deze bijlage ziet u de volledige voorbeeldcode voor de integratie van de financiële dimensies (Kostenplaats en Project) op regelniveau.
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());
}
}