Integrate APS with Dynamics 365 for SCM

Integrate APS with Dynamics 365 for SCM

APS means “Advanced Planning and Scheduling”. APS systems are used to optimize production processes by planning and scheduling manufacturing orders, taking into account factors like resource availability, machine capacity, and production deadlines. As a “better, smarter MRP”, APS helps companies improve efficiency, reduce lead times, and respond quickly to changes in sales or production orders.

In the German market, the 2 widely used programs are FELIOS from Inform-Software and HYDRA by MPDV. To be precise, their APS system is called FEDRA, with the HYDRA being a sister Manufacturing Execution System (MES). I had an occasion to integrate both with Dynamics 365 for SCM. By the way, a MES integration is something else and it already exists in Dynamics 365: Integrate with third-party manufacturing execution systems – Supply Chain Management | Dynamics 365 | Microsoft Learn.

High-level concept of the APS interface

An integrated APS uses data from the operations system, such as Dynamics 365 for SCM, including open sales, purchase, and production orders, current stock levels, and master data (BOMs, routes, resources, products and materials). All the data may be extracted from D365 with the standard or slightly modified entities.

The APS then generates an optimized production plan, often suggesting new raw material purchases and production orders, acting as a full-scale master planning system. However, we may neglect the planned order proposals and only import the updated production routes to align the ERP system with the APS. The process reduces to 5 steps:

  1. After the nightly D365 MRP run, the above data is exported as .TXT files, representing the day’s snapshot.
  2. APS loads these files, refreshes its database, and performs a planning run.
  3. Operators may adjust the plan in APS the day after.
  4. The final plan, with updated order and route dates, is exported as a set of inbound .TXT files.
  5. The production route file is used to update production route operations in D365 via a custom entity, since the standard one may only write to Created production orders.

The production route entity at step “5” is teased in another blog: “Import a D365 FO entity with a virtual KEY field“. The entity should skip rescheduling if the operation dates in the file match those in Dynamics 365 and only touch route operation/jobs in the following statuses: Scheduled, Released, Started.

Key step: adopt the APS route schedule

Once the route is updated with the start, end dates and times (the FELIOS system only knows the date), the “jobs” and other internal structures in Dynamics 365 must be aligned with the updated operation times. The necessary actions triggered by each line = route operation in the APS file are:
 
  1. Unlock the production order if it’s locked.
  2. Re-schedule jobs associated with the route operation, using the dates and times imprinted upon the ProdRoute record. This will update capacity reservations according to the new dates. As a positive side effect, this will adjust the start and end dates of the production order if the operation is the first or last in the route. It will also update raw material demand dates and times on related BOM lines in the production order.
  3. Lock the production order, protecting the scheduled route operation from further changes.
The below code snippet performs these key actions:
				
					    public void reschedule2jobs()
    {        
        ProdTable       prodTable = prodRoute.prodTable();
        ProdRouteJob    prodRouteJob;

        // 1. Unlock the order if locked
        if (prodTable.ProdLocked)
        {
            Args args = new Args(this);
            args.record(prodTable);
            ProdMultiLockForReschedule::construct(args).run();
        }

        // 2. Prepare a planning parameters set
        ProdParmScheduling  parm;
        parm.initParmDefault();
        parm.initFromProdParametersDim(prodTable.prodParametersDim());
        parm.CapLimited         = NoYes::No; // The APS system knows the capacity, resource, mat. availability better
        parm.MatLimited         = NoYes::No;
        parm.WrkCtrIdSched      = prodRoute.WrkCtrIdCost;

        // 3. Schedule the setup job, if any
        prodRouteJob = ProdRouteJob::findJobType(prodRoute.ProdId, prodRoute.OprNum, prodRoute.OprPriority, RouteJobType::Setup);
        if (prodRouteJob)
        {
            parm.initFromProdRouteJob(prodRouteJob);
            parm.SchedDirection = ProdSchedDirection::ForwardFromSchedDate;
            parm.SchedDate = prodRoute.FromDate;
            parm.SchedTime = prodRoute.FromTime;
            ProdUpdScheduling_Job::newParmBuffer(parm).run();
        }

        // 4. Schedule the process job
        prodRouteJob = ProdRouteJob::findJobType(prodRoute.ProdId, prodRoute.OprNum, prodRoute.OprPriority, RouteJobType::Process);
        if (prodRouteJob)
        {
            parm.initFromProdRouteJob(prodRouteJob);
            parm.SchedDirection = ProdSchedDirection::BackwardFromSchedDate;
            parm.SchedDate = prodRoute.ToDate;
            parm.SchedTime = prodRoute.ToTime;
            ProdUpdScheduling_Job::newParmBuffer(parm).run();
        }

        // 5. Lock the production order against any manual re-scheduling
        prodTable.reread();
        Args args = new Args(this);
        args.record(prodTable);
        ProdMultiLockForReschedule::construct(args).run();
    }
				
			

Remark: the highlighted line is used to change the work centre in the route operation. The APS system sends a new resource number, it is written directly into the route operation into the cost resource column. From there, the programme relays the changed resource number to the D365 planning core to reserve the capacity of the new work centre and de-reserve the capacity of the old.

Import a D365 FO entity with a virtual KEY field

Import a D365 FO entity with a virtual KEY field

I’ve got a case recently where a delimited text file had to be imported into Dynamics where a key field was a virtual one. Namely, an external APS system sent a single ProdOprNum of the record, whereas the respective ProdRoute table in Dynamics 365 for SCM has a composite key ProdId + OprNum. Pre-processing the file with an XSLT and splitting the column was not an option, because the Microsoft XSLT parser still does not implement the XSLT 2.0 standard and cannot uptake a text file.

The most important lesson to learn was that the – generally useful – guidance Use a virtual field to receive and parse an inbound field | Microsoft Learn did not work, because the system kernel started looking for the record to update BEFORE the compound key was parsed in the mapEntityToDataSource() method of the entity. It was not able to find the right record to update, which lead to an error later on.

The solution is as follows:

  • Follow the above guidance from Microsoft and create an entity with the key fields ProdId, OprNum (both mapped) and one unmapped virtual field ProdOprNum.
  • With a right mouse click, create or refresh the corresponding staging table.
  • Here comes the crucial part: remove any relation such as Staging.ProdId=Entity.ProdId && Staging.OprNum=Entity.OprNum from the staging table. You should do so every time you refresh the staging table, D365 will namely re-create it over and over again. As a side effect, the Data management will think you insert the records while in reality the record is updated, but this does not matter.
  • Implement the splitting in the initializeEntityDataSource(), select the right record to update and plant it as shown below:
				
					 public void initializeEntityDataSource(DataEntityRuntimeContext _entityCtx, DataEntityDataSourceRuntimeContext _dataSourceCtx)
{
	super(_entityCtx, _dataSourceCtx);
	
	switch (_dataSourceCtx.name()) 
	{
		case dataEntityDataSourceStr(ProdRouteSchedEntity, ProdRoute):
			ProdRoute prodRoute = _dataSourceCtx.getBuffer() as ProdRoute;
			if (prodRoute)
			    break;
			    
			this.ProdId = substr(this.ProdOprNum, 1, 10);
			this.OprNum = any2int(substr(this.ProdOprNum, 11, 14));
			
			prodRoute = ProdRoute::find(this.ProdId, this.OprNum, RouteOprPriority::Primary, true);
			_dataSourceCtx.setBuffer(prodRoute);
	}
}
				
			

Z4-Meldung an Bundesbank in D365 for Finance

Z4-Meldung an Bundesbank in D365 for Finance

The Z4-Meldung “Zahlungen im Außenwirtschaftsverkehr (abbreviated “AWV”) is a financial report that German companies must submit to the Deutsche Bundesbank as part of their external sector statistics. Companies are usually required to submit this report on a monthly basis, depending on the volume of transactions.

All German companies that engage in cross-border financial activities (e.g., loans, deposits, securities transactions) with a value above a specific threshold (currently €12500 per transaction) are required to submit this report. The worst part of it: not only  transactions with  non-EU countries must be reported, but inbound and outbound payments to EU countries as well. This is a nightmare in the tightly integrated EU economy.

Luckily, payments related to trade in goods (such as exports or imports) are not reported in the Z4-Meldung. These transactions are typically covered under other reporting mechanisms, such as the Intrastat for trade within the EU. Instead, the Z4-Meldung focuses on financial transactions unrelated to the direct exchange of goods e.g., loans, financial investments, and – most importantly – services. In particular, these transactions may be:

  • payments for services rendered for a foreign entity or paid to a foreign supplier,
  • for instance, freight costs
  • bank interests paid or received
  • cash discounts given to foreign customers or received from foreign suppliers
  • rebates received or paid
  • intercompany recharge to or from a foreign HQ or a subsidiary
  • VAT payments to a different EU tax authority. 

The Z4 report has a simple XML structure following their proprietary “XMW” schema: XMW – Elektronisches Meldewesen im XML-Format. In the regular payment process, in- and outpayments are classified with a transaction type (BELEGART) of “1” or “2”, respectively. In every transaction the foreign country is listed (ISO2), along with the transaction description and the amount in tsd. Euros. The sub-type classifier “KENNZAHL” poses a significant challenge: there are ~1000 codes in total from “A” for airfare through “S” for services till “U” for the use of software. This list from hell is published here in an inconvenient Excel file:  “Kennzahlenliste mit Belegarten”.

We are happy to share an Electronic Reporting configuration for the Z4 report in Dynamics 365 for Finance. Only the regular payment transactions (“DIKAPPOSTEN”) are supported, not shares or similar capital investments. To segregate payments by the highly detailed “KENNZAHL“, we suggest using the voucher prefix. For example, you may need to introduce different GL journal names for salary payments and commissions payments, both with distinct voucher number sequences. To avoid mistakes, we recommend separate payment methods, especially for goods and services, with every payment method to be processed in its own AP/AR payment journal.

Setup and use

  1. Download the 3 configurations here: XMW_Model_Mapping_Format_v2
  2. Install them in the following order: XMW Deutsche Bundesbank model -> XMW Mapping -> Z4 (format). Beware: the configurations are provided to you “AS IS” and with all faults and defects without warranty of any kind.
  3. Select the Z4 configuration and open Application specific parameters / Setup. You may Import a rudimentary setup from the ZIP file.
  4. With this setup you match patterns in the voucher of the bank transaction to the “KENNZAHL” codes. In some cases, you may decide to start looking for keywords in the payment description instead. This will require a slight modification of the formula in model/Messages/Message/Transactions/$ClassifierEnum. Alternatively, you may start probing for vendors or introduce a special vendor group with regards to the Z4 reporting; this is going to require a minor adjustment in the mapping and the format.
  5. Set the status of the application specific parameters to Completed when ready.
  6. Select the Z4 format in the Electronic reporting tree and hit Run.
  7. The Firmennummer is the unique end-to-end ID issued by the Bundesbank at the registration. You may also set the Extranet-ID of the user, if known. The mandatory phone, e-mail and other contact information are extracted from the employee card of the current user (the user who is executing the report i.e. you).
  8. With the Period date you select the reporting period (month). An empty period date means the current [session] month. Try to exclude bank transactions with regards to the trade of goods with the filter of Records to include, for example by the voucher number, if possible. You may also consider opening a different bank account for the regular intracommunity business. Again, try to separate payments of service, commissions etc. with distinct journals = voucher number.
  9. Hit OK and execute the report.
  10. You may also run the report in an unattended, batch mode. Configure an Electronic reporting destination such as an internal e-mail address or a SharePoint folder, schedule a monthly batch and keep the period date empty to support the rolling date. You can monitor the batch runs in Organisation administration > Electronic reporting > Electronic reporting archived jobs.

Result

The resulting file may look like this:

				
					<LIEFERUNG-AWZEL xmlns="http://www.bundesbank.de/xmw/2003-01-01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bundesbank.de/xmw/2003-01-01/BbkXmwAwzel.xsd" version="1.0" erstellzeit="2024-09-27T08:30:33+02:00" stufe="Produktion" bereich="Statistik" dateireferenz="1">
    <ABSENDER>
        <FIRMENNR>00345678</FIRMENNR>
        <NAME>ER-Consult GmbH</NAME>
        <STRASSE>Franz Lehar-Gasse 18</STRASSE>
        <PLZ>7111</PLZ>
        <ORT>Parndorf</ORT>
        <LAND>AT</LAND>
        <KONTAKT>
            <ANREDE>Miss</ANREDE>
            <VORNAME>Julia</VORNAME>
            <ZUNAME>Funderburk</ZUNAME>
            <ABTEILUNG>Sales & Marketing</ABTEILUNG>
            <TELEFON>425-555-5053</TELEFON>
            <EMAIL>julia@contoso.com</EMAIL>
            <EXTRANET-ID>EXNTESTA</EXTRANET-ID>
        </KONTAKT>
    </ABSENDER>
    <KOMMENTAR>Und ich dachte, ich bleibe unter dem Radar...</KOMMENTAR>
    <MELDUNG erstellzeit="2024-09-27T08:30:34+02:00">
        <MELDEPFLICHTIGER>
            <FIRMENNR>00345678</FIRMENNR>
            <NAME>ER-Consult GmbH</NAME>
            <STRASSE>Franz Lehar-Gasse 18</STRASSE>
            <PLZ>7111</PLZ>
            <ORT>Parndorf</ORT>
            <LAND>AT</LAND>
            <KONTAKT>
                <ANREDE>Miss</ANREDE>
                <VORNAME>Julia</VORNAME>
                <ZUNAME>Funderburk</ZUNAME>
                <ABTEILUNG>Sales & Marketing</ABTEILUNG>
                <TELEFON>425-555-5053</TELEFON>
                <EMAIL>julia@contoso.com</EMAIL>
                <EXTRANET-ID>EXNTESTA</EXTRANET-ID>
            </KONTAKT>
        </MELDEPFLICHTIGER>
        <MELDETERMIN>2024-09</MELDETERMIN>
        <VDR_04>
            <DIKAPPOSTEN belegart="2" kennzahl="523" zahlungszweck="Provision 2024">
                <BETRAG land="CN" landname="China" betragsref="APPM000063">-18</BETRAG>
            </DIKAPPOSTEN>
        </VDR_04>
    </MELDUNG>
</LIEFERUNG-AWZEL>