package com.kaidi.oa.web;
import com.kaidi.oa.common.ApiException;
import com.kaidi.oa.common.ApiResp;
import com.kaidi.oa.common.Money;
import com.kaidi.oa.common.NotFoundException;
import com.kaidi.oa.domain.BomItem;
import com.kaidi.oa.domain.BomVarianceDetail;
import com.kaidi.oa.domain.CostAdjustLog;
import com.kaidi.oa.domain.CostPurchaseOrder;
import com.kaidi.oa.domain.InventoryValuationConfig;
import com.kaidi.oa.domain.MaterialIssue;
import com.kaidi.oa.domain.MfgPurchaseOrder;
import com.kaidi.oa.domain.StandardCost;
import com.kaidi.oa.repository.BomItemRepository;
import com.kaidi.oa.repository.BomVarianceDetailRepository;
import com.kaidi.oa.repository.CostAdjustLogRepository;
import com.kaidi.oa.repository.CostPurchaseOrderRepository;
import com.kaidi.oa.repository.InventoryValuationConfigRepository;
import com.kaidi.oa.repository.MaterialIssueRepository;
import com.kaidi.oa.repository.MfgPurchaseOrderRepository;
import com.kaidi.oa.repository.StandardCostRepository;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* 成本控制部·深化缺口补齐(Round-4,推 PARTIAL→MET)。
*
*
本控制器集中补齐以下 7 条 PARTIAL 缺口:
*
*
* - 缺口1:MBOM→标准成本自动重算联动
* POST /mbom-to-standard-cost — 从指定项目的 BomItem 树展开,
* 按每条明细的 unitPrice × bidQty 加总重算本项目的 StandardCost.materialCost;
* 同时同步 BomVarianceDetail 的 stdQty/stdLossRate 回填(解决「未硬关联自动填充」)。
*
* - 缺口2:采购均价同步(历史采购价来源 = 采购均价而非合同额)
* POST /sync-purchase-avg-price — 从 CostPurchaseOrder 按物料名分组计算采购均价,
* 回填 historyAvgPrice;并将均价与 MfgPurchaseOrder 最新价对比,同步写入统一成本基础。
*
* - 缺口3:批次完工自动触发BOM差异计算
* POST /batch-complete-trigger — 工单完工时传入工单号 + 期间,
* 系统自动从领料单取实际投料量 / 实际价格,与 BomVarianceDetail 标准数据对比,
* 生成五维差异明细(无需手录)。
*
* - 缺口4:结算成本锁定专用端点
* POST /contract-settlement-lock — 合同结算后对该合同下所有 CostAdjustLog
* 写入锁定快照,防止随意修改;返回最终成本摘要(材料/人工/费用/总计)。
*
* - 缺口5:差异责任按部门归集(§6-2)
* GET /variance-by-dept — 读取 BomVarianceDetail,把 workOrderCode 前缀/projectName
* 按部门映射规则归集,返回各部门的用量差/价格差/总差异/条目数,
* 辅助差异责任追究(采购/生产/工程各责其责)。
*
* - 缺口6:InventoryValuationConfig 驱动真实出入库计价引擎
* POST /valuation-engine/calc — 用当前已生效的计价方法对一批领料单做模拟定价,
* 返回在不同计价方法(移动平均 / FIFO)下的成本差异,供财务决策。
*
* - 缺口7:@Scheduled 出入库→成本归集自动事件驱动(§8)
* 定时任务每天凌晨自动扫描「已领」状态领料单(24h 内),
* 同步回填至 BomVarianceDetail 的实际用量,并写 CostAdjustLog 留痕。
* 另每 2 小时扫描 MfgPurchaseOrder 新完成订单,同步到 CostPurchaseOrder 统一台账。
*
*
* 金额全部走 {@link Money}/BigDecimal;写口受 AuthInterceptor default-deny 保护;
* 读侧含成本金额已登记 SENSITIVE_READ_PREFIXES(见 sharedFileSnippets)。
*/
@RestController
@RequestMapping("/api/oa/cost-ctrl-deep")
public class CostCtrlDeepController {
private final BomItemRepository bomItemRepo;
private final BomVarianceDetailRepository varRepo;
private final CostPurchaseOrderRepository costPoRepo;
private final MfgPurchaseOrderRepository mfgPoRepo;
private final StandardCostRepository stdCostRepo;
private final MaterialIssueRepository issueRepo;
private final InventoryValuationConfigRepository valuationRepo;
private final CostAdjustLogRepository adjustLogRepo;
public CostCtrlDeepController(
BomItemRepository bomItemRepo,
BomVarianceDetailRepository varRepo,
CostPurchaseOrderRepository costPoRepo,
MfgPurchaseOrderRepository mfgPoRepo,
StandardCostRepository stdCostRepo,
MaterialIssueRepository issueRepo,
InventoryValuationConfigRepository valuationRepo,
CostAdjustLogRepository adjustLogRepo) {
this.bomItemRepo = bomItemRepo;
this.varRepo = varRepo;
this.costPoRepo = costPoRepo;
this.mfgPoRepo = mfgPoRepo;
this.stdCostRepo = stdCostRepo;
this.issueRepo = issueRepo;
this.valuationRepo = valuationRepo;
this.adjustLogRepo = adjustLogRepo;
}
// ======================================================================
// 缺口1:MBOM → 标准成本自动重算联动
// ======================================================================
public record MbomToStdCostRequest(
Long projectId, String productName, String period,
String costCenter, String operator) {}
/**
* MBOM展开→标准成本重算:从指定项目的 BomItem 树累加所有子项 unitPrice×bidQty,
* 回填/新建该产品的 StandardCost.materialCost,同时把 BomItem.bidQty 回填到
* 同项目+物料名的 BomVarianceDetail.stdQty,解决「标准BOM与差异明细未硬关联」。
*/
@PostMapping("/mbom-to-standard-cost")
@Transactional
public ApiResp