allow DocActions to be selected in a more flexible way
Description
Environment
None
relates to
Activity
Show:
Carlos Ruiz October 15, 2018 at 9:23 AM
Thanks @redhuan d. oon
With commit 8276d6b is possible now to add a Reactivate option after Closed.
Note that valid options per each status are still restricted by DocumentEngine.getActionOptions - that sounds safe as not any change can be valid.
Sample of adding a Reactivate option in closed Sales Order that actually calls reopen it:
@Override
protected void doHandleEvent(Event event) {
String type = event.getTopic();
if (IEventTopics.DOCACTION.equals(type)) {
DocActionEventData eventData = getEventData(event);
if (eventData.po instanceof MOrder) {
MOrder order = (MOrder) eventData.po;
// when the order is closed
if (order.getDocStatus().equals(DocumentEngine.STATUS_Closed)) {
if (! eventData.options.contains(DocumentEngine.ACTION_ReActivate)) {
// add Reactivate option to the list
eventData.options.set(eventData.indexObj.getAndIncrement(), DocumentEngine.ACTION_ReActivate);
// set the default option to Reactivate
eventData.docAction.set(0, DocumentEngine.ACTION_ReActivate);
}
}
}
} else if (IEventTopics.DOC_BEFORE_REACTIVATE.equals(type)) {
PO po = getPO(event);
if (po instanceof MOrder) {
MOrder order = (MOrder) po;
if (MOrder.DOCSTATUS_Closed.equals(order.getDocStatus())) {
order.reopenIt();
}
}
}
}
@Override
protected void initialize() {
registerTableEvent(IEventTopics.DOCACTION, MOrder.Table_Name);
registerTableEvent(IEventTopics.DOC_BEFORE_REACTIVATE, MOrder.Table_Name);
}
Carlos Ruiz October 15, 2018 at 8:37 AM
Bring comment from @redhuan d. oon at IDEMPIERE-3729
@Hiep Lq brought my attention to https://idempiere.atlassian.net/browse/IDEMPIERE-3599. It seems brilliant in trying to solve the same issue. But i can see in DocumentEngine, Line 968, there is a hard stop (return 0):
// Closed, Voided, REversed .. CL/VO/RE
else if (docStatus.equals(DocumentEngine.STATUS_Closed)
|| docStatus.equals(DocumentEngine.STATUS_Voided)
|| docStatus.equals(DocumentEngine.STATUS_Reversed))
return 0;
Carlos Ruiz January 8, 2018 at 9:04 PMEdited
Committed as AbstractEventHandler
example of extending:
add Prepare to Inventory Move
protected void initialize() {
registerTableEvent(IEventTopics.DOCACTION, MMovement.Table_Name);
}
protected void doHandleEvent(Event event) {
String type = event.getTopic();
if (type.equals(IEventTopics.DOCACTION)) {
DocActionEventData eventData = getEventData(event);
if (eventData.po instanceof MMovement) {
MMovement movement = (MMovement) eventData.po;
// when the movement document is in Draft status
if (movement.getDocStatus().equals(DocumentEngine.STATUS_Drafted)) {
if (!eventData.options.contains(DocumentEngine.ACTION_Prepare)) {
// add Prepare option to the list
eventData.options.set(eventData.indexObj.getAndIncrement(), DocumentEngine.ACTION_Prepare);
// set the default option to Prepare
eventData.docAction.set(0, DocumentEngine.ACTION_Prepare);
}
}
}
}
}
do not allow to complete orders if not printed
protected void initialize() {
registerTableEvent(IEventTopics.DOCACTION, MOrder.Table_Name);
}
protected void doHandleEvent(Event event) {
String type = event.getTopic();
if (type.equals(IEventTopics.DOCACTION)) {
DocActionEventData eventData = getEventData(event);
if (eventData.po instanceof MOrder) {
MOrder order = (MOrder) eventData.po;
// when the order is draft or in progress
if (order.getDocStatus().equals(DocumentEngine.STATUS_Drafted) || order.getDocStatus().equals(DocumentEngine.STATUS_InProgress)) {
// and the order is not printed yet
if (! order.isPrinted()) {
if (eventData.options.contains(DocumentEngine.ACTION_Complete)) {
// then do not allow to complete
eventData.options.remove(DocumentEngine.ACTION_Complete);
eventData.indexObj.decrementAndGet();
}
// and set the default option to Prepare
eventData.docAction.set(0, DocumentEngine.ACTION_Prepare);
}
}
}
}
}
Fixed
Details
Details
Assignee
Carlos Ruiz
Carlos RuizReporter
Thomas Bayen
Thomas BayenPriority
Created January 6, 2018 at 2:50 PM
Updated December 1, 2018 at 12:02 PM
Resolved October 15, 2018 at 9:23 AM
Up to now the document actions that can be selected are hardcoded. You can set access rights dependent on the document type (and the user role). But you can not set different actions dependent on the window or the previous document state or on whatever circumstances.
Use cases:
I want that a document can be completed only after it is printed. That means to look into IsPrinted (this is a bad example because IsPrinted is not maintained in iDempiere but it is just an example)
Our workflow says that a document is drafted. When we want to deliver it is prepared. When it is delivered it should be completed. That means a drafted document is never allowed to be completed. If we do that accidentally we get a mess in our business processes.
Other users want to Prepare an Inventory Move. Thats not possibel actually.
The action selection is hardcoded in DocumentEngine.getValidActions()
I talked with Carlos and we discussed two approaches:
Make it configurable by an event plugin.
Use a dynamic validation for that.
Because the second was my idea I want to repeat what I already wrote about that:
_The DocAction is a field. And a field has a field definition record. And the field definition record contains a configuration for a dynamic validation. This gives a list of things as a result. You compare this list with what DocumentEngine.getValidActions says and use only the entries that are in both lists. That means the dymamic validator allows to filter the standard list of actions.
The dynamic validator allows to use sql for some magic. And if I am not wrong it allows even to use context variables to change behaviour based on the window, the user or whatever._
My solution (dynamic validator) seems straightforward for me. It can be used like any other fields dynamic validators and can be used in core. The plugin approach is a bit more flexible.
A big difference is that a plugin allows to add additional actions. The validator only filters the given. Or do you see a better approach? Do we need to do both things? Is there really a need to add additional actions? Can we give the validator the possibility to not only filter but set the whole list?
(this belongs to FREIBIER-024)