I am not able to set "Created From" on Invoice to reference a Sales Order record through the SOAP web service. I have tried setting this field directly and also through Initialize API (initializing a invoice record using the sales order and then copying the CreatedFrom field from the initialized invoice to the invoice being created).
I don't get any errors yet the Created From field doesn't get set on the created invoice record.
The code I am trying looks something like this:
// initialize invoice from sales order
var initializeRecord = new InitializeRecord()
{
type = InitializeType.invoice,
reference = new InitializeRef() { internalId = "sales-order-internal-id", type = InitializeRefType.salesOrder, typeSpecified = true }
};
var result = Utility.Do(async () => await connector.NetSuiteClient.initializeAsync(initializeRecord));
SuiteTalk.Invoice initializedInvoice = result.record;
// create a invoice and copy over the "createdFrom" field
var invoice = new SuiteTalk.Invoice();
invoice.createdFrom = initializedInvoice.createdFrom;
/* set other fields on the invoice and add some line items
....
...
..
*/
// finally create the invoice
var result = Utility.Do(async () => await connector.NetSuiteClient.addAsync(invoice));
How I can associate this invoice to a sales order while creating it?
What you will have to do -- as painful as it is -- is to use the transformed record provided to you by netsuite from the initializeRecord call, and then modify it. It is a pain because there are several fields that you will have to unset as you fill in the record.
The issue is that there are createdFrom associations on both the header level, and the line level, that need to be filled in for the sales order to invoice association to take.
here are the list of line fields that I had to unset in the invoice:
line-level:
quantityRemaining
quantityAvailable
quantityFulfilled
quantityOrdered
quantityOnHand
costEstimate
taxRate1
here are the header-level fields:
subTotal
total
totalCostEstimate
estGrossProfit
estGrossProfitPercent
discountTotal
giftCertApplied
shippingTax1Rate
Thank you 2ps.
In my case this code was enough
Invoice invoice = (Invoice)initializeResponse.record;
invoice.totalCostEstimateSpecified = false;
invoice.estGrossProfitPercentSpecified = false;
invoice.discountTotalSpecified = false;
invoice.totalSpecified = false;
foreach (var item in invoice.itemList?.item)
{
item.quantityRemainingSpecified = false;
item.costEstimateSpecified = false;
}
var writeResponse = svc2.add(invoice);
Related
I am working on a C# method that receives a json object containing the following values.
meetingid
o_agendaitem
o_legistarid
o_title
n_agendaitem
n_legistarid
n_title
The values with an "o" represent the record that comes from the database.
The values with an "n" represent the edited values from a web form.
I put all together to pass it to the C# method:
var data = JSON.stringify({
'meetingid': $scope.meetingId,
'o_agendaitem': o_xref.AgendaItem,
'o_legistarid': o_xref.LegistarID,
'o_title': o_xref.Title,
'n_agendaitem': n_xref.AgendaItem,
'n_legistarid': n_xref.LegistarID,
'n_title': n_xref.Title,
I am having trouble writing the logic in a clear and simple way.
For example, lets say I have the data below:
44841 1 62704 Title#1
44841 2 62218 Title#2
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
The user edits the values, and changes the 1 for a 6, then the data would change to:
44841 2 62218 Title#2
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
44841 6 62704 Title#6
Meeting Id and Agenda Item are a Composite Key.
I use the "o" agenda item value and meeting id, to find that record, and if exists, delete it, and then create a new entry with the "n" values.
But I need to check that the meeting id + the "n" agenda item don't exist already too.
Example:
44841 1 62704 Title#1
44841 2 62218 Title#2 < I edit this row, I want to change the 2 for a 6, but I made a mistake and instead entered a 5, then based on my current logic, I would be deleting a record I did not mean to delete. How can I add a new check to make sure that the user is made aware of this error?
44841 3 62663 Title#3
44841 4 62679 Title#4
44841 5 62709 Title#5
This is my attempt to accomplish so far (I added notes in the parts I am not sure if I am doing it right or not):
public ActionResult UpdateXRefItem(Parameters data)
{
bool status = false;
string message = "";
using (Entities entities = new Entities())
{
//use the "o" value to find the record
var oldRec = entities.XRef_WIP.Find(data.meetingId, data.o_agendaItem);
if (oldRec != null) {
//I am not sure if I should remove the record here, or better do a few more checks.
//entities.XRef_WIP.Remove(oldRec);
//use the "n" value combined with the meeting id to see if already exists
var newVal = entities.XRef_WIP.Find(data.meetingId, data.n_agendaItem);
//if the value from the form already exists, return a message to the user
if (newVal != null)
{
status = false;
message = "This Agenda Item already exists for this Cross-Reference List. Please verfy your data and try again.";
}
else{
//after removing the "old" record, and check that the combination of meeting id + agenda item
//do not exist alreay, then create a new table entry.
//I cannot update agenda item because it is part of the composite key.
//entities.XRef_WIP.Add(new XRefWIP
//{
// MeetingID = data.meetingId,
// AgendaItem = data.n_agendaItem,
// LegistarID = data.n_legistarId,
// Title = data.n_title
//});
//status = true;
//message = "Cross-Reference record has been succssfully updated.";
//entities.SaveChanges();
}
}
}
return new JsonResult { Data = new { status = status, message = message } };
}
I hope this makes sens and someone is willing to offer a hand to complete the logic. I think I am close, but I am having trouble putting my ideas together.
Thank you,
Erasmo
See if the solution works, the idea being that you check if the record you want to add exists and if it does you stop the user from proceeding.
public ActionResult UpdateXRefItem(Parameters data)
{
using (Entities entities = new Entities())
{
// First check if the item you are trying to add exists
var currentRec = entities.XRef_WIP.Find(data.meetingId, data.n_agendaItem);
// Stop the user from continueing with the transaction
if (currentRec != null)
return new JsonResult { Data = new { status = false, message = "Record already exists." } };
// Use the "o" value to find the record
var oldRec = entities.XRef_WIP.Find(data.meetingId, data.o_agendaItem);
// If it exists then delete it
if (oldRec != null) {
entities.XRef_WIP.Remove(oldRec);
// Add new record
entities.XRef_WIP.Add(new XRefWIP()
{
MeetingID = data.meetingId,
AgendaItem = data.n_agendaItem,
LegistarID = data.n_legistarId,
Title = data.n_title
});
// Return a new result
return new JsonResult { Data = new { status = true, message = "Success!" } };
}
I’m trying to automate linking the NetSuite purchase order to a NetSuite sale order and the following is the code I tried to accomplish this task. But I'm getting error (see at the bottom). Can you please check and let me know what I’m missing here?
Purchase Order Creation code:
var createPurchaseOrder = new PurchaseOrder();
createPurchaseOrder.entity = new RecordRef()
{
internalId = “653”
//type = RecordType.purchaseOrder,
//typeSpecified = true
};
RecordRef soRecordRef = new RecordRef();
soRecordRef.internalId = “XXXXXXXX”;
soRecordRef.type = RecordType.salesOrder;
soRecordRef.typeSpecified = true;
createPurchaseOrder.createdFrom = soRecordRef;
RecordRef depRecordRef = new RecordRef();
depRecordRef.internalId = “3”;
depRecordRef.name = “eBay : eBay FNC”;
depRecordRef.type = RecordType.department;
depRecordRef.typeSpecified = true;
createPurchaseOrder.department = depRecordRef;
PurchaseOrderItem[] Items = new PurchaseOrderItem[1];
Items[0] = new PurchaseOrderItem();
RecordRef item = new RecordRef();
item.type = RecordType.nonInventoryPurchaseItem;
item.typeSpecified = true;
item.internalId = “XXXXX”;
Items[0].item = item;
Items[0].rate = “5”;
Items[0].quantity = 1;
Items[0].quantitySpecified = true;
PurchaseOrderItemList purchaseOrderItemList = new PurchaseOrderItemList();
purchaseOrderItemList.item = Items;
createPurchaseOrder.itemList = purchaseOrderItemList;
WriteResponse response = Service.add(createPurchaseOrder);
Code I'm using to Update Purchase Order Number in Sales Order:
var updateSalesOrder = new SalesOrder();
updateSalesOrder.internalId = “XXXXXXXX”;
SalesOrderItem[] soItems = new SalesOrderItem[1];
var soItem = new SalesOrderItem();
RecordRef roItem = new RecordRef();
roItem.type = RecordType.inventoryItem;
roItem.typeSpecified = true;
roItem.internalId = “XXXXX”;
soItem.item = roItem;
RecordRef prLevel = new RecordRef();
prLevel.type = RecordType.priceLevel;
prLevel.internalId = “-1”;
prLevel.typeSpecified = true;
soItem.price = prLevel;
soItem.rate = “15”;
soItem.quantity = 1;
soItem.quantitySpecified = true;
RecordRef poItem = new RecordRef();
poItem.type = RecordType.purchaseOrder;
poItem.typeSpecified = true;
poItem.internalId = purchaseOrder.internalId;
soItem.createdPo = poItem;
soItems[0] = soItem;
SalesOrderItemList salesOrderItemList = new SalesOrderItemList();
salesOrderItemList.item = soItems;
updateSalesOrder.itemList = salesOrderItemList;
response = Service.update(updateSalesOrder);
if (response.status.isSuccess != true) throw new Exception(response.status.statusDetail[0].message);
But I get the follwoing Error:
You do not have permissions to set a value for element createPOSpecified due to one of the following reasons: 1) The field is read-only; 2) An associated feature is disabled; 3) The field is available either when a record is created or updated, but not in both cases.
Note: createPOSpecified is not displayed in the sales order screen in NetSuite. When I try to update a field in the sales order which exist in the form, then I am able to update it successfully but the field I am trying to update (createPOSpecified ) is not available in this sales form. In this case how can I update this ? Also is this the better way of linking the purchase order with sales order?
Thanks,
Hemant.
Updated 25-May-2020 (Responding to Anand Rajaram)
We are using the ADMINISTRATOR role for creating purchase order and linking that to sales order. A user with this role has been provided by our client and we don’t have permission to see fields that are displayed in the screen and have been restricted for EDIT. But we are able to edit most of the field displayed in the screen.
createPOSpecified is not a custom field. It’s a property in the SALESORDETITEM class. It will not be displayed in any sales order form.
If this is the proper code for creating purchase order and linking that to sales order, then I have few queries:
3.1 When we create purchase order through NetSuite by clicking the dropship link in the sales order item grid, we are able to see Mark Shipped button.
But when we create purchase order through code, it is displaying the Receive button and there was no changes in the purchase order status.
3.2 **createdFrom** field is displaying as below when we create purchase order through netsuite.
This field is not displaying when we are creating purchase order through code. We have provided information for createdFrom property, but not sure why it is not displaying
We assume this is the field that helps to link with sales order. We have provided this information while creating item fulfillment and vendor bill and these are properly linked with sales order, but we are not sure why purchase order is not getting linked with sales order.
Finally on the below comments which you have provided
Which is basically having a custom transaction body field on Sales Order form, and once PO is created, update the newly created PO in Sales Order field.
We don’t have any custom transaction body field in our sales order form for providing purchase order. But once purchase order is created through NetSuite, purchase order number will be displayed in the sales order item grid.
So all this boils down to: what is that we have missed in the code and what is that we have to fix to display the "Mark Shipped" button, “Created From" label and linking Purchase Order to Sales Order.
Thanks,
Hemant.
I don't have an answer, but hopefully I can contribute. First of all, I think you're approaching this from the wrong direction. Rather than creating the PO and then trying to link it to the SO, I think you'll have to initialize the PO via the native dropship process and then save the PO. For example, creating a drop ship PO is pretty easy in SuiteScript 2.0. Here's how it's done:
var purchaseOrder = record.create ({
type: record.Type.PURCHASE_ORDER,
isDynamic: true,
defaultValues: {
recordmode: 'dynamic',
soid: '11111',
dropship: true,
custid: '22222',
entity: '33333'
}
})
This new PO is populated with all valid items from the SO and when it's saved all the linking is done automatically (createdFrom is automatically set on the PO; createdPo is automatically set on the SO item). I tried to recreate this in SuiteTalk using two different methods, both of which failed. Here they are:
The first approach tries to emulate the SuiteScript method using the initialize() method. This is how you create an SO from an Estimate, or an IF from an SO, so it seems promising:
var initrec = new InitializeRecord
{
type = InitializeType.purchaseOrder,
reference = new InitializeRef
{
internalId = "11111",
type = InitializeRefType.salesOrder,
typeSpecified = true
}
};
var res = NSBase.Client.Service.initialize(initrec);
// "You can not initialize purchaseOrder by referencing salesOrder."
The error is self-explanatory. It's not possible to create a PO from an SO using initialize(). This is very disheartening.
The second approach essentially tries to programmatically click the "drop ship" link on the line item. It fails with a similar error to the one you encountered before:
var objSO = new SalesOrder();
objSO.internalId = "11111";
objSO.itemList = new SalesOrderItemList
{
item = new SalesOrderItem[]
{
new SalesOrderItem { line = 10, lineSpecified = true, createPo = SalesOrderItemCreatePo._dropShipment, createPoSpecified = true }
},
replaceAll = false
};
var result = Service.update(objSO);
// "You do not have permissions to set a value for element item.createpo due to one of the following reasons: 1) The field is read-only; 2) An associated feature is disabled; 3) The field is available either when a record is created or updated, but not in both cases."
Unfortunately, this is the best I can do. The initialization approach definitely seems like the most likely solution to the problem, and the fact that it fails makes me wonder if it is even possible to create a drop ship/special order PO using SuiteTalk.
As an addendum to Will C.'s outstanding answer, there are three undocumented fields that you can use in suitescript to associate a purchase order line with a sales order line.
These fields are:
createdfrom -- this should be set to the internalid of the salesorder
orderdoc -- this should be set to the internalid of the salesorder
orderline -- this should be set to the 1-indexed line id of the sales order item that you want to link to the purchase order
id -- this should be set to the concatenation of orderdoc and orderline separated by an _ (underscore).
these four fields allow you to associate any arbitrary purchase order line with a sales order line even if those lines were not pulled into the purchase order from the call to record.create.
So following could be your issues:
Permission Issue: What role are you trying to create and link the two transactions? Does that role have appropriate permissions to achieve the same? Also are there any restrictions added in the custom field "createPOSpecified"?
Custom Field Setting: You mentioned that the field "createPOSpecified" is not available in Sales Order form. Review the custom field definition and see if the field is applied to "Sales". If not, then the field will not be available in all Sales transaction forms, and you wouldn't be able to update it in the script. Also in the custom field settings, verify if the field is set to "Read Only". If it is then change it to either normal or disabled (if you don't want users to manually edit it).
And to your final question, yes, this is an appropriate way to custom link a Sales Order and a Purchase Order transaction in NetSuite. Which is basically having a custom transaction body field on Sales Order form, and once PO is created, update the newly created PO in Sales Order field.
With the suggestion and from #Will Charbonneau, we have tested the following script using SuiteScript 1.0 and it helped us to link the purchase order to a sales order.
var po = nlapiCreateRecord('purchaseorder', {recordmode:"dynamic", soid:soInternalId,poentity:vendorEntity,vendor:vendorEntity,entity:custEntity});
var id = nlapiSubmitRecord(po, true)
nlapiLogExecution("DEBUG", "DropShip PO Created", "PO=" + id);
Thanks,
Hemant.
I am currently developing a program that should create invoices and add lines based on the selection of table.
This is my table (created using DevExpress):
These are future invoice lines that have a company ID as reference.
What I want to do with the future button 'Create invoices' is create 1 invoice per selected ID and add all of the selected lines to the invoicelines table.
My tables are:
Invoice
InvoiceID
CompanyID
InvoiceLines
ID
InvoiceID
Title
....
Based on my example picture, the result I would desire is:
2 invoices with each having 2 lines added
The problem is that I do not know how to handle this in my code.
I currently have:
private void btn_ExportToDB_ItemClick(object sender, ItemClickEventArgs e)
{
List<int> PWIDs = new List<int>();
List<int> distinct = new List<int>();
Dictionary<int, int> lines = new Dictionary<int, int>();
if (gridView.SelectedRowsCount > 0)
{
for (int i = gridView.SelectedRowsCount - 1; i >= 0; i--)
{
int rowHandle = gridView.GetSelectedRows()[i];
int PWID = int.Parse(gridView.GetRowCellValue(rowHandle, "pwID_PW").ToString());
PWIDs.Add(PWID);
lines.Add(rowHandle, PWID);
MessageBox.Show("customer id " + PWID);
distinct = PWIDs.Distinct().ToList();
}
foreach (int value in distinct)
{
}
}
}
I tried to do the following:
1- Create a dictionary in which I put the selected rowhandle + pwID.
2- After, I loop through my dictionary and select distinct pwID values (these are used for my invoices).
3- Hereafter, I'm stuck with my invoice lines, that I can't seem to add to my invoicelines table because I think my design on handling this problem is wrong.
How could I solve this in the most efficient way?
If you can return the entire ticket objects from the selection list in your screenshot then you could run a piece of code as below;
public List<Invoice> CreateInvoices(IEnumerable<Ticket> selectedTickets)
{
// Group tickets by company id
return selectedTickets.GroupBy(ticket => ticket.CompanyId)
// Convert grouping into new Invoice objects
.Select(g => new Invoice
{
// Build invoice object
CompanyId = g.Key,
// Build invoice lines
InvoiceLines = g.Select(line => new InvoiceLine() {Title = line.Title}).ToList()
}).ToList();
}
This shall return a IEnumerable of Invoices containing multiple InvoiceLines inside of each.
this code suppose to update only one value in a table and keep all the other value as is .
how ever I cant find out why , the db.SaveChanges doesn't save it . in the debugger I can see that the value has change ,
this is my code
if (model.rev.GBU == "Good")
{
//This section will do:---- 1> get subjectId -- 2> Update only in the subjectid good rank
//Update this subjectid (int)good +1 data
model.sub.SubjectId = R_getvelue.SubjectId;
//get the relevant value from the Db
var Good = R_getvelue.Good;
int iGoodRating = Convert.ToInt32(Good);
//Add 1 (one )to the value in the Db
iGoodRating++;
model.sub.Good = iGoodRating;
//Keep All the existing data in the other fields
UpdateModel(model.sub);
// db.Entry(model.sub).State = EntityState.Modified; >removing all values biside "Good
}
await db.SaveChangesAsync();
return RedirectToAction("index");
}
ViewBag.OccupationId = new SelectList(db.occupation, "OccupationId", "OccupationDecription", model.Occupation);
return View(model);
}
I am using the following code in my .NET web service that gets its data form a CSV file.
private List<Item> ietms = new List<Item>();
public ItemRepository()
{
string filename = HttpRuntime.AppDomainAppPath + "App_Data\\items.csv";
var lines = File.ReadAllLines(filename).Skip(1).ToList();
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
var columns = line.Split('$');
//get rid of newline characters in the middle of data lines
while (columns.Length < 9)
{
i += 1;
line = line.Replace("\n", " ") + lines[i];
columns = line.Split('$');
}
//Remove Starting and Trailing open quotes from fields
columns = columns.Select(c => { if (string.IsNullOrEmpty(c) == false) { return c.Substring(1, c.Length - 2); } return string.Empty; }).ToArray();
var temp = columns[5].Split('|', '>');
items.Add(new Item()
{
Id = int.Parse(columns[0]),
Name = columns[1],
Description = columns[2],
Category = temp[0]
});
}
}
This code gets a list of products from the CSV file along with its name, description etc. Each product belongs to either one or two categories : Category = temp[0].
Each product's category is found in a column of the csv file with it's data structured as such:
Groups>Subgroup>item, in which case, this product belongs to category "Groups".
The category column of a product may also be structured as:
MajorGroup|Groups>Subgroup>item, in which case this product belongs to category "MajorGroup".
Also, in many cases a product's category column may be structured as:
MajorGroup|Groups>Subgroup>item|SecondGroup, in which case this product belong to both the categories "MajorGroup" and "SecondGroup"
The code above that I am currently using does half the job. If a product has a category defined in the CSV file as MajorGroup|Groups>Subgroup>item|SecondGroup, it assigns it to category "MajorGroups" but not "SecondGroup".
This line var temp = columns[5].Split('|', '>'); gets the first value structured taht way and separated by a pipe and sets it as the product's category here Category = temp[0].
How do I fix this so that if the category is structured as MajorGroup|Groups>Subgroup>item|SecondGroup, with two categories, then it will show up in both categories.
How do I assign the product to one or more categories depending on the structure of the category column data.
This works for the most part, but how do I alter the code to check and assign for both categories?
Can I change this var temp = columns[5].Split('|', '>'); to get both teh first and the last value if it exists and assign both to Category = temp[0].
To get the second group values given the problem statement as specified you could do the following.
...
var temp = columns[5].Split('|', '>');
string categories= temp[0];
if (input.Count(x => x == '|') >= 2)
{
categories+= "," + temp.Last();
}
...
Category = categories;
Then one could get a list of Items that is assigned to a category by the following function:
static public IList<Item> GetProductsByCategory(string category, IList<Item> items)
{
return items.Where(x => x.Category.Split(',').Contains(category,StringComparer.OrdinalIgnoreCase)).ToList();
}
A much cleaner solution is to store the categories within the Item class as something that implements ILIST.
You should definitely use some CSV parser instead of doing this manually. There are too many potential problems and issues when parsing CSV manually that it is much easier and faster to use some existing tool like:
FileHelpers
Fast CSV Reader