Last active
June 16, 2022 19:30
-
-
Save anesvijskij/7b671f4a21ec6c8f564162a94231dbca to your computer and use it in GitHub Desktop.
Code sample for ARInvoiceEntry workflow customization in 2022R1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// - Modify order of existing composite/parent steps (pending email, pending print) | |
// - Add additional state (pending review) | |
// - Trigger events by workflow on field changes (subscribe on changing amount field – trigger back to pending review state) | |
using System; | |
using PX.Common; | |
using PX.Data; | |
using PX.Data.WorkflowAPI; | |
using PX.Objects.AR; | |
namespace MyProject | |
{ | |
using static BoundedTo<ARInvoiceEntry, ARInvoice>; | |
/// <summary> | |
/// The DAC extension with the new fields | |
/// </summary> | |
public class ARInvoice_ReviewExtension : PXCacheExtension<ARInvoice> | |
{ | |
#region UsrReviewed | |
/// <summary> | |
/// The additional DAC field that indicates whether the invoice is reviewed | |
/// </summary> | |
public abstract class usrReviewed : PX.Data.BQL.BqlBool.Field<usrReviewed> | |
{ | |
} | |
[PXDBBool] | |
[PXDefault(false)] | |
[PXUIField(DisplayName = "Reviewed")] | |
public virtual Boolean? UsrReviewed { get; set; } | |
#endregion | |
// The new document status | |
#region PendingReview Status | |
public const string PendingReview = "I"; // 'R' is already used for CreditHold | |
public class pendingReview : PX.Data.BQL.BqlString.Constant<pendingReview> | |
{ | |
public pendingReview() : base(PendingReview) | |
{ | |
} | |
} | |
#endregion | |
} | |
/// <summary> | |
/// A class that represents a union of two DAC properties | |
/// An event will be fired if any of the two properties (CuryOrigDocAmt 'Detail Total' OR CuryOrigDiscAmt 'Cash Discount') are changed | |
/// </summary> | |
public class OnUpdateCuryOrig : TypeArrayOf<IBqlField> | |
.FilledWith<ARInvoice.curyOrigDocAmt, ARInvoice.curyOrigDiscAmt> | |
{ | |
} | |
// ReSharper disable once InconsistentNaming | |
// ReSharper disable once UnusedMember.Global | |
public class ARInvoiceEntry_ReviewWorkflowExtension : PXGraphExtension<ARInvoiceEntry_Workflow, ARInvoiceEntry> | |
{ | |
/// <summary> | |
/// The action that marks the document as reviewed | |
/// </summary> | |
public PXAction<ARInvoice> MarkReviewed; | |
[PXButton] | |
[PXUIField(DisplayName = "Mark as Reviewed")] | |
public virtual void markReviewed() | |
{ | |
} | |
/// <summary> | |
/// The additional condition that will be used in workflow | |
/// </summary> | |
public class Conditions : Condition.Pack | |
{ | |
/// <summary> | |
/// The condition is true if the UsrReviewed property equals True | |
/// </summary> | |
public Condition IsInspected => GetOrCreate(b => b.FromBql< | |
ARInvoice_ReviewExtension.usrReviewed.IsEqual<True>>()); | |
} | |
/// <summary> | |
/// Override default screen configuration | |
/// </summary> | |
/// <param name="config"></param> | |
public override void Configure(PXScreenConfiguration config) | |
{ | |
Configure(config.GetScreenConfigurationContext<ARInvoiceEntry, ARInvoice>()); | |
} | |
private void Configure(WorkflowContext<ARInvoiceEntry, ARInvoice> context) | |
{ | |
// Get the 'Process' category to add the new action to it | |
var processingCategory = context.Categories.Get(ARInvoiceEntry_Workflow.CategoryID.Processing); | |
// Get the conditions pack to use it in the workflow configuration | |
var conditions = context.Conditions.GetPack<Conditions>(); | |
// Include the MarkReviewed action into the screen configuration | |
var markReviewedAction = context.ActionDefinitions.CreateExisting<ARInvoiceEntry_ReviewWorkflowExtension>( | |
extension => extension.MarkReviewed, act => act | |
// Add the MarkReviewed action to the 'Process' category | |
.WithCategory(processingCategory) | |
// Update the usrReviewed field when the MarkReviewed action is executed | |
.WithFieldAssignments(fields => | |
fields.Add<ARInvoice_ReviewExtension.usrReviewed>(v => | |
v.SetFromValue(true)))); | |
// Create a new workflow event handler named OnUpdateCuryOrigHandler that will be subscribed to change | |
// of the CuryOrigDocAmt OR CuryOrigDiscAmt fields | |
var onUpdateCuryOrigHandler = context.WorkflowEventHandlers.Create(config => | |
config.WithTargetOf<ARInvoice>().OfFieldsUpdated<OnUpdateCuryOrig>() | |
.IsNew("OnUpdateCuryOrigHandler") | |
.UsesTargetAsPrimaryEntity() | |
// when one of the amount fields changes, the usrReviewed field is assigned FALSE | |
.WithFieldAssignments(fields => fields.Add<ARInvoice_ReviewExtension.usrReviewed>(v => | |
v.SetFromValue(false)))); | |
// Update the screen configuration of the ARInvoiceEntry graph | |
context.UpdateScreenConfigurationFor(screen => | |
// Update the default ARInvoiceEntry workflow | |
screen.UpdateDefaultFlow(flow => | |
// Modify workflow states | |
flow.WithFlowStates(states => | |
// Update the main sequence with the name 'HoldToBalance' | |
states.UpdateSequence<ARDocStatus.HoldToBalance>(sequence => | |
// Modify the sequence states | |
sequence.WithStates(sequenceStates => | |
{ | |
// Modify the 'pendingEmail' state by moving it forward, after the 'creditHold' state | |
sequenceStates.Update<ARDocStatus.pendingEmail>(state => | |
state.PlaceAfter<ARDocStatus.creditHold>()); | |
// Add the new 'pendingReview' state | |
sequenceStates.Add<ARInvoice_ReviewExtension.pendingReview>( | |
flowState => | |
{ | |
return flowState | |
// Make the new state 'skippable' when the document has already been inspected | |
.IsSkippedWhen(conditions.IsInspected) | |
// Place the new state after the 'pendingPrint' state | |
.PlaceAfter<ARDocStatus.pendingPrint>() | |
// Make the 'putOnHold' and 'markReviewed' actions available in this state | |
.WithActions(actions => | |
{ | |
actions.Add(g => g.putOnHold); | |
actions.Add(markReviewedAction, | |
config => config | |
.IsDuplicatedInToolbar() | |
.WithConnotation(ActionConnotation.Primary)); | |
}); | |
}); | |
}) | |
// Make the onUpdateCuryOrigHandler event handler is active in all child states of the sequence | |
.WithEventHandlers(handlers => handlers.Add(onUpdateCuryOrigHandler))) | |
) | |
// Modify transitions for the new state | |
.WithTransitions(transitions => | |
{ | |
// Add a transition from the 'pendingInspection' state to the next state in sequence when the 'MarkReviewed' action is selected | |
transitions.Add(source => | |
source.From<ARInvoice_ReviewExtension.pendingReview>().ToNext() // in fact, next state is 'Balanced' | |
.IsTriggeredOn(markReviewedAction)); | |
// Add a transition from the sequence (and this means that for all child states of the sequence) to the | |
// beginning of the sequence on fields changing to start the process of "searching" of the valid state again | |
transitions.Add(source => | |
source.From<ARDocStatus.HoldToBalance>().To<ARDocStatus.HoldToBalance>() | |
.IsTriggeredOn(onUpdateCuryOrigHandler)); | |
})) | |
// Add the new action to the screen configuration | |
.WithActions(actions => actions.Add(markReviewedAction)) | |
// Modify field states in the screen configuration | |
.WithFieldStates(fields => | |
{ | |
// Add the new 'PendingInspection' value to the Status field | |
fields.Add<ARInvoice.status>(field => | |
field.SetComboValue(ARInvoice_ReviewExtension.PendingReview, "On Review")); | |
// Make the new 'usrReviewed' field always disabled | |
fields.Add<ARInvoice_ReviewExtension.usrReviewed>(field => field.IsDisabledAlways()); | |
}) | |
// Add the new workflow event handler to the screen configuration | |
.WithHandlers(handlers => handlers.Add(onUpdateCuryOrigHandler))); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment