Difference between the Inventory Adjustment journal, movement Journal, transfer Journal in AX 2012

Difference between the Inventory Adjustment journal, movement Journal, transfer Journal in AX 2012

Song Nghia - Business Analyst


Inventory Adjustment:

When you use the inventory adjustment journal, you can add cost to an item when you move the inventory. The additional cost is posted automatically to a particular general ledger account according to the Item group posting profile setup.

Movement Journal:

When you use the inventory movement journal, you can add cost to an item when you move the inventory.  You would have to allocate the additional cost to a particular general ledger account manually in this case.

Transfer Journal:

The inventory transfer journal can be used to transfer items from one warehouse to another within the company without associating any cost implications to it. This journal is the only one out of the three, where you would have to mention both the FROM and TO inventory dimensions like site and warehouse. 


Thanks for: https://gvkkishore.wordpress.com/2016/07/19/difference-between-the-inventory-adjustment-journal-movement-journal-transfer-journal-in-ax-2012/

Production Orders are cleaned up / deleted

 Production Orders are cleaned up / deleted



Song Nghia - Business Analyst

This will get the job done but needs more testing. After Production Orders are cleaned up / deleted, this will clean up the ProdParm* related tables.

static void Job39(Args _args)
{
ProdParmBOMCalc prodParmBomCalc;
ProdParmCostEstimation ProdParmCostEstimation;
ProdParmHistoricalCost ProdParmHistoricalCost;
ProdParmRelease ProdParmRelease;
ProdParmReportFinished ProdParmReportFinished;
ProdParmScheduling ProdParmScheduling;
ProdParmSplit ProdParmSplit;
ProdParmStartUp ProdParmStartUp;
ProdParmStatusDecrease ProdParmStatusDecrease;
ProdParmUpdate ProdParmUpdate;
ProdTable prodTable;

delete_from prodParmBomCalc
notexists join ProdTable
where prodTable.ProdId == prodParmBomCalc.ProdId;

delete_from ProdParmCostEstimation
notexists join ProdTable
where prodTable.ProdId == ProdParmCostEstimation.ProdId;

delete_from ProdParmHistoricalCost
notexists join ProdTable
where prodTable.ProdId == ProdParmHistoricalCost.ProdId;

delete_from ProdParmRelease
notexists join ProdTable
where prodTable.ProdId == ProdParmRelease.ProdId;

delete_from ProdParmReportFinished
notexists join ProdTable
where prodTable.ProdId == ProdParmReportFinished.ProdId;

delete_from ProdParmScheduling
notexists join ProdTable
where prodTable.ProdId == ProdParmScheduling.ProdId;

delete_from ProdParmSplit
notexists join ProdTable
where prodTable.ProdId == ProdParmSplit.ProdId;

delete_from ProdParmStartUp
notexists join ProdTable
where prodTable.ProdId == ProdParmStartUp.ProdId;

delete_from ProdParmStatusDecrease
notexists join ProdTable
where prodTable.ProdId == ProdParmStatusDecrease.ProdId;

}
Clean Up Production Orders

Clean Up Production Orders

Song Nghia - Business Analyst

Let's Clean Up Ended Production Orders!

If you need to clean up your production orders and their related journals as well as related info, then you'll want to look at the clean up functionality for production orders. If you want to clean up just the journals, please take a look at this.

AX 2012

Similar to the Production Journals clean up functionality, this will remove production journals but also more related into. This is also easier to use as its date driven rather than status driven so if you want to use it to clear our a closed year, this is a great option.

This will delete ended production orders (ProdTable) ended on or before the date you select with business logic and alerts disabled. Next, it will delete from the following:

Production BOM (ProdBOM)
Production Route (ProdRoute)
Route Jobs (ProdRouteJob)
Capactity Reservations (wrkCtrCapRes)
Calculations (prodCalcTrans)
Production Journal Table (prodJournalTable)
BOM journal Transactions (prodJournalBOM)
Production Journal Transactions (prodJournalProd)
Routing journal transactions (prodJournalRoute)

This will very effectively remove any production order and its related records but it leaves all inventory and financial tracking in place.

Dynamics 365 for Finance and Supply Chain

Functionally, this is just like the AX 2012 version and can be found in the same place. However, similar the cleanup functionality for journals, this has an updated method for deleting Production Orders (ProdTable) records. Below are the 2 methods of most interest. They create a query using methods to wrap direct SQL rather than use a buffer with a row or set based operation ( like while select or delete_from )

    private SysDaQueryObject buildDeleteProductionOrdersQuery(ProdTable _prodTable)
    {
        SysDaQueryObject prodTableDeleteQuery = new SysDaQueryObject(_prodTable);
        prodTableDeleteQuery.whereClause(this.buildDeleteProductionOrdersWhereClause(_prodTable));
        
        return prodTableDeleteQuery;
    }

    protected SysDaBinaryExpression buildDeleteProductionOrdersWhereClause(ProdTable _prodTable)
    {
        return new SysDaAndExpression(
            new SysDaEqualsExpression(new SysDaFieldExpression(_prodTable, fieldStr(ProdTable, ProdStatus)), new SysDaValueExpression(ProdStatus::Completed)),
            new SysDaLessThanOrEqualsExpression(new SysDaFieldExpression(_prodTable, fieldStr(ProdTable, RealDate)), new SysDaValueExpression(realDateBefore)));
    }


Thanks for: http://www.atomicax.com/article/clean-production-orders

D365FO Debugging

D365FO Debugging

Song Nghia -  Analyst, IT Business Systems

Follow the following steps to allow debugging in Dynamics 365 for Finance and Operations

  • On Visual studio go to Dynamics 365 > Options > Debugging

  • On Visual studio go to Debug > Options > Debugging > Symbols
  • Set your project as startup project by right clicking on project and selecting 'Set as StartUp Project'
  • Set the element which you want to debug as startup object by right clicking on element and selecting 'Set as StartUp Object'
  • Set a breakpoint in first line of code snippet which you want to debug.
  • You now have two options:

    Option 1:
    • Start the project directly from Visual Studio by clicking on 'Start' button on Action pane or use shortcut F5. It will build the project and run on a new browser instance.
         Option 2:
    • Build your project (shortcut Alt+b,u)
    • On internet explorer login to your environment, in case if already logged in refresh the tab.
    • Attach 'w3wp.exe' process by clicking on Debug > Attach to process... (or use shortcut ctrl+alt+p). On the opened form check the 'Show processed from all users'  and select 'w3wp.exe' from the list and click Attach button. It will attach services need for debugging.




Your breakpoint will hit once you run your target form/class/report. :)

Source: 
https://microsoftdynamix.blogspot.com/2019/01/d365fo-debugging.html
How-to send PDF document to a printer from X++

How-to send PDF document to a printer from X++


 Image

In AX 2012 it could be done with 2 lines of X++ code:

#WinAPI
WinApi::shellExecute(fileName,'', fileFolder, #ShellExePrint);

Now it’s not that easy. Files are in Azure BLOB storage instead of a folder, printers are in a local network that is not accessible from the cloud and WinApi is deprecated. To print standard reports Document Routing Agent should be installed. We need it to send PDF directly from the system as well.

Let’s say we want to print PDF file saved in document attachments (DocuRef). First, we need to check if a printer selected by a user is active and get printer details:

SrsReportPrinterContract activePrinterContract = SrsReportRunUtil::getActivePrinter(printerName);
if (!activePrinterContract.parmPrinterName())
{
    return;
}

printerName value should come from somewhere, for example a field on a form. In this case we can use SrsReportRunUtil::lookupPrinters() method to add a lookup with all available printers:

public static void lookupPrinters(FormStringControl _ctrl)
{
    SrsReportRunUtil::lookupPrinters(_ctrl);
}

Then we need to create print destination settings:

SRSPrintDestinationSettings srsPrintDestinationSettings = new SRSPrintDestinationSettings();
srsPrintDestinationSettings.printMediumType(SRSPrintMediumType::Printer);
srsPrintDestinationSettings.fileFormat(SRSReportFileFormat::PDF);
srsPrintDestinationSettings.printerName(activePrinterContract.parmPrinterName());
srsPrintDestinationSettings.printerWhere(activePrinterContract.parmPrinterPath());
srsPrintDestinationSettings.numberOfCopies(1);
srsPrintDestinationSettings.collate(false);
srsPrintDestinationSettings.printOnBothSides(SRSReportDuplexPrintingSetting::None);
srsPrintDestinationSettings.printAllPages(true);
srsPrintDestinationSettings.fromPage(0);
srsPrintDestinationSettings.toPage(0);

srsPrintDestinationSettings.printerWhere() is important bit here. This parameter accepts path to a printer. It’s possible to install multiple DRA on different servers and path for DRA installed on a print server could be different to path for DRA installed on any other server, so watch for this.

To send document we need to read file into a memory stream:

container fileCon = DocumentManagement::getAttachmentAsContainer(_docuRef);
var stream = Binary::constructFromContainer(fileCon).getMemoryStream();

And create new DocumentContract:

DocumentContract documentContract = DocumentContractFactory::Instance.Create(DocumentContractType::Pdf);

documentContract.Name = _docuRef.Name;
documentContract.Contents =  stream.ToArray();
documentContract.TargetType = TargetType::Printer;
documentContract.Settings = srsPrintDestinationSettings.printerPageSettings();
documentContract.ActivityID = newGuid();

If you send multiple documents ActivityID should be initialized for each document.  Don’t forget to add a reference to Microsoft.Dynamics.AX.Framework.DocumentContract:

using Microsoft.Dynamics.AX.Framework.DocumentContract;

And finally send the contract to DRA:

SrsReportRunPrinter::sendDocumentContractToDocumentRouter(documentContract);

Whole method:

public static void sendToPrinter(DocuRef _docuRef, str _printerName)
{
    SrsReportPrinterContract activePrinterContract = SrsReportRunUtil::getActivePrinter(_printerName);
    if (!activePrinterContract.parmPrinterName())
    {
        return;
    }

    SRSPrintDestinationSettings srsPrintDestinationSettings = new SRSPrintDestinationSettings();
    srsPrintDestinationSettings.printMediumType(SRSPrintMediumType::Printer);
    srsPrintDestinationSettings.fileFormat(SRSReportFileFormat::PDF);
    srsPrintDestinationSettings.printerName(activePrinterContract.parmPrinterName());
    srsPrintDestinationSettings.printerWhere(activePrinterContract.parmPrinterPath());
    srsPrintDestinationSettings.numberOfCopies(1);
    srsPrintDestinationSettings.collate(false);
    srsPrintDestinationSettings.printOnBothSides(SRSReportDuplexPrintingSetting::None);
    srsPrintDestinationSettings.printAllPages(true);
    srsPrintDestinationSettings.fromPage(0);
    srsPrintDestinationSettings.toPage(0);

    container fileCon = DocumentManagement::getAttachmentAsContainer(_docuRef);
    if (fileCon)
    {
        var stream = Binary::constructFromContainer(fileCon).getMemoryStream();
        if (stream)
        {
            DocumentContract documentContract = DocumentContractFactory::Instance.Create(DocumentContractType::Pdf);
            documentContract.Name = _docuRef.Name;
            documentContract.Contents =  stream.ToArray();
            documentContract.TargetType = TargetType::Printer;
            documentContract.Settings = srsPrintDestinationSettings.printerPageSettings();
            documentContract.ActivityID = newGuid();

         
            SrsReportRunPrinter::sendDocumentContractToDocumentRouter(documentContract);
        }
    }
}

If you want to check documents printed or see if there are any in a queue you can go to Common -> Inquiries -> Document routing status

Image