Contact lookup based on Customer selection - acumatica

I need to create a Contact lookup in SO screen (SO301000). I have already created user defined custom field as below. I is listing all contacts but it is not refreshing based on when select customer. Do I have to write any event for CustomerID to refresh these Contact lookup? Does anyone has any idea?
[PXDBInt]
[PXUIField(DisplayName = "Contact")]
[PXSelector(typeof(Search2<Contact.contactID,
LeftJoin<BAccount, On<BAccount.bAccountID, Equal<Contact.bAccountID>>>>),
DescriptionField = typeof(Contact.displayName), Filterable = true, DirtyRead = true)]
[PXRestrictor(typeof(Where<Contact.isActive, Equal<True>>), PX.Objects.CR.Messages.ContactInactive, typeof(Contact.displayName))]
[PXDBChildIdentity(typeof(Contact.contactID))]
public virtual int? UsrCustContactID { get; set; }
public abstract class usrCustContactID : IBqlField { }

You are missing Where Clause and you need to use BAccount2 instead of BAccount. SOOrderEntry Graph has data view defined with Vendor DAC which gets initialized first/before BAccount and framework will substitute it with Vendor DAC. To prevent this, you need to use BAccount2 DAC in your BQL.
using System;
using PX.Data;
using PX.Objects.SO;
using PX.Objects.CR;
namespace DemoPkg
{
public class SOOrderPXExt : PXCacheExtension<SOOrder>
{
#region UsrContactID
public abstract class usrContactID : IBqlField { }
[PXDBInt()]
[PXUIField(DisplayName = "Contact", Visibility = PXUIVisibility.Visible)]
[PXSelector(typeof(Search2<Contact.contactID,
LeftJoin<BAccount2, On<BAccount2.bAccountID, Equal<Contact.bAccountID>>>>),
DescriptionField = typeof(Contact.displayName), Filterable = true, DirtyRead = true)]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
[PXFormula(typeof(Default<CRCase.customerID>))]
[PXRestrictor(typeof(Where<Contact.contactType, NotEqual<ContactTypesAttribute.bAccountProperty>,
And<Where<BAccount2.bAccountID, Equal<Current<SOOrder.customerID>>,
Or<Current<SOOrder.customerID>, IsNull>>>>), PX.Objects.CR.Messages.ContactBAccountDiff)]
[PXRestrictor(typeof(Where<Contact.isActive, Equal<True>>), PX.Objects.CR.Messages.ContactInactive,
typeof(Contact.displayName))]
public virtual Int32? UsrContactID { get; set; }
#endregion
}
}
And make sure you have AutoRefresh set to true in aspx for PXSelector control of this field.

I see you do not have a where condition in your selector to be based on the given customer. Try updating your selector to match something like this...
[PXSelector(typeof(Search2<Contact.contactID,
LeftJoin<BAccount, On<BAccount.bAccountID, Equal<Contact.bAccountID>>>,
Where<BAccount.bAccountID, Equal<Current<SOOrder.customerID>>>>),
DescriptionField = typeof(Contact.displayName), Filterable = true, DirtyRead = true)]
In your page also make sure your selector has AutoRefresh="true"
Example:
<px:PXSelector ID="edWeightUOM" runat="server"
DataField="WeightUOM" Size="S" AutoRefresh="true" />

Related

Need help in selector control

I created a selector control which displays a list of all serial #s from INItemLotSerial table, it works fine, the problem is the description field is showing InventoryID, how to show InventoryCD. Please have a look at below sample code.
[PXSelector(typeof(Search<INItemLotSerial.lotSerialNbr>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(INItemLotSerial.inventoryID))]
// i also joined with InventoryItem but this doesn't works.
[PXSelector(typeof(Search2<INItemLotSerial.lotSerialNbr,
LeftJoinSingleTable<InventoryItem, On<InventoryItem.inventoryID,Equal<INItemLotSerial.inventoryID>>>>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(InventoryItem.inventoryCD))]
The main problem with DescriptionField property is that it is waiting for getting the field from the same table for which Selector is written. But in the case of ID/CD usually, the CD is not present in the table where ID is present, except the main table.
UPDATED I have removed previous code (implementation using custom attributes and FieldSelecting event handler) because of the performance issues which it is bringing with it. The code below is resulting with the same lookup but getting the data with one inner join instead of all the requests which the previous code was doing.
You can do the following to get this lookup with description:
Create a PXProjection on INItemLotSerial and InventoryItem tables like below:
[PXCacheName("Lot Serials with Inventory CD")]
[PXProjection(typeof(Select2<INItemLotSerial,
InnerJoin<InventoryItem,
On<INItemLotSerial.inventoryID, Equal<InventoryItem.inventoryID>>>>))]
public class INItemLotSerialWithInventoryItem : IBqlTable
{
[PXDBInt(BqlField = typeof(INItemLotSerial.inventoryID))]
[PXUIField(DisplayName = "Inventory ID", Visibility = PXUIVisibility.Visible, Visible = false)]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(InventoryItem.inventoryCD))]
[PXUIField(DisplayName = "Inventory ID")]
public virtual string InventoryCD { get; set; }
public abstract class inventoryCD : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(INItemLotSerial.lotSerialNbr))]
[PXUIField(DisplayName = "Lot/Serial Nbr")]
public virtual string LotSerialNbr { get; set; }
public abstract class lotSerialNbr : IBqlField { }
}
Set your selector to use this PXProjection like below:
[PXSelector(typeof(Search<INItemLotSerialWithInventoryItem.lotSerialNbr>),
new Type[] { typeof(INItemLotSerialWithInventoryItem.lotSerialNbr) },
SubstituteKey = typeof(INItemLotSerialWithInventoryItem.lotSerialNbr),
DescriptionField = typeof(INItemLotSerialWithInventoryItem.inventoryCD))]
As a result, you will get lookup like below:

Enabling "AllowAddNew" and/or "AllowEdit" on selector for custom DAC

I have created a custom DAC, Graph, and Screen to manage tracking of new entity for client, called "Management Company." New screen is simple Grid with CRUD controls. Selector on Customer screen allows me to choose from list of "Management Company" entries. I also added new field to Contact DAC and DB table to track which "Management Company" is assigned to a given contact. This field is where the selector is configured.
I am trying to enable the "AllowAddNew" and/or "AllowEdit" properties of this selector. When I set to "True," the buttons show up but they don't do anything.
I can create and save new entries to this DB table via the new screen and I can retrieve them with the Selector just fine. I just can't create new from selector.
I have tried looking this up and I'm not finding much information.
How can I achieve this?
Sample of DAC:
namespace PX.Objects.CR
{
[Serializable]
public class UsrCustomerManagementCompany : IBqlTable
{
#region MancompID
[PXDBString(16, IsUnicode = true, InputMask = ">LLLLLLLLLLLLLLLL", IsKey = true)]
[PXUIField(DisplayName = "Company ID")]
[PXDefault]
public virtual string MancompID { get; set; }
public abstract class mancompID : IBqlField { }
#endregion
#region MancompName
[PXDBString(60, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Company Name")]
[PXDefault]
public virtual string MancompName { get; set; }
public abstract class mancompName : IBqlField { }
#endregion
#region MancompDescr
[PXDBString(4000, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Description")]
public virtual string MancompDescr { get; set; }
public abstract class mancompDescr : IBqlField { }
#endregion
}
}
Graph:
using System;
using PX.Data;
using PX.Objects.CR;
namespace ClientCode
{
public class ManagementCompanyMaint : PXGraph<ManagementCompanyMaint, UsrCustomerManagementCompany>
{
public PXSelect<UsrCustomerManagementCompany> ManagementCompanies;
}
}
Contact DAC Extension and Selector:
namespace PX.Objects.CR
{
public class ContactExt : PXCacheExtension<PX.Objects.CR.Contact>
{
#region UsrManagementCompany
[PXDBString]
[PXUIField(DisplayName = "Management Company")]
[PXSelector(
typeof(Search<UsrCustomerManagementCompany.mancompID>),
new Type[]
{
typeof(UsrCustomerManagementCompany.mancompID),
typeof(UsrCustomerManagementCompany.mancompName)
},
SubstituteKey = typeof(UsrCustomerManagementCompany.mancompName)
)]
public virtual string UsrManagementCompany { get; set; }
public abstract class usrManagementCompany : IBqlField { }
#endregion
}
}
I think all you are missing is the PXPrimaryGraph attribute on your DAC to indicate for that record type what is the main graph to refer to.
In your case try adding...
[PXPrimaryGraph(typeof(ClientCode.ManagementCompanyMaint))]
[Serializable]
public class UsrCustomerManagementCompany : IBqlTable
{
//...
}
Here are some related questions that might also help:
How to create a hyperlink user field
how to use AllowEdit in Acumatica

Data in Contact selector disappearing after the selection of business account

I am just having a simple selector for contacts in my custom sreen. User can choose the Business Account, Prospect, customer.. and the related contacts should load to the selector. Below is my selector DAC definition.
I found weird that the Selector loads data for the first 2 Business account selection, After that for all subsequent selection the contact information is not available. If i rebuild my project again this data appears. Looks like some additional conditions are getting appended. But i am not sure what is going wrong. Please assist.
#region ContactOpportunity
public abstract class contactOpportunity : PX.Data.IBqlField { }
[PXDBInt]
[PXUIField(DisplayName = "Contact")]
[PXSelector(typeof(Search2<Contact.contactID,
InnerJoin<BAccount, On<BAccount.bAccountID, Equal<Contact.bAccountID>>>,
Where<BAccount.bAccountID, Equal<Current<UsrQuotation.baccountOpportunity>>,
And<Contact.isActive, Equal<True>>>>),
SubstituteKey = typeof(Contact.displayName), Filterable = true)]
public virtual Int32? ContactOpportunity { get; set; }
#endregion
For those who encounter similar issue,
After i change the DAC to BAccountCRM it works as usual. I dont know what actually happens when i use the other DAC.
#region ContactOpportunity
public abstract class contactOpportunity : PX.Data.IBqlField { }
[PXDBInt]
[PXUIField(DisplayName = "Contact")]
[PXSelector(typeof(Search2<Contact.contactID,
InnerJoin<BAccountCRM, On<BAccountCRM.bAccountID, Equal<Contact.bAccountID>>>,
Where<BAccountCRM.bAccountID, Equal<Current<UsrQuotation.baccountOpportunity>>,
And<Contact.isActive, Equal<True>>>>),
SubstituteKey = typeof(Contact.displayName), Filterable = true)]
public virtual Int32? ContactOpportunity { get; set; }
#endregion

I am trying to bring selector(Formula CD) for filter data in formula master screen,

FormulaID is the identity field and it's new developed screen, but I am not able to get the selector. I have tried all the ways that I know to achieve that:
Maintaining parent and child relationships
Dataview with BQL Query
Below are the definitions of the FormulaID and FormulaCD from DAC:
#region FormulaID
public abstract class formulaID : PX.Data.IBqlField
{
}
protected int? _FormulaID;
[PXDBIdentity(IsKey =true)]
[PXUIField(Enabled = false)]
public virtual int? FormulaID
{
get
{
return this._FormulaID;
}
set
{
this._FormulaID = value;
}
#endregion
#region FormulaCD
public abstract class formulaCD : PX.Data.IBqlField
{
}
protected string _FormulaCD;
[PXDBString(30,IsUnicode = true)]
[PXUIField(DisplayName = "Formula ID", Visibility = PXUIVisibility.SelectorVisible)]
[PXSelector(typeof(Search<TSFormula.formulaCD>),
typeof(TSFormula.descr),SubstituteKey = typeof(TSFormula.formulaCD), ValidateValue = false)]
public virtual string FormulaCD
{
get
{
return this._FormulaCD;
}
set
{
this._FormulaCD = value;
}
}
#endregion
In my example below, the PXDBIdentity integer field is not enabled and not visible. The String field is marked isKey=true with the PXSelector described. The ASPX page markup offers the px:PXSelector tag for the String field as shown:
<px:PXSelector ID="edContractNumber" runat="server" DataField="ContractNumber"
AutoRefresh="True" DataSourceID="ds" NullText="<NEW>">
<GridProperties FastFilterFields="ShortName">
<Columns>
<px:PXGridColumn DataField="ContractNumber" Width="90px"></px:PXGridColumn>
<px:PXGridColumn DataField="ShortName" Width="120px"></px:PXGridColumn>
</Columns>
</GridProperties>
</px:PXSelector>
Here are the DAC fields:
#region ContractID
public abstract class contractID : PX.Data.IBqlField
{
}
[PXDBIdentity()]
[PXDefault(0)]
[PXUIField(Visible = false, Enabled = false)]
public virtual int? ContractID
{
get;
set;
}
#endregion
#region ContractNumber
public abstract class contractNumber : PX.Data.IBqlField
{
}
[PXDBString(IsKey = true)]
[PXDefault()]
[PXSelector(typeof(Search3<MyDAC.contractNumber,
OrderBy<Desc<MyDAC.contractID>>>),
new Type[] {
typeof(MyDAC.shortName),
typeof(MyDAC.contractNumber)},
DirtyRead = true)]
[PXUIField(DisplayName = "Contract Number", Required = true)]
public virtual string ContractNumber { get; set; }
#endregion

Data in a processing screen disappears

I have a custom processing page. The main DAC of the data view is ARRegister, but there is the data view delegate. Both the view & delegate join ARCashSale & ARInvoice to the main DAC, The reason for this is...some records are cash sales, and others are invoices, overdue charges, ect. A few grid columns are included, which displays data specific to a cash sale. I invoke a static method in my process graph to assign the process delegate. The method runs with no errors.
In the data view delegate, I check the doc type for each record returned from the BQL.
If cash sale, then
yield return new PXResult<ARRegister, ARCashSale>(register, cashsale)
ELSE
yield return new PXResult<ARRegister>(register)
The reason for the delegate is to check some other conditions which cannot be determined using standard BQL. I notice the data in the column specific to a cash sale disappears after the user selects 'Process All'. I am unable to determine the reason. Checking to see if others have experienced this.
DataView
public PXProcessingJoin<ARRegister,
LeftJoin<cs.ARCashSale, On<ARRegister.docType, Equal<cs.ARCashSale.docType>, And<ARRegister.refNbr, Equal<cs.ARCashSale.refNbr>>>,
LeftJoin<ARInvoice, On<ARRegister.docType, Equal<ARInvoice.docType>, And<ARRegister.refNbr, Equal<ARInvoice.refNbr>>>,
InnerJoin<Customer,On<ARRegister.customerID,Equal<Customer.bAccountID>>>>>,
Where2<Where<ARRegister.released, Equal<True>, And<ARRegister.branchID, Equal<Current<AccessInfo.branchID>>>>,
And<Where<Customer.finChargeApply,Equal<True>>>>> Registers;
This is an older question, but I had a similar issue.
You need to add a boolean field named "Selected" to DACs you want to process.
The way I solved it was using a local DAC.
You can make it inherit from ARRegister and just add the required field.
In my case I used PXProjection, inherited from the main DAC and added the fields I needed from the joined DACs. Note that you need to add the BqlField = typeof(DAC.field) property to the type attribute of these fields to map them to the correct DAC.
Then in the PXProcessing view you just use your local DAC.
Also, it is very useful to try the Request Profiler screen (SM205070) when troubleshooting BQL.
Basically in processing screens sub DAC (other than Main DAC in view), filed values will not persist once the process completed.
In this case, the PXProjection will help us to persist the values even after completion of the process for the rows/records in processing screens.
Please find the sample Projection View and DAC below.
[PXProjection(typeof(Select2<SOShipment, InnerJoin<SOOrderShipment,
On<SOOrderShipment.shipmentNbr, Equal<SOShipment.shipmentNbr>,
And<SOShipment.status, Equal<SOShipmentStatus.confirmed>>>,
InnerJoin<SOOrder, On<SOOrderShipment.orderType, Equal<SOOrder.orderType>,
And<SOOrderShipment.orderNbr, Equal<SOOrder.orderNbr>>>>>>))]
Projection DAC:
[Serializable]
public class ProjectionShipmentDAC : IBqlTable
{
#region Selected
public abstract class selected : IBqlField
{
}
protected bool? _Selected = false;
[PXBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected
{
get
{
return _Selected;
}
set
{
_Selected = value;
}
}
#endregion
#region Status
[PXDBString(1, IsFixed = true, BqlField = typeof(SOShipment.status))]
[PXUIField(DisplayName = "Status")]
[SOShipmentStatus.List()]
public virtual string Status { get; set; }
public abstract class status : IBqlField { }
#endregion
#region ShipmentNbr
[PXDBString(15, IsKey = true, IsUnicode = true, BqlField = typeof(SOShipment.shipmentNbr))]
[PXUIField(DisplayName = "Shipment Nbr.")]
public virtual string ShipmentNbr { get; set; }
public abstract class shipmentNbr : IBqlField { }
#endregion
#region ShipDate
[PXDBDate(BqlField = typeof(SOShipment.shipDate))]
[PXUIField(DisplayName = "Shipment Date")]
public virtual DateTime? ShipDate { get; set; }
public abstract class shipDate : IBqlField { }
#endregion
#region CustomerID
[PXDBInt(BqlField = typeof(SOShipment.customerID))]
[PXUIField(DisplayName = "Customer")]
[PXSelector(typeof(Customer.bAccountID), new Type[] { typeof(Customer.acctCD), typeof(Customer.acctName) },
SubstituteKey = typeof(Customer.acctCD), DescriptionField = typeof(BAccount.acctName))]
public virtual int? CustomerID { get; set; }
public abstract class customerID : IBqlField { }
#endregion
#region Shipped Quantity
[PXDBDecimal(BqlField = typeof(SOShipment.shipmentQty))]
[PXUIField(DisplayName = "Shipped Quantity")]
public virtual decimal? ShipmentQty { get; set; }
public abstract class shipmentQty : IBqlField { }
#endregion
}
Have you played around with MatrixMode and/or SyncPosition on your page grid? You might need SyncPosition="True"
Also, does the issue occur if not using process all? (process 1 or 2 rows)

Resources