package com.sy.coinage.workshop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.sy.coinage.app.req.ShopConfirmReq;
import com.sy.coinage.app.req.WareConfirmReq;
import com.sy.coinage.core.constants.BusinessEnum;
import com.sy.coinage.core.exception.RetException;
import com.sy.coinage.core.redis.RedisService;
import com.sy.coinage.core.vo.CacheUserVO;
import com.sy.coinage.sys.model.Dept;
import com.sy.coinage.sys.model.Dict;
import com.sy.coinage.sys.service.IDeptService;
import com.sy.coinage.sys.service.IDictService;
import com.sy.coinage.sys.service.IUserService;
import com.sy.coinage.websocket.WebSocketServer;
import com.sy.coinage.workshop.WareUtil;
import com.sy.coinage.workshop.req.WorkshopOutboundAddReq;
import com.sy.coinage.workshop.req.WorkshopOutboundReq;
import com.sy.coinage.workshop.req.vo.BaseInfoVO;
import com.sy.coinage.workshop.req.vo.SerialNumberVO;
import com.sy.coinage.workshop.service.*;
import com.sy.coinage.workshop.vo.OutboundVO;
import com.sy.coinage.workshop.vo.ViewInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sy.coinage.workshop.dao.WorkshopOutboundDao;
import com.sy.coinage.workshop.model.*;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * -车间出库单据（库房解缴|返检单据|）ServiceImpl
 */
@Slf4j
@Service
public class WorkshopOutboundServiceImpl extends ServiceImpl<WorkshopOutboundDao, WorkshopOutbound> implements IWorkshopOutboundService {

    @Autowired
    private IBaseInfoService baseInfoService;

    @Autowired
    private IStockService stockService;

    @Autowired
    private IWorkshopOutboundDetailService workshopOutboundDetailService;

    @Autowired
    private IWorkshopAccountService workshopAccountService;

    @Autowired
    private IProductService productService;

    @Autowired
    private IUserService userService;

    @Autowired
    private RedisService redisService;
    @Autowired
    private IDictService dictService;

    @Autowired
    private IDeptService deptService;

    @Autowired
    private ILogisticsService logisticsService;

    @Autowired
    private IWorkshopLogService workshopLogService;

    @Override
    @Transactional
    public void eliminateOut(WorkshopOutboundReq req, CacheUserVO user) throws ParseException {
        Dept dept = this.deptService.getByCode(req.getWareId());

        // 如果没有品名 查部门最后一条
        if (req.getSecuritiesId() == null || req.getSecuritiesId() == 0) {
            BaseInfo info = baseInfoService.getLast(dept.getId());
            req.setSecuritiesId(info.getSecuritiesId());
        }
        // 需要在SecuritiesId赋值后，在进行查询
        Product product = productService.getById(req.getSecuritiesId());

        String checkUserName = this.userService.getById(req.getCheckId()).getRealName();
        Dict dict = dictService.getByVal("shopWareOpt", req.getWareId(), null);
        Integer wareId = Integer.parseInt(dict.getVal());
        Integer racketNum = 0;
        // 录入自动拍数
        if (req.getAutoLs() != null && req.getAutoLs().size() > 0) {
            racketNum = racketNum + req.getAutoLs().size();
            for (SerialNumberVO vo : req.getAutoLs()) {
                BaseInfo info = this.baseInfoService.updateWorkShopStatus(vo.getSerialNumber(), vo.getRfid(), 2, 13, null, null, null, null);
                // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
                // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包12：已发货
                // FIXME 记录物流信息日志表
                this.logisticsService.add(vo.getRfid(), vo.getSerialNumber(), wareId, null, 3, req.getCheckId(), checkUserName, user.getId(), user.getRealName(), null, null);
                this.sendOutMsg1(req, checkUserName, user.getRealName(), info, wareId);
//                String crownBetweenStr = info.getCrownPrefix() + info.getCrownFrom() + "-"
//                        + info.getCrownPrefix() + info.getCrownTo();
                this.workshopLogService.addWorkshopLog(info, info.getCrownBetweenStr(), info.getSecuritiesId(),
                        info.getSecurities(), vo.getRfid(), vo.getSerialNumber(),
                        info.getDeptId(), info.getDept(),
                        user.getId(), user.getRealName(),
                        req.getCheckId(), checkUserName, 5, 3, 10, null, null);
            }
        }
        // 录入手动拍数
        if (req.getManualLs() != null && req.getManualLs().size() > 0) {
            racketNum = racketNum + req.getManualLs().size();
            for (SerialNumberVO vo : req.getManualLs()) {
                BaseInfo info = this.baseInfoService.updateWorkShopStatus(vo.getSerialNumber(), null, 2, 13, null, null, null, null);
                // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
                // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台
                // FIXME 记录物流信息日志表
                this.logisticsService.add(vo.getRfid(), vo.getSerialNumber(), wareId, null, 3, req.getCheckId(), checkUserName, user.getId(), user.getRealName(), null, null);
                this.sendOutMsg1(req, checkUserName, user.getRealName(), info, wareId);
//                String crownBetweenStr = info.getCrownPrefix() + info.getCrownFrom() + "-"
//                        + info.getCrownPrefix() + info.getCrownTo();
                this.workshopLogService.addWorkshopLog(info, info.getCrownBetweenStr(), info.getSecuritiesId(),
                        info.getSecurities(), vo.getRfid(), vo.getSerialNumber(),
                        info.getDeptId(), info.getDept(),
                        user.getId(), user.getRealName(),
                        req.getCheckId(), checkUserName, 5, 3, 10, null, null);
            }
        }
        // 剔除品库
        if ((req.getBoxNum() != null && req.getBoxNum() > 0) || (req.getPiecesTotal() != null && req.getPiecesTotal() > 0)) {
            Integer areaId = BusinessEnum.ELIMINATE_AREA.value();
            // 更新实时剔除品库存 返回实时库存
            this.stockService.stockSub(req.getSecuritiesId(), product.getSecurities(), areaId, wareId, dict.getName(), racketNum, req.getBoxNum(), req.getPiecesTotal(), 0);
            Integer eliminateStockTotal = this.stockService.getEliminateStockTotal(wareId, req.getSecuritiesId(), product);
            // 添加车间台账 剔除品库内部出库到机台
            Integer piecesTotal = racketNum * product.getBoxAmount() * product.getAmountPerBox() + (req.getBoxNum() == null ? 0 : req.getBoxNum()) * product.getAmountPerBox() + (req.getPiecesTotal() == null ? 0 : req.getPiecesTotal());
            // 3剔除品实物收发卡
            this.workshopAccountService.shopOut(1, dept.getId(), dept.getName(), user.getId(),
                    user.getRealName(), req.getCheckId(), checkUserName, null, piecesTotal,
                    eliminateStockTotal, 3, 1, "机台", null, req.getSecuritiesId(),
                    product.getSecurities());
            this.workshopLogService.addWorkshopLog(null, null, product.getId(),
                    product.getSecurities(), null, null,
                    dept.getId(), dept.getName(),
                    user.getId(), user.getRealName(),
                    req.getCheckId(), checkUserName, 5, 3, 10, 0, req.getPiecesTotal());
        }
    }

    @Override
    @Transactional
    public void shopOut(WorkshopOutboundReq req, CacheUserVO user) throws ParseException {
        Dept dept = this.deptService.getByCode(req.getWareId());
        // 取拍最大最小净重的BaseInfo
        BaseInfo info = this.baseInfoService.getBySecuritiesId(req.getSecuritiesId(),
                dept.getId(), 2);
        Product product = productService.getById(req.getSecuritiesId());
        WorkshopOutbound model = new WorkshopOutbound();
        model.setSecuritiesId(req.getSecuritiesId());
        model.setSecurities(product.getSecurities());
        //状态:1.新建 2.已出库 3.已入库 解缴单状态 直接出库
        model.setStatus(2);
        // 车间出库单单号
        model.setOrderNo(this.creadeOutOrderNo(product.getSecurities(), dept.getId()));
        // 单据类型： 1: 解缴单  2: 返检单
        model.setOrderType(1);
        model.setDeptId(dept.getId());
        model.setDept(dept.getName());
        model.setCreateId(user.getId());
        model.setCreateBy(user.getRealName());
        Integer racketNum = 0;
        if (req.getAutoLs() != null && req.getAutoLs().size() > 0) {
            racketNum = racketNum + req.getAutoLs().size();
        }
        // 录入手动拍数
        if (req.getManualLs() != null && req.getManualLs().size() > 0) {
            racketNum = racketNum + req.getManualLs().size();
        }
        model.setPlanNum(racketNum);

//        Integer totalRacket = (req.getAutoLs() == null ? 0 : req.getAutoLs().size()) + (req.getManualLs() == null ? 0 : req.getManualLs().size());
        // 实际拍数（由于仓位不同，实际手持端可多次修改，与计划拍数相等后，该条目不再显示）
        model.setRealNum(0);
        // 拍最大最小净重
        model.setMaxNetWeight(info.getMaxWeight());
        model.setMinNetWeight(info.getMinWeight());
        model.setCheckId(req.getCheckId());
        model.setChecker(this.userService.getById(req.getCheckId()).getRealName());
        // 添加出库主表
        this.save(model);
        Integer wareId = Integer.parseInt(dept.getRemark());
        // 添加出库子表
        OutboundVO vo = this.addDetail(model, req, wareId);
        // 合计箱数=拍数*每拍箱数
        model.setTotalBoxAmount(vo.getTotalBoxAmount());
        // 合计枚数
        model.setTotalAmount(vo.getTotalBoxAmount() * product.getAmountPerBox());
        this.updateById(model);
        Integer areaId = BusinessEnum.PRODUCT_AREA.value();
        Dict dict = dictService.getByVal("shopWareOpt", wareId, null);
        // 更新实时封箱成品库存 返回实时库存
        this.stockService.stockSub(model.getSecuritiesId(), model.getSecurities(),
                areaId, wareId, dict.getName(), vo.getRacketQty(), vo.getBoxQty(), 0, 0);
        Integer wareTotal1 = this.stockService.getProductStockTotal(req.getWareId(), req.getSecuritiesId(), product);
        Integer piecesTotal = vo.getTotalBoxAmount() * product.getAmountPerBox();
        // 2封箱成品实物收发卡-付出
        this.workshopAccountService.shopOut(1,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal,
                wareTotal1, 2, 2, "总库", null, model.getSecuritiesId(),
                model.getSecurities());
        Integer inventory = this.workshopAccountService.getLastInventory(model.getSecuritiesId(),
                user.getDeptId()) - piecesTotal;
        // 4印花成品出入库台账-付出
        this.workshopAccountService.shopOut(1, model.getDeptId(), model.getDept(),
                model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal, inventory, 4, 2, "封箱成品",
                null, model.getSecuritiesId(), model.getSecurities());
        // 5封箱成品出入库台账-收入
        this.workshopAccountService.shopIn(1, model.getDeptId(), model.getDept(),
                model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), model.getOrderNo(), piecesTotal, piecesTotal, 5, 2,
                "印花成品", null, model.getSecuritiesId(), model.getSecurities());
        // 5封箱成品出入库台账-付入
        this.workshopAccountService.shopOut(1, model.getDeptId(), model.getDept(),
                model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), model.getOrderNo(), piecesTotal, 0, 5, 2,
                "总库", null, model.getSecuritiesId(), model.getSecurities());
        // 单号增加
        this.addOutOrderNo(model.getSecurities(), model.getDeptId());
    }


    /**
     * 外部出库
     *
     * @param model
     * @param req
     * @param wareId
     */
    private OutboundVO addDetail(WorkshopOutbound model, WorkshopOutboundReq req, Integer wareId) throws ParseException {
        OutboundVO outboundVO = new OutboundVO();
        // 录入自动拍数
        if (req.getAutoLs() != null && req.getAutoLs().size() > 0) {
            for (SerialNumberVO vo : req.getAutoLs()) {
                BaseInfo info = baseAdd(vo.getSerialNumber(), vo.getRfid(), model, req, wareId);
                outboundVO.setTotalBoxAmount(outboundVO.getTotalBoxAmount() + info.getQty());
//                if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
//                        info.getOriginType() == 2) {
//                    outboundVO.setBoxQty(outboundVO.getBoxQty() + info.getQty());
//                } else {
                outboundVO.setRacketQty(outboundVO.getRacketQty() + 1);
//                }
                // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
                // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包12：已发货
                // FIXME 记录物流信息日志表
                this.logisticsService.add(vo.getRfid(), vo.getSerialNumber(), wareId, null, 3, model.getCheckId(), model.getChecker(), model.getCreateId(), model.getCreateBy(), null, null);
            }
        }
        // 录入手动拍数
        if (req.getManualLs() != null && req.getManualLs().size() > 0) {
            for (SerialNumberVO vo : req.getManualLs()) {
                BaseInfo info = baseAdd(vo.getSerialNumber(), null, model, req, wareId);
                outboundVO.setTotalBoxAmount(outboundVO.getTotalBoxAmount() + info.getQty());
//                if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
//                        info.getOriginType() == 2) {
//                    outboundVO.setBoxQty(outboundVO.getBoxQty() + info.getQty());
//                } else {
                outboundVO.setRacketQty(outboundVO.getRacketQty() + 1);
//                }
                // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
                // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台
                // FIXME 记录物流信息日志表
                this.logisticsService.add(vo.getRfid(), vo.getSerialNumber(), wareId, null, 3, model.getCheckId(), model.getChecker(), model.getCreateId(), model.getCreateBy(), null, null);
            }
        }
        return outboundVO;
    }

    private Integer totalQty(Stock stock, Product product) {
        return (stock.getRacketNum() == null ? 0 : stock.getRacketNum()) * product.getBoxAmount() * product.getAmountPerBox() + (stock.getBoxNum() == null ? 0 : stock.getBoxNum()) * product.getAmountPerBox() + (stock.getPiecesTotal() == null ? 0 : stock.getPiecesTotal());
    }

    private BaseInfo baseAdd(String serialNumber, String rfid, WorkshopOutbound model, WorkshopOutboundReq req, Integer wareId) throws ParseException {
        // * -生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认 7.车间抽检出库
        // 8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台 -10
        BaseInfo info = this.baseInfoService.updateWorkShopStatus(serialNumber, rfid, 2, 3, null, null, null, null);
        WorkshopOutboundDetail detail = new WorkshopOutboundDetail();
        detail.setOutId(model.getId());
        detail.setRfid(info.getRfid());
        detail.setSerialNumber(info.getSerialNumber());
        detail.setTareWeight(info.getTareWeight());
        detail.setGrossWeight(info.getGrossWeight());
        detail.setNetWeight(detail.getGrossWeight() - detail.getTareWeight());
        // 将rfid对应的冠字号范围，拼装成字符串，保存在此字端，用于页面展示
//        detail.setCrownBetweenStr(info.getCrownPrefix() + info.getCrownFrom() + "-" + info.getCrownPrefix() + info.getCrownTo());
        detail.setCrownBetweenStr(info.getCrownBetweenStr());
        detail.setOutTime(model.getCreateTime());
        detail.setWeightor(info.getWeightor());
        detail.setWeightorId(info.getWeightorId());
        detail.setCrownBetween(req.getCrownFrom() + "-" + req.getCrownTo());
        this.workshopOutboundDetailService.save(detail);
        // 车间日志表
//        String crownBetweenStr = info.getCrownPrefix() + info.getCrownFrom() + "-"
//                + info.getCrownPrefix() + info.getCrownTo();
        this.workshopLogService.addWorkshopLog(info, info.getCrownBetweenStr(), model.getSecuritiesId(),
                model.getSecurities(), info.getRfid(), info.getSerialNumber(),
                model.getDeptId(), model.getDept(),
                model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), 4, 1, 3, null, null);
        // 发送消息推送
        this.sendOutMsg(model, detail, info, wareId);
        return info;
    }

    private void sendReturnOutMsg(WorkshopOutbound model, WorkshopOutboundDetail detail, BaseInfo info, String wareId) {
        ViewInfoVO vo = new ViewInfoVO();
        BeanUtils.copyProperties(detail, vo);
        //  (value = "总库-1复核异常 1解缴待入库 2解缴中 3解缴已入库 5已发货 6返检出库 7返检已入库  ：
        vo.setStatus(6);
        vo.setInOrOut(3);
        vo.setOperateTime(detail.getOutTime());
        vo.setSecurities(model.getSecurities());
        vo.setChecker(model.getChecker());
        vo.setCreateBy(model.getCreateBy());
//        String s = info.getWeightDate() + " " + info.getWeightTime();
        vo.setPrintTime(info.getNewPrintTime());
        vo.setDept(model.getDept());
        vo.setLocWare(info.getLocWare());
        vo.setLocWareId(info.getLocWareId());
        WebSocketServer.sendMessageClient(String.valueOf(info.getLocWareId()), vo);
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        vo.setStatus(4);
        vo.setInOrOut(2);
//        Dict d = this.dictService.getByVal("locationOpt", wareId, null);
//        vo.setLocWare(d.getName());
        WebSocketServer.sendMessageClient(wareId, vo);
    }

    private void sendOutMsg(WorkshopOutbound model, WorkshopOutboundDetail detail, BaseInfo info, Integer wareId) {
        ViewInfoVO vo = new ViewInfoVO();
        BeanUtils.copyProperties(detail, vo);
        //  (value = "总库-1复核异常 1解缴待入库 2解缴中 3解缴已入库 5已发货：
        vo.setStatus(1);
        vo.setOperateTime(detail.getOutTime());
        vo.setSecurities(model.getSecurities());
        vo.setChecker(model.getChecker());
        vo.setCreateBy(model.getCreateBy());
//        String s = info.getWeightDate() + " " + info.getWeightTime();
        vo.setPrintTime(info.getNewPrintTime());
        vo.setDept(model.getDept());
        vo.setInOrOut(2);
        WebSocketServer.sendMessageClient("201", vo);
        WebSocketServer.sendMessageClient("202", vo);
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        vo.setStatus(2);
        vo.setInOrOut(3);
        vo.setLocWare(info.getLocWare());
        WebSocketServer.sendMessageClient(String.valueOf(wareId), vo);
    }

    private void sendOutMsg1(WorkshopOutboundReq req, String checker, String createBy, BaseInfo info, Integer wareId) {
        ViewInfoVO vo = new ViewInfoVO();
        //  (value = "总库-1复核异常 1解缴待入库 2解缴中 3解缴已入库 5已发货：
        vo.setOperateTime(new Date());
        vo.setSecurities(info.getSecurities());
        vo.setChecker(checker);
        vo.setCreateBy(createBy);
//        String s = info.getWeightDate() + " " + info.getWeightTime();
//        vo.setPrintTime(DateUtils.toDate(s, DateUtils.YY_MM_DD_HH_mm_ss));
        vo.setPrintTime(info.getNewPrintTime());
        vo.setDept(info.getDept());
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        vo.setStatus(4);
        WebSocketServer.sendMessageClient(String.valueOf(wareId), vo);
    }

    @Override
    @Transactional
    public void returnCheckWareOut(WareConfirmReq req, CacheUserVO user) {
        WorkshopOutbound model = this.getByOrderNo(req.getOrderNo());
        if (model == null) {
            throw new RetException("返检单不存在，单号=" + req.getOrderNo());
        }
        //<modify> by hliu date=20231127 返捡单总库出库，写到收入人里</modify>
        //</old>
//        model.setCreateId(user.getId());
//        model.setCreateBy(user.getRealName());
//        model.setCheckId(req.getCheckId());
//        model.setChecker(this.userService.getById(req.getCheckId()).getRealName());
        //</old>
        //<new>
        model.setReceiverCheckId(req.getCheckId());
        model.setReceiverCheck(this.userService.getById(req.getCheckId()).getRealName());
        model.setReceiverId(user.getId());
        model.setReceiver(user.getRealName());
        //</new>
        model.setPlanNum(req.getSerialNumberArray().size());
        model.setShopRealNum(0);
        Date now = new Date();
        Integer totalBox = 0;
        List<WorkshopOutboundDetail> list = new ArrayList<>();
        for (String serialNumber : req.getSerialNumberArray()) {
            WorkshopOutboundDetail detail = this.workshopOutboundDetailService.getBySerialNumber(serialNumber, model.getId());
            detail.setOutTime(now);
            this.workshopOutboundDetailService.updateById(detail);
            list.add(detail);
            // FIXME 查询状态为4:总库入库 修改返捡时间和状态9返检总库出库
            BaseInfo info = this.baseInfoService.updateReturnCheckStatus(detail.getRfid(), 4, 9, now);
            Dept dept = this.deptService.getById(info.getDeptId());
            Dict dict = this.dictService.getByVal("wareOpt", info.getLocWareId(), null);
            // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
            // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台
//            this.addLogistics(detail.getRfid(), detail.getSerialNumber(), info.getLocWareId(), dict.getName(), 9
//                    , model.getCheckId(), model.getChecker(),
//                    model.getCreateId(), model.getCreateBy());
            // FIXME 记录物流信息日志表
            this.logisticsService.add(detail.getRfid(), detail.getSerialNumber(),
                    info.getLocWareId(), null, 9, model.getCheckId(), model.getChecker(),
                    model.getCreateId(), model.getCreateBy(), null, null);
            OutboundVO outboundVO = new OutboundVO();
            if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
                    info.getOriginType() == 2) {
                outboundVO.setBoxQty(info.getQty());
            } else {
                outboundVO.setRacketQty(1);
            }
            this.stockService.stockSub(model.getSecuritiesId(), model.getSecurities(),
                    info.getLocAreaId(), info.getLocWareId(), dict.getName(), outboundVO.getRacketQty(), outboundVO.getBoxQty(), 0, 0);
            totalBox = totalBox + info.getQty();
            // 发送消息推送
            this.sendReturnOutMsg(model, detail, info, dept.getRemark());
        }
        Product product = this.productService.getById(model.getSecuritiesId());
        BaseInfo info = this.baseInfoService.getByRfid(list.get(0).getRfid(), 9);
        List<Stock> stockList = this.stockService.getStockList(info.getLocWareId(), model.getSecuritiesId());
        Integer wareTotal = WareUtil.piecesTotal(stockList, product);
//        Integer box = 0;
//        for (Stock stock : stockList) {
//            box = box + stock.getBoxNum() + stock.getRacketNum() * product.getBoxAmount();
//        }
//        Integer wareTotal = box * product.getAmountPerBox();
        // 添加总库台账 减 去错误编号拍子数量
        // 1.实物收发卡-付出（负收入）
        this.workshopAccountService.shopOut(2, model.getDeptId(), model.getDept(),
                model.getReceiverId(), model.getReceiver(), model.getReceiverCheckId(), model.getReceiverCheck(), null,
                totalBox * product.getAmountPerBox(), wareTotal, 1, 1, model.getDept(), info.getLocWareId(), model.getSecuritiesId(),
                model.getSecurities());

        // 如果全部返检出库明细单 都存在主库出库时间 主单状态改为主库已出库
        LambdaQueryWrapper<WorkshopOutboundDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.isNull(WorkshopOutboundDetail::getOutTime);
        long count = this.workshopOutboundDetailService.count(queryWrapper);
        if (count == 0L) {
            // 状态:1.新建 2.已出库 3.已入库
            model.setStatus(2);
        }
        this.updateById(model);
    }

    @Override
    @Transactional
    public void returnCheckShopIn(WareConfirmReq req, CacheUserVO user) {
        WorkshopOutbound model = this.getByOrderNo(req.getOrderNo());
        if (model == null) {
            throw new RetException("返检单不存在，单号=" + req.getOrderNo());
        }
        Integer total = 0;
        model.setShopRealNum(model.getShopRealNum() + req.getSerialNumberArray().size());
        //<modify> 返捡车间入库，将人员写入到付出人字段上 </modify>
        //<old>
//        model.setReceiverCheckId(req.getCheckId());
//        model.setReceiverCheck(this.userService.getById(req.getCheckId()).getRealName());
        //</old>
        //<new>
        model.setCreateId(user.getId());
        model.setCreateBy(user.getRealName());
        model.setCheckId(req.getCheckId());
        model.setChecker(this.userService.getById(req.getCheckId()).getRealName());
        Dept dept = this.deptService.getById(model.getDeptId());
        Date date = new Date();
        Dict dict = dictService.getByVal("shopWareOpt", dept.getRemark(), null);
        Integer wareId = Integer.parseInt(dict.getVal());
        Integer areaId = BusinessEnum.ELIMINATE_AREA.value();
        OutboundVO outboundVO = new OutboundVO();
        for (String serialNumber : req.getSerialNumberArray()) {
            WorkshopOutboundDetail detail = this.workshopOutboundDetailService.getBySerialNumber(
                    serialNumber, model.getId());
            detail.setAreaId(areaId);
            // FIXME 库位名从字典中取
            detail.setAreaCn(BusinessEnum.PRODUCT_AREA.getName());
//            detail.setWareId(wareId);
//            detail.setWareCn(dict.getName());
            detail.setInTime(date);
            this.workshopOutboundDetailService.updateById(detail);
            // FIXME 查询状态为9返检总库出库 修改车间返捡确认时间和状态10:返检车间确认
            BaseInfo info = this.baseInfoService.getByRfid(detail.getRfid(), 9);
            total = total + info.getQty();
            Integer oldWareId = info.getLocWareId();
            info.setStatus(10);
            Dict d = this.dictService.getByVal("locationOpt", wareId, null);
            info.setLocWare(d.getName());
            info.setLocWareId(wareId);
            info.setLocAreaId(areaId);
            this.baseInfoService.updateById(info);
            if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
                    info.getOriginType() == 2) {
                outboundVO.setBoxQty(outboundVO.getBoxQty() + info.getQty());
            } else {
                outboundVO.setRacketQty(outboundVO.getRacketQty() + 1);
            }
            // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
            // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台
//            this.addLogistics(detail.getRfid(), detail.getSerialNumber(), wareId, dict.getName(), 10,
//                    model.getReceiverCheckId(), model.getReceiverCheck(),
//                    model.getReceiverId(), model.getReceiver());
            // FIXME 记录物流信息日志表
            this.logisticsService.add(detail.getRfid(), detail.getSerialNumber(), wareId,
                    dict.getName(), 10, model.getReceiverCheckId(), model.getReceiverCheck(),
                    model.getReceiverId(), model.getReceiver(), null, null);
//            String crownBetweenStr = info.getCrownPrefix() + info.getCrownFrom() + "-"
//                    + info.getCrownPrefix() + info.getCrownTo();
            this.workshopLogService.addWorkshopLog(info, info.getCrownBetweenStr(), model.getSecuritiesId(),
                    model.getSecurities(), detail.getRfid(), detail.getSerialNumber(),
                    model.getDeptId(), model.getDept(),
                    model.getCreateId(), model.getCreateBy(),
                    model.getCheckId(), model.getChecker(), 1, 5, 4, null, null);
            // 发送消息推送
            this.sendReturnInMsg(model, detail, info, oldWareId, wareId);
        }

        // 成品库入库
        Product product = this.productService.getById(model.getSecuritiesId());
        Integer piecesTotal = total * product.getAmountPerBox();
        // FIXME  剔除品入库
        this.stockService.stockAdd(model.getSecuritiesId(), model.getSecurities(),
                areaId, wareId, dict.getName(), req.getSerialNumberArray().size(), 0, 0, 0);
        // 添加车间台账
        // 5封箱成品出入库台账-收入(付出负)
        this.workshopAccountService.shopIn(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), model.getOrderNo(), piecesTotal, piecesTotal, 5,
                1, "总库", null, model.getSecuritiesId(), model.getSecurities());
        // 5封箱成品出入库台账-付出(收入负)
        this.workshopAccountService.shopOut(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), model.getOrderNo(), piecesTotal, 0, 5, 1, "印花成品", null,
                model.getSecuritiesId(), model.getSecurities());
        Integer inventory =
                this.workshopAccountService.getLastInventory(model.getSecuritiesId(),
                        user.getDeptId()) + piecesTotal;
        // 4-收入(付出负)
        this.workshopAccountService.shopIn(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal, inventory, 4, 1, "封箱成品",
                null, model.getSecuritiesId(), model.getSecurities());
        this.stockService.getStock(model.getSecuritiesId(), wareId, 1);
        // 产成品库总数 + 入库数
        Integer wareTotal1 = this.stockService.getProductStockTotal(wareId, model.getSecuritiesId(), product) + piecesTotal;
//        Integer wareTotal1 = WareUtil.piecesTotal(stock1, product) + piecesTotal;
        // 2-收入(付出负)
        this.workshopAccountService.shopIn(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal, wareTotal1, 2, 2, "总库",
                null, model.getSecuritiesId(), model.getSecurities());
        // 2-付出(收入负)
        this.workshopAccountService.shopOut(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal, (wareTotal1 - piecesTotal),
                2,
                1, "剔除品", null, model.getSecuritiesId(), model.getSecurities());
        Integer eliminateStockTotal = this.stockService.getEliminateStockTotal(wareId, model.getSecuritiesId(), product);
        // 3-收入(付出负)
        this.workshopAccountService.shopIn(2,
                model.getDeptId(), model.getDept(), model.getCreateId(), model.getCreateBy(),
                model.getCheckId(), model.getChecker(), null, piecesTotal, eliminateStockTotal, 3, 1, "封箱成品",
                null, model.getSecuritiesId(), model.getSecurities());
        LambdaQueryWrapper<WorkshopOutboundDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.isNull(WorkshopOutboundDetail::getInTime);
        long count = this.workshopOutboundDetailService.count(queryWrapper);
        if (count == 0L) {
            // 状态:1.新建 2.已出库 3.已入库
            model.setStatus(3);
        }
        this.updateById(model);
    }

    @Override
    @Transactional
    public List<String> wareIn(ShopConfirmReq req, CacheUserVO user) {
        List<String> error = new ArrayList<>();
        WorkshopOutbound model = this.getByOrderNo(req.getOrderNo());
        if (model == null) {
            throw new RetException("解缴单不存在，单号=" + req.getOrderNo());
        }
//        model.setShopRealNum(req.getRealNum());
        model.setReceiverId(user.getId());
        model.setReceiver(user.getRealName());
        model.setReceiverCheckId(req.getCheckId());
        model.setReceiverCheck(this.userService.getById(req.getCheckId()).getRealName());
        Date date = new Date();
        Dict dict = dictService.getByVal("wareOpt", req.getWareId(), -1);
        Dict dictArea = dictService.getByVal(String.valueOf(req.getWareId()), req.getAreaId(), -1);
        Integer racketNum = 0;
        OutboundVO outboundVO = new OutboundVO();
        if (req.getAutoArray() != null && req.getAutoArray().size() > 0) {
            racketNum = racketNum + req.getAutoArray().size();
            for (String serialNumber : req.getAutoArray()) {
                BaseInfo info = this.baseWareAdd(serialNumber, model, req, error, date, dict, dictArea);
                if (info != null) {
                    outboundVO.setTotalBoxAmount(outboundVO.getTotalBoxAmount() + info.getQty());
                    if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
                            info.getOriginType() == 2) {
                        outboundVO.setBoxQty(outboundVO.getBoxQty() + info.getQty());
                    } else {
                        outboundVO.setRacketQty(outboundVO.getRacketQty() + 1);
                    }
                }
            }
        }
        if (req.getManualArray() != null && req.getManualArray().size() > 0) {
            racketNum = racketNum + req.getManualArray().size();
            for (String serialNumber : req.getManualArray()) {
                BaseInfo info = this.baseWareAdd(serialNumber, model, req, error, date, dict, dictArea);
                if (info != null) {
                    outboundVO.setTotalBoxAmount(outboundVO.getTotalBoxAmount() + info.getQty());
                    if ((info.getUnpackageStatus() != null && info.getUnpackageStatus().intValue() == 2) ||
                            info.getOriginType() == 2) {
                        outboundVO.setBoxQty(outboundVO.getBoxQty() + info.getQty());
                    } else {
                        outboundVO.setRacketQty(outboundVO.getRacketQty() + 1);
                    }
                }
            }
        }
        model.setRealNum(model.getRealNum() + racketNum - error.size());
        Product product = this.productService.getById(model.getSecuritiesId());
        this.stockService.stockAdd(model.getSecuritiesId(), model.getSecurities(), req.getAreaId(), req.getWareId(), dict.getName(), outboundVO.getRacketQty(), outboundVO.getBoxQty(), 0, 0);
        List<Stock> stockList = this.stockService.getStockList(req.getWareId(), model.getSecuritiesId());
        Integer wareTotal = WareUtil.piecesTotal(stockList, product);
        // 增加总库台账 减去错误编号拍子数量
        Integer total = outboundVO.getTotalBoxAmount() * product.getAmountPerBox();
        // 1.实物收发卡-收
        this.workshopAccountService.shopIn(1, model.getDeptId(), model.getDept(),
                model.getCreateId(), model.getCreateBy(), model.getCheckId(), model.getChecker(),
                model.getOrderNo(), total, wareTotal, 1, 2, model.getDept(),
                req.getWareId(), model.getSecuritiesId(), model.getSecurities());
        // 如果全部返检出库明细单 都存在确认入库时间 主单状态改为车间已入库
        LambdaQueryWrapper<WorkshopOutboundDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(WorkshopOutboundDetail::getOutId, model.getId());
        queryWrapper.isNull(WorkshopOutboundDetail::getInTime);
        long count = this.workshopOutboundDetailService.count(queryWrapper);
        if (count == 0L) {
            // 状态:1.新建 2.已出库 3.已入库
            model.setStatus(3);
        }
        this.updateById(model);
        return error;
    }

    private BaseInfo baseWareAdd(String serialNumber, WorkshopOutbound model, ShopConfirmReq req, List<String> error, Date date, Dict dict, Dict dictArea) {
        WorkshopOutboundDetail detail = this.workshopOutboundDetailService.getBySerialNumber(serialNumber, model.getId());
        if (detail == null || !detail.getOutId().equals(model.getId())) {
            // 记录错误的编号
            error.add(serialNumber);
            return null;
        }
        detail.setAreaId(req.getAreaId());
        // FIXME 库位名从字典中取
        detail.setAreaCn(dictArea.getName());
        detail.setWareId(req.getWareId());
        detail.setWareCn(dict.getName());
        detail.setInTime(date);
        this.workshopOutboundDetailService.updateById(detail);
        // 查询状态为车间出库的
        BaseInfo info = this.baseInfoService.getByRfid(detail.getRfid(), 3);
        Integer shopWareId = info.getLocWareId();
        // 生命周期状态:1初始登记 2:车间入库 3:车间出库 4:总库入库 5:主库抽检出库 6.车间抽检入库确认
        // 7.车间抽检出库8 主库抽检返回确认 9返检总库出库 10:返检车间确认 11拆包 12：已发货 13:返回机台
        info.setStatus(4);
        Dict d = this.dictService.getByVal("locationOpt", req.getWareId(), null);
        info.setLocWare(d.getName());
        info.setLocWareId(req.getWareId());
        info.setLocAreaId(req.getAreaId());
        this.baseInfoService.updateById(info);
        // FIXME 记录物流信息日志表
        this.logisticsService.add(detail.getRfid(), detail.getSerialNumber(), req.getWareId(), dict.getName(), 4, model.getReceiverCheckId(), model.getReceiverCheck(), model.getReceiverId(), model.getReceiver(), null, null);
        // 发送消息推送
        this.sendInMsg(model, detail, info, shopWareId);
        return info;
    }

    private void sendInMsg(WorkshopOutbound model, WorkshopOutboundDetail detail, BaseInfo info, Integer shopWareId) {
        ViewInfoVO vo = new ViewInfoVO();
        BeanUtils.copyProperties(detail, vo);
        vo.setOperateTime(detail.getInTime());
        vo.setSecurities(model.getSecurities());
        vo.setChecker(model.getReceiverCheck());
        vo.setCreateBy(model.getReceiver());
        vo.setPrintTime(info.getNewPrintTime());
        vo.setDept(model.getDept());
        vo.setLocWare(info.getLocWare());
        vo.setLocWareId(info.getLocWareId());
        //  (value = "总库-1复核异常 1解缴待入库 2解缴中 3解缴已入库 5已发货：
        vo.setStatus(3);
        vo.setInOrOut(2);
        WebSocketServer.sendMessageClient("201", vo);
        WebSocketServer.sendMessageClient("202", vo);
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        Dict d = this.dictService.getByVal("locationOpt", shopWareId, null);
        // 临时库位置信息不变
        vo.setLocWare(d.getName());
        vo.setInOrOut(3);
        WebSocketServer.sendMessageClient(String.valueOf(shopWareId), vo);
    }

    private void sendReturnInMsg(WorkshopOutbound model, WorkshopOutboundDetail detail, BaseInfo info, Integer wareId, Integer shopWareId) {
        ViewInfoVO vo = new ViewInfoVO();
        BeanUtils.copyProperties(detail, vo);
        vo.setOperateTime(detail.getInTime());
        vo.setSecurities(model.getSecurities());
        vo.setChecker(model.getReceiverCheck());
        vo.setCreateBy(model.getReceiver());
        vo.setPrintTime(info.getNewPrintTime());
        vo.setDept(model.getDept());
        vo.setLocWare(info.getLocWare());
        vo.setLocWareId(info.getLocWareId());
        //  (value = "总库-1复核异常 1解缴待入库 2解缴中 3解缴已入库 5已发货 6返检出库 7返检已库  ：
        vo.setStatus(7);
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        vo.setInOrOut(3);
        Dict d = this.dictService.getByVal("locationOpt", wareId, null);
        vo.setLocWare(d.getName());
        WebSocketServer.sendMessageClient(String.valueOf(wareId), vo);
        // ：临时库-1复核异常 1入库 2解缴出库 3解缴已入库 4返检待入库 5返检已入库 6内部出库
        vo.setLocWare(info.getLocWare());
        vo.setLocWareId(info.getLocWareId());
        vo.setStatus(5);
        vo.setInOrOut(2);
        WebSocketServer.sendMessageClient(String.valueOf(shopWareId), vo);
    }


    /**
     * 管理人员操作-创建返检单
     *
     * @param req
     * @param user
     */
    @Override
    @Transactional
    public void create(WorkshopOutboundAddReq req, CacheUserVO user) {
        // 取总库已入库的
        Product product = productService.getById(req.getSecuritiesId());
        WorkshopOutbound model = new WorkshopOutbound();
        model.setSecuritiesId(req.getSecuritiesId());
        model.setSecurities(product.getSecurities());
        //状态:1.新建 2.已出库 3.已入库 解缴单状态 直接出库
        model.setStatus(1);
        // 车间出库单单号
        model.setOrderNo(this.creadeOutOrderNo(product.getSecurities(), user.getDeptId()));
        // 单据类型： 1: 解缴单  2: 返检单
        model.setOrderType(2);
        model.setDeptId(user.getDeptId());
        model.setDept(user.getDept());
        // BaseInfo取来源部门
//        model.setCreateId(user.getId());
//        model.setCreateBy(user.getRealName());
        model.setPlanNum(req.getDetailList().size());
        // 合计箱数=拍数*每拍箱数
        model.setTotalBoxAmount(model.getPlanNum() * product.getBoxAmount());
        // 合计枚数=总箱数*每箱数量
        model.setTotalAmount(model.getTotalBoxAmount() * product.getAmountPerBox());
        // 添加返检主表
        this.save(model);
        // 添加返检子表
        this.addCreateSub(model, req.getDetailList());
        // 单号增加
        this.addOutOrderNo(model.getSecurities(), user.getDeptId());

    }

    private void addCreateSub(WorkshopOutbound model, List<BaseInfoVO> list) {
        this.workshopOutboundDetailService.remove(new LambdaQueryWrapper<WorkshopOutboundDetail>().eq(WorkshopOutboundDetail::getOutId, model.getId()));
        for (BaseInfoVO vo : list) {
            // 总库入库的
            BaseInfo info = this.baseInfoService.getByRfid(vo.getRfid(), 4);
            WorkshopOutboundDetail detail = new WorkshopOutboundDetail();
//            detail.setId(vo.getId());
            detail.setRfid(vo.getRfid());
            detail.setSerialNumber(vo.getSerialNumber());
            detail.setOutId(model.getId());
            detail.setTareWeight(info.getTareWeight());
            detail.setGrossWeight(info.getGrossWeight());
            // 净重
            detail.setNetWeight(detail.getGrossWeight() - detail.getTareWeight());
//            detail.setCrownBetweenStr(info.getCrownPrefix() + info.getCrownFrom() + "-" + info.getCrownPrefix() + info.getCrownTo());
            detail.setCrownBetweenStr(info.getCrownBetweenStr());
            detail.setWeightor(info.getWeightor());
            detail.setWeightorId(info.getWeightorId());
            detail.setWareId(info.getLocWareId());
            Dict dict = this.dictService.getByVal("wareOpt", info.getLocWareId(), null);
            detail.setWareCn(dict.getName());
            detail.setAreaId(info.getLocAreaId());
            Dict dict1 = this.dictService.getByVal(String.valueOf(info.getLocWareId()), info.getLocAreaId(), null);
            detail.setAreaCn(dict1.getName());
            this.workshopOutboundDetailService.save(detail);

        }
    }

    private WorkshopOutbound getByOrderNo(String orderNo) {
        LambdaQueryWrapper<WorkshopOutbound> queryWrapper = new LambdaQueryWrapper<>();
        // 单号
        queryWrapper.eq(WorkshopOutbound::getOrderNo, orderNo);
        return this.getOne(queryWrapper);
    }

    public String creadeOutOrderNo(String securities, Integer deptId) {
        // 车间出库单号由系统自动生成，编号由“品名+部门编号+T2+3位数字编号”构成，Y211-1T2001
        // 车间出库单号
        String resp = null;
        String key = "workshopout_num:" + deptId + ":" + securities;
        if (redisService.hasKey(key)) {
            resp = securities + "-" + deptId + "T2" + String.format("%03d",
                Integer.parseInt(this.redisService.get(key)) + 1);
        } else {
            this.redisService.set(key, "0");
            resp = securities + "-" + deptId + "T2" + String.format("%03d", 1);
        }
        return resp;
    }

    public void addOutOrderNo(String securities, Integer deptId) {
        String key = "workshopout_num:" + deptId + ":" + securities;
        this.redisService.set(key,
            String.valueOf(Integer.parseInt(this.redisService.get(key)) + 1));
    }

    @Override
    @Transactional
    public void delete(Integer id) {
        this.removeById(id);
        this.workshopOutboundDetailService.remove(
            new LambdaQueryWrapper<WorkshopOutboundDetail>().eq(WorkshopOutboundDetail::getOutId,
                id));
    }

    @Override
    @Transactional
    public void update(WorkshopOutboundAddReq req, CacheUserVO user) {
        WorkshopOutbound model = this.getById(req.getId());
        this.addCreateSub(model, req.getDetailList());
    }

}
