NetSuite - using nlapiCopyRecord function - netsuite

I am generating an invoice when the original invoice is overdue by a certain time period. I want to know if we can use the nlapiCopyRecord to make a copy of the original invoice but allow us to insert a new line that will replace the old line item? I haven't found any sample to show how this is done.
Thanks.
Edit 1:
var new_inv = nlapiCopyRecord('invoice', internal_id,
{
item : 66,
amount: amount,
description: 'TEST'
});
var copiedId = nlapiSubmitRecord(new_inv);
return copiedId;
Above code fails in my scheduled script. You have entered an invalid default value for this record initialize operation.
I would like to override the line item on the newly copied invoice

Yes that is possible, just like if you copy a record in the UI you can modify the copy. You also need to remember that you need to save the record object after you have copied it.

Why are you doing this? If you are trying to charge a late fee you'd probably be better off by adding an expense line to the original invoice record. If you don't have expenses turned on then you could add an other "Other Charge for Sale"
If your code is running server side then:
var invRec = nlapiLoadRecord('invoice', internal_id);
var chargeIndex = invRec.getLineItemCount('item') + 1;
// don't think you need this for the end position invRec.insertLineItem('item', chargeIndex);
invRec.setLineItemValue('item', 'item', chargeIndex, charge_item_id);
invRec.setLineItemValue('item', 'rate', chargeIndex, amount);
invRec.setLineItemValue('item', 'amount', chargeIndex, amount);
nlapiSubmitRecord(invRec);
OR if you use an expense
var invRec = nlapiLoadRecord('invoice', internal_id);
invRec.insertLineItem('expense', 1);
invRec.setLineItemValue('expense', 'account', 1, penalty_account);
invRec.setLineItemValue('expense', 'amount', 1, amount);
invRec.setLineItemValue('expense', 'memo', 1, 'TEST');
nlapiSubmitRecord(invRec);

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.

Suitescript Invoice for only Fulfilled Items on Sales Orders

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.

How to handle inventory details, getting an error?

This is my code:
var invAdjRec = nlapiCreateRecord('inventoryadjustment');
var lotNumber = "lot123456";
invAdjRec.setFieldValue('account', '850');
invAdjRec.selectNewLineItem('inventory');
invAdjRec.setCurrentLineItemValue('inventory', 'item', '2904');
invAdjRec.setCurrentLineItemValue('inventory', 'location', '3');
invAdjRec.setCurrentLineItemValue('inventory', 'adjustqtyby', '10');
var inventoryDetail = invAdjRec.createCurrentLineItemSubrecord('inventory','inventorydetail');
inventoryDetail.selectNewLineItem('inventoryassignment'); inventoryDetail.setCurrentLineItemValue('inventoryassignment', 'issueinventorynumber', lotNumber);
inventoryDetail.setCurrentLineItemValue('inventoryassignment', 'quantity', 10);
inventoryDetail.commitLineItem('inventoryassignment'); inventoryDetail.commit();
invAdjRec.commitLineItem('inventory');
nlapiSubmitRecord(invAdjRec);
This is my error:
Please enter value(s) for: Serial/Lot Number
Looks like, according to that error message, you are just missing: serialnumber.
invAdjRec.setCurrentLineItemValue('inventory','serialnumber',lotNumber);
The issueinventorynumber field is a select field of a inventorynumber record and expects an internal id passed in. If the lot you wish to set exists, you can examine an existing record that uses it to obtain the id. If the Lot Number does not yet exist, you may not be able to create it.
In your script, you use issueinventorynumber field id as the quantity to be adjusted, but this can only be used for negative inventory adjustments. For positive inventory adjustments, you need to use receiptinventorynumber instead
There's another field not listed in the record browser that must be set:
subrecordInvDetail.setCurrentSublistValue({
sublistId: 'inventoryassignment',
fieldId: 'receiptinventorynumber',
value: serialNumber
});

Issue with List Apply in NetSuite

Unable to find a matching line for sublist apply with key: [doc,line] and value: [5489377,1].
I'm seeing this error when I try to update an apply list on a NetSuite transaction object. The "doc" is the internal ID of the object, and the line number seems to correspond to a line number on the object.
Why is this happening? Can't seem to find a solution.
This works for applying a credit memo to a particular invoice. invId is the internalid of the invoice record:
function applyPayment(creditMemo, payAmount, invId){
var didApply = false;
creditMemo.setFieldValue('autoapply', 'F');
if(payAmount === null) payAmount = creditMemo.getFieldValue('amountremaining');
for(var i = 1; i<=creditMemo.getLineItemCount('apply'); i++){
if(invId == creditMemo.getLineItemValue('apply', 'doc', i)){
didApply = true;
creditMemo.setLineItemValue('apply', 'apply', i, 'T');
creditMemo.setLineItemValue('apply', 'amount',i, payAmount);
}else if('T' == creditMemo.getLineItemValue('apply', 'apply', i)) creditMemo.setLineItemValue('apply', 'apply', i, 'F');
}
if(didApply) nlapiSubmitRecord(creditMemo);
}
We were getting this error with the Chargebee-Netsuite integration and the solution was to open the corresponding Accounting Period in Netsuite and rerun the sync.
Like you mentioned, the first number[5489377,1] is the Netsuite internal ID of the affected document. If you navigate to the document in Netsuite and it has a padlock this could be the reason Locked document
Open the Accounting Period for the affected document and rerun the sync. setup/accounting/manage accounting periods Manage accounting periods

Netsuite - Set Line Item Value

I am trying to figure out how to set a custom value item from the value of another field on the item field. I am not getting any errors, but it is not changing the value.
Here is the code:
function validatePOLineItem(type){
if(type == 'item'){
for (var i = 0; i <= nlapiGetLineItemCount('item'); i++) {
// Get the value for amount on the item line
var amount = nlapiGetCurrentLineItemValue('item', 'amount');
// Get the value for the PO Amount on the item line
var po_amount = nlapiGetCurrentLineItemValue('item', 'custcol_po_amount');
// Set PO Amount equal to Amount on the item line
nlapiSetCurrentLineItemValue('item', po_amount, amount);
}
}
}
Looks like you left off the column name you are trying to set. Also, you should just have to update the "current" line…
function validatePOLineItem(type){
if(type == 'item'){
var amount = nlapiGetCurrentLineItemValue('item', 'amount');
nlapiSetCurrentLineItemValue('item', 'custcol_po_amount', amount);
}
}
Is this a client side script or a user event script? If it is a client side script that is deployed on the validate line event, then you do not need to do a loop. Since the function will trigger every time a line is added. Also you will need to add 'return true' as the last line for the line to be added.
You cannot manipulate the amount field programmatically. You need to modify the quantity or the rate instead. If you are trying to apply a discount or something like that, then I recommend using NetSuite's Discount Items or Promo Codes mechanisms.
Question: Why do you need to have a loop if you are just dealing with the current line item?
Suggested Solution: If you want to achieve it, you can set the Rate equal to 'custcol_po_amount' but this will only work if the quantity is 1 and assuming that the value on 'custcol_po_amount' is accurate.

Resources