package cn.ezhizao.project.business.controller;

import cn.ezhizao.common.utils.SecurityUtils;
import cn.ezhizao.common.utils.poi.ExcelUtil;
import cn.ezhizao.framework.aspectj.lang.annotation.Log;
import cn.ezhizao.framework.aspectj.lang.enums.BusinessType;
import cn.ezhizao.framework.web.controller.BaseController;
import cn.ezhizao.framework.web.domain.AjaxResult;
import cn.ezhizao.framework.web.page.TableDataInfo;
import cn.ezhizao.project.business.domain.*;
import cn.ezhizao.project.business.service.*;
import cn.ezhizao.project.system.domain.SysDept;
import cn.ezhizao.project.system.domain.SysDeptUser;
import cn.ezhizao.project.system.domain.SysRole;
import cn.ezhizao.project.system.domain.SysUserRole;
import cn.ezhizao.project.system.service.ISysDeptService;
import cn.ezhizao.project.system.service.ISysDeptUserService;
import cn.ezhizao.project.system.service.ISysRoleService;
import cn.ezhizao.project.system.service.ISysUserRoleService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生产子计划明细Controller
 *
 * @author ezhizao
 * @date 2023-11-15
 */
@RestController
@RequestMapping("/business/planDetailSubDetail")
public class BizProductionPlanDetailSubDetailController extends BaseController {
    @Resource
    private IBizLotService bizLotService;
    @Resource
    private IBizProductionPlanDetailService bizProductionPlanDetailService;
    @Resource
    private IBizProductionPlanDetailSubDetailService bizProductionPlanDetailSubDetailService;

    @Resource
    private IBizProductionResourceGroupSubPlanService bizProductionResourceGroupSubPlanService;

    @Resource
    private IBizProductionResourceGroupService bizProductionResourceGroupService;

    @Resource
    private ISysDeptUserService deptUserService;
    @Resource
    private ISysDeptService deptService;
    @Resource
    private ISysUserRoleService roleService;
    @Resource
    private IBizDayworkService dayworkService;
    @Resource
    private IBizTechnologicalProcessService bizTechnologicalProcessService;
    @Resource
    private IBizTakeStockPeriodService bizTakeStockPeriodService;
    @Resource
    HttpServletRequest request;
    @Resource
    private ISysRoleService sysRoleService;


    /**
     * 查询生产子计划明细列表
     */
//    @PreAuthorize("@ss.hasPermi('business:productionBatch:list')")
    @GetMapping("/list")
    @Log(title = "查询生产计划明细列表", businessType = BusinessType.SELECT)
    public TableDataInfo list(BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail) throws NoSuchFieldException, IllegalAccessException {
//        String tenantId = request.getHeader("tenantId");
//        bizProductionPlanDetailSubDetail.setTenantId(Long.valueOf(tenantId));
        startPage();
        List<BizProductionPlanDetailSubDetail> list = bizProductionPlanDetailSubDetailService.getList(bizProductionPlanDetailSubDetail);
        return getDataTable(list);
    }

    @GetMapping("/getDept")
    @Log(title = "查询工段列表", businessType = BusinessType.SELECT)
    public AjaxResult getDept() throws NoSuchFieldException, IllegalAccessException {
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        String tenantId = request.getHeader("tenantId");

        QueryWrapper<SysDeptUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("deleted", 0);
        queryWrapper.eq("is_formman", 1);
//        if (!"0".equals(tenantId)) {
//            queryWrapper.eq("tenant_id", Long.valueOf(tenantId));
//        }
        List<SysDeptUser> deptUserList = deptUserService.list(queryWrapper);
        List<Long> ids = new ArrayList<>();
        for (SysDeptUser item : deptUserList) {
            ids.add(item.getDeptId());
        }
        List<SysDept> deptOptions = new ArrayList<>();
        if (ids.size() > 0) {
            deptOptions = deptService.getDeptOptions(ids);
        }

        return success(deptOptions);
    }


    /**
     * 查询当前工段
     */
    @Log(title = "特殊报工查询工段", businessType = BusinessType.SELECT)
    @GetMapping("/specialGetDeptList")
    public AjaxResult specialGetDeptList() throws NoSuchFieldException, IllegalAccessException {
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<Long> ids = new ArrayList<>();
        //先查询登陆人员的部门信息
        QueryWrapper<SysDeptUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("deleted", 0);
        List<SysDeptUser> deptUserList = deptUserService.list(queryWrapper);
        if (deptUserList.size() > 0) {
            for (SysDeptUser item : deptUserList) {
                ids.add(item.getDeptId());
            }
        }

        List<SysDept> deptOptions = new ArrayList<>();
        if (!ids.isEmpty()) {
            deptOptions = deptService.getDeptOptions(ids);
        }

        if (deptOptions.isEmpty()) {
            SysDept sysDept = new SysDept();
            sysDept.setType("0");
            sysDept.setIsWorkSection(1);
            List<SysDept> deptList = deptService.selectDeptList(sysDept);
            ids = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toList());
            deptOptions = deptService.getDeptOptions(ids);
        }


        return success(deptOptions);
    }

    /**
     * 查询当前工段（工段长
     */
//    @PreAuthorize("@ss.hasPermi('business:planDetailSubDetail:list')")
    @GetMapping("/getDeptList")
    @Log(title = "查询工段长负责工段", businessType = BusinessType.SELECT)
    public AjaxResult getDeptList() throws NoSuchFieldException, IllegalAccessException {
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        //这个人绑定的权限
        SysUserRole userRole = new SysUserRole();
        userRole.setUserId(userId);
        List<SysRole> roles = roleService.selectRoleListByUserId(userRole);
        List<Long> ids = new ArrayList<>();
        boolean isDispatch = false;
        if (userId != 1) {
            QueryWrapper<SysDeptUser> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            queryWrapper.eq("deleted", 0);
            if (roles.size() > 0) {
                List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
                if (roleKeyList.size() > 0 && !roleKeyList.contains("adjuster")) {
                    queryWrapper.eq("is_formman", 1);
                }
            }

            List<SysDeptUser> deptUserList = deptUserService.list(queryWrapper);
            //工段下员工
            if (deptUserList.size() > 0) {
                for (SysDeptUser item : deptUserList) {
                    ids.add(item.getDeptId());
                }
            } else {
                userId = 1L;
            }
        }
        //管理员能看到所有工段
        if (userId == 1L) {
            isDispatch = true;
            SysDept sysDept = new SysDept();
            sysDept.setType("0");
            sysDept.setIsWorkSection(1);
            List<SysDept> deptList = deptService.selectDeptList(sysDept);
            ids = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toList());
        }
        List<SysDept> deptOptions = new ArrayList<>();
        if (ids.size() > 0) {
            deptOptions = deptService.getDeptOptions(ids);
        }
        TableDataInfo info = getDataTable(deptOptions);
        Map<String, Object> map = new HashMap<>();

        map.put("isDispatch", isDispatch);
        info.setOthers(map);
        return success(info);
    }

    //在这个工段下的人
    @GetMapping("/getDeptListByUserId")
    @Log(title = "查询工段下的人员", businessType = BusinessType.SELECT)
    public AjaxResult getDeptListByUserId() throws NoSuchFieldException, IllegalAccessException {
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<Long> ids = new ArrayList<>();
        boolean isDispatch = false;
        if (userId != 1) {
            QueryWrapper<SysDeptUser> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            queryWrapper.eq("deleted", 0);
            List<SysDeptUser> deptUserList = deptUserService.list(queryWrapper);
            if (deptUserList.size() > 0) {
                for (SysDeptUser item : deptUserList) {
                    ids.add(item.getDeptId());
                }
            } else {
                userId = 1L;
            }
        }
        //管理员能看到所有工段
        if (userId == 1L) {
            isDispatch = true;
            SysDept sysDept = new SysDept();
            sysDept.setType("0");
            sysDept.setIsWorkSection(1);
            List<SysDept> deptList = deptService.selectDeptList(sysDept);
            ids = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toList());
        }
        List<SysDept> deptOptions = new ArrayList<>();
        if (ids.size() > 0) {
            deptOptions = deptService.getDeptOptions(ids);
        }
        TableDataInfo info = getDataTable(deptOptions);
        Map<String, Object> map = new HashMap<>();

        map.put("isDispatch", isDispatch);
        info.setOthers(map);
        return success(info);
    }

    @Log(title = "查询当前员工在包装线的工段", businessType = BusinessType.SELECT)
    @GetMapping("/getDeptListByUserIdInPackage")
    public AjaxResult getDeptListByUserIdInPackage() throws NoSuchFieldException, IllegalAccessException {
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<Long> ids = new ArrayList<>();
        boolean isDispatch = false;
        if (userId != 1) {
            List<SysRole> sysRoles = sysRoleService.selectRoleMenu(userId);
            if (sysRoles.size() > 0) {
                List<String> permissionsList = sysRoles.stream()
                        .flatMap(role -> role.getPermissions().stream()) // 将每个Set<String>扁平化为流
                        .collect(Collectors.toList());
                if (permissionsList.contains("business:packageResourceGroup:allDept")) {
                    userId = 1L;
                }
            }
            QueryWrapper<SysDeptUser> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId);
            queryWrapper.eq("deleted", 0);
            List<SysDeptUser> deptUserList = deptUserService.list(queryWrapper);
            if (deptUserList.size() > 0) {
                for (SysDeptUser item : deptUserList) {
                    ids.add(item.getDeptId());
                }
            } else {
                userId = 1L;
            }
        }
        //管理员能看到所有工段
        if (userId == 1L) {
            isDispatch = true;
            SysDept sysDept = new SysDept();
            sysDept.setType("0");
            sysDept.setIsWorkSection(1);
            List<SysDept> deptList = deptService.selectDeptList(sysDept);
            ids = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toList());
        }
        List<SysDept> deptOptions = new ArrayList<>();
        if (ids.size() > 0) {
            deptOptions = deptService.getDeptOptions(ids);
            if (userId == 1) {
                SysDept sysDept = new SysDept();
                sysDept.setLabel("全部");
                sysDept.setValue(-1l);
                deptOptions.add(0, sysDept);
            }
        }
        TableDataInfo info = getDataTable(deptOptions);
        Map<String, Object> map = new HashMap<>();

        map.put("isDispatch", isDispatch);
        info.setOthers(map);
        return success(info);
    }

    /**
     * 导出生产子计划明细列表
     */
    @PreAuthorize("@ss.hasPermi('business:planDetailSubDetail:export')")
    @Log(title = "导出生产子计划明细列表", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail) throws NoSuchFieldException, IllegalAccessException {
        setTenantId(bizProductionPlanDetailSubDetail);
        List<BizProductionPlanDetailSubDetail> list = bizProductionPlanDetailSubDetailService.getList(bizProductionPlanDetailSubDetail);
        ExcelUtil<BizProductionPlanDetailSubDetail> util = new ExcelUtil<BizProductionPlanDetailSubDetail>(BizProductionPlanDetailSubDetail.class);
        util.exportExcel(response, list, "生产子计划明细数据");
    }

    /**
     * 获取生产子计划明细详细信息
     */
    @PreAuthorize("@ss.hasPermi('business:planDetailSubDetail:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail = new BizProductionPlanDetailSubDetail();
        bizProductionPlanDetailSubDetail.setId(id);
        List<BizProductionPlanDetailSubDetail> list = bizProductionPlanDetailSubDetailService.getList(bizProductionPlanDetailSubDetail);
        if (list.size() > 1) {
            return error();
        }
        return success(list.get(0));
    }

    /**
     * 获取生产子计划明细尾批状态
     */
//    @PreAuthorize("@ss.hasPermi('business:planDetailSubDetail:query')")
    @GetMapping(value = "/getLastLotStatus/{id}")
    @Log(title = "获取生产子计划明细尾批状态", businessType = BusinessType.SELECT)
    public AjaxResult getLastLotStatus(@PathVariable("id") Long id) {
        QueryWrapper<BizProductionPlanDetailSubDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("production_plan_detail_id", id)
                .eq("deleted", 0)
                .eq("last_lot_status", 1);
        Boolean lastLotStat = bizProductionPlanDetailSubDetailService.list(queryWrapper).size() > 0 ? true : false;
        return success(lastLotStat);
    }

    /**
     * 新增生产子计划明细
     * 直接生成所有批次
     */

    @Log(title = "新增生产子计划明细", businessType = BusinessType.INSERT)
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/save")
    public AjaxResult add(@RequestBody BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail) throws NoSuchFieldException, IllegalAccessException {
        //判断当前是否在盘点
        BizTakeStockPeriod bizTakeStockPeriod = bizTakeStockPeriodService.query().eq("status", 1).one();
        if (bizTakeStockPeriod != null && bizTakeStockPeriod.getStatus() == 1) {
            return error("当前在盘点，禁止投产");
        }
        String tenantId = request.getHeader("tenantId");
        bizProductionPlanDetailSubDetail.setTenantId(Long.valueOf(tenantId));

        /*
         * 德迈仕生产批号生成规则
         * 数据表mrp10201关键字段
         *
         * cpart: 产品编码字段
         * prver: 工艺版本字段（目前mrp10201表中没有该字段，需要德迈仕新增）
         * zzqty: 制造量（支），产品制造支数
         * phsl:  生成总批数
         * phsl1: 单个批次的产品支数
         * phsl2: 尾批次的产品支数
         *
         * 通过cpart、prver从产品工艺表sfc10401获取产品加工工艺后，根据phsl、phsl1、phsl2生成对应数量的批号。
         * 验证公式：zzqty=phsl1 * (phsl - 1) + phsl2
         *
         * 举例：
         * zzqty = 15000
         * phsl  = 273
         * phsl1 = 55
         * phsl2 = 40
         * 生成272批 支数为55的批次，再生成1批 支数为40的批次
         * 总计273个批次，15000产品支数
         *
         * 批号12位规则：
         * 第一位:       D表示德迈仕，J表示金华德
         * 第二三位:     年份后两位
         * 第四位:      月份（10月、11月、12月分别用A、B、C表示）
         * 第五至八位:   生产计划单流水号
         * 第九至十二位: 生产计划单下属批次流水号，
         *
         * D23727410070: 以这个批号为例
         * D    -德迈仕
         * 23   -2023年
         * 7    -7月
         * 2741 -生成计划单号（流水号）
         * 007  -批次流水号，这是第7个批次
         * 0    -用于分批，也就是一个批号，最多可以分9批
         *
         * 批次号生成规则
         * docno    docseq
         * D239096  1
         * D239096: D    --德迈仕
         *          23   --2023年
         *          9    --月份
         *          096  --序号的一部分
         *          1    --序号的另外一部分
         *          0961 --批次号中的“生产计划单流水号”部分
         */
        // 20240703 生产批次工艺版本变更 当前工艺版本需要查询最新的工艺版本保存。
        BizTechnologicalProcess bizTechnologicalProcess = bizTechnologicalProcessService.query().eq("product_id", bizProductionPlanDetailSubDetail.getProductId()).eq("technology_version", bizProductionPlanDetailSubDetail.getTechnologyVersion()).eq("is_history", 0).one();
        if (bizTechnologicalProcess == null) {
            return error("该产品下没有工序信息，请同步产品信息或检查产品数据");
        }
        bizProductionPlanDetailSubDetail.setTechnologicalProcessId(bizTechnologicalProcess.getId());

        /* 获取生成计划明细 */
        final BizProductionPlanDetail planDetail = bizProductionPlanDetailService.getById(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
        final int lotNumber = planDetail.getTotalLotNumber();
        final int oneLotQuantity = planDetail.getOneLotQuantity();
        final int lastLotQuantity = planDetail.getLastLotQuantity();
        final String baseCode = planDetail.getProductionPlanNo().substring(0, 4);
        final String baseCodeSeq = planDetail.getProductionPlanNo().substring(4, 7) + planDetail.getLineNumber().toString();
        List<BizLot> productionPlanDetailId = bizLotService.query().eq("production_plan_detail_id", planDetail.getId()).eq("deleted", 0).list();
        if (productionPlanDetailId.size() == 0) {
            List<BizLot> bizLotList = new ArrayList<>();
            for (int i = 0; i < lotNumber; ++i) {
                BizLot bizLot = new BizLot();
                /* 将i转换为3位字符串，位数不够，前面补0 */
                String lotSeq = String.format("%03d", i + 1);
                bizLot.setLotCode(baseCode + baseCodeSeq + lotSeq + "0");
                bizLot.setProductionPlanDetailId(planDetail.getId());
                bizLot.setTechnologicalProcessId(bizTechnologicalProcess.getId());
                bizLot.setTechnologyVersion(bizProductionPlanDetailSubDetail.getTechnologyVersion());
                bizLot.setProductId(bizProductionPlanDetailSubDetail.getProductId());
                bizLot.setProductDescription(bizProductionPlanDetailSubDetail.getProductDescription());
                bizLot.setProductionQuantity(oneLotQuantity);
                bizLot.setTenantId(bizProductionPlanDetailSubDetail.getTenantId());
                if (i == lotNumber - 1) {
                    bizLot.setProductionQuantity(lastLotQuantity);
                    bizLot.setIsLast(1);
                }
                bizLotList.add(bizLot);
            }
            if (bizLotList.size() > 0) {
                bizLotService.saveOrUpdateBatch(bizLotList);
            }
        } else {
            // 获取未开始加工的所有批次 修改批次工艺
            List<BizLot> bizLotList = bizLotService.query().eq("is_amend", 0).eq("is_waste_recycling", 0).eq("is_waste", 0).eq("production_plan_detail_id", bizProductionPlanDetailSubDetail.getProductionPlanDetailId()).notExists("select 1 from biz_daywork where biz_lot.id = biz_daywork.lot_id and biz_daywork.deleted = 0").list();
            bizLotList.forEach(l -> {
                l.setTechnologicalProcessId(bizTechnologicalProcess.getId());
            });
            bizLotService.saveOrUpdateBatch(bizLotList);
        }

        SysDept dept = deptService.selectDeptById(bizProductionPlanDetailSubDetail.getDeptId());
        //判断是否自动分配
        if (dept.getAutoAllocation() == 1) {

            //查询资源组数据
            BizProductionResourceGroup group = new BizProductionResourceGroup();
            group.setDeptId(dept.getDeptId());
            group.setIsDeactivate(0);
            List<BizProductionResourceGroup> groupList = bizProductionResourceGroupService.getList(group);

            //查询是否分配过
            BizProductionResourceGroupSubPlan subPlan = new BizProductionResourceGroupSubPlan();
            subPlan.setProductionPlanDetailId(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
            subPlan.setDeptId(dept.getDeptId());
            List<BizProductionResourceGroupSubPlan> list = bizProductionResourceGroupSubPlanService.getAotList(subPlan);
            //如果有数据代表分配过
            if (list.size() == 0) {
                //当没有分配过时，分配给所有资源组
                List<BizProductionResourceGroupSubPlan> planList = new ArrayList<>();
                for (BizProductionResourceGroup item : groupList) {
                    BizProductionResourceGroupSubPlan plan = new BizProductionResourceGroupSubPlan();
                    plan.setProductionPlanDetailId(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
                    plan.setProductId(bizProductionPlanDetailSubDetail.getProductId());
                    plan.setTechnologicalProcessId(bizProductionPlanDetailSubDetail.getTechnologicalProcessId());
                    plan.setDeptId(dept.getDeptId());
                    plan.setResourceGroupId(item.getId());
                    plan.setResourceGroupCode(item.getCode());
                    plan.setTenantId(bizProductionPlanDetailSubDetail.getTenantId());
                    plan.setCreatorId(getUserId());
                    planList.add(plan);
                }
                //保存
                bizProductionResourceGroupSubPlanService.saveBatch(planList);
            }
        }
        return toAjax(bizProductionPlanDetailSubDetailService.saveOrUpdate(bizProductionPlanDetailSubDetail));
    }

    /**
     * 旧方法判断是否已经生成所有批次，假设已生成就不修改了，只修改所有批次的工艺版本，现在改成按照投产数量生成批次。
     * 假设投产数量修改了，则保留所有已生产批次，假设修改数量小于之前生产的数量返回错误，假设修改数量大于之前生产的数量则生成新的批次。
     * 根据计划明细生成批次
     *
     * @param bizProductionPlanDetailSubDetail
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */

    @Log(title = "新增生产子计划明细", businessType = BusinessType.INSERT)
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/saveByNumber")
    public AjaxResult addNew(@RequestBody BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail) throws NoSuchFieldException, IllegalAccessException {
        //判断当前是否在盘点
        BizTakeStockPeriod bizTakeStockPeriod = bizTakeStockPeriodService.query().eq("status", 1).one();
        if (bizTakeStockPeriod != null && bizTakeStockPeriod.getStatus() == 1) {
            return error("当前在盘点，禁止投产");
        }
        String tenantId = request.getHeader("tenantId");
        bizProductionPlanDetailSubDetail.setTenantId(Long.valueOf(tenantId));

        /*
         * 德迈仕生产批号生成规则
         * 数据表mrp10201关键字段
         *
         * cpart: 产品编码字段
         * prver: 工艺版本字段（目前mrp10201表中没有该字段，需要德迈仕新增）
         * zzqty: 制造量（支），产品制造支数
         * phsl:  生成总批数
         * phsl1: 单个批次的产品支数
         * phsl2: 尾批次的产品支数
         *
         * 通过cpart、prver从产品工艺表sfc10401获取产品加工工艺后，根据phsl、phsl1、phsl2生成对应数量的批号。
         * 验证公式：zzqty=phsl1 * (phsl - 1) + phsl2
         *
         * 举例：
         * zzqty = 15000
         * phsl  = 273
         * phsl1 = 55
         * phsl2 = 40
         * 生成272批 支数为55的批次，再生成1批 支数为40的批次
         * 总计273个批次，15000产品支数
         *
         * 批号12位规则：
         * 第一位:       D表示德迈仕，J表示金华德
         * 第二三位:     年份后两位
         * 第四位:      月份（10月、11月、12月分别用A、B、C表示）
         * 第五至八位:   生产计划单流水号
         * 第九至十二位: 生产计划单下属批次流水号，
         *
         * D23727410070: 以这个批号为例
         * D    -德迈仕
         * 23   -2023年
         * 7    -7月
         * 2741 -生成计划单号（流水号）
         * 007  -批次流水号，这是第7个批次
         * 0    -用于分批，也就是一个批号，最多可以分9批
         *
         * 批次号生成规则
         * docno    docseq
         * D239096  1
         * D239096: D    --德迈仕
         *          23   --2023年
         *          9    --月份
         *          096  --序号的一部分
         *          1    --序号的另外一部分
         *          0961 --批次号中的“生产计划单流水号”部分
         */
        // 20240703 生产批次工艺版本变更 当前工艺版本需要查询最新的工艺版本保存。
        BizTechnologicalProcess bizTechnologicalProcess = bizTechnologicalProcessService.query().eq("product_id", bizProductionPlanDetailSubDetail.getProductId()).eq("technology_version", bizProductionPlanDetailSubDetail.getTechnologyVersion()).eq("is_history", 0).one();
        if (bizTechnologicalProcess == null) {
            return error("该产品下没有工序信息，请同步产品信息或检查产品数据");
        }
        bizProductionPlanDetailSubDetail.setTechnologicalProcessId(bizTechnologicalProcess.getId());

        /* 获取生成计划明细 */
        final BizProductionPlanDetail planDetail = bizProductionPlanDetailService.getById(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
        final int lotNumber = planDetail.getTotalLotNumber();
        final int oneLotQuantity = planDetail.getOneLotQuantity();
        final int lastLotQuantity = planDetail.getLastLotQuantity();
        final String baseCode = planDetail.getProductionPlanNo().substring(0, 4);
        final String baseCodeSeq = planDetail.getProductionPlanNo().substring(4, 7) + planDetail.getLineNumber().toString();
        List<BizLot> productionPlanDetailId = bizLotService.query().eq("production_plan_detail_id", planDetail.getId()).eq("is_superaddition", 0).eq("is_waste_recycling", 0).eq("deleted", 0).list();
        // 获取所有的生产子计划。
        List<BizProductionPlanDetailSubDetail> subDetails = bizProductionPlanDetailSubDetailService.query().eq("production_plan_detail_id", planDetail.getId()).ne("id", bizProductionPlanDetailSubDetail.getId() == null ? 0L : bizProductionPlanDetailSubDetail.getId()).list();
        List<BizLot> bizLotList = bizLotService.query().eq("is_amend", 0).eq("is_waste_recycling", 0).eq("is_waste", 0).eq("production_plan_detail_id", bizProductionPlanDetailSubDetail.getProductionPlanDetailId()).exists("select 1 from biz_daywork where biz_lot.id = biz_daywork.lot_id and biz_daywork.deleted = 0").list();
        String latestCode = baseCode + baseCodeSeq + String.format("%03d", planDetail.getTotalLotNumber()) + "0";
        // 目前投产总批数
        int allLotNumber = subDetails.stream().mapToInt(BizProductionPlanDetailSubDetail::getLotNumber).sum() + bizProductionPlanDetailSubDetail.getLotNumber();
        List<BizLot> changedLists = new ArrayList<>();
        boolean hasLatest = productionPlanDetailId.stream().anyMatch(v -> v.getLotCode().equals(latestCode));
        // 判断总数是否大于总的批次数量，假设大于则新增，假设小于则删除，删除前判断删除的批次中是否有已生产的批次。
        if (productionPlanDetailId.size() < (subDetails.stream().mapToInt(BizProductionPlanDetailSubDetail::getLotNumber).sum() + bizProductionPlanDetailSubDetail.getLotNumber())) {
            // 新增批次

            int addLotNumber = allLotNumber - productionPlanDetailId.size();
            for (int i = 0; i < addLotNumber; ++i) {
                BizLot bizLot = new BizLot();
                /* 将i转换为3位字符串，位数不够，前面补0 */
//                String lotSeq = String.format("%03d", productionPlanDetailId.size() + i + 1);
//                String lotCode = getCurrentLotNumber(baseCode, baseCodeSeq, productionPlanDetailId, i, addLotNumber, changedLists, bizProductionPlanDetailSubDetail.getLastLotStatus().equals(1), planDetail.getTotalLotNumber());
                // 判断是否新增尾批
                String lotSeq = String.format("%03d", productionPlanDetailId.size() + (hasLatest ? -1 : 0) + i + 1);
                String lotCode = baseCode + baseCodeSeq + lotSeq + "0";
                if (bizProductionPlanDetailSubDetail.getId() == null && bizProductionPlanDetailSubDetail.getLastLotStatus().equals(1) && addLotNumber == (i + 1)) {
                    lotCode = latestCode;
                }
                bizLot.setLotCode(lotCode);
                bizLot.setProductionPlanDetailId(planDetail.getId());
                bizLot.setTechnologicalProcessId(bizTechnologicalProcess.getId());
                bizLot.setTechnologyVersion(bizProductionPlanDetailSubDetail.getTechnologyVersion());
                bizLot.setProductId(bizProductionPlanDetailSubDetail.getProductId());
                bizLot.setProductDescription(bizProductionPlanDetailSubDetail.getProductDescription());
                bizLot.setProductionQuantity(oneLotQuantity);
                bizLot.setTenantId(bizProductionPlanDetailSubDetail.getTenantId());
                if (latestCode.equals(bizLot.getLotCode())) {
                    bizLot.setProductionQuantity(lastLotQuantity);
                    bizLot.setIsLast(1);
                }
//                if (i == lotNumber - 1) {
//                    bizLot.setProductionQuantity(lastLotQuantity);
//                    bizLot.setIsLast(1);
//                }
                changedLists.add(bizLot);
            }
        } else {
            // 判断批次是否可以删除
            // 获取所有已加工的批次
            int delNumber = productionPlanDetailId.size() - allLotNumber;
            // 尾批已创建则判断加工中的是否包含尾批 假设不包含尾批还要多一个
            boolean addLatest = productionPlanDetailId.stream().anyMatch(v -> v.getLotCode().equals(latestCode)) && bizLotList.stream().noneMatch(v -> v.getLotCode().equals(latestCode));
            if (bizLotList.size() > (addLatest ? allLotNumber + 1 : allLotNumber)) {
                // 已加工数量大于修改后总批数则返回错误
                throw new RuntimeException("已加工数量超出投产总批数，请确认后修改");
            } else {
                // 查询所有未开始的批次 批次号倒序 应该删除的批次数量。
                List<BizLot> removeList = productionPlanDetailId.stream().filter(v -> bizLotList.stream().noneMatch(t -> t.getId().equals(v.getId())) && !v.getLotCode().equals(latestCode)).sorted(Comparator.comparing(BizLot::getLotCode).reversed()).limit(delNumber).collect(Collectors.toList());
                bizLotService.removeBatchByIds(removeList);
                changedLists.addAll(productionPlanDetailId.stream().filter(v -> bizLotList.stream().noneMatch(t -> t.getId().equals(v.getId())) && removeList.stream().noneMatch(t -> t.getId().equals(v.getId()))).collect(Collectors.toList()));
            }
        }
        // 获取所有未生产批次，修改批次工艺
        changedLists.forEach(l -> {
            l.setTechnologicalProcessId(bizTechnologicalProcess.getId());
        });

        if (changedLists.size() > 0) {
            bizLotService.saveOrUpdateBatch(changedLists);
        }

        SysDept dept = deptService.selectDeptById(bizProductionPlanDetailSubDetail.getDeptId());
        //判断是否自动分配
        if (dept.getAutoAllocation() == 1) {

            //查询资源组数据
            BizProductionResourceGroup group = new BizProductionResourceGroup();
            group.setDeptId(dept.getDeptId());
            group.setIsDeactivate(0);
            List<BizProductionResourceGroup> groupList = bizProductionResourceGroupService.getList(group);

            //查询是否分配过
            BizProductionResourceGroupSubPlan subPlan = new BizProductionResourceGroupSubPlan();
            subPlan.setProductionPlanDetailId(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
            subPlan.setDeptId(dept.getDeptId());
            List<BizProductionResourceGroupSubPlan> list = bizProductionResourceGroupSubPlanService.getAotList(subPlan);
            //如果有数据代表分配过
            if (list.size() == 0) {
                //当没有分配过时，分配给所有资源组
                List<BizProductionResourceGroupSubPlan> planList = new ArrayList<>();
                for (BizProductionResourceGroup item : groupList) {
                    BizProductionResourceGroupSubPlan plan = new BizProductionResourceGroupSubPlan();
                    plan.setProductionPlanDetailId(bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
                    plan.setProductId(bizProductionPlanDetailSubDetail.getProductId());
                    plan.setTechnologicalProcessId(bizProductionPlanDetailSubDetail.getTechnologicalProcessId());
                    plan.setDeptId(dept.getDeptId());
                    plan.setResourceGroupId(item.getId());
                    plan.setResourceGroupCode(item.getCode());
                    plan.setTenantId(bizProductionPlanDetailSubDetail.getTenantId());
                    plan.setCreatorId(getUserId());
                    planList.add(plan);
                }
                //保存
                bizProductionResourceGroupSubPlanService.saveBatch(planList);
            }
        }
        return toAjax(bizProductionPlanDetailSubDetailService.saveOrUpdate(bizProductionPlanDetailSubDetail));
    }

    /**
     * @param baseCode               首字母
     * @param baseCodeSeq            计划单批次
     * @param productionPlanDetailId 已投产批次
     * @param i                      序号
     * @param addNum                 总数量
     * @param added                  新增批次
     * @param isLatest               是否尾批
     * @param allLotNumber           计划总投产
     * @return 实际序号
     */

    private String getCurrentLotNumber(String baseCode, String baseCodeSeq, List<BizLot> productionPlanDetailId, int i, int addNum, List<BizLot> added, boolean isLatest, int allLotNumber) {
        // 该批次不是当前批次的最后一批的时候获取投产批次中最后一批
        String latestCode = baseCode + baseCodeSeq + String.format("%03d", allLotNumber) + "0";
        if ((i + 1) == addNum && isLatest && productionPlanDetailId.stream().noneMatch(v -> v.getLotCode().equals(latestCode))) {
            return latestCode;
        } else {
            String firstCode = baseCode + baseCodeSeq + String.format("%03d", 1) + "0";
            return checkCode(firstCode, productionPlanDetailId, added);
        }
    }

    private String checkCode(String code, List<BizLot> productionPlanDetailId, List<BizLot> added) {
        // 递归判断该批号是否已存在，不存在则返回批号
        if (productionPlanDetailId.stream().noneMatch(v -> v.getLotCode().equals(code)) && added.stream().noneMatch(v -> v.getLotCode().equals(code))) {
            return code;
        } else {
            String newCode = code.substring(0, 8) + String.format("%03d", Integer.valueOf(code.substring(8, 11))) + "0";
            return checkCode(newCode, productionPlanDetailId, added);
        }
    }

    /**
     * 获取所有未使用批号
     *
     * @param productionPlanDetailId 已使用批号列表
     * @param baseCode               首字母
     * @param baseCodeSeq            计划单批次
     * @param allLotNumber           批数
     * @return 未使用批号列表
     */
    private Queue<String> getCodes(List<BizLot> productionPlanDetailId, String baseCode, String baseCodeSeq, int allLotNumber) {
        Queue<String> line = new ArrayDeque<>();
        for (int i = 1; i < allLotNumber; i++) {
            String currentCode = baseCode + baseCodeSeq + String.format("%03d", i) + "0";
            if (productionPlanDetailId.stream().noneMatch(t -> t.getLotCode().equals(currentCode))) {
                line.offer(currentCode);
            }

        }
        return line;
    }

    /**
     * 修改生产子计划明细
     */

    @Log(title = "修改生产子计划明细", businessType = BusinessType.UPDATE)
    @PutMapping("/save")
    public AjaxResult edit(@RequestBody BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail) throws NoSuchFieldException, IllegalAccessException {
//        String tenantId = request.getHeader("tenantId");
//        bizProductionPlanDetailSubDetail.setTenantId(Long.valueOf(tenantId));
        return toAjax(bizProductionPlanDetailSubDetailService.updateById(bizProductionPlanDetailSubDetail));
    }

    /**
     * 删除生产子计划明细
     */

    @Log(title = "删除生产子计划明细", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable List<Long> ids) {
        //如果已经投产的生产子计划已经生成批次，则不能删除
        QueryWrapper<BizProductionPlanDetailSubDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", ids.get(0));
        BizProductionPlanDetailSubDetail bizProductionPlanDetailSubDetail = bizProductionPlanDetailSubDetailService.list(queryWrapper).get(0);
        QueryWrapper<BizDaywork> query = new QueryWrapper<>();
        query.eq("production_plan_detail_id", bizProductionPlanDetailSubDetail.getProductionPlanDetailId());
        query.eq("deleted", 0);
        List<BizDaywork> list = dayworkService.list(query);
        if (list.size() > 0) {
            return error("该批次已经生成就不能删除子计划");
        }
        BizTakeStockPeriod bizTakeStockPeriod = bizTakeStockPeriodService.query().eq("status", 1).one();
        if (bizTakeStockPeriod != null && bizTakeStockPeriod.getStatus() == 1) {
            return error("正在盘点，不能删除");
        }
        return toAjax(bizProductionPlanDetailSubDetailService.removeBatchByIds(ids));
    }

    @Log(title = "根据数量删除生产子计划", businessType = BusinessType.DELETE)
    @DeleteMapping("/removeByNum/{ids}")
    public AjaxResult removeByNum(@PathVariable List<Long> ids) {
        // 判断盘点状态
        BizTakeStockPeriod bizTakeStockPeriod = bizTakeStockPeriodService.query().eq("status", 1).one();
        if (bizTakeStockPeriod != null && bizTakeStockPeriod.getStatus() == 1) {
            return error("正在盘点，不能删除");
        }
        // 获取删除列表
        List<BizProductionPlanDetailSubDetail> bizProductionPlanDetailSubDetails = bizProductionPlanDetailSubDetailService.query().in("id", ids).list();
        if (!bizProductionPlanDetailSubDetails.isEmpty()) {
            // 判断删除列表中是否包含尾批 假设包含尾批则查询尾批 假设包含尾批则从尾批开始删除 假设不包含尾批从尾批之前开始删除
            Integer delLotCount = bizProductionPlanDetailSubDetails.stream().mapToInt(BizProductionPlanDetailSubDetail::getLotNumber).sum();
            Long productionPlanDetailId = bizProductionPlanDetailSubDetails.get(0).getProductionPlanDetailId();
            List<BizLot> lots = new ArrayList<>();
            // 是否有尾批
            if(bizProductionPlanDetailSubDetails.stream().anyMatch(t -> t.getLastLotStatus().equals(1))) {
                lots = bizLotService.query().eq("production_plan_detail_id", productionPlanDetailId).orderByDesc("lot_code").last("limit " + delLotCount).list();
            } else {
                lots = bizLotService.query().eq("production_plan_detail_id", productionPlanDetailId).eq("is_last", 0).orderByDesc("lot_code").last("limit " + delLotCount).list();
            }
            List<BizDaywork> bizDayworks = dayworkService.query().in("lot_id", lots.isEmpty() ? Collections.singletonList(0L) : lots.stream().map(BizLot::getId).collect(Collectors.toList())).list();
            if (bizDayworks.size() > 0) {
                return error("该批次已经开始报工就不能删除子计划");
            }
            return bizProductionPlanDetailSubDetailService.removeBatchByIds(bizProductionPlanDetailSubDetails) && (lots.isEmpty() || bizLotService.removeBatchByIds(lots)) ? success() : error("删除失败");
        }
        return success();
    }
}
