Learn an Item's Default Locator From Stock Movements
These two Groovy scripts teach each Item where it lives. When a Stock Transfer or an Opening Stock Document is saved, the locator it put the item in becomes the item's default locator in that warehouse.
Recommended pattern
These scripts are not tied to any customer's setup. You can paste them as they are; they take no parameters.
| Class names | EALearnDefaultLocatorFromStockTransfer (stock transfer) and EALearnDefaultLocatorFromOpeningStock (opening stock) |
| Scripts | EALearnDefaultLocatorFromStockTransfer.groovy, EALearnDefaultLocatorFromOpeningStock.groovy |
| Runs on | Stock Transfer or Opening Stock Document, on the Post Commit event |
| Uses | No parameters. Leave Parameter 1 empty. |
| Shipped as | Source to paste. It is not built into the system. |
When to use it
In a warehouse whose Locator Usage is Required, every document line needs a locator. When a line leaves it empty and the warehouse has no default locator of its own, the system looks at the item: the line of the item's Details grid for that warehouse that is ticked as the default locator supplies it. Keeping those lines up to date by hand, for thousands of items, is not realistic.
These scripts keep them up to date from the stock movements themselves. Load the opening stock into its shelves, or move an item to a new shelf with a stock transfer, and from then on documents in that warehouse fill that shelf for the item by themselves.
How it works
For every line of the saved document:
- It finds the warehouse and locator the line put the item in. On a stock transfer that is the line's to warehouse and to locator. On an opening stock document it is the line's warehouse and locator.
- It finds the item's line for that warehouse in the item's Details grid.
- If that line already has the same locator, nothing is done for this document line.
- If it has another locator, the line is updated.
- If the item has no line for that warehouse, one is added.
- It sets the line to the warehouse and the locator, and ticks it as the default locator. The stock transfer script also ticks Desirable.
- It saves the item. The item goes through its normal save, so its own validations apply.
The scripts run on Post Commit, after the document itself is saved.
Use it where every line has a locator
The scripts copy the line's locator as it is. A line without a locator would leave the item's line for that warehouse with an empty locator. In a warehouse whose Locator Usage is Required, every line has one, so attach the flow to documents in such warehouses.
Setting it up
- Open Administration → Display Customization → Entity Flow and create a new flow.
- Set Target Type to Stock Transfer (or Opening Stock Document) and Target Action to Post Commit.
- In the Details grid, paste the matching script into Groovy Script. Leave Class Name empty: it is filled in when you save.
- Save the flow. For both document types, create one flow for each, each with its own script.
| Parameter | Title shown after saving | What to enter |
|---|---|---|
| Parameter 1 | Not used - leave empty | Nothing. |
Example
A new item is loaded with an opening stock document into warehouse WH1, locator L1. After the document is saved, the item's Details grid has a new line:
| Warehouse | Locator | Default locator | Desirable |
|---|---|---|---|
| WH1 | L1 | ✓ |
Later, a stock transfer moves part of the stock within WH1 from L1 to L2. After it is saved, the same line reads:
| Warehouse | Locator | Default locator | Desirable |
|---|---|---|---|
| WH1 | L2 | ✓ | ✓ |
Messages you may see
The scripts raise no messages of their own. If the item cannot be saved, the item's own validation messages are shown.
The script
The stock transfer script:
package com.namasoft.modules.supplychain.domain.utils.plugnplay.groovy
import com.namasoft.common.utilities.ObjectChecker
import com.namasoft.infra.domainbase.common.detail.EntityDetailLine
import com.namasoft.infra.domainbase.datafields.BooleanDF
import com.namasoft.infra.domainbase.datafields.LongTextDF
import com.namasoft.infra.domainbase.entity.base.EntityAction
import com.namasoft.infra.domainbase.entity.base.EntityMediator
import com.namasoft.infra.domainbase.util.Result
import com.namasoft.modules.supplychain.domain.details.ItemDimensionsDetail
import com.namasoft.modules.supplychain.domain.details.StockTransferLine
import com.namasoft.modules.supplychain.domain.entities.InvItem
import com.namasoft.modules.supplychain.domain.entities.StockTransfer
import com.namasoft.modules.supplychain.domain.valueobjects.ItemDimensions
import com.namasoft.modules.supplychain.domain.valueobjects.ItemDimensionsData
import com.namasoft.modules.supplychain.domain.valueobjects.ItemSpecificDimensions
public class EALearnDefaultLocatorFromStockTransfer implements EntityAction<StockTransfer> {
@Override
Result doAction(StockTransfer doc, LongTextDF... parameters) {
Result result = Result.createAccumulatingResult();
for (StockTransferLine l : doc.getDetails()) {
def item = l.getItem().getItem()
def detail = item.getDetails().stream().filter({
it.getItemDimensions() != null && it.getItemDimensions().getSpecificDimensions() != null && ObjectChecker.areEqual(it.getItemDimensions().getSpecificDimensions().getWarehouse(), l.getToWarehouse())
}).findFirst().orElse(null);
if (detail != null && ObjectChecker.areEqual(detail.getItemDimensions().getSpecificDimensions().getLocator(), l.getToLocator()))
continue;
item.startEditing().addToAccumulatingResult(result);
if (detail == null) {
detail = new ItemDimensionsDetail();
item.getDetails().add(detail);
detail.setItemDimensions(new ItemDimensions());
detail.getItemDimensions().setSpecificDimensions(new ItemSpecificDimensions());
detail.setData(new ItemDimensionsData());
} else {
detail = EntityDetailLine.findById(item.getDetails(), detail.getId());
}
detail.getItemDimensions().getSpecificDimensions().setWarehouse(l.getToWarehouse());
detail.getItemDimensions().getSpecificDimensions().setLocator(l.getToLocator());
detail.setDefaultLocator(BooleanDF.TRUE());
detail.getData().setDesirable(BooleanDF.TRUE());
EntityMediator.commitFromBusinessAction(item).addToAccumulatingResult(result);
}
return result;
}
@Override
String describe() {
return "Stock Transfer: for every line, records the line's to locator as the item's default locator in its to warehouse. " +
"The item's Details grid gets a line for that warehouse (or its existing line is updated) with the locator, " +
"the default locator box ticked and Desirable ticked, and the item is saved. Items whose line for that warehouse " +
"already has the same locator are not saved again. Attach it to the Post Commit event of Stock Transfer. Parameter 1 is not used.";
}
@Override
List<String> columnNames() {
return Arrays.asList("Not used - leave empty");
}
}The opening stock script:
package com.namasoft.modules.supplychain.domain.utils.plugnplay.groovy
import com.namasoft.common.utilities.ObjectChecker
import com.namasoft.infra.domainbase.common.detail.EntityDetailLine
import com.namasoft.infra.domainbase.datafields.BooleanDF
import com.namasoft.infra.domainbase.datafields.LongTextDF
import com.namasoft.infra.domainbase.entity.base.EntityAction
import com.namasoft.infra.domainbase.entity.base.EntityMediator
import com.namasoft.infra.domainbase.util.Result
import com.namasoft.modules.supplychain.domain.details.ItemDimensionsDetail
import com.namasoft.modules.supplychain.domain.details.OpeningStockLine
import com.namasoft.modules.supplychain.domain.entities.OpeningStockDocument
import com.namasoft.modules.supplychain.domain.valueobjects.ItemDimensions
import com.namasoft.modules.supplychain.domain.valueobjects.ItemDimensionsData
import com.namasoft.modules.supplychain.domain.valueobjects.ItemSpecificDimensions
public class EALearnDefaultLocatorFromOpeningStock implements EntityAction<OpeningStockDocument> {
@Override
Result doAction(OpeningStockDocument doc, LongTextDF... parameters) {
Result result = Result.createAccumulatingResult();
for (OpeningStockLine l : doc.getDetails()) {
def item = l.getItem().getItem()
def detail = item.getDetails().stream().filter({
it.getItemDimensions() != null && it.getItemDimensions().getSpecificDimensions() != null && ObjectChecker.areEqual(it.getItemDimensions().getSpecificDimensions().getWarehouse(), l.getSpecificDimensions().getWarehouse())
}).findFirst().orElse(null);
if (detail != null && ObjectChecker.areEqual(detail.getItemDimensions().getSpecificDimensions().getLocator(), l.getSpecificDimensions().getLocator()))
continue;
item.startEditing().addToAccumulatingResult(result);
if (detail == null) {
detail = new ItemDimensionsDetail();
item.getDetails().add(detail);
detail.setItemDimensions(new ItemDimensions());
detail.getItemDimensions().setSpecificDimensions(new ItemSpecificDimensions());
detail.setData(new ItemDimensionsData());
} else {
detail = EntityDetailLine.findById(item.getDetails(), detail.getId());
}
detail.getItemDimensions().getSpecificDimensions().setWarehouse(l.getSpecificDimensions().getWarehouse());
detail.getItemDimensions().getSpecificDimensions().setLocator(l.getSpecificDimensions().getLocator());
detail.setDefaultLocator(BooleanDF.TRUE());
EntityMediator.commitFromBusinessAction(item).addToAccumulatingResult(result);
}
return result;
}
@Override
String describe() {
return "Opening Stock Document: for every line, records the line's locator as the item's default locator in the line's warehouse. " +
"The item's Details grid gets a line for that warehouse (or its existing line is updated) with the locator and " +
"the default locator box ticked, and the item is saved. Items whose line for that warehouse already has the same " +
"locator are not saved again. Attach it to the Post Commit event of Opening Stock Document. Parameter 1 is not used.";
}
@Override
List<String> columnNames() {
return Arrays.asList("Not used - leave empty");
}
}Related
- EAGroovyAction: how Groovy scripts are attached to entity flows and task schedules.
- The Document Screen: how a document line picks its warehouse and locator.
- Understanding Inventory Items