Maybe this is an issue with persistance and the domain object. So I have a list of manual notes that can be added to a person. My person class looks similar to this (I've wrapped the object in transients to ignore persistence):
class Person {
...
List<String> notes = new ArrayList<String>()
...
}
When I update a person with a note (textfield on view will allow note to be added), I want to do something simple like adding the new note to the array list tied to the person:
class PersonController {
...
def update() {
def contactInstance = Contact.get(params.id)
if (!contactInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'contact.label', default: 'Contact'), params.id])
redirect(action: "list")
return
}
if (params.version) {
def version = params.version.toLong()
if (contactInstance.version > version) {
contactInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'contact.label', default: 'Contact')] as Object[],
"Another user has updated this Contact while you were editing")
render(view: "edit", model: [contactInstance: contactInstance])
return
}
}
contactInstance.properties = params
/**
* Check for inactive - Then flag with user and date tag
*/
if(params.isActive == null) {
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm");
Date date = new Date();
contactInstance.properties.isActiveNote = "Made inactive by " + session.user + " on " + dateFormat.format(date) + "."
}
/**
* Date stamp of the note itself
*/
if(params.notes.equals("")) {}
else {
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm");
Date date = new Date();
//contactInstance.properties.notes = contactInstance.properties.notes + " " + params.notes + " - (" + dateFormat.format(date) + " " + session.user + ");"
contactInstance.allThese.add(contactInstance.properties.notes)
println(contactInstance.allThese)
}
if (!contactInstance.save(flush: true)) {
render(view: "edit", model: [contactInstance: contactInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'contact.label', default: 'Contact'), contactInstance.id])
redirect(action: "show", id: contactInstance.id)
}
...
}
The call to update from my gsp:
<g:actionSubmit class="save" action="update" value="${message(code:'default.button.update.label', default: 'Update')}" />
But it just seems to store the array with a single note. Is there a persistance issue with Grails domain objects and collections? It could very well be a simple issue on my end!
Thanks for all the help.
If your view includes multiple <input> fields or <textarea>s with the same name ("notes", to match the field name in your Domain class), Grails will automatically bind the values to your notes array.
You might also look into the new params.list() method that would allow you to iterate through request parameters and then add them individually to your array.
Related
I am getting the following error while push/add items to an array in groovy.
$groovy main.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
at main$_buildOutNotes_closure2.doCall(main.groovy:82)
at main.buildOutNotes(main.groovy:75)
at main$buildOutNotes.callCurrent(Unknown Source)
at main.run(main.groovy:64)
Here is the function:
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
Here are the articles that show push and add use
https://mrhaki.blogspot.com/2015/01/groovy-goodness-pop-and-push-items-in.html
def list = ['Groovy', 'is', 'great!']
list.push('rocks!')
http://docs.groovy-lang.org/next/html/documentation/working-with-collections.html
def list = [5, 6, 7, 8]
emptyList.add(5)
I am building the example code on https://www.tutorialspoint.com/execute_groovy_online.php.
You can view the example here
http://tpcg.io/NGw4szCv
Here is the full code as well:
//package com.javacodegeeks.groovy.date;
//import static java.util.Calendar.*;
//import groovy.json.*;
//import java.util.Properties;
//import java.util.List;
//progress notes object
class ProgressNotes {
def ActionDate
String ActionBy
String Status
String ProgressNote
ProgressNotes(inActionDate, inActionBy, inStatus, inNote){
this.ActionDate = inActionDate
this.ActionBy = inActionBy
this.Status = inStatus
this.ProgressNote = inNote
}
}
//delimiter
String delimiter = "##";
//out notes
ProgressNotes[] outNotes;
//date patterns
def dateInSystemPattern = "yyyy-MM-dd HH:mm:ss";
def dateIncomingPattern = "MM/dd/yyyy hh:mm ";
/************** SAMPLE DATA START ****************/
//incoming note string
String incomingNote = "2019-12-15T01:29:44 User1: December 13 went to pickup the toilet at the wholesaler " +
"then went to site then remove and replace the toilet then found out that there is a " +
"fruit inside the toilet then clean up the site and silicone around the toilet then " +
"throw the old toilet at dumpster." + delimiter +
"2019-12-13T10:43:05 User2: applied 3 bottles of urinal treatment. let sit for an " +
"hour. augered out urinal main. draining excellent. tried augering toilet. object stuck in " +
"toilet. will not come out. Don will replace." + delimiter +
"2019-12-13T09:18:51 user3: PO 508758 - unclog Washroom " +
"Details: " +
"Unclog toilet bowl and urinal in. Room 116.";
//in system notes
ProgressNotes[] systemNotes = [
["2012-01-26T14:52:50", "User1", "DISPATCHED", "reassign to Space Planning to confirm space availability"],
["2012-02-01T12:23:05", "User2", "DISPATCHED", "spoke to requestor and she has a few relocations and POD requirements."],
["2012-02-01T12:23:45", "User3", "DISPATCHED", "Contacted Customer for clarification spreadsheet is forthcoming for this request."],
["2012-02-03T18:45:00", "User1", "DISPATCHED", "Extending date to allow for clean-up of backlog."]
];
/************** SPLIT incomingNote ****************/
def incomingNotes = [];
if (incomingNote != ""){
incomingNotes = incomingNote.split(delimiter);
}
/************** PICK NOTES ****************/
if (!incomingNotes){
//No incoming notes push the system notes out
outNotes = systemNotes;
}
else{
//check and build the outnotes
outNotes = buildOutNotes(incomingNotes, systemNotes);
}
println("OUTNOTES Length: " + outNotes.length)
println(" ");
/************** HELPER METHODS ****************/
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
def splitIncoming(incoming){
//date time characters
int dateTimeChars = 20;
def dateAndTimePart = incoming.substring(0,dateTimeChars).trim();
String remainingNote = incoming.substring(dateTimeChars);
String userPart = "";
String notePart = "";
def remainingNotes = remainingNote.split(":");
if(remainingNotes){
userPart = remainingNotes.getAt(0);
notePart = incoming.substring(dateTimeChars+userPart.length()+1).trim();
}
//build the object
def outNote = new ProgressNotes(dateAndTimePart, userPart, "", notePart);
return outNote;
}
You use an array in your code (ProgressNotes[]), not a list (List<ProgressNotes>). Any of the mentioned methods (add and push) does not exist for Java (and thus Groovy) arrays. An array is fixed size, so once initialized, you can't add any new elements to it - you can only replace existing elements. If you try to add a new element to the array, you will get IndexOutOfBoundsException. Just look at this simple example:
String[] list = ["foo", "bar"]
assert list[0] == "foo"
assert list[1] == "bar"
try {
list[2] = "new"
} catch (IndexOutOfBoundsException e) {
println "Caught!"
}
list[1] = "abc"
println list
Output:
Caught!
[foo, abc]
If you want to use List.add() or List.push() (or event groovier leftShift like [] << "elem") you need to use a list instead of an array. Arrays are a good choice if you know the size of the collection is fixed.
//out notes
List<ProgressNotes> outNotes;
Given a simple poco
public class Model
{
[PrimaryKey]
public int ID { get; set; }
public string Description { get; set; }
}
this works fine ...
var connectionString = #"Data Source=WIN8PC\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True;";
connectionFactory = new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider);
using (var db = connectionFactory.OpenDbConnection())
{ db.DropAndCreateTable<Model>(); }
var model0 = new Model { ID = 0, Description = "Item Zero" };
var model1 = new Model { ID = 1, Description = "Item One" };
using (var db = connectionFactory.OpenDbConnection())
{ db.Save(model0, model1); }
as does this ...
model0.Description += " updated";
model1.Description += " updated";
using (var db = connectionFactory.OpenDbConnection())
{
db.Save(model0);
db.Save(model1);
}
however, this crashes with a primary key violation exception ...
model0.Description += " updated again";
model1.Description += " updated again";
using (var db = connectionFactory.OpenDbConnection())
{ db.Save(model0, model1); }
The record with ID zero is required, as this is a lookup table to replace an existing C# enum type. This is a local copy of distributed data (that I don't control), so there's no reason to have an auto-increment key.
The issue appears to be in OrmLiteWriteCommandExtensions.SaveAll() - any row with id == defaultValue is assumed to be a new item, rather than an update of an existing record. The same issue occurs in the parallel async methods too.
Is there any other way to get around this issue, other than by saving each record individually (inside a transaction). It would be preferable to save all updated records for a table in one command.
Save is a high-level API that will INSERT or UPDATE based on whether or not the Primary Key has a value. If you want to insert a default Primary Key value you can use Insert instead as seen in this Live Example on Gistlyn:
public class Model
{
[PrimaryKey]
public int ID { get; set; }
public string Description { get; set; }
}
db.DropAndCreateTable<Model>();
var model0 = new Model { ID = 0, Description = "Item Zero" };
var model1 = new Model { ID = 1, Description = "Item One" };
db.Insert(model0, model1);
var rows = db.Select<Model>();
"Inserted Rows: {0}".Print(rows.Dump());
Which outputs:
Inserted Rows: [
{
ID: 0,
Description: Item Zero
},
{
ID: 1,
Description: Item One
}
]
Order form has two fields, Ship Date (datefield) and Lead Time (number field in days).
When a ship date is entered, it should be greater than the current date + the lead time. alert user if not, and do not allow save of record.
There is one exception to the rule though: If the approved for rush field and approved by field is filled out the alert is not necessary and record can be saved.
++ It would be a plus if this could actually not account for weekend days. But not mandatory
function checkLeadTime
{
var shipDate = Xrm.Page.getAttribute("requestdeliveryby").getValue();
var leadTime = Xrm.Page.getAttribute("orbus_leadtime").getValue();
var leadTimeDate = Xrm.Page.getAttribute("orbus_leadtimedate").getValue():
if(leadTime != NULL)
{
var approvedRushProduction = xrm.getAttribute("orbus_projectapprovedrush").getValue();
var approvedBy = Xrm.Page.getAttribute("orbus_approvedbyid").getValue();
var currentTime = new Date();
var newDate = currentTime.setDays(currentTime.getDays + leadTime);
leadTimeDate.setValue(newDate);
if(approvedRushProduction == 0 && approvedBy == NULL)
{
if ( newDate < shipDate)
{
alert("Sorry, Ship Date is less than lead time!");
}
else
{
alert("Current Time = " + newDate);
}
}
}
}
You can attach your function to the form onSave event.
if ( newDate < shipDate ) {
alert("Sorry, Ship Date is less than lead time!");
Xrm.Page.context.getEventArgs().preventDefault();
} else {
alert("Current Time = " + newDate);
}
Use Xrm.Page.context.getEventArgs().preventDefault() to stop the save event.
To stop saving and show error message you can use:
Xrm.Page.getControl(fieldName).setNotification(message);
http://garethtuckercrm.com/2013/10/17/crm-2013-new-features-javascript-notifications/
I am trying to remove the user accounts which are inactive from last 30 days.
I tried fetching User Information List. Checked all of it's properties and fields but coudn't find anything related to last login time.
You can do something like this
public DateTime Get(string attr, string UserName)
{
DomainConfiguration domainConfig = new DomainConfiguration();
using (new SPMonitoredScope("AD Properties"))
{
using (DirectoryEntry domain = new DirectoryEntry("LDAP://" + domainConfig.DomainName, domainConfig.UserName, domainConfig.Password))
{
//DirectorySearcher searcher = new DirectorySearcher(domain, "(|(objectClass=organizationalUnit)(objectClass=container)(objectClass=builtinDomain)(objectClass=domainDNS))");
DirectorySearcher searcher = new DirectorySearcher(domain);
searcher.PageSize = 1000;
searcher.Filter = "(SAMAccountName='" + UserName + "')";
//searcher.Filter = "(|(objectCategory=group)(objectCategory=person))";
searcher.Filter = "(&(objectClass=user) (cn=" + UserName + "))";
var user = searcher.FindOne();
DateTime LastLogon = DateTime.FromFileTime((Int64)user.Properties["lastLogon"].Value);
return LastLogon;
}
}
}
Hope this Helps you.
I do not know why it does gives me the some older dates than i expected.
but at least it will compile and run.
using System.DirectoryServices.AccountManagement;
private static DateTime? GetUserIdFromDisplayName(string displayName)
{
// set up domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find user by display name
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, displayName);
if (user != null)
{
return user.LastLogon;
}
else
{
return null;
}
}
}
In my current project I need write in a table all values are changed in the application.
Ex. the guy update the UserName, I need put in a table UserName old value "1" new value "2".
I tried use the ObjectStateEntry but this return all fields. I think the FW return all because my code.
public USER Save(USER obj)
{
using(TPPTEntities db = new TPPTEntities())
{
db.Connection.Open();
USER o = (from n in db.USERs where n.ID == obj.ID select n).FirstOrDefault();
if (o == null)
{
o = new USER()
{
BruteForce = 0,
Email = obj.Email,
IsBlock = false,
LastLogin = DateTime.Now,
Name = obj.Name,
UserName = obj.UserName,
UserPassword = new byte[0],
};
db.AddToUSERs(o);
}
else
{
o.Email = obj.Email;
o.Name = obj.Name;
o.UserName = obj.UserName;
}
db.SaveChanges();
db.Connection.Close();
}
return obj;
}
A way to get old and new values is this:
var ose = this.ObjectStateManager.GetObjectStateEntry(o.EntityKey);
foreach (string propName in ose.GetModifiedProperties())
{
string.Format("Property '{0}', old value: {1}, new value: {2}",
propName, ose.OriginalValues[propName], ose.CurrentValues[propName]);
}
This is pretty useless, of course, but I'm sure you'll know what to do in the foreach loop to store the changes.
Is this a WCF Service? In that case, the changes will probably never come trough since changes to the Object Graph are made where the Object Context is not available. Consider using Self-Tracking Entities