Suitescript Invoice for only Fulfilled Items on Sales Orders - netsuite

I am fairly new to suitescript and would like to build a script that runs on a recurring schedule and only invoices based on item fulfillments (ie there is a one to one relationship between the invoice and item fulfillment but potentially a one to many relationship between the sales order and invoice/item fulfillments).
I am able to successfully run a script that transforms the sales order in total into an invoice - however this includes all items on sales order and not just the items that have been fulfilled (we have many situations in our business in which only partial fulfillment occurs and the order must then be closed out). At this point from my research I'm unable to find other examples of this script being successfully created. My initial idea is to somehow store the 'qtyfulfilled' on the sales order in an an array with the items and somehow create an invoice with this. However to me it seems that the transform function is the best way to retain a link between sales order and the invoice.
var invoice = record.transform({
fromType: record.Type.SALES_ORDER,
fromID: salesOrderId,
toType: record.Type.INVOICE,
isDynamic: true
});
This returns an invoice with all items from the sales order rather than just the fulfilled items.
EDIT:
I implemented a version of the code suggested where I just iterate over the lines of the sales order and replace each line quantity on the invoice with 'fulfilledquantity' - however I realized when the quantity is zero the invoice appears to be still storing that line at a zero value.
Downstream of the invoice script I'm passing the invoice record to our EDI provider via a NS integration. The invoices created via my script map into the EDI 810 (invoice record) with 0 quantity lines for the line items that werent fulfilled which cause errors in the EDI file.
I wrote the following to iterate through invoice lines after the quantity and remover the zero quantity lines but am getting the following error: "name":"USER_ERROR","message":"Please choose an item to add" -
for (var k = 0; k < lineCount; k++) {
var currentInvoiceLineQuantity = newInvoice.getSublistValue ({
sublistId: 'item',
fieldId: 'quantity',
line: k
});
if(currentInvoiceLineQuantity === 0){
newInvoice.removeSublistSubrecord({
sublistId: 'item',
fieldid: 'item',
line: k
});
k--;
lineCount--;
}
}

After you transform the sales order to an invoice, iterate through the lines and change the quantity to only invoice the amount that was fulfilled.
The sample below is from a legacy script written in SS1.0 but will translate pretty easily to SS2.0.
var lineCount = invoice.getLineItemCount('item');
for (var i = 1; i <= lineCount; i++) {
const quantity = invoice.getLineItemValue('item', 'quantity', i);
const remaining = invoice.getLineItemValue('item', 'quantityremaining', i);
invoice.setLineItemValue('item', 'quantity', i, quantity - remaining);
}

If you do this in an After Submit user event on Item Fulfillments you can transform the related Sales Order to an invoice and then walk backwards through the line times and delete the ones not on the fulfillment.
You should check quantities for the against the shipped quantities and you may need to have logic for billing any unfulfillable items ( I do that either the first fulfillment on an order or when there are no more lines to ship. )
Finally you may need to handle shipping charge logic to either charge the amount of shipping on each fulfillment or charge the fulfillment’s shipping up to the shipping charge on the original sales order.

Related

NetSuite SuiteTalk Item Fulfillment Error - VALID_LINE_ITEM_REQD

Any suggestions are greatly appreciated!
The sales orders can have 50-100 lines. Each SO will have multiple item fulfillments. When we create the IF through SuiteTalk, we don't know which lines are already fulfilled or which lines are partially fulfilled.
When I create the soap, I am overriding the item list.
The code works fine if there is only 2 items on the sales order and all lines are being fulfilled.
The trouble arises when another process has added / deleted items on the sales order thus causing an order with 5 lines to have soLines such as 1, 45, 245, 300 & 301. If I am trying to fulfill line 245, I will receive VALID_LINE_ITEM_REQD.
Would anyone have suggestions on what needs to be initialized?
Thank you in advance,
Bill
Below is a sample request/response.
Request
<record xmlns:q1="urn:sales_2020_1.transactions.webservices.netsuite.com" xsi:type="q1:ItemFulfillment" externalId="mrk-so-2446425">
<q1:createdFrom internalId="2446425"/>
<q1:shippedDate>2020-12-25T06:00:00Z</q1:shippedDate>
<q1:shipStatus>_shipped</q1:shipStatus>
<q1:tranDate>2020-12-25T06:00:00Z</q1:tranDate>
<q1:generateIntegratedShipperLabel>false</q1:generateIntegratedShipperLabel>
<q1:itemList>
<q1:item>
<q1:location internalId="308"/>
<q1:quantity>1</q1:quantity>
<q1:item internalId="82198"/>
<q1:orderLine>101</q1:orderLine>
</q1:item>
</q1:itemList>
</record>
Response
<writeResponse>
<platformCore:status xmlns:platformCore="urn:core_2020_1.platform.webservices.netsuite.com" isSuccess="false">
<platformCore:statusDetail type="ERROR">
<platformCore:code>VALID_LINE_ITEM_REQD</platformCore:code>
<platformCore:message>You must have at least one valid line item for this transaction.</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
<baseRef xmlns:platformCore="urn:core_2020_1.platform.webservices.netsuite.com" type="itemFulfillment" xsi:type="platformCore:RecordRef" externalId="mrk-so-2446425"/>
</writeResponse>
Did you start your item fulfillment with an initialize call?
ItemFulfillmentItem has a field 'orderLine'.
This matches with the SalesOrderItem 'line' field.
If what you actually want to fulfill can be calculated from the SO information then I just do a reverse loop through the fulfillment lines, using the orderLine value to key into the SalesOrder and remove any fulfillment lines that shouldn't be on this fulfillment.
You must have at least one valid line item for this transaction
This is cause for a lot reasons:
That line (item) doesn´t exists in this SO
That item doesn´t have available quantity (all backordered)
that line is closed
wrong internalid for the item
after you made the first fulfillment review your SO (partially fulfillment) and verify you remant lines
After you transform your SO to fulfillment
var itemFulfillMent = record.transform( {
fromType: context.createdfrom.recordType,
fromId: context.createdfrom.id,
toType: 'itemfulfillment',
isDynamic: true,
} );
make a array with (line, item)
var itemsPosition = {};
for ( var i = 0; i < itemFulfillMent.getLineCount( 'item' ); i ++ ) {
itemsPosition[ itemFulfillMent.getSublistValue( 'item', 'item', i ) ] = i;
}
in the itemsPosition you will now know line of your item.

Unable to find a matching line for sublist item with key: [orderLine] and value: [1]

I am using SuiteTalk to create an item fulfillment from an existing sales order. This works for non-serialized orders, but not for serialized SOs.
I get the following error:
Unable to find a matching line for sublist item with key: [orderLine] and value: [1].
The line numbers do however match, since there is only one line, and this has line number "1". The line item does have a quantity of 3, each item being added to the fulfillment separately with the same line number. Could this be the problem?
My code:
ItemFulfillmentItem ffItem = new ItemFulfillmentItem();
ffItem.item = ifitemlist.item[b].item;
ffItem.itemReceive = true;
ffItem.itemReceiveSpecified = true;
ffItem.itemIsFulfilled = true;
ffItem.itemIsFulfilledSpecified = true;
ffItem.orderLineSpecified = true;
ffItem.orderLine = ifitemlist.item[b].orderLine;
ffItem.quantity = msg.despatchCartons[i].items[a].qtyDespatched;
ffItem.quantitySpecified = true;
ifitems.Add(ffItem);
For the specific fulfillment, the above code runs 3 times. This is because each of the 3 items on this Line has a separate serial number.
Any help would be appreciated. Thanks in advance!
To resolve this, you need to create an Inventory Detail record for each line on the Item Fulfillment record. The Inventory Detail record will contain the serial number and quantity per serial number for the specific line item.
The SuiteScript 2.0 code for this, using a User Event script:
var currentRecord = scriptContext.currentRecord;
var subrecordInvDetail = currentRecord.getSublistSubrecord({
sublistId: 'item',
fieldId: 'inventorydetail',
line: item_line_num
});
Run the following code for each serial number on your current line:
subrecordInvDetail.setSublistValue({
sublistId: 'inventoryassignment',
fieldId: 'issueinventorynumber',
line: serial_num_line,
value: 'Serial_Number'
});
subrecordInvDetail.setSublistValue({
sublistId: 'inventoryassignment',
fieldId: 'quantity',
line: serial_num_line,
value: 'Quantity_Value'
});
subrecordInvDetail.save();
This is the first thing that pops up when I google for the same error, so hopefully this may help someone and save a few hours of their life ! I know it's not strictly an answer to the question asked. In my case I had this same error when trying to create an Item Receipt for a Purchase Order. The issue turned out to be that I was not incrementing the lineOrder parameter for the ItemReceiptItem. Therefore I had multiple lines in the Item Receipt that had the same lineOrder (which matches to that in the original Purchase Order). Would have been more useful if the error message said something along the lines that there were duplicates.

NetSuite SuiteScript 2.0 how to update Sales Order Item which is part of the Item Group

I'm trying to update Term field on Sales Order Item Line in NetSuite.
I can do that fine using ue (user event script) on beforeSubmit event.
That works when I hit Save button when Sales Order is in Edit mode.
Now, the issue:
I am trying to update Sales Order Item field (Term) BEFORE Save button is hit but AFTER Add button is clicked, for components of Item Group.
I have written a function and added to postSourcing(scriptContaxt) event that does the updating but it works only for NON-Inventory Items.
This does not work for Item Groups.
When Item Group is added to the Sales Order, inventory (components) are automatically populated onto the form.
Does anybody know what event is triggered when components of Item Group are added to the Sales Order form?
Thank you
Kris
OK... Further research and explanation:
This is the code for updating a single filed when Non-Inventory Item is added to the Sales Order (adding SKU number to the Sales Order Line):
function postSourcing(scriptContext)
{
setSalesOrderItemTerm(scriptContext.currentRecord);
}
function setSalesOrderItemTerm(salesorder)
{
var itemId = salesorder.getCurrentSublistValue({
sublistId: 'item',
fieldId: 'item'
});
if(!itemId)
return;
var result = search.create({
type: search.Type.ITEM,
columns: ['custitem_abs_item_term'],
filters: [{name: 'internalid',operator: search.Operator.IS,values: itemId},
{name: 'isinactive',operator: search.Operator.IS,values: 'F'}]
}).run().getRange({start: 0, end: 1});
if(result.length > 0)
{
var term = result[0].getValue('custitem_abs_item_term');
salesorder.setCurrentSublistValue({
sublistId: 'item',
fieldId: 'custcol_swe_contract_item_term_months',
value: term
});
}
}
This works absolutely fine for a single, Non-Inventory Item.
On the other hand when I try to add Item Group (this is different than KIT) the Term field (id: custcol_swe_contract_item_term_months) is not being updated from its item, the Term field is updated with value on the Item Group.
FYI. Item Group is constructed with Item Group (Group Header called Group), its components (other Item types i.e. Non-Inventory Items) and Empty Items (called End Group -> usually empty)
So when you have an Item Group that has two Non-Inventory Items in it and you try to add that Item Group to Sales Order those two Non-Inventory items will be automatically added to Sales Order (along with Group and End Group row).
I am trying to find out on what event those two Non-Inventory items are added to Sales Order.
I hope this is more clear now.
Thank you
Kris
My answer below, also applies to this
https://stackoverflow.com/a/44013939/5389329 "Creating a SalesOrder within NetSuite with Item Groups"

Netsuite: Manually modify [invoiced] field in Sales Order > Item subtab

Is there any way to manually modify [invoiced] field in Sales Order > Item subtab via SuiteScript?
Currently I am able to update this field via nlapiTransformRecord(sales order to invoice), but in case there are two item fulfillment for same item, I do not want to combine these together instead I want to show two rows in Invoice. I am able to achieve this by using SelectNewLineItem but the problem is the [invoiced] field is not being updated. Please help. Thanks.
As show in below, if I use SelectLineItem it works perfectly. But SelectNewLineItem somehow failed to update the [invoiced] field.
var newrecords = nlapiTransformRecord('salesorder', results[0][9],
'invoice', {
recordmode : 'dynamic'
});
//Temporary hardcoded this to 2 for testing
if (m == 2) {
newrecords.selectNewLineItem('item');
newrecords.setCurrentLineItemValue('item', 'item',
results[m][10]);
newrecords.setCurrentLineItemValue('item', 'quantity',
results[m][4]);
newrecords.setCurrentLineItemValue('item', 'amount',
results[m][5]);
newrecords.commitLineItem('item');
} else {
var lineNum = newrecords.findLineItemValue('item', 'item',
results[m][10]);
newrecords.selectLineItem('item', lineNum);
newrecords.setCurrentLineItemValue('item', 'item',
results[m][10]);
newrecords.setCurrentLineItemValue('item', 'quantity',
results[m][4]);
newrecords.setCurrentLineItemValue('item', 'amount',
results[m][5]);
newrecords.commitLineItem('item');
}
}
nlapiSubmitRecord(newrecords, false, true);}
I don't believe this is possible.
Basically, there are two types of lines on Invoices - those linked to a Sales Order, and standalone lines. Linked lines will reflect back to the Sales Order, and do not change stock on hand (as that is changed by the Fulfillment), while standalone lines have no connection to the Sales Order.
The only (API supported) way to create an Invoice that is linked back to the Sales Order is using nlapiTransformRecord(). If you then manually add lines with record.selectNewLineItem() they cannot be linked back.

NetSuite - how to create reference link

When the billing schedule runs it auto generates invoices from sales order. When this happens - how can I create a link on the sales order that will allow me to load the corresponding invoice in code?
I need this so I can grab couple of field values from the invoice but I can't access the invoice directly from another entity which seems only related to sales order.
EDIT 1:
var fil = [];
fil[0] = new nlobjSearchFilter('createdfrom', null, 'is', nlapiGetRecordId())
var col = [];
col[0] = new nlobjSearchColumn('internalid');
var invoices = nlapiSearchRecord('invoice', null, fil, col);
nlapiLogExecution('DEBUG', 'field val', invoices);
Throws invalid operator or not in proper syntax: createdfrom.
Though adding a link on the Sales Order is a viable solution, it's not your only option. Alternatively, you could do a search for invoices where the createdfrom field is the internal ID of your Sales Order. Something like in SuiteScript 1.0:
var invoices = nlapiSearchRecord('invoice', null,
[['createdfrom', 'is', nlapiGetRecordId()]],
[/* create search columns for the fields you need off the invoice */]
) || [];
or in 2.0:
var invoices = search.create({
"type": search.Type.INVOICE,
"filters": [['createdfrom', 'is', context.currentRecord.id]],
"columns": [/* create search columns for the fields you need off the invoice */]
}).run().each(processResult);
This will get you a list of all the Invoices created from your Sales Order (which is likely only 1).
If you believe you need a link to the Invoice on the Sales Order, you could add the custom body field, then create a User Event on the Invoice record that populates this new field with its createdfrom value on the Before Submit event. But then what happens if your Sales Order gets paid via multiple Invoices?

Resources