Two Composite relations

In the quick start videos, it shows how calculate the amount of an order. However, the price was an attribute of the product entity. What if I have an entity called Price Type and another called price which is composite of PriceType (Same like Order & OrderLine) PriceType being Order and Price being OrderLine. However the Customer has a pricetype when being created. So how can I set the price in the OrderLine from the Price Entity based on the PriceType of the Customer.
I have tried a lot of things, but many of them failed or gave wrong results.

I really need your help

Thanks in advance,
KH

Hi Karim,

The solution can be as follows.

  • Add PriceType and Price entities as you described.

  • Make references from Customer to PriceType and from OrderLine to Price.

  • In OrderLineEdit, inject priceField and initialize its LookupAction in postInit() method to pass PriceType instance to the prices lookup screen:


public class OrderLineEdit extends AbstractEditor<OrderLine> {

    @Named("fieldGroup.price")
    private PickerField priceField;

    @Override
    protected void postInit() {
        Customer customer = getItem().getOrder().getCustomer();
        if (customer != null) {
            PickerField.LookupAction lookupAction = (PickerField.LookupAction) priceField.getActionNN(PickerField.LookupAction.NAME);
            lookupAction.setLookupScreenParams(ParamsMap.of("priceType", customer.getPriceType()));
        }
    }
}
  • In PriceBrowse, which is used as a lookup screen for selecting prices, change the datasource query depending on the passed PriceType:

public class PriceBrowse extends AbstractLookup {

    @WindowParam
    private PriceType priceType;

    @Inject
    private CollectionDatasource<Price, UUID> pricesDs;

    @Override
    public void init(Map<String, Object> params) {
        if (priceType != null) {
            pricesDs.setQuery("select e from sales$Price e where e.priceType.id = :param$priceType");
        }
    }
}
  • Now when a user selects a Customer for an Order, this Customer’s PriceType will be used to select a Price for all OrderLines.

See the whole sample project attached.

sales.zip (124.7K)

Hi Konstantin,

I am really grateful for all your help on this question and the other one as well. I just have a couple of questions. You said that I have to relate the price in OrderLine with Price table, but the thing is, that’s not part of the design.

So I thought of doing it as such.

in OrderLine.edit


package com.company.sales.web.orderline;

import com.company.sales.entity.*;
import com.haulmont.cuba.gui.components.AbstractEditor;
import com.haulmont.cuba.gui.data.CollectionDatasource;

import javax.inject.Inject;
import javax.swing.*;
import java.math.BigDecimal;
import java.util.Map;
import java.util.UUID;

public class OrderLineEdit extends AbstractEditor<OrderLine> {
    @Inject
    private CollectionDatasource<Price, UUID> pricesDs;

    private BigDecimal getPrice(Product product, PriceType priceType){
        BigDecimal tmp = BigDecimal.ZERO;
        for(Price price : pricesDs.getItems()){
            if(price.getPricetype() == priceType && price.getProduct() == product){
                tmp = price.getPrice();
                break;
            }
        }
        return tmp;
    }

    @Override
    public void init(Map<String, Object> params) {
        pricesDs.refresh();
    }

    @Override
    protected void postInit() {
        PriceType priceType = null;
        try{
            priceType = getItem().getOrder().getCustomer().getPricetype();
        }
        catch (NullPointerException e){
            throw new NullPointerException("Customer Field is Empty");
        }

        BigDecimal price = getPrice(getItem().getProduct(), priceType);
        getItem().setPrice(price);
    }
}

But it gives an Exception error in priceType = getItem().getOrder().getCustomer().getPricetype();

Error:
IllegalStateException: Cannot get unfetched attribute [pricetype] from detached object com.company.sales.entity.Price-942a6f66-9fb5-db8c-b285-b5fe4bdd9ae1 [detached].

Can you tell me how can I solve this problem?

Thanks

Hi Karim,

generally this IllegalStateException comes from the fact that the view that you are using does not include certain elements of the entity graph you try to access. In this case it will probably be the pricetype attribute of the customer entity. Just look at the OrderLine view you created.

@the haulmont people: Would it be possible to get a “better to understand” exception with a reference to the view ? I oftentimes saw that create some kind of confusion around this exception / problem… That would be cool!

Bye,
Mario

Thank you so much Mario
I really appreciate your response. It really helped me understand a lot of things and solve several problems that I had. However, there is something that I don’t really get. I add another collection data source in the OrderLineEdit which has the customerwithPriceType. however when I try to get the pricetype from the new collectiondatasource, it returns null. I think the problem is that the Customer that I pass to my getPriceType function is a detached object. At least, that’s what was shown when I tried to display the Cusomter that I got. It said [detached] at the end. Check my code below.


package com.company.sales.web.orderline;

import com.company.sales.entity.*;
import com.haulmont.cuba.gui.components.AbstractEditor;
import com.haulmont.cuba.gui.components.PickerField;
import com.haulmont.cuba.gui.data.CollectionDatasource;

import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.*;
import java.math.BigDecimal;
import java.util.Map;
import java.util.UUID;

public class OrderLineEdit extends AbstractEditor<OrderLine> {
    @Named("fieldGroup.product")
    private PickerField productField;

    @Inject
    private CollectionDatasource<Customer, UUID> customersDs;
    @Inject
    private CollectionDatasource<PriceType, UUID> priceTypesDs;
    @Inject
    private CollectionDatasource<Product, UUID> productsDs;

    private Customer custtmp = new Customer();

    private PriceType getPriceType(Customer customer){
        PriceType x = null;
        for(Customer tmp : customersDs.getItems()){
            if(tmp.getId() == customer.getId()){
                x = tmp.getPricetype();
                break;
            }
        }
        return x;
    }

    private BigDecimal getPrice(Product product, PriceType priceType){
        BigDecimal tmp = BigDecimal.ZERO;
        for(Price price : priceType.getPrice()){
            if(price.getProduct() == product){
                tmp = price.getPrice();
                break;
            }
        }
        return tmp;
    }

    @Override
    public void init(Map<String, Object> params) {
        productField.addLookupAction();
        productField.addOpenAction();

        customersDs.refresh();
        priceTypesDs.refresh();

    }
    @Override
    protected void postInit() {
        custtmp = getItem().getOrder().getCustomer(); // Detached???
        JOptionPane.showMessageDialog(null, custtmp);

        productsDs.addCollectionChangeListener(e -> setPrice());
    }

    private void setPrice() {
        JOptionPane.showMessageDialog(null, custtmp);

        PriceType priceType = null;

        try{
            priceType = getPriceType(custtmp);
        }
        catch (NullPointerException e){
            throw new NullPointerException("Customer Field is Empty");
        }

        JOptionPane.showMessageDialog(null, priceType);  // is null, not sure why??!!!

        //BigDecimal price = getPrice(getItem().getProduct(), priceType);
        //getItem().setPrice(price);
    }
}

Could you please tell me how to solve this problem?

So you need the PriceType of the Customer of the Order of the OrderLine displayed in the editor screen.

Then just add this branch to the view which is used in the datasource of the screen, in this case it is orderLine-with-product:


    <view class="com.company.sales.entity.OrderLine"
          name="orderLine-with-product">
        <property name="product"
                  view="_local"/>
        <property name="quantity"/>
        <property name="price"
                  view="_minimal"/>
        <property name="order"
                  view="_minimal">
            <property name="customer"
                      view="_minimal">
                <property name="priceType"
                          view="_minimal"/>
            </property>
        </property>
    </view>

See screenshot for how it looks in Studio.

view

Would it be possible to get a “better to understand” exception with a reference to the view ?

This is now a default message for EclipseLink. We’ll think about making it more clear for our case. Pointing to a specific view is hardly possible though.

Yeah you were correct. All I was missing was that I need to add these attributes in my view.
Thanks, it’s working fine now.