package policy.rule;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
|
import foundation.dao.DataPackage;
|
import foundation.dao.DataSource;
|
import foundation.dao.DataWriter;
|
import foundation.data.entity.Entity;
|
import foundation.data.entity.EntitySet;
|
import foundation.data.object.DataObject;
|
import foundation.data.object.EntitySaver;
|
import foundation.persist.NamedSQL;
|
import policy.OrderLineType;
|
import policy.price.PolicyBucket;
|
|
public class OrderCalculator {
|
|
public static String Field_Header_Amt_List = "amt_list";
|
public static String Field_Header_Amt_AfterOnsite = "amt_after_onsite";
|
public static String Field_Header_Amt_Onsite= "amt_onsite";
|
public static String Field_Header_Amt_Rebate = "amt_rebate";
|
public static String Field_Header_Amt_Net = "amt_net";
|
|
public static String Field_Header_Amt_OnsiteQty= "amt_onsite_qty";
|
public static String Field_Header_Amt_OnsitePrice = "amt_onsite_price";
|
public static String Field_Header_Amt_OncePrice = "amt_once_price";
|
public static String Field_Header_Amt_OnceQty = "amt_once_qty";
|
|
public static String Field_Header_Qty_Total= "qty_total";
|
public static String Field_Header_Qty_List = "qty_list";
|
public static String Field_Header_Qty_Discount = "qty_discount";
|
public static String Field_Header_Qty_Onsite = "qty_onsite";
|
public static String Field_Header_Qty_Rebate = "qty_rebate";
|
|
public static String Field_Detail_TypeCode = "type_code";
|
public static String Field_Detail_Price_List = "price_list";
|
public static String Field_Detail_Price_Working = "price";
|
public static String Field_Detail_SKU_ID = "sku_id";
|
public static String Field_Detail_Qty = "qty";
|
public static String Field_Detail_Qty_List = "qty_list";
|
public static String Field_Detail_Qty_Discount = "qty_discount";
|
public static String Field_Detail_Qty_Onsite = "qty_onsite";
|
public static String Field_Detail_Qty_Rebate = "qty_rebate";
|
public static String Field_Detail_Qty_Price = "qty_price";
|
public static String Field_Detail_Amt_List = "amt_List";
|
public static String Field_Detail_Amt_AfterDiscount = "amt_after_discount";
|
public static String Field_Detail_Amt_Discount = "amt_discount";
|
public static String Field_Detail_Amt_Rebate = "amt_rebate";
|
public static String Field_Detail_Amt_Net = "amt_net";
|
|
public static String Field_RebateDetail_Amt = "amt";
|
|
public static BigDecimal Rate_Ceiling = BigDecimal.valueOf(1000000);
|
|
protected DataWriter dataWriter;
|
protected Entity master;
|
protected EntitySet details;
|
protected EntitySet rebates;
|
protected boolean calculated;
|
|
// totalAmt(折扣前金额) = netAmt(折扣后金额) + onsiteAmt(即时折扣额) + rebateAmt(买赠池金额)
|
private BigDecimal listAmt;
|
private BigDecimal afterOnsiteAmt;
|
private BigDecimal onsiteAmt;
|
private BigDecimal rebateAmt;
|
private BigDecimal netAmt;
|
|
// totalQty(总数量) = standardQty(共价数量) + priceQty(优惠价数量) + rebateQty(买赠数量) + onsiteQty(即时买赠数量 + 一次性买赠数量)
|
private BigDecimal totalQty;
|
private BigDecimal listQty;
|
private BigDecimal discountQty;
|
private BigDecimal priceQty;
|
private BigDecimal onsiteQty;
|
private BigDecimal rebateQty;
|
|
// discountAmt = onsiteQtyAmt(即时买赠数量金额) + onsitePriceAmt (即时优惠价格金额) + oncePriceAmt (一次性优惠价格金额)
|
private BigDecimal onsiteQtyAmt;
|
private BigDecimal onsitePriceAmt;
|
private BigDecimal oncePriceAmt;
|
private OnsiteRecords onsiteRecords;
|
|
|
public OrderCalculator(DataWriter dataWriter) {
|
this.dataWriter = dataWriter;
|
this.calculated = false;
|
this.onsiteRecords = new OnsiteRecords();
|
}
|
|
public void exec() {
|
if (calculated) {
|
return;
|
}
|
|
synchronized (this) {
|
if (calculated) {
|
return;
|
}
|
|
//1. 计算明细行数量、单价、金额
|
doCalculateQtyAndAmt();
|
|
//2. 计算折扣池(票折)总金额
|
doCalculateHeaderRebate();
|
|
//3. 计算折扣池(票折)行金额
|
doCalculateLineRebate();
|
|
//4. 将计算结果设置到订单头上(不更新数据库)
|
setHeaderValues();
|
|
//5.
|
calculated = true;
|
}
|
}
|
|
private void doCalculateQtyAndAmt() {
|
//1. 初始化汇总数据
|
listAmt = BigDecimal.ZERO;
|
afterOnsiteAmt = BigDecimal.ZERO;
|
onsiteAmt = BigDecimal.ZERO;
|
netAmt = BigDecimal.ZERO;
|
|
onsiteQtyAmt = BigDecimal.ZERO;
|
onsitePriceAmt = BigDecimal.ZERO;
|
oncePriceAmt = BigDecimal.ZERO;
|
|
totalQty = BigDecimal.ZERO;
|
listQty = BigDecimal.ZERO;
|
discountQty = BigDecimal.ZERO;
|
priceQty = BigDecimal.ZERO;
|
onsiteQty = BigDecimal.ZERO;
|
rebateQty = BigDecimal.ZERO;
|
|
|
if (details == null || details.isEmpty()) {
|
return;
|
}
|
|
//2. 遍历行计算数量、金额
|
String customerId = master.getString("customer_id");
|
|
for (Entity entity: details) {
|
doCalculateOneLineQtyAndAmt(customerId, entity);
|
}
|
|
//3. 计算即时优惠金额
|
onsiteAmt = listAmt.subtract(afterOnsiteAmt);
|
|
//4. 计算折扣数量
|
discountQty = onsiteQty.add(rebateQty);
|
|
}
|
|
private void doCalculateOneLineQtyAndAmt(String customerId, Entity entity) {
|
OrderLineType lineType = OrderLineType.parse(entity.getString(Field_Detail_TypeCode));
|
|
//1. 重新获取价格
|
String skuId = entity.getString(Field_Detail_SKU_ID);
|
BigDecimal listPrice = getListPrice(customerId, skuId);
|
|
// BigDecimal listPrice = entity.getBigDecimal(Field_Detail_Price_List, BigDecimal.ZERO);
|
|
//2. 单价与数量
|
BigDecimal lineQty = entity.getBigDecimal(Field_Detail_Qty, BigDecimal.ZERO);
|
BigDecimal discountQty = entity.getBigDecimal("qty_discount", BigDecimal.ZERO);
|
BigDecimal workingPrice = entity.getBigDecimal(Field_Detail_Price_Working, BigDecimal.ZERO);
|
|
BigDecimal lineListAmt = listPrice.multiply(lineQty);
|
|
entity.set(Field_Detail_Price_List, listPrice);
|
entity.set(Field_Detail_Amt_List, lineListAmt);
|
|
//3. 根据类型进行汇总
|
if (OrderLineType.ListPrice == lineType) {
|
entity.set(Field_Detail_Amt_AfterDiscount, lineListAmt);
|
entity.set(Field_Detail_Amt_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, lineListAmt);
|
|
entity.set(Field_Detail_Qty_List, lineQty);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
listQty = listQty.add(lineQty);
|
afterOnsiteAmt = afterOnsiteAmt.add(lineListAmt);
|
}
|
else if (OrderLineType.OnsitePrice == lineType) {
|
BigDecimal lineAfterDiscountAmt = workingPrice.multiply(lineQty);
|
BigDecimal lineDiscountAmt = lineListAmt.subtract(lineAfterDiscountAmt);
|
|
entity.set(Field_Detail_Amt_AfterDiscount, lineAfterDiscountAmt);
|
entity.set(Field_Detail_Amt_Discount, lineDiscountAmt);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, lineAfterDiscountAmt);
|
entity.set(Field_Detail_Qty_List, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Discount, lineQty);
|
entity.set(Field_Detail_Qty_Price, discountQty);
|
entity.set(Field_Detail_Qty_Onsite, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
discountQty = discountQty.add(lineQty);
|
onsitePriceAmt = onsitePriceAmt.add(lineDiscountAmt);
|
afterOnsiteAmt = afterOnsiteAmt.add(lineAfterDiscountAmt);
|
priceQty = priceQty.add(lineQty);
|
}
|
else if (OrderLineType.OnsiteQty == lineType) {
|
entity.set(Field_Detail_Amt_AfterDiscount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Discount, lineListAmt);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, BigDecimal.ZERO);
|
|
entity.set(Field_Detail_Qty_List, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, lineQty);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
onsiteQtyAmt = onsiteQtyAmt.add(lineListAmt);
|
onsiteQty = onsiteQty.add(lineQty);
|
}
|
else if (OrderLineType.OncePrice == lineType) {
|
BigDecimal lineAfterDiscountAmt = workingPrice.multiply(lineQty);
|
BigDecimal lineDiscountAmt = lineListAmt.subtract(lineAfterDiscountAmt);
|
|
entity.set(Field_Detail_Amt_AfterDiscount, lineAfterDiscountAmt);
|
entity.set(Field_Detail_Amt_Discount, lineDiscountAmt);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, lineAfterDiscountAmt);
|
|
entity.set(Field_Detail_Qty_List, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
oncePriceAmt = oncePriceAmt.add(lineDiscountAmt);
|
afterOnsiteAmt = afterOnsiteAmt.add(lineAfterDiscountAmt);
|
}
|
else if (OrderLineType.OnceQty == lineType) {
|
entity.set(Field_Detail_Amt_AfterDiscount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Discount, lineListAmt);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, BigDecimal.ZERO);
|
|
entity.set(Field_Detail_Qty_List, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, lineQty);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
onsiteQtyAmt = onsiteQtyAmt.add(lineListAmt);
|
onsiteQty = onsiteQty.add(lineQty);
|
}
|
else if (OrderLineType.RebateQty == lineType) {
|
entity.set(Field_Detail_Amt_AfterDiscount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, BigDecimal.ZERO);
|
|
entity.set(Field_Detail_Qty_List, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Rebate, lineQty);
|
|
rebateQty = rebateQty.add(lineQty);
|
}
|
else if (OrderLineType.RebateAmt == lineType) {
|
|
}
|
else if (OrderLineType.Customize == lineType) {
|
listPrice = entity.getBigDecimal(Field_Detail_Price_Working, BigDecimal.ZERO);
|
lineListAmt = listPrice.multiply(lineQty);
|
|
entity.set(Field_Detail_Price_List, listPrice);
|
entity.set(Field_Detail_Amt_List, lineListAmt);
|
|
entity.set(Field_Detail_Amt_AfterDiscount, lineListAmt);
|
entity.set(Field_Detail_Amt_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Rebate, BigDecimal.ZERO);
|
entity.set(Field_Detail_Amt_Net, lineListAmt);
|
|
entity.set(Field_Detail_Qty_List, lineQty);
|
entity.set(Field_Detail_Qty_Discount, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Onsite, BigDecimal.ZERO);
|
entity.set(Field_Detail_Qty_Rebate, BigDecimal.ZERO);
|
|
listQty = listQty.add(lineQty);
|
afterOnsiteAmt = afterOnsiteAmt.add(lineListAmt);
|
}
|
|
totalQty = totalQty.add(lineQty);
|
listAmt = listAmt.add(lineListAmt);
|
onsiteAmt = listAmt.subtract(afterOnsiteAmt);
|
}
|
|
private void doCalculateHeaderRebate() {
|
//1.
|
rebateAmt = BigDecimal.ZERO;
|
|
if (rebates == null || rebates.isEmpty()) {
|
netAmt = afterOnsiteAmt.subtract(rebateAmt);
|
return;
|
}
|
|
for (Entity entity: rebates) {
|
BigDecimal value = entity.getBigDecimal(Field_RebateDetail_Amt, BigDecimal.ZERO);
|
rebateAmt = rebateAmt.add(value);
|
}
|
|
//2.
|
netAmt = afterOnsiteAmt.subtract(rebateAmt);
|
}
|
|
private void doCalculateLineRebate() {
|
if (afterOnsiteAmt.compareTo(BigDecimal.ZERO) == 0) {
|
return;
|
}
|
|
BigDecimal rate = divide(rebateAmt, afterOnsiteAmt);
|
|
//3. 将票折金额分摊到明细行
|
if (details == null || details.isEmpty()) {
|
return;
|
}
|
|
int max = details.size() - 1;
|
BigDecimal remainRebateAmt = rebateAmt;
|
BigDecimal lineAfterDiscountAmt = BigDecimal.ZERO;
|
BigDecimal lineRebateAmt = BigDecimal.ZERO;
|
BigDecimal lineNetAmt = BigDecimal.ZERO;
|
|
//3.1 除最后一行外,其他行进行分摊(RealAmt与分摊率相乘)
|
for (int i = 0; i < max; i++) {
|
Entity entity = details.getEntity(i);
|
lineAfterDiscountAmt = entity.getBigDecimal(Field_Detail_Amt_AfterDiscount, BigDecimal.ZERO);
|
lineRebateAmt = lineAfterDiscountAmt.multiply(rate).setScale(2, RoundingMode.HALF_UP);
|
lineNetAmt = lineAfterDiscountAmt.subtract(lineRebateAmt);
|
|
entity.set(Field_Detail_Amt_Rebate, lineRebateAmt);
|
entity.set(Field_Detail_Amt_Net, lineNetAmt);
|
|
remainRebateAmt = remainRebateAmt.subtract(lineRebateAmt);
|
|
if (OnsiteRecord.isOnsiteLine(entity)) {
|
onsiteRecords.load(entity);
|
}
|
}
|
|
//3.2 最后一行,用减法取剩余
|
Entity entity = details.getEntity(max);
|
lineAfterDiscountAmt = entity.getBigDecimal(Field_Detail_Amt_AfterDiscount, BigDecimal.ZERO);
|
lineRebateAmt = remainRebateAmt;
|
lineNetAmt = lineAfterDiscountAmt.subtract(lineRebateAmt);
|
|
entity.set(Field_Detail_Amt_Rebate, lineRebateAmt);
|
entity.set(Field_Detail_Amt_Net, lineNetAmt);
|
}
|
|
public boolean setDataPackage(DataPackage dataPackage, DataSource... dataSource) {
|
//1.
|
if (dataPackage == null) {
|
dataWriter.reportOneError("setDataPackage", "无法获取订单");
|
return false;
|
}
|
|
//2.
|
master = dataPackage.getMasterEntity(dataSource);
|
if (master == null || master.isEmpty()) {
|
dataWriter.reportOneError("setDataPackage", "订单头为空");
|
return false;
|
}
|
|
//3.
|
details = dataPackage.getItemEntitySet("so_order_detail", dataSource);
|
if (details == null || details.isEmpty()) {
|
dataWriter.reportOneError("setDataPackage", "订单明细为空");
|
return false;
|
}
|
|
//4.
|
rebates = dataPackage.getItemEntitySet("so_order_detail_rebate", dataSource);
|
return true;
|
}
|
|
private void setHeaderValues() {
|
master.set(Field_Header_Amt_List, listAmt);
|
master.set(Field_Header_Amt_AfterOnsite, afterOnsiteAmt);
|
master.set(Field_Header_Amt_Onsite, onsiteAmt);
|
master.set(Field_Header_Amt_Rebate, rebateAmt);
|
master.set(Field_Header_Amt_Net, netAmt);
|
|
master.set(Field_Header_Qty_Total, totalQty);
|
master.set(Field_Header_Qty_List, listQty);
|
master.set(Field_Header_Qty_Discount, discountQty);
|
master.set(Field_Header_Qty_Rebate, rebateQty);
|
master.set(Field_Header_Qty_Onsite, onsiteQty);
|
}
|
|
public boolean saveToDB(DataWriter dataWriter) throws Exception {
|
String orderId = master.getId();
|
String orderNo = master.getString("code");
|
|
//1. 保存订单头
|
DataObject dataObject = DataObject.getInstance("so_order");
|
EntitySaver saver = dataObject.createEntitySaver(master);
|
|
saver.set(Field_Header_Amt_List, listAmt);
|
saver.set(Field_Header_Amt_AfterOnsite, afterOnsiteAmt);
|
saver.set(Field_Header_Amt_Onsite, onsiteAmt);
|
saver.set(Field_Header_Amt_Rebate, rebateAmt);
|
saver.set(Field_Header_Amt_Net, netAmt);
|
|
saver.set(Field_Header_Qty_Total, totalQty);
|
saver.set(Field_Header_Qty_List, listQty);
|
saver.set(Field_Header_Qty_Discount, discountQty);
|
saver.set(Field_Header_Qty_Rebate, rebateQty);
|
saver.set(Field_Header_Qty_Onsite, onsiteQty);
|
|
saver.update();
|
|
//2. 保存订单明细
|
dataObject = DataObject.getInstance("so_order_detail");
|
|
for (Entity entity: details) {
|
saver = dataObject.createEntitySaver(entity);
|
|
saver.set(Field_Detail_Price_List, entity.getValue(Field_Detail_Price_List));
|
saver.set(Field_Detail_Amt_List, entity.getValue(Field_Detail_Amt_List));
|
saver.set(Field_Detail_Amt_AfterDiscount, entity.getValue(Field_Detail_Amt_AfterDiscount));
|
saver.set(Field_Detail_Amt_Discount, entity.getValue(Field_Detail_Amt_Discount));
|
saver.set(Field_Detail_Amt_Rebate, entity.getValue(Field_Detail_Amt_Rebate));
|
saver.set(Field_Detail_Amt_Net, entity.getValue(Field_Detail_Amt_Net));
|
|
saver.set(Field_Detail_Qty_List, entity.getValue(Field_Detail_Qty_List));
|
saver.set(Field_Detail_Qty_Discount, entity.getValue(Field_Detail_Qty_Discount));
|
saver.set(Field_Detail_Qty_Onsite, entity.getValue(Field_Detail_Qty_Onsite));
|
saver.set(Field_Detail_Qty_Rebate, entity.getValue(Field_Detail_Qty_Rebate));
|
|
saver.update();
|
|
// 一次性政策消耗
|
DataObject recordObject;
|
String recordId = entity.getString("record_id");
|
OrderLineType lineType = OrderLineType.parse(entity.getString(Field_Detail_TypeCode));
|
|
if (OrderLineType.OncePrice == lineType) {
|
recordObject = DataObject.getInstance("md_prod_price_detail");
|
}
|
else if (OrderLineType.OnceQty == lineType) {
|
recordObject = DataObject.getInstance("agm_record");
|
}
|
else {
|
continue;
|
}
|
|
Entity recordEntity = recordObject.getTableEntity(recordId);
|
String usedOrderId = recordEntity.getString("order_id");
|
|
if ((usedOrderId != null) && (!orderId.equals(usedOrderId))) {
|
dataWriter.reportOneError("onceRecordUsed", "一次型政策【" + recordEntity.getString("record_no")+ "】已使用");
|
return false;
|
}
|
|
saver = recordObject.createEntitySaver(recordId);
|
saver.set("order_id", orderId);
|
saver.set("order_no", orderNo);
|
saver.update();
|
}
|
|
//3. 刷新明细 SKU NCC ID
|
String id = master.getId();
|
|
NamedSQL namedSQL = NamedSQL.getInstance("updateOrderDetailNCId");
|
namedSQL.setParam("orderId", id);
|
namedSQL.execute();
|
|
return true;
|
}
|
|
public static BigDecimal divide(BigDecimal one, BigDecimal another, int scale) {
|
if (one == null) {
|
return BigDecimal.ZERO;
|
}
|
|
if (another == null || BigDecimal.ZERO.compareTo(another) == 0) {
|
if (one.compareTo(BigDecimal.ZERO) > 0) {
|
return Rate_Ceiling;
|
}
|
|
return BigDecimal.ZERO;
|
}
|
|
return one.divide(another, scale, RoundingMode.HALF_UP);
|
}
|
|
public static BigDecimal divide(BigDecimal one, BigDecimal another) {
|
return divide(one, another, 4);
|
}
|
|
private BigDecimal getListPrice(String customerId, String skuId) {
|
PolicyBucket policyBucket = PolicyBucket.getInstance();
|
BigDecimal result = policyBucket.getOnePrice(customerId, skuId);
|
|
return result;
|
}
|
|
public BigDecimal getListAmt() {
|
return listAmt;
|
}
|
|
public BigDecimal getAfterOnsiteAmt() {
|
return afterOnsiteAmt;
|
}
|
|
public BigDecimal getOnsiteAmt() {
|
return onsiteAmt;
|
}
|
|
public BigDecimal getRebateAmt() {
|
return rebateAmt;
|
}
|
|
public BigDecimal getNetAmt() {
|
return netAmt;
|
}
|
|
public BigDecimal getTotalQty() {
|
return totalQty;
|
}
|
|
public BigDecimal getListQty() {
|
return listQty;
|
}
|
|
public BigDecimal getDiscountQty() {
|
return discountQty;
|
}
|
|
public BigDecimal getOnsiteQty() {
|
return onsiteQty;
|
}
|
|
public BigDecimal getRebateQty() {
|
return rebateQty;
|
}
|
|
public BigDecimal getOnsitePriceAmt() {
|
return onsitePriceAmt;
|
}
|
|
public BigDecimal getOnsiteQtyAmt() {
|
return onsiteQtyAmt;
|
}
|
|
public BigDecimal getOncePriceAmt() {
|
return oncePriceAmt;
|
}
|
|
public OnsiteRecords getOnsiteRecords() {
|
return onsiteRecords;
|
}
|
|
}
|