Correct way to programmatically add entries to a composition.

My code worked until I upgraded to Cuba 6.6 (although it probably is my fault).

I have an entity called “WorksOrder” that includes a composition “WorksOrderIngredients” with “RawMaterials” that is populated according to a recipe, and quantities that is determined by the mass required - amongst others

The views are defined as per the manual, on the WorksOrderEdit screen the datasources are defined as:


<dsContext>
        <datasource id="worksOrderDs"
                    class="com.cernol.works.entity.WorksOrder"
                    view="worksOrder-view">
            <collectionDatasource id="worksOrderPackingsDs"
                                  property="worksOrderPackings"/>
            <collectionDatasource id="worksOrderIngredientsDs"
                                  property="worksOrderIngredients"/>
            <collectionDatasource id="worksOrderLablesDs"
                                  property="worksOrderLables"/>
        </datasource>
        <collectionDatasource id="problemListsDs"
                              class="com.cernol.works.entity.ProblemList"
                              refreshMode="NEVER"/>
    </dsContext>

Then in the WorksOrderEdit.java I defined a listener on the “productPicker” which calls the following code:


    public void resetIngredients() {

        BigDecimal ingredientCost = BigDecimal.ZERO;

        removeAllIngredients();

        if (getItem().getProduct() != null) {

            LoadContext<Product> loadContext = LoadContext.create(Product.class)
                    .setId(getItem().getProduct().getId())
                    .setView("product-view");

            Product myProduct = dataManager.load(loadContext);

            for (Formula formula : myProduct != null ? myProduct.getFormula() : null) {

                WorksOrderIngredient orderIngredient = metadata.create(WorksOrderIngredient.class);

                BigDecimal partsPer100 = formula.getPartsPer100();

                orderIngredient.setWorksOrder(getItem());
                orderIngredient.setSequenceNo(formula.getSequenceNo());
                orderIngredient.setRawMaterial(formula.getRawMaterial());
                orderIngredient.setMass(getItem().getMass()
                        .multiply(partsPer100)
                        .divide(BigDecimal.valueOf(100.0), 4, BigDecimal.ROUND_HALF_DOWN));


                BigDecimal currentCost = stockItemService.getPointInTimeCost(
                        orderIngredient.getRawMaterial().getId(),
                        getItem().getDocumentOn());

                orderIngredient.setKgCost(currentCost);

                BigDecimal onhandQuantity = stockItemService.
                        getPointInTimeQuantity(orderIngredient.getRawMaterial().getId(), getItem().getDocumentOn());

                if (onhandQuantity.compareTo(orderIngredient.getMass()) < 0) {
                    ProblemList problem = new ProblemList();
                    problem.setParent(getItem().getId());
                    problem.setDescription("Not enough stock: " +
                            orderIngredient.getRawMaterial().getInstanceName() +
                            "(" +
                            onhandQuantity +
                            " left)");

                    problemListsDs.addItem(problem);
                }

                worksOrderIngredientsDs.addItem(orderIngredient);
 //               worksOrderIngredientsDs.includeItem(orderIngredient);

                ingredientCost = ingredientCost.add(orderIngredient.getLineCost());
            }
        }

        getItem().setRawMaterialCost(ingredientCost);
    }

    private void removeAllIngredients() {
        for (WorksOrderIngredient worksOrderIngredient : new ArrayList<>(worksOrderIngredientsDs.getItems())) {
            worksOrderIngredientsDs.removeItem(worksOrderIngredient);
        }
    }

After the screen is complete (I use the extendedEditWindowActions) and you Save it - the data is written to the DB, but if you then “Save and Exit” duplicates the Ingredients…

If I just “Save and exit” and goes back, everything is fine.

If I use worksOrderIngredientsDs.includeItem instead of addItem I get a “During synchronization a new object was found through a relationship that was not marked cascade PERSIST” which points to a WorksOrderIngredient item… although my views are good as far as I can establish…

Any pointers would be appreciated.

Hi,

I couldn’t reproduce your problem on the test project (see attached).

The following code does not lead to any issues on saving:

public void createProgrammatically() {
    for (OrderLine line : new ArrayList<>(linesDs.getItems())) {
        linesDs.removeItem(line);
    }

    List<Product> products = dataManager.loadList(LoadContext.create(Product.class).setQuery(
            LoadContext.createQuery("select p from sales$Product p")
    ));
    for (Product product : products) {
        OrderLine line = metadata.create(OrderLine.class);
        line.setProduct(product);
        line.setQuantity(BigDecimal.ONE);
        line.setOrder(getItem());
        linesDs.addItem(line);
    }
}

sales.zip (117.4K)