With Colorado adding a new deliver fee tax, I am in search of a method to call the Avalara recalculation process after updating an invoice in NetSuite. We have to add a new line item associated with this tax and then have it reprocess the Avalara functions to be able to include the new line on the Avalara tax. I have completed a simple workflow to add the new line. However, it does not recall the Avalara scripts, thus, not adding the information to the taxing file. Has anyone been able to call Avalara scripts in NetSuite post workflow or script completions?
Thanks in advance,
Brad
I spent far too much time dealing with this exact issue last week. I tried to go the workflow route as well but ultimately decided a script would be needed. I tried a few methods, but the ultimate solution was to create a non-fulfillable, non-inventory item and use a User Event script deployed on the Sales Orders to add the RDF item to applicable records when they are created. In our case, these SOs are created in the UI, from a CSV import, or imported from our Shopify Webstore through a Suitelet (and Celigo). The basis of the script is as follows:
const beforeSubmit = (context) => {
let salesOrder = context.newRecord;
let shipState = salesOrder.getValue('shipstate');
let channel = salesOrder.getValue('class');
let total = salesOrder.getValue('total');
let customerId = salesOrder.getValue('entity');
if (!rdfExists(salesOrder) && shipState == 'CO' && (channel == '5' || channel == '6') || (channel == '15' && total > 0)) {
if (customerTaxable(customerId)) {
addRDF(salesOrder);
}
}
}
Essentially this is just checking some parameters (that the SO doesn't already have a RDF line item somehow, that the Ship State is CO, and that it is one of our custom channels that meets the criteria for these orders. If the order meets those criteria it will load the Customer record to check if the customer is taxable. And if they are taxable, it adds the RDF line item to the record.
This obviously includes some helper functions, which I will include snippets of, but take them with a grain of salt, we are in a highly customized instance and what works for us may not work for you (also I'm not a professional developer and this was written for completeness, not optimization).
function rdfExists(record) {
//Loop through line items looking for 'Colorado Retail Delivery Fee' item
let numLines = record.getLineCount('item');
const RDFITEM = runtime.getCurrentScript().getParameter('custscript_co_rdf_id');
for (let i = 0; i < numLines; i++) {
let itemId = record.getSublistValue({
sublistId: 'item',
fieldId: 'item',
line: i
});
if (itemId == RDFITEM) {
return true;
}
}
return false;
}
function addRDF(record) {
const RDFITEM = runtime.getCurrentScript().getParameter('custscript_co_rdf_id');
let numLines = record.getLineCount('item');
record.insertLine({
sublistId: 'item',
line: numLines,
ignoreRecalc: false
});
record.setSublistValue({
sublistId: 'item',
fieldId: 'item',
line: numLines,
value: RDFITEM
});
record.setSublistValue({
sublistId: 'item',
fieldId: 'quantity',
line: numLines,
value: 1
});
record.setSublistValue({
sublistId: 'item',
fieldId: 'taxcode',
line: numLines,
value: '5025'
});
}
function customerTaxable(customerId) {
let customerRecord = record.load({
type: record.Type.CUSTOMER,
id: customerId
});
if (customerRecord.getValue('taxable')) {
return true;
} else {
return false;
}
}
This successfully added the line to the Sales Orders but was still not calling Avalara to calculate the tax on the new item. To accomplish this, there were a few more steps:
Set the item's Avatax Tax Code to OF400000 (per Avalara help article here)
Do essentially the opposite of this Avalara help article and move the AVA_TransactionTab_2 UE script to the bottom of the Scripted Record list (Avalara says it goes at the bottom, but ours was first for some reason).
Modify the file /SuiteBundles/Bundle 1894/utility/AVA_TaxLibrary.js to allow for calculation of taxes on the new RDF item. This is the part I'm least excited about. I tried a ton of things, but the tax wouldn't calculate. Very long story short the line item was "failing" the AVA_MainTaxCodeCheck function that was called in the AVA_RequiredFieldsFunction that was called in the AVA_TransactionBeforeSubmit function and therefore not running the AVA_CalculateTax function... confused yet? Basically I just modified that library to include a statement that if it failed to find the lineTaxCode for an item, to check if it was my RDF Item, and if it was to calculate tax regardless (the line item is only added to the orders I want it to taxed on). I don't want to share my hardcoding of that library in public, but message me if you want more info.
From there we modified the SO and Invoice PDF templates to include logic to remove the $.27 from the tax line on these orders and add it as it's own line item:
All in all - it's a solution that works for us. Obviously your buisness case will be different, but this is what worked for us, and hopefully can at least steer you in the right direction! All this for 27 cents...
Related
Is it possible to fill the duedate field based on the terms field? For example, I have a date in the duedate field, but I want to extend it based on the terms field. How can I do it? Through Suitescript or workflow?
The code is incomplete because I don’t know if I’m on the right path.
(NB: It is a user event)
function beforeLoad(context) {
}
function beforeSubmit(context) {
}
function afterSubmit(context) {
var dataEntrega = context.currentRecord
currentRecordRecord.getValue({
fieldId: 'duedate'
})
var dataCondicoes = context.currentRecord
currentRecord.getValue({
fieldId: 'terms'
})
}
return {
beforeLoad: beforeLoad,
beforeSubmit: beforeSubmit,
afterSubmit: afterSubmit
}
Why do you need to script this? I believe the due date is calculated based on the terms out of the box, no need for scripting
The following code should work. Depending on your needs I recommend adding some limitations to when this logic is executed. For example you can only execute based on if the transaction/record mode is create/copy (decide if you want to include edit or not). You can also check the status of the transaction, and only execute if the status is not partially paid/paid in full...
function afterSubmit(context) {
//load record
var curRec = context.newRecord; //can substitute "context.oldRecord", or "currentRecord.get();"
//get current due date
var transDueDate = curRec.getValue({fieldId: 'duedate'});
//get the terms, this will likely come as an internal id. use getText if you want the text.
var transTerms = curRec.getValue({fieldId: 'terms'});
//empty string to hold terms as a number of days
var addtlDays;
//transform the internal id to terms as a number of days
switch (transTerms){
case 1: // Ex: 1 = internal id for term "Net 15"
addtlDays = 15;
break;
case 2: // Ex: 2 = internal id for term "Net 30"
addtlDays = 30;
break;
//add additional case statements as needed
default:
addtlDays = 0;
}
//calculuate the new due date
var d = new Date(transDueDate);
var newDueDate = d.setDate(d.getDate() + addtlDays);
//set the new due date
curRec.setValue({
fieldId: 'duedate',
value: newDueDate,
ignoreFieldChange: true //optional, default is false
});
}
Currently I am able to send data to NS through a Restlet but it seems that I am only able to create a customrecord type but cannot seem to find out how to create a non custom record.
How do I find the name or endpoints. I have added my restlet to the SuiteScript but that is as far as I got.
EDIT
PartOrderREST.postRESTlet = function(dataIn) {
nlapiLogExecution('audit', 'PartOrderREST.postRESTlet', 'JSON=' + JSON.stringify(dataIn));
try {
var nsFields = new PO_OBJ_FIELDS();
var recPO = nlapiCreateRecord(nsFields.purchaseorder, {recordmode: 'dynamic'});
recPO.setFieldValue('entity', dataIn['entity']);
recPO.setFieldValue('name', dataIn['name']);
recPO.setFieldValue('employee', dataIn['employee']);
recPO.setFieldValue('class', dataIn['class']);
recPO.setFieldValue('location', dataIn['location']);
recPO.setFieldValue('exchangerate', '2.15');
recPO.setFieldValue('currency', '2.15');
recPO.setFieldValue('trandate', dataIn['trandate']);
} catch(err) {
nlapiLogExecution('audit', 'PartOrderREST.postRESTlet', err.message);
return {'error': 'error 1' + err.message}
}
try {
recPO.selectNewLineItem('item');
recPO.setCurrentLineItemValue('item', 'quantity', 1);
recPO.setCurrentLineItemValue('item', 'item', dataIn['item']);
recPO.commitLineItem('item');
recPO.selectNewLineItem('item');
recPO.setCurrentLineItemValue('item', 'quantity', 1);
recPO.setCurrentLineItemValue('item', 'item', dataIn['item']);
recPO.commitLineItem('item');
var idPO = nlapiSubmitRecord(recPO, true);
return {'nswoid': idPO};
} catch(err) {
nlapiLogExecution('audit', 'PartOrderREST.postRESTlet', err.message);
return {'error': 'error 2' + err.message}
}
};
This issue is that I get an error saying something along the lines of need line item to create record. I try adding the item to the purchase order but it is removed before it is submitted.
The first part of the code in the first try statement seems to work. I does not seem to successfully add a line item of 'item' given the specific nsid of the item I want to add to the order. 'item' is said to be the only required element of the Purchase order.
Can you hardcode the value in your restlet to see if your restlet works without parameterised data?
I have tried below code and it works:
var recPO=nlapiCreateRecord('purchaseorder', {recordmode: 'dynamic'});
recPO.setFieldValue('customform',formId);
recPO.setFieldValue('entity', entityId);
recPO.setFieldValue('name', 'Name');
recPO.setFieldValue('employee', employeeId);
recPO.selectNewLineItem('item');
recPO.setCurrentLineItemValue('item','item',itemId);
recPO.setCurrentLineItemValue('item', 'amount', 1);
recPO.setCurrentLineItemValue('item', 'quantity',100);
recPO.commitLineItem('item');
var id = nlapiSubmitRecord(recPO, true);
If you still get error after hardcoding the values then there might be some script deployed on PurchaseOrder throwning the error. You can check the scripts deployed on record by following below path:-
Customization->Scripting->Scripted Records->Purchase Order
Code Portion Click Here
I am trying to populate the Department line item field as per the Department Transaction Body Field, please assist to check if my codes are right.. i am new to suitescript.
var itemDepartment = nlapiGetFieldValue('department');
var nlapiSetCurrentLineItemValue = nlapiSetCurrentLineItemValue('item', 'department_display', itemDepartment);
It keeps stating that department_display is not an internal ID.
Please advise.
Thank you.
Can you try setting text instead,if it is a client script.
The id of the department column field is 'department'
var itemDepartment = nlapiGetFieldText('department');
nlapiSetCurrentLineItemText('item', 'department', itemDepartment);
The id of the department column field is also department, same as the header field.
Therefore, the second line of you snippet should be:
nlapiSetCurrentLineItemValue('item','department',itemDepartment);
EDIT
As per the comment below, please find below the full code snippet to populate lines department from the customer on before submit:
function onBeforeSubmit(type) {
if (type == 'create' || type == 'edit') {
var customerId = nlapiGetFieldValue('entity');
var itemDepartment = nlapiLookupField('customer',customerId, 'custentity_department');
var itemCount = nlapiGetLineItemCount('item');
for (var i = 1; i <= itemCount; i++) {
nlapiSetLineItemValue('item', 'department', i, itemDepartment);
}
}
}
Also, as a side note, you don't have to load the whole record in order to get a field value. You should use nlapiLookupField instead. It's much faster, safer and less api usage.
if (latestpayment == 0) {
var newrecord = nlapiTransformRecord('vendorbill',
results[x][7], 'vendorpayment');
newrecord.setFieldValue('amount', intPayment);
// newrecord.setFieldValue('documentstatus', 'B');
// newrecord.setFieldValue('status', 'Paid In Full');
// newrecord.setFieldValue('statusRef', 'paidInFull');
newrecord.setFieldValue('account', stRAcctId);
newrecord.setFieldValue('trandate', stRPostingDate);
nlapiSubmitRecord(newrecord);
nlapiSubmitRecord(record);
} else {
var newrecord = nlapiTransformRecord('vendorbill',
results[x][7], 'vendorpayment');
newrecord.setFieldValue('amount', intPayment);
newrecord.setFieldValue('debitamount', payment);
newrecord.setFieldValue('paidamount', payment);
nlapiLogExecution('DEBUG', 'payment', payment);
// newrecord.setFieldValue('documentstatus', 'B');
// newrecord.setFieldValue('status', 'Paid In Full');
// newrecord.setFieldValue('statusRef', 'paidInFull');
newrecord.setFieldValue('account', stRAcctId);
newrecord.setFieldValue('trandate', stRPostingDate);
record.setFieldValue('amountpaid', payment);
record.setFieldValue('amountremaining', intlatestpayment);
nlapiLogExecution('DEBUG', 'amountremaining', intlatestpayment);
nlapiSubmitRecord(newrecord);
nlapiSubmitRecord(record);
I have a customized form to submit and all those selected will be transform from vendor bill to vendor payment. I have no issue on this when I do full amount payment and the status will be shown as Paid In Full.
However, when I do partially payment, I cannot do anything and change the status to OPEN. I tried to set the value for both vendor bill and vendor payment record but its doesnt work. Any idea on how to control this partially payment and shown as OPEN with all the amount paid and amount remaining show?
Thank you.
var newrecords = nlapiCreateRecord('vendorpayment', {
recordmode : 'dynamic'
});
newrecords.setFieldValue('entity', aresults[0]
.getValue('internalid'));
newrecords.setFieldValue('account', stRAcctId);
newrecords.setFieldValue('trandate', stRPostingDate);
var lineNum = newrecords.findLineItemValue('apply',
'internalid', results[x][7]);
newrecords.selectLineItem('apply', lineNum);
newrecords.setCurrentLineItemValue('apply', 'apply', 'T');
newrecords.setCurrentLineItemValue('apply', 'amount', payment);
newrecords.commitLineItem('apply');
nlapiSubmitRecord(newrecords, false, false);
nlapiSubmitRecord(record);
Thanks god the above code works. I am able to create a vendor payment record with the code above. Thanks to the link: http://blog.prolecto.com/2013/06/02/automate-creating-checks-to-pay-bills/
It saves my day :)
I am trying to incorporate a check at the item line level when creating an invoice. Basically if they are adding an item within a certain category (custitem8) i need an alert to pop up for the sales rep.
Not sure if this should be using fieldchanged or validateline.
Sorry Im not really a programmer and am learning on the job mostly by trial and error. Thanks for your help.
function ValidateLine(type)
{
if (nlapiGetCurrentLineItemValue('item', 'custitem8') = 'Order in Only - Not For Trade Guide')
{
alert("Order In Only, Please contact Purchasing");
}
return true;
}
The suggested code will not work, instead of using nlapiGetLineItemValue use nlapiGetCurrentLineItemValue.
the code should look like this.
postSourcing(sublistId, fieldId) {
if(sublistId == "item" && fieldId == "item") {
var itemId = nlapiGetCurrentLineItemValue(sublistId, fieldId);
var category = nlapiLookupField("item", itemId, "custitem8");
if(category == "Order in Only - Not For Trade Guide") {
alert("Order In Only, Please contact Purchasing");
}
}
}
I'm assuming you just need an alert when the user selects a line Item? If so, I would suggest using postSourcing(sublistId, fieldId) (though using validateLine(sublistId) works just fine).
As for the actual function content, I'm assuming (based on the field ID) "custitem8" is a field on the Item record. If so, you will have to load the field from the Item record first.
Based on my understanding of your post, I would go about it like this:
postSourcing(sublistId, fieldId) {
if(sublistId == "item" && fieldId == "item") {
var itemId = nlapiGetLineItemValue("item", "item");
var category = nlapiLookupField("item", itemId, "custitem8");
if(category == "Order in Only - Not For Trade Guide") {
alert("Order In Only, Please contact Purchasing");
}
}
}
And just a note, I don't really know the data type of the "custitem8" field, so I'm just assuming it's a free-form text field.