Create Invoice PO base on Packing Slip/Product Receipt by X++

This code will created pending invoice base on VendPackingSlipJour data and posting that’s pending invoice at the end of code.

PurchFormLetter             purchFormLetter;

    PurchTable                  purchTable;
    PurchLine                   purchLine;

    VendPackingSlipJour         vendPackingSlipJour;
    VendPackingSlipTrans        vendPackingSlipTrans;

    VendInvoiceInfoTable        vendInvoiceInfoTable;
    VendInvoiceInfoLine         vendInvoiceInfoLine;
    VendInvoiceInfoSubTable     vendInvoiceInfoSubTable;
    VendInvoiceInfoSubLine      vendInvoiceInfoSubLine;

    PurchId                     purchId;
    PackingSlipId               packingSlipId;
    ;

    purchId = '9001-PO-16-000745';
    packingSlipId = '9001-PO-16-000745-2';

    ttsBegin;
    purchTable = PurchTable::find(purchId);

    while select vendPackingSlipJour
        where vendPackingSlipJour.PurchId == purchTable.PurchId
        && vendPackingSlipJour.PackingSlipId == packingSlipId
    {

        //Generate Pending Invoice Header
        vendInvoiceInfoTable.clear();
        vendInvoiceInfoTable.initValue();
        vendInvoiceInfoTable.initFromPurchTable(purchTable);

        vendInvoiceInfoTable.DocumentOrigin  = DocumentOrigin::Manual;
        vendInvoiceInfoTable.CurrencyCode = purchTable.CurrencyCode;
        vendInvoiceInfoTable.DeliveryName = purchTable.DeliveryName;
        vendInvoiceInfoTable.Num = "INV-" + vendPackingSlipJour.PackingSlipId; //add invoice number in here
        vendInvoiceInfoTable.PurchName = purchTable.PurchName;
        vendInvoiceInfoTable.VendInvoiceSaveStatus = VendInvoiceSaveStatus::Pending;
        vendInvoiceInfoTable.DocumentDate = systemDateGet();
        vendInvoiceInfoTable.LastMatchVariance = LastMatchVarianceOptions::OK;
        vendInvoiceInfoTable.ParmJobStatus = ParmJobStatus::Waiting;
        vendInvoiceInfoTable.Approved = NoYes::Yes;
        vendInvoiceInfoTable.Approver = DirPersonUser::currentWorker();

        vendInvoiceInfoTable.DefaultDimension = vendInvoiceInfoTable.copyDimension(purchTable.DefaultDimension);

        vendInvoiceInfoTable.defaultField(fieldNum(VendInvoiceInfoTable,RemittanceLocation),null,purchTable);
        vendInvoiceInfoTable.defaultField(fieldNum(VendInvoiceInfoTable,FixedDueDate),null,purchTable);
        vendInvoiceInfoTable.defaultField(fieldNum(VendInvoiceInfoTable,ExchRate),null,purchTable);
        vendInvoiceInfoTable.defaultField(fieldNum(VendInvoiceInfoTable,TransDate),null,purchTable);
        vendInvoiceInfoTable.defaultField(fieldNum(VendInvoiceInfoTable,PaymMode),null,purchTable);

        vendInvoiceInfoTable.insert();

        //Generate Vend Invoice Info reference
        if(vendInvoiceInfoTable)
        {
            vendInvoiceInfoSubTable.clear();
            vendInvoiceInfoSubTable.initValue();
            vendInvoiceInfoSubTable.defaultRow();

            vendInvoiceInfoSubTable.ParmId = vendInvoiceInfoTable.ParmId;
            vendInvoiceInfoSubTable.OrigPurchId = vendInvoiceInfoTable.PurchId;
            vendInvoiceInfoSubTable.PurchName = vendInvoiceInfoTable.PurchName;
            vendInvoiceInfoSubTable.TableRefId = vendInvoiceInfoTable.TableRefId;

            vendInvoiceInfoSubTable.insert();
        }

        //select all packing slip line
        while select vendPackingSlipTrans
            where vendPackingSlipTrans.PackingSlipId == vendPackingSlipJour.PackingSlipId
            && vendPackingSlipTrans.VendPackingSlipJour == vendPackingSlipJour.RecId
        {
            //Generate Pending Invoice Line
            purchLine = vendPackingSlipTrans.purchLine();
            vendInvoiceInfoLine.clear();
            vendInvoiceInfoLine.initValue();
            vendInvoiceInfoLine.defaultRow(null,purchLine);
            vendInvoiceInfoLine.initFromPurchLine(purchLine);

            vendInvoiceInfoLine.DeliveryName = vendInvoiceInfoTable.DeliveryName;
            vendInvoiceInfoLine.ParmId = vendInvoiceInfoTable.ParmId;
            vendInvoiceInfoLine.TableRefId = vendInvoiceInfoTable.TableRefId;
            vendInvoiceInfoLine.currencyCode = vendInvoiceInfoTable.CurrencyCode;
            vendInvoiceInfoLine.LineNum = any2int(purchLine.LineNumber);

            vendInvoiceInfoLine.InvoiceAccount = vendInvoiceInfoTable.InvoiceAccount;
            vendInvoiceInfoLine.InventDimId = vendPackingSlipTrans.InventDimId;
            vendInvoiceInfoLine.OrderAccount  = vendInvoiceInfoTable.OrderAccount;
            vendInvoiceInfoLine.ItemId = vendPackingSlipTrans.ItemId;
            vendInvoiceInfoLine.InventTransId = vendPackingSlipTrans.InventTransId;

            vendInvoiceInfoLine.DocumentOrigin = DocumentOrigin::Manual;

            vendInvoiceInfoLine.ReceiveNow = vendPackingSlipTrans.Qty;
            vendInvoiceInfoLine.RemainBefore = vendPackingSlipTrans.Qty;
            vendInvoiceInfoLine.RemainBeforeInvent = vendPackingSlipTrans.Qty;

            vendInvoiceInfoLine.PurchPrice = purchLine.PurchPrice;
            vendInvoiceInfoLine.InventNow = vendPackingSlipTrans.Qty;
            vendInvoiceInfoLine.LineAmount = (purchLine.LineAmount / purchLine.QtyOrdered) * vendPackingSlipTrans.Qty;

            vendInvoiceInfoLine.DefaultDimension = purchLine.DefaultDimension;

            vendInvoiceInfoLine.insert();

            //Generate Vend Invoice Info reference from packing slip
            if(vendInvoiceInfoLine.RecId)
            {
                vendInvoiceInfoSubLine.clear();
                vendInvoiceInfoSubLine.initValue();
                vendInvoiceInfoSubLine.defaultRow();
                vendInvoiceInfoSubLine.ParmId = vendInvoiceInfoTable.ParmId;
                vendInvoiceInfoSubLine.LineRefRecId = vendInvoiceInfoLine.RecId;
                vendInvoiceInfoSubLine.ReceiveNow = vendPackingSlipTrans.Qty;
                vendInvoiceInfoSubLine.InventNow = vendPackingSlipTrans.Qty;
                vendInvoiceInfoSubLine.JournalRefRecId = vendPackingSlipTrans.RecId;
                vendInvoiceInfoSubLine.JournalRefTableId = vendPackingSlipTrans.TableId;
                vendInvoiceInfoSubLine.DocumentId = vendPackingSlipTrans.PackingSlipId;
                vendInvoiceInfoSubLine.insert();
            }
        }

        //Posting pending invoice invoice
        purchFormLetter = PurchFormLetter_Invoice::newFromSavedInvoice(vendInvoiceInfoTable);
        purchFormLetter.update(vendInvoiceInfoTable.purchTable(),vendInvoiceInfoTable.Num);
    }

    ttsCommit;

    info(strFmt('Purchase Order %1 invoiced',purchTable.PurchId));

Missing reference for “display method returning object” error when build form in AX7

Error example :
When build the project contains new custom form and datasource “CustInvoiceJour” , i encountered error missing module reference for EDT logisticsAddressing.

capture

Solutions :
Find the object using search function, then you can see the model reference for that object. After that, update your model parameters and checklist the missing references.

capture

Note:
The new model that my project referenced to is using Extension Model approach.

Export data from Retail POS to Excel without installing Microsoft Excel

If you want to export data from Retail POS to Excel without installing Microsoft Excel, you can use one of DevExpress features to achieve this.
Here is my sample code :

using (SaveFileDialog sfdExport = new SaveFileDialog())
{
	sfdExport.Filter = "Excel (2003)(.xls)|*.xls";
	if (sfdExport.ShowDialog() != DialogResult.Cancel)
	{
		try
		{
			DataTable dtExport = new DataTable();
			//You can get your data and fill it here

			using (DevExpress.XtraGrid.GridControl grExport = new DevExpress.XtraGrid.GridControl())
			{
				using (DevExpress.XtraGrid.Views.Grid.GridView gvExport = new DevExpress.XtraGrid.Views.Grid.GridView())
				{
					gvExport.OptionsBehavior.AutoPopulateColumns = true;
					gvExport.GridControl = grExport;

					grExport.BindingContext = new BindingContext();
					grExport.DataSource = dtExport;
					grExport.MainView = gvExport;
					grExport.ForceInitialize();

					gvExport.PopulateColumns();
					gvExport.ExportToXls(strExportFilePath);
				}
			}
		}
		catch (Exception ex)
		{
			LSRetailPosis.ApplicationExceptionHandler.HandleException(this.ToString(), ex);
			throw;
		}
	}
}

You can fill the data table or use other type of datasources.

Get tax amount through x++

static void TIDgetTax(Args _args)
{
    TaxAmount           tax;
    CustInvoiceTrans    CustInvoiceTrans;
    ;

    CustInvoiceTrans =  CustInvoiceTrans::findRecId(5637155209);
    tax = Tax::calcTaxAmount(CustInvoiceTrans.TaxGroup, CustInvoiceTrans.TaxItemGroup, CustInvoiceTrans.InvoiceDate,
                            CustInvoiceTrans.CurrencyCode, CustInvoiceTrans.LineAmount, TaxModuleType::FreeTxtInvoice);

    info(strfmt('%1', tax));
}

Shrink database SQL Query

Shrink database data.

USE DatabaseName;
GO
-- Shrink the truncated file to 1024MB.
DBCC SHRINKFILE (DataBaseFileName, 1024);
GO

Shrink database log.

USE DatabaseName;
GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE DatabaseName
SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DBCC SHRINKFILE (DatabaseFileLogName, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE DatabaseName
SET RECOVERY FULL;
GO

Reindex All Table On Database SQL Query

Script to re-index All Table On Database.

DECLARE @Database VARCHAR(255)   
DECLARE @Table VARCHAR(255)  
DECLARE @cmd NVARCHAR(500)  
DECLARE @fillfactor INT 

SET @fillfactor = 90 

DECLARE DatabaseCursor CURSOR FOR  
SELECT name FROM master.dbo.sysdatabases   
WHERE name IN ('DATABASENAME')   
ORDER BY 1  

OPEN DatabaseCursor  

FETCH NEXT FROM DatabaseCursor INTO @Database  
WHILE @@FETCH_STATUS = 0  
BEGIN  

   SET @cmd = 'DECLARE TableCursor CURSOR FOR SELECT ''['' + table_catalog + ''].['' + table_schema + ''].['' + 
  table_name + '']'' as tableName FROM [' + @Database + '].INFORMATION_SCHEMA.TABLES 
  WHERE table_type = ''BASE TABLE'''   

   -- create table cursor  
   EXEC (@cmd)  
   OPEN TableCursor   

   FETCH NEXT FROM TableCursor INTO @Table   
   WHILE @@FETCH_STATUS = 0   
   BEGIN   

		Print  ('ALTER INDEX ALL ON ' + @Table)
		SET @cmd = 'ALTER INDEX ALL ON ' + @Table + ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),@fillfactor) + ')' 
		EXEC (@cmd) 

       FETCH NEXT FROM TableCursor INTO @Table   
   END   

   CLOSE TableCursor   
   DEALLOCATE TableCursor  

   FETCH NEXT FROM DatabaseCursor INTO @Database  
END  
CLOSE DatabaseCursor   
DEALLOCATE DatabaseCursor

Consuming webservices (SOAP) from AX example code

public static container UserInfoByIDRequest(str _storeName, str _idType, str _idNo, str _extRef,str _username = "",str _password = "")
{
    TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.Summary summary;
    TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.RemittanceClient svcClient;
    TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.UserInfoByIDRequest request;
    TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.UserInfoByIDResponse response;

    CLRObject clrObject;
    System.Exception ex;
    str errorCode, errorMesg;
    container  returnCon;
    boolean    canConnectAPI;
    str        errorCanConnectAPI;

    new InteropPermission(InteropKind::ClrInterop).assert();
    try
    {
        clrObject = CLRInterop::getType("TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.RemittanceClient");
        svcClient = AifUtil::createServiceClient(clrObject);

        request = new TIDIDSSIMPONI.ServiceReferences.TIDIDSSR.UserInfoByIDRequest();

        //if no username or password provided, get from AX
        if(!_username || !_password)
        {
            _username = TIDIDSSimponiWebConsumeMethod::getUserNamePasswordStore(_storeName).IPMS_GaleriUserName;
            _password = TIDIDSSimponiWebConsumeMethod::getUserNamePasswordStore(_storeName).IPMS_GaleriPassword;
        }

        request.set_Username(_username);
        request.set_Password(_password);
        request.set_StoreName(_storeName);
        request.set_IdType(_idType);
        request.set_IdNo(_idNo);
        request.set_ExtRef(_extRef);

        //consuming webservice API
        response = svcClient.GetUserInfoByID(request);
        summary = response.get_Summary();

        //unpack to variable
        errorCode = summary.get_ErrorCode();
        errorMesg = summary.get_ErrorMessage();

        //packing all variable to container and return it
        returnCon += errorCode;
        returnCon += errorMesg;



        //sucess connect to webAPI
        canConnectAPI = true;

    }
    catch(Exception::CLRError)
    {
        ex = CLRInterop::getLastException();
        info(ex.ToString());

        canConnectAPI = false;
        errorCanConnectAPI =ex.ToString();
    }

    return [canConnectAPI, errorCanConnectAPI, returnCon];
}

Only foreign key constraints are allowed on this table.

Best Practice Error

Only foreign key constraints are allowed on this table

Capture

How to rectify this best practice error.

For Custom Table

  1. Export table to XPO
  2. Edit it with text editor, and change
    • from: EnforceFKRelation 1
    • to: EnforceFKRelation 0
  3. delete table in AX, synchronize
  4. import  xpo file

Capture

For Standard  Table

Some custom relation table may have some error on standard table, we can fix this  best practice error using same trick as above, but for this one lets check on XPO file and change REFERENCETYPE value.

  1. Export table to XPO
  2. Edit it with text editor, and change
    • from: REFERENCETYPE  NORMAL
    • to: REFERENCETYPE PKFK
  3. delete table in AX, synchronize
  4. import  xpo file

Capture.

Starting Dynamics AX for Retail (POS Development)

One of the most challenging on dynamics AX is to develop POS on AX Retail module.

The Dynamics AX for Retail POS application was designed base on .Net Application using C# language so it’s little bit odd for regular AX technical who always develop using  X++, so since the POS is not using standard AX MorpX so we need to prepare to develop POS.

  1. Visual Studio for development tools.
  2. Dev Express License (Optional for better touch GUI)
  3. Retail SDK (POS Source Code)

Let’s  jump   into number 3. I assume you already have visual studio on dev server.

You can found Retail SDK on Dynamics AX Installation.

Installation

After installation finished you can found the source code  on document folder.

Source Code

For very first time lets  focus at the two folder on the POS Plug-ins, there is Services and Triggers because almost all POS function source code is on that folder.

Services: Services are actually .Net assemblies. POS implements many of the features as services using interfaces and can be modified using Visual Studio. POS loads these services at run time by calling the interfaces. So whenever you modify or extend the standard service keep the assembly name same as original so that POS can recognize it and call it at run time.

Triggers: Triggers are called before and after the operations. There are two types of triggers, Pre-triggers and Post-triggers. Pre-triggers provide a way of validation before a certain operation is executed. Post-triggers are used to respond to an operation after it has finished. You can modify the triggers same way as services.

*I use Dynamics AX 2012 R3 CU8 on this tutorial, also work for AX 2012 Feature Pack, AX 2012 R2, and AX 2o12 R3.

Create Custom Script

So first lets open the Triggers folder and double click Triggers.sln file, you will show all the triggers source code now expand on visual studio, lets pick our first function to modify.

For this tutorial we will add some function when POS starting, so lets’s open ApplicationTriggers Project and open  ApplicationTriggers.cs , at this class you will found the some of methods, this methods will be calling when POS staring, for example  ApplicationStart, ApplicationStop, PreLogon, PostLogon and etc.

Try to add some code at ApplicationStart method, i assume this method will be calling when POS application starting, lets add some simple script at here, let’s try add some dialog box.

 public void ApplicationStart()
{
     //Our Custom Script
     //<begin>
     MessageBox.Show("POS aplication starting....");
     //<end>

     string source = "IApplicationTriggers.ApplicationStart";
     string value = "Application has started";
     LSRetailPosis.ApplicationLog.Log(source, value, LSRetailPosis.LogTraceLevel.Debug);
     LSRetailPosis.ApplicationLog.WriteAuditEntry(source, value);
     ...
     ...
}

To make the script running on the POS we need to build the project in to dll and copy the dll on POS folder.

Try to run your POS, and the message box will be showing when POS starting.

Deploy Custom Script/dll

You should copy all  custom *.dll files on the Extensions folder at Retail POS Folder. Don’t replace the original ones.

We can using two different Extensions Folder on POS .

1. Retail POS\Extensions

Source Code2. Retail POS\Services\Extensions

Source Code

Always use the first one for custom Triggers *.dll, and second one for Services *.dll to make it easy to remember and maintenance.

Lets Starting To Debug POS Custom Code.

For debug the code, we can change some properties on the visual studio project,  use our first sample project, right click on that project and chose Properties.

First open Build  Tab on Properties UI and select the Output Path because this is Triggers so i  select “C:\Program Files (x86)\Microsoft Dynamics AX\60\Retail POS\Extensions\” Folder.

Source Code

And than open Debug tab, at the Start Action group select Start external program and select your POS.exe file.

Source Code

just put your break point on your custom script, and run the project using F5 or Start Button. Lets see the magic happen.

*Note: You can copy all POS Folder to your own folder to make the  original POS Files safety.

set WaterMark on textbox c# AX POS

extension method :

public static class TextBoxWatermarkExtensionMethod
{
     private const uint ECM_FIRST = 0x1500;
     private const uint EM_SETCUEBANNER = ECM_FIRST + 1;

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
     private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

     public static void SetWatermark(this TextBox textBox, string watermarkText)
     {
        SendMessage(textBox.Handle, EM_SETCUEBANNER, 0, watermarkText);
     }
}

how to use it example on form constructor :

public TIDFrmIDSKirimUang()
{
     InitializeComponent();

     TextBoxWatermarkExtensionMethod.SetWatermark(txtTNoHP,"0815xxxxxxx");
     TextBoxWatermarkExtensionMethod.SetWatermark(txtNoHP,"0815xxxxxxx");
}

reference :
http://vidmar.net/weblog/archive/2008/11/05/watermarked-textbox-in-windows-forms-on-.net.aspx

Running Batch Job from X++ example code

static void TID_ExecuteBatchJobDistribution(Args _args)
{
    BatchHeader header;
    SysRecurrenceData sysRecurrenceData;
    Batch batch;
    BatchJob batchJob;
    RetailConnScheduleRunner _RetailConnScheduleRunner; // Class extends RunBaseBatch
    BatchInfo processBatchInfo;
    BatchRetries noOfRetriesOnFailure = 4;
    ;

    // Setup the RunBaseBatch Job
    header = BatchHeader::construct();
    _RetailConnScheduleRunner = new RetailConnScheduleRunner();
    processBatchInfo = _RetailConnScheduleRunner.batchInfo();
    processBatchInfo.parmRetriesOnFailure(noOfRetriesOnFailure);
    processBatchInfo.parmCaption(_RetailConnSchedule.Name); // Description Batch Job
    processBatchInfo.parmGroupId('RTL'); // Batch Gorup
    processBatchInfo.parmBatchExecute(NoYes::Yes);
    header.addTask(_RetailConnScheduleRunner);

    // Set the recurrence data
    sysRecurrenceData = SysRecurrence::defaultRecurrence();
    SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData, DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), 20)); // Set range of recurrence
    SysRecurrence::setRecurrenceNoEnd(sysRecurrenceData);
    SysRecurrence::setRecurrenceUnit(sysRecurrenceData, SysRecurrenceUnit::Minute); // Set reccurence pattern
    header.parmRecurrenceData(sysRecurrenceData);
    // Set the batch alert configurations
    header.parmAlerts(NoYes::No, NoYes::Yes, NoYes::No, NoYes::Yes, NoYes::Yes);
    header.save();

    // Update the frequency to run the job to every two minutes
    ttsbegin;
    select forupdate batchJob
    join batch
    where batchJob.RecId == batch.BatchJobId
    && batch.ClassNumber == classnum(RetailConnScheduleRunner);

    sysRecurrenceData = batchJob.RecurrenceData;
    sysRecurrenceData = conpoke(sysRecurrenceData, 8, [10]);
    batchJob.RecurrenceData = sysRecurrenceData;
    batchJob.update();
    ttscommit;
}

How to access AX 7 from other machine (preview VM Version : AX7 Update 1)

version : AX7 Update 1.
When we need multiple users to test the new AX7 , it’s imposible to ask everyone to run VM on their local because not everyone have the specs to run the VM. So what i do is to make AX7 available on local network so our team can test its new features.

Step by step how to do it :
1. Setup network Bridged Adapter on VM settings

Capture

2. setup your network ip on inside your VM,  (cmd : ipconfig /all on host machine to view ip config)
Capture

3. Edit your local host file on C:\Windows\System32\drivers\etc . add AX 7 VM IP and usnconeboxax1aos.cloud.onebox.dynamics.com

Capture

4. Copy and install SSL certificate from VM to avoid HTTPS error when open the link. How to copy certificate : open IIS -> Website (AOSServiceDSC) -> Site Binding (on right pane) -> Edit Site Binding -> View SSL Certificate -> Details -> Copy to file .

Capture

5. install SSL certificate from VM to avoid HTTPS error when open the link. How to install certificate on the chrome.  url help link:

https://support.globalsign.com/customer/portal/articles/1211541-install-client-digital-certificate—windows-using-chrome

 

6. Now open your browser, and open the ax 7 at url at https://usnconeboxax1aos.cloud.onebox.dynamics.com , login using your credential, and voila !! done..

Capture

marking inventTrans example code

public static void FNDMarkInventTrans(InventTransId _inventTransId,InventTransId _refInventTransId,InventQty _qtyTomark)
{
    InventTrans issueInventTrans;
    TmpInventTransMark tmpInventTransMask;
    Map mapMarkNow;
    container con;
    real qty;
    Map mapTmp;
    MapEnumerator mapEnumerator;

    InventTransOriginId issueInventTransOriginId =
        InventTransOrigin::findByInventTransId(_inventTransId).RecId;

    InventTransOriginId receiptInventTransOriginId =
        InventTransOrigin::findByInventTransId(_refInventTransId).RecId;

    InventQty qtyToMark = _qtyTomark;

    ttsBegin;

    issueInventTrans = InventTrans::findByInventTransOrigin(
        issueInventTransOriginId);

    [con, qty] = TmpInventTransMark::packTmpMark(
        InventTransOrigin::find(issueInventTransOriginId),
        issueInventTrans.inventDim(),
        issueInventTrans.Qty);

    mapTmp = Map::create(con);
    mapEnumerator = mapTmp.getEnumerator();
    while (mapEnumerator.moveNext())
    {
        tmpInventTransMask = mapEnumerator.currentValue();

        if (tmpInventTransMask.InventTransOrigin == receiptInventTransOriginId)
        {
            tmpInventTransMask.QtyMarkNow = qtyToMark;
            tmpInventTransMask.QtyRemain -= tmpInventTransMask.QtyMarkNow;
            mapMarkNow = new Map(Types::Int64, Types::Record);
            mapMarkNow.insert(tmpInventTransMask.RecId, tmpInventTransMask);

            TmpInventTransMark::updateTmpMark(
                issueInventTransOriginId,
                issueInventTrans.inventDim(),
                -qtyToMark,
                mapMarkNow.pack());
        }
    }

    ttsCommit;
}

reference code :
https://community.dynamics.com/ax/b/dynamicsaxnotesonthecuffs/archive/2013/01/08/marking-from-code-part-2

get Available budget example code

source code from form : BudgetControlStatistics
Capture

example code :

  static void TIDF_getBudgetAvailable(Args _args)
{
    
    //class and table
    BudgetControlStatisticsManager budgetControlStatsManager = new BudgetControlStatisticsManager();
    
    //table
    BudgetTmpControlStatistics  currentBudgetTmpControlStatistics;
    BudgetCycle                 budgetCycle;
    BudgetCycleTimeSpan         budgetCycleTimeSpan;
    FiscalCalendar              fiscalCalendar;
    DimensionAttributeValueCombination dimensionAttributeValueCombination;
    DimensionHierarchy          dimensionHierarchy;
    
    //variable
    DimensionValue  dim1;
    container       conDimValue;
    MainAccountNum  mainAccountNum;
    RecId           ledgerDimension;
    
    //financial dimension
    mainAccountNum = "6100-1001";
    dim1           = "5102";
    
    select firstonly dimensionAttributeValueCombination 
    where 
        dimensionAttributeValueCombination.DisplayValue == strFmt("%1-%2",mainAccountNum,dim1)
    &&  dimensionAttributeValueCombination.LedgerDimensionType == LedgerDimensionType::BudgetControl;
    
    //budget cycle
    fiscalCalendar   = fiscalCalendar::findByCalendarId("2016");
    BudgetCycleTimeSpan = budgetCycleTimeSpan::findByNameAndCalendar("SEM",fiscalCalendar.RecId);
    BudgetCycle =  budgetCycle::findBudgetCycleByDate(BudgetCycleTimeSpan.RecId,today());
    
    currentBudgetTmpControlStatistics = budgetControlStatsManager.fillBudgetTmpControlStatistics(Ledger::current(),
                                                                                                'ALL',
                                                                                                BudgetControlStatisticsPeriodOption::FiscalPeriod,
                                                                                                BudgetBalanceType::Accumulated,
                                                                                                BudgetCycle.RecId,
                                                                                                dimensionAttributeValueCombination.RecId,
                                                                                                0,
                                                                                                0,
                                                                                                true,
                                                                                                NoYes::No,
                                                                                                true);
    
   while select  currentBudgetTmpControlStatistics
   {
        info(strFmt("%1 -- %2 ",currentBudgetTmpControlStatistics.PeriodStartDate,currentBudgetTmpControlStatistics.TotalFundsAvailableAmountMST));
   }

}

result :
Capture

Auto Approve Workflow by Code x++

Example code :

static void TIDF_autoApproveWFOvertime(Args _args)
{
    WorkflowWorkItemTable WorkflowWorkItemTable;

    while select WorkflowWorkItemTable
    where
        WorkflowWorkItemTable.UserId == '32501112'
    &&  workflowWorkItemTable.Type == WorkflowWorkItemType::WorkItem
    &&  workflowWorkItemTable.Status == WorkflowWorkItemStatus::Pending
    &&  WorkflowWorkItemTable.RefTableId == tableNum(HrsOvertime)
    {
        WorkflowWorkItemActionManager::dispatchWorkItemAction(
                                    WorkflowWorkItemTable,
                                    "Auto Approve by ADMIN", // comment
                                    '32501112', // << user ID
                                    WorkflowWorkItemActionType::Complete,
                                    "HRSOvertimeApprove", // << menu item action approval workflow
                                    false); //is not web Menu Item
    }

}

Capture

Resume Workflow from x++ example code

example code :

static void TIDF_resumeWFOverTime(Args _args)
{
    WorkflowTrackingStatusTable WorkflowTrackingStatusTable;
    HrsOvertime                 hrsOverTime;
    
    
    while select WorkflowTrackingStatusTable 
    where 
        WorkflowTrackingStatusTable.TrackingStatus == WorkflowTrackingStatus::Faulted 
    && WorkflowTrackingStatusTable.WorkflowType == WorkflowTrackingStatusWorkflowType::Workflow
    && WorkflowTrackingStatusTable.ContextTableId == tableNum(HrsOvertime)
    {
        //consistency check valid data
        select firstOnly hrsOverTime where hrsOverTime.RecId == WorkflowTrackingStatusTable.ContextRecId;
        if(hrsOverTime)
        {
            Workflow::resumeWorkflow(WorkflowTrackingStatusTable.CorrelationId);      
        }
    }
}

source : https://community.dynamics.com/ax/f/33/t/126119

Workflow status for selected record display method

Display WorkflowTrackingStatus WFStatus()
{
    WorkflowTrackingStatusTable   WorkflowTrackingStatusTable;
    ;
    select firstOnly  WorkflowTrackingStatusTable 
    order by WorkflowTrackingStatusTable.RecId DESC
    where 
        WorkflowTrackingStatusTable.ContextTableId         == this.TableId
    &&  WorkflowTrackingStatusTable.ContextRecId           == this.RecId;
    
    
    return WorkflowTrackingStatusTable.TrackingStatus;
}

get USER ID and Security Role from HcmWorker

Example Code :

display Notes TIDgetUserIdAndRoles()
{
    DirPerson               DirPerson;
    DirPersonUser           DirPersonUser;
    SecurityUserRole        SecurityUserRole;
    SecurityRole            SecurityRole;
    Notes                   returnNotes;
    str value;
    ;


    DirPerson       = DirPerson::find(this.Person);
    DirPersonUser   = DirPersonUser::findWorker(this.RecId);
    returnNotes      = strFmt("User ID : %1 \n\n ",DirPersonUser.User);
    returnNotes      = strFmt("%1Security Roles : \n",returnNotes);
    
    while select * from SecurityUserRole 
        where SecurityUserRole.User == DirPersonUser.User
    join SecurityRole
        where SecurityRole.RecId == SecurityUserRole.SecurityRole
    {
        if(subStr(SecurityRole.Name,1,1) == '@')
        {
            value = SysLabel::labelId2String2(SecurityRole.Name, companyinfo::languageId());
            returnNotes = strFmt("%1 %2\n",returnNotes,value);
        }
        else
        {
            returnNotes = strFmt("%1 %2\n",returnNotes, SecurityRole.Name);
        }
    }
    
    return returnNotes;
}

get Report to Position Worker Name with Employment Recid SQL Query

Example code :

select top 1 name from DIRPARTYTABLE
inner join HcmWorker ON HcmWorker.PERSON = DIRPARTYTABLE.RECID
AND HcmWorker.RECID = 
	(select top 1 worker from HCMPOSITIONWORKERASSIGNMENT  
	INNER JOIN HCMPOSITION ON HCMPOSITIONWORKERASSIGNMENT.POSITION =  HCMPOSITION.RECID
	AND HCMPOSITION.RECID = 
		(select TOP 1 ParentPosition from  hcmPositionHierarchy 
			INNER JOIN  hcmPositionHierarchyType on hcmPositionHierarchy.PositionHierarchyType = hcmPositionHierarchyType.RECID AND hcmPositionHierarchyType.HierarchyType = 0
			where hcmPositionHierarchy.Position = 
				(select top 1 POSITION from HCMPOSITIONWORKERASSIGNMENT  
				INNER JOIN HCMPOSITION ON HCMPOSITIONWORKERASSIGNMENT.POSITION =  HCMPOSITION.RECID
				where GETDATE() between HCMPOSITIONWORKERASSIGNMENT.VALIDFROM AND HCMPOSITIONWORKERASSIGNMENT.VALIDTO 
				AND HCMPOSITIONWORKERASSIGNMENT.WORKER = 
					(select TOP 1 WORKER from hcmEmployment where hcmEmployment.RECID = 5637152077)))
	where GETDATE() between HCMPOSITIONWORKERASSIGNMENT.VALIDFROM AND HCMPOSITIONWORKERASSIGNMENT.VALIDTO);

get worker name from position with SQL Queries

example code :

select top 1 name from DIRPARTYTABLE
inner join HcmWorker ON HcmWorker.PERSON = DIRPARTYTABLE.RECID
AND HcmWorker.RECID = 
	(select top 1 worker from HCMPOSITIONWORKERASSIGNMENT  
	INNER JOIN HCMPOSITION ON HCMPOSITIONWORKERASSIGNMENT.POSITION =  HCMPOSITION.RECID
	AND HCMPOSITION.POSITIONID = 'P0003' 
	where GETDATE() between HCMPOSITIONWORKERASSIGNMENT.VALIDFROM AND HCMPOSITIONWORKERASSIGNMENT.VALIDTO);

Job upload production order

//coded by fanddy
static void uploadProductionOrder(Args _args)
{
    // Progress Bar Variables
    #AviFiles
    SysOperationProgress        _ProgressBar = new SysOperationProgress();

    // Excel Variables
    SysExcelApplication     application;
    SysExcelWorkbooks       workbooks;
    SysExcelWorkbook        workbook;
    SysExcelWorksheets      worksheets;
    SysExcelWorksheet       worksheet;
    SysExcelCells           cells;
    str                     Filename;
    COMVariantType          type;
    int                     row;
    FilenameFilter filenameFilter = ['*.xlsx,*.xls'];

    //variable
    ProdId              prodId;
    ItemId              itemid;
    Qty                 qtySched;
    TransDate           StartDate,endDate,deliveryDate,bomDate;
    InventSiteId        inventSiteId;
    InventLocationId    inventLocationId;
    ProdStatus          prodStatus;
    ProdBackStatus      prodBackStatus;
    ProdReservation     prodReservation;
    BOMId               bomId;

    //tables
    ProdTable       prodtable;
    InventTable     inventTable;
    InventDim       inventDim;
      // convert into str from excel cell value
    str COMVariant2Str(COMVariant _cv, int _decimals = 0, int _characters = 0, int _separator1 = 0, int _separator2 = 0)
    {
        switch (_cv.variantType())
        {
            case (COMVariantType::VT_BSTR):      return _cv.bStr();
            case (COMVariantType::VT_R4):        return num2str(_cv.float(),_characters,_decimals,_separator1,_separator2);
            case (COMVariantType::VT_R8):        return num2str(_cv.double(),_characters,_decimals,_separator1,_separator2);
            case (COMVariantType::VT_DECIMAL):   return num2str(_cv.decimal(),_characters,_decimals,_separator1,_separator2);
            case (COMVariantType::VT_DATE):      return date2str(_cv.date(),123,2,1,2,1,4);
            case (COMVariantType::VT_EMPTY):     return '';
            default:
            throw error(strfmt('@SYS26908', _cv.variantType()));
        }
        return '';
    }




    filename = WinAPI::getOpenFileName(0, filenameFilter, '', '');
    if (filename=='')
    {
        warning('Filename must be specified correctly.');
        return;
    }

    //Filename = "C:\\prodorder.xls";
    application = SysExcelApplication::construct();
    workbooks   = application.workbooks();

    // gets the default ExchangeType RecId
    try
    {
        workbooks.open(Filename);
    }
    catch (Exception::Error)
    {
        throw error("File not found");
    }

    workbook    = workbooks.item(1);
    worksheets  = workbook.worksheets();
    worksheet   = worksheets.itemFromNum(1);
    cells       = worksheet.cells();
    row         = 1;

    // Start Progress Bar
    _ProgressBar.setCaption("Upload Production Order...");
    _ProgressBar.setAnimation(#AviTransfer);
    ttsBegin;
    do
    {
        //Incrementing the row line to next Row
        row++;

        _ProgressBar.setText(strFmt('Processing %1...', row));
        prodId              = COMVariant2Str(cells.item(row,1).value());
        itemid              = COMVariant2Str(cells.item(row,2).value());
        qtySched            = cells.item(row,3).value().double();
        startDate           = cells.item(row,4).value().date();
        endDate             = cells.item(row,5).value().date();
        inventSiteId        = COMVariant2Str(cells.item(row,6).value());
        inventLocationId    = COMVariant2Str(cells.item(row,7).value());
        prodStatus          = str2enum(ProdStatus,COMVariant2Str(cells.item(row,8).value()));
        prodBackStatus      = str2enum(ProdBackStatus,COMVariant2Str(cells.item(row,9).value()));
        deliveryDate        = cells.item(row,10).value().date();
        prodReservation     = str2enum(prodReservation,COMVariant2Str(cells.item(row,11).value()));
        bomDate             = cells.item(row,12).value().date();
        bomId               = COMVariant2Str(cells.item(row,13).value());


        // Initialize InventTable
        inventTable.clear();
        inventTable = inventTable::find(itemid);

        // Initialize the base values
        prodtable.clear();
        prodtable.initValue();
        prodtable.initFromInventTable(inventTable);
        prodtable.ProdId                = prodId;
        prodtable.ItemId                = inventTable.ItemId;
        prodtable.QtySched              = qtySched;
        prodtable.SchedStart            = StartDate;
        prodtable.SchedEnd              = endDate;
        prodtable.DlvDate               = deliveryDate;
        prodtable.RemainInventPhysical  = qtySched;
        prodtable.BOMDate               = today();
        prodtable.ProdStatus            = prodStatus;
        prodtable.BackorderStatus       = prodBackStatus;
        prodtable.Reservation           = prodReservation;

        // Initialize InventDim (Obrigatory)
        inventDim.clear();
        inventDim.InventSiteId          = inventSiteId;
        inventDim.InventLocationId      = inventLocationId;
        inventDim                       = inventDim::findOrCreate(inventDim);
        prodtable.InventDimId           = inventDim.inventDimId;

        // Set the active BOM and Route
        if(!bomId)
        {
            prodtable.BOMId = BOMVersion::findActive(prodtable.ItemId,
                                                        prodtable.BOMDate,
                                                        prodtable.QtySched,
                                                        inventDim).BOMId;
        }
        if(!prodtable.RouteId)
        {
            prodtable.RouteId = RouteVersion::findActive(prodtable.ItemId,
                                                         prodtable.BOMDate,
                                                         prodtable.QtySched,
                                                         inventDim).RouteId;
        }

        // Initialize BOMVersion
        prodtable.initBOMVersion();
        // Initialize RouteVersion
        prodtable.initRouteVersion();
        //Use ProdTableType class to create the production order
        prodtable.type().insert();
        

        print (strFmt('Line number %1 - Production order %2 - Item ID %3', row,prodtable.ProdId, itemId));

        type = cells.item(row+1, 1).value().variantType();
    }
    while (type != COMVariantType::VT_EMPTY);
    
    ttsCommit;


    // quits the application
    application.quit();

    info('Upload success');
}

Hands on AX7 version CTP8 (Technical Preview)

Just got AX7 VM yesterday. I can’t say much due to the Microsoft Confidential Policy about this Tehnical Preview of AX7. But below is the screenshot of AX7. And i tried to develop a simple form on USR layer too.

 

Capture14

Capture3

Capture2

Capture11

Capture12

Capture6

Capture7

Capture10

Capture15

Capture

how to access Technical Preview and all the presentation about AX7 from microsoft :
https://mbs.microsoft.com/customersource/global/AX/news-events/news/Microsoft_Dynamics_AX_Public_Preview

Hello AX 7 !!

Capture

AX7 is becoming hot topic on several technical groups forum. The most epic changes is AX7 development now using Visual Studio and not using MorphX like the previous version of AX.

AX7 Development code still using x++ , but the ways of the developing is similar to .NET application development. And now it doesn’t need direct connection to AOS service and all the source code is saved as XML file on the project folder. But it still have the same AX layers model architecture.

ax-7-preview_499x275

dynaxblog2

The UI itself is Metro Style and beautiful. It doesn’t looks like an ERP and more like some mobile apps style. and from what i saw on the introduction video from Microsoft, it is Web Based now and the AX AOS hosted on IIS.

Enterprise portal has been removed on AX7. I personnaly agree with that. Customization on enterprise portal has been very painful and not efficient, you need to incremental CIl, deploying more than 1 object. and sometimes need to restart IIS and AOS for just displaying updated webpage.

anyway, Hello AX7 !!. and welcome to hellodax as new category.

full links on introduction video :
https://mbspartner.microsoft.com/AX/Videos/829

source screenshot :
https://sbsgroupusa.files.wordpress.com/2015/12/dynaxblog2.png?w=610
http://www.to-increase.com/media/524952/ax-7-preview_499x275.jpg

Hide(remove) menu item action approval workflow

Some of our clients don’t need “Delegate” or “Deny” on workflow,
so here below is how to hide Workflow Approval menu items,

just remove “ActionMenuItems” and “WebActionMenuItems” for web on the Workflow Approval AOT properties.
Capture.PNG

do Full CIL and Incremental CIL after remove the properties,
menu items will be remove from workflow user approval.

result
Capture