On Screen FA303000, tab ACTIONS -> Dispose.
I would like to hide the "Before disposal" drop down and define the "Depreciate" action by default. I tried to customize the attributes but without success.
Code :
public class FA_DisposeParams_Ext : PXCacheExtension<PX.Objects.FA.DisposeParams>
{
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXCustomizeBaseAttribute(typeof(PXUIFieldAttribute), "Visible", false)]
[PXCustomizeBaseAttribute(typeof(PXUIFieldAttribute), "Visibility", PXUIVisibility.Invisible)]
[PXCustomizeBaseAttribute(typeof(PXDefaultAttribute), "Constant", "D")]
public string ActionBeforeDisposal { get; set; }
}
You should try setting visibility on initialize in your graph extension. Something like the following:
public override void Initialize()
{
PXCache cache = Base.Packages.Cache;
PXUIFieldAttribute.SetVisible<SOPackageDetail.createdByID>(cache, null, false);
}
Related
I am currently trying to add the customer account class to the viewing area of the GL404000 screen with the by extending the GLTran DAC.
The AccountID, AccountCD and Account name are already available so I thought it would be an easy task.
In GLTranR, I saw where they were pulling in the account data:
public new abstract class referenceID : PX.Data.BQL.BqlInt.Field<referenceID> { }
[PXDBInt()]
[PXDimensionSelector("BIZACCT", typeof(Search<BAccountR.bAccountID>), typeof(BAccountR.acctCD), DescriptionField = typeof(BAccountR.acctName), DirtyRead = true)]
[PXUIField(DisplayName = CR.Messages.BAccountCD, Enabled = false, Visible = false)]
public override Int32? ReferenceID
{
get
{
return this._ReferenceID;
}
set
{
this._ReferenceID = value;
}
}
The line that I attempted to change to my need was the [PXDimensionSelector()] however I cannot get this to pull in the class data. Even when I dont change the code at all it will not populate the column.
public new abstract class usrBusinessAccountClass : PX.Data.BQL.BqlInt.Field<usrBusinessAccountClass> { }
protected Int32? _UsrBusinessAccountClass;
[PXDBInt()]
[PXDimensionSelector("BIZACCT", typeof(Search<BAccountR.bAccountID>), typeof(BAccountR.acctCD), DescriptionField = typeof(BAccountR.acctClass), DirtyRead = true)]
[PXUIField(DisplayName = "Business Account Class", Enabled = false, Visible = false)]
public virtual Int32? UsrBusinessAccountClass
{
get {return _UsrBusinessAccountClass;}
set{ _UsrBusinessAccountClass = value;} // set does work but value does not???
}
just for a test I changed the setter to:
set { _UsrBusinessAccountClass = 1234; }
And that populated the column with the value 1234, so that is why I think my issue is just with selecting the class.
I would show this but I need 10 rep to post images.
You are on the proper track. The following code shows how to retrieve additional data to display within the screen.
Non-Database backed field on the DAC you wish to display data on :
public sealed class GLTranRExtension : PXCacheExtension<GLTranR>
{
public abstract class usrClassID : BqlString.Field<usrClassID>
{
}
[PXString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXSelector(typeof(CRCustomerClass.cRCustomerClassID), DescriptionField = typeof(CRCustomerClass.description))]
[PXUIField(DisplayName = "Business Account Class")]
public string UsrClassID { get; set; }
}
Graph extension in which the extension field is then populated with data :
public class AccountByPeriodEnqExtension : PXGraphExtension<AccountByPeriodEnq>
{
protected virtual void __(Events.RowSelecting<GLTranR> e)
{
if(e.Row is GLTranR row)
{
GLTranRExtension rowExt = row.GetExtension<GLTranRExtension>();
using(new PXConnectionScope())
{
BAccount bAccount = PXSelectReadonly<BAccount, Where<BAccount.bAccountID, Equal<Required<BAccount.bAccountID>>>>.Select(this.Base, row.ReferenceID);
if(bAccount != null)
{
rowExt.UsrClassID = bAccount.ClassID;
}
}
}
}
}
You will also need to add within the GL404000 the UI elements for your new extension fields. The result will look as follows :
I have added custom fields in customization form using the customization form, steps i have added:
1) Go to shipment form and select Transaction Grid.
2) Select Add fields.
3) Select custome and Add 4 fields and save and publish.
4) Add all 4 fields and select used and save.
5) and publish again and all 4 columns are visible.
6) I have added a user fields name Aloow (PXDBBool), UsrPClocation(Location) and UsrPCwarehouse(Site),
and i set the below attribute in warehour and location.
7) But on SOSHipLine_InventotyID_FieldUpdate event, i am setting Allow, location, warehouse all 3 values, but values are not showing in Grid, what is the resoon?
#region UsrQCSiteID
[PXUIField(DisplayName = "PC Warehouse")]
[SiteAvail(typeof(SOShipLine.inventoryID), typeof(SOShipLine.subItemID))]
[PXUIRequired(typeof(Where<usrAllow, Equal<True>>))]
[PXUIEnabled(typeof(Where<usrAllow, Equal<True>>))]
[PXDefault()]
public virtual int? UsrPCSiteID {
get; set;
}
public abstract class usrPCSiteID:PX.Data.BQL.BqlInt.Field<usrPCSiteID> {
}
#endregion UsrPCSiteID
#region UsrPCLocationID
[PXUIField(DisplayName = "PC Location")]
[SOLocationAvail(typeof(SOShipLine.inventoryID), typeof(SOShipLine.subItemID), typeof(SOShipLineExt.usrPCSiteID), typeof(SOLine.tranType), typeof(SOShipLine.invtMult))]
[PXUIRequired(typeof(Where<usrQCRequired, Equal<True>>))]
[PXUIEnabled(typeof(Where<usrQCRequired, Equal<True>>))]
[PXDefault()]
public virtual int? UsrPCLocationID {
get; set;
}
public abstract class usrPCLocationID:PX.Data.BQL.BqlInt.Field<usrPCLocationID> {
}
#endregion UsrPCLocationID
#region Allow
[PXDBBool]
[PXUIField(DisplayName = "Allow")]
public virtual bool? UsrAllow {
get; set;
}
public abstract class usrAllow:PX.Data.BQL.BqlBool.Field<usrAllow> {
}
#endregion
IS SOShipLine allow updating custom values?
The steps that you described look correct.
I recreated this scenario locally:
1 - My DAC extension looks as follows:
public class SOShipLineExt : PXCacheExtension<PX.Objects.SO.SOShipLine>
{
#region UsrPCSiteID
[PXUIField(DisplayName = "PC Warehouse")]
[SiteAvail(typeof(SOShipLine.inventoryID), typeof(SOShipLine.subItemID))]
public virtual int? UsrPCSiteID {
get; set;
}
public abstract class usrPCSiteID:PX.Data.BQL.BqlInt.Field<usrPCSiteID> {
}
#endregion UsrPCSiteID
#region UsrPCLocationID
[PXUIField(DisplayName = "PC Location")]
[SOLocationAvail(typeof(SOShipLine.inventoryID), typeof(SOShipLine.subItemID), typeof(SOShipLineExt.usrPCSiteID), typeof(SOShipLine.tranType), typeof(SOShipLine.invtMult))]
public virtual int? UsrPCLocationID {
get; set;
}
public abstract class usrPCLocationID:PX.Data.BQL.BqlInt.Field<usrPCLocationID> {
}
#endregion UsrPCLocationID
#region Allow
[PXDBBool]
[PXUIField(DisplayName = "Allow")]
public virtual bool? UsrAllow {
get; set;
}
public abstract class usrAllow:PX.Data.BQL.BqlBool.Field<usrAllow> {
}
#endregion
}
Notes about the DAC Extension:
I removed references to PXUIEnabled and PXUIRequired. If you are looking to disable the Site and the Location based on the checkbox's value, I recommend you manage this logic in the RowSelected event. (it may be feasible with your approach but I have not used it before)
The PXDefault references were also removed, given that in most cases the Shipment is created directly from the Sales Order page. As you have it right now, the field is mandatory but no default value is being assigned which will cause an error. You have 2 options: 1) indicate the value in the PXDefault() attribute, or 2) Set the property PXPersistingCheck.Nothing.
Note that your SOLocationAvail attribute has an error in the 4th parameter. You should use typeof(SOShipLine.tranType) instead of typeof(SOLine.tranType). This was generating an error when the shipment was created from the SO.
2 - My FieldUpdated event looks as follows:
public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry>
{
protected virtual void SOShipLine_InventoryID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
if (e.Row == null) return;
SOShipLine line = (SOShipLine)e.Row;
SOShipLineExt lineExt = cache.GetExtension<SOShipLineExt>(line);
if (lineExt != null)
{
lineExt.UsrAllow = true;
lineExt.UsrPCSiteID=154;
lineExt.UsrPCLocationID=155;
}
}
}
Notes about the Graph extension:
I hard-coded the Warehouse and Location values. I recommend you do the same on your end while testing is being done, just make sure that the IDs exist in your DB, or that you query the CD value and then use its corresponding ID value.
Results:
When the Shipment is created from a SO, the values are being correctly assigned:
Good day
I am trying to set my Input mask at runtime according to the country that is selected.
I created the following DAC to save the Input mask:
namespace PX.Objects.CS
{
public class CountryExt : PXCacheExtension<PX.Objects.CS.Country>
{
#region UsrPhoneMask
[PXDBString(50)]
[PXUIField(DisplayName="Phone Mask")]
public virtual string UsrPhoneMask { get; set; }
public abstract class usrPhoneMask : PX.Data.BQL.BqlString.Field<usrPhoneMask> { }
#endregion
}
}
The part I am struggling with is when I Override the attribute on-screen level, this is the Phone1 field on the Customer screen:
namespace PX.Objects.AR
{
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
#region Event Handlers
[PXDBString(50, IsUnicode = true,
InputMask = Search<CountryExt.usrPhoneMask, Where<Country.countryID,Equal<Current<Country.countryID>>>>]
[PXUIField(DisplayName = "Phone 1", Visibility = PXUIVisibility.SelectorVisible)]
// [PhoneValidation()]
[PXMassMergableField]
[PXPersonalDataField]
protected virtual void Contact_Phone1_CacheAttached(PXCache cache){ }
}
}
I know it is the search I am implementing wrong just don't know how to fix it.
I have also tried setting it using a const
public const string Masknum = PXSelect<CountryExt.usrPhoneMask, Where<Country.countryID, Equal<Current<Country.countryID>>>>;
public class masknum : PX.Data.BQL.BqlString.Constant<masknum>
{
public masknum() : base(Masknum) {; }
}
PXSelect<CountryExt.usrPhoneMask, Where<Country.countryID,Equal<Current<Country.countryID>>>>;
[PXDBString(50, IsUnicode = true, InputMask = Masknum)]
[PXUIField(DisplayName = "Phone 1", Visibility = PXUIVisibility.SelectorVisible)]
// If you do not remove below attribute the mask will not be set
// [PhoneValidation()]
// [PXMassMergableField]
[PXPersonalDataField]
protected virtual void Contact_Phone1_CacheAttached(PXCache cache)
{
}
Property values declared in DAC attribute are not dynamic because they are required to be constant values by .NET Framework compilers. To change the mask at runtime you should investigate the ReturnState property of CacheAttached and FieldSelecting events.
From the Source Code page (SM204570) you can search for the OrderSiteSelectorAttribute which implements a dynamic mask using these methods.
Other examples shipped in the product source code might be more useful for your use case so you can also do a broad search for ReturnState. Here's the relevant code shown in the screenshot above:
public override void CacheAttached(PXCache sender)
{
base.CacheAttached(sender);
PXDimensionAttribute attr = new PXDimensionAttribute(SiteAttribute.DimensionName);
attr.CacheAttached(sender);
attr.FieldName = _FieldName;
PXFieldSelectingEventArgs e = new PXFieldSelectingEventArgs(null, null, true, false);
attr.FieldSelecting(sender, e);
_InputMask = ((PXSegmentedState)e.ReturnState).InputMask;
}
public override void SubstituteKeyFieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
base.SubstituteKeyFieldSelecting(sender, e);
if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered)
{
e.ReturnState = PXStringState.CreateInstance(e.ReturnState, null, null, null, null, null, _InputMask, null, null, null, null);
}
}
EDIT:
From the matching DAC RowSelected event, try calling the SetInputMask method.
If that is producing the expected results it is the most straightforward way to achieve dynamic input mask:
PXStringAttribute.SetInputMask<DAC.field>(sender, inputMaskValue);
I need to build a processing screen for customer locations that determines and then updates the residential flag on locations.
This code correctly processes each selected record and appears to update the appropriate fields. But the problem I am encountering is that my changes to Location are not being saved back to the database.
The Customer Locations graph requires the business account to be specified before you can enter a Location ID, and I suspect that because of that I cannot simply update the Locations view on the graph. But I cannot find any documentation or code examples indicating what approach I should use here.
Here is the code on my processing screen graph:
public class ProcessCustomerLocations : PXGraph<ProcessCustomerLocations>
{
public PXCancel<Location> Cancel;
public PXProcessing<Location, Where<Location.isActive, Equal<True>>> Locations;
public static void Process(List<Location> locations)
{
var graph = PXGraph.CreateInstance<CustomerLocationMaint>();
CustomerLocationMaint_Extension graphExt = graph.GetExtension<CustomerLocationMaint_Extension>();
foreach (var location in locations)
{
graphExt.UpdateLocation(location, true);
}
}
public ProcessCustomerLocations()
{
Locations.SetProcessDelegate(Process);
}
}
And here is my code on the CustomerLocationMaint_Extension graph:
public class CustomerLocationMaint_Extension : PXGraphExtension<CustomerLocationMaint>
{
public void UpdateLocation(Location location, bool isMassProcess = false)
{
bool isRes = false;
Base.Location.Current = Base.Location.Search<Location.locationID>(location.LocationID, location.BAccountID);
LocationExt locationExt = location.GetExtension<LocationExt>();
// INSERT CODE TO DETERMINE VALUE OF isRes
locationExt.UsrResidentialValidated = true;
location.CResedential = isRes;
Base.Location.Update(location);
Base.Actions.PressSave();
}
}
One of the fields I am updating on Location is a custom field called UsrResidentialValidated. Here is the code for that field.
namespace PX.Objects.CR
{
public class LocationExt : PXCacheExtension<PX.Objects.CR.Location>
{
#region UsrResidentialValidated
[PXDBBool]
[PXUIField(DisplayName="Residential Validated")]
public virtual bool? UsrResidentialValidated { get; set; }
public abstract class usrResidentialValidated : IBqlField { }
#endregion
}
}
Update
Thanks to some help from #Samvel I've modified the UpdateLocation code as follows. The following code does save the changes to the database (both on the custom field and the non-custom field), which is great. However, in order to do that, I had to create a new Location object "myLocation" and am no longer using the "location" object that the PXProcessing graph passed to UpdateLocation. This means that after processing, when the processing screen displays the processed records with the modified data (after processing finishes and before you refresh the screen), it does not show the updated values. Is there any way to both have the processing screen show the updated values and save the changes to the database?
public void UpdateLocation(PX.Objects.CR.Location location, bool isMassProcess = false)
{
bool isRes = true;
Location myLocation = PXSelect<Location,
Where<Location.bAccountID, Equal<Required<Location.bAccountID>>, And<Location.locationID, Equal<Required<Location.locationID>>>>>
.Select(this.Base, location.BAccountID, location.LocationID);
this.Base.Location.Current = myLocation;
LocationExt locationExt = myLocation.GetExtension<LocationExt>();
locationExt.UsrResidentialValidated = true;
myLocation.CResedential = isRes;
Base.Location.Current = Base.Location.Update(myLocation);
this.Base.Save.Press();
}
UPDATED
I have updated the code to correspond to your case. After processing all the records the records in the grid are being updated and showing modified records.
You can download the customization package for this code by this link
To create a processing page for updating Location you should do the following steps:
Add "Selected" field to the Location DAC
public sealed class LocationExt: PXCacheExtension<Location>
{
#region Selected
public abstract class selected : IBqlField
{ }
[PXBool()]
[PXDefault(true,PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Selected")]
public bool? Selected { get; set; }
#endregion
#region UsrResidentialValidated
[PXDBBool]
[PXUIField(DisplayName = "Residential Validated")]
public bool? UsrResidentialValidated { get; set; }
public abstract class usrResidentialValidated : IBqlField { }
#endregion
}
This step is required because otherwise your delegate for SetProcessDelegate will never be called.
Acumatica is checking if there is at least one selected record before calling Process Delegate.
Create the Processing Graph like below:
using PX.Data;
using PX.Objects.CR;
using System.Collections.Generic;
namespace CustomerLocationUpdate
{
public class ProcessCustomerLocations : PXGraph<ProcessCustomerLocations>
{
public PXCancel<Location> Cancel;
public PXProcessingJoin<Location,InnerJoin<BAccountR,On<Location.bAccountID,Equal<BAccountR.bAccountID>>>,
Where<Location.isActive, Equal<True>,And<Location.locType, Equal<PX.Objects.CR.LocTypeList.customerLoc>>>> Locations;
public static void Process(List<Location> locations)
{
var graph = PXGraph.CreateInstance<PX.Objects.AR.CustomerLocationMaint>();
CustomerLocationMaint_Extension graphExt = graph.GetExtension<CustomerLocationMaint_Extension>();
foreach (var location in locations)
{
graphExt.UpdateLocation(location, true);
graph.Clear();
}
}
public ProcessCustomerLocations()
{
Locations.SetProcessDelegate(Process);
}
}
}
As you can see I have implicitly specified PX.Objects.AR and PX.Objects.CR for some reason the program has worked only this way on my instance.
Create the UpdateLocation method in the GraphExtension:
using PX.Data;
namespace CustomerLocationUpdate
{
public class CustomerLocationMaint_Extension : PXGraphExtension<PX.Objects.AR.CustomerLocationMaint>
{
public void UpdateLocation(PX.Objects.CR.Location location, bool isMassProcess = false)
{
bool isRes = false;
this.Base.Location.Current = PXSelect<PX.Objects.CR.Location,Where<PX.Objects.CR.Location.bAccountID,Equal<Required<PX.Objects.CR.Location.bAccountID>>,And<PX.Objects.CR.Location.locationID,Equal<Required<PX.Objects.CR.Location.locationID>>>>>.Select(this.Base,location.BAccountID,location.LocationID);
this.Base.Location.Current.CResedential = isRes;
LocationExt locationExt = PXCache<PX.Objects.CR.Location>.GetExtension<LocationExt>(this.Base.Location.Current);
locationExt.UsrResidentialValidated = false;
this.Base.Location.Current = this.Base.Location.Update(this.Base.Location.Current);
this.Base.Save.Press();
}
}
}
As you can see I am setting the Location.Current using PXSelect and not Location.Current.Search.
For some reason Location.Current.Search is always returning null.
May be it is caused by the PXProjectionAttribute applied to it, I am not sure what is the exact reason.
I want to show which user created Invoice, for this i have added default Acumatica field, but the label is showing as Created By, how can i change that label name to "Invoice Created By". Please have a look at below screenshot for field am referring to.
You could use PXUIFieldAttribute.SetDisplayName static method to change DAC field’s display name. This change will be applicable only for Sales Invoice Entry Graph (SO303000 screen)
public class SOInvoiceEntryPXDemoExt : PXGraphExtension<SOInvoiceEntry>
{
public override void Initialize()
{
PXUIFieldAttribute.SetDisplayName<ARInvoice.createdByID>(Base.Document.Cache, "Invoice Created By");
}
}
If you need display name changed for this field in all screens, you need to have DAC Extension as below:
With this, attributes specified in an extension DAC apply to DAC class in every Graph of the application unless a Graph replaces them with other attributes.
public class ARInvoicePXDemoExt : PXCacheExtension<ARInvoice>
{
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXUIField(DisplayName = "Invoice Created By", Enabled = false, IsReadOnly = true)]
public virtual Guid? CreatedByID { get; set; }
}
You need to add CreatedByID field on screen SO303000
And set DisplayMode property to Text.
The 'CreatedByID' I believe is an audit field and therefore cannot easily change the ui of the additional data fields available through the control. The resolution I would suggest is a non database backed UI field that is populated during row selecting. Example can be found below :
public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry>
{
public virtual void SOOrder_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
SOOrder row = e.Row as SOOrder;
if(row != null)
{
SOOrderExtension rowExt = PXCache<SOOrder>.GetExtension<SOOrderExtension>(row);
Users user = PXSelectReadonly<Users, Where<Users.pKID, Equal<Required<Users.pKID>>>>.Select(this.Base, row.CreatedByID);
if(user != null)
{
rowExt.InvoiceCreatedBy = user.DisplayName;
}
}
}
}
public class SOOrderExtension : PXCacheExtension<SOOrder>
{
public abstract class invoiceCreatedBy : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName = "Invoice Created By")]
public virtual string InvoiceCreatedBy { get; set; }
}