Creating a Custom Plug-in Implementation for Extend E- Invoicing Plug-in

A custom extend e- invoicing plug-in implementation customizes and extends the structure of generated e-documents. You can modify the XML file rendered from the e-document template (Template for outbound e-documents) to:

This functionality is not limited to Avalara templates; you can also use it with non- Avalara templates. You can also use this functionality to extend JSON e-documents.

The extend e- invoicing plug-in implementation that you define executes in the following order:

  1. Custom data source plug-in implementation.

  2. Rendering the data sources on e-document template (template for outbound e-documents).

  3. Digital signature plug-in implementation (if defined).

  4. Outbound extension plug-in implementation.

  5. XSD validation (if defined).

  6. Outbound validation plug-in implementation (if defined).

  7. PDF generation (if configured).

Based on the sequence above, note the following:

The following code provides a sample custom plug-in implementation for Extend E-Invoicing that extends the rendered e-document available at params.eDocContent and returns the extended e-document result.content:

          /**
 *@NApiVersion 2.x
 *@NScriptType plugintypeimpl
 */
define(["N/log"], function (log) {
    /**
     * Modifies the e-document content.
     *
     * @param {Object} params - Parameters for document modification.
     * @param {Object} params.transactionRecord - Details of the transaction record (loaded via N/record)
     * @param {string|number} params.transactionId - Transaction Internal ID
     * @param {string|number} params.userId - User internal ID (Current User or First Active Admin)
     * @param {Object} params.transactionData - Returned as part of CDS response (customDataSources[0].data)
     * @param {string} params.eDocContent - E-document content
     * @returns {Object} Result object indicating success or failure.
     * @returns {boolean} result.success - Indicates whether the modification was successful.
     * @returns {string} [result.content] - The modified e-document content (present when success is true).
     * @returns {string} [result.eiAuditTrailMsg] - Audit trail message or error description (present when success is false).
     *
     */
    function extend(params) {
        var result = {
            success: false,
        };
        try {
            var finalContent = "";
            /**
             * Implement logic to extend the e-document content. For XML e-documents, N/xml can be used.
             */
            result.success = true;
            result.content = finalContent; // Contains final XML or JSON value
        } catch (err) {
            // Log any errors that occur during e-document content extension.
            log.error("Plugin Error", err);
            // Return an error response indicating failure.
            // eiAuditTrailMsg is displayed on the Transaction E-Document Audit Trail.
            result.eiAuditTrailMsg = err.message; // Default message will be used if message is not provided
        }
        return result;
    }

    return {
        extend: extend,
    };
}); 

        

Invoice XML Extension Example

This section consists of the Invoice XML extension example: Input, Required Additions, and Final Output.

Input (XML before modification from params.eDocContent)

              <Invoice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
  xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
  xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
  xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
  xmlns:ava="Custom_Components"
  xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2
http://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd">
    <cbc:ID>12345</cbc:ID>
  </Invoice> 

            

XML added under <invoice>

              <cec:UBLExtensions>
    <cec:UBLExtension>
        <cec:ExtensionContent>
            <ava:DocumentExtension>
                <ava:InvoiceData>
                    <ava:InvoiceType>R1</ava:InvoiceType>
                </ava:InvoiceData>
                <ava:InvoiceDataCorrection>
                    <ava:TypeOfCorrection>S</ava:TypeOfCorrection>
                    <ava:RectificationAmount>
                        <ava:RectifiedTaxableAmount>-118.5</ava:RectifiedTaxableAmount>
                        <ava:RectifiedTaxAmount>-24.9</ava:RectifiedTaxAmount>
                        <ava:RectifiedSurcharge>0</ava:RectifiedSurcharge>
                    </ava:RectificationAmount>
                </ava:InvoiceDataCorrection>
            </ava:DocumentExtension>
        </cec:ExtensionContent>
    </cec:UBLExtension>
</cec:UBLExtensions> 

            

Final result (high-level)

The final XML is the original params.eDocContent plus the above cec:UBLExtensions block appended under <Invoice>.

Code Sample:

              /**                                                                                                                        
   *@NApiVersion 2.1                                                                                                         
   *@NScriptType plugintypeimpl                                                                                              
   */
  define(["N/log", "N/xml"], function(log, xml) {
      /**                                                                                                                    
       * Modifies the e-document content.                                                                                    
       *                                                                                                                     
       * params.eDocContent = XML before modification (originally rendered Invoice XML)                                      
       */
      function extend(params) {
          var result = {
              success: false,
          };

          try {
              var xmlBeforeModification = params.eDocContent;
              log.debug("XML before modification (params.eDocContent)", xmlBeforeModification);

              var finalContent = addRequiredContent(xmlBeforeModification);

              result.success = true;
              result.content = finalContent;
          } catch (err) {
              log.error("Plugin Error", err);
              result.eiAuditTrailMsg = err.message;
          }

          return result;
      }

      function addRequiredContent(eDocContent) {
          if (!eDocContent) {
              throw new Error("Missing eDocContent (XML before modification).");
          }

          var xmlDoc = xml.Parser.fromString({
              text: eDocContent,
          });

          applyRequiredExtension(xmlDoc);

          return xml.Parser.toString({
              document: xmlDoc,
          });
      }

      // Adds required XML under <Invoice>:                                                                                  
      // <cec:UBLExtensions> ... </cec:UBLExtensions>                                                                        
      function applyRequiredExtension(doc) {
          var cecNS =
              "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2";
          var avaNS = "Custom_Components";

          var ubExtensions = doc.createElementNS({
              namespaceURI: cecNS,
              qualifiedName: "cec:UBLExtensions",
          });

          var ubExtension = doc.createElementNS({
              namespaceURI: cecNS,
              qualifiedName: "cec:UBLExtension",
          });
          ubExtensions.appendChild({ newChild: ubExtension });

          var extensionContent = doc.createElementNS({
              namespaceURI: cecNS,
              qualifiedName: "cec:ExtensionContent",
          });
          ubExtension.appendChild({ newChild: extensionContent });

          var docExtension = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:DocumentExtension",
          });
          extensionContent.appendChild({ newChild: docExtension });

          var invoiceData = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:InvoiceData",
          });
          docExtension.appendChild({ newChild: invoiceData });

          var invoiceType = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:InvoiceType",
          });
          invoiceType.textContent = "R1";
          invoiceData.appendChild({ newChild: invoiceType });

          var invoiceDataCorrection = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:InvoiceDataCorrection",
          });
          docExtension.appendChild({ newChild: invoiceDataCorrection });

          var typeOfCorrection = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:TypeOfCorrection",
          });
          typeOfCorrection.textContent = "S";
          invoiceDataCorrection.appendChild({ newChild: typeOfCorrection });

          var rectificationAmount = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:RectificationAmount",
          });
          invoiceDataCorrection.appendChild({ newChild: rectificationAmount });

          var rectifiedTaxableAmount = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:RectifiedTaxableAmount",
          });
          rectifiedTaxableAmount.textContent = "-118.5";
          rectificationAmount.appendChild({ newChild: rectifiedTaxableAmount });

          var rectifiedTaxAmount = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:RectifiedTaxAmount",
          });
          rectifiedTaxAmount.textContent = "-24.9";
          rectificationAmount.appendChild({ newChild: rectifiedTaxAmount });

          var rectifiedSurcharge = doc.createElementNS({
              namespaceURI: avaNS,
              qualifiedName: "ava:RectifiedSurcharge",
          });
          rectifiedSurcharge.textContent = "0";
          rectificationAmount.appendChild({ newChild: rectifiedSurcharge });

          var invoiceNode = doc.getElementsByTagName({
              tagName: "Invoice",
          })[0];

          if (!invoiceNode) {
              throw new Error("Invoice root node not found.");
          }

          invoiceNode.appendChild({ newChild: ubExtensions });
      }

      return {
          extend: extend,
      };
  }); 

            

General Notices