Lägga till datafält i momsintegreringen genom att använda tillägg
I den här artikeln beskrivs hur du använder X++-tillägg för att lägga till datafält i momsintegreringen. De här fälten kan utökas till momsdatamodellen för momstjänsten och användas för att bestämma momskoder. Mer information finns i Lägg till datafält i momskonfigurationer.
Datamodell
Data i datamodellen transporteras av objekt och implementeras av klasser.
Här är en lista över huvudobjekt:
- AxClass/TaxIntegrationDocumentObject
- AxClass/TaxIntegrationLineObject
- AxClass/TaxIntegrationTaxLineObject
I följande bild visas hur de här objekten är relaterade.
]
Ett Dokument objekt kan innehålla många Rad objekt. Varje objekt innehåller metadata för momstjänsten.
-
TaxIntegrationDocumentObject
haroriginAddress
metadata, som innehåller information om källadressen ochincludingTax
metadata, som anger om radbeloppet inkluderar moms. -
TaxIntegrationLineObject
haritemId
,quantity
ochcategoryId
metadata.
Notering
TaxIntegrationLineObject
implementerar även Avgift objekt.
Integreringsflöde
Data i flödet hanteras av aktiviteter.
Huvudaktiviteter
- AxClass/TaxIntegrationCalculationActivityOnDocument
- AxClass/TaxIntegrationCurrencyExchangeActivityOnDocument
- AxClass/TaxIntegrationDataPersistenceActivityOnDocument
- AxClass/TaxIntegrationDataRetrievalActivityOnDocument
- AxClass/TaxIntegrationSettingRetrievalActivityOnDocument
Aktiviteter körs i följande ordning:
- Ställa in hämtning
- Datahämtning
- Beräkningstjänst
- Valutakurs
- Databeständighet
Du kan till exempel utöka datahämtning före beräkningstjänsten.
Datahämtningsaktiviteter
Datahämtning aktiviteter hämtar data från databasen. Adaptrar för olika transaktioner är tillgängliga för att hämta data från olika transaktionsregister:
- AxClass/TaxIntegrationPurchTableDataRetrieval
- AxClass/TaxIntegrationPurchParmTableDataRetrieval
- AxClass/TaxIntegrationPurchREQTableDataRetrieval
- AxClass/TaxIntegrationPurchRFQTableDataRetrieval
- AxClass/TaxIntegrationVendInvoiceInfoTableDataRetrieval
- AxClass/TaxIntegrationSalesTableDataRetrieval
- AxClass/TaxIntegrationSalesParmDataRetrieval
I dessa datahämtning aktiviteter kopieras data från databasen till TaxIntegrationDocumentObject
och TaxIntegrationLineObject
. Eftersom alla dessa aktiviteter utökar samma abstrakta mallklass har de vanliga metoder.
Beräkning av serviceaktiviteter
Aktiviteten Beräkningstjänst är länken mellan momstjänsten och momsintegrationen. Den här aktiviteten ansvarar för följande funktioner:
- Konstruera begäran.
- Bokför begäran till momstjänsten.
- Få svaret från momstjänsten.
- Analysera svaret.
Ett datafält som du lägger till på denna begäran bokförs tillsammans med andra metadata.
Implementering av filnamnstillägg
I det här avsnittet finns detaljerade steg som förklarar hur du implementerar filnamnstillägget. Det använder Kostnadsställe och Projekt ekonomiska dimensioner som exempel.
Steg 1. Lägg till datavariabeln i objektklassen
Objektklassen innehåller datavariabeln och getter-/ setter-metoderna för data. Lägg till datafältet till antingen TaxIntegrationDocumentObject
eller TaxIntegrationLineObject
, beroende på vilken nivå fältet har. I följande exempel används radnivån och filnamnet är TaxIntegrationLineObject_Extension.xpp
.
Notering
Om datafältet som du lägger till är på dokumentnivå, ändrar du filnamnet till 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;
}
}
Kostnadsställe och Projekt läggs till som privata variabler. Skapa getter- och setter-metoder för dessa datafält för att manipulera data.
Steg 2. Hämta data från databasen
Ange transaktionen och utöka lämpliga adapterklasser för att hämta data. Om du till exempel använder en inköpsorder transaktion, måste du utöka TaxIntegrationPurchTableDataRetrieval
och TaxIntegrationVendInvoiceInfoTableDataRetrieval
.
Notering
TaxIntegrationPurchParmTableDataRetrieval
ärvs från TaxIntegrationPurchTableDataRetrieval
. Det bör inte ändras om inte logiken och tabellerna purchTable
och purchParmTable
skiljer sig åt.
Om datafältet ska läggas till för alla transaktioner, förläng alla DataRetrieval
klasser.
Eftersom alla Datahämtning utöka samma mallklass, klasstrukturer, variabler och metoder liknar varandra.
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);
}
Följande exempel visar basstrukturen när registret PurchTable
används.
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)
...
}
När CopyToDocument
metoden anropas finns this.purchTable
bufferten redan. Syftet med denna metod är att kopiera alla nödvändiga data från this.purchTable
till document
genom att använda settermetoden som skapades i DocumentObject
klassen.
På samma sätt finns det redan en this.purchLine
buffert i CopyToLine
-metoden. Syftet med denna metod är att kopiera alla nödvändiga data från this.purchLine
till _line
genom att använda settermetoden som skapades i LineObject
klassen.
Det enklaste tillvägagångssättet är att utvidga CopyToDocument
och CopyToLine
metods. Vi rekommenderar dock att du först försöker copyToDocumentFromHeaderTable
och copyToLineFromLineTable
metoderna. Om de inte fungerar som du behöver, implementera din egen metod och anropa den CopyToDocument
och CopyToLine
. Det finns tre vanliga situationer där du kan använda det här sättet:
Det obligatoriska fältet är i
PurchTable
ellerPurchLine
. I denna situation kan du utökacopyToDocumentFromHeaderTable
ochcopyToLineFromLineTable
. Här är exempelkoden./// <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); }
Den obligatoriska informationen finns inte i standardregistret för transaktionen. Det finns dock vissa kopplingsrelationer till standardregistret, och fältet är obligatoriskt på de flesta rader. I den här situationen, byt ut
getDocumentQueryObject
ellergetLineObject
att fråga tabellen efter anslutningsförhållande. I följande exempel är fältet Leverera nu integreras med försäljningsordern på radnivå.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(); } }
I det här exemplet
mcrSalesLineDropShipment
buffert och frågan definieras igetLineQueryObject
. Frågan använder relationenMCRSalesLineDropShipment.SalesLine == SalesLine.RecId
. Medan du utökar i den här situationen kan du ersättagetLineQueryObject
med ditt eget uppbyggda frågeobjekt. Observera dock följande poäng:- Eftersom returvärdet för metoden
getLineQueryObject
metoden ärSysDaQueryObject
, du måste konstruera det här objektet med SysDa-metoden. - Det går inte att ta bort register som finns.
- Eftersom returvärdet för metoden
De data som krävs är relaterade till transaktionsregistret av en komplicerad sammankopplingsrelation, eller relationen s.k. ett till ett (1:1) men ett till många (1:N). I den här situationen blir det lite komplicerat. Situationen gäller exemplet på ekonomiska dimensioner.
I den situationen kan du implementera egna metoder för att hämta data. Här är exempelkoden i
TaxIntegrationPurchTableDataRetrieval_Extension.xpp
filen.[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; } }
Steg 3. Lägga till data i en begäran
Utöka eller copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject
eller copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine
metod för att lägga till data i begäran. Här är exempelkoden i TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension.xpp
filen.
[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()));
}
}
I den här koden _destination
är omslagsobjektet som används för att generera förfrågan och _source
är TaxIntegrationLineObject
objektet.
Notering
Definiera fältnamn som används i förfrågning som privat const str. Strängen ska vara exakt samma som nodnamnet (inte etiketten) som läggs till i lägg till datafält i skattekonfigurationer.
Ange fältet i metoden copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine genom att använda metoden SetField. Datatypen för den andra parametern bör vara sträng. Om datatypen inte är sträng, konverterar du sträng. Om datatypen är X++ enum-typ rekommenderar vi att du använder en enum2Symbol-metod för att konvertera enum-värdet till en sträng. Uppräkningsvärdet som läggs till i skattekonfigurationen ska vara exakt samma som uppräkningsnamnet. Här följer en lista över differenser mellan uppräkningsvärde, etikett och namn.
- Namnet på uppräkning är ett symboliskt namn i kod. enum2Symbol() kan konvertera enum-värdet till dess namn.
- Värdet på uppräkningen är heltal.
- Etiketten för uppräkningen kan skilja sig åt mellan föredragna språk. enum2Str() kan konvertera enum-värdet till dess etikett.
Modellberoende
Om du vill kunna bygga projektet lägger du till följande referensmodeller till modellberoendena:
- ApplicationPlatform
- ApplicationSuite
- Skattemotor
- Dimensioner, om ekonomiska dimensioner används
- Andra nödvändiga modeller som refereras i koden
Validering
När du har slutfört föregående steg kan du validera ändringarna.
- I Ekonomi, gå till Leverantörsreskontra och lägg till &debug=vs%2CconfirmExit& till URL. Till exempel
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&
. Det sista & är viktig. - Öppna sidan inköpsorder och välj Ny för att skapa en inköpsorder.
- Ställ in värdet för det anpassade fältet och välj sedan Moms. En felsökningsfil med prefixet TaxServiceTroubleshootingLog hämtas automatiskt. Den här filen innehåller transaktionsinformationen som bokförs i momsberäkningstjänsten.
- Kontrollera om det anpassade fältet som har lagts till finns i avsnittet Indata JSON för beräkning av momstjänst och om värdet är korrekt. Om värdet inte stämmer kan du kontrollera stegen i det här dokumentet.
Filexempel:
===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
...
}
]
},
}
...
Bilaga
Denna visar den fullständiga exempelkoden för integrering av ekonomiska dimensioner Kostnadsställe och Projekt på radnivå.
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());
}
}