Skip to content

Link Free Item Lines of a Sales Return to the Invoice ​

This Groovy script fills the Source Line Id of the free item lines on a sales return. Each free line ends up pointing at the matching free line of the sales invoice the return was made from.

Recommended pattern

This script is not tied to any customer's setup. It takes no parameters, so you can paste it as it is.

Class nameEASetSourceLineIDForFreeItemInSalesReturn
ScriptEASetSourceLineIDForFreeItemInSalesReturn.groovy
Runs onSales Return, on Pre Update Calculated Fields. It works on any sales document whose From Document is another sales document.
UsesNo parameters. Leave Parameter 1 empty.
Shipped asSource to paste. It is not built into the system.

When to use it ​

On a sales invoice, a free item hangs under the line it was given with: buy two printers, get one ink cartridge free. When the customer returns the printers and the cartridge, the printer lines of the return point back at the invoice's printer line through their Source Line Id. The free cartridge line often does not. It was added under the returned printer line, and nothing told it which invoice line it stands for.

A free line with no source line cannot be traced back to the invoice. Reports that compare returns with invoices miss it, and so does any flow that relies on the link. This script repairs the link on every save.

How it works ​

  1. It needs a From Document. If the return has no From Document, or the From Document is not a sales document, the script does nothing.
  2. It looks only at free lines that are not linked yet: lines with Free Item ticked and an empty Source Line Id. Every other line is left alone.
  3. It finds the line the free item belongs to on the return. A free line that does not belong to another line is skipped.
  4. It follows that line to the invoice. The line's Source Line Id points at a line of the From Document.
  5. It picks the free line of the same item that belongs to that invoice line, and copies its id into the free line's Source Line Id. If there is no such line, the free line stays as it was.

Setting it up ​

  1. Open Administration → Display Customization → Entity Flow and create a flow with Target Type set to Sales Return.
  2. In the Details grid, add a line with the action Pre Update Calculated Fields, and paste the whole script from the file above into Groovy Script. Leave Class Name empty: it is filled in when you save.
  3. Leave every parameter empty. After saving, Parameter 1 shows the title Not used - leave empty.
  4. Save the flow. Class Name now shows com.namasoft.modules.supplychain.domain.utils.EASetSourceLineIDForFreeItemInSalesReturn.

Example ​

An invoice has two lines: 2 printers, and under them 1 free ink cartridge. A return is made from that invoice with 1 printer linked to the invoice's printer line, and under it 1 free cartridge with an empty Source Line Id. When the return is saved, the cartridge line's Source Line Id becomes the id of the invoice's cartridge line. A copy of the same return saved without the flow keeps the cartridge line unlinked.

The script ​

groovy
package com.namasoft.modules.supplychain.domain.utils

import com.namasoft.common.utilities.ObjectChecker
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.generic.GenericReference
import com.namasoft.infra.domainbase.util.DetailLineUtil
import com.namasoft.infra.domainbase.util.Result
import com.namasoft.modules.supplychain.domain.details.BasicSCDocumentLine
import com.namasoft.modules.supplychain.domain.details.SalesLine
import com.namasoft.modules.supplychain.domain.entities.SalesDocument

public class EASetSourceLineIDForFreeItemInSalesReturn implements EntityAction<SalesDocument> {
    @Override
    Result doAction(SalesDocument doc, LongTextDF... parameters) {
        Result result = Result.createAccumulatingResult();
        SalesDocument fromDoc = GenericReference.toRealOrNull(doc.getFromDoc(), SalesDocument.class);
        if (fromDoc == null)
            return result;
        for (SalesLine line : doc.getDetails()) {
            if (ObjectChecker.isNotEmptyOrNull(line.getSourceLineId()) || BooleanDF.isFalse(line.getFreeLine()))
                continue;
            SalesLine masterDocLine = DetailLineUtil.findLineWithId(doc.getDetails(), line.getMasterRowId());
            if (masterDocLine == null)
                continue;

            List<BasicSCDocumentLine> fromDocLines = fromDoc.getDetails();
            BasicSCDocumentLine fromDocLine = fromDocLines.stream().filter(l -> ObjectChecker.areEqual(masterDocLine.getSourceLineId(), l.getMasterRowId()) && ObjectChecker.areEqual(line.fetchItem(), l.fetchItem())).findFirst().orElse(null);
            if (fromDocLine != null)
                line.setSourceLineId(fromDocLine.getId())
        }
        return result;
    }

    @Override
    String describe() {
        return "Sales return (or any sales document made from another sales document): a free item line that has no source line id gets one. " +
                "The script takes the line the free line belongs to, finds that line's source line in the from-doc, and links the free line to the free line " +
                "of the same item that belongs to that source line. Lines that are not free, or are already linked, are left as they are. Parameter 1 is not used.";
    }

    @Override
    List<String> columnNames() {
        return Arrays.asList("Not used - leave empty");
    }
}