使用 @DefaultValueCalculator
我们在细节里每行计算的方式并不是最好的。它至少有两个缺点。首先,用户可能希望改变某个产品的单价。其次,如果一个产品的价格发生变化,所有发票的金额也发生变化,这并不是我们想要的。
为了避免这些缺点,最好的方法就是在每行存储格该行的价格。接下来,我们在 Detail 类添加一个持久属性 pricePerUnit,并在使用 @DefaultValueCalculator 时,从 Product 中的价格去计算。以下可以看到我们所获得的效果:
计算初始值的逻辑将在 PricePerUnitCalculator 中,它只会从产品中读取价格。以下是计算器的代码:
package com.yourcompany.invoicing.calculators; // 在 calculators 包
import org.openxava.calculators.*;
import com.yourcompany.invoicing.model.*;
import lombok.*;
import static org.openxava.jpa.XPersistence.*; // 用于使用 getManager()
public class PricePerUnitCalculator implements ICalculator {
@Getter @Setter
int productNumber;
public Object calculate() throws Exception {
Product product = getManager() // XPersistence 的 getManager()
.find(Product.class, productNumber); // 寻找产品
return product.getPrice(); // 返回价格
}
}
接下请,在 Detail 类里添加 pricePerUnit 属性 :
@DefaultValueCalculator(
value=PricePerUnitCalculator.class, // 这个类会计算初始值
properties=@PropertyValue(
name="productNumber", // 计算器的 productNumber 属性由详细信息的 product.number 填充
from="product.number")
)
@Money
BigDecimal pricePerUnit; // 常规的持久属性
这样子,当用户选择一个产品时,该价格字段将使用产品的价格填充,但由于它是持久属性,用户可以更改它。未来,如果产品的价格发生变化,在详细信息中的单价不会改变。
这代表您必须调整金额计算属性:
@Money
@Depends("pricePerUnit, quantity") // pricePerUnit 而不是 product.number
public BigDecimal getAmount() {
if (pricePerUnit == null) return BigDecimal.ZERO; // pricePerUnit 而不是 product 和 product.getPrice()
return new BigDecimal(quantity).multiply(pricePerUnit); // pricePerUnit 而不是 product.getPrice()
}
getAmount() 使用 pricePerUnit 作为源而不是 product.price。
最后,我们必须编辑 CommercialDocument 实体,以在列表显示新属性:
@ElementCollection
@ListProperties("product.number, product.description, quantity, pricePerUnit, amount") // 添加 pricePerUnit
Collection<Detail> details;
现在可以使用订单和发票模块,并试试详细信息里的新方法。