i would like to Hide the column(except Inventory ID and Description) in the "Expense Item" Lookup screen on the Expense Receipt screen(EP301020) for all the users.
How can i set that some columns (which are not needed) in the "Available column" section in "Column Configuration" by default for all the users and only the required ones are available in the lookup screen.By default, all the columns are included in the selected column list in the Column Configuration.
Please advise.
Thanks
Below is code to hide the columns from Available and Selected list box. If all you need is to initialize the columns in Available and Selected list box consider using Acumatica Default Table Layout feature. Note that column configuration is a user configuration so you can initialize the columns but you can't override the user choices after initialization.
To completely remove the columns from the selector you need to redefine the InventoryID selector and explicitly declare the columns you want to see in the second parameter of PXSelector.
You can do so by creating a graph extension on ExpenseClaimDetailEntry and using CacheAttached method to redefine the selector:
using PX.Data;
using PX.Objects.IN;
namespace PX.Objects.EP
{
public class ExpenseClaimDetailEntry_Extension : PXGraphExtension<ExpenseClaimDetailEntry>
{
[PXMergeAttributes(Method = MergeMethod.Replace)]
[PXDefault]
[PXUIField(DisplayName = "Expense Item")]
[PXSelector(typeof(InventoryItem.inventoryID),
/* List of available/visible columns go here */
new Type[] { typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr) },
SubstituteKey = typeof(InventoryItem.inventoryCD),
DescriptionField = typeof(InventoryItem.descr))]
[PXRestrictor(typeof(Where<InventoryItem.itemType, Equal<INItemTypes.expenseItem>>), Messages.InventoryItemIsNotAnExpenseType)]
protected virtual void EPExpenseClaimDetails_InventoryID_CacheAttached(PXCache sender)
{
}
}
}
Related
I'm new to Acumatica, could you please help me? I have too screens IN202500 (stock items) and SO301000(sales orders). I added a field to stock items and now I need to show a value from that field in grid column of sale orders for each stock items. I suppose that I need to use PXDefault attribute for this?
There are a number of ways you can do this. I'll provide 3 possibilities.
If your View used by the grid contains InventoryItem, you may be able simply to select your custom field from InventoryItem and add it directly to the screen. I'll assume this is not an option or you likely would have found it already.
Create a custom field in a DAC extension on SOLine where you add your custom field as unbound (PXString, not PXDBString) and then use PXDBScalar or PXFormula to populate it. I haven't used PXDBScalar or PXFormula to retrieve a value from a DAC Extension, so I'll leave it to you to research. I do know this is super easy if you were pulling a value directly from InventoryItem, so worth doing the research.
Create as an unbound field as in #2, but populate it in the SOLine_RowSelecting event. This is similar to JvD's suggestion, but I'd go with RowSelecting because it is the point where the cache data is being built. RowSelected should be reserved, in general, for controlling the UI experience once the record is already in the cache. Keep in mind that this will require using a new PXConnectionScope, as Acuminator will advise and help you add. (Shown in example.) In a pinch, this is how I would do it if I don't have time to sort out the generally simpler solution provided as option 2.
Code for Option 3:
#region SOLine_RowSelecting
protected virtual void _(Events.RowSelecting<SOLine> e)
{
SOLine row = (SOLine)e.Row;
if (row == null)
{
return;
}
using (new PXConnectionScope())
{
SOLineExt rowExt = row.GetExtension<SOLineExt>();
InventoryItem item = SelectFrom<InventoryItem>
.Where<InventoryItem.inventoryID.IsEqual<#P.AsInt>>
.View.Select(Base, row.InventoryID);
InventoryItemExt itemExt = item.GetExtension<InventoryItemExt>();
rowExt.UsrSSMyDatAField = itemExt.UsrSSMyDataField;
}
}
#endregion
The field in the DAC is defined like this.
#region NextMonthHours
[PXDBDecimal(2, MinValue = 0.0, MaxValue = 280.0)]
[PXUIField(DisplayName = "Next Month Hours")]
[PXDefault(TypeCode.Decimal, "0.0")]
public virtual Decimal? NextMonthHours { get; set; }
public abstract class nextMonthHours : PX.Data.BQL.BqlDecimal.Field<nextMonthHours> { }
#endregion
I change the display name of the field in RowSelected event.
PXUIFieldAttribute.SetDisplayName<EVEPPlannedHoursDetails.nextMonthHours>(sender, nextMonth+"Hours");
where nextMonth is "February".
I need to add this field to Acumatica Mobile Screen. When I go to web service schema the field name is "FebruaryHours"
<s:element minOccurs="0" maxOccurs="1" name="FebruaryHours" type="tns:Field"/>
I cannot use the name "FebruaryHours" because it changes every month but I also when I use field name NextMonthHours it is not added in the mobile screen.
Any idea how to solve this issue?
Thanks
There's quite a few ways to workaround this depending on the use case and whether the label value is static or dynamic.
If all you want to do is to change a static label in UI without having to change the display name property you can add a separate label and merge group.
Here's an example to change Billable in UI without changing DisplayName property using that technique.
Set SuppressLabel property to true to hide the original label bounded to DisplayName on UI.
Use ADD CONTROLS tab to add a Layout Rule with Merge property set to true.
Use ADD CONTROLS tab to add a label control in the merged group.
Put the original field in the merge group so they show up together on the same line in UI.
End result, label is now a UI control and wouldn't interfere with DisplayName property.
I've got a PXSelector that selects several fields, the first one being a field whose value may be repeated (as follows):
Field1 Field2 Field3
1234 LL description1
1234 PS description2
1234 CC description3
4321 BB description4
PXSelector code:
[PXSelector(typeof(myTable.field1)
,typeof(myTable.field1)
,typeof(myTable.field2)
,typeof(myTable.field3)
,DescriptionField = typeof(myTable.field3))]
The DAC for the selected table has Field1 and Field2 as keys.
If I select row two or three above, I'll get the row one's Field3 description every time. Is there a way to ensure that I only get the description of the row that I've selected, instead of always getting the first occurrence?
You have to make the selector operate on a single key because the selected value is the key field not the whole DAC record.
One possible approach is to add a unique record number column to the database table, make the selector operate on that column and set a different 'TextField' property for the selector so it doesn't show the record number.
Here's how I did it to make a selector on SerialNumber/InventoryItem which is not a unique value (contains duplicate).
Database scripts will vary with database systems. I'm using this script for MSSQL to add the unique record number to the table:
--[mysql: Skip]
--[IfNotExists(Column = SOShipLineSplit.UsrUniqueID)]
BEGIN
alter table SOShipLineSplit add UsrUniqueID int identity(1,1)
END
GO
This is the DAC declaration. I use PXDBIdentity to match the DB identity field type that tag the column as a record number field:
[Serializable]
public class SOShipLineSplit_Extension : PXCacheExtension<SOShipLineSplit>
{
#region UsrUniqueID
public abstract class usrUniqueID : IBqlField { }
[PXDBIdentity(IsKey = false)]
[PXUIField(DisplayName = "ID")]
public virtual int? UsrUniqueID { get; set; }
#endregion
}
I use another DAC field for the selector which uses this unique id key and I set the description to the real field I want to appear in the selector:
#region SerialUniqueID
public abstract class serialUniqueID : IBqlField { }
[PXSelector(typeof(Search5<SOShipLineSplit_Extension.usrUniqueID,
InnerJoin<SOShipment, On<SOShipment.customerID, Equal<Current<customerID>>,
And<SOShipment.shipmentNbr, Equal<SOShipLineSplit.shipmentNbr>>>,
InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<SOShipLineSplit.inventoryID>>>>,
Where<SOShipLineSplit.lotSerialNbr, NotEqual<StringEmpty>>,
Aggregate<GroupBy<SOShipLineSplit.lotSerialNbr,
GroupBy<SOShipLineSplit.inventoryID>>>>),
typeof(SOShipLineSplit.lotSerialNbr),,
typeof(SOShipLineSplit.inventoryID),
typeof(InventoryItem.descr),
CacheGlobal = true,
DescriptionField = typeof(SOShipLineSplit.lotSerialNbr))]
[PXDBInt]
[PXUIField(DisplayName = "Serial Number")]
public virtual int? SerialUniqueID { get; set; }
#endregion
For this selector I want to display LotSerialNbr in the textbox instead of the unique id. Because the selector displays it's key by default I need to use the TextField property:
<px:PXSelector ID="edSerialUniqueID"
runat="server"
DataField="SerialUniqueID"
TextField="LotSerialNbr"
AutoGenerateColumns="True">
The selector value will contain the selected unique id field. To get the record you can issue another request to the database using that key.
Is there a way to refresh the weight that appear in the Totals tab of a Sales Order in Acumatica? if you create a Sales Order and add an item with Weight say 2KG and save it, the Totals tab will correctly display 2KG. But then I changed the weight in the Inventory Item section to 5KG. Is there a way to get the Sales Order to update that weight (apart from deleting the item and adding it back)?
Thanks,
G
Weight is stored at SO Line in the database and calculated automatically on selecting Inventory item.
Acumatica will automatically refresh default value when you update InvendoryItemID or UOM.
Not sure that this is best approach, but i can suggest 2 ways:
1) If you need it with non-programmatic way, you can use Export Scenarios to update UOM (and than change it back) for all Open/Hold orders.
2) Another is way with customization - create an action that will update wheigt.
You can click this action automatically with using the same import scenarios or GI mass actions.
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> recalculateWeight;
[PXUIField(DisplayName = "Recalculate Weight", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update, Visible = false)]
[PXButton(SpecialType = PXSpecialButtonType.Process)]
public virtual void RecalculateWeight()
{
foreach(SOLine line in Base.Transactions.Select())
{
Base.Transactions.Cache.SetDefaultExt<SOLine.unitWeigth>(line);
Base.Transactions.Update(line);
}
}
}
Current customization project I'm working on has the requirement of displaying / editing a grid with a "Sort Order" for records. The "SortOrder" field is read only with up/down buttons to allow the user to re-order the items in the grid.
The "SortOrder" column in the DAC is a simple Int field.
The PXSelect statement for the grid is using a OrderBy>> to display the records.
The Grid in the ASPX is a defined with "SyncPosition= true"
I've added an Up/Down button that increments/decrements the "SortOrder" value for the current selected record.
The issue that I'm running into is that the first time "Up" or "Down" is clicked, the "SortOrder" field is updated however the rows do not move. Once I click Save to persist the update, the grid then refreshes with the right order.
I've looked through the the rest of the code but all other situations where this is used is for treeviews, not grids.
I've tried adding a View.RequestRefresh() at the end of my Action but this doesn't cause the reorder.
What would be the best way without a Persist after each move to get the Grid to update and reflect the current order from the cache values? As usual I'm assuming I'm overlooking something simple.
Any advice would be appreciated.
I had a look at the generic inquiry designer source code - it has an up/down button in the grid to reorder the fields. The views don't have an OrderBy clause:
public PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>>> Parameters;
OrderBy is not necessary because the LineNbr field is a key field - system automatically orders the records by the key fields.
public abstract class lineNbr : IBqlField { }
[PXDBInt(IsKey = true)]
[PXDefault]
[PXLineNbr(typeof(GIDesign))]
[PXParent(typeof(Select<GIDesign,
Where<GIDesign.designID, Equal<Current<GIFilter.designID>>>>))]
public virtual int? LineNbr { get; set; }
The code for the button looks like this:
[PXButton(ImageKey = Sprite.Main.ArrowUp, Tooltip = ActionsMessages.ttipRowUp)]
[PXUIField(DisplayName = ActionsMessages.RowUp, MapEnableRights = PXCacheRights.Update)]
protected void moveUpFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter prev = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Less<Current<GIFilter.lineNbr>>>>, OrderBy<Desc<GIFilter.lineNbr>>>.Select(this);
if (prev != null)
this.SwapItems(this.Parameters.Cache, prev, this.Parameters.Current);
}
[PXButton(ImageKey = Sprite.Main.ArrowDown, Tooltip = ActionsMessages.ttipRowDown)]
[PXUIField(DisplayName = ActionsMessages.RowDown, MapEnableRights = PXCacheRights.Update)]
protected void moveDownFilter()
{
if (this.Parameters.Current == null)
return;
GIFilter next = PXSelect<GIFilter, Where<GIFilter.designID, Equal<Current<GIDesign.designID>>, And<GIFilter.lineNbr, Greater<Current<GIFilter.lineNbr>>>>, OrderBy<Asc<GIFilter.lineNbr>>>.Select(this);
if (next != null)
this.SwapItems(this.Parameters.Cache, next, this.Parameters.Current);
}
The SwapItems function is shared between all the move up / move down actions:
private void SwapItems(PXCache cache, object first, object second)
{
object temp = cache.CreateCopy(first);
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(first, field.Name, cache.GetValue(second, field.Name));
foreach (Type field in cache.BqlFields)
if (!cache.BqlKeys.Contains(field))
cache.SetValue(second, field.Name, cache.GetValue(temp, field.Name));
cache.Update(first);
cache.Update(second);
}
Finally, there's a bit of JavaScript code in the ASPX code - it may or may not be what you're missing to get the feature to work correctly; i'm not exactly sure what it's doing but would encourage you to open SM208000.aspx in an editor and look for commandResult. Also check out the CallbackCommands that are defined on the grids which support up/down - it may have something to do with it.