Sunday, November 28, 2021

How to create and print a QR code on SSRS report in AX 2009

 In order to meet the KSA GAZT regulations, it is mandatory to print a QR code containing 5 fields including Company name, VAT registration number, Invoice date/time, VAT Amount, and Invoice Amount including VAT on any customer invoice. 

In order to meet above requirements, you need to first declare two variables on global level using X++ such as:


    //global variable

   //PKR qr code

    int                     qrCodeByteArrIndex;

    System.Byte[]           qrCodeByteArray;


Then there need to be two methods, one is used to populate the global byte array and the other one as display method to display QR code on a bitmap control:

/// <summary>
/// The method takes TLV structure for KSA QR code and sets into byte array containing data for QR code.
/// </summary>
/// <param name="_tag">
/// The serial number of structure.
/// </param>
/// <param name="_length">
/// The length of value of field.
/// </param>
/// <param name="[System.Byte[]]_byteArrayStr">
/// The value of a field.
/// </param>
/// <remarks>
/// Various field values such as VAT number, company name, VAT Amount, Invoice Amount including VAT, and Transaction date
/// and time need to be printed as QR code. The method serves as setting each TLV structure into a byte array.
/// </remarks>
void pkr_QRCodeFillByteArray(int _tag, int _length, System.Byte[] _byteArrayStr)
{
    int counter;

    qrCodeByteArray.SetValue( System.Convert::ToByte(_tag), qrCodeByteArrIndex);
    qrCodeByteArrIndex++;
    qrCodeByteArray.SetValue(System.Convert::ToByte(_length), qrCodeByteArrIndex);
    qrCodeByteArrIndex++;

    for (counter=0; counter < CLRInterOp::getAnyTypeForObject(_byteArrayStr.get_Length()); counter++)
    {
        qrCodeByteArray.SetValue(_byteArrayStr.GetValue(counter), qrCodeByteArrIndex);
        qrCodeByteArrIndex++;
    }
}


The display method as below: 

public display container  qrcode()
{
    str                                 strCompanyName, strVATNum, strDateTime, strTotalVATAmount, strTotalAmountIncVAT, strQrCodeBase64;
    int                                 qrByteArrayLength;
    Bindata                             bindata = new Bindata();
    System.Drawing.Bitmap               obj;
    Filepath                            filePath;
    container                           con;
    Microsoft.Dynamics.QRCode.Encoder   encoder;
    Set                                 permissionSet;
    System.Text.Encoding                encodingUTF8;
;
    filePath = @"C:\temp\newQRCode.bmp";
    permissionSet = new Set(Types::Class);
    permissionSet.add(new FileIOPermission(filePath,'rw'));
    permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
    CodeAccessPermission::assertMultiple(permissionSet);

    encoder   = new Microsoft.Dynamics.QRCode.Encoder();
    encodingUTF8 = System.Text.Encoding::get_UTF8();
    qrCodeByteArrIndex = 0;

    strCompanyName = companyInfo.Name;
    strVATNum = companyInfo.CoRegNum;
    strDateTime =  date2Str(custInvoiceJour.InvoiceDate,321,DateDay::Digits2,DateSeparator::Hyphen, DateMonth::Digits2, DateSeparator::Hyphen, DateYear::Digits4)+ "T12:00:00Z";
    strTotalVATAmount = strfmt("%1", totalamount+totalVATAmount); 
    strTotalAmountIncVAT = strfmt("%1", totalVATAmount); 

    qrByteArrayLength = strlen(strfmt("%1%2%3%4%5", strCompanyName, strVATNum, strDateTime, strTotalVATAmount, strTotalAmountIncVAT)) + 10;

    qrCodeByteArray = new System.Byte[qrByteArrayLength](); //initialize global byte array as per length of all TLV structures plus 2 chars per each struct for TL.
    
    element.pkr_QRCodeFillByteArray(1, strlen(strCompanyName), encodingUTF8.GetBytes(strCompanyName));
    element.pkr_QRCodeFillByteArray(2, strlen(strVATNum), encodingUTF8.GetBytes(strVATNum));
    element.pkr_QRCodeFillByteArray(3, strlen(strDateTime), encodingUTF8.GetBytes(strDateTime));
    element.pkr_QRCodeFillByteArray(4, strlen(strTotalVATAmount), encodingUTF8.GetBytes(strTotalVATAmount));
    element.pkr_QRCodeFillByteArray(5, strlen(strTotalAmountIncVAT), encodingUTF8.GetBytes(strTotalAmountIncVAT));

    strQrCodeBase64 = System.Convert::ToBase64String(qrCodeByteArray);
    info(strfmt("%1", strQrCodeBase64));
    obj = new System.Drawing.Bitmap(encoder.Encode(strQrCodeBase64));
    obj.Save(filePath, System.Drawing.Imaging.ImageFormat::get_Bmp());
    bindata.loadFile(filePath);

    con = bindata.getData();
    CodeAccessPermission::revertAssert();

    return con;
}


Lastly, place a bitmap control on an appropriate place on report design and then call display method from the control's properties.

The implementation may vary but the above code used for AX 2009 may be utilized with small changes.