Newly added row is not reflecting on UI instantly in SAP B1 - sapb1

When I add new row into the database and call loadfromdatasource method, it is not reflecting. I need to re-log to check newly added row. How can I instantly refresh data in matrix?
Code:
oUserTable.Code = "Temp1"; oUserTable.UserFields.Fields.Item("U_barcode").Value = oEditTxt.String; oUserTable.UserFields.Fields.Item("U_keyword").Value = oEditTxt1.String; oUserTable.UserFields.Fields.Item("U_pdocentry").Value = DocEntry; oUserTable.UserFields.Fields.Item("U_pobjtype").Value = "17"; int i = oUserTable.Add(); SAPbouiCOM.Form oForm = Application.SBO_Application.OpenForm(SAPbouiCOM.BoFormObjectEnum.fo_Order, "", DocEntry.ToString()); if (i != 0){ oApp.SetStatusBarMessage("Error" + oCompany.GetLastErrorDescription(), SAPbouiCOM.BoMessageTime.bmt_Medium, false); }else{ oApp.SetStatusBarMessage("Successfully inserted data" + oCompany.GetLastErrorDescription(), SAPbouiCOM.BoMessageTime.bmt_Medium, false); oMatrix.LoadFromDataSource();}}}

Related

Multiple instances of forms has data writing to first opened form

I have this SAP B1 addon where a user can open multiple instances of a UDO from.
The form is loaded from XML.
SAPUtility.LoadFromXML(SboConnection.SboApplication, GUI.FormsPath, "SalesOrder.xml");
I have CFLs but when I open the 2nd form and select a customer on that 2nd form, the data writes to the form I opened first. I am using a user datasource for the CFLs.
oItem = _form.Items.Item("CardCode");
_cardCode = oItem.Specific;
_cCode = _form.DataSources.UserDataSources.Add("CardCode", SAPbouiCOM.BoDataType.dt_SHORT_TEXT, 50);
_cardCode.DataBind.SetBound(true, "", "CardCode");
_cardCode.Value = "";
...............................
if (oDataTable.UniqueID == "CFL_CarCod" || oDataTable.UniqueID == "CFL_CarNam")
{
if (oDataTable != null)
{
cardCode = System.Convert.ToString(oDataTable.GetValue(0, 0), null);
cardName = System.Convert.ToString(oDataTable.GetValue(1, 0), null);
}
RemoveContacts();
_cCode.ValueEx = cardCode;
_cName.ValueEx = cardName;
AddContacts();
}
In item events, I have this:
_form = SboConnection.SboApplication.Forms.Item(pVal.FormUID);
How do I fix this and make data write to the proper active form?

Merging many spreadsheets into report file exceeds maximum execution time

I am using the following script to add rows of files from a student loop in the Google spreadsheet if credits are less than x. The script was working good but as the data in the spreadsheet is being added daily, now the script is throwing "Exceeded maximum execution time" error (we have more than 2000 files). As I am new to scripting I don't know how to optimize the code.
Could someone help me to optimize the code or any solution so that the execution time take less than 5 min. Every time you compare to an email, it has to be compared to many emails. Please Help!
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
//If credits are less than X: write
if (credits < 120) {
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u - 1][1]) {
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if (bandera == 1) {
var nombre = data[0][1];
sheet2[0].getRange(lastrow + 1, 1).setValue(nombre);
sheet2[0].getRange(lastrow + 1, 2).setValue(email);
sheet2[0].getRange(lastrow + 1, 3).setValue(credits);
sheet2[0].getRange(lastrow + 1, 4).setValue(fecha);
}
}
}
SpreadsheetApp.flush();
}
The questioner's code is taking taking more than 4-6 minutes to run and is getting an error Exceeded maximum execution time.
The following answer is based solely on the code provided by the questioner. We don't have any information about the 'filecombined' spreadsheet, its size and triggers. We are also in the dark about the various student spreadsheets, their size, etc, except that we know that there are 2,000 of these files. We don't know how often this routine is run, nor how many students have credits <120.
getvalues and setvalues statements are very costly; typically 0.2 seconds each. The questioners code includes a variety of such statements - some are unavoidable but others are not.
In looking at optimising this code, I made two major changes.
1 - I moved line 27 var data2 = sheet2[0].getDataRange().getValues();
This line need only be executed once and I relocated it at the top of the code just after the various "filecombined" commands. As it stood, this line was being executed once for every student spreadsheet; this along may have contributed to several minutes of execution time.
2) I converted certain setvalue commands to an array, and then updated the "filecombined" spreadsheet from the array once only, at the end of the processing. Depending on the number of students with low credits and who are not already on the "filecombined" sheet, this may represent a substantial saving.
The code affected was lines 47 to 50.
line47: sheet2[0].getRange(lastrow+1, 1).setValue(nombre);
line48: sheet2[0].getRange(lastrow+1, 2).setValue(email);
line49: sheet2[0].getRange(lastrow+1, 3).setValue(credits);
line50: sheet2[0].getRange(lastrow+1, 4).setValue(fecha);
There are setvalue commands also executed at lines 38 and 39 (if the student is already on the "filecombined" spreadsheet), but I chose to leave these as-is. As noted above, we don't know how many such students there might be, and the cost of these setvalue commands may be minor or not. Until this is clear, and in the light of other time savings, I chose to leave them as-is.
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
// create some arrays
var Newdataarray = [];
var Masterarray = [];
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//If credits are less than X: write
if (credits < 120){
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u-1][1]){
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if(bandera == 1){
var nombre = data[0][1];
Newdataarray = [];
Newdataarray.push(nombre);
Newdataarray.push(email);
Newdataarray.push(credits);
Newdataarray.push(fecha);
Masterarray.push(Newdataarray);
}
}
}
// update the target sheet with the contents of the array
// these are all adding new rows
lastrow = filecombined.getLastRow();
sheet2[0].getRange(lastrow+1, 1, Masterarray.length, 4);
sheet2[0].setValues(Masterarray);
SpreadsheetApp.flush();
}
As I mentioned in my comment, the biggest issue you have is that you repeatedly search an array for a value, when you could use a much faster lookup function.
// Create an object that maps an email address to the (last) array
// index of that email in the `data2` array.
const knownEmails = data2.reduce(function (acc, row, index) {
var email = row[1]; // email is the 2nd element of the inner array (Column B on a spreadsheet)
acc[email] = index;
return acc;
}, {});
Then you can determine if an email existed in data2 by trying to obtain the value for it:
// Get this email's index in `data2`:
var index = knownEmails[email];
if (index === undefined) {
// This is a new email we didn't know about before
...
} else {
// This is an email we knew about already.
var u = ++index; // Convert the array index into a worksheet row (assumes `data2` is from a range that started at Row 1)
...
}
To understand how we are constructing knownEmails from data2, you may find the documentation on Array#reduce helpful.

Copy value of active cell to a different/non active sheet - How to determine which target/destination sheet to use

This script copies the value of the active cell, to another sheet based on the value in the cell next to it.
If I write the exact cell in my script, it works, but every time the script is run, it will be based on a different cell.
I also need to fine tune the destination.
Here it is:
function copytoTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Booking In');
var data = sheet.getActiveCell();
var value = ss.getSheetByName('Booking In').getActiveCell().getA1Notation();
var operator = data.offset(0, 1).getValue();
if (operator == "Michelle") {
var ts = SpreadsheetApp.getActiveSpreadsheet();
var tss = ts.getSheetByName('MICHELLE Schedule');
ts.setActiveSheet(ts.getSheetByName('MICHELLE Schedule'));
tss.getRange(1, 2).activate();
tss.getRange(value).copyTo(tss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
else if (operator == "Georgia") {
ss.setActiveSheet(ss.getSheetByName("GEORGIA Schedule"));
ss.getCurrentCell().offset(0, 1, 4, 1).activate();
ss.getRange('\'Booking In\'!P12').copyTo(ss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
else if (operator == "Julie") {
ss.setActiveSheet(ss.getSheetByName("JULIE Schedule"));
ss.getCurrentCell().offset(0, 1, 4, 1).activate();
ss.getRange('\'Booking In\'!P12').copyTo(ss.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
}
ss.setActiveSheet(ss.getSheetByName('Booking In'), true);
}
Instead of using multiple if / else blocks, you can association a sheet tab name with the operator name in an object. Then look up the sheet tab name by operator name.
function copytoTabs() {
var activeCell,objectOfMappedValues,operator,sheet,sourceSs,targetSheetTabName,trgtSh;
sourceSs = SpreadsheetApp.getActiveSpreadsheet();
objectOfMappedValues = {//This is an object literal - the word literal means that the values are
//hard coded here in the function as opposed to being set with code
"Michelle":"MICHELLE Schedule",
"Georgia":"GEORGIA Schedule",
"Julie":"JULIE Schedule"
}
activeCell = sourceSs.getActiveCell();
Logger.log('activeCell: ' + activeCell)
operator = activeCell.offset(0, 1).getValue();//Get the value of one cell
targetSheetTabName = objectOfMappedValues[operator];//Get the sheet tab name for this operator
Logger.log('targetSheetTabName: ' + targetSheetTabName)
trgtSh = ts.getSheetByName(targetSheetTabName);//Get the sheet tab to be the target to set a value
Logger.log('trgtSh.getName(): ' + trgtSh.getName())
trgtSh.getRange(activeCell.getA1Notation()).copyTo(trgtSh.getActiveRange())
}
This code may not be everything that you are asking for, but hopefully it will advance you to the final solution.

SharePoint get value of rich text box control created programatically

I'm writing a custom web part that need to use a couple of rich text box controls. I'm placing the controls onto the web part programatically. When the web part gets a save postback I'm able to capture the data from all the fields except the two rich text box ones. What's the trick to be able to get the value of a rich text box?
The code I"m using to place my form controls is:
private void CreateInputControls()
{
inputPanel.Controls.Clear();
SPList list = SPContext.Current.Site.RootWeb.Lists["MyList"];
SPContentType cType = list.ContentTypes[0];
Table table = new Table();
table.CellPadding = 3;
table.CellSpacing = 0;
SPContext newContext = SPContext.GetContext(System.Web.HttpContext.Current, list.DefaultView.ID, list.ID, list.ParentWeb);
foreach (SPField field in cType.Fields)
{
if (!field.Hidden && field.CanBeDisplayedInEditForm)
{
FieldLabel fieldLabel = new FieldLabel();
fieldLabel.ControlMode = SPControlMode.New;
fieldLabel.ListId = list.ID;
fieldLabel.FieldName = field.InternalName;
fieldLabel.ItemContext = newContext;
fieldLabel.RenderContext = newContext;
fieldLabel.Field.Required = fieldLabel.Field.Required;
FormField formField = new FormField();
formField.ControlMode = SPControlMode.New;
formField.ListId = list.ID;
formField.FieldName = field.InternalName;
formField.ItemContext = newContext;
formField.RenderContext = newContext;
formField.ID = field.InternalName;
formField.EnableViewState = true;
TableRow row = new TableRow();
table.Rows.Add(row);
TableCell cellLabel = new TableCell();
TableCell cellField = new TableCell();
cellLabel.Controls.Add(fieldLabel);
cellField.Controls.Add(formField);
row.Cells.Add(cellLabel);
row.Cells.Add(cellField);
}
}
inputPanel.Controls.Add(table);
}
The code I'm using to save a new item is:
private void UpdateItem(string bannerImageURL, string thumbnailImageURL)
{
SPList list = SPContext.Current.Site.RootWeb.Lists["MyList"];
SPContentType cType = list.ContentTypes[0];
SPItem item = list.AddItem();
foreach (SPField field in cType.Fields)
{
if (!field.Hidden && field.CanBeDisplayedInEditForm)
{
FormField formField = (FormField)inputPanel.FindControl(field.InternalName);
if (formField != null)
{
// Saves data for all fields EXCEPT for rich text box (sharepoint multiline columns).
item[field.Title] = formField.Value;
}
}
}
item.Update();
}
Maybe there's an issue with the field name. Try to use the InternalName.
item[field.InternalName] = formField.Value;
I have been struggling with this and am using a workaround which I thought I'd post as this was quite frustrating.
The problem is that the RTE control is rendered empty and then populated from a hidden control with JavaScript on the client. However this hidden control is accessible server side thus:
switch (formField.Field.Type)
{
case SPFieldType.Note:
var rtf = (RichTextField)formField.Controls[0];
item[field.Title] = rtf.HiddenInput.Value;
break;
default:
item[field.Title] = formField.Value;
break;
}
This may need extending for other field types but you get the idea...

Cannot add an entity that already exists. (LINQ to SQL)

in my database there are 3 tables
CustomerType
CusID
EventType
EventTypeID
CustomerEventType
CusID
EventTypeID
alt text http://img706.imageshack.us/img706/8806/inserevent.jpg
Dim db = new CustomerEventDataContext
Dim newEvent = new EventType
newEvent.EventTypeID = txtEventID.text
db.EventType.InsertOnSubmit(newEvent)
db.SubmitChanges()
'To select the last ID of event'
Dim lastEventID = (from e in db.EventType Select e.EventTypeID Order By EventTypeID Descending).first()
Dim chkbx As CheckBoxList = CType(form1.FindControl("CheckBoxList1"), CheckBoxList)
Dim newCustomerEventType = New CustomerEventType
Dim i As Integer
For i = 0 To chkbx.Items.Count - 1 Step i + 1
If (chkbx.Items(i).Selected) Then
newCustomerEventType.INTEVENTTYPEID = lastEventID
newCustomerEventType.INTSTUDENTTYPEID = chkbxStudentType.Items(i).Value
db.CustomerEventType.InsertOnSubmit(newCustomerEventType)
db.SubmitChanges()
End If
Next
It works fine when I checked only 1 Single ID of CustomerEventType from CheckBoxList1. It inserts data into EventType with ID 1 and CustomerEventType ID 1. However, when I checked both of them, the error message said
Cannot add an entity that already exists.
Any suggestions please? Thx in advance.
Did you change the EventID before you pressed the button again. To me it looks as you did not. This would result that the code tries to insert the event with the ID 1 into the database, although, it is already there.
Maybe try increasing the event ID automatically or check whether the event is alreay present before trying to insert it.
Edit:
OK, here is what I think you want to do ... if I understood it correctly (it's in C# as I am more fluent in that language - however you should be able to easily convert the algorithm to VB - so just take it as pseudo code):
var db = new CustomerEventDataContext();
var newEvent = db.EventTypeSingleOrDefault(x => x.EventTypeId == txtEventID.Text);
if (newEvent != null) {
newEvent = new EventType();
newEvent.EventTypeId = txtEventID.Text;
db.EventType.InsertOnSubmit();
}
var chkbx = (CheckBoxList) form1.FindControl("CheckBoxList1");
for (int i = 0; i < chkbx.Items.Count; i++) {
var value = chkbxStudentType.Items(i).Value;
if (db.CustomerEventTypes.SingleOrDefault(x => x.EventTypeId == newEvent.EventTypeId) != null) {
// item already exists
} else {
var newCustomerEventType = new CustomerEventType();
newCustomerEventType.INTEVENTTYPEID = newEvent.EventTypeId;
newCustomerEventType.INTSTUDENTTYPEID = value;
db.CustomerEventType.InsertOnSubmit(newCustomerEventType);
}
}
db.SubmitChanges();
Two things I noticed:
You were adding the new EventType and then selecting the last event type based upon the id. This may not result in the item you just added.
You do not have to call SubmitChanges after each InsertOnSubmit. The DataBaseContext implementaton holds the inserted objects for you and you can reference them. Then you do a single submit to commit all changes. Note, however, in some complex circumstances a separate SubmitChanges is necessary, but this is rarely the case.