I have write a custom field that extends SPFieldLookup. I set AllowMultipleValues = true;
Here is Field Control Value:
public override object Value
{
get
{
EnsureChildControls();
SPFieldLookupValueCollection vals = new SPFieldLookupValueCollection();
ICollection s = TinBaiLienQuanPicker.SelectedIds;
if (s != null && s.Count > 0)
{
foreach (var i in s)
{
ListItem z = availableItems.Find(x => (x.Value == i.ToString()));
if (z != null)
{
vals.Add(new SPFieldLookupValue(int.Parse(z.Value), z.Text));
}
}
}
return vals;
}
set
{
EnsureChildControls();
base.Value = value as SPFieldLookupValueCollection;
}
}
When control save field data, I see it return a collection which have multiple value.
But when I retrieve data again, I receive only the first value. I get value from Control Field ' ListItemFieldValue property.
Please give me a tip. Thank you very much.
Data storage logic is different in the database for lookup field with one value compared to multiple values. Check that in the field type definition xml ParentType is set to LookupMulti instead of Lookup.
You have to inherit the field control class from MultipleLookupField not from LookupField. Are you sure you are doing this?
Related
I would like to select from the database using a PXSelect and then receive a value that respects the segmented key.
Example:
var item = InventoryItem.PK.Find(graph, someInventoryID);
string inventoryCDRespectingSegmentedKey = SomeClass.SomeMethod(item.InventoryCD); // InventoryCD is a string value without the separators defined in the Segmented key at this point
I am not sure if this is the best way to get the value respecting segments or not, but this is how I do it usually.
if (row.InventoryID != null)
{
var item = InventoryItem.PK.Find(sender.Graph, row.InventoryID);
if (item != null)
{
var inventoryCDRespectingSegmentedKey = sender.Graph.Caches[typeof(InventoryItem)].GetValueExt<InventoryItem.inventoryCD>(item) as PXSegmentedState;
PXTrace.WriteInformation($"{inventoryCDRespectingSegmentedKey.ToString()}");
}
}
I have a customization that uses BLC code to obtain the results of a Generic Inquiry and use that data to run a process.
The generic inquiry has several fields that are formulae and, for some reason, the returned results show null when they do, in fact, contain an amount - calculated by the formula:
Here's the BLC code to loop through the GI rows:
GIDesign gi = PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base, "ACAllocationBranches");
if (gi != null)
{
//Creates Generic Inquiry Graph for the specified inquiry
PXGenericInqGrph graph = PXGenericInqGrph.CreateInstance(gi.DesignID.Value);
//Set the filter...
graph.Caches[typeof(GenericFilter)].SetValueExt(graph.Filter.Current, "RefNbr", apinv.RefNbr);
//Loops through each returned result row of Generic Inquiry
foreach (GenericResult resultRow in graph.Views["Results"].SelectMulti())
{
GIMap.Clear();
//Loops through objects returned from one - not an object per field
foreach (string key in resultRow.Values.Keys)
{
//Loops through list of each object and the fields we need values from for each data key
foreach (GIResult resultMap in PXSelectReadonly<GIResult, Where<GIResult.designID, Equal<Required<GIResult.designID>>, And<GIResult.objectName, Equal<Required<GIResult.objectName>>>>>.Select(graph, new object[] { gi.DesignID.Value, key }))
{
//retrieves field value from data object specified
var result = graph.Caches[resultRow.Values[key].GetType()].GetValue(resultRow.Values[key], resultMap.Field);
//Load up the GIMap dictionary...
if(resultMap.IsActive == true)
GIMap.Add(key + "." + resultMap.Field, result);
}
}
}
}
Here's what the GI formula field looks like:
Unfortunately, these formula fields, like I mentioned, return null as a value in the Code, where the actual Generic Inquiry has numeric values.
Is it possible - or is there another way, through that BLC code example, to obtain those formula values?
Thanks much...
I suggest you to try this code:
public PXAction<SOOrder> Prc;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Prc")]
protected virtual IEnumerable prc(PXAdapter adapter)
{
GIDesign gi =
PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base,
"PMBudget");
var graph = PXGenericInqGrph.CreateInstance(gi.DesignID.Value);
List<bool> descs = new List<bool>(PXView.Descendings);
List<string> sorts = new List<string>(PXView.SortColumns);
int startRow = 0;
int totalRows = 0;
var result = graph.Results.View.Select(null, null, PXView.Searches, sorts.ToArray(), descs.ToArray(),
null, ref startRow, PXView.MaximumRows, ref totalRows)
.Select(x => (GenericResult)x);
return adapter.Get();
}
Results were like those:
I have added a field to the Prepare Replenishment form that needs to be updated when a user selects a row in the Replenishment Item grid. How do I access the field. I know it is in the INReplenishmentFilterExt but I can't figure out how to get access to the extension.
Edit #1: I am able to get the value of the field but it does not update on the screen when I use cache.SetValue. I am trying to update this filter extension field from inside of the Selected event handler.
protected void INReplenishmentItem_Selected_FieldUpdating(PXCache cache, PXFieldUpdatingEventArgs e)
{
var row = (INReplenishmentItem)e.Row;
if (row == null)
return;
INReplenishmentFilter filter = Base.Filter.Current;
INReplenishmentFilterExt filterExt = PXCache<INReplenishmentFilter>.GetExtension<INReplenishmentFilterExt>(filter);
decimal poAmount = filterExt.UsrPOAmount.HasValue ? filterExt.UsrPOAmount.Value : 0;
decimal lastPrice = pvi.LastPrice.HasValue ? pvi.LastPrice.Value : 0;
decimal newPOAmount = poAmount + lastPrice;
cache.SetValue<INReplenishmentFilterExt.usrPOAmount>(filterExt, newPOAmount);
}
You cannot set the value of the Filter using the Cache of INReplenishmentItem.
I have edited my answer, the code below should work.
//Always use virtual methods for Event Handlers
protected virtual void INReplenishmentItem_Selected_FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
{
//Try not to use vars. Especially when you know the Type of the object
INReplenishmentItem row = e.Row as INReplenishmentItem;
if (row != null)
{
INReplenishmentFilter filter = Base.Filter.Current;
INReplenishmentFilterExt filterExt = PXCache<INReplenishmentFilter>.GetExtension<INReplenishmentFilterExt>(filter);
decimal poAmount = filterExt.UsrPOAmount ?? 0;
decimal lastPrice = pvi.LastPrice ?? 0;//Not sure what pvi is
decimal newPOAmount = poAmount + lastPrice;
//"sender" is the cache that specifically stores the datatype of row
//Therefor you cannot use it to update records of a different datatype
//You also should not pass an Extension into the argument that should be the row object you are trying to update
Base.Filter.Cache.SetValueExt<INReplenishmentFilterExt.usrPOAmount>(filter, newPOAmount);
}
}
I'm trying to use setup data from one table to allow me to format fields on the fly / dynamically. I know I can change field names and visibility based on the PXUIFieldAttribute class, but changing the precision or string length is a bit trickier, obviously. From the research I've done, I've come up with the following example code that seems like it should work - but I get the error:
"Unable to cast object of type 'PX.Data.PXUIFieldAttribute' to type 'PX.Data.PXDBDecimalAttribute'.
I don't see why this is occurring...
protected virtual void xTACOpenSourceDetail_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var osd = (PXCache)sender;
foreach (PXDBDecimalAttribute attribute in this.Caches<xTACOpenSourceDetail>().GetAttributes("Number1"))
{
PXDBDecimalAttribute someAttribute = attribute as PXDBDecimalAttribute;
if (someAttribute != null)
{
someAttribute.DBProperties._precision = 4;
}
}
}
I just tried the below code in sales order screen and it seems working!
var props = typeof(SOOrder).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(PXDecimalAttribute)));
foreach (System.Reflection.PropertyInfo item in props)
{
PXDecimalAttribute.SetPrecision(this.Base.Caches[typeof(SOOrder)], item.Name, 1);
}
You might need to change this to match your DAC.
So I'm working on an app that will select data from one database and update an identical database based on information contained in a 'Publication' Table in the Authoring database. I need to get a single object that is not connected to the 'Authoring' context so I can add it to my 'Delivery' context.
Currently I am using
object authoringRecordVersion = PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).Find(record.RecordId);
object deliveryRecordVersion = PublishingFactory.DeliveryContext.Set(recordType.Entity.GetType()).Find(record.RecordId));
to return my records. Then if the 'deliveryRecordVersion' is null, I need to do an Insert of 'authoringRecordVersion' into 'PublishingFactory.DeliveryContext'. However, that object is already connected to the 'PublishingFactory.AuthoringContext' so it won't allow the Add() method to be called on the 'PublishingFactory.DeliveryContext'.
I have access to PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).AsNoTracking()
but there is no way to get at the specific record I need from here.
Any suggestions?
UPDATE:
I believe I found the solution. It didn't work the first time because I was referencing the wrong object on when setting .State = EntityState.Detached;
here is the full corrected method that works as expected
private void PushToDelivery(IEnumerable<Mkl.WebTeam.UWManual.Model.Publication> recordsToPublish)
{
string recordEntity = string.Empty;
DbEntityEntry recordType = null;
// Loop through recordsToPublish and see if the record exists in Delivery. If so then 'Update' the record
// else 'Add' the record.
foreach (var record in recordsToPublish)
{
if (recordEntity != record.Entity)
{
recordType = PublishingFactory.DeliveryContext.Entry(ObjectExt.GetEntityOfType(record.Entity));
}
if (recordType == null)
{
continue;
////throw new NullReferenceException(
//// string.Format("Couldn't identify the object type stored in record.Entity : {0}", record.Entity));
}
// get the record from the Authoring context from the appropriate type table
object authoringRecordVersion = PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).Find(record.RecordId);
// get the record from the Delivery context from the appropriate type table
object deliveryRecordVersion = PublishingFactory.DeliveryContext.Set(recordType.Entity.GetType()).Find(record.RecordId);
// somthing happened and no records were found meeting the Id and Type from the Publication table in the
// authoring table
if (authoringRecordVersion == null)
{
continue;
}
if (deliveryRecordVersion != null)
{
// update record
PublishingFactory.DeliveryContext.Entry(deliveryRecordVersion).CurrentValues.SetValues(authoringRecordVersion);
PublishingFactory.DeliveryContext.Entry(deliveryRecordVersion).State = EntityState.Modified;
PublishingFactory.DeliveryContext.SaveChanges();
}
else
{
// insert new record
PublishingFactory.AuthoringContext.Entry(authoringRecordVersion).State = EntityState.Detached;
PublishingFactory.DeliveryContext.Entry(authoringRecordVersion).State = EntityState.Added;
PublishingFactory.DeliveryContext.SaveChanges();
}
recordEntity = record.Entity;
}
}
As you say in your comment the reason why you can't use .Single(a => a.ID == record.RecordId) is that the ID property is not known at design time. So what you can do is get the entity by the Find method and then detach it from the context:
PublishingFactory.AuthoringContext
.Entry(authoringRecordVersion).State = EntityState.Detached;