Tilføje datafelter i momsintegrationen ved hjælp af udvidelse
Denne artikel forklarer, hvordan du kan bruge X++ udvidelser til at tilføje datafelter i momsintegrationen. Disse felter kan udvides til momsdatamodellen for momstjenesten og bruges til bestemmelse af momskoder. Du kan finde flere oplysninger i Tilføje datafelter i momskonfigurationer.
Datamodel
Dataene i datamodellen udføres af objekter og implementeres af klasser.
Her er en liste over de vigtigste objekter:
- AxClass/TaxIntegrationDocumentObject
- AxClass/TaxIntegrationLineObject
- AxClass/TaxIntegrationTaxLineObject
I følgende illustration vises, hvordan disse objekter er relateret.
]
Et Document-objekt kan indeholde mange Line-objekter. Hvert objekt indeholder metadata til momstjenesten.
-
TaxIntegrationDocumentObject
haroriginAddress
-metadata, som indeholder oplysninger om kildeadressen, ogincludingTax
-metadata, der angiver, om linjebeløbet inkluderer moms. -
TaxIntegrationLineObject
haritemId
,quantity
ogcategoryId
som metadata.
Bemærk!
TaxIntegrationLineObject
implementerer også Charge-objekter.
Integrationsflow
Dataene i flowet manipuleres af aktiviteter.
Nøgleaktiviteter
- AxClass/TaxIntegrationCalculationActivityOnDocument
- AxClass/TaxIntegrationCurrencyExchangeActivityOnDocument
- AxClass/TaxIntegrationDataPersistenceActivityOnDocument
- AxClass/TaxIntegrationDataRetrievalActivityOnDocument
- AxClass/TaxIntegrationSettingRetrievalActivityOnDocument
Aktiviteterne køres i følgende rækkefølge:
- Hentning af indstilling
- Datahentning
- Beregningstjeneste
- Valutakurs
- Datapersistens
Udvid f.eks. Hentning af data før Beregningstjeneste.
Datahentningsaktiviteter
Datahentning-aktiviteter henter data fra databasen. Der er adaptere til forskellige transaktioner, som kan hente data fra forskellige transaktionstabeller:
- AxClass/TaxIntegrationPurchTableDataRetrieval
- AxClass/TaxIntegrationPurchParmTableDataRetrieval
- AxClass/TaxIntegrationPurchREQTableDataRetrieval
- AxClass/TaxIntegrationPurchRFQTableDataRetrieval
- AxClass/TaxIntegrationVendInvoiceInfoTableDataRetrieval
- AxClass/TaxIntegrationSalesTableDataRetrieval
- AxClass/TaxIntegrationSalesParmDataRetrieval
I disse Datahentning-aktiviteter kopieres data fra databasen til TaxIntegrationDocumentObject
og TaxIntegrationLineObject
. Da alle disse aktiviteter udvider den samme abstrakte skabelonklasse, har de almindelige metoder.
Aktiviteter for beregningstjeneste
Aktiviteten Beregningstjeneste er linket mellem momstjenesten og momsintegrationen. Denne aktivitet er ansvarlig for følgende funktioner:
- Konstruere anmodningen.
- Bogføre anmodningen til momstjenesten.
- Få svaret fra momstjenesten.
- Analysere svaret.
Et datafelt, du føjer til anmodningen, bogføres sammen med andre metadata.
Implementering af udvidelse
Dette afsnit indeholder detaljerede trin, der forklarer, hvordan du implementerer udvidelsen. Til dette formål bruges Bærer og Projekt som eksempler på de økonomiske dimensioner.
Trin 1. Tilføje datavariablen i objektklassen
Objektklassen indeholder datavariablen og henter/sætter-metoderne for dataene. Føj datafeltet til enten TaxIntegrationDocumentObject
eller TaxIntegrationLineObject
afhængigt af feltets niveau. I følgende eksempel bruges linjeniveauet, og filnavnet er TaxIntegrationLineObject_Extension.xpp
.
Bemærk!
Hvis det datafelt, du tilføjer, er på dokumentniveau, skal du ændre filnavnet til 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;
}
}
Bærer og Projekt tilføjes som private variabler. Opret henter- og sættermetoder for disse datafelter for at manipulere dataene.
Trin 2. Hente data fra databasen
Angiv transaktionen, og udvid de relevante adapterklasser for at hente dataene. Hvis du f.eks. bruger transaktionen Indkøbsordre, skal du udvide TaxIntegrationPurchTableDataRetrieval
og TaxIntegrationVendInvoiceInfoTableDataRetrieval
.
Bemærk!
TaxIntegrationPurchParmTableDataRetrieval
er nedarvet fra TaxIntegrationPurchTableDataRetrieval
. Den må ikke ændres, medmindre logikken i purchTable
- og purchParmTable
-tabellerne er forskellig.
Hvis datafeltet skal tilføjes for alle transaktioner, skal du udvide alle DataRetrieval
-klasser.
Da alle aktiviteter for Datahentning udvider den samme skabelonklasse, er klassestrukturerne, variablerne og metoderne ens.
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);
}
I følgende eksempel vises den grundlæggende struktur, når tabellen PurchTable
bruges.
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 metoden CopyToDocument
kaldes, findes this.purchTable
-bufferen allerede. Formålet med denne metode er at kopiere alle de påkrævede data fra this.purchTable
til objektet document
ved hjælp af den sættermetode, der blev oprettet i DocumentObject
-klassen.
På samme måde findes der allerede en this.purchLine
-buffer i CopyToLine
-metoden. Formålet med denne metode er at kopiere alle de påkrævede data fra this.purchLine
til objektet _line
ved hjælp af den sættermetode, der blev oprettet i LineObject
-klassen.
Den mest ligetil fremgangsmåde er at udvide metoderne CopyToDocument
og CopyToLine
. Det anbefales dog, at du prøver metoderne copyToDocumentFromHeaderTable
og copyToLineFromLineTable
først. Hvis de ikke virker efter behov, skal du implementere din egen metode og kalde den i CopyToDocument
og CopyToLine
. Der er tre almindelige situationer, hvor du kan bruge denne metode:
Det påkrævede felt er i
PurchTable
ellerPurchLine
. I denne situation kan du udvidecopyToDocumentFromHeaderTable
ogcopyToLineFromLineTable
. Her er eksempelkoden./// <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 påkrævede data findes ikke i standardtabellen for transaktionen. Der findes dog visse join-relationer til standardtabellen, og feltet skal udfyldes på de fleste linjer. I denne situation skal du erstatte
getDocumentQueryObject
ellergetLineObject
for at forespørge på tabellen ved hjælp af en join-relation. I følgende eksempel er feltet Levér nu integreret med salgsordren på linjeniveau.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 dette eksempel erklæres en
mcrSalesLineDropShipment
-buffer, og forespørgslen defineres igetLineQueryObject
. Forespørgslen bruger relationenMCRSalesLineDropShipment.SalesLine == SalesLine.RecId
. Når du udvider i denne situation, kan du erstattegetLineQueryObject
med dit eget konstruerede forespørgselsobjekt. Men bemærk følgende punkter:- Da returværdien af metoden
getLineQueryObject
erSysDaQueryObject
, skal du opbygge dette objekt ved hjælp af metoden SysDa. - Den eksisterende tabel kan ikke fjernes.
- Da returværdien af metoden
De påkrævede data er relateret til transaktionstabellen af en kompliceret join-relation, eller relationen er ikke en til en (1:1), men en til mange (1:N). I denne situation bliver det lidt kompliceret. Denne situation gælder for eksemplet med økonomiske dimensioner.
I denne situation kan du implementere din egen metode for at hente dataene. Her er eksempelkoden 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; } }
Trin 3. Føje data til anmodningen
Udvid metoden copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject
eller copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine
for at føje data til anmodningen. Her er eksempelkoden 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 denne kode er _destination
det ombryderobjekt, der bruges til at generere anmodningen, og _source
er TaxIntegrationLineObject
-objektet.
Bemærk!
Definer det feltnavn, der bruges i anmodningsformularen, som privat konstruktionsstreng. Strengen skal være nøjagtig den samme som det node-navn (ikke etiket), der tilføjes i artiklen Tilføje datafelter i momskonfigurationer.
Angiv feltet i metoden copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine ved hjælp af metoden SetField. Datatypen for den anden parameter skal være streng. Hvis datatypen ikke er streng, skal du konvertere den. Hvis datatypen er X++ enumtype, anbefales det, at du bruger enum2Symbol til at konvertere enum-værdien til en streng. Den enum-værdi, der tilføjes i momskonfigurationen, skal være nøjagtig den samme som enum-navnet. Nedenfor vises en liste over forskelle mellem enumværdi, label og navn.
- Navnet på enum er navnet på en kode, der angiver navnet på en kode. enum2Symbol() kan bruges til at konvertere enumværdi til navnet.
- Enum-værdien er et heltal.
- Labelen i enum kan være forskellig på tværs af foretrukne sprog. enum2Str() kan bruges til at konvertere enumværdi til etiket.
Modelafhængighed
Hvis projektet skal opbygges korrekt, skal du føje følgende referencemodeller til modelafhængighederne:
- ApplicationPlatform
- ApplicationSuite
- Momsprogram
- Dimensioner, hvis der bruges økonomisk dimension
- Der refereres til andre nødvendige modeller i koden
Validering
Når du har fuldført de foregående trin, kan du validere ændringerne.
- I Finans skal du gå til Kreditor og føje &debug=vs%2CconfirmExit& til URL-adressen. For eksempel
https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&
. Det sidste & er afgørende. - Åbn siden Indkøbsordre, og vælg Ny for at oprette en indkøbsordre.
- Angiv værdien for det brugerdefinerede felt, og vælg derefter Moms. En fejlfindingsfil med præfikset TaxServiceTroubleshootingLog downloades automatisk. Denne fil indeholder de transaktionsoplysninger, der er bogført til Momsberegningstjeneste.
- Kontrollér, om det tilpassede felt, der er tilføjet, findes i afsnittet Momsberegningstjenesteinput i JSON, og om værdien er korrekt. Hvis værdien ikke er korrekt, skal du dobbelttjekke trinnene i dette dokument.
Fileksempel:
===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
...
}
]
},
}
...
Appendiks
Dette appendiks viser den komplette eksempelkode til integration af de økonomiske dimensioner Bærer og Projekt på linjeniveau.
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());
}
}