Sunday, January 29, 2017

Visual Studio cache clear

Hey guys,

Today, while playing around with Visual Studio templates, I did which had put me in a fix. I had changed some config files and after which I started getting weird errors on VS launch. (Sorry don't remember the exact errors now)

Then I revert the config files from the backup which I had taken before doing the changes still the errors didn't go away. Then it struck me that it's got something to do with the VS cache and on googling I found the below method to clear the cache:

1. Close Visual Studio (ensure devenv.exe is not present in the Task Manager)
2. Delete the %USERPROFILE%\AppData\Local\Microsoft\VisualStudio\14.0\ComponentModelCache directory
3. Restart Visual Studio.

You should change the VisualStudio/14.0 to the version of visual studio you are using. I am using VS2015.

Credits: https://github.com/Codealike/Codealike-KnowledgeBase/blob/master/clear-visual-studio-component-cache.md

Sunday, January 8, 2017

Sending barcode print to Label printers

Hi guys,

Last few days have been really tiring for me doing this particular task.

Requirement:
Barcode labels have to be printed from AX2012 for a particular work using a Thermal barcode printer.
Label size:2.48in X 0.91in

This may seem to be an easy solution but in reality, this may make your head spin.

Solution provided: SSRS report
Challenge faced: As the label size is landscape mode when you try to print the label from SSRS report, it prints the label tilting it to 90 degrees to the left. This is due to the label printer drivers as far as I could find out through google.

Also, there is a limitation in SSRS which automatically converts the mode to portrait or landscape based on the dimensions you provide for the report. So both these individual limitations make it impossible to print a rectangle label.

Solution provided 2: AX report
The challenge faced: This had seemed to be the best approach as the report renders itself as per the default printer settings. So when we were printing then it was coming out perfect and was scannable too. But the issue came when we were printing from different machines, it was not scannable!! This had me wondering that what could be the issue cause the labels from my machines (scannable) and other machines (not scannable) were exactly same.

The barcode font that we were using was IDAUTOMATIONHC39M which is of Code39 type and we got to know that Code39 expands the barcode horizontally and vertically. The universally best barcode is to use Code128.

Finally what had worked:
Solution provided 3: Direct printing using PRN file
Now this was really interesting as I had never done anything of this regard yet. So first I got a prn (TSC programmed) file. Then googled and got the solution to do so.

The challenge faced: PRN files hit directly to the printer by using printer name/IP address. In our case, the printer and AX were in different domains and we were sharing them through remote desktop. So when you share a printer through RDP then the name of the printer "TSC ME240" gets a suffix of  "TSC ME240 redirected 23" and hence the PRN command from AX was not able to find the printer "TSC ME240".

Solution: Put printer on Public IP

We had to put the printer on public IP and add it to the server with the same name by a fresh installation of printer drivers and avoid redirecting.

The Task:
1. Got the PRN from the Printer vendor.
2. Create a table for storing the PRN code similar to WHSDocumentRoutingLayout. Fields: LayoutId, Description, PrinterName, DefaultConfig, PRNCode
3. Write a find, findByDefault method.
4. Create a form for the same table.
5. Create a new layout, mark it default and define the TSC/ZPL code in the form.
Below code is in TSC programming langauge in this case for TSC label printers:

<xpml><page quantity='0' pitch='23.1 mm'></xpml>SIZE 61.7 mm, 23.1 mm
GAP 3 mm, 0 mm
DIRECTION 0,0
REFERENCE 0,0
OFFSET 0 mm
SET PEEL OFF
SET CUTTER OFF
SET PARTIAL_CUTTER OFF
<xpml></page></xpml><xpml><page quantity='1' pitch='23.1 mm'></xpml>SET TEAR ON
CLS
BARCODE 462,117,"128M",27,0,180,2,4,"OIDPIDBarcode"
CODEPAGE 1252
TEXT 440,83,"0",180,12,12,"EcomOrderProductId"
TEXT 449,158,"0",180,12,10,"ItemId"
TEXT 463,44,"0",180,8,10,"ItemName"
PRINT 1,1
<xpml></page></xpml><xpml><end/></xpml>

6. Now, create a class "WHSBarcodePrinting" with the following methods:

  • Class declaration

class WHSBarcodePrinting
{
     RecordSortedList    list;
}
  • initMenuFields - To read the fields to which needs to be printed.
RecordSortedList initMenuFields()
{
    TmpSysTableField    tmpField;
    DictField           dictField;
    DictTable           dictTable = new DictTable(tableNum(WHSWorkLine));
    int                 length = dictTable.fieldCnt();
    int                 i;

    for (i = 1; i <= length; ++i)
    {
        dictField = new DictField(tableNum(WHSWorkLine), dictTable.fieldCnt2Id(i));

        if (!dictField.isSystem() || !dictField.visible() && dictField.name() == 'ItemId' || dictField.name() == 'EcomOrderProductId')
        {
            tmpField.FieldId    = dictField.id();
            tmpField.FieldName  = dictField.name();
            tmpField.FieldLabel = dictField.label();
            list.ins(tmpField);
        }
    }

    return list;
}
  • new
public void new()
{
    list = new RecordSortedList(tableNum(TmpSysTableField));
    list.sortOrder(fieldNum(TmpSysTableField, FieldLabel));

    this.initMenuFields();
}
  • printDocument - To get the final string command to be sent to the printer.
public boolean printDocument(PrinterName _printerName, WHSLayoutId _layoutId, WHSWorkLine _label)
{
    str         finalStr;
    boolean     ret;

    finalStr = this.translate(WHSBarcodePrintingSetup::find(_layoutId).zpl, _label);

    Microsoft.Dynamics.AX.WHS.DeviceCom.Printer::SendStringToPrinter(_printerName, finalStr);

    return ret;
}
  • translate - Reading the PRN code and replacing the variable with Ax table values to be printed on label.
str translate(str _inputStr, WHSWorkLine _label)
{
    TmpSysTableField    tmpField;
    FieldLabel          label;
    str                 outputStr;

    outputStr = _inputStr;

    list.first(tmpField);

    while (tmpField.FieldLabel != '')
    {
        outputStr = strReplace(outputStr, strFmt('%1', tmpField.FieldName), _label.(tmpField.FieldId));
        outputStr = strReplace(outputStr, 'ItemName', _label.displayItemName());
        outputStr = strReplace(outputStr, 'OIDPIDBarcode', "!105" + subStr(_label.EcomOrderProductId, 0, 8) + "!100" +
        subStr(_label.EcomOrderProductId, 9, 3) + "!099" + subStr(_label.EcomOrderProductId, 12, strLen(_label.EcomOrderProductId)));
        label = tmpField.FieldLabel;

        tmpField.clear();

        list.next(tmpField);

        if (label == tmpField.FieldLabel)
        {
            break;
        }
    }

    return outputStr;
}
  • main - Filtering the records for which label has to be printed.
static void main(Args _args)
{
    WHSBarcodePrinting         barcodePrint = new WHSBarcodePrinting();
    PrinterName                     printerName;
    WHSWorkTable                    whsWorkTable;
    WHSWorkLine                     whsWorkLine;
    WHSBarcodePrintingSetup    barcodeSetup;
    WHSLayoutId                     layoutId;

    barcodeSetup    = WHSBarcodePrintingSetup::findByDefault(NoYes::Yes);
    printerName     = barcodeSetup.PrinterName;
    layoutId        = barcodeSetup.LayoutId;

    barcodePrint.initMenuFields();

    if (_args.record() && _args.record().TableId == tableNum(WHSWorkTable))
    {
        whsWorkTable    =   _args.record();

        if (whsWorkTable.WorkTransType == WHSWorkTransType::Sales && (whsWorkTable.WorkStatus != WHSWorkStatus::Cancelled
            || whsWorkTable.WorkStatus != WHSWorkStatus::Skipped))
        {
            while select ItemId, EcomOrderProductId from whsWorkLine
                order by whsWorkLine.WMSLocationId, whsWorkLine.ItemId
                where whsWorkLine.WorkId == whsWorkTable.WorkId
                && whsWorkLine.WorkType == WHSWorkType::Pick
            {
                barcodePrint.printDocument(printerName, layoutId, whsWorkLine);
            }
        }
    }
    else if (_args.record() && _args.record().TableId == tableNum(WHSWorkLine))
    {
        whsWorkLine     =   _args.record();
        barcodePrint.printDocument(printerName, layoutId, whsWorkLine);
    }
    else
    {
        throw error('This command must be run from a Work Id.');
    }
}

Pheww!! After all this you will be able to print the labels comfortably which are readable by any scanner. :)

Reference: http://jhodge65.blogspot.in/2015/03/zebra-printer-label-printing-with.html