How to add user in Map using dataclass, Kotlin - android-studio

So I want to add user in map and then check if it's added in there.
for instance, user enters their information, im saving their email as key and information in dataclass as value
private val users = mutableMapOf<String, User>()
private fun add(){
check()
val email = binding.email.text.toString()
val firstName = binding.firstname.text.toString()
val lastName = binding.lastname.text.toString()
val age = binding.age.text.toString()
users["default"] = User("email","firstName","lastName","age")
for (user in users.keys){
if (email == user) {
Toast.makeText(this,"not successful",Toast.LENGTH_SHORT).show()
break
}else{
users[email] = User(email,firstName,lastName,age)
Toast.makeText(this,"successful",Toast.LENGTH_SHORT).show()
}
}
}
but when i click on the button in first run, it just says successful, but on the second click it should return not successful because this user was already created, but it shows both toasts.
can u tell me why it does this and how can i fix it?

this has to do with the fact that the for loop has no guarantee that it will iterate the keys in the order you expect.
For example, consider this map:
users["default"] = User("email","firstName","lastName","age")
users["zzz#xyz.com"] = User("email","zz","z","age")
suppose that the keys would be iterated in alphabetical order(this is not actually the case most of the time) and that you had email=zzz#xyz.com
On the first iteration, the user string would refer to default. Since email == user is false, it would go to the else branch of your loop. Then, on the next iteration, user will be zzz#xyz.com and email == user will be true; just sending the "toast" notification twice.
It seems that you're trying to insert an element if it doesn't exists, or show an error if it does. Instead of a for loop, consider checking for the existence of the previous key with containsKey and then adding a new element if necessary.
// In kotlin, you can create a map with initial values by using the "to" infix function
private val users = mutableMapOf<String, User>(
"default" to User("email","firstName","lastName","age")
)
private fun add() {
check()
val email = binding.email.text.toString()
val firstName = binding.firstname.text.toString()
val lastName = binding.lastname.text.toString()
val age = binding.age.text.toString()
if (users.containsKey(email)) {
Toast.makeText(this,"not successful",Toast.LENGTH_SHORT).show()
return
}
users[email] = User(email,firstName,lastName,age)
Toast.makeText(this,"successful",Toast.LENGTH_SHORT).show()
}

This is because you are comparing the map key with the email value. You need to compare the value of the email. Try to do as follows within the for: email == user.value.email.

Related

Receive in activity a value from intent and create a new SharedPreference

I have an activity which receives an intent with a putExtra from other activity.
And I want to create a SharedPreference each time the activity receives the putExtra value, in this case a String so I can show all the Strings stored and show in a TextView without loosing the previous String shown.
tvTextView = (TextView)findViewById(R.id.tvTextView);
Bundle extras = getIntent().getExtras();
linearLayout = (LinearLayout)findViewById(R.id.linearLayout);
if (extras != null) {
newNote = extras.getString("Note");
Button noteButton = new Button(this);
noteButton.setText(newNote);
linearLayout.addView(noteButton);
// and get whatever type user account id is
SharedPreferences prefs = getSharedPreferences("MisPreferencias",getApplication().MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("newNote", note);
editor.commit();
String note = prefs.getString("newNote", "Welcome");
tvDiario.setText(note);
This is my code but it only saves the last string I want to show a string and then when I get a different String from intent show it but keep showing the previous, as a story. I need to edit the SharedPreference with different values each time the activity receive the putExtra data.
I don't think you fully understand SharedPreferences; it's a key-value data storage. By calling:
editor.putString("newNote", note);
you always override your saved value under the newNote key by new value note. editor.putString does not append your new data to your stored data, it rewrites the data.
The solution: you need to get your stored data first, append new data to those stored data, and store the updated data. Try:
newNote = extras.getString("Note");
...
SharedPreferences prefs = getSharedPreferences("MisPreferencias", getApplication().MODE_PRIVATE);
String storedNotes = prefs.getString("notes", "");
SharedPreferences.Editor editor = prefs.edit();
editor.putString("notes", storedNotes + newNote + "\n");
editor.commit();
String notes = prefs.getString("notes", "");
tvDiario.setText(notes);
I exchanged your newNote key by notes to better describe what is actually stored. I also recommend to read on documentation of Editor.putString and SharedPreferences.getString, I feel like you don't have a clear idea of the interface yet.

Filter out users in a drop down on PCF

In ClaimCenter I am trying to filter a drop down list to only display users that have a certain role. I am using a User input cell. There is no value range on the userinput cell only value. The value is set the user that they select so right now it's displaying all the users in the system instead of just the ones with the role that i want. Is there a way to show just the users that have the "Adjuster" role. I don't see a filter option either on this cell.
Right click the element and select "Change Element Type"
Then select "Range Input".
Then in the ValueRange property add a call to the code that you write.
The code to should find the subset of users that you want to show in the drop down and return them as a List or User[], something like this might work
function myValueRangeFunction(pClaim: Claim) : User[] {
//gets the group from the DB by public ID
var adjusterGroup = Group ("cc:123");
var adjustersOnly = new Set<User>();
var groupUsers = adjusterGroup.MembersNoSystemUsers
adjustersOnly.addAll(groupUsers*.Users)
return adjustersOnly.toArray()
}
You need to change the input type to Range Input (or Range Cell in case you are using List View) where valueRange property calls a method which retrieves users with a specific role.
.pcf file:
<RangeInput
editable="true"
id="userInput"
label=""Adjusters""
value="claim.AssignedUser"
valueRange="UserRoleUtil_Ext.Adjusters"
valueType="entity.User"/>
UserRoleUtil_Ext.gs:
uses gw.api.database.Query
uses gw.api.database.Relop
class UserRoleUtil_Ext {
public static property get Adjusters() : User[] {
var adjusterRole = Query.make(Role).compare(Role#Name, Relop.Equals, "Adjuster").select().AtMostOneRow
// Alternatively, you can retrieve the Role by its public-id, e.g.:
// var roleRetrievedById = Query.make(Role).compare(Role#PublicID, Relop.Equals, "cc:1").select().AtMostOneRow
return adjusterRole.AllUsersArray
}
}

Value does not fall within the expected range - Exception for SharePoint Lookup Field

I am trying to copy data from one list to other list (both lists are on different sites) along with lookup columns. But, I am getting an error for lookup field as:
Value does not fall within the expected range
Code works and data gets copied for other non-lookup fields. I tried every possible way including increasing List View Lookup Threshold and all possible ways of code but still error persists at ExecuteQuery().
Below is my code for lookup field:
if (field is FieldLookup && field.InternalName == "Country")
{
var CountryLookup = (item.FieldValues["Country"] as FieldLookupValue).LookupValue.ToString();
var CountryLookupId = (item.FieldValues["Country"] as FieldLookupValue).LookupId.ToString();
FieldLookupValue flvRDS = new FieldLookupValue();
flvRDS.LookupId = int.Parse(CountryLookupId);
itemToCreate["Country"] = flvRDS;
itemToCreate.Update();
destContext.ExecuteQuery();
}
Help is really appreciated.
I assume item is the new ListItem you're trying to create on your target list.
But you're never in fact reading any value from field here! So basically, you're trying to set your new FieldLookup.LookupId with the item["Country"].LookupId, which should logically be empty at this moment.
Here's a method I use to retrieve a lookup field ListItem from a value, feel free to modify it to fit your need, since I don't know how you want to retrieve it (SPList is an alias for Microsoft.SharePoint.Client.List).
private ListItem GetLookupItem(FieldLookup lookupField, string lookupValue)
{
string mappingField = lookupField.LookupField;
Microsoft.SharePoint.Client.List lookupList = Context.Web.Lists.GetById(new Guid(lookupField.LookupList));
Context.Load(lookupList);
Context.ExecuteQuery();
ListItemCollection libListItems = lookupList.GetItems(CamlQuery.CreateAllItemsQuery());
Context.Load(libListItems, items => items.Include(
itemlookup => itemlookup.Id,
itemlookup => itemlookup[mappingField]));
Context.ExecuteQuery();
foreach (ListItem mappedItem in libListItems)
{
object mappedField = mappedItem[mappingField];
if (mappedField != null && mappedField.ToString().Equals(lookupValue))
return mappedItem;
}
return null;
}
Now that you have the corresponding ListItem, you can set your item.LookupId with its Id:
if (field is FieldLookup && field.InternalName == "Country")
{
FieldLookupValue flvRDS = new FieldLookupValue();
flvRDS.LookupId = GetLookupItem(field as FieldLookup, "France").Id; // here, dunno how you get your country's name
itemToCreate["Country"] = flvRDS;
itemToCreate.Update();
destContext.ExecuteQuery();
}
Feel free to add some more previous code if you want an answer more suited for your specific issue.

Acumatica API: Using .ActionConvertToBAccount for converting Lead is failing

I'm trying to use the Acumatica API (Version 4.20.2231) for the Lead screen to convert a Lead to a Business Account. I was a bit worried about this even before I tried it because when you use the "Convert to Business Account" Action in Acumatica, it pops up a small dialog box. Here's what it looks like in Acumatica after choosing Actions > Convert to Business Account:
With the API, the first arrangement of commands I tried was failing to convert the Lead and not producing any kind of error. Eventually, I found a sequence of commands that produced an error that references dialog boxes, so that makes me think I'm on the right track. Maybe I just don't know how to manipulate the dialog box using the Commands. Does anyone know where I'm going wrong? Here is my code:
Public Function ConvertLeadToCustomer(ByVal leadID As String, ByVal firstName As String, ByVal lastName As String, ByRef companyName As String) As String
Dim CR301000 As CR301000Content = m_context.CR301000GetSchema()
m_context.CR301000Clear()
' converting a lead requires that there is a value for company, so create one if it is blank
If companyName = "" Then
companyName = lastName & ", " & firstName
End If
' create key field
Dim leadKeyValue As Value = New Value With {.LinkedCommand = CR301000.LeadSummary.LeadID, .Value = leadID}
' create company field, since its required
Dim companyValue As Value = New Value With {.LinkedCommand = CR301000.DetailsSummary.CompanyName, .Value = companyName, .Commit = True}
Dim updateLeadCommands As Command() = {leadKeyValue, CR301000.Actions.ActionConvertToBAccount, companyValue, CR301000.Actions.Save}
Dim updateLeadResult As CR301000Content() = m_context.CR301000Submit(updateLeadCommands)
' TO DO: search Business Accounts by name to find new Business Account ID
Dim newBAID As String = ""
Return newBAID
End Function
And here is the error returned when CR301000Submit is called:
System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> PX.Data.PXDialogRequiredException
at PX.Data.DialogManager.a(String A_0, PXGraph A_1, String A_2, Object A_3, String A_4, String A_5, MessageButtons A_6, MessageIcon A_7, Boolean A_8, InitializePanel A_9)
at PX.Data.DialogManager.AskExt(PXView view, String key, InitializePanel initializeHandler, Boolean repaintControls)
at PX.Data.PXView.AskExt(InitializePanel initializeHandler, Boolean refreshRequired)
at PX.Objects.CR.LeadMaint.ConvertToBAccount(PXAdapter adapter)
at PX.Data.PXAction`1.a(PXAdapter A_0)
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Api.SyImportProcessor.SyStep.CommitChanges(Object itemToBypass, PXFilterRow[] targetConditions)
at PX.Api.SyImportProcessor.ExportTableHelper.ExportTable()
at PX.Api.ScreenUtils.Submit(String screenId, Command[] commands, SchemaMode schemaMode, PXGraph graph)
at PX.Api.Services.ScreenService.Submit(String id, IEnumerable`1 commands, SchemaMode schemaMode)
at PX.Api.Soap.Screen.ScreenGate.Submit(Command[] commands)
--- End of inner exception stack trace ---
Your current question
Your error occurs because the action ConvertToBAccount is calling a Pop-Up Dialog and is expecting an answer:
if (AccountInfo.AskExt((graph, view) => graph.Views[view].Cache.Clear(), true) != WebDialogResult.OK) return contacts;
The way to tell Acumatica of it answer is to send the value "OK" before even calling the action. Depending on your configuration you might also want populate the field in this pop-up:
Dim commandsConvert As Command() =
{
New Value With {.Value = leadID, .LinkedCommand = CR301000.LeadSummary.LeadID, .Commit = True},
New Value With {.Value = "OK", .LinkedCommand = CR301000.NewAccountServicesSettings.ServiceCommands.DialogAnswer, .Commit = True}, 'This is how close the pop-up. We fill the field from the pop-up after this line
New Value With {.Value = newCompanyCD, .LinkedCommand = CR301000.NewAccountServicesSettings.BAccountID}, 'With autonumbering On, no need for this line.
New Value With {.Value = newCompanyName, .LinkedCommand = CR301000.NewAccountServicesSettings.AccountName}, 'The default value will be taken from DetailsSummary.CompanyName
CR301000.Actions.ActionConvertToBAccount
}
m_context.CR301000Submit(commandsConvert)
Your future question
Converting from a Lead to a BAccount is a 2-step process where you are redirected to your newly created BAccount and where you need to save it. It will not be converted as long as you don't save it.
This is generally a pretty straight forward process or you simply submit Save to the page where you were redirected (CR303000):
'Once the Process is completed, We want to save the new record.
'If we want to edit some information on the new Business Account
'this is the right place to do it.
Dim newBAID As String = String.Empty
Dim commandsBAccount As Command() =
{
CR303000.Actions.Save,
CR303000.AccountSummary.BusinessAccount
}
Dim newBAccountContent As CR303000Content() = m_context.CR303000Submit(commandsBAccount)
If newBAccountContent.Length > 0 Then
newBAID = newBAccountContent(0).AccountSummary.BusinessAccount.Value
End If
As long as you keep the same cookie container, the UserState should be aware that you are currently on a dirty CR303000 with cached information. If you use a custom web service endpoint that included both CR301000 and CR303000 there is nothing to deal with.
Unfortunatly, in this scenario it doesn't work.
It seems that the PXRedirectRequiredException is raised from within a PXLongOperation (read Thread) and that the Web-Service doesn't pick the dirty state of BAccount. The only solution I can find right now is to customize the action ConvertToBAccount to remove threading:
public class LeadMaintExt : PXGraphExtension<LeadMaint>
{
[PXUIField(DisplayName = Messages.ConvertToBAccount, MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.Process)]
public virtual IEnumerable ConvertToBAccount(PXAdapter adapter)
{
List<Contact> contacts = new List<Contact>(adapter.Get().Cast<Contact>());
foreach (Contact lead in contacts)
{
if (Base.AccountInfo.AskExt((graph, view) => graph.Views[view].Cache.Clear(), true) != WebDialogResult.OK) return contacts;
bool empty_required = !Base.AccountInfo.VerifyRequired();
BAccount existing = PXSelect<BAccount, Where<BAccount.acctCD, Equal<Required<BAccount.acctCD>>>>.SelectSingleBound(Base, null, Base.AccountInfo.Current.BAccountID);
if (existing != null)
{
Base.AccountInfo.Cache.RaiseExceptionHandling<LeadMaint.AccountsFilter.bAccountID>(Base.AccountInfo.Current, Base.AccountInfo.Current.BAccountID, new PXSetPropertyException(Messages.BAccountAlreadyExists, Base.AccountInfo.Current.BAccountID));
return contacts;
}
if (empty_required) return contacts;
Base.Save.Press();
//PXLongOperation.StartOperation(this, () => ConvertToAccount(lead, AccountInfo.Current));
LeadMaint.ConvertToAccount(lead, Base.AccountInfo.Current);
}
return contacts;
}
}
I am looking for a better way to address the situation and I'll edit my answer when I find it.
The commands specified should be for the dialog not the summary.
The required fields and the dialog answer should be provided before calling the action.
Below is the sample used for create Receipt action in receipts screen.
Note: C# code
commands = new Command[]
{
new Value
{
Value = CreatedOn.HasValue ? ((DateTime)CreatedOn.Value).ToLongDateString() : "",
LinkedCommand = SOSchema.SpecifyShipmentParameters.ShipmentDate,
Commit = true
},
new Value
{
Value = "OK",
LinkedCommand = SOSchema.SpecifyShipmentParameters.ServiceCommands.DialogAnswer,
Commit = true
},
SOSchema.Actions.ActionCreateReceipt
};
context.SO301000Submit(commands);
Hope this helps.

how to retrieve multiple properties using #NameLookup in lotus notes

I am using #NameLookUp formula to retrieve internet address by giving a search key and it is working fine.But now i want to retrive not only the internet address but also some other properties like FirstName and LastName.
Here is the formula i am using to #Namelookup internet address by giving search string.
Vector vec=m_session.evaluate("#NameLookup([NoUpdate];\""+ userName + "\"; \"InternetAddress\")");
//username is the String variable(Search Criteria)
Can anyone please help how to retrieve multiple properties(like firstName and lastName along with InternetAddress) by evaluate the formula only once. If it cant be done using #Namelookup is there any other way..?
This is a typical example when using evaluate() to call a Formula is not a good idea.
What you want to do is to get the NotesDocument class and read values from it.
Something like this (disclaimer, I am not a Java developer):
// Open Domino Directory on specified server
Database db = session.getDatabase("YourServer/Domain", "names.nsf");
// Get a view with user name is sorted first column
View view = db.getView("($Users)");
// Get the person document for specified user
Document doc = view.getDocumentByKey(userName, true);
if (doc != null) {
// Get text values from Notes document
String emailAddress = doc.getItemValueString("InternetAddress");
String officePhone = doc.getItemValueString("OfficeNumber");
String officeAddress = doc.getItemValueString("OfficeStreetAddress");
}
I believe this would be faster than multiple lookups using evaluate(), and you also have the added benefit of full error handling, and all being native code.
#NameLookup only returns the value of one item per call.
Assuming your goal is to only have one Evaluate statement, you could chain the calls together and return an array of values in a certain order:
Vector vec=m_session.evaluate("FirstName := #NameLookup([NoUpdate];\""+ userName + "\"; \"FirstName\"); LastName:= #NameLookup([NoUpdate];\""+ userName + "\"; \"LastName\"); InternetAddress :=#NameLookup([NoUpdate];\""+ userName + "\"; \"InternetAddress\"); FirstName:LastName:InternetAddress");
Or possibly:
String firstName = m_session.evaluate("#NameLookup([NoUpdate];\""+ userName + "\"; \"FirstName\")");
String lastName = m_session.evaluate("#NameLookup([NoUpdate];\""+ userName + "\"; \"LastName\")");
String internetAddress = m_session.evaluate("#NameLookup([NoUpdate];\""+ userName + "\"; \"InternetAddress\")");
And then add those three strings in any order into your Vector.
Another approach is to use the DirectoryNavigator class. I believe it's been available since Notes/Domino 8.5 (perhaps even before that). DirectoryNavigator uses some of the same core logic as #NameLookup, so it should perform well.
Here's some sample code. I haven't tested this exact code, but I adapted it from production code that does a similar lookup:
String firstName = null;
String lastName = null;
String inetAddress = null;
Vector<String> lookupItems = new Vector<String>();
lookupItems.addElement("FirstName");
lookupItems.addElement("LastName");
lookupItems.addElement("InternetAddress");
Vector<String> vName = new Vector<String>();
vName.addElement(userName);
Directory dir = session.getDirectory();
DirectoryNavigator dirNav = dir.lookupNames("($Users)", vName, lookupItems, true);
if( dirNav != null && dirNav.getCurrentMatches() != 0 ) {
// Digest the results of the lookup
Vector<String> value = null;
value = dirNav.getFirstItemValue();
firstName = value.elementAt(0);
value = dirNav.getNextItemValue();
lastName = value.elementAt(0);
value = dirNav.getNextItemValue();
inetAddress = value.elementAt(0);
}

Resources