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; } }