package cn.superdesk.uniorder.service.thirdpart.qchjc.statistics.impl;

import cn.hutool.core.io.FileUtil;
import cn.superdesk.uniorder.common.enums.thirdpart.jd.SysQuartzScheduleTaskStatementOfAccountRunStateEnum;
import cn.superdesk.uniorder.common.excel.ExcelResponse;
import cn.superdesk.uniorder.common.numerical.AnBeanUtils;
import cn.superdesk.uniorder.common.numerical.AnDateUtils;
import cn.superdesk.uniorder.common.rest.response.R;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.dao.entity.base.UnifiedOrderGoods;
import cn.superdesk.uniorder.dao.entity.statistics.receivable.StReceivableDayDataEntity;
import cn.superdesk.uniorder.dao.entity.statistics.receivable.StReceivableDayDataEntityExample;
import cn.superdesk.uniorder.dao.entity.statistics.receivable.StReceivableDetailDataEntity;
import cn.superdesk.uniorder.dao.entity.statistics.receivable.StReceivableDetailDataEntityExample;
import cn.superdesk.uniorder.dao.entity.sys.notify.SysNotify;
import cn.superdesk.uniorder.dao.mapper._statistics.*;
import cn.superdesk.uniorder.dao.mapper.base.UnifiedOrderGoodsMapper;
import cn.superdesk.uniorder.dto._constant.ResourceConstant;
import cn.superdesk.uniorder.dto.param.accounting.statistics.ReceivableDetailDataQueryParam;
import cn.superdesk.uniorder.dto.param.accounting.statistics.ReceivableStatisticsPageQueryParam;
import cn.superdesk.uniorder.dto.vo._statistics.*;
import cn.superdesk.uniorder.notify.NotifyUtils;
import cn.superdesk.uniorder.service.thirdpart.qchjc.statistics.ReceivableStatisticsService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.PageHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author xc
 * @description
 * @date 2022/4/1 11:43
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ReceivableStatisticsServiceImpl implements ReceivableStatisticsService {

    @Autowired
    private ResourceConstant excelExportConstant;

    private static final String TEMPLATE_EXPORT_EXCEL_RECEIVABLE_STATISTIC = "exportReceivableStatistic.xlsx";


    //应收查询Mapper
    private final ReceivableStatisticsMapper receivableStatisticsMapper;

    //应收明细Mapper
    private final StReceivableDetailDataEntityMapper stReceivableDetailDataEntityMapper;

    //应收每日明细
    private final StReceivableDayDataEntityMapper stReceivableDayDataEntityMapper;

    private final UnifiedOrderGoodsMapper unifiedOrderGoodsMapper;


    //应付明细Mapper
    private final StPayableDetailDataEntityMapper stPayableDetailDataEntityMapper;

    //应付每日明细
    private final StPayableDayDataEntityMapper stPayableDayDataEntityMapper;


    /**
     * 查询应收明细数据
     *
     * @return
     */
    @Override
    public List<ReceivableDetailDataVo> queryReceivableDetailData(ReceivableDetailDataQueryParam queryParam) {
        return receivableStatisticsMapper.queryReceivableDetailData(queryParam);
    }

    /**
     * 查询应付明细数据
     * @return
     */
    //@Override
    //public List<PayableDetailDataVo> queryPayableDetailData(ReceivableDetailDataQueryParam queryParam) {
    //    return receivableStatisticsMapper.queryPayableDetailData(queryParam);
    //}

//    /**
//     * 查询应收明细数据
//     * @return
//     */
//    @Override
//    public List<ReceivableDayDataVo> queryReceivableDayData(ReceivableDayDataQueryParam queryParam) {
//        return receivableStatisticsMapper.queryReceivableDayData(queryParam);
//    }

    /**
     * #积分结算_对账单状态变更为“已确认”时
     * 保存应收明细数据
     * 异常需要回滚
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveReceivableDetailData(ReceivableDetailDataQueryParam queryParam, LoginSysUser loginSysUser) {

        log.info("\n===>保存应收明细数据-saveReceivableDetailData 参数:{}", JSON.toJSONString(queryParam));

        //查询应收明细数据
        List<ReceivableDetailDataVo> receivableDetailDataVoList = this.queryReceivableDetailData(queryParam);

        log.info("\n===>保存应收明细数据-查询应收明细数据 条数:{}", receivableDetailDataVoList.size());

        //保存应用明细数据
        int num = 1;
        for (ReceivableDetailDataVo receivableDetailDataVo : receivableDetailDataVoList) {

            log.info("\n===>保存应收明细数据-当前处理第:{} 条", num);
            ++num;

            StReceivableDetailDataEntity stReceivableDetailDataEntity = AnBeanUtils.copy(receivableDetailDataVo, StReceivableDetailDataEntity.class);

            if (stReceivableDetailDataEntity.getCreditType().intValue() == 0) {
                stReceivableDetailDataEntity.setCreditType(Byte.valueOf("0"));
                stReceivableDetailDataEntity.setCreditName("微信支付");
            } else {
                stReceivableDetailDataEntity.setCreditType(Byte.valueOf("1"));
                stReceivableDetailDataEntity.setCreditName("福利积分");
            }

            //#生成该应收账款流水数据时计算：应收账款=收入（不含税）+税额，保留小数点两位
            //#income_no_tax+tax_amount 代码计算
            stReceivableDetailDataEntity.setReceivableAmount(stReceivableDetailDataEntity.getIncomeNoTax().add(stReceivableDetailDataEntity.getTaxAmount()));

            if (StringUtils.isNotEmpty(stReceivableDetailDataEntity.getRefundOrderSn())) { //售后订单
                //转为负值
                stReceivableDetailDataEntity.setGoodsQuantity(-stReceivableDetailDataEntity.getGoodsQuantity());
                stReceivableDetailDataEntity.setIncomeNoTax(stReceivableDetailDataEntity.getIncomeNoTax().negate());
                stReceivableDetailDataEntity.setReceivableAmount(stReceivableDetailDataEntity.getReceivableAmount().negate());
            }

            stReceivableDetailDataEntity.setCreateUserId(loginSysUser.getSysUserId());
            stReceivableDetailDataEntity.setCreatedAt(new Date());
            stReceivableDetailDataEntity.setCreateUserName(loginSysUser.getFullName());
            stReceivableDetailDataEntity.setAccountAt(new Date());
            stReceivableDetailDataEntity.setAccountState(Byte.valueOf("0"));//入账状态 0未入账 1已入账 3撤销入账 4已撤销
            stReceivableDetailDataEntity.setAccountingOrderSn(receivableDetailDataVo.getAccountingOrderSn());
            stReceivableDetailDataEntity.setAccountingState(Byte.valueOf("1"));//#对账(结算)单 状态:0-未确认;1-已确认;-1-已取消

            //客户结算中心id
            StReceivableDetailDataEntityExample stReceivableDetailDataEntityExample = new StReceivableDetailDataEntityExample();
            stReceivableDetailDataEntityExample.createCriteria().andCreditIdEqualTo(stReceivableDetailDataEntity.getCreditId());

            //数据库是否存在 不存在 才保存明细和 改应收统计状态
            Integer accountStateNum = stReceivableDetailDataEntityMapper.selectByExample(stReceivableDetailDataEntityExample).size();
            if (accountStateNum == 0) { //定时任务保存 不做更新 需要时在修改

                //明细数据
                stReceivableDetailDataEntityMapper.insertSelective(stReceivableDetailDataEntity);

                //更新商品-应收统计状态 为 已统计
                UnifiedOrderGoods unifiedOrderGoods = new UnifiedOrderGoods();
                unifiedOrderGoods.setId(stReceivableDetailDataEntity.getGoodsId());
                //unifiedOrderGoods.setReceivableStatisticsState(ReceivablePayableStatisticsStatusEnum.未冲销.getStatus());
                unifiedOrderGoods.setUpdateTime(new Date());
                unifiedOrderGoodsMapper.updateByPrimaryKeySelective(unifiedOrderGoods);
            } else {
                log.info("\n===>保存应收明细数据-saveReceivableDetailData creditId-客户结算中心id:{} 存在入明细数据 请检查程序\n", stReceivableDetailDataEntity.getCreditId());
            }

        }

    }

    /**
     * 应收明细数据定时任务到每日数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveReceivableDetailDataToDayData(ReceivableDetailDataQueryParam queryParam, LoginSysUser loginSysUser) {

        //查询应收明细数据_已汇总
        log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData 参数:{}", JSON.toJSONString(queryParam));

        List<ReceivableDayDataVo> receivableDayDataVoList = receivableStatisticsMapper.queryReceivableDayData(queryParam);

        log.info("\n===>应收明细数据定时任务到每日数据-查询应收每日数据 条数:{}", receivableDayDataVoList.size());

        int num = 1;
        for (ReceivableDayDataVo receivableDayDataVo : receivableDayDataVoList) {

            log.info("\n===>应收明细数据定时任务到每日数据-当前处理第:{} 条", num);
            ++num;

            //应收每日数据
            StReceivableDayDataEntity stReceivableDayDataEntity = AnBeanUtils.copy(receivableDayDataVo, StReceivableDayDataEntity.class);
            //入账类型 1系统入账 2手工记账
            stReceivableDayDataEntity.setAccountType(Byte.valueOf("1"));
            stReceivableDayDataEntity.setCreateUserId(loginSysUser.getSysUserId());
            stReceivableDayDataEntity.setCreatedAt(new Date());
            stReceivableDayDataEntity.setAccountAt(new Date());
            stReceivableDayDataEntity.setCreateUserName(loginSysUser.getFullName());
            stReceivableDayDataEntity.setAccountType(Byte.valueOf("1"));//入账类型 1-正常入账 2-撤销入账
            stReceivableDayDataEntity.setAccountingOrderSn(receivableDayDataVo.getAccountingOrderSn());
            stReceivableDayDataEntity.setAccountWay(Byte.valueOf("0"));//入账方式 0系统入账 1手工记账


            StReceivableDayDataEntityExample stReceivableDayDataEntityExample = new StReceivableDayDataEntityExample();
            stReceivableDayDataEntityExample.createCriteria().andDetailIdEqualTo(receivableDayDataVo.getDetailId()).andAccountTypeEqualTo(Byte.valueOf("1"));//入账类型 1-正常入账 2-撤销入账 3-冲销账款
            Integer stDayDataNum = stReceivableDayDataEntityMapper.selectByExample(stReceivableDayDataEntityExample).size();
            if (stDayDataNum == 0) { //明细数据Id 在每日数据里为唯一

                //重复判断
                stReceivableDayDataEntityMapper.insert(stReceivableDayDataEntity);

                //明细数据 反写状态 入账日期-入账状态
                //credit_id + finish_time;
                String detailId = receivableDayDataVo.getDetailId();
                String[] detailIds = detailId.split(",");
                for (String id : detailIds) {

                    StReceivableDetailDataEntity updateDataEntity = new StReceivableDetailDataEntity();
                    updateDataEntity.setId(Long.parseLong(id));
                    updateDataEntity.setAccountAt(receivableDayDataVo.getAccountAt());
                    updateDataEntity.setAccountState(Byte.valueOf("1"));//入账状态 0未入账 1已入账 3撤销入账 4已撤销
                    updateDataEntity.setReceivableDayId(stReceivableDayDataEntity.getId());//入账id
                    updateDataEntity.setAccountingOrderSn(stReceivableDayDataEntity.getAccountingOrderSn());//流水所属对账单编号

                    updateDataEntity.setUpdateUserId(loginSysUser.getSysUserId());
                    updateDataEntity.setUpdateTime(new Date());
                    updateDataEntity.setUpdateUserName(loginSysUser.getFullName());
                    stReceivableDetailDataEntityMapper.updateByPrimaryKeySelective(updateDataEntity);
                }
            } else {
                log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData detailId-明细数据Id:{} 存在入每日数据 请检查程序\n", receivableDayDataVo.getDetailId());
            }
        }


        //保存应收明细数据到 应收每日数据

    }

    /**
     * 实收_积分结算_对账单_回款状态变更为“已回款”时
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receivableReturnedMoney(ReceivableDetailDataQueryParam queryParam, LoginSysUser loginSysUser) {


        log.info("\n===>实收_积分结算_对账单_回款状态变更为“已回款”时-receivableReturnedMoney 参数:{}", JSON.toJSONString(queryParam));

        //（1）将“应收流水表”中所属该流水对账单编号下所有应收流水（含微信支付应收流水）：
        //·回款冲销对账单编号：填充为该回款状态变更为“已回款”的对账单编号内容；
        //·冲销人id&冲销人&冲销时间：均取当前对账单变更为已回款状态的操作人及操作时间；
        //·更新人&更新人id&更新时间：均取当前对账单变更为已回款状态的操作人及操作时间；

        StReceivableDetailDataEntityExample example = new StReceivableDetailDataEntityExample();
        example.createCriteria().andAccountingOrderSnEqualTo(queryParam.getAccountingOrderSn());
        List<StReceivableDetailDataEntity> listStReceivableDetailDataEntitys = stReceivableDetailDataEntityMapper.selectByExample(example);
        for (StReceivableDetailDataEntity stReceivableDetailDataEntity : listStReceivableDetailDataEntitys) {

            StReceivableDetailDataEntity updateStReceivableDetailDataEntity = new StReceivableDetailDataEntity();
            updateStReceivableDetailDataEntity.setId(stReceivableDetailDataEntity.getCreditId());
            updateStReceivableDetailDataEntity.setAccountState(Byte.valueOf("1"));//对账(结算)单 0未入账 1已入账 3撤销入账 4已撤销
            updateStReceivableDetailDataEntity.setChargeStatus(Byte.valueOf("2"));//冲销状态 1-未冲销 2-已冲销 3-已撤销'
            updateStReceivableDetailDataEntity.setChargeUserId(loginSysUser.getSysUserId());
            updateStReceivableDetailDataEntity.setChargeUserName(loginSysUser.getFullName());
            updateStReceivableDetailDataEntity.setCollectionOrderSn(queryParam.getAccountingOrderSn());

            updateStReceivableDetailDataEntity.setUpdateUserId(loginSysUser.getSysUserId());
            updateStReceivableDetailDataEntity.setUpdateTime(new Date());
            updateStReceivableDetailDataEntity.setUpdateUserName(loginSysUser.getFullName());

            stReceivableDetailDataEntityMapper.updateByPrimaryKeySelective(updateStReceivableDetailDataEntity);

        }

        //（2）将“应收流水表”中所属该冲销对账单编号的所有应收流水生成对应的应收汇总数据：
        // ①前置条件：所属该对账单中最后一条应收流水数据变更成功时；
        // ②汇总对象：将“应收流水表”中所属冲销该对账单&入账状态“已入账”&冲销状态为“未冲销”的应收流水进行汇总，
        // 不含非该冲销对账单或入账状态非“已入账”或冲销状态非“未冲销”的应收流水；


        //入账状态0未入账 1已入账 3撤销入账 4已撤销
        //查询应收明细数据_已汇总
        log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData 参数:{}", JSON.toJSONString(queryParam));

        queryParam.setAccountingOrderSn(queryParam.getAccountingOrderSn());
        queryParam.setAccountState(Byte.valueOf("1"));//对账(结算)单 0未入账 1已入账 3撤销入账 4已撤销
        queryParam.setChargeStatus(Byte.valueOf("1"));//冲销状态 1-未冲销 2-已冲销 3-已撤销
        List<ReceivableDayDataVo> receivableDayDataVoList = receivableStatisticsMapper.queryReceivableDayData(queryParam);

        log.info("\n===>应收明细数据定时任务到每日数据-查询应收每日数据 条数:{}", receivableDayDataVoList.size());

        int num = 1;
        for (ReceivableDayDataVo receivableDayDataVo : receivableDayDataVoList) {

            log.info("\n===>应收明细数据定时任务到每日数据-当前处理第:{} 条", num);
            ++num;


            //应收每日数据
            StReceivableDayDataEntity stReceivableDayDataEntity = AnBeanUtils.copy(receivableDayDataVo, StReceivableDayDataEntity.class);
            //入账类型 1系统入账 2手工记账
            stReceivableDayDataEntity.setAccountType(Byte.valueOf("1"));
            stReceivableDayDataEntity.setCreateUserId(loginSysUser.getSysUserId());
            stReceivableDayDataEntity.setCreatedAt(new Date());
            stReceivableDayDataEntity.setCreateUserName(loginSysUser.getFullName());
            stReceivableDayDataEntity.setAccountType(Byte.valueOf("3"));//入账类型 1-正常入账/2-撤销入账/3-冲销账款
            stReceivableDayDataEntity.setAccountingOrderSn(receivableDayDataVo.getAccountingOrderSn());
            //stReceivableDayDataEntity.setIncomeNoTax(receivableDayDataVo.getIncomeNoTax().negate());
            //stReceivableDayDataEntity.setTaxAmount(receivableDayDataVo.getTaxAmount().negate());
            //stReceivableDayDataEntity.setReceivableAmount(receivableDayDataVo.getReceivableAmount().negate());
            stReceivableDayDataEntity.setServiceNoName(receivableDayDataVo.getServiceNoName());
            stReceivableDayDataEntity.setAccountWay(Byte.valueOf("0"));


            StReceivableDayDataEntityExample stReceivableDayDataEntityExample = new StReceivableDayDataEntityExample();
            stReceivableDayDataEntityExample.createCriteria().andDetailIdEqualTo(receivableDayDataVo.getDetailId()).andAccountTypeEqualTo(Byte.valueOf("3"));//入账类型 1-正常入账 2-撤销入账 3-冲销账款;
            Integer stDayDataNum = stReceivableDayDataEntityMapper.selectByExample(stReceivableDayDataEntityExample).size();
            if (stDayDataNum == 0) { //明细数据Id 在每日数据里为唯一


                //重复判断
                stReceivableDayDataEntityMapper.insertSelective(stReceivableDayDataEntity);

                //明细数据 反写状态 入账日期-入账状态
                //credit_id + finish_time;
                String detailId = receivableDayDataVo.getDetailId();
                String[] detailIds = detailId.split(",");
                for (String id : detailIds) {

                    //        该汇总数据所关联的应收流水_入账状态：变更为“已撤销”；
                    //        该汇总数据所关联的应收流水_入账id：保持原入账id不变；
                    //        该汇总数据所关联的应收流水_冲销/撤销入账id：取该汇总数据的入账id；
                    //        该汇总数据所关联的应收流水_更新时间：填充该汇总数据的创建时间；

                    StReceivableDetailDataEntity updateDataEntity = new StReceivableDetailDataEntity();
                    updateDataEntity.setId(Long.parseLong(id));
                    updateDataEntity.setAccountAt(receivableDayDataVo.getAccountAt());
                    updateDataEntity.setAccountState(Byte.valueOf("1"));//入账状态 0未入账 1已入账 3撤销入账 4已撤销
                    updateDataEntity.setReceivableDayId(stReceivableDayDataEntity.getId());//入账id
                    updateDataEntity.setChargeReceivableDayId(stReceivableDayDataEntity.getId());
                    updateDataEntity.setAccountingOrderSn(stReceivableDayDataEntity.getAccountingOrderSn());//流水所属对账单编号
                    updateDataEntity.setChargeStatus(Byte.valueOf("2")); //冲销状态 1-未冲销 2-已冲销 3-已撤销

                    updateDataEntity.setUpdateUserId(loginSysUser.getSysUserId());
                    updateDataEntity.setUpdateTime(new Date());
                    updateDataEntity.setUpdateUserName(loginSysUser.getFullName());
                    stReceivableDetailDataEntityMapper.updateByPrimaryKeySelective(updateDataEntity);
                }
            } else {
                log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData detailId-明细数据Id:{} 存在入每日数据 请检查程序\n", receivableDayDataVo.getDetailId());
            }
        }


    }

    /**
     * 3.2.1.2 积分结算_对账单状态变更为“已取消”时
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receivableCancelAccounting(ReceivableDetailDataQueryParam queryParam, LoginSysUser loginSysUser) {

        log.info("\n===>应手_积分结算_对账单状态变更为“已取消”时-receivableCancelAccounting 参数:{}", JSON.toJSONString(queryParam));

        //第一步：该对账单“应收流水”处理
        //（1）将“应收流水表”所属该对账单编号的应收流水所对应的所有订单流水（订单流水id）：其应收统计状态变更为“未统计”；
        //根据账单编号 更新 所有订单流水 其应收统计状态变更为“未统计” where = "已统计" 部分
        //UniorderAccountingScoreCustomerStrategy.opCancelAccounting


        //（2）将“应收流水表”所属该对账单编号下所有应收流水：
        //·对账单状态：变更为“已取消”；
        //·冲销状态：变更为“已撤销”；
        //·冲销人id&冲销人&冲销时间：均取该对账单变更为已取消状态的操作人及时间；

        //（3）检查“应收流水表”所属该对账单编号下所有应收流水的入账状态：
        // ①若该应收流水的入账状态为“未入账”：
        // ·该应收流水的入账状态：变更为“已撤销”；
        // ·该应收流水的更新人&更新人id&更新时间：均取当前已取消对账单的操作人及操作时间；
        // ②若该应收流水的入账状态为“已入账”&撤销入账id为空：将该应收流水的入账状态变更为“撤销入账”


        StReceivableDetailDataEntityExample example = new StReceivableDetailDataEntityExample();
        example.createCriteria().andAccountingOrderSnEqualTo(queryParam.getAccountingOrderSn());
        List<StReceivableDetailDataEntity> listStReceivableDetailDataEntitys = stReceivableDetailDataEntityMapper.selectByExample(example);

        //查询应收明细数据
        //List<ReceivableDetailDataVo> receivableDetailDataVoList = this.queryReceivableDetailData(queryParam);
        for (StReceivableDetailDataEntity stReceivableDetailDataEntity : listStReceivableDetailDataEntitys) {


            //更新商品-应收统计状态 为 已统计
            StReceivableDetailDataEntity updateStReceivableDetailDataEntity = new StReceivableDetailDataEntity();
            updateStReceivableDetailDataEntity.setId(stReceivableDetailDataEntity.getId());
            updateStReceivableDetailDataEntity.setAccountingState(Byte.valueOf("-1"));//对账状态 1-已确认;-1-已取消
            updateStReceivableDetailDataEntity.setChargeStatus(Byte.valueOf("3"));//冲销状态 1-未冲销 2-已冲销 3-已撤销
            updateStReceivableDetailDataEntity.setChargeUserId(loginSysUser.getSysUserId());
            updateStReceivableDetailDataEntity.setChargeUserName(loginSysUser.getFullName());

            //入账状态 0未入账 1已入账 3撤销入账 4已撤销
            if (stReceivableDetailDataEntity.getAccountState().equals(Byte.valueOf("0"))) {//未入账
                updateStReceivableDetailDataEntity.setAccountState(Byte.valueOf("4"));//已撤销
            } else if (stReceivableDetailDataEntity.getAccountState().equals(Byte.valueOf("1")) && null == stReceivableDetailDataEntity.getChargeReceivableDayId()) {// ②若该应收流水的入账状态为“已入账”&撤销入账id为空：
                updateStReceivableDetailDataEntity.setAccountState(Byte.valueOf("3"));//撤销入账
            }

            updateStReceivableDetailDataEntity.setUpdateUserId(loginSysUser.getSysUserId());
            updateStReceivableDetailDataEntity.setUpdateTime(new Date());
            updateStReceivableDetailDataEntity.setUpdateUserName(loginSysUser.getFullName());

            stReceivableDetailDataEntityMapper.updateByPrimaryKeySelective(updateStReceivableDetailDataEntity);

        }


//        （4）入账状态处理完成后，对该对账单编号下所有入账状态为“撤销入账”的应收流水处理：
//        ①若无撤销入账的应收流水：即无需处理；
//        ②若有，对该对账单编号下所有入账状态“撤销入账”的应收流水数据：按同流水所属对账单编号&同客户（客户结算中心id）&同订单类型&同积分类型（积分支付/微信支付）&同税率的应收流水数据进行汇总统计出“应收汇总”数据；
//        ·入账类型：默认为“撤销入账”；
//        ·入账方式：默认为“系统入账”；
//        ·入账日期：取该组汇总数据汇总时第一条应收流水的冲销时间，年-月-日，即该对账单取消的时间；
//        ·“收入(不含税)、税额、应收账款”金额：负值[该字段的汇总数据总和]；
//        ·其他应收汇总字段：请参照《3.1.4 应收汇总》需求；
//        ·每条“撤销入账”应收汇总数据创建成功时：

//        该汇总数据所关联的应收流水_入账状态：变更为“已撤销”；
//        该汇总数据所关联的应收流水_入账id：保持原入账id不变；
//        该汇总数据所关联的应收流水_冲销/撤销入账id：取该汇总数据的入账id；
//        该汇总数据所关联的应收流水_更新时间：填充该汇总数据的创建时间；


        //入账状态0未入账 1已入账 3撤销入账 4已撤销
        //查询应收明细数据_已汇总
        log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData 参数:{}", JSON.toJSONString(queryParam));

        queryParam.setAccountingOrderSn(queryParam.getAccountingOrderSn());
        queryParam.setAccountState(Byte.valueOf("3"));
        List<ReceivableDayDataVo> receivableDayDataVoList = receivableStatisticsMapper.queryReceivableDayData(queryParam);

        log.info("\n===>应收明细数据定时任务到每日数据-查询应收每日数据 条数:{}", receivableDayDataVoList.size());

        int num = 1;
        for (ReceivableDayDataVo receivableDayDataVo : receivableDayDataVoList) {

            log.info("\n===>应收明细数据定时任务到每日数据-当前处理第:{} 条", num);
            ++num;


            //·“收入(不含税)、税额、应收账款”金额：负值[该字段的汇总数据总和]；


            //应收每日数据
            StReceivableDayDataEntity stReceivableDayDataEntity = AnBeanUtils.copy(receivableDayDataVo, StReceivableDayDataEntity.class);
            //入账类型 1系统入账 2手工记账
            stReceivableDayDataEntity.setCreateUserId(loginSysUser.getSysUserId());
            stReceivableDayDataEntity.setCreatedAt(new Date());
            stReceivableDayDataEntity.setCreateUserName(loginSysUser.getFullName());
            stReceivableDayDataEntity.setAccountType(Byte.valueOf("2"));//入账类型 1-正常入账 2-撤销入账 3-冲销入账
            stReceivableDayDataEntity.setAccountingOrderSn(receivableDayDataVo.getAccountingOrderSn());
            stReceivableDayDataEntity.setIncomeNoTax(receivableDayDataVo.getIncomeNoTax().negate());
            stReceivableDayDataEntity.setTaxAmount(receivableDayDataVo.getTaxAmount().negate());
            stReceivableDayDataEntity.setReceivableAmount(receivableDayDataVo.getReceivableAmount().negate());
            stReceivableDayDataEntity.setServiceNoName(receivableDayDataVo.getServiceNoName());
            stReceivableDayDataEntity.setAccountWay(Byte.valueOf("0"));//入账方式 0系统入账 1手工记账


            StReceivableDayDataEntityExample stReceivableDayDataEntityExample = new StReceivableDayDataEntityExample();
            stReceivableDayDataEntityExample.createCriteria().andDetailIdEqualTo(receivableDayDataVo.getDetailId()).andAccountTypeEqualTo(Byte.valueOf("2"));//入账类型 1-正常入账 2-撤销入账 3-冲销账款;
            Integer stDayDataNum = stReceivableDayDataEntityMapper.selectByExample(stReceivableDayDataEntityExample).size();
            if (stDayDataNum == 0) { //明细数据Id 在每日数据里为唯一


                //重复判断
                stReceivableDayDataEntityMapper.insert(stReceivableDayDataEntity);

                //明细数据 反写状态 入账日期-入账状态
                //credit_id + finish_time;
                String detailId = receivableDayDataVo.getDetailId();
                String[] detailIds = detailId.split(",");
                for (String id : detailIds) {

                    //        该汇总数据所关联的应收流水_入账状态：变更为“已撤销”；
                    //        该汇总数据所关联的应收流水_入账id：保持原入账id不变；
                    //        该汇总数据所关联的应收流水_冲销/撤销入账id：取该汇总数据的入账id；
                    //        该汇总数据所关联的应收流水_更新时间：填充该汇总数据的创建时间；

                    StReceivableDetailDataEntity updateDataEntity = new StReceivableDetailDataEntity();
                    updateDataEntity.setId(Long.parseLong(id));
                    updateDataEntity.setAccountAt(receivableDayDataVo.getAccountAt());
                    updateDataEntity.setAccountState(Byte.valueOf("4"));//入账状态 0未入账 1已入账 3撤销入账 4已撤销
                    updateDataEntity.setReceivableDayId(stReceivableDayDataEntity.getId());//入账id
                    updateDataEntity.setAccountingOrderSn(stReceivableDayDataEntity.getAccountingOrderSn());//流水所属对账单编号

                    updateDataEntity.setUpdateUserId(loginSysUser.getSysUserId());
                    updateDataEntity.setUpdateTime(new Date());
                    updateDataEntity.setUpdateUserName(loginSysUser.getFullName());
                    stReceivableDetailDataEntityMapper.updateByPrimaryKeySelective(updateDataEntity);
                }
            } else {
                log.info("\n===>应收明细数据定时任务到每日数据-saveReceivableDetailDataToDayData detailId-明细数据Id:{} 存在入每日数据 请检查程序\n", receivableDayDataVo.getDetailId());
            }
        }


    }

    /**
     * 应收报表分页查询
     *
     * @param queryParam
     * @return
     */
    @Override
    public R<List<ReceivableStatisticsPageVo>> getReceivableStatisticsPageVoList(ReceivableStatisticsPageQueryParam queryParam) {


        if (!queryParam.isExportExcel()) { //非导出excel 查询分页后的settlementId

            PageHelper.startPage(queryParam.getPageNumber(), queryParam.getPageSize());
            //获得应收报表分页后的settlementId
            List<Integer> listSettlementId = receivableStatisticsMapper.getReceivableStatisticsPageLimitSettlementId(queryParam);
            listSettlementId.add(-1);//增加零星客户
            PageHelper.clearPage();

            queryParam.setListSettlementId(listSettlementId);
        }

        //应收报表List
        List<ReceivableStatisticsPageVo> resultList = receivableStatisticsMapper.getReceivableStatisticsPageVoList(queryParam);
        for (ReceivableStatisticsPageVo receivableStatisticsPageVo : resultList) {
            if (receivableStatisticsPageVo.getSettlementName().equals("小计")) {
                receivableStatisticsPageVo.setOperationTaxRate(null);
            }
        }


        //查询应收报表总页数
        Integer totalPageNum = receivableStatisticsMapper.getReceivableStatisticsTotalPageNum(queryParam);

        //合计行
        List<Map<String, Object>> totalDataList = receivableStatisticsMapper.getReceivableStatisticsTotalData(queryParam);
        Map<String, Object> headData = new HashMap<>();
        if (null != totalDataList && totalDataList.size() >= 1) {
            headData = totalDataList.get(0);
        }

        return new R<List<ReceivableStatisticsPageVo>>(queryParam.getPageNumber(), queryParam.getPageSize(), totalPageNum, resultList, headData);

    }

    /**
     * 导出excel汇总数据
     *
     * @param queryParam
     * @return
     */
    public List<ReceivableStatisticsPageVo> getReceivableStatisticsPageVoByExcel(ReceivableStatisticsPageQueryParam queryParam) {

        List<ReceivableStatisticsPageVo> receivableStatisticsPageVoList = new ArrayList<>();
        queryParam.setExportExcel(true);  //导出参数 不做分页处理
        R<List<ReceivableStatisticsPageVo>> exportReceivableStatisticsData = this.getReceivableStatisticsPageVoList(queryParam);
        if (null != exportReceivableStatisticsData && StringUtils.isNotEmpty(exportReceivableStatisticsData.getCode()) && exportReceivableStatisticsData.getCode().equals("200")) {

            receivableStatisticsPageVoList = exportReceivableStatisticsData.getData();
            Integer seq = 0;
            BigDecimal divideParam = new BigDecimal("100");
            List<String> listSettlementId = new ArrayList<>();

            for (ReceivableStatisticsPageVo receivableStatisticsPageVo : receivableStatisticsPageVoList) {

                if (null != receivableStatisticsPageVo.getOperationTaxRate()) {
                    receivableStatisticsPageVo.setFormatOperationTaxRate(receivableStatisticsPageVo.getOperationTaxRate().divide(divideParam, 2, BigDecimal.ROUND_HALF_DOWN));
                }

                String settlementId = receivableStatisticsPageVo.getSettlementId().toString();
                if (!listSettlementId.contains(settlementId)) {
                    listSettlementId.add(settlementId);
                    ++seq;
                }
                receivableStatisticsPageVo.setSeq(String.valueOf(seq));

            }

            Map<String, Object> headMap = exportReceivableStatisticsData.getHeadData();
            ReceivableStatisticsPageVo headMapVo = new ReceivableStatisticsPageVo();
            headMapVo.setSeq("合计");

            if (null != headMapVo) {

                if (headMap.containsKey("incomeNoTax")) {
                    headMapVo.setIncomeNoTax((BigDecimal) headMap.get("incomeNoTax"));
                }
                if (headMap.containsKey("taxAmount")) {
                    headMapVo.setTaxAmount((BigDecimal) headMap.get("taxAmount"));
                }
                if (headMap.containsKey("receivableAmount")) {
                    headMapVo.setReceivableAmount((BigDecimal) headMap.get("receivableAmount"));
                }
            }

            receivableStatisticsPageVoList.add(headMapVo);
        }
        return receivableStatisticsPageVoList;

    }


    @Override
    public SysNotify getReceivableStatisExcel(ReceivableStatisticsPageQueryParam queryParam) {


        //汇总数据 导出excel 使用
        List<ReceivableStatisticsPageVo> receivableStatisticsPageVoList = this.getReceivableStatisticsPageVoByExcel(queryParam);

        //每日数数据 导出excel 使用
        List<ReceivableStatisticsDayDataByExcelVo> receivableStatisticsDayDataByExcelVoList = receivableStatisticsMapper.getReceivableStatisticsDayDataByExcel(queryParam);
        log.info("\n===>getReceivableStatisExcel-每日数数据:有{} 条", receivableStatisticsDayDataByExcelVoList.size());
        log.info("\n===>getReceivableStatisExcel-每日数数据:{}", JSON.toJSONString(receivableStatisticsDayDataByExcelVoList));


        //明细数据 导出excel 使用
        List<ReceivableStatisticsDetailDataByExcelVo> receivableStatisticsDetailDataByExcelVoList = receivableStatisticsMapper.getReceivableStatisticsDetailDataByExcel(queryParam);
        log.info("\n===>getReceivableStatisExcel-明细数据:有{} 条", receivableStatisticsDetailDataByExcelVoList.size());

        ReceivableStatisticsExportDateExcelVo receivableStatisticsExportDateExcelVo = new ReceivableStatisticsExportDateExcelVo();
        try {
            if (StringUtils.isNotEmpty(queryParam.getStartTime())) {
                Date startTime = AnDateUtils.parseDate(queryParam.getStartTime());
                receivableStatisticsExportDateExcelVo.setStartTime(AnDateUtils.format(startTime, "yyyy年MM月dd日"));
            }
            if (StringUtils.isNotEmpty(queryParam.getEndTime())) {
                Date endTime = AnDateUtils.parseDate(queryParam.getEndTime());
                receivableStatisticsExportDateExcelVo.setEndTime(AnDateUtils.format(endTime, "yyyy年MM月dd日"));
            }
        } catch (Exception e) {
            log.info("\n===>getReceivableStatisExcel-格式化导出日期异常 {},{}", queryParam.getStartTime(), queryParam.getEndTime());
        }


        Map<String, Object> exportDateMap = AnBeanUtils.beanToMap(receivableStatisticsExportDateExcelVo, "exportDate");
        log.info("\n ===>导出日期exportDateMap :{}", JSON.toJSONString(exportDateMap));

        for (ReceivableStatisticsDetailDataByExcelVo receivableStatisticsDetailDataByExcelVo : receivableStatisticsDetailDataByExcelVoList) {

        }

        String excelExportPathRoot = excelExportConstant.getPathRoot();
        String excelExportRelativePathTemplate = excelExportConstant.getPathTemplate();
        String excelExportRelativePathStorage = excelExportConstant.getPathStorage();

        //excelExportPathRoot = "C:\\Users\\linjinyu\\tmp";
        //excelExportRelativePathTemplate = "\\templat";
        //excelExportRelativePathStorage = "\\_storage";

        String relativePath = File.separator + "excel" + File.separator + "statistic" + File.separator + "receivable" + File.separator;

        String templateRelativePath = excelExportPathRoot + excelExportRelativePathTemplate + relativePath;
        String templateFullPath;

        String targetFullPath = excelExportPathRoot + excelExportRelativePathStorage;
        String targetFileName = "汇总(应收账款)" + "_" + "sheet_应付账款" + ExcelResponse.addTail() + ".xlsx";
        String targetWriteFileFullPath = targetFullPath + relativePath + targetFileName;

        FileUtil.touch(targetWriteFileFullPath);

        /******************************************  start ******************************************/

        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(targetWriteFileFullPath);
        ExcelWriter excelWriter = null;

        //WriteSheet writeSheet0 = EasyExcel.writerSheet(0, "汇总").registerWriteHandler(new MyMergeStrategy()).build();
        WriteSheet writeSheet0 = EasyExcel.writerSheet(0, "应收账款").build();
        WriteSheet writeSheet1 = EasyExcel.writerSheet(1, "应收汇总数据").build();
        WriteSheet writeSheet2 = EasyExcel.writerSheet(2, "应收流水数据").build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.FALSE).build();


        // 文件
        templateFullPath = templateRelativePath + TEMPLATE_EXPORT_EXCEL_RECEIVABLE_STATISTIC;

        excelWriter = excelWriterBuilder.withTemplate(templateFullPath).build();

        excelWriter.fill(new FillWrapper("detail", receivableStatisticsPageVoList), fillConfig, writeSheet0).fill(exportDateMap, writeSheet0);
        excelWriter.fill(new FillWrapper("day", receivableStatisticsDayDataByExcelVoList), fillConfig, writeSheet1);//每日数据
        excelWriter.fill(new FillWrapper("detail", receivableStatisticsDetailDataByExcelVoList), fillConfig, writeSheet2);


        // 别忘记关闭流
        excelWriter.finish();


        /******************************************  start ******************************************/

        return NotifyUtils.createSysNotify(targetFileName, relativePath + targetFileName, SysQuartzScheduleTaskStatementOfAccountRunStateEnum.成功);
    }


}
