I'm looking for help, I want to concatenate the type value and tax category value within the description field.
Any ideas?
Thanks!!
There are a couple of ways in which you can go about this.
Given that there are 2 fields involved in your concatenation, and any of them could be changed at any given moment, you could manage the logic in the RowUpdated event. In which case, you should filter by the specific fields to be considered in order to prevent this logic to be executed for any other field.
For instance:
protected virtual void InventoryItem_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
InventoryItem newRow = (InventoryItem)e.Row;
InventoryItem oldRow = (InventoryItem)e.OldRow;
if (!sender.ObjectsEqual<InventoryItem.itemType>(e.Row, e.OldRow)
|| !sender.ObjectsEqual<InventoryItem.taxCategoryID>(e.Row, e.OldRow))
{
//Add concatenation logic
sender.SetValue<InventoryItem.descr>(newRow, "NEW CONCATENATED VALUE");
}
}
The advantage of this approach is that you have all the logic centralized in the one place.
As an alternative approach, you could use the FieldUpdated events from ItemType and TaxCategoryID.
Regardless of the option that you select, both fields should have the CommitChanges property set to true in the ASPX in order to have the logic executed immediately after taking the focus out of the field
Create a customization. In the customization project, go to Database Script, create a sql script. Add the following command
update inventoryitem set descr = case when charindex(' type: ',descr)>0
then left(descr,charindex(' type: ',descr))
else descr end + ' type: ' + case
when itemtype = 'F' then 'Finished Good'
when itemtype = 'M' then 'Component Part'
when itemtype = 'A' then 'SubAssembly'
when itemtype = 'N' then 'Non-Stock Item'
when itemtype = 'L' then 'Labor'
when itemtype = 'S' then 'Service'
when itemtype = 'C' then 'Charge'
else 'Expense' end + ' tax: ' + TaxCategoryID
Publish the customization whenever you want to update the values.
Related
I had added a custome field "UsrWgtIndex" on Allocation panel in Purchase Receipts. And its value is the sum of the other custom field "UsrWgtPerUnit".
But strange thing happened. The value of UsrWgtIndex keeps the same when I open diffrent allocation panel. It is always the value of first row of transations.
My code is below, and I'm really confused about this. In logic, the code will sum each row of transations, and assign each row of the "UsrWgtIndex". But it's always the value of the first row.
Anyone can help on this? Thanks a lot!
namespace PX.Objects.PO {
public class POReceiptEntry_Extension: PXGraphExtension < POReceiptEntry > {
#region Event Handlers
decimal totalCgt = 0 M,
tempTotal = 0 M;
protected void POReceiptLine_RowSelected(PXCache cache, PXRowSelectedEventArgs e) {
POReceiptLine poRLine = (POReceiptLine) e.Row;
if (poRLine != null) {
totalCgt = 0 M;
foreach(POReceiptLineSplit line in Base.splits.Select()) {
POReceiptLineSplitExt poReceiptLineSplitExt = line.GetExtension < POReceiptLineSplitExt > ();
var recentQty = poReceiptLineSplitExt.UsrWgtPerUnit;
var temp = Convert.ToDecimal(recentQty);
totalCgt = totalCgt + temp;
};
var cgt = Convert.ToDecimal(totalCgt);
if (totalCgt != null) {
cache.SetValue < POReceiptLineExt.usrTotalWgt > (poRLine, cgt);
//This line is setting the value of usrWgtIndex
cache.SetValue < POReceiptLineExt.usrWgtIndex > (poRLine, cgt);
};
}
}
}
}
More detail update:
The customer field “usrWgtIndex” belongs to the data class “POReceiptLine”. But I make its control on the Allocations Panel.
I had made a test: set value to the field “UnassignedQty” on allocations panel with the same value I give to “usrWgtIndex”. It works correctly. Or I changed the other field in POReceiptLine data class with same value in the same time, It works fine again.
3.It seems that if I change a custom field on allocations panel, this strange thing would happen…
More Detail Update2:
I didn't add the "UsrWgtIndex" on the DAC LotSerOptions. I added it on the POReceiptLine. Because when I add the custom field on LotSerOptions, I can't assign its value with setValueEXT methord, it seems there is no DAC named "LotSerOptionsExt".
So I just put "UsrWgtIndex" on DAC POReceiptLine, and assign its value with
cache.SetValue<POReceiptLineExt.usrWgtIndex>(poRLine, cgt);
The 'splits' DataView depends on 'Current' POReceiptLine:
PXSelect<POReceiptLineSplit, Where<POReceiptLineSplit.receiptNbr, Equal<Current<POReceiptLine.receiptNbr>>,
And<POReceiptLineSplit.lineNbr, Equal<Current<POReceiptLine.lineNbr>>,
And<Where<POLineType.goodsForInventory, Equal<Current<POReceiptLine.lineType>>,
Or<POLineType.goodsForSalesOrder, Equal<Current<POReceiptLine.lineType>>,
Or<POLineType.goodsForDropShip, Equal<Current<POReceiptLine.lineType>>>>>>>>> splits;
The issue is that 'Current' POReceiptLine doesn't change when user clicks (select) another POReceiptLine in 'transactions' grid. Setting the grid 'SyncPosition' to true in your customization project should ensure 'Current' value is properly set when users change record selection in the grid:
Is there an eloquent way, more or less, to get the last displayed record in a grid in Acumatica? Let's say even if they do all the sorting and rearranging, is there a way for example when pressing a button on a grid to get the last record? Basically, I would like to copy that record as a new one.
Create a PXAction for your button.
Inside the PXAction iterate in your data view until the last record.
For example, if the name of your Data view Bound to your grid is YzLines, and object type in the grid line (DAC) is Yz, then it can be:
Yz lastLine;
foreach (Yz line in YzLines.Select())
lastLine = line;
To get to the last record you can also use .Last() or .LastOrDefault().
If you need the last record according to client sorting, you should implement a data view delegate, it looks like this:
protected virtual IEnumerable yzLines()
{
PXSelectBase<Yz> cmd =
new PXSelectJoinGroupBy<Yz, ...>(this);
int startRow = PXView.StartRow; //Get starting row of the current page
int totalRows = 0;
foreach (PXResult<Yz> res in
cmd.View.Select(null, null,
PXView.Searches,
ARDocumentList.View.GetExternalSorts(),//Get sorting fields
ARDocumentList.View.GetExternalDescendings(),//Get sorting direction
ARDocumentList.View.GetExternalFilters(),//Get filters
ref startRow,
PXView.MaximumRows, //Get count of records in the page
ref totalRows))
{
//processing of records
}
PXView.StartRow = 0;//Reset starting row
}
I am new to the API and I'm trying to get values from the active view. I am using the following code as a mock up to what I'm trying to do:
public void GetViewProperties()
{
String viewname;
String typename;
String levelname;
String Output;
ViewFamilyType VfamType;
Level lev;
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
VfamType = new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Where(q => q.Name == "1-0-Model").First() as ViewFamilyType;
//Find the level that matches the active view
lev = new FilteredElementCollector(doc).OfClass(typeof(Level))
.Where(q => q.Name == "00").First() as Level;
//Get the view's current name
viewname = currentView.Name.ToString();
//Get the name of the view family type
typename = VfamType.Name;
//Get the name of the level
levelname = lev.Name.ToString();
//Combine results for task dialog
Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test",Output);
}
I'm cheating at the moment by grabbing the view type and level by name. I really want them to be found by looking at the properties of the active view. I can't figure out how I am meant to access the view type and level name properties. I need to make lambda use a variable e.g. (q => q.Name == Level.name), (q => q.Name == ViewFamilyType.name).
Thanks in advance!
Here is your code corrected:
public void GetViewProperties()
{
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
var VfamType = (ViewFamilyType)doc.GetElement(currentView.GetTypeId());
//Find the level that matches the active view
Level lev = currentView.GenLevel;
//Get the view's current name
string viewname = currentView.Name;
//Get the name of the view family type
string typename = VfamType.Name;
//Get the name of the level
string levelname = lev.Name;
//Combine results for task dialog
string Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test", Output);
}
You don't need to use a FilteredElementCollector to get these informations. And if you need elsewhere, you don't need a Where: just put your lambda in the First:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.First(q => q.Name == "1-0-Model")
If you need to access in your lambda a property specific to a class, not defined on Element, you can use Cast:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Cast<ViewFamilyType>().First(vft => vft.IsValidDefaultTemplate)
And please do not declare all your variable at the start of your methods. You're not writing Pascal. Declare variables as close to the first spot that you use them as possible. It makes your code much more readable. The closer a variable is declared to where it is used, the less scrolling/searching you have to do when reading the code later and it also naturally narrow their scope.
You're probably looking for View.GenLevel property. This will work for views related to levels, such as Plan Views. Note that if this View is not generated by a level, this property is null.
I have a requirement to sync some entities (account, lead, contact etc) to a database table outside of the crm database but on the same server. I am looking for a supported way for doing this. Here's what I have tried, that don't work:
I first created table in the outside database that matches the schema from dbo.account (view). Then I wrote post create, post update, post assign and post delete plugins to create, update or delete the record in the outside table (using ADO.Net). I have written the plugin in the most generic way so that it can be registered for any entity with minimum changes to the plugin (by not hardcoding the field names). Doing it this way, the problem I am running into is with the fields that are foreign key to other tables. Eg. in dbo.account, there are fields like PrimaryContactId and PrimaryContactIdName, PreferredSystemUserId and PreferredSystemUserIdName, ParentAccountId and ParentAccountIdName etc. In the input parameters for the plugin, the xxxxId fields are available when they are updated, but not the 'xxxxIdName' fields. Because of which I am not able to 'sync' the table as is.
Is there a solution to make my plugin solution work?
Is there a better supported way for having a sync table?
Thanks in advance,
PS: 1. The data sync has to be in real time
PS: 2. Here is my function to get the query that does the update
private static string PrepareUpdateQuery(ITracingService tracingService, IEnumerable<KeyValuePair<string, object>> attributeCollection, string entityName, string entityIdName)
{
var query = "Update MainDb.MSCRM." + entityName + " set ";
foreach (KeyValuePair<string, object> keyValuePair in attributeCollection)
{
tracingService.Trace("Key: {0}", keyValuePair.Key);
if (keyValuePair.Key != entityIdName && keyValuePair.Key != "modifiedonbehalfby")
{
query = query + keyValuePair.Key + " = ";
if (keyValuePair.Value == null)
query = query + "null, ";
else
{
var typeOfValue = keyValuePair.Value.GetType().Name;
tracingService.Trace("typeOfValue: {0}", typeOfValue);
switch (typeOfValue)
{
case "EntityReference":
query = query + "'" + ((EntityReference)keyValuePair.Value).Id + "', ";
break;
case "OptionSetValue":
query = query + ((OptionSetValue)keyValuePair.Value).Value + ", ";
break;
case "BooleanManagedProperty":
query = query + (((BooleanManagedProperty)keyValuePair.Value).Value ? "1" : "0") + ", ";
break;
default:
query = query + "'" + keyValuePair.Value + "', ";
break;
}
}
}
}
return query;
}
If all you're after is the name of the entity that is an attribute on your currently executing plugin, the EntityReference object has a Name property that should contain that name. If it doesn't you you can query CRM with the id and logical name to get any value that you're looking for on the referenced entity.
Edit 1
If you're just moving the data, why even bother setting the referenced name? I'd removed those names from your database table, and just create a view that looks up the corresponding entity's name. It's what CRM is doing. It also makes your other database more normalized. IE. If you update the name of an entity that is referenced by another entity, you will have to search for and update all of those names...
the xxxIdName fields are just a helper for the views really, you can easily figure out what they
should contain.
For example, say you have an account 'some company' with a primary contact called 'bob bobson'.
when processing the account entity the primarycontactId will be a guid and the primarycontactIdName will be 'bob bobson', the accountIdName will be 'some company'.
easiest way to do this in your plugin is to look up the related entity and get the value from there - 90% of the time it's just the name field.
you also need to consider however if you are doing the right thing in using the CRM schema, perhaps it would be better to copy only the fields you need and use your own schema for the sync table.
UPDATE: just saw your code, you are overwritting the value contained in query and not setting it back to the base query, so you will get odd results/errors on the second pass through the foreach
If you're dead set on putting the related entity name in the primary entity table you can always grab it like this:
var entityEntityRef = (EntityReference)keyValuePair.Value;
var relatedEntity = service.Retrieve(entityRef.LogicalName, entityRef.Id, new ColumnSet(true));
Now relatedEntity as all the attributes available. You'll mostly be looking for the Name field, but some entities are different, like contact which uses the full name field I believe.
You can, in fact, register a single plugin for all entities (checking, of course, that the one that's firing the message is in the list of treated ones).
IEnumerable<String> supportees = new String[]{ "account", "contact" };
if(!supportees.Any(element
=> element == targetLogicalName))
return;
As for the linked entities, you have three choices.
Just skip them. Not full data sync but easies to implement.
Store the guids only. Data sync is instance-wide - limited but moderately easy.
Get all the linked data. Full information but a recursive PIA to develop.
Hi I have 2 external lists 'A' and 'B'. with an associated column in A, that looks up for B.
When i view/edit item in browser, it shows correct values as shown in picture below.
But when i try to access the list in code, i can access all column values, but associated column value comes null.
The code look something like this :
items = listReports.GetItems();
System.Collections.Generic.List<ReportItem> reportItems = new List<ReportItem>();
foreach (SPListItem it in items)
{
if (it != null)
{
ReportItem item = new ReportItem();
// extItem comes null
var extItem = it["ExtCol"];
// extItem comes null
DateTime date;
if (DateTime.TryParse(it["GeneratedOn"].ToString(), out date))
{
item.dateGenerated = date.Date;
}
DateTime time;
if (DateTime.TryParse(it["GeneratedOn"].ToString(), out time))
{
item.timeGenerated = time.Date;
}
reportItems.Add(item);
}
}
I'm not sure, but, "ExtCol" - is that rigth name for field in your external type? For external items sharepoint may substitute a name of external item/column in the source field name.