fix:MCP成本计算规则调整

dev_1.0.2
jiangpeng 2026-06-03 18:03:27 +08:00
parent 671bbd5692
commit 5fb2faecdc
10 changed files with 171 additions and 38 deletions

View File

@ -244,7 +244,7 @@
<span class="stocking-product-meta__label">产品型号</span>
<span class="stocking-product-meta__value stocking-product-meta__value--strong">{{ item.model || '-' }}</span>
<el-button
v-if="item.vendorName && item.vendorName.startsWith('新华三')"
v-if="(item.vendorName && item.vendorName.startsWith('新华三')) || (item.productType != 1 && item.productType != 2)"
type="primary"
plain
icon="el-icon-plus"

View File

@ -1,13 +1,13 @@
<template>
<div>
<el-table v-loading="loading" :data="logList">
<el-table-column label="入库单号" align="center" prop="innerCode" />
<el-table-column label="产品编码" align="center" prop="productCode" />
<el-table-column label="入库单号" align="center" prop="innerCode" width="150" />
<el-table-column label="产品编码" align="center" prop="productCode" width="150" />
<el-table-column label="产品型号" align="center" prop="model" />
<el-table-column label="入库数量" align="center" prop="quantity" />
<el-table-column label="入库数量" align="center" prop="quantity" width="100" />
<el-table-column label="制造商" align="center" prop="vendorName" />
<el-table-column label="入库仓" align="center" prop="warehouseName" />
<el-table-column label="经办人" align="center" prop="createByName" />
<el-table-column label="入库仓" align="center" prop="warehouseName" width="120" />
<el-table-column label="经办人" align="center" prop="createByName" width="120" />
<el-table-column label="入库时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>

View File

@ -1,16 +1,16 @@
<template>
<div>
<el-table v-loading="loading" :data="logList">
<el-table-column label="出库单号" align="center" prop="outerCode" />
<el-table-column label="出库单号" align="center" prop="outerCode" width="150" />
<el-table-column label="合同编号" align="center" prop="orderCode" />
<el-table-column label="项目名称" align="center" prop="projectName" show-overflow-tooltip />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="出库状态" align="center" prop="outerStatus">
<el-table-column label="数量" align="center" prop="quantity" width="100" />
<el-table-column label="出库状态" align="center" prop="outerStatus" width="120">
<template slot-scope="scope">
<dict-tag :options="outerStatusOptions" :value="scope.row.outerStatus"/>
</template>
</el-table-column>
<el-table-column label="经办人" align="center" prop="createByName" />
<el-table-column label="经办人" align="center" prop="createByName" width="120" />
<el-table-column label="出库时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>

View File

@ -383,7 +383,7 @@ export default {
return;
}
this.form.inventoryInfoList=[]
let quantity = Number(this.form.quantity);
let quantity = Number(this.form.quantity) - Number(this.form.innerQuantity || 0);
let productSn = this.inputSn;
let snPrefix = "";
let snNumber = "";

View File

@ -2,7 +2,7 @@
<el-dialog :title="title" :close-on-click-modal="false" :visible.sync="visible" width="80%" append-to-body @close="handleClose">
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="120px">
<el-form-item label="采购单号" prop="purchaseNo">
<el-form-item label="采购单号123" prop="purchaseNo">
<el-input
v-model="queryParams.purchaseNo"
placeholder="请输入采购单号"
@ -55,7 +55,7 @@
<el-table-column label="单价" align="center" prop="price" width="100"/>
<el-table-column label="小计" align="center" prop="price" width="100">
<template slot-scope="scope">
{{ scope.row.quantity * scope.row.price}}
{{ ((Number(scope.row.quantity) || 0) * (Number(scope.row.price) || 0)).toFixed(2) }}
</template>
</el-table-column>
</el-table>
@ -129,6 +129,8 @@ export default {
watch: {
visible(val) {
if (val) {
this.queryParams.type = this.type;
this.queryParams.productCode = this.productCode;
this.getList();
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.sip.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderProductBindAmountDto {
/** 产品编码 */
private String productCode;
/** 成本含税 */
private BigDecimal costWithTax;
/** 成本不含税 */
private BigDecimal costWithoutTax;
/** 不含税收入 */
private BigDecimal amountWithoutTax;
/** 毛利 */
private BigDecimal grossProfit;
/** 毛利率 */
private BigDecimal grossProfitRate;
/** 订单备货状态true: 已备货完成false: 未备货完成) */
private Boolean orderStockingStatus;
}

View File

@ -0,0 +1,20 @@
package com.ruoyi.sip.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UnboundPurchaseOrderProductDto {
/** 合同编号 */
private String orderCode;
/** 产品编码 */
private String productCode;
/** 入库单号 */
private String innerCode;
}

View File

@ -12,11 +12,9 @@ import com.ruoyi.sip.domain.OmsReceivableBill;
import com.ruoyi.sip.domain.ProjectInfo;
import com.ruoyi.sip.domain.ProjectOrderInfo;
import com.ruoyi.sip.domain.ProjectProductInfo;
import com.ruoyi.sip.dto.OrderProductBindAmountDto;
import com.ruoyi.sip.llm.tools.support.AbstractMcpToolProvider;
import com.ruoyi.sip.mapper.InventoryInfoMapper;
import com.ruoyi.sip.mapper.OmsInventoryInnerMapper;
import com.ruoyi.sip.mapper.OmsPurchaseOrderMapper;
import com.ruoyi.sip.mapper.ProjectInfoMapper;
import com.ruoyi.sip.mapper.*;
import com.ruoyi.sip.service.IInventoryInfoService;
import com.ruoyi.sip.service.IInventoryOuterService;
import com.ruoyi.sip.service.IOmsReceivableBillService;
@ -43,7 +41,7 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
private static final String INDUSTRY_TYPE_DICT_TYPE = "bg_hysy";
private static final String INDUSTRY_TYPE_YYS_DICT_TYPE = "bg_yys";
private static final String PROJECT_STAGE_DICT_TYPE = "project_stage";
private static final String NOT_DELIVERED_TEXT = "未货完成";
private static final String NOT_DELIVERED_TEXT = "未货完成";
@Autowired
private IProjectOrderInfoService projectOrderInfoService;
@ -72,6 +70,9 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
@Autowired
private IOmsReceivableBillService omsReceivableBillService;
@Autowired
private ProjectOrderInfoMapper projectOrderInfoMapper;
@Override
protected String getToolName() {
return "project_order_info";
@ -195,11 +196,11 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
item.put("archiveTime", formatDateTime(orderInfo.getApproveTime()));
ProductGroupSummary softwareSummary = appendProductFields(item, "software", softwareList,
orderShipmentMap, Collections.emptyMap(), true);
orderShipmentMap, Collections.emptyMap(), true, orderInfo.getOrderCode());
ProductGroupSummary hardwareSummary = appendProductFields(item, "hardware", hardwareList,
orderShipmentMap, Collections.emptyMap(), true);
orderShipmentMap, Collections.emptyMap(), true, orderInfo.getOrderCode());
ProductGroupSummary serviceSummary = appendProductFields(item, "service", serviceList,
Collections.emptyMap(), serviceCostMap.getOrDefault(orderInfo.getOrderCode(), Collections.emptyMap()), false);
Collections.emptyMap(), serviceCostMap.getOrDefault(orderInfo.getOrderCode(), Collections.emptyMap()), false, orderInfo.getOrderCode());
BigDecimal orderAmountWithTax = defaultValue(orderInfo.getShipmentAmount());
BigDecimal discountAmountWithTax = softwareSummary.salesWithTax
@ -208,20 +209,21 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
BigDecimal discountAmountWithoutTax = softwareSummary.salesWithoutTax
.add(hardwareSummary.salesWithoutTax)
.add(serviceSummary.salesWithoutTax);
boolean allDelivered = softwareSummary.allDelivered && hardwareSummary.allDelivered && serviceSummary.allDelivered;
boolean allStocked = softwareSummary.allStocked && hardwareSummary.allStocked && serviceSummary.allStocked;
BigDecimal costWithTax = softwareSummary.costWithTax.add(hardwareSummary.costWithTax).add(serviceSummary.costWithTax);
BigDecimal costWithoutTax = softwareSummary.costWithoutTax.add(hardwareSummary.costWithoutTax).add(serviceSummary.costWithoutTax);
item.put("orderAmountWithTax", orderAmountWithTax);
item.put("discountedOrderAmountWithTax", discountAmountWithTax);
item.put("discountedOrderAmountWithoutTax", discountAmountWithoutTax);
item.put("costTotalWithTax", allDelivered ? costWithTax : NOT_DELIVERED_TEXT);
item.put("costTotalWithoutTax", allDelivered ? costWithoutTax : NOT_DELIVERED_TEXT);
item.put("costTotalWithTax", allStocked ? costWithTax : NOT_DELIVERED_TEXT);
item.put("costTotalWithoutTax", allStocked ? costWithoutTax : NOT_DELIVERED_TEXT);
if (allDelivered) {
BigDecimal grossProfit = discountAmountWithoutTax.subtract(costWithoutTax);
item.put("grossProfit", grossProfit);
item.put("grossProfitRate", percentage(grossProfit, costWithoutTax));
if (allStocked) {
BigDecimal totalGrossProfit = softwareSummary.grossProfit.add(hardwareSummary.grossProfit).add(serviceSummary.grossProfit);
BigDecimal totalAmountWithoutTax = softwareSummary.amountWithoutTax.add(hardwareSummary.amountWithoutTax).add(serviceSummary.amountWithoutTax);
item.put("grossProfit", totalGrossProfit);
item.put("grossProfitRate", percentage(totalGrossProfit, totalAmountWithoutTax));
} else {
item.put("grossProfit", NOT_DELIVERED_TEXT);
item.put("grossProfitRate", NOT_DELIVERED_TEXT);
@ -501,7 +503,8 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
List<ProjectProductInfo> productInfos,
Map<String, ProductShipmentSummary> shipmentMap,
Map<String, ProductCostSummary> serviceCostMap,
boolean shipmentRequired) {
boolean shipmentRequired,
String orderCode) {
ProductGroupSummary summary = new ProductGroupSummary();
if (CollUtil.isEmpty(productInfos)) {
return summary;
@ -509,9 +512,29 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
for (int i = 0; i < productInfos.size(); i++) {
int index = i + 1;
ProjectProductInfo productInfo = productInfos.get(i);
ProductCostDisplay costDisplay = shipmentRequired
? buildShipmentProductCostDisplay(productInfo, shipmentMap.get(productInfo.getProductBomCode()))
: buildServiceProductCostDisplay(serviceCostMap.get(productInfo.getProductBomCode()));
OrderProductBindAmountDto bindAmountDto = projectOrderInfoMapper.selectOrderProductBindAmount(
orderCode, productInfo.getProductBomCode());
ProductCostDisplay costDisplay = new ProductCostDisplay();
if (bindAmountDto != null) {
costDisplay.delivered = bindAmountDto.getOrderStockingStatus() != null && bindAmountDto.getOrderStockingStatus();
costDisplay.costWithTax = defaultValue(bindAmountDto.getCostWithTax());
costDisplay.costWithoutTax = defaultValue(bindAmountDto.getCostWithoutTax());
costDisplay.displayValue = costDisplay.costWithTax;
summary.amountWithoutTax = summary.amountWithoutTax.add(defaultValue(bindAmountDto.getAmountWithoutTax()));
summary.grossProfit = summary.grossProfit.add(defaultValue(bindAmountDto.getGrossProfit()));
summary.grossProfitRate = defaultValue(bindAmountDto.getGrossProfitRate());
summary.orderStockingStatus = bindAmountDto.getOrderStockingStatus();
if (bindAmountDto.getOrderStockingStatus() == null || !bindAmountDto.getOrderStockingStatus()) {
summary.allStocked = false;
}
} else {
costDisplay.delivered = false;
costDisplay.displayValue = NOT_DELIVERED_TEXT;
summary.allStocked = false;
}
item.put(prefix + "Code" + index, productInfo.getProductBomCode());
item.put(prefix + "Level2Type" + index, productInfo.getLevel2Type());
@ -520,6 +543,9 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
item.put(prefix + "SalesAmount" + index, defaultValue(productInfo.getAllPrice()));
item.put(prefix + "CostAmount" + index, costDisplay.displayValue);
item.put(prefix + "TaxRate" + index, productInfo.getTaxRate());
item.put(prefix + "GrossProfit" + index, bindAmountDto != null ? defaultValue(bindAmountDto.getGrossProfit()) : BigDecimal.ZERO);
item.put(prefix + "GrossProfitRate" + index, bindAmountDto != null ? defaultValue(bindAmountDto.getGrossProfitRate()) : BigDecimal.ZERO);
item.put(prefix + "OrderStockingStatus" + index, bindAmountDto != null ? bindAmountDto.getOrderStockingStatus() : false);
summary.salesWithTax = summary.salesWithTax.add(defaultValue(productInfo.getAllPrice()));
summary.salesWithoutTax = summary.salesWithoutTax.add(toOrderAmountWithoutTax(defaultValue(productInfo.getAllPrice()), productInfo.getTaxRate()));
@ -705,7 +731,7 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
"softwareCodeX", "软件产品第 X 行编码,对应 softwareLevel2TypeX/softwareModelX/softwareQuantityX/softwareSalesAmountX/softwareCostAmountX/softwareTaxRateX",
"hardwareCodeX", "硬件产品第 X 行编码,对应 hardwareLevel2TypeX/hardwareModelX/hardwareQuantityX/hardwareSalesAmountX/hardwareCostAmountX/hardwareTaxRateX",
"serviceCodeX", "服务产品第 X 行编码,对应 serviceLevel2TypeX/serviceModelX/serviceQuantityX/serviceSalesAmountX/serviceCostAmountX/serviceTaxRateX",
"cost_rule", "软件和硬件未发货完成时成本字段返回“未发货完成”;服务默认视为已完成;订单不含税成本为各产品按各自税率换算后求和"
"cost_rule", "所有产品未配货完成时成本字段返回“未配货完成”;订单不含税成本为各产品按各自税率换算后求和"
));
return metadata;
}
@ -813,10 +839,15 @@ public class ProjectOrderInfoToolProvider extends AbstractMcpToolProvider {
private static class ProductGroupSummary {
private boolean allDelivered = true;
private boolean allStocked = true;
private BigDecimal salesWithTax = BigDecimal.ZERO;
private BigDecimal salesWithoutTax = BigDecimal.ZERO;
private BigDecimal amountWithoutTax = BigDecimal.ZERO;
private BigDecimal costWithTax = BigDecimal.ZERO;
private BigDecimal costWithoutTax = BigDecimal.ZERO;
private BigDecimal grossProfit = BigDecimal.ZERO;
private BigDecimal grossProfitRate = BigDecimal.ZERO;
private Boolean orderStockingStatus = false;
}
private static class ReceivableSummary {

View File

@ -5,10 +5,12 @@ import java.util.List;
import com.ruoyi.sip.domain.InventoryDelivery;
import com.ruoyi.sip.domain.ProjectOrderInfo;
import com.ruoyi.sip.dto.HomepageQueryDto;
import com.ruoyi.sip.dto.OrderProductBindAmountDto;
import com.ruoyi.sip.dto.OrderProductMatchBindDto;
import com.ruoyi.sip.dto.OrderProductMatchDto;
import com.ruoyi.sip.dto.OrderProductMatchSnDetailDto;
import com.ruoyi.sip.dto.StatisticsDetailDto;
import com.ruoyi.sip.dto.UnboundPurchaseOrderProductDto;
import com.ruoyi.sip.vo.OrderInfoVo;
import org.apache.ibatis.annotations.Param;
@ -113,4 +115,9 @@ public interface ProjectOrderInfoMapper
List<OrderProductMatchSnDetailDto> selectProductMatchSnDetailList(@Param("purchaseNo") String purchaseNo,
@Param("productCode") String productCode);
OrderProductBindAmountDto selectOrderProductBindAmount(@Param("orderCode") String orderCode,
@Param("productCode") String productCode);
List<UnboundPurchaseOrderProductDto> selectUnboundPurchaseOrderProductList();
}

View File

@ -951,7 +951,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
) t6 on t3.product_code = t6.product_code
left join oms_vendor_info as t7 on t3.vendor_code = t7.vendor_code
where t2.order_code = #{orderCode}
and t3.type in (1, 2)
group by t3.product_code, t3.model
order by t3.type, t3.product_code
</select>
@ -1026,13 +1025,53 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
t1.order_code,
t1.outer_code
from oms_inventory_info as t1
inner join oms_inventory_inner as t2
on t1.inner_code = t2.inner_code
left join oms_warehouse_info as t3
on t2.warehouse_id = t3.id
inner join oms_inventory_inner as t2 on t1.inner_code = t2.inner_code
left join oms_warehouse_info as t3 on t2.warehouse_id = t3.id
where t2.purchase_no = #{purchaseNo}
and t1.product_code = #{productCode}
order by t1.product_sn
</select>
<select id="selectOrderProductBindAmount" resultType="com.ruoyi.sip.dto.OrderProductBindAmountDto">
select
product_code,
sum(cost_with_tax) as cost_with_tax,
sum(cost_without_tax) as cost_without_tax,
sum(amount_without_tax) as amount_without_tax,
sum(amount_without_tax) - sum(cost_without_tax) as gross_profit,
cast((sum(amount_without_tax) - sum(t.cost_without_tax)) / sum(amount_without_tax) * 100 as decimal(10,2)) as gross_profit_rate,
(case when sum(bind_num) >= max(quantity) then true else false end) as order_stocking_status
from (
select
t1.project_id,
t2.product_code,
t2.bind_num * t4.price as cost_with_tax,
cast(t2.bind_num * t4.price / (1 + t4.tax_rate) as decimal(10,2)) as cost_without_tax,
cast(t2.bind_num * t5.price / (1 + t5.tax_rate / 100) as decimal(10,2)) as amount_without_tax,
t2.bind_num,
t5.quantity
from project_order_info as t1
inner join oms_purchase_order_map as t2 on t1.id = t2.order_id
inner join oms_purchase_order as t3 on t2.purchase_id = t3.id
inner join oms_purchase_order_item as t4 on t3.id = t4.purchase_id and t2.product_code = t4.product_code
inner join project_product_info as t5 on t1.project_id = t5.project_id and t2.product_code = t5.product_bom_code
where t2.bind_num > 0
and t1.order_code = #{orderCode}
and t2.product_code = #{productCode}
) as t
group by product_code
</select>
<select id="selectUnboundPurchaseOrderProductList" resultType="com.ruoyi.sip.dto.UnboundPurchaseOrderProductDto">
select
t1.order_code,
t1.product_code,
t1.inner_code
from oms_inventory_info as t1
inner join project_order_info as t2
on t1.order_code = t2.order_code
where t2.id not in (select order_id from oms_purchase_order_map)
group by t1.order_code, t1.product_code, t1.inner_code
</select>
</mapper>