Saturday, December 19, 2020

GST E-Invoice API Integration Explained

Dear Reader, GSPs/ASPs & Dynamics NAV Service Provider/Consultants are too busy to integrate E-Invoice solutions. Taxpayers with Turnover between Rs 100 Cr- Rs 500 Cr can contact the GSP / ERPs for the willingness to integrate the e-Invoice APIs through them.

Lots of solutions being offered. Customers are in rush to get it done ASAP as dead line is approaching.

Mostly solution are opted by customer:

  1. API : Plain Text  
  2. API : With Encryption & Decryption
  3. SFTP file transfer (Excel/CSV/JSON)
Further solutions are offered with two type of schema:
  1. GSP's own schema
  2. Government Compatible
Below I am explaining solution step by step, how to integrate E-Invoice APIs.

Hope all of us are aware about Microsoft has already given solution (can be checked in recent Cumulative updates & can be found on Posted Sales Invoice / Credit Memo):
  1. Generate IRN
  2. Export Json File
  3. Import Json File
There is a codeunit ID; 16511 with Name: e-Invoice, for export & import JSON request & response based on Government Schema (based on NIC portal & can be checked here). Which is provided by Microsoft to export & import Json files.

So, not need to create external solution (eg. DLLs for calling API & parsing received response). API request & response can be done in NAV only using NAV Codeunits & standard .net variables of Windows .net framework & NAV add-ins.

Creating outside solution will require extra maintenance. You need to update your DLL, place DLL in add-in folder & update your NAV codes. Making such solution, you are making customer highly dependent. So, its better to write code natively in NAV for API call & response parsing which can be easily updated & deployed. 

Below are major steps to reuse NAV existing solutions:

Step 1: We have already solution available to prepare JSON structure with required data. We need to reuse this solution to fit into GSP's own schema or Govt. compatible schema. It's easy & quick to modify given Codeunit 16511 or can save in available Codeunit's ID of customer's license with different name. For example Codeunit ID: 50005 Name: e-Invoice Management.

Step 2: Create a Setup table to store GSP's API URLs & Credentials, etc. For example:


Note: You may add/modify filed as per GSP details. Create a corresponding page for this table.

Step 3: Create a log/entry table to store API request & response status & details. For Example: e-Invoice Entry


Note: Above fields are for example only. You may add/modify as per your need. Create a corresponding page for this table.

Step 4: Modify e-Invoice Management codeunit's methos "ExportAsJson(DocumentNo);" to call API & process response. Get the e-Invoice Setup and check if Integration is enabled then Call API otherwise leave the original code to save the JSON file. 

Define the below variables in function "ExportAsJson":

 Update "ExportAsJson" function as below:

 LOCAL PROCEDURE ExportAsJson@1500012(DocumentNo@1500000 : Code[20]);

    VAR

      NewStream@1055602 : InStream;
      HttpWebRequestMgt@1170000000 : Codeunit 1297;
      TempBlob@1170000003 : Record 99008535;
      HTTPStatusCode@1170000002 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpStatusCode";
      ResponseHeader@1170000001 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Collections.Specialized.NameObjectCollectionBase";
      InStr@1170000004 : InStream;
      Json@1170000005 : Text;
      JSONManagement@1170000006 : Codeunit 5459;
      WasSuccess@1170000007 : Boolean;
      ErrorText@1170000008 : Text;
      TrnsIdentifier@1170000009 : Text[250];
      AckNumber@1170000014 : Text[30];
      AckDateText@1170000015 : Text[30];
      IRNText@1170000010 : Text[250];
      SignedQR@1170000011 : Text;
      SignedInv@1170000012 : Text;     
      eInvoiceEntry@1170000016 : Record 50051;
      OStream@1170000017 : OutStream;
      RequestText@1170000018 : Text;
      QRPayload@1000000001 : Text;
      IRNStatus@1000000002 : Text;
      eInvoiceSetup@1000 : Record 50050;
      AuthToken@1004 : Text[250];
      SuccessText@1000000004 : Text[10];
      QRCodeFileName@1000000005 : Text;
      FileMgmt@1000000006 : Codeunit 419;
                  ArrayString@1000000009 : Text; 
      JObject@1000000008 : DotNet "'Newtonsoft.Json'.Newtonsoft.Json.Linq.JObject";
      JSONArray@1000000007 : DotNet "'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'.Newtonsoft.Json.Linq.JArray";
      RequestResponse@1000000010 : BigText;
    BEGIN
      eInvoiceSetup.GET();
               IF eInvoiceSetup."Integration Enabled" THEN BEGIN 
                               
      HttpWebRequestMgt.Initialize(eInvoiceSetup."Base URL" + eInvoiceSetup."
Generate E-Invoice URL");
      HttpWebRequestMgt.DisableUI();
      HttpWebRequestMgt.SetMethod('POST');
                         //Write similar function to get the Auth Token
      HttpWebRequestMgt.AddHeader('Bearer', AuthToken);
      HttpWebRequestMgt.SetContentType('application/json');
      HttpWebRequestMgt.SetReturnType('application/json');
                         RequestText := FORMAT(StringBuilder.ToString); 
      HttpWebRequestMgt.AddBodyAsText(RequestText);
      
      WasSuccess := FALSE;
      ErrorText := '';
      IRNText := '';
      SignedQR := '';
      SignedInv := '';
       AckNumber := '';
      QRPayload := '';
      IRNStatus := '';
      TempBlob.INIT;
      TempBlob.Blob.CREATEINSTREAM(InStr);
      IF HttpWebRequestMgt.GetResponse(InStr,HTTPStatusCode,ResponseHeader) THEN BEGIN
        IF FORMAT(HTTPStatusCode.ToString) IN ['OK','Ok','ok'] THEN
          WasSuccess := TRUE;
          Json := TempBlob.ReadAsText('',TEXTENCODING::UTF8);
          
          JSONManagement.InitializeFromString(Json);
          SuccessText := JSONManagement.GetValue('Success');
          IF UPPERCASE(SuccessText) = 'Y' THEN BEGIN
            AckNumber := JSONManagement.GetValue('AckNo');
            AckDateText := JSONManagement.GetValue('AckDt');
            IRNText := JSONManagement.GetValue('Irn');
            SignedInv := JSONManagement.GetValue('SignedInvoice');
            SignedQR := JSONManagement.GetValue('SignedQRCode');
            IRNStatus := JSONManagement.GetValue('Status');
          END ELSE BEGIN
            JObject := JObject.JObject();
            JSONManagement.GetJSONObject(JObject);
            ArrayString := JObject.SelectToken('ErrorDetails').ToString;
            CLEAR(JSONManagement);
            CLEAR(JObject);
            JObject := JObject.JObject();
            JSONArray := JSONArray.JArray();
            JSONManagement.InitializeCollection(ArrayString);
            JSONManagement.GetJsonArray(JSONArray);
            FOREACH JObject IN  JSONArray DO BEGIN
              ErrorText := JObject.GetValue('error_message').ToString;
            END
          END;
      END ELSE
        SuccessText := 'N';
 
      //Create Entry
      eInvoiceEntry.INIT;
      IF IsInvoice THEN
        eInvoiceEntry."Document Type" := eInvoiceEntry."Document Type"::Invoice
      ELSE
        eInvoiceEntry."Document Type" := eInvoiceEntry."Document Type"::CrMemo;
      eInvoiceEntry."Document No." := DocumentNo;
      
      CLEAR(RequestResponse);
      RequestResponse.ADDTEXT(RequestText);
      eInvoiceEntry."Request JSON".CREATEOUTSTREAM(OStream);
      RequestResponse.WRITE(OStream);
      
      CLEAR(RequestResponse);
      RequestResponse.ADDTEXT(Json);
      eInvoiceEntry."Response JSON".CREATEOUTSTREAM(OStream);
      RequestResponse.WRITE(OStream);
     
      IF NOT eInvoiceEntry.INSERT THEN
        eInvoiceEntry.MODIFY;
     
      IF UPPERCASE(SuccessText) = 'Y' THEN BEGIN
        QRCodeFileName := GetQRCode(SignedQR);
        QRCodeFileName := MoveToMagicPath(QRCodeFileName); // To avoid confirmation dialogue on RTC
        CLEAR(TempBlob);
        FileMgmt.BLOBImport(TempBlob,QRCodeFileName);
        IF TempBlob.Blob.HASVALUE THEN BEGIN
          eInvoiceEntry."QR Code Image" := TempBlob.Blob;
        END;
        eInvoiceEntry.Status := eInvoiceEntry.Status::Generated;
        eInvoiceEntry."Acknowledgment No." := AckNumber;
        eInvoiceEntry."Acknowledgment Date" := AckDateText;
        eInvoiceEntry.IRN := IRNText;
        eInvoiceEntry."IRN Status" := IRNStatus;
        eInvoiceEntry.SignedQRWriteAsText(SignedQR,TEXTENCODING::UTF8);
        eInvoiceEntry.SignedInvWriteAsText(SignedInv,TEXTENCODING::UTF8);
        eInvoiceEntry."QR Code".CREATEOUTSTREAM(OStream,TEXTENCODING::UTF8);
        OStream.WRITETEXT(QRPayload,STRLEN(QRPayload));
      END ELSE BEGIN
        eInvoiceEntry.Status := eInvoiceEntry.Status::Fail;
        eInvoiceEntry."Error Message" := COPYSTR(ErrorText,1,250);
      END;
      eInvoiceEntry.RequestWriteAsText(RequestText,TEXTENCODING::UTF8);
      eInvoiceEntry.ResponseWriteAsText(Json,TEXTENCODING::UTF8);
      eInvoiceEntry."Created By" := USERID;
      eInvoiceEntry."Created Date Time" := CURRENTDATETIME;
      eInvoiceEntryMODIFY;
    END ELSE BEGIN 
                   FileName := DELCHR(FileName,'=','/');
                     ServerFile := FileManagement.ServerTempFileName('.json');
                     IF FILE.EXISTS(ServerFile) THEN
                       ERASE(ServerFile);
                    TempFile.CREATE(ServerFile);
                    TempFile.CREATEOUTSTREAM(OutStrm);
                    OutStrm.WRITETEXT(DN_JSonConvert.Parse(StringBuilder.ToString).ToString);
                    TempFile.CLOSE;
                    LocalFile := FileManagement.SaveFileDialog('Select Folder to Save Json',FileName + '.Json','*.JSON|*.json');
                    IF LocalFile <> '' THEN
FileManagement.DownloadToFile(ServerFile,LocalFile); 
    END;
END;

Note: This code might not run directly, you need to change/update accordingly. Codeunit: Http Web Request Mgt., JSON Management, & File Management are sufficient to call API & easily parse response using few supporting .net variables.

Step 5: Similarly need to write code for cancel IRN. Give a button at e-Invoice Entries page.

Step 6: Finalize your solution by putting required validations to avoid errors. Check the link for validation. 

Happy to help you, pleas comment & give feedback. Or, you may contact us for further detailed information. 

Thanks for reading...


Friday, December 18, 2020

Digital selling helps Grant Thornton engage clients at scale

This year, I've shared inspiring stories of organizations responding to adversity by building digital resilience into the fabric of their operations and cultures. A common thread between these organizations is the agility to be better prepared for change, as well as the ability to stay focused on putting customers first. That's why it's apropos toRead more

The post Digital selling helps Grant Thornton engage clients at scale appeared first on Microsoft Dynamics 365 Blog.

Thursday, December 17, 2020

Fraud trends part 2: holiday fraud and account protection

In this blog series we are exploring the latest fraud-related events and issues on the rise in our digitized world. The first installment in the series explored account takeovers (ATO) and the $7 billion yearly loss affecting even the largest organizations. In this post, we are examining three current trends contributing to increased fraud inRead more

The post Fraud trends part 2: holiday fraud and account protection appeared first on Microsoft Dynamics 365 Blog.

Increase visibility into inventory across your supply chain

To run an effective manufacturing and distribution operation, you need an accurate understanding of your inventory and where it's located along the supply chain. What's more, having clear visibility into your inventory has become critically important as organizations shift to omnichannel order fulfillment and distribution and therefore need to manage inventory located in multiple placesRead more

The post Increase visibility into inventory across your supply chain appeared first on Microsoft Dynamics 365 Blog.

Tuesday, December 15, 2020

Ways to mitigate post-purchase fraud during the holidays

With many retailers extending online holiday sales, consumers are taking advantage of the ease of shopping from home. Online shopping can also enable more impulse buying, which in turn increases the likelihood of returns and cancelations and can open the door to abuse and fraud. Merchants need the right tools and controls to helpRead more

The post Ways to mitigate post-purchase fraud during the holidays appeared first on Microsoft Dynamics 365 Blog.

Monday, December 14, 2020

Robust warehouse execution with Dynamics 365 Supply Chain Management

To help companies execute mission-critical manufacturing and distribution processes without interruptions, we recently announced a preview for cloud and edge scale unit add-ins for Dynamics 365 Supply Chain management.

The post Robust warehouse execution with Dynamics 365 Supply Chain Management appeared first on Microsoft Dynamics 365 Blog.

Thursday, December 10, 2020

How healthcare organizations can share information securely

With the global disruption of the pandemic, healthcare organizations are particularly challenged to promote telehealth services and engage patients and providers while adhering to strict compliance and security regulations. Marketing automation and customer relationship management (CRM) tools such as Microsoft Dynamics 365 Marketing and Microsoft Dynamics 365 Customer Insights are expanding the ways in whichRead more

The post How healthcare organizations can share information securely appeared first on Microsoft Dynamics 365 Blog.

Tuesday, December 8, 2020

5 takeaways how a company turned needs into opportunities

In today's modern sales environment a trend is surfacing among B2B sellers and buyers: the desire for more authentic, personalized relationships. But despite the desire for personalized relationships, purchasing involves more people than ever, and successful sales require sellers to navigate an increasingly large group of stakeholders. While increasingly large groups of stakeholders may createRead more

The post 5 takeaways how a company turned needs into opportunities appeared first on Microsoft Dynamics 365 Blog.

Monday, December 7, 2020

3 unique success stories—building resilience into the supply chain

Even before the pandemic, organizations across industries, from manufacturing to retailers, felt the squeeze to modernize supply chain operations. The historic supply chain shock of 2020 has accelerated the need to both respond to immediate disruptions while building resilience into every layer of the supply chain. In the past, rapidly overhauling a supply chain operationRead more

The post 3 unique success stories—building resilience into the supply chain appeared first on Microsoft Dynamics 365 Blog.

Thursday, December 3, 2020

Unify ecosystem-wide data with a flexible customer data platform

In today's digital economy, delivering an exceptional customer experience across every touchpoint has quickly become a competitive differentiator. Most organizations realize if they hope to remain successful they must effectively leverage the vast amounts of data available to them in order to support the personalized engagement that customers expect. According to McKinsey, organizations that harnessRead more

The post Unify ecosystem-wide data with a flexible customer data platform appeared first on Microsoft Dynamics 365 Blog.

Tuesday, December 1, 2020

3 ways to minimize fraud this holiday season

Even in challenging times, the holiday season's irresistible deals attract both customers and fraudsters. A differentiated fraud prevention strategy is essential to keep a merchant's fraud losses minimized while letting legitimate customers continue to have a smooth shopping experience. Consumers often change buying and engagement patterns with merchants during the holiday season like shipping toRead more

The post 3 ways to minimize fraud this holiday season appeared first on Microsoft Dynamics 365 Blog.