Skip to main content

Hi Everyone,

 

I wouldn’t mind some direction before I get stuck into this. In short I want a processing screen that performs a single action over all the selected records instead of one action per record.

A bit of background first, our company doesn’t quite a manufacture our products, but we do build them using the kitting feature. (Packaging and labeling and basic assembly done in house etc) I have developed a customisation that takes a forecast of what we are expecting to sell (sale units) and it then calculates how much of the purchase components that we will need, compares this to our current inventory levels and then looks at things like the vendor lead time, safety stock etc and generates recommendations for what we should by and when. A small example of this looks like below (The actual run of this would have a lot more lines with larger spread vendors)

We intend to run this with a fresh forecast every week, so while the plan may generate multiple purchase recommendations within the forecast duration if the vendors lead time is shorter than the duration it’s unlikely that we would be wanting to create purchase orders for these later recommendations, as we will deal with these in the coming weeks.

We also have a requirement to group selected lines by vendor and location into consolidated orders.

We would like the screen that takes care of selecting the plan lines and converting them into purchase orders to look and feel like a processing screen however instead of submitting 10 records and awaiting the result of 10 actions, we would like to submit 10 records and await the response of 1 action that may produce 1 or more purchase orders.

Does anyone have any ideas for the best way to go about this?

I was over thinking this this morning.

My ASPX looks like a processing from.

The detail view isn’t a processing view it’s just the good old fashioned view.

        public SelectFrom<MAXXPOSupplyPlanDetail>.
Where<MAXXPOSupplyPlanDetail.planID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.planID.FromCurrent>.
And<MAXXPOSupplyPlanDetail.purchaseOrderNbr.IsNull>.
And<MAXXPOSupplyPlanDetail.vendorID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.vendorID.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.vendorID.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.siteID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.siteID.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.siteID.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.requestedDate.IsGreaterEqual<MAXXPOGeneratePOFromSupplyPlanFilter.fromDateRequested.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.fromDateRequested.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.requestedDate.IsLessEqual<MAXXPOGeneratePOFromSupplyPlanFilter.toDateRequested.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.toDateRequested.FromCurrent.IsNull>>>.
OrderBy<MAXXPOSupplyPlanDetail.inventoryID.Desc, MAXXPOSupplyPlanDetail.requestedDate.Asc>.
View
SupplyPlanDetails;

And then the action creates a list of the detail records that were selected which is passed to a PXLongOperation.

        #region i Actions ]
public PXAction<MAXXPOGeneratePOFromSupplyPlanFilter> RunGeneratePurchaseOrder;
PXButton(Connotation = PX.Data.WorkflowAPI.ActionConnotation.Success)]
PXUIField(DisplayName = "Generate Purchase Order", Enabled = true)]
protected virtual IEnumerable runGeneratePurchaseOrder(PXAdapter adapter)
{

List<MAXXPOGeneratePOFromSupplyPlanFilter> list = new List<MAXXPOGeneratePOFromSupplyPlanFilter>();
foreach (MAXXPOGeneratePOFromSupplyPlanFilter purchasePlan in adapter.Get<MAXXPOGeneratePOFromSupplyPlanFilter>())
{
list.Add(purchasePlan);
}

var filters = Filter.Current;
List<MAXXPOSupplyPlanDetail> selectedSupplyPlanDetails =
SupplyPlanDetails
.Select()
.RowCast<MAXXPOSupplyPlanDetail>()
.Where(x => x.Selected == true)
.ToList();

PXLongOperation.StartOperation(this, delegate ()
{
GeneratePurchaseOrders(filters,selectedSupplyPlanDetails);
});

return list;
}

 


Hi Matthew! I don’t know how much of this code you’ve been able to convert already, but I figure I’d provide a basic example of how to turn this existing page into a processing page for anyone else curious:

 

Convert SelectFrom data view to PXProcessing data view

public PXProcessing<MAXXPOSupplyPlanDetail, Where<MAXXPOSupplyPlanDetail.planID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.planID.FromCurrent>.
And<MAXXPOSupplyPlanDetail.purchaseOrderNbr.IsNull>.
And<MAXXPOSupplyPlanDetail.vendorID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.vendorID.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.vendorID.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.siteID.IsEqual<MAXXPOGeneratePOFromSupplyPlanFilter.siteID.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.siteID.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.requestedDate.IsGreaterEqual<MAXXPOGeneratePOFromSupplyPlanFilter.fromDateRequested.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.fromDateRequested.FromCurrent.IsNull>>.
And<MAXXPOSupplyPlanDetail.requestedDate.IsLessEqual<MAXXPOGeneratePOFromSupplyPlanFilter.toDateRequested.FromCurrent>.
Or<MAXXPOGeneratePOFromSupplyPlanFilter.toDateRequested.FromCurrent.IsNull>>>,
OrderBy<MAXXPOSupplyPlanDetail.inventoryID.Desc, MAXXPOSupplyPlanDetail.requestedDate.Asc>> SupplyPlanDetails;

Adding a PXProcessing data view like this will automatically add the “Process” and “Process All” buttons to the toolbar on the page. You can then specify the process delegate of the data view, which will be the method that triggers when the user clicks either of the two buttons.

SetProcessDelegate call

public GraphConstructor()
{
var filters = Filter.Current;
List<MAXXPOSupplyPlanDetail> selectedSupplyPlanDetails =
SupplyPlanDetails
.Select()
.RowCast<MAXXPOSupplyPlanDetail>()
.Where(x => x.Selected == true)
.ToList();
SupplyPlanDetails.SetProcessDelegate(
delegate (List<MAXXPOSupplyPlanDetail> list)
{
GeneratePurchaseOrders(filters, selectedSupplyPlanDetails);
});
}

In the above snippet, just replace “GraphConstructor” with whatever the name of your graph is that the screen is using. One nice additional benefit is that the call for GeneratePurchaseOrders will automatically run in a long running operation when it’s used as the process delegate for the processing data view, so there’s no need to create the long running operation separately (There won’t be any adverse effects if you do, doing so will simply use the existing long operation thread for the delegate if there is one). 

You’ll also just want to make sure that you have an unbound “Selected” field added to your custom DAC, like so

Selected checkbox

#region Selected
public abstract class selected : BqlBool.Field<selected> { }

lPXBool]
&PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
#endregion

Then the processing page will use whatever records are marked as selected and process those when the user clicks “Process”.

 

For anyone else looking for more info, I would definitely recommend going through the material for the T240 Processing Forms course here: T240 Processing Forms 2023 R2 (acumatica.com)


Hi @NicholasBova52 , 

Thanks for replying, I attempted to implement your suggestions however running into an issue where nothing is happening when clicking either of the processing buttons. Debugging the code the constructor looks to be working fine however the GeneratePurchaseOrders method never gets hit.


Hi @NicholasBova52 , 

Thanks for replying, I attempted to implement your suggestions however running into an issue where nothing is happening when clicking either of the processing buttons. Debugging the code the constructor looks to be working fine however the GeneratePurchaseOrders method never gets hit.

After looking at the code again, I think there is a mistake I made in the setup for the processing delegate. We should be passing the list variable of MAXXPOSupplyPlanDetail to the method GeneratePurchaseOrders instead of the one selected from selectedSupplyPlanDetails. Since no records are selected at the time the constructor runs, the collection is empty and the process delegate method will not run.

Corrected code

public GraphConstructor()
{
SupplyPlanDetails.SetProcessDelegate(
delegate (List<MAXXPOSupplyPlanDetail> list)
{
GeneratePurchaseOrders(Filter.Current, list);
});
}

Give this a try and let me know if the method is still not triggering.


Thanks @NicholasBova52 ,

It was giving me a PX1008 error so I tweaked the code a smidge, to make it calm down.

            var filter = Filter.Current;
SupplyPlanDetails.SetProcessDelegate(
delegate (List<MAXXPOSupplyPlanDetail> list)
{
GeneratePurchaseOrders(filter, list);
});

The screen seems to be behaving how I expect.


Reply