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:
-
Add new node to the XML file.
-
Add or remove attributes to the existing node.
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:
-
Custom data source plug-in implementation.
-
Rendering the data sources on e-document template (template for outbound e-documents).
-
Digital signature plug-in implementation (if defined).
-
Outbound extension plug-in implementation.
-
XSD validation (if defined).
-
Outbound validation plug-in implementation (if defined).
-
PDF generation (if configured).
Based on the sequence above, note the following:
-
In the Outbound Extension Plug-in implementation:
-
The system provides the XML with the digital signature applied (if defined) for extension.
-
You cannot reference the generated PDF because the system creates it later, after plug-in execution.
-
-
The system applies template validations (XPath regex, XSD, and Outbound Validation Plug-in implementation) to the extended e-document, as it performs these validations after the (optional) extension.
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,
};
});