Show unbound field with PXFormula - acumatica

I'm trying to show a value calculated with PXFormula but the field doesn't show the value.
I have my CustomDAC named EDITran
public class EDITran : IBqlTable
{
#region Doctype
[PXDBString(50, IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Doctype")]
[PXStringList
(new string[]
{"SO", "SHI", "INV" },
new string[]
{"Sales Order", "Shipment", "Invoice"}
)]
public virtual string Doctype { get; set; }
public abstract class doctype : PX.Data.BQL.BqlString.Field<doctype> { }
#endregion
#region Erprefnbr
[PXDBString(30, IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "ERP RefNbr")]
public virtual string Erprefnbr { get; set; }
public abstract class erprefnbr : PX.Data.BQL.BqlString.Field<erprefnbr> { }
#endregion
#region Sync
[PXDBBool()]
[PXUIField(DisplayName = "Sync")]
public virtual bool? Sync { get; set; }
public abstract class sync : PX.Data.BQL.BqlBool.Field<sync> { }
#endregion
}
}
so I what to show the value of sync field on the Sales Order screen. The key is the ERP RefNbr (which would be SOOrder.OrderNbr)
I have added the custom non persisted field on the SOOrderExt DAC with this attributes
using PX.Objects.SO;
[PXBool]
[PXUIField(DisplayName="EDI Sync" , Enabled = false)]
[PXFormula(typeof(Selector<SOOrder.orderNbr,
Selector<EDITran.erprefnbr,
EDITran.sync>>))]
But when I added a record in EDITran and try to visualize it in SOOrder Form and I checked that EDITran.Sync = 1, it doesn't show the saved value.
Sales Order Screen
What I'm I doing wrong? Is the PXFormula correctly used?

PXFormula used incorrectly. PXFormula attribute works only with current DAC (which is SOOrder) or foreign DAC, existing in PXSelector join condition (you can use Selector keyword to get it).
For example, here is the SOOrder.OrderNbr selector declaration
[SO.RefNbr(typeof(Search2<SOOrder.orderNbr,
LeftJoinSingleTable<Customer, On<SOOrder.customerID, Equal<Customer.bAccountID>,
And<Where<Match<Customer, Current<AccessInfo.userName>>>>>>,
Where<SOOrder.orderType, Equal<Optional<SOOrder.orderType>>,
And<Where<Customer.bAccountID, IsNotNull,
Or<Exists<Select<SOOrderType,
Where<SOOrderType.orderType, Equal<SOOrder.orderType>,
And<SOOrderType.aRDocType, Equal<ARDocType.noUpdate>,
And<SOOrderType.behavior, Equal<SOBehavior.sO>>>>>>>>>>,
OrderBy<Desc<SOOrder.orderNbr>>>), Filterable = true)]
public virtual String OrderNbr
you can get some fields from the related Customer record, using Selector keyword
[PXFormula(typeof(Selector<
SOOrder.orderNbr,
Customer.consolidateStatements>))]
As a result, there are two possible solutions:
1) Rewrite SOOrder.OrderNbr selector declaration with your EDITran DAC
...
[SO.RefNbr(typeof(Search2<SOOrder.orderNbr,
LeftJoinSingleTable<Customer, On<SOOrder.customerID, Equal<Customer.bAccountID>,
And<Where<Match<Customer, Current<AccessInfo.userName>>>>>,
LeftJoin<EDITran, On<EDITran.doctype, Equal<SOOrder.orderType>,
And<EDITran.erprefnbr, Equal<SOOrder.orderNbr>>>>>,
Where<SOOrder.orderType, Equal<Optional<SOOrder.orderType>>,
And<Where<Customer.bAccountID, IsNotNull,
Or<Exists<Select<SOOrderType,
Where<SOOrderType.orderType, Equal<SOOrder.orderType>,
And<SOOrderType.aRDocType, Equal<ARDocType.noUpdate>,
And<SOOrderType.behavior, Equal<SOBehavior.sO>>>>>>>>>>,
OrderBy<Desc<SOOrder.orderNbr>>>), Filterable = true)]
public virtual String OrderNbr
Then it will be possible to get you field from there
[PXFormula(typeof(Selector<
SOOrder.orderNbr,
EDITran.sync>))]
2) Use PXDBScalar attribute to just get what you need. Note this will be a separate request to the database!!
...
[PXBool]
[PXDBScalar(typeof(Search<EDITran.sync,
Where<EDITran.doctype, Equal<SOOrder.orderType>,
And<EDITran.erprefnbr, Equal<SOOrder.orderNbr>>>>))]
public virtual bool? Sync

I'm not sure that's the right use of PXFormula. Here's the reference I normally use when I need to use PXFormula. See if it helps you simplify your PXFormula. You may need to simplify to a single Selector and define a foreign key to be able to complete your lookup.
Also, are you sure that the database contains the values that you expect? I often find that my problem is not where I think, and your issue may lie in setting the values or writing them to the database in your business logic.
It appears that you need the PXFormula to look to your EDITran table to find the record identified by the key (1st field specified in Selector) to then return back the value of the second field specified, which would reside in EDITran.
From Acumatica Developers Blog (AsiaBlog):
Selector
Selector does following:
Fetches a PXSelectorAttribute defined on the foreign key field (KeyField) of the current DAC.
Fetches the foreign data record currently referenced by the selector.
Using calculates and returns an expression on that data record as defined by ForeignOperand.
public class APVendorPrice : IBqlTable
{
// Inventory attribute is an aggregate containing a PXSelectorAttribute
// inside, which is also valid for Selector<>.
[Inventory(DisplayName = "Inventory ID")]
public virtual int? InventoryID
[PXFormula(typeof(Selector<
APVendorPrice.inventoryID,
InventoryItem.purchaseUnit>))]
public virtual string UOM { get; set; }
}
From that, I'd expect your PXFormula to look more like:
[PXFormula(typeof(Selector<
SOOrder.orderNbr,
EDITran.sync>))]
... assuming you have enough defined to tell Acumatica how to relate SOOrder.orderNbr to EDITran.erprefnbr.

Related

Acumatica how to make a Custom Branch field not mandatory or be able to select branch from a lookup

I'm facing the following issue:
I have created a user field "UsrBranch".
Using the Branch lookup from the SO Financial Settings, I'm able to select and save into my new "UsrBranch" field, but it makes the field mandatory, even when I set the field as "Required = false".
Example 1:
[PXUIField(DisplayName = "Branch")]
[Branch(typeof(Coalesce<Search<Location.cBranchID,
Where<Location.bAccountID, Equal<Current<SOOrder.customerID>>,
And<Location.locationID, Equal<Current<SOOrder.customerLocationID>>>>>,
Search<Branch.branchID, Where<Branch.branchID,
Equal<Current<AccessInfo.branchID>>>>>), Required = false)]
public virtual Int32? UsrBranch { get; set; }
public abstract class usrBranch : PX.Data.BQL.BqlString.Field<usrBranch> { }
If I change the lookup to a PXSelector it displays the Branches, but I'm not able to select any.
Example 2:
[PXUIField(DisplayName = "Branch")]
[PXSelector(typeof(Branch.branchID),
SubstituteKey = typeof(Branch.branchCD))]
public virtual Int32? UsrBranch { get; set; }
public abstract class usrBranch : PX.Data.BQL.BqlString.Field<usrBranch> { }
Any Help will be gratefully appreciated.
The "Required" Property controls whether or not the star will show up on the field in the UI. The "PersistingCheck" property on the PXDefaultAttribute controls whether or not a value is mandatory.
The constructor of the BranchAttribute adds a PXDefaultAttribute to the field, and the default value for the "PersistingCheck" property is PXPersistingCheck.Null. This disallows the field from being null when saved. Since BranchAttribute derives from the AcctSubAttribute/PXAggregateAttribute you can set the "PersistingCheck" property on the BranchAttribute and it will set the "PersistingCheck" property on the added PXDefaultAttribute. To make it so the field can be null, set the "PersistingCheck" property to PXPersistingCheck.Nothing on the BranchAttribute:
[Branch(typeof(Coalesce<
Search<Location.cBranchID,
Where<Location.bAccountID, Equal<Current<SOOrder.customerID>>,
And<Location.locationID, Equal<Current<SOOrder.customerLocationID>>>>>,
Search<Branch.branchID,
Where<Branch.branchID, Equal<Current<AccessInfo.branchID>>>>>), PersistingCheck = PXPersistingCheck.Nothing)]
public virtual int? UsrBranch { get; set; }
Additionally, there is a PXUIFieldAttribute on the BranchAttribute so adding another one onto the field is redundant.

Acumatica : Switching Selector Attribute on the same DAC Field

Good Day!
I have a field in my DAC where I need to change the Selector attribute depending on the setup that I set on my preferences. As you may know, there is an existing LeadSelector Attribute and CustomerSelector Attribute on acumatica. I wish to change the selector attribute of that given field if I set Customer on my preferences, and vice versa.
Is there any available resources right here now?
I've been thinking of creating an Extended Selector attribute on which I will check what is the preference setup then inherit the LeadSelector or CustomerSelector on the Extended Selector. But I think it might not be possible.
The other thing that I've been thinking, is to add both selectors on the attribute and remove them from the graph level whenever which preference is set up.
I'm also thinking of creating 2 selectors, on which I will hide the other depending on the preference setup. But the problem is, the selector is being used not only on one page, and it's a hassle if I create 2 selectors just to solve that issue. And also in the future it might not just lead and customer selectors.
I hope you can help me, I'm out of ideas. Thank you so much.
UPDATE 09-24-2019
I created a custom selector attribute for Lead and Customer Selector attributes. And it's working just I want it to be, but now my problem is, the description field won't show on the text box or on that field, also, there are error such as 'Investor name cannot be found in the system'.
Investor Selector Attribute
public class InvestorSelectorAttribute : PXCustomSelectorAttribute
{
public InvestorSelectorAttribute() : base(typeof(REInvestor.accountID))
{
DescriptionField = typeof(REInvestor.acctName);
SubstituteKey = typeof(REInvestor.acctName);
}
protected IEnumerable GetRecords()
{
var leads = new PXSelect<Contact,
Where<Contact.contactType, Equal<ContactTypesAttribute.lead>,
Or<Where<Contact.contactType, Equal<ContactTypesAttribute.person>,
And<Contact.status, Equal<LeadStatusesAttribute.converted>>>>>>(this._Graph);
var contacts = new PXSelect<BAccountR>(this._Graph);
REFeature setup = PXSelect<REFeature>.Select(this._Graph);
if (setup.InvestorType == InvestorTypesAttribute.LeadVal)
{
foreach (Contact lead in leads.Select())
{
yield return new REInvestor { AccountID = lead.ContactID, AcctName = lead.DisplayName };
}
}
else
{
foreach (BAccountR contact in contacts.Select())
{
yield return new REInvestor { AccountID = contact.BAccountID, AcctName = contact.AcctName, AcctCD = contact.AcctCD };
}
}
}
}
Unbound REInvestor DAC
[Serializable]
[PXCacheName("Investor")]
public class REInvestor : IBqlTable
{
public abstract class accountID : BqlInt.Field<accountID> { }
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AccountID, Visibility = PXUIVisibility.SelectorVisible)]
public virtual int? AccountID { get; set; }
public abstract class acctName : BqlString.Field<acctName> { }
[PXDBString(128, InputMask = "", IsUnicode = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AccountName, Visibility = PXUIVisibility.SelectorVisible)]
public virtual string AcctName { get; set; }
public abstract class acctCD : BqlString.Field<acctCD> { }
[PXDBString(128, InputMask = "", IsUnicode = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AcctCD, Visibility = PXUIVisibility.SelectorVisible)]
public virtual string AcctCD { get; set; }
}
** DAC Integration **
[PXDBInt]
[PXUIField(DisplayName = REMessages.DisplayNames.InvestorsName, Required = true)]
[InvestorSelector()]
[PXDefault(PersistingCheck = PXPersistingCheck.NullOrBlank)]
public virtual int? ContactID { get; set; }
I really need your help and suggestions. Thank you so much.
Is ok to have 2 fields defined and one displayed.
Each one with his own selector, and PXUIField Description.
You set visibility for one of them base on the setup field at graph level (RowSelected event)
In case you need to merge them on persist (persist both fields in one bound field), you can simply use 2 unbound fields for collecting/displaying data.
on updating/persisting event, you can update the values from unbound fields to unique database field
on retrieve you can make an Data View - Delegate to populate the unbound fields base on configuration;
When you plan to move on multiple pages (graphs) you move the code from the graph to an PXEventSubscriberAttribute
You add the new attribute at the DAC level.
This way you have access on all Graph events that may need (persisting, selecting, updating, ...). All the code stays in one place.
For multiple DACs you still need to create the fields; add them the new attribute.

How to change the Display Name for one of the PXSelector fields

I need to change the Display Name to "Primary Vendor" for a BAccount.acctName field, which is the last field to display in the PXSelector that I have created.
I have tried creating an field Extension which does the trick, but this option also renames the field for another inquiry page, therefore I cannot use it.
The following is my code:
Selector
[PXNonInstantiatedExtension]
public class SO_SOLine_ExistingColumn :
PXCacheExtension<PX.Objects.SO.SOLine>
{
#region InventoryID
[PXMergeAttributes(Method =
MergeMethod.Append)]
[PXSelector(typeof(Search2<InventoryItem.inventoryCD,
LeftJoin<BAccount, On<BAccount.bAccountID,
Equal<InventoryItem.preferredVendorID>>>,
Where<InventoryItem.descr, IsNotNull>>),
typeof(InventoryItem.inventoryID),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(InventoryItem.postClassID),
typeof(InventoryItem.itemStatus),
typeof(InventoryItem.itemType),
typeof(InventoryItem.baseUnit),
typeof(InventoryItem.salesUnit),
typeof(InventoryItem.purchaseUnit),
typeof(InventoryItem.basePrice),
typeof(BAccount.acctName), ValidateValue = false) ]
public int? InventoryID { get; set; }
#endregion
}
Field Extension
public class BAccountExt : PXCacheExtension<PX.Objects.CR.BAccount>
{
#region UsrCustomField
[PXDBString(250, IsUnicode = true, BqlField =
typeof(BAccountR.acctName))]
[PXUIField(DisplayName = "Primary Vendor")]
public virtual string AcctName { get; set; }
public abstract class acctName : IBqlField
{
}
#endregion
}
As you found out the cache extension modifications applies to all screens who use that DAC. There is another extension mechanism applied on a per graph basis called CacheAttached that is applied after the cache extension.
To use it first you need to identify the graph of the screen you want to customize and the DAC field you want to modify. You can use the inspect element feature for this. In this example the graph for Customers screen is 'CustomerMaint' and the DAC field is 'Customer.acctName':
Once you have that information you can create an extension for that graph and extend the DAC field inside it. DAC field extensions defined in the graph using the CacheAttached method will only apply to screens who uses that graph:
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXUIField(DisplayName = "Display Name For Customers Graph")]
public virtual void Customer_AcctName_CacheAttached(PXCache sender)
{
}
}
The prototype convention for CacheAttached extensions is:
void DAC_DACField_CacheAttached(PXCache sender) { }
You change DAC and DACField to the field you are targeting. Method definition (body) should remain empty. The attributes decorating the CacheAttached method will apply to the field you're customizing. With attribute PXMerge you can tweak how the CacheAttached extension is applied, it allows to merge the field new attributes of the extension with the base one or completely replace the base attributes.
For more details look at this blog post:
http://asiablog.acumatica.com/2017/01/append-and-replace-of-dacs-attributes.html
you can also try like below but this is limited to the specific graph.
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
public override void Initialize()
{
PXUIFieldAttribute.SetDisplayName<Customer.acctName>(Base.BAccount.Cache, "Primary Vendor");
}
}

Acumatica Numbering Sequence - cant default to the new symbol

I've got a custom table storing serviceable components within fixed assets. The list is accessed using a grid on the AssetMaint screen.
I've set up an ID field to populate using a numbering sequence. I'm unsure how to set the default so this field populates with "<NEW>" by default, which is then updated by the numbering sequence when the component is saved.
It's sort of working, but there are a few issues. When I click the add button, the ID field is blank, but when I click add again, it populates with the "<NEW>" symbol. However if I add a third record before hitting save, the second record does not populate with "<NEW>". The second record is also not saved, unless I manually put "<NEW>" in the ID field.
This is part of the DAC for the component table:
[Serializable]
public class FAServiceComponent : IBqlTable
{
#region AssetID
public abstract class assetID : IBqlField { }
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(FixedAsset.assetID), DefaultForUpdate = false)]
[PXParent(typeof(Select<FixedAsset, Where<FixedAsset.assetID, Equal<Current<FAServiceComponent.assetID>>>>))]
[PXUIField(DisplayName = "Asset ID", Visible = false, Enabled = false)]
public virtual int? AssetID { get; set; }
#endregion
#region serviceComponentID
public abstract class serviceComponentID : IBqlField { }
[PXDBString(30, IsKey = true, IsUnicode = true)]
[PXUIField(DisplayName = "Component ID")]
[PXDefault(typeof(Search2<Numbering.newSymbol,
InnerJoin<FixedAsset, On<FixedAssetExt.usrServiceComponentNumberingSeq, Equal<Numbering.numberingID>, And<FixedAsset.assetID, Equal<Current<FixedAsset.classID>>>>>>))]
[Numbering]
public virtual string ServiceComponentID { get; set; }
#endregion
#region serviceComponentDescription
public abstract class description : IBqlField { }
[PXDBString(255)]
[PXUIField(DisplayName = "Description")]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
public virtual string Description { get; set; }
#endregion
public class NumberingAttribute : AutoNumberAttribute
{
public NumberingAttribute():
base(typeof(Search<FixedAssetExt.usrServiceComponentNumberingSeq, Where<FixedAsset.assetID, Equal<Current<FixedAsset.classID>>>>),
typeof(AccessInfo.businessDate)) {; }
}
}
Check out T200 course available at Acumatica Open University. Part 4 Lesson 8 show you how to use the AutoNumberAttribute.
I would first suggest to remove the following code, as it is not required to make the < New > symbol work.
[PXDefault(typeof(Search2<Numbering.newSymbol,
InnerJoin<FixedAsset, On<FixedAssetExt.usrServiceComponentNumberingSeq, Equal<Numbering.numberingID>, And<FixedAsset.assetID, Equal<Current<FixedAsset.classID>>>>>>))]
You should have a setup screen where you choose the numbering sequence you want to use. As an example, the Sales Orders Preferences screen (SO101000) has the Shipment Numbering Sequence field, bound to SOSetup.ShipmentNumberingID.
In your graph, make sure you have the setup data view, something like public PXSetup<Setup> AutoNumSetup;. The PXSetup DAC should match with your setup screen, e.g. PXSetup<SOSetup> ShipmentSetup
In your DAC, the numbered field should have the AutoNumberAttribute referencing the setup table. e.g.
[AutoNumber(typeof(SOSetup.ShipmentNumberingID), typeof(SOShipment.shipDate))]
In Numbering Sequence screen (CS201010), make sure that Manual Numbering is unchecked and that you have a New Number Symbol set for the numbering sequence you are using.

How to default an employeeid field to the current logged in user

I have a custom inquiry screen which uses an employeeid as the header filter field. What I'd like to do is default that employeeid field to the current logged in user, if possible. I've tried the following, but both give me a cast error (version 5.3.2562):
1.) [PXDBDefault(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.bAccountID, Equal<Current<AccessInfo.userID>>>>))]
2.) [PXDBDefault(typeof(AccessInfo.userID))]
Here's the entire DAC code for the filter field:
#region EmployeeID
public abstract class employeeID : IBqlField
{
}
[PXInt]
[PXDBDefault(typeof(AccessInfo.userID))]
[PXUIField(DisplayName = "Employee ID")]
[PXSelector(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.status, Equal<SetupTypes.active>>>),
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName),
SubstituteKey = typeof(EPEmployee.acctCD),
DescriptionField = typeof(EPEmployee.acctName))]
public virtual int? EmployeeID { get; set; }
#endregion
What's the correct way to obtain this?
Update 6/30/2017:
After implementing the solution (using PXDefault instead of PXDBDefault) as follows:
[PXDefault(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.bAccountID, Equal<Current<AccessInfo.userID>>>>))]
I get the following error:
We have a non filter field in a transaction able that defaults to the current employee.
This should work:
[PXDefault(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.bAccountID, Equal<Current<AccessInfo.userID>>>>))]
Note that you do not want to use PXDBDefault for default values unless they are values coming from a parent DAC when linked with PXParent. Use PXDefault for defaults.
If this does not work in your filter, try PXUnboundDefault in place of PXDefault.
Edit 6/30/2017: new error might be on your selector. There is an employee selector already available. Remove your PXSelector and use [PXEPEmployeeSelector] on your field and see if this solves your error between uniqueidentifier and int
AccessInfo.userID seems to be a GUID. In order to avoid the casting error you need to relate AccessInfo.userID to EPEmployee.userID while searching for EPEmployee.bAccountID. Final code should look like this:
#region EmployeeID
public abstract class usrEmployeeID : IBqlField
{
}
[PXEPEmployeeSelector]
[PXUnboundDefault(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.userID, Equal<Current<AccessInfo.userID>>>>))]
[PXUIField(DisplayName = "Employee ID")]
public virtual int? UsrEmployeeID { get; set; }
#endregion

Resources