Jaa


Vero-integroinnin tietokenttien lisääminen laajennuksen avulla

Tässä artikkelissa kerrotaan, kuinka X++ -laajennuksia käytetään tietokenttien lisäämiseen verointegrointiin. Nämä kentät voidaan laajentaa veropalvelun verotietomalliin, ja niitä voidaan käyttää verokoodien määrittämiseen. Lisätietoja on kohdassa Tietokenttien lisääminen verokonfiguraatioihin.

Tietomalli

Tietomallin tietoja säilyttävät objektit ja luokat toteuttavat ne.

Seuraavassa on luettelo tärkeimmistä objekteista:

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

Seuraavassa kuvassa kerrotaan, miten nämä objektit liittyvät toisiinsa.

Tietomallin objektisuhde. ]

Document-objekti voi sisältää useita Line-objekteja. Kukin objekti sisältää veropalvelun metatietoja.

  • TaxIntegrationDocumentObject-kohteessa on originAddress-metatieto, jotka sisältävät tietoja lähde-osoitteesta ja includingTax-metatieto, joka ilmaisee, sisältääkö rivisumma arvonlisäveron.
  • TaxIntegrationLineObject-kohteella on itemId-, quantity- ja categoryId-metatiedot.

Muistiinpano

TaxIntegrationLineObject toteuttaa myös Charge-objektit.

Integroinnin työnkulku

Toiminnot manipuloivat työnkulun tietoja.

Tärkeimmät toiminnot

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

Tehtävät suoritetaan seuraavassa järjestyksessä:

  1. Noutamisen määrittäminen
  2. Tietojen noutaminen
  3. Laskentapalvelu
  4. Valuutan vaihto
  5. Tietojen säilyvyys

Esimerkiksi laajenna Tietojen nouto ennen kuin Laskentapalvelu.

Tietojen noutamisen tehtävät

Tietojen noutamisen tehtävät noutavat tietoja tietokannasta. Eri tapahtumien sovittimet ovat käytettävissä tietojen hakemiseen eri tapahtumatauluista:

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

Näissä tietojen noutamisen tehtävissä tiedot kopioidaan tietokannasta TaxIntegrationDocumentObject- ja TaxIntegrationLineObject-kohteeseen. Koska kaikki nämä tehtävät laajentavat saman abstraktin malliluokan, niillä on yhteisiä metodeita.

Laskentapalvelun tehtävät

Laskentapalvelun tehtävä on veropalvelun ja verointegraaton välinen linkki. Tämä tehtävä on vastuussa seuraavista toiminnoista:

  1. Muodosta pyyntö.
  2. Lähetä pyyntö veropalveluun.
  3. Hae vastaus veropalvelusta.
  4. Jäsennä vastaus.

Pyyntöön lisättävä tietokenttä lähetetään yhdessä muiden metatietojen kanssa.

Laajennuksen toteutus

Tässä osassa on yksityiskohtaisia ohjeita laajennuksen toteuttamisesta. Esimerkkejä ovat taloushallinnon dimensiot Kustannuspaikka ja Projekti.

Vaihe 1. Tietomuuttujan lisääminen objektiluokkaan

Objektiluokka sisältää tietomuuttujan sekä tietojen getter-/setter-metodit. Lisää tietokenttä joko TaxIntegrationDocumentObject- tai TaxIntegrationLineObject-objektiin kentän tason mukaan. Seuraavassa esimerkissä käytetään rivitasoa, ja tiedoston nimi on TaxIntegrationLineObject_Extension.xpp.

Muistiinpano

Jos lisätty tietokenttä on tiedostotasolla, muuta tiedoston nimeksi 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;
    }
}

Kustannuspaikka ja Projekti lisätään yksityisinä muuttujina. Voit käsitellä tietoja luomalla getter- ja setter-metodit näille tietokentille.

Vaihe 2. Tietojen noutaminen tietokannasta

Määritä tapahtuma ja nouda tiedot laajentamalla asianmukaiset sovitinluokat. Jos käytät esimerkiksi Ostotilaus-tapahtumaa, on laajennettava TaxIntegrationPurchTableDataRetrieval- ja TaxIntegrationVendInvoiceInfoTableDataRetrieval-luokkia.

Muistiinpano

TaxIntegrationPurchParmTableDataRetrieval on peritty TaxIntegrationPurchTableDataRetrieval-luokasta. Sitä ei saa muuttaa, ellei purchTable- ja purchParmTable-taulujen logiikka ole erilainen.

Jos tietokenttä on lisättävä kaikille tapahtumille, laajenna kaikki DataRetrieval-luokat.

Koska Tietojen nouto -tehtävät laajentavat samaa malliluokkaa, luokkarakenteet, muuttujat ja metodit ovat samankaltaisia.

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);
}

Seuraavassa esimerkissä esitetään perusrakenne, kun PurchTable-taulua käytetään.

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)
    ...
}

Kun CopyToDocument-metodia kutsutaan, this.purchTable-puskuri on jo olemassa. Tämän metodin avulla voit kopioida kaikki tarvittavat tiedot this.purchTable-kohteesta document-objektiin käyttämällä DocumentObject-luokassa luotua setter-metodia.

Samoin this.purchLine-puskuri on jo olemassa CopyToLine-metodissa. Tämän metodin avulla voit kopioida kaikki tarvittavat tiedot this.purchLine-kohteesta _line-objektiin käyttämällä LineObject-luokassa luotua setter-metodia.

Kaikkein suoraviivaisin tapa on laajentaa CopyToDocument- ja CopyToLine-metodeja. On kuitenkin suositeltavaa kokeilla ensin copyToDocumentFromHeaderTable- ja copyToLineFromLineTable-metodeja. Jos ne eivät toimi niin kuin on tarpeen, toteuta oma metodi ja kutsu sitä CopyToDocument- ja CopyToLine-metodeissa. Tätä lähestymistapaa voidaan käyttää kolmessa yleisessä tilanteessa:

  • Pakollinen kenttä on kohteessa PurchTable tai PurchLine. Tässä tapauksessa voit laajentaa kohteita copyToDocumentFromHeaderTable ja copyToLineFromLineTable. Tässä on esimerkkikoodi.

    /// <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);
    }
    
  • Vaadittavat tiedot eivät ole tapahtuman oletustaulussa. Oletustauluun liittyy kuitenkin joitakin liitossuhteita, ja useimmilla riveillä kenttä on pakollinen. Tässä tapauksessa voit korvata getDocumentQueryObject- tai getLineObject-metodin kyselläksesi taulusta liitossuhteen avulla. Seuraavassa esimerkissä Toimita nyt -kenttä integroidaan rivitasolla myyntitilauksen kanssa.

    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();
        }
    }
    

    Tässä esimerkissä mcrSalesLineDropShipment-puskurit määritetään ja kysely määritetään kohdassa getLineQueryObject. Kysely käyttää suhdetta MCRSalesLineDropShipment.SalesLine == SalesLine.RecId. Kun olet laajentamassa tässä tilanteessa, voit korvata getLineQueryObject-kohteen omalla luodulla kyselyobjektilla. Ota kuitenkin seuraavat seikat huomioon:

    • Koska getLineQueryObject-metodin palautusarvo on SysDaQueryObject, sinun on muodostettava tämä objekti SysDa-lähestymistavan avulla.
    • Aiemmin luotua taulua ei voi poistaa.
  • Vaadittavat tiedot liittyvät tapahtumatauluun monimutkaisella liitossuhteella, tai suhde ei ole yksi yhteen (1:1) vaan yksi moneen (1:N). Tässä tapauksessa tilanne on hieman monimutkaisempi. Tämä tilanne koskee taloushallinnon dimensioiden esimerkkiä.

    Tässä tapauksessa voit toteuttaa oman metodin tietojen hakemiseksi. Tässä on esimerkkikoodi TaxIntegrationPurchTableDataRetrieval_Extension.xpp-tiedostossa.

    [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;
        }
    }
    

Vaihe 3. Tietojen lisääminen pyyntöön

Laajenna copyToTaxableDocumentHeaderWrapperFromTaxIntegrationDocumentObject- tai copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine-metodi lisätäksesi tiedot pyyntöön. Tässä on esimerkkikoodi TaxIntegrationCalculationActivityOnDocument_CalculationService_Extension.xpp-tiedostossa.

[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()));
    }
}

Tässä koodissa _destination on paketointiobjekti, jota käytetään pyynnön luonnissa ja _source on TaxIntegrationLineObject-objekti.

Muistiinpano

Määritä kentän nimi, jota käytetään pyynnössä nimellä private const str. Merkkijonon on oltava täsmälleen sama kuin solmun nimi (ei selite), joka lisättiin artikkelissa Tietokenttien lisääminen veromäärityksiin.

Määritä kenttä menetelmässä copyToTaxableDocumentLineWrapperFromTaxIntegrationLineObjectByLine käyttämällä SetField-menetelmää. Toisen parametrin tietotyypin on oltava merkkijono. Jos tietotyyppi ei ole merkkijono, muunna se merkkijonoksi. Jos tietotyyppi on X++ -kielen luettelointityyppi, suosittelemme luettelointiarvon muuntamiseen merkkijonoksi enum2Symbol-menetelmää. Veromäärityksessä lisätyn luettelointiarvon on täsmättävä täysin luetteloinnin nimen kanssa. Seuraavassa on luettelo luettelointiarvon, selitteen ja nimen eroista.

  • Luetteloinnin nimi on symbolinen nimi koodissa. Menetelmä enum2Symbol() voi muuntaa luetteloinnin sen nimeksi.
  • Luetteloinnin arvo on kokonaisluku.
  • Luetteloinnin tunniste voi vaihdella ensisijaisten kielten välillä. Menetelmä enum2Str() voi muuntaa luetteloinnin sen selitteeksi.

Malliriippuvuus

Kokoa projekti onnistuneesti lisäämällä malliriippuvuuksiin seuraavat viitemallit:

  • ApplicationPlatform
  • ApplicationSuite
  • Veromoduuli
  • Dimensiot, jos taloushallinnon dimensioita käytetään
  • Muut tarpeelliset mallit, joihin viitataan koodissa

Oikeellisuustarkistus

Kun olet suorittanut edelliset vaiheet, voit vahvistaa muutoksesi.

  1. Siirry Financessa kohtaan Ostoreskontra ja lisää URL-osoitteeseen &debug=vs%2CconfirmExit&. Esimerkiksi https://usnconeboxax1aos.cloud.onebox.dynamics.com/?cmp=DEMF&mi=PurchTableListPage&debug=vs%2CconfirmExit&. Lopussa on oltava &.
  2. Voit luoda ostotilauksen avaamalla sivun Ostotilaus ja valitsemalla Uusi.
  3. Määritä mukautetun kentän arvo ja valitse sitten Arvonlisävero. Vianmääritystiedosto etuliitteellä TaxServiceTroubleshootingLog latautuu automaattisesti. Tämä tiedosto sisältää veronlaskentapalveluun kirjatut tiedot.
  4. Tarkista, että lisätty mukautettu kenttä näkyy Veropalvelun laskennan syöte-JSON -osassa ja että sen arvo on oikein. Jos arvo ei ole oikein, käy tämän asiakirjan vaiheet läpi uudelleen.

Tiedostoesimerkki:

===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
        ...
      }
    ]
  },
}
...

Liite

Tästä liitteestä ilmenee taloushallinnon dimensioiden (Kustannuspaikka ja Projekti) integroinnin koko esimerkkikoodi rivitasolla.

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());
    }
}