PMTimeActivity Usr Fields - acumatica

I'm lost and not sure I am going about this the right way.
I have placed a PXAction Button on the EP305000 screen.
EP305000 Screen
When the button is pressed I wanted to search for any records in the PMTimeActivity table for the current user that has the UsrPIXIClockIn NOT null AND the UsrPIXIClockOut Null.
If found, need to set its value to the current time. If not found then create a new record with the UsrPIXIClockIn field set to the current time.
However, my first attempt of just trying to read the Usr fields is creating an error. When I try to parse a PXResultSet, code lines containing my Usr Fields are throwing an error when compiling.
There error is: 'PX.Objects.CR.PMTimeActivity' does not contain a definition for 'UsrPIXIClockIn' and no extension method 'UsrPIXIClockIn' accepting a first argument of type 'PX.Objects.CR.PMTimeActivity'
I am not sure where this definition belongs or how to define it. Here is the Code I have:
[Serializable]
public class PMTimeActivityExt : PXCacheExtension<PMTimeActivity>
{
public PXSelect<PMTimeActivity> PMTimeActivity;
#region UsrPIXIClockIn
[PXDBTime(DisplayMask = "t", UseTimeZone = false)]
[PXUIField (DisplayName="Clock In")]
public virtual DateTime? UsrPIXIClockIn { get; set; }
public abstract class usrPIXIClockIn : IBqlField { }
#endregion
#region UsrPIXIClockOut
[PXDBTime(DisplayMask = "t", UseTimeZone = false)]
[PXUIField (DisplayName="Clock Out")]
public virtual DateTime? UsrPIXIClockOut { get; set; }
public abstract class usrPIXIClockOut : IBqlField { }
#endregion
#region UsrPIXITotalHours
[PXDBDecimal]
[PXUIField (DisplayName="Total Hours")]
public virtual decimal? UsrPIXITotalHours { get; set; }
public abstract class usrPIXITotalHours : IBqlField { }
#endregion
}
public class TimeCardMaint_Extension : PXGraphExtension<TimeCardMaint>
{
public PXSelect<PMTimeActivity> PMTimeActivity;
public PXAction<EPTimeCard> PunchCard;
public PXAction<PX.Objects.EP.EPTimeCard> PunchTimeCard;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Punch Time Card")]
protected void punchTimeCard(PXCache cache)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendLine("Started...\n");
EPEmployee employeeByUserID = PXSelect<EPEmployee, Where<EPEmployee.bAccountID, Equal<Current<EPTimeCard.employeeID>>>>.Select(this.Base);
Guid userID = (Guid) employeeByUserID.UserID;
sb.AppendLine("user ID:" + userID.ToString() + "\n");
// This one returns all records.
PXResultset<PMTimeActivity> TimeActivity = PXSelect<PMTimeActivity>.Select(this.Base);
foreach (PMTimeActivity timeRecord in TimeActivity)
{
if (timeRecord.OwnerID == userID) {
sb.AppendLine("UsrPIXIClockIn:" + timeRecord.UsrPIXIClockIn + "\n");
}
}
sb.AppendLine("\nEnded...\n");
throw new PXException("Clicked Punch Time Card!\n" + sb.ToString());
}
}
As for the PXSelect for the PXResultSet I tried this:
PXResultset<PMTimeActivity> TimeActivity = PXSelect<PMTimeActivity, Where<Required<PMTimeActivity.ownerID>, Equal<Current<PMTimeActivity.ownerID>>>>.Select(this.Base, userID);
But no records are ever found. What am I doing wrong?

The error message boils down to: PMTimeActivity does not contain UsrPIXIClockIn.
PMTimeActivity is the base DAC and the user fields are in the extended DAC PMTimeActivityExt.
You need to fetch the extension from the base DAC first using GetExtension method and access your user field on the extension record timeRecordExt.UsrPIXIClockIn:
foreach (PMTimeActivity timeRecord in TimeActivity)
{
PMTimeActivityExt timeRecordExt = timeRecord.GetExtension<PMTimeActivityExt>();
if (timeRecordExt != null && timeRecord.OwnerID == userID) {
sb.AppendLine("UsrPIXIClockIn:" + timeRecordExt.UsrPIXIClockIn + "\n");
}
}
In your question I see there's a data view declaration in the DAC extension. Maybe that's a typo, you should remove it as DAC extension should only contain user fields. This is important because Acumatica aggressively parses these code structures. So remove that line from PMTimeActivityExt class:
public PXSelect<PMTimeActivity> PMTimeActivity;

Related

How to include a DAC extension unbound field in a Search<> parameter of PXSelector?

I have segmented INLocation.LocationCD and need to restrict the PXSelector to include or exclude a particular segment based on the screen where the location is being entered. I have been able to access the DAC extension field in the displayed columns of the PXSelector, but the field is evaluated as null during the Search<> parameter of PXSelector.
I have tried:
referencing INLocationExt.myField directly,
making an inherited DAC to define the custom field directly,
and creating a PXProjection in hopes that the additional layer would cause the unbound field to be retrieved to populate the PXSelector in case the field was not being loaded in time for the Search<>.
Key points:
This is an unbound field in the DAC extension
It returns an value based evaluating the last segment of the INLocation.LocationCD
Generic Inquiries properly display this value
I have been unable to make PXSelector return any values when I reference the unbound field unless I simply check And
The field defined in my DAC extension:
[PXString(1)]
[PXUIField(DisplayName = "Condition")]
[ConditionType.List]
public String UsrSSCondition
{
get
{
if (LocationCD == null || LocationCD.Length == 0) return ConditionType.Undefined;
switch (LocationCD.Substring(LocationCD.Length - 1, 1))
{
case "N":
return ConditionType.New;
case "R":
return ConditionType.Repair;
case "C":
return ConditionType.Core;
case "U":
return ConditionType.Used;
default:
return ConditionType.Undefined;
}
}
}
public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }
The PXSelector:
[PXSelector(typeof(Search<INLocation.locationID, Where<INLocation.receiptsValid, Equal<True>,
And<INLocationExt.usrSSCondition, NotEqual<ConditionType.core>>>>),
typeof(INLocation.locationCD),
typeof(INLocation.active),
typeof(INLocation.primaryItemID),
typeof(INLocation.primaryItemClassID),
typeof(INLocationExt.usrSSCondition),
typeof(INLocation.receiptsValid),
SubstituteKey = typeof(INLocation.locationCD))]
The PXProjection:
[Serializable]
[PXCacheName("SSCS INLocation")]
[PXProjection(typeof(Select<INLocation>))]
public partial class SSINLocation : IBqlTable
{
#region LocationID
[PXDBInt(IsKey = true, BqlField = typeof(INLocation.locationID))]
public int? LocationID { get; set; }
public abstract class locationID : PX.Data.BQL.BqlInt.Field<locationID> { }
#endregion
#region LocationCD
[PXDBString(BqlField = typeof(INLocation.locationCD))]
public String LocationCD { get; set; }
public abstract class locationCD : PX.Data.BQL.BqlString.Field<locationCD> { }
#endregion
#region UsrSSCondition
[PXDBString(BqlField = typeof(INLocationExt.usrSSCondition))]
public String UsrSSCondition { get; set; }
public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }
#endregion
#region ReceiptsValid
[PXDBBool(BqlField = typeof(INLocation.receiptsValid))]
public bool? ReceiptsValid { get; set; }
public abstract class receiptsValid : PX.Data.BQL.BqlBool.Field<receiptsValid> { }
#endregion
#region Active
[PXDBBool(BqlField = typeof(INLocation.active))]
public bool? Active { get; set; }
public abstract class active : PX.Data.BQL.BqlBool.Field<active> { }
#endregion
#region PrimaryItemID
[PXDBInt(BqlField = typeof(INLocation.primaryItemID))]
public int? PrimaryItemID { get; set; }
public abstract class primaryItemID : PX.Data.BQL.BqlInt.Field<primaryItemID> { }
#endregion
#region PrimaryItemClassID
[PXDBInt(BqlField = typeof(INLocation.primaryItemClassID))]
public int? PrimaryItemClassID { get; set; }
public abstract class primaryItemClassID : PX.Data.BQL.BqlInt.Field<primaryItemClassID> { }
#endregion
}
I have tried simple versions and various combinations of those to no avail. How do I leverage my "condition" in the Search<> clause of the PXSelector?
Edit 1:
Picture of PXSelector returning values as a column - does not work as a PXRestrictor or as a where clause in Select<>.
Edit 2: More info
I have simplified the DAC extension to use PXFormula and pick out the last character of the LocationCD retrieved by PXFormula to set the value.
We need to use the last segment of the LocationCD to manage the condition of the part in a bin.  This would allow us to separate cost as well as manage MRO Spares for maintenance as New, Used, Repaired, and Requiring Repair while also allowing the ability to specify other conditions later (like NCM if received damaged, etc.) if needed.  While some materials can be used globally, materials of certain conditions need to be available under certain use cases.  My intended strategy is to apply the rules to the last segment of the Location CD to allow the PXSelector to control user entry, either as a DAC extension on INLocation or as a Cache_Attached in the relevant graphs, if necessary.
I created a DAC extension on INLocation for usrSSCondition as a PXString.  My latest attempt was to use a PXFormula to pull the LocationCD value and then custom code on the set{} to pick out the last segment and set the code for the relevant condition.  (This technique was actually new to me, and a response in the stackoverflow post guided me to the idea.)
When using in a PXSelector as a displayed column, I can see the value.  However, the Select<> does not allow me to tap into that segment or custom PXString field used to display that condition.  I was hoping that some "behind the scenes magic" would evaluate my PXString field to limit the results, but it seems that the field is returned as null during the Select and then processed in a later step of DAC processing.  When I think about what a Select is doing, it would make sense that data not stored in the database cannot be used to filter the results.  PXRestrictor does not impact it either.
1) Is there a way to get my DAC to process the PXString value before the PXSelector applies the where clause?
2) Is this something I need to take to an attribute for post-processing?  (If so, any suggestions on where to look for a simple example?)
Updated DAC:
#region usrSSCondition
private String _condition;
[PXString]
[PXUIField(DisplayName = "Condition")]
[PXFormula(typeof(INLocation.locationCD))]
[ConditionType.List]
public String UsrSSCondition
{
get { return _condition; }
set
{
string Loc = value;
if (Loc == null || Loc.Length == 0)
{
_condition = ConditionType.Undefined;
}
else
{
_condition = (Loc.Substring(Loc.Length - 1, 1)) switch
{
"N" => ConditionType.New,
"R" => ConditionType.Repair,
"C" => ConditionType.Core,
"U" => ConditionType.Used,
_ => ConditionType.Undefined,
};
}
}
}
Do not ever use code inside the getter, it won't be work properly in BQL expressions!
If you wanna check Loc.Substring(Loc.Length - 1, 1) somewhere in BQL just write your own BQL function
public class ConditionTypeBySegment<Source> : BqlFunction, IBqlOperand, IBqlCreator
where Source : IBqlOperand
{
private IBqlCreator _source;
public void Verify(PXCache cache, object item, List<object> pars, ref bool? result, ref object value)
{
if (!getValue<Source>(ref _source, cache, item, pars, ref result, out value) || value == null)
return;
if (value is string strValue)
{
switch (strValue.Substring(strValue.Length - 1, 1))
{
case "N":
value = ConditionType.New;
break;
case "R":
value = ConditionType.Repair;
break;
case "C":
value = ConditionType.Core;
break;
case "U":
value = ConditionType.Used;
break;
default:
value = ConditionType.Undefined;
break;
}
return;
}
value = ConditionType.Undefined;
}
public bool AppendExpression(ref SQLExpression exp, PXGraph graph, BqlCommandInfo info, BqlCommand.Selection selection)
{
...
return true;
}
}
or use a combination of existing functions. For example:
[PXSelector(typeof(Search<INLocation.locationID,
Where<INLocation.receiptsValid, Equal<True>,
And<Substring<FABookBalance.deprToPeriod, Sub<StrLen<FABookBalance.deprToPeriod>, int1>, int1>, NotEqual<ConditionTypes.tCore>>>>),
typeof(INLocation.locationCD),
typeof(INLocation.active),
typeof(INLocation.primaryItemID),
typeof(INLocation.primaryItemClassID),
typeof(INLocationExt.usrSSCondition),
typeof(INLocation.receiptsValid),
SubstituteKey = typeof(INLocation.locationCD))]
public static class ConditionTypes
{
public class tNew : PX.Data.BQL.BqlString.Constant<tNew> { public tNew() : base("N") { } }
public class tRepair : PX.Data.BQL.BqlString.Constant<tRepair> { public tRepair() : base("R") { } }
public class tCore : PX.Data.BQL.BqlString.Constant<tCore> { public tCore() : base("C") { } }
public class tUsed : PX.Data.BQL.BqlString.Constant<tUsed> { public tUsed() : base("U") { } }
}
Simple solution - simplify. Changed the field to simply hold the value and then let the BLC set the value when the locaitonCD value is set. On creation of the record, the locationCD field is empty, so defining the FieldDefaulting logic causes the condition to be undefined initially. By monitoring FieldUpdated of the LocationCD, we can then reapply the FieldDefaulting rules to the "real" value.
DAC field definition:
#region usrSSCondition
[PXDBString]
[PXUIField(DisplayName = "Condition")]
[ConditionType.List]
public String UsrSSCondition { get; set; }
public abstract class usrSSCondition : PX.Data.BQL.BqlString.Field<usrSSCondition> { }
#endregion
Event Handlers in the BLC:
#region INLocationExt_UsrSSCondition_FieldDefaulting
protected void INLocation_UsrSSCondition_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
INLocation row = (INLocation)e.Row;
string Loc = row?.LocationCD;
if (Loc == null || Loc.Length == 0)
{
e.NewValue = ConditionType.Undefined;
}
else
{
e.NewValue = (Loc.Substring(Loc.Length - 1, 1)) switch
{
ConditionType.New => ConditionType.New,
ConditionType.Repair => ConditionType.Repair,
ConditionType.Core => ConditionType.Core,
ConditionType.Used => ConditionType.Used,
_ => ConditionType.Undefined,
};
}
}
#endregion
#region INLocation_LocationCD_FieldUpdated
protected void _(Events.FieldUpdated<INLocation.locationCD> e)
{
INLocation row = (INLocation)e.Row;
e.Cache.SetDefaultExt<INLocationExt.usrSSCondition>(row);
}
#endregion
Since locations are defined in INSiteMaint, the event handlers in that graph allow setting the field value to store in the database without any translation. That enables use of PXRestrictorAttribute to limit available locations accordingly or write rules to set the location flags on the INSiteMaint screen.
Below is one example of CacheAttached to add a PXRestrictor to prevent receipt into a Core location type unless it is done from the NcmTag Screen. (A heavy hand controlling what locations a user may select is not needed universally, so this was not applied globally to the DAC field.)
#region INTran_LocationID_CachedAttached
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRestrictor(typeof(Where<INLocationExt.usrSSCondition, NotEqual<ConditionType.core>,
Or<Current<AccessInfo.screenID>, Equal<SSCS.Constants.NcmTagScreenID>>>), "")]
#endregion
Also worth noting, since my purpose is to use a character of the LocationCD for the end user to recognize the location type, I have to prevent the user from changing the LocationCD value through the RowSelected event for INLocation.
#region INLocation_RowSelected
protected void _(Events.RowSelected<INLocation> e)
{
INLocation row = e.Row;
if(row?.SiteID != null)
{
INLocationExt rowExt = row.GetExtension<INLocationExt>();
PXUIFieldAttribute.SetEnabled<INLocation.locationCD>(e.Cache, row,
!DisableLocationRename(row?.LocationID));
}
}
#endregion
#region DisableLocationRename
protected virtual bool DisableLocationRename(int? locationID)
{
int counter = PXSelect<SSINNcmTag,
Where<SSINNcmTag.locationID, Equal<Required<SSINNcmTag.locationID>>,
And<SSINNcmTag.tranRefNbr, IsNull>>>
.SelectSingleBound(Base, null, locationID).Count;
if (counter > 0) return true;
counter = PXSelect<INLocationStatus,
Where <INLocationStatus.locationID, Equal<Required<INLocationStatus.locationID>>,
And<INLocationStatus.qtyOnHand, Greater<DecimalZero>>>>
.SelectSingleBound(Base, null, locationID).Count;
if (counter > 0) return true;
return false;
}
#endregion
While we have the ability to write some very interesting code, it is important to stop once in a while to ask, "Why am I making this complicated?" Whenever possible, simplify.

how to add a field on the Project Quotes screen PQ000025

Good morning, I want to add a new field on this screen Project Quotes but in doing so I get this message, that the table does not exist.
How should or what is the way to achieve it.
Thanks in advance
Imagen 01
The added field in the database
enter image description here
He added the field in the database and then I generated my extension.
namespace PX.Objects.CR
{
public class PMQuoteExt : PXCacheExtension<PX.Objects.CR.CRQuote>
{
#region UsrNota
[PXDBString(-1, InputMask = "", BqlField = typeof(PMQuoteStandaloneExt.usrNotaText))]
[PXUIField(DisplayName = "Nota ")]
public virtual string UsrNotaText { get; set; }
public abstract class usrNotaText : IBqlField { }
#endregion
}
public class PMQuoteStandaloneExt : PXCacheExtension<PX.Objects.CR.Standalone.CRQuote>
{
#region UsrNota
[PXDBString(-1, InputMask = "")]
[PXUIField(DisplayName = "Nota ")]
public virtual string UsrNotaText { get; set; }
public abstract class usrNotaText : IBqlField { }
#endregion
}
}
public class PMQuoteMaint_Extension : PXGraphExtension<PMQuoteMaint>
{
public PXSelect<PX.Objects.CR.Standalone.CRQuote> Test;
}
However, when I record, it does not fill the field.
that I am making a mistake or doing wrong.
Can you tell me please.
Thank you
PMQuote is not an actual DB table, but a BQL projection between tables:
CR.Standalone.CRQuote
CROpportunityRevision
CR.Standalone.CROpportunity
The way that I would tackle this is:
Add the field in table CRQuote
Extend the graph and override the Projection with the inclusion of the new CRQuote field.
UPDATE:
Based on #HB_Acumatica suggestion, step 2 would get simplified to a DAC extension (no need for the Graph extension). Much simpler to maintain in subsequent Acumatica versions!
UPDATE 2:
The extended DACs do not look correct in your question. Keep in mind that you should extend the original table (CRQuote), and the projection in order to have the value persisted.
The following definition worked correctly on my end:
//Projection extension
public class PMQuoteExt : PXCacheExtension<PMQuote>
{
#region UsrCustomField
[PXDBString(100, BqlField = typeof(CRQuoteExt.usrCustomField))]
[PXUIField(DisplayName="Custom Field")]
public virtual string UsrCustomField { get; set; }
public abstract class usrCustomField : IBqlField { }
#endregion
}
//Actual Table extension
public class CRQuoteExt : PXCacheExtension<PX.Objects.CR.Standalone.CRQuote>
{
#region UsrCustomField
[PXDBString(100)]
[PXUIField(DisplayName="Custom Field")]
public virtual string UsrCustomField { get; set; }
public abstract class usrCustomField : IBqlField { }
#endregion
}

Building processing screen for customer locations

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.

An error occurred while processing the field Style : Incorrect syntax near ')'

I want to achieve, is to get the QtyOnHand of a specific inventoryID in InSiteStatus table.
I created a DAC for InventoryID and labeled it as "Style" and another for QtyOnHand.
I used FieldUpdated event handler, so every time an user selects a new inventoryID it will update the QtyOnHand field.
Here's my work.
DAC:
public class AllocationFilter: IBqlTable
{
#region Style
public abstract class style : IBqlField
{
}
[PXInt]
[PXUIField(DisplayName = "Style")]
[PXSelector(typeof(InventoryItem.inventoryID)
, typeof(InventoryItem.inventoryCD)
, typeof(InventoryItem.descr)
, SubstituteKey = typeof(InventoryItem.inventoryCD)
)]
public virtual int? Style { get; set; }
#endregion
#region OnHand
public abstract class onHand : IBqlField
{ }
[PXDecimal()]
[PXUIField(DisplayName = "On Hand", Enabled = false)]
public virtual decimal? OnHand { get; set; }
#endregion
}
Event Handler:
protected void AllocationFilter_Style_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
// Obtain the new data record that contains the updated
// values of all data fields
AllocationFilter filterAlloc = (AllocationFilter)e.Row;
if (filterAlloc == null)
return;
// This is where the error triggers...
PXResultset<INSiteStatus> insitestatus = PXSelect<INSiteStatus,
Where<INSiteStatus.inventoryID,
Equal<AllocationFilter.style>>>.Select(this);
foreach (INSiteStatus sitestatus in insitestatus)
{
filterAlloc.OnHand = sitestatus.QtyOnHand;
}
}
I think there is a problem with my PXResultSet, can you help me guys?
You have two options that I see here.
One, create a View for this
public PXSelect<INSiteStatus,Where<INSiteStatus.inventoryID,Equal<Current<AllocationFilter.style>>> StyleStatus;
Then in your event handler
foreach (INSiteStatus sitestatus in StyleStatus.Select())
{......}
Or two, inline like you have it
PXResultset<INSiteStatus> insitestatus = PXSelect<INSiteStatus,
Where<INSiteStatus.inventoryID,
Equal<Required<INSiteStatus.inventoryID>>>.Select(this,filterAlloc.Style);
Unless you need greater control of the select statement, I would use the View

Inserted Records in Cache on Graph Extension Not Saving to Database

I have an extension graph onto INReceiptEntry. I set up a view for the custom DAC that I want a cache generated for that will get objects inserted into. After I generate one of the objects I want to insert into the cache, I use ViewName.Insert(Object); (also used ViewName.Cache.Insert(Object) with same results) within the graph extension's RowPersisting handler. This normally would store the data items in the corresponding database data table associated to the DAC, but nothing is stored in the DB. Instead of If statement fires and I get a popup stating that the object wasn't inserted. Here is my code:
public class INReceiptEntry_Extension : PXGraphExtension<INReceiptEntry>
{
public PXSelect<EMPWorkOrderINRegister> WorkOrderINRegisters;
#region Event Handlers
protected void INRegister_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
var row = (INRegister)e.Row;
var rowExt = PXCache<INRegister>.GetExtension<INRegisterExt>(row);
//Get Target Objects
foreach (INTranSplit split in PXSelect<INTranSplit, Where<INTranSplit.refNbr,
Equal<Required<INRegister.refNbr>>, And<INTranSplit.tranType, Equal<TranType>>>>
.Select(Base, Base.CurrentDocument.Current.RefNbr))
{
EMPWorkOrderINRegister WOINR = new EMPWorkOrderINRegister();
WOINR.Woid = rowExt.Usrwoid;
WOINR.RefNbr = split.RefNbr;
WOINR.SplitLineNbr = split.SplitLineNbr;
if (WorkOrderINRegisters.Insert(WOINR) == null)
{
Base.CurrentDocument.Ask("Did not insert WOINR:" + WOINR.RefNbr.ToString() + ", " + WOINR.SplitLineNbr.ToString(), MessageButtons.OK);
return;
}
}
}
#endregion
}
Any reason this isn't inserting into the custom cache? Does using a graph extension or executing this cache insert in a Persisting function have anything to do with why this is failing?
By request, the DAC:
using System;
using PX.Data;
using PX.Objects.IN;
namespace Manufacturing
{
[Serializable]
public class EMPWorkOrderINRegister : IBqlTable
{
#region Id
[PXDBIdentity()]
[PXUIField(DisplayName = "Id")]
public int? Id { get; set; }
public class id : IBqlField { }
#endregion
#region Woid
[PXDBInt()]
[PXUIField(DisplayName = "Woid")]
public int? Woid { get; set; }
public class woid : IBqlField { }
#endregion
#region RefNbr
[PXDBString(15, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Receipt Number")]
public string RefNbr { get; set; }
public class refNbr : IBqlField { }
#endregion
#region SplitLineNbr
[PXDBInt()]
[PXUIField(DisplayName = "Split Line Nbr")]
public int? SplitLineNbr { get; set; }
public class splitLineNbr : IBqlField { }
#endregion
#region AvailableSNs
[PXString()]
[PXUIField(DisplayName = "Available SNs")]
public string AvailableSNs { get; set; }
public class availableSNs : IBqlField { }
#endregion
[PXString()]
[PXDBScalar(typeof(Search<INTranSplit.lotSerialNbr, Where<INTranSplit.refNbr, Equal<EMPWorkOrderINRegister.refNbr>,
And<INTranSplit.splitLineNbr, Equal<EMPWorkOrderINRegister.splitLineNbr>>>>))]
public string SelectedSN { get; set; }
public class selectedSN : IBqlField { }
}
}
Eric, there is one major issue with your DAC, which is the lack of key fields.
Additional area of improvement in the INRegister_RowPersisting handler. RowPersisting handlers are designed to either validate the data record before it's committed to database or cancel the commit operation of a specific data record. For more details, please refer to the Acumatica Framework API Reference
The better approach for your scenario is to override the Persist method and insert missing EMPWorkOrderINRegister records before execution of the base Persist method. It's also a good idea to use a try-catch statement and delete EMPWorkOrderINRegister records with the Inserted status if base Persist method had failed.

Resources