package com.goafanti.expenseAccount.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.goafanti.admin.bo.AdminListBo;
import com.goafanti.admin.bo.AdminPublicReviewerBo;
import com.goafanti.admin.service.DepartmentService;
import com.goafanti.common.bo.AdminNoticeBo;
import com.goafanti.common.bo.EmailBo;
import com.goafanti.common.constant.AFTConstants;
import com.goafanti.common.dao.*;
import com.goafanti.common.enums.NoticeStatus;
import com.goafanti.common.enums.NoticeTypes;
import com.goafanti.common.error.BusinessException;
import com.goafanti.common.model.*;
import com.goafanti.common.utils.AsyncUtils;
import com.goafanti.common.utils.DateUtils;
import com.goafanti.common.utils.LoggerUtils;
import com.goafanti.common.utils.StringUtils;
import com.goafanti.common.utils.excel.FileUtils;
import com.goafanti.common.utils.excel.NewExcelUtil;
import com.goafanti.common.utils.pdf.PDFUtils;
import com.goafanti.common.utils.weChat.WeChatUtils;
import com.goafanti.core.mybatis.BaseMybatisDao;
import com.goafanti.core.mybatis.JDBCIdGenerator;
import com.goafanti.core.mybatis.page.Pagination;
import com.goafanti.core.shiro.token.TokenManager;
import com.goafanti.expenseAccount.Enums.EAProcessStatus;
import com.goafanti.expenseAccount.Enums.EATypes;
import com.goafanti.expenseAccount.Enums.EAsecondaryTypes;
import com.goafanti.expenseAccount.bo.*;
import com.goafanti.expenseAccount.service.ExpenseAccountService;
import com.goafanti.order.bo.TOrderNewBo;
import com.goafanti.order.service.OrderNewService;
import com.goafanti.organization.bo.OrganizationListOut;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPRow;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ExpenseAccountServiceImpl extends BaseMybatisDao<ExpenseAccountMapper> implements ExpenseAccountService {
    @Resource
    private  AdminMapper adminMapper;
    @Resource
    private ExpenseAccountMapper expenseAccountMapper;
    @Resource
    private PublicReleaseMapper publicReleaseMapper;
    @Resource
    private ExpenseAccountLogMapper expenseAccountLogMapper;
    @Resource
    private AsyncUtils asyncUtils;
    @Resource
    private WeChatUtils weChatUtils;
    @Resource
    private ExpenseAccountDetailsMapper expenseAccountDetailsMapper;
    @Resource
    private ExpenseAccountPrivateMapper expenseAccountPrivateMapper;
    @Resource
    private ExpenseAccountExamineMapper expenseAccountExamineMapper;
    @Resource
    private JDBCIdGenerator idGenerator;
    @Resource
    private FinanceCountMapper financeCountMapper;
    @Resource
    private ExpenseRelationshipMapper expenseRelationshipMapper;
    @Resource
    private DepartmentService departmentService;
    @Resource
    private DepartmentMapper departmentMapper;
    @Resource
    private ExpenseConfigDetailsMapper expenseConfigDetailsMapper;
    @Resource
    private TOrderPublicReleaseCountMapper tOrderPublicReleaseCountMapper;
    @Resource
    private OrderNewService orderNewService;
    @Resource
    private OrderYearMaxDurationMapper orderYearMaxDurationMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private TOrderMidMapper tOrderMidMapper;
    @Resource
    private AdminPublicReviewerMapper adminPublicReviewerMapper;
    @Resource
    private LegalHolidaysMapper legalHolidaysMapper;




    @Value(value = "${upload.path}")
    private final String	uploadPath			= null;



    @Override
    public Integer add(InputExpenseAccount in) {
        //如果关联公出，获取公出信息
        if (in.getPrid()!=null){
            PublicRelease pr = publicReleaseMapper.selectById(in.getPrid());
            in.setReleaseStart(pr.getReleaseStart());
            in.setReleaseEnd(pr.getReleaseEnd());
            in.setDistrictName(pr.getDistrictName());
            in.setUserNames(pr.getUserNames());
            in.setDuration(pr.getDuration());
            if (in.getOrderNo()==null)in.setOrderNo(pr.getOrderNo());
        }
        //判断报销类型，0=固定费用，1=报销到订单
        if (in.getOrderNo()==null){
            in.setTargetType(0);
        }else {
            in.setTargetType(1);
        }
        if (in.getAid()==null)in.setAid(TokenManager.getAdminId());
        Admin admin = adminMapper.selectById(in.getAid());
        in.setAname(admin.getName());
        in.setApplyDep(admin.getDepartmentId());
        in.setPayDep(admin.getDepartmentId());
        expenseAccountMapper.insertSelective(in);
        return in.getId();
    }



    /**
     * 新增报销审核流程
     */
    private void addExpenseAccountExamine(Integer id,ExpenseAccount ea) {
        List<ExpenseAccountExamine> list=new ArrayList<>();
        expenseAccountExamineMapper.deleteByEaid(ea.getId());
        String aid = TokenManager.getAdminId();
        OrganizationListOut dep = departmentMapper.selectAllById(ea.getApplyDep());
        AdminNoticeBo a = adminMapper.selectMyAndSuperBySuperId(aid);
        //暂时只发起到财务
        for (int i=1;i<4;i++){
                ExpenseAccountExamine eae=new ExpenseAccountExamine(id,i);
            if (i==1){
                if (a.getExpenseSuperExamine()==1){
                    //新增一级
                    String examineId=null;
                    String examineName=null;
                    List<AdminPublicReviewerBo> exList = adminPublicReviewerMapper.selectByAid(aid).stream().filter(e -> e.getType() == 2).collect(Collectors.toList());

                    if(a.getExpenseSuperExamine()==1) {
                        eae.setStatus(0);
                    } else {
                        eae.setStatus(1);
                    }
                    if (!exList.isEmpty()){
                        StringBuilder sb = new StringBuilder();
                        for (AdminPublicReviewerBo e : exList) {
                            ExpenseAccountExamine eaet=new ExpenseAccountExamine(id,i);
                            examineId=e.getReviewerId();
                            examineName=e.getReviewerName();
                            eaet.setAuditor(examineId);
                            eaet.setAuditorname(examineName);
                            if (eaet.getAuditor().equals(aid))eaet.setStatus(1);
                            else  eaet.setStatus(0);
                            list.add(eaet);
                            sb.append(examineName).append(",");
                        }
                            ea.setExamineName(sb.substring(0,sb.length()-1));
                    }else {
                        examineId=a.getId();
                        examineName=a.getName();
                        eae.setAuditor(examineId);
                        eae.setAuditorname(examineName);
                        ea.setExamineName(examineName);
                    }




                }

            }else if (i==2){
                if (StringUtils.isBlank(dep.getManagerName())){
                    throw new BusinessException("报销申请部门[管理员]不存在。");
                }
                    eae.setAuditor(dep.getManagerId());
                    eae.setAuditorname(dep.getManagerName());
            }else {
                if (StringUtils.isBlank(dep.getExpenseFinanceName()))throw new BusinessException("报销部门财务审核人不存在。");
                eae.setAuditor(dep.getExpenseFinanceId());
                eae.setAuditorname(dep.getExpenseFinanceName());
            }
            if (StringUtils.isNotEmpty(eae.getAuditor())){
                if (eae.getAuditor().equals(aid))eae.setStatus(1);
                else  eae.setStatus(0);
                list.add(eae);
            }
        }
        expenseAccountExamineMapper.insertBatch(list);
    }

    /**
     * 新增报销日志
     * @param eaid 报销编号
     * @param status 状态 0发起 1通过 2完成 3驳回 4修改
     * @param str 备注
     */
    private void addExpenseAccountLog(Integer eaid, Integer status,Integer processStatus,String aid, String str) {
        addExpenseAccountLog(eaid,status,processStatus,aid,str,null);
    }
    private void addExpenseAccountLog(Integer eaid, Integer status,Integer processStatus,String aid, String str,Date date){
        addExpenseAccountLog(eaid,status,processStatus,aid,str,date,null);
    }
    /**
     * 新增报销日志
     * @param eaid 报销编号
     * @param status 状态 0发起 1通过 2完成 3驳回 4修改
     * @param str 备注
     */
    private void addExpenseAccountLog(Integer eaid, Integer status,Integer processStatus,String aid, String str,Date date,Integer showStatus) {
        ExpenseAccountLog eaLog=new ExpenseAccountLog();
        eaLog.setEaid(eaid);
        eaLog.setStatus(status);
        eaLog.setRemarks(str);
        eaLog.setProcessStatus(processStatus);
        eaLog.setAuditor(aid);
        if (showStatus!=null){
            eaLog.setShowStatus(showStatus);
        }else {
            eaLog.setShowStatus(0);
        }
        if (date !=null)eaLog.setCreateTime(date);
        expenseAccountLogMapper.insertSelective(eaLog);
    }

    /**
     * 发起、驳回发起流程切换和日志站内信发送
     */
    private void addExpenseAccountLogAndNoticEmail(ExpenseAccount in, Integer status, String str) {
        String aid=TokenManager.getAdminId();
        addExpenseAccountLog(in.getId(),status,in.getProcessStatus(),aid,str);
        if (status==0)in.setProcessStatus(1);
        Admin admin = adminMapper.selectById(aid);
        if (admin.getExpenseSuperExamine()==0){
            String str2=String.format("未设置[%s]审核。",EAProcessStatus.getDesc(in.getProcessStatus()));
            Date date=new Date();
            date.setTime(date.getTime()+1000L);
            addExpenseAccountLog(in.getId(),1,in.getProcessStatus(),aid,str2,date,2);
            in.setProcessStatus(in.getProcessStatus()+1);
        }else {
            List<AdminPublicReviewerBo> adminPublicReviewerBos = adminPublicReviewerMapper.selectByAid(aid);
            //获取上级
            List<AdminPublicReviewerBo> superList=adminPublicReviewerBos.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
            //无上级
            boolean success = false;
            if (superList.isEmpty())success=true;
            //我也是我的上级
            boolean success2 = false;
            if (!superList.isEmpty()){
                for (AdminPublicReviewerBo e : superList) {
                    if (e.getReviewerId().equals(admin.getId())){
                        success2=true;
                        break;
                    }
                }
            }
            if (success||success2){

                String str2=String.format("%s跳过[%s]。",success?"发起人无上级":"发起人与上级同人",EAProcessStatus.getDesc(in.getProcessStatus()));
                Date date=new Date();
                date.setTime(date.getTime()+1000L);
                addExpenseAccountLog(in.getId(),1,in.getProcessStatus(),aid,str2,date,2);
                in.setProcessStatus(in.getProcessStatus()+1);
            }
        }
        createExamineByProcess(in);
    }


    /**
     * 判断审核流程与发送日志与邮件
     */
    private void createExamineByProcess(ExpenseAccount in) {
        //审核流程推送到最新
        AdminListBo admin = adminMapper.getDeptNameByAid(in.getAid());
        OrganizationListOut dep = departmentMapper.selectAllById(in.getApplyDep());
        List<ExpenseConfigDetails>  configDetailsList = expenseConfigDetailsMapper.selectByDepId(in.getApplyDep());
        if (in.getApproval()==null)in.setApproval(0);
        examineByprocess(in,admin,dep,configDetailsList);
        List<ExpenseAccountExamine> list=expenseAccountExamineMapper.selectByEaidAndProcessStatus(in.getId(), in.getProcessStatus());
        List<Admin> aids=new ArrayList<>();
        StringBuilder aname =new StringBuilder();
        for (ExpenseAccountExamine e : list) {
            if (Objects.equals(e.getProcessStatus(), in.getProcessStatus()) &&e.getStatus()==0){
                Admin ad = adminMapper.selectById(e.getAuditor());
                aids.add(ad);
                aname.append(ad.getName()).append(",");
            }
        }
        in.setExamineName(aname.substring(0,aname.length()-1));
        String str = "编号[" + in.getCheckNo() + "]需要审核" + "，请查看并审核。";
        addNoticeAndEmail(in,  EAProcessStatus.SJSH.getCode(), str, admin.getName(),aids);
    }

    @Override
    @Transactional
    public int update(InputExpenseAccount in) {
        OutExpenseAccount useEA = expenseAccountMapper.selectByid(in.getId());
        if (useEA.getStatus()!=0&&useEA.getStatus()!=3){
            throw new BusinessException("不能操作审核中与完成的订单");
        }
        if (in.getStartTimeStr()!=null&&in.getEndTimeStr()!=null){
            in.setReleaseStart(DateUtils.StringToDate(in.getStartTimeStr(), AFTConstants.YYYYMMDDHHMMSS));
            in.setReleaseEnd(DateUtils.StringToDate(in.getEndTimeStr(), AFTConstants.YYYYMMDDHHMMSS));
        }

        //计算总金额
        if (in.getTotalAmount()==null){
            pushExpenseAccountAmount(in);
        }
        in.setProcessStatus(0);
        if (in.getOrderNo()==null){
            in.setTargetType(0);
        }else {
            in.setTargetType(1);
        }
        //计算本单已扣金额  如果有选择借支单，则需要修改借支状态
        if (in.getDebitId()!=null){
            //如果以前没有选择借支,修改报销和借支数据
            if (useEA.getDebitId()==null){
                pushExpenseAccountDebit(in);
            }else if (!Objects.equals(useEA.getDebitId(), in.getDebitId())) {
                //如果是修改编号
                pushExpenseAccountDebit(in);
                //并且要处理原编号的问题
                pushResettingDebit(in,useEA.getDebitId());
            }
        }
        if (in.getSettlementAmount()==null)in.setSettlementAmount(useEA.getSettlementAmount());
        in.setStatus(0);
        return expenseAccountMapper.updateByPrimaryKeySelective(in);
    }

    private void pushExpenseAccountAmount(ExpenseAccount in) {
        OutExpenseAccount useEA = expenseAccountMapper.selectByid(in.getId());
        List<OutExpenseAccountDetails> list = expenseAccountDetailsMapper.selectByEAid(in.getId());
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal realAmount = BigDecimal.ZERO;
        for (OutExpenseAccountDetails e : list) {
            totalAmount=totalAmount.add(e.getAmount());
            realAmount=realAmount.add(e.getRealAmount());
        }
        in.setTotalAmount(totalAmount);
        if (useEA==null||useEA.getSettlementAmount()==null){
            in.setAmount(realAmount);
        }else {
            in.setAmount(realAmount.subtract(useEA.getSettlementAmount()));
        }
        in.setRealAmount(in.getAmount());

    }

    @Override
    public Object updateDebitId(InputExpenseAccount in) {
        OutExpenseAccount useEA = expenseAccountMapper.selectByid(in.getId());
        if (useEA.getDebitId()!=null)pushResettingDebit(in,useEA.getDebitId());
        expenseAccountMapper.updateDeleteDebitId(in.getId());
        return 1;
    }

    @Override
    public Object delete(String ids) {
        String[] split = ids.split(",");
        for (String s : split) {
            Integer id=Integer.valueOf(s);
            ExpenseAccount expenseAccount = expenseAccountMapper.selectByPrimaryKey(id);
            if (expenseAccount.getStatus()!=0&&expenseAccount.getStatus()!=3){
                throw new BusinessException("该费用单状态无法删除");
            }
            List<OutExpenseAccountLog> logs = expenseAccountLogMapper.selectByEaid(id);
            if (logs.isEmpty()){
                expenseAccountMapper.deleteByPrimaryKey(id);
            }else {
                ExpenseAccount update =new ExpenseAccount();
                update.setId(id);
                update.setStatus(4);
                expenseAccountMapper.updateByPrimaryKeySelective(update);
            }
        }
        return 1;
    }

    @Override
    public Object pageListExport(InputPageListBo in) {
        List<OutExpenseAccountBo> list = (List<OutExpenseAccountBo>) pageList(in).getList();
        for (OutExpenseAccountBo e : list) {
            if (e.getType()!=0){
                e.setTypeOther(EATypes.getDescByCode(e.getType()));
            }else{
                e.setTypeOther(String.format("其他(%s)",e.getTypeOther()));
            }
        }
        NewExcelUtil excelUtil=new NewExcelUtil(OutExpenseAccountBo.class);
        return excelUtil.exportExcel(list,"费用单列表",uploadPath);
    }

    @Override
    public Object detailsListExport(InputDetailsListBo in) {
        List<OutExpenseAccountDetailsList> list = (List<OutExpenseAccountDetailsList>) detailsList(in).getList();
        for (OutExpenseAccountDetailsList e : list) {
            //新增计算法定
            Integer statutoryDays = pushDtailsDays(e);
            e.setStatutoryDays(statutoryDays);

            if (e.getTargetType()!=null){
                if (e.getTargetType()==0){
                    e.setTargetName("固定费用");
                }else {
                    e.setTargetName(e.getContractNo()+"/"+e.getBuyerName());
                }
            }
            if (e.getType()!=0){
                e.setTypeOther(EAsecondaryTypes.getDescByCode(e.getType()));
            }else{
                //如果是其他，又说明显示说明，没有说明显示费用类型
                if (StringUtils.isBlank(e.getTypeOther())){
                    if (e.getSonType()!=0){
                        e.setTypeOther(EATypes.getDescByCode(e.getSonType()));
                    }else{
                        e.setTypeOther(String.format("其他(%s)",e.getSonTypeOther()));
                    }
                }else {
                    e.setTypeOther(String.format("其他(%s)",e.getTypeOther()));
                }
            }
            if(e.getReleaseStart()!=null&&e.getReleaseEnd()!=null){
                e.setReleaseTimeStr(DateUtils.formatDate(e.getReleaseStart(),AFTConstants.YYYYMMDDHHMM)+"~"+DateUtils.formatDate(e.getReleaseEnd(),AFTConstants.YYYYMMDDHHMM));
            }

        }
        NewExcelUtil excelUtil=new NewExcelUtil(OutExpenseAccountDetailsList.class);
        return excelUtil.exportExcel(list,"费用详细列表",uploadPath);
    }

    private Integer pushDtailsDays(OutExpenseAccountDetailsList e) {
        Integer statutoryDays = 0;
        //获取俩个日期之间的所有日期
        long days = DateUtils.getDaysBetween(e.getReleaseStart(),e.getReleaseEnd());
            List<LegalHolidays> legalHolidays = legalHolidaysMapper.selectAll();
            //法定节假日
            Set<String> holidaysSet = legalHolidays.stream()
                    .filter(et -> et.getStatus() == 1)
                    .map(holiday -> DateUtils.formatDateYYYYMMdd(holiday.getHolidays()))
                    .collect(Collectors.toSet());
            //需要补班
            Set<String> workDaysSet = legalHolidays.stream()
                    .filter(et -> et.getStatus() == 2)
                    .map(holiday -> DateUtils.formatDateYYYYMMdd(holiday.getHolidays()))
                    .collect(Collectors.toSet());

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            LocalDate startDate = LocalDate.from(e.getReleaseStart().toInstant().atZone(ZoneId.systemDefault()));

            for (int i = 0; i <= days; i++) {
                LocalDate currentDate = startDate.plusDays(i);
                int dayOfWeek = currentDate.getDayOfWeek().getValue();
                String dateString = currentDate.format(formatter);

                // 周六(6)和周日(7)默认为节假日
                if (dayOfWeek > 5) {
                    // 如果是补班日，则不是节假日
                    if (!workDaysSet.contains(dateString)) {
                        statutoryDays++;
                    }
                } else {
                    // 工作日如果是法定节假日，则是节假日
                    if (holidaysSet.contains(dateString)) {
                        statutoryDays++;
                    }
                }
            }

        return statutoryDays;
    }



    @Override
    public Object getDepDetails(Integer id) {
        OutExpenseAccount outExpenseAccount = expenseAccountMapper.selectByid(id);
        return departmentMapper.selectAllById(outExpenseAccount.getApplyDep());
    }

    @Override
    public Object updateTypeOther(Integer id, String typeOther,String remarks,String orderNo) {
        ExpenseAccount ea = new ExpenseAccount();
        ea.setId(id);
        if (StringUtils.isNotEmpty(typeOther)){
            ea.setTypeOther(typeOther);
            expenseAccountMapper.updateByPrimaryKeySelective(ea);
        }
        if (StringUtils.isNotEmpty(remarks)){
            ea.setRemarks(remarks);
            expenseAccountMapper.updateByPrimaryKeySelective(ea);
        }
        if (StringUtils.isBlank(orderNo)){
            ea.setTargetType(0);
            expenseAccountMapper.updateRemoveOrderNo(ea);
        }else {
            ea.setOrderNo(orderNo);
            ea.setTargetType(1);
            expenseAccountMapper.updateByPrimaryKeySelective(ea);
        }
        if (StringUtils.isNotEmpty(typeOther)){
            MainExpenseAccount mainExpenseAccount = expenseAccountMapper.selectbySonId(id);
            StringBuilder types=new StringBuilder();
            for (SonExpenseAccount e : mainExpenseAccount.getSonList()) {
                if (e.getType()!=0){
                    types.append(EATypes.getDescByCode(e.getType()));
                }else {
                    types.append(e.getTypeOther());
                }
                if (e.getType()==2){
                    if (e.getSecondaryType()==0){
                        types.append("-").append(e.getSecondaryTypeOther());
                    }else{
                        String descByCode = EAsecondaryTypes.getDescByCode(e.getSecondaryType());
                        types.append("-").append(descByCode);
                    }
                }
                types.append(",");
            }
            expenseAccountMapper.updateSonTypeOther(id,types.substring(0,types.length()-1));
        }
        return 1;
    }

    @Override
    public Object updateDetailsTypeOther(Integer id, String typeOther) {
        ExpenseAccountDetails in =new ExpenseAccountDetails();
        in.setId(id);
        in.setTypeOther(typeOther);
        return expenseAccountDetailsMapper.updateByPrimaryKeySelective(in);
    }

    @Override
    public Object mainExpenseExport(Integer id, HttpServletResponse response) {
        MainExpenseAccount mainExpenseAccount = mainExpense(id);
        pushMainExpenseExport(mainExpenseAccount,response,uploadPath);
        return 1;
    }

    @Override
    public void pushExpenseTotalAmount() {
//                        orderNewService.pushOrderPublicReleaseCount("800326086952370176");
        List<TOrderNew> tOrderNews = orderNewService.selectGetAll();
        for (TOrderNew e : tOrderNews) {
            orderNewService.pushOrderPublicReleaseCount(e);

        }
    }

    public void pushMainExpenseExport(MainExpenseAccount mainExpenseAccount,HttpServletResponse response, String uploadPath) {
        String title = "报销明细表";
        String attName = String.format("报销明细表_%s.pdf",new Date().getTime());
        String realFileName = uploadPath+"/tmp/"+new Date().getTime() + ".pdf";

        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        Document document=new Document();
        try {
            FileUtils.setAttachmentResponseHeader(response, title,attName);
            FileOutputStream outputStream = new FileOutputStream(realFileName);
            PdfWriter.getInstance(document, outputStream);
            document.open();
            Paragraph paragraph = new Paragraph(title, PDFUtils.getTitleFont());
            paragraph.setAlignment(1);
            document.add(paragraph);
            //默认算60
            PDFUtils.pushExpenseTitle(document,mainExpenseAccount);
            PdfPTable table = new PdfPTable(12);
            table.setWidthPercentage(100);
            ArrayList<PdfPRow> rows = table.getRows();
            float[] columnWidths ={12,24,14,50,50,42,28,18,12,12,50,50};
            table.setWidths(columnWidths);
            table.setSpacingBefore(10f);
            table.setSpacingAfter(10f);
            //填充表格头部
            addTitle(rows);
            //获取填充数据
            List<Map<Integer,String>> mapList = pushMainExpenseAccount(mainExpenseAccount);
            //填充数据写入表格
            pushTable(rows,mapList);
            document.add(table);
            document.close();
            outputStream.close();
//            FileUtils.writeBytes(realFileName, response.getOutputStream());
//            FileUtils.deleteFile(realFileName);
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        }

    }

    private static void addTitle(ArrayList<PdfPRow> rows) {
        PdfPCell[] cells =new PdfPCell[12];
        PdfPRow row=new PdfPRow(cells);
        cells[0] = new PdfPCell(new Phrase("序号",PDFUtils.getFont(8)));
        cells[1] = new PdfPCell(new Phrase("费用时间",PDFUtils.getFont(8)));
        cells[2] = new PdfPCell(new Phrase("时长",PDFUtils.getFont(8)));
        cells[3] = new PdfPCell(new Phrase("出差地址",PDFUtils.getFont(8)));
        cells[4] = new PdfPCell(new Phrase("出差企业",PDFUtils.getFont(8)));
        cells[5] = new PdfPCell(new Phrase("合同编号",PDFUtils.getFont(8)));
        cells[6] = new PdfPCell(new Phrase("类型",PDFUtils.getFont(8)));
        cells[7] = new PdfPCell(new Phrase("详情",PDFUtils.getFont(8)));
        cells[8] = new PdfPCell(new Phrase("费用",PDFUtils.getFont(8)));
        cells[9] = new PdfPCell(new Phrase("实报",PDFUtils.getFont(8)));
        cells[10] = new PdfPCell(new Phrase("出差事由",PDFUtils.getFont(8)));
        cells[11] = new PdfPCell(new Phrase("报销订单/部门",PDFUtils.getFont(8)));
        rows.add(row);
    }

    private void pushTable(ArrayList<PdfPRow> rows, List<Map<Integer, String>> mapList) {
        for (Map<Integer, String> map : mapList) {
            PdfPCell[] cells =new PdfPCell[12];
            PdfPRow row=new PdfPRow(cells);
            for (int i = 0; i < 12; i++) {
                cells[i] = new PdfPCell(new Phrase(map.get(i),PDFUtils.getFont(8)));
            }
            rows.add(row);
        }
    }

    private List<Map<Integer, String>> pushMainExpenseAccount(MainExpenseAccount mainExpenseAccount) {
        List<Map<Integer,String>> mapList = new ArrayList<>();
        if (mainExpenseAccount.getSonList()!=null){
            int index=0;
            for (SonExpenseAccount e : mainExpenseAccount.getSonList()) {
                List<SonExpenseAccountDetails> detList = e.getDetList();
                //详情计算，详情完成后需要一个统计
                int detIndex = 0;
                for (SonExpenseAccountDetails det : detList) {
                    index++;
                    detIndex++;
                    Map<Integer, String> map = new HashMap<>();
                    map.put(0, String.valueOf(index));
                    if (detIndex==1){
                        if (e.getPublicReleaseType()!=null){
                            map.put(1,DateUtils.formatDate(e.getReleaseStart(),AFTConstants.YYYYMMDDHHMM)+"~"+DateUtils.formatDate(e.getReleaseEnd(),AFTConstants.YYYYMMDDHHMM));
                            map.put(2,e.getDuration());
                            map.put(3,e.getDistrictName());
                            map.put(4,e.getUserNames());
                            map.put(6,getExpenseType(e));
                        }else {
                            map.put(1,det.getAgreeTime());
                            map.put(2,"");
                            map.put(3,"");
                            map.put(4,"");
                            map.put(6,"");
                        }
                    }else {
                        map.put(1,det.getAgreeTime());
                        map.put(2,"");
                        map.put(3,"");
                        map.put(4,"");
                        map.put(6,"");
                    }

                    map.put(5,e.getContractNo());
                    map.put(7,getExpenseSecondaryType(e));
                    map.put(8,getBigDecimalZeros(det.getAmount()));
                    map.put(9,getBigDecimalZeros(det.getRealAmount()));
                    map.put(10,e.getPlan());
                    if (index==1){
                        map.put(11,getOrderNoAndDepartment(e,mainExpenseAccount.getApplyDepName()));
                    }else {
                        map.put(11,"");
                    }
                    mapList.add(map);
                    if (detIndex==detList.size()){
                        index++;
                        addCount(e.getAmount(),e.getRealAmount(),mapList,index);
                    }
                }
            }
            //循环结束新增合计
            index++;
            addCount(mainExpenseAccount.getAmount(),mainExpenseAccount.getRealAmount(),mapList,index,1);
        }
        return  mapList;
    }

    private String getBigDecimalZeros(BigDecimal amount) {
        return amount.stripTrailingZeros().toEngineeringString();
    }

    private void addCount(BigDecimal amount,BigDecimal realAmount,List<Map<Integer, String>> mapList,int index) {
        addCount(amount,realAmount,mapList,index,0);
    }
    private void addCount(BigDecimal amount,BigDecimal realAmount,List<Map<Integer, String>> mapList,int index,int type) {
        String str="小计";
        if (type==1)str="合计";
        Map<Integer, String> map = new HashMap<>();
        for (int i = 0; i < 12; i++){
            if (i==0){
                map.put(i, String.valueOf(index));
            }else if (i==7){
                map.put(i, str);
            }else if (i==8){
                map.put(i, getBigDecimalZeros(amount));
            }else if (i==9){
                map.put(i, getBigDecimalZeros(realAmount));
            }else {
                map.put(i, "");
            }
        }
        mapList.add(map);
    }



    private String getOrderNoAndDepartment(SonExpenseAccount e,String applyDepName) {
        return e.getOrderNo()+"\n"+applyDepName;
    }

    private String getExpenseSecondaryType(SonExpenseAccount e) {
        //交通工具待完善
        if (e.getSecondaryType()==0){
            return EAsecondaryTypes.getDescByCode(e.getSecondaryType())+"("+e.getSecondaryTypeOther()+")";
        }
        return EAsecondaryTypes.getDescByCode(e.getSecondaryType());
    }


    private String getExpenseType(SonExpenseAccount e) {
        if (e.getType()==0){
            return EATypes.getDescByCode(e.getType())+"("+e.getTypeOther()+")";
        }
        return EATypes.getDescByCode(e.getType());
    }



    private void pushResettingDebit(InputExpenseAccount in, Integer debitId) {
        ExpenseAccount debit = expenseAccountMapper.selectByPrimaryKey(debitId);
        List<ExpenseAccount> list = expenseAccountMapper.selectByDebitId(debitId);
        ExpenseAccount newDebit=new ExpenseAccount();
        newDebit.setId(debitId);
        //计算除开当前单，已选择抵扣金额
        BigDecimal count=new BigDecimal(0);
        for (ExpenseAccount e : list) {
            if (!in.getId().equals(e.getId())){
                count=count.add(e.getSettlementAmount());

            }
        }
        newDebit.setSettlementAmount(count);
        if (count.compareTo(new BigDecimal(0))==0){
            newDebit.setLiquidationStatus(0);
        }else if (count.compareTo(debit.getRealAmount())<0){
            newDebit.setLiquidationStatus(1);
        }else if (count.compareTo(debit.getRealAmount())==0){
            newDebit.setLiquidationStatus(2);
        }
        expenseAccountMapper.updateByPrimaryKeySelective(newDebit);
    }



    private void pushExpenseAccountDebit(InputExpenseAccount in) {
        ExpenseAccount debit = expenseAccountMapper.selectByPrimaryKey(in.getDebitId());
        ExpenseAccount newDebit=new ExpenseAccount();
        newDebit.setId(debit.getId());
        //计算剩余需要支付金额
        BigDecimal decbitAmount=debit.getRealAmount().subtract(debit.getSettlementAmount());
        //本次金额大于等于需要抵扣金额，则变成完全抵扣，抵扣金额变成本次已付金额
        if (in.getRealAmount().compareTo(decbitAmount)>=0){
            in.setSettlementAmount(decbitAmount);
            newDebit.setSettlementAmount(debit.getRealAmount());
            newDebit.setLiquidationStatus(2);
        }else {
//           本次金额小于需要抵扣，则本次变更全抵扣，借支已付加上本次金额
            in.setSettlementAmount(in.getRealAmount());
            BigDecimal setAmount=new BigDecimal(0).add(debit.getSettlementAmount()).add(in.getRealAmount());
            newDebit.setSettlementAmount(setAmount);
            newDebit.setLiquidationStatus(1);
        }
        expenseAccountMapper.updateByPrimaryKeySelective(newDebit);
    }

    @Override
    public int addDetails(InputExpenseAccountDetails in) {
        if (in.getAgreeTimes()!=null){
            in.setAgreeTime(DateUtils.StringToDate(in.getAgreeTimes(),AFTConstants.YYYYMMDD));
        }
        ExpenseAccount expenseAccount = expenseAccountMapper.selectByPrimaryKey(in.getEaid());
        if (expenseAccount.getType()==2){
            in.setType(expenseAccount.getSecondaryType());
            if (in.getType()==0){
                in.setTypeOther(expenseAccount.getSecondaryTypeOther());
            }
        }
        in.setRealAmount(in.getAmount());
        expenseAccountDetailsMapper.insertSelective(in);
        ExpenseAccount ea =new ExpenseAccount();
        ea.setId(in.getEaid());
        pushExpenseAccountAmount(ea);
        expenseAccountMapper.updateByPrimaryKeySelective(ea);
        return 1;
    }

    @Override
    public int deleteDetails(Integer id) {
        ExpenseAccountDetails expenseAccountDetails = expenseAccountDetailsMapper.selectByPrimaryKey(id);
        Integer eaid=expenseAccountDetails.getEaid();
        expenseAccountDetailsMapper.deleteByPrimaryKey(id);
        ExpenseAccount ea =new ExpenseAccount();
        ea.setId(eaid);
        pushExpenseAccountAmount(ea);
        expenseAccountMapper.updateByPrimaryKeySelective(ea);
        return 1;
    }

    @Override
    public List<OutExpenseAccountDetails> detailsList(Integer eaid) {

        return expenseAccountDetailsMapper.selectByEAid(eaid);
    }

    @Override
    public Object logList(Integer eaid) {
        List<OutExpenseAccountLog> list = expenseAccountLogMapper.selectByEaid(eaid);
        if (!list.isEmpty()) {
            OutExpenseAccountLog lastout = list.get(list.size() - 1);
            ExpenseAccount expenseAccount = expenseAccountMapper.selectByPrimaryKey(eaid);
            Integer processStatus = lastout.getProcessStatus();
                if (lastout.getStatus() < 2) {
                    if (processStatus ==0||processStatus ==2||processStatus ==3){
                        List<OutExpenseAccountLog> outExpenseAccountLogs = expenseAccountExamineMapper.selectNotExamineByEaidAndProcessStatus(eaid, processStatus);
                        list.addAll(outExpenseAccountLogs);
                    }else {
                        List<ExpenseAccountExamine> expenseAccountExamines = expenseAccountExamineMapper.selectByEaidAndProcessStatus(eaid, processStatus);
                        for (ExpenseAccountExamine e : expenseAccountExamines) {
                            if (e.getStatus()==0){
                                OutExpenseAccountLog out =new OutExpenseAccountLog();
                                out.setEaid(eaid);
                                out.setProcessStatus(e.getProcessStatus());
                                out.setAuditorName(e.getAuditorname());
                                list.add(out);
                            }
                        }
                    }
                    if (processStatus<8)addDetailsLog(list, expenseAccount,processStatus);
                }
        }
            return list;
    }



    private void addDetailsLog(List<OutExpenseAccountLog> list, ExpenseAccount expenseAccount,Integer useStatus) {
        OrganizationListOut dep = departmentMapper.selectAllById(expenseAccount.getApplyDep());
        List<ExpenseConfigDetails> configDetails = expenseConfigDetailsMapper.selectByDepId(expenseAccount.getApplyDep());
        for (int processStatus = useStatus; processStatus < 8; processStatus++) {
            if (processStatus ==3){
                if (dep.getExpenseRetrialFinanceExamine()==1){
                    List<ExpenseConfigDetails> collect = configDetails.stream().filter(e -> e.getType() == 1).collect(Collectors.toList());
                    for (ExpenseConfigDetails e : collect) {
                        addDetailsTypeLog(list, expenseAccount, e);
                    }
                }
            }else if (processStatus ==4){
                if (expenseAccount.getApproval()==1&&dep.getCeoExamine()==1){
                    List<ExpenseConfigDetails> collect = configDetails.stream().filter(e -> e.getType() == 2).collect(Collectors.toList());
                    for (ExpenseConfigDetails e : collect) {
                        addDetailsTypeLog(list, expenseAccount, e);
                    }
                }
            }else if (processStatus ==5){
                if (dep.getCfoExamine()==1){
                    List<ExpenseConfigDetails> collect = configDetails.stream().filter(e -> e.getType() == 3).collect(Collectors.toList());
                    for (ExpenseConfigDetails e : collect) {
                        addDetailsTypeLog(list, expenseAccount, e);
                    }
                }
            }else if (processStatus ==6){
                if (dep.getViceCeoExamine()==1){
                    List<ExpenseConfigDetails> collect = configDetails.stream().filter(e -> e.getType() == 4).collect(Collectors.toList());
                    for (ExpenseConfigDetails e : collect) {
                        addDetailsTypeLog(list, expenseAccount, e);
                    }
                }
            }else if (processStatus ==7){
                if (dep.getCashierExamine()==1){
                    List<ExpenseConfigDetails> collect = configDetails.stream().filter(e -> e.getType() == 5).collect(Collectors.toList());
                    for (ExpenseConfigDetails e : collect) {
                        addDetailsTypeLog(list, expenseAccount, e);
                    }
                }
            }
        }

    }

    private static void addDetailsTypeLog(List<OutExpenseAccountLog> list, ExpenseAccount expenseAccount, ExpenseConfigDetails e) {
        OutExpenseAccountLog addLog=new OutExpenseAccountLog();
        addLog.setEaid(expenseAccount.getId());
        addLog.setAuditor(e.getAid());
        addLog.setAuditorName(e.getAdminName());
        list.add(addLog);
    }



    @Override
    public int updateExamine(InputExpenseAccount in) {
        ExpenseAccount useEa = expenseAccountMapper.selectByPrimaryKey(in.getId());
        ExpenseAccount newEa=new ExpenseAccount();
        newEa.setId(useEa.getId());
        newEa.setApproval(in.getApproval());
        in.setCheckNo(useEa.getCheckNo());
        in.setProcessStatus(useEa.getProcessStatus());
        //审核通知 获取发送人
        AdminListBo admin = adminMapper.getDeptNameByAid(useEa.getAid());
        //项目推送流程及添加日志
        addExamineExpenseAccountLog(admin,in, useEa, newEa);
        //完成申请
        if (newEa.getStatus()==2){
            //如果是借支则订单新增
            List<ExpenseAccount> expenseAccounts1 = expenseAccountMapper.selectListByMainId(in.getId());
            for (ExpenseAccount e : expenseAccounts1) {
                if (e.getType()!=4){
                    //不然就看是否挂载借支订单进行抵扣
                    if (e.getDebitId()!=null){
                        ExpenseAccount useDebit=expenseAccountMapper.selectByPrimaryKey(e.getDebitId());
                        if (useDebit.getLiquidationStatus()==2){
                            List<ExpenseAccount> expenseAccounts = expenseAccountMapper.selectByDebitId(e.getDebitId());
                            boolean flag=true;
                            for (ExpenseAccount expenseAccount : expenseAccounts) {
                                if(!expenseAccount.getId().equals(in.getId())&&expenseAccount.getStatus()!=2){
                                    flag=false;
                                    break;
                                }
                            }
                            if (flag){
                                ExpenseAccount debit=new ExpenseAccount();
                                debit.setId(e.getDebitId());
                                debit.setLiquidationStatus(3);
                                expenseAccountMapper.updateByPrimaryKeySelective(debit);
                            }
                        }
                    }
                }
                ExpenseAccount updateSon=new ExpenseAccount();
                updateSon.setId(e.getId());
                updateSon.setStatus(2);
                expenseAccountMapper.updateByPrimaryKeySelective(updateSon);
                if (e.getOrderNo()!=null){
                    tOrderMidMapper.updateOExpense(e.getOrderNo());
                }
            }
        }else if (newEa.getStatus()==1){
            if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.BMFZRSH.getCode())){
                pushFinanceCount(admin.getDepFinance(),useEa.getCreateTime(),0);
                //处理金额修改
                if(in.getRealList()!=null){
                    List<Map> maps = JSONObject.parseArray(in.getRealList(), Map.class);
                    for (Map map: maps) {
                        Integer type= (Integer) map.get("type");
                        Integer id= (Integer) map.get("id");
                        BigDecimal amount= new BigDecimal(map.get("amount").toString());
                        //type=1总计，type=2报销金额
                        if(type==1){
                            ExpenseAccount ea =new ExpenseAccount();
                            ea.setId(id);
                            ea.setRealAmount(amount);
                            expenseAccountMapper.updateByPrimaryKeySelective(ea);
                        }else if (type==2){
                            ExpenseAccountDetails ead=new ExpenseAccountDetails();
                            ead.setId(id);
                            ead.setRealAmount(amount);
                            expenseAccountDetailsMapper.updateByPrimaryKeySelective(ead);
                        }
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.CWSH.getCode())){
                pushFinanceCount(admin.getDepFinance(),useEa.getCreateTime(),1);
            }
        }else if (newEa.getStatus()==3){
            if (Objects.equals(useEa.getProcessStatus(), EAProcessStatus.BMFZRSH.getCode())){
                pushFinanceCount(admin.getDepFinance(),useEa.getCreateTime(),2);
            }else if (newEa.getProcessStatus()>EAProcessStatus.CWSH.getCode()){
                pushFinanceCount(admin.getDepFinance(),useEa.getCreateTime(),3);
            }
            expenseAccountExamineMapper.updateStatusByEaidAndAid(in.getId(),null,0,0);
        }

        expenseAccountMapper.updateByPrimaryKeySelective(newEa);
        expenseAccountMapper.updateSonByMainId(newEa.getId());
        return 1;
    }


    private void addFinanceCount(String aid,Date startTime ) {
        String forDate=DateUtils.formatDate(startTime,AFTConstants.YYYYMMDD);
        addFinanceCount(aid,forDate);
    }

    private void addFinanceCount(String aid,String startTime ) {
        FinanceCount financeCount = financeCountMapper.selectByAidAndDates(aid, startTime);
        if (financeCount ==null){
            financeCount =FinanceCount.initialization(aid, startTime);
            financeCount.setExpenseCount(1);
            financeCount.setExpenseUnauditedCount(1);
            financeCountMapper.insertSelective(financeCount);
        }else {
            FinanceCount newFinanceCount =new FinanceCount();
            newFinanceCount.setId(financeCount.getId());
            newFinanceCount.setExpenseCount(financeCount.getExpenseCount()+1);
            newFinanceCount.setExpenseUnauditedCount(financeCount.getExpenseUnauditedCount()+1);
            financeCountMapper.updateByPrimaryKeySelective(newFinanceCount);
        }
    }



    private void addExamineExpenseAccountLog(AdminListBo admin,InputExpenseAccount in, ExpenseAccount useEa, ExpenseAccount newEa) {
        List<Admin> aids=new ArrayList<>();
        StringBuilder str=new StringBuilder();
        Integer status=in.getStatus();
        if (status==1){
            //审核处理审核表
            expenseAccountExamineMapper.updateStatusByEaidAndAid(in.getId(),TokenManager.getAdminId(),1,0);
            str.append("编号[").append(in.getCheckNo()).append("]需要审核").append("，请查看并审核。");
            newEa.setAid(useEa.getAid());
            newEa.setCreateTime(useEa.getCreateTime());
            newEa.setApplyDep(useEa.getApplyDep());
            //在跳入董事长审核的时候非特批直接跳过，原则上下个流程不可再跳过
            OrganizationListOut dep = departmentMapper.selectAllById(newEa.getApplyDep());
            List<ExpenseConfigDetails>  configDetailsList = expenseConfigDetailsMapper.selectByDepId(newEa.getApplyDep());
            if(Objects.equals(useEa.getProcessStatus(), EAProcessStatus.CNSH.getCode())){
                expenseAccountExamineMapper.updateStatusByEaidAndAid(in.getId(),TokenManager.getAdminId(),1,1);
                boolean expenseExamine = pushExpenseExamine(configDetailsList, newEa,  EAProcessStatus.CNSH.getCode());
                if (expenseExamine){
                    //完成后的流程
                    status =2;
                    aids.add(adminMapper.selectById(admin.getId()));

                    newEa.setStatus(status);
                    newEa.setExamineName("");
                }else {
                    status =1;
                    List<ExpenseAccountExamine> list=expenseAccountExamineMapper.selectByEaidAndProcessStatus(newEa.getId(), useEa.getProcessStatus());
                    StringBuilder aname =new StringBuilder();
                    for (ExpenseAccountExamine e : list) {
                        if (e.getStatus()==0){
                            Admin ad=adminMapper.selectById(e.getAuditor());
                            aids.add(ad);
                            aname.append(ad.getName()).append(",");
                        }
                    }
                    newEa.setExamineName(aname.substring(0,aname.length()-1));
                }
                addExpenseAccountLog(in.getId(),status,useEa.getProcessStatus(),TokenManager.getAdminId(),in.getReason());
            }else {
                //小于为固定单审核，大于可能被配置多人审核
                if (useEa.getProcessStatus().equals(EAProcessStatus.BMFZRSH.getCode())||
                        useEa.getProcessStatus().equals(EAProcessStatus.CWSH.getCode())
                ){
                    boolean flag=false;
                    List<ExpenseAccountExamine> list=expenseAccountExamineMapper.selectByEaidAndProcessStatus(newEa.getId(), useEa.getProcessStatus());
                    for (ExpenseAccountExamine e : list) {
                        if (e.getAuditor().equals(TokenManager.getAdminId())){
                            flag=true;
                        }
                    }
                    if (!flag)throw new BusinessException("审核角色错误。请刷新页面");
                    newEa.setProcessStatus(useEa.getProcessStatus()+1);
                }else {
                    newEa.setProcessStatus(useEa.getProcessStatus());
                }
                addExpenseAccountLog(in.getId(),status,useEa.getProcessStatus(),TokenManager.getAdminId(),in.getReason());
                newEa.setStatus(1);
                if (newEa.getApproval()==null)newEa.setApproval(useEa.getApproval());
                //流程的推动
                examineByprocess(newEa,admin,dep,configDetailsList);
                //推动到出纳如果设置无需则完成不设置下个人，否则就要设置
                if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.CNSH.getCode())){
                    if(dep.getCashierExamine()==1){
                        pushExamineName(newEa, aids);
                    }else {
                        //完成后的流程
                        status =2;
                        aids.add(adminMapper.selectById(admin.getId()));
                        newEa.setStatus(status);
                        newEa.setExamineName("");
                    }
                }else {
                    pushExamineName(newEa, aids);
                }

            }

        }else {
            aids.add(adminMapper.selectById(admin.getId()));
            newEa.setStatus(3);
            newEa.setProcessStatus(0);
            str.setLength(0);
            addExpenseAccountLog(in.getId(),newEa.getStatus(),useEa.getProcessStatus(),TokenManager.getAdminId(),in.getReason());
            str.append("您的报销审核已驳回，报销编号[").append(in.getCheckNo()).append("]，请注意查看。");
        }
        if (newEa.getStatus()==null)newEa.setStatus(1);
        addNoticeAndEmail(in,  status,str.toString(),admin.getName(),aids);
    }

    private void pushExamineName(ExpenseAccount newEa, List<Admin> aids) {
        List<ExpenseAccountExamine> list=expenseAccountExamineMapper.selectByEaidAndProcessStatus(newEa.getId(), newEa.getProcessStatus());
        StringBuilder aname =new StringBuilder();
        for (ExpenseAccountExamine e : list) {
            if (Objects.equals(e.getProcessStatus(), newEa.getProcessStatus()) &&e.getStatus()==0){
                Admin ad=adminMapper.selectById(e.getAuditor());
                aids.add(ad);
                aname.append(ad.getName()).append(",");
            }
        }
        newEa.setExamineName(aname.substring(0,aname.length()-1));
    }

    /**
     *
     * @param type 0 双新增 1 未审核减一 2双减 3总数减一
     */
    private void pushFinanceCount(String aid, Date createTime, Integer type) {
        String startTime= DateUtils.formatDate(createTime,AFTConstants.YYYYMMDD);
        FinanceCount financeCount = financeCountMapper.selectByAidAndDates(aid, startTime);
        FinanceCount newFinance=new FinanceCount();
        if (financeCount==null){
            newFinance=FinanceCount.initialization(aid,createTime);
            newFinance.setExpenseCount(1);
            newFinance.setExpenseUnauditedCount(1);
            financeCountMapper.insertSelective(newFinance);
        }else {
            newFinance.setId(financeCount.getId());
            if(type==0){
                newFinance.setExpenseCount(financeCount.getExpenseCount()+1);
                newFinance.setExpenseUnauditedCount(financeCount.getExpenseUnauditedCount()+1);
            }else if(type==1){
                newFinance.setExpenseUnauditedCount(financeCount.getExpenseUnauditedCount()-1);
            }else if(type==2){
                newFinance.setExpenseCount(financeCount.getExpenseCount()-1);
                newFinance.setExpenseUnauditedCount(financeCount.getExpenseUnauditedCount()-1);
            }else if(type==3){
                newFinance.setExpenseCount(financeCount.getExpenseCount()-1);
            }
            financeCountMapper.updateByPrimaryKeySelective(newFinance);
        }


    }

    /**
     * 审核流程推送，只要不是完成状态就继续判断是否跳过
     * @param newEa
     */
    private void examineByprocess( ExpenseAccount newEa,AdminListBo admin,OrganizationListOut dep,List<ExpenseConfigDetails> list) {
        List<ExpenseAccountExamine> examineList;
        //小于财务复审的，走单审核
        if (newEa.getProcessStatus().equals(EAProcessStatus.BMFZRSH.getCode())||
                newEa.getProcessStatus().equals(EAProcessStatus.CWSH.getCode())){
            examineList = expenseAccountExamineMapper.selectByEaidAndProcessStatus(newEa.getId(), newEa.getProcessStatus());
            boolean flag=false;
            String aid="";
            if (!examineList.isEmpty()){
                for (ExpenseAccountExamine e : examineList) {
                    if (e.getStatus()==1){
                        flag=true;
                        aid=e.getAuditor();
                        break;
                    }
                }
            }
            //如果当前审核人已经审核，则跳过当前审核
            if (flag){
                String str=String.format("重复审核人跳过[%s]。",EAProcessStatus.getDesc(newEa.getProcessStatus()));
                Date date=new Date();
                date.setTime(date.getTime()+ 1000L);
                addExpenseAccountLog(newEa.getId(),1,newEa.getProcessStatus(),aid,str,date,2);
                if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.BMFZRSH.getCode())){
                    addFinanceCount(admin.getDepFinance(),newEa.getCreateTime());
                }
                newEa.setProcessStatus(newEa.getProcessStatus()+1);
                examineByprocess(newEa,admin,dep,list);
            }
        }else {
            //大于等于财务复审的，走多审核
            String str=null;
            //跳过标记
            boolean flag2= false;
            //审核通过标记
            boolean flag3= false;
            Date date2=new Date();
            date2.setTime(date2.getTime()+ 1000L);
            String tip=null;
            String aid=null;
            if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.SJSH.getCode())){
                //财务复审财务复审
                if (admin.getExpenseSuperExamine()==0){
                    flag2=true;
                    tip=EAProcessStatus.SJSH.getDesc();
                }else {
                    boolean expenseExamine = pushExpenseExamine(list, newEa,  EAProcessStatus.SJSH.getCode());
                    if (expenseExamine){
                        flag3=true;
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.CWFS.getCode())){
                //财务复审财务复审
                if (dep.getExpenseRetrialFinanceExamine()==0){
                    flag2=true;
                    tip=EAProcessStatus.CWFS.getDesc();
                }else {
                    boolean expenseExamine = pushExpenseExamine(list, newEa,  EAProcessStatus.CWFS.getCode());
                    if (expenseExamine){
                        flag3=true;
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.DSZSH.getCode())){
                if (newEa.getApproval()!=1){
                    flag2=true;
                    tip="特批(财务选择)";
                }else {
                    if (dep.getCeoExamine()==1){
                        boolean expenseExamine = pushExpenseExamine(list, newEa,  EAProcessStatus.DSZSH.getCode());
                        if (expenseExamine){
                            flag3=true;
                        }
                    }else {
                        flag2=true;
                        tip="特批(部门设置)";
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.CWGGLYSH.getCode())){
                if (dep.getExpenseRetrialFinanceExamine()==0){
                    flag2=true;
                    tip=EAProcessStatus.CWGGLYSH.getDesc();
                    aid=null;
                }else {
                    boolean expenseExamine = pushExpenseExamine(list, newEa,  EAProcessStatus.CWGGLYSH.getCode());
                    if (expenseExamine){
                        flag3=true;
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.GSFSSH.getCode())){
                if (dep.getExpenseRetrialFinanceExamine()==0){
                    flag2=true;
                    tip=EAProcessStatus.GSFSSH.getDesc();
                }else {
                    boolean expenseExamine = pushExpenseExamine(list, newEa,  EAProcessStatus.GSFSSH.getCode());
                    if (expenseExamine){
                        flag3=true;
                    }
                }
            }else if (Objects.equals(newEa.getProcessStatus(), EAProcessStatus.CNSH.getCode())){
                if (dep.getExpenseRetrialFinanceExamine()==0){
                    flag2=true;
                    tip=EAProcessStatus.CNSH.getDesc();
                }else {
                //财务出纳在审核时不与前置审核做同步
                    pushExpenseExamine(list, newEa,  EAProcessStatus.CNSH.getCode());
                }
            }
            if (flag2){
                if (str==null)str=String.format("未设置[%s]审核,跳过此流程",tip);
                if (newEa.getProcessStatus()<EAProcessStatus.CNSH.getCode()){
                    date2.setTime(date2.getTime()+100);
                    addExpenseAccountLog(newEa.getId(),1,newEa.getProcessStatus(),aid,str,date2,2);
                    newEa.setProcessStatus(newEa.getProcessStatus()+1);
                    examineByprocess(newEa,admin,dep,list);
                }else {
                    date2.setTime(date2.getTime()+100);
                    addExpenseAccountLog(newEa.getId(),2,newEa.getProcessStatus(),aid,str,date2,2);
                    newEa.setProcessStatus(newEa.getProcessStatus());
                    newEa.setStatus(2);
                }
            }else if (flag3){
                newEa.setProcessStatus(newEa.getProcessStatus()+1);
                examineByprocess(newEa,admin,dep,list);
            }
        }
    }

    private boolean pushExpenseExamine(List<ExpenseConfigDetails> list, ExpenseAccount newEa, Integer status) {
        int finalConfigDetails = getFinalConfigDetails(status);
        //查询审核表，如果为空就新增，不然直接判定审核
        List<ExpenseAccountExamine> expenseAccountExamines = expenseAccountExamineMapper.selectByEaidAndProcessStatus(newEa.getId(), status);
        int count = 0;
        int size;
        if (expenseAccountExamines.isEmpty()){
            List<ExpenseConfigDetails> collect = list.stream().filter(e -> e.getType() == finalConfigDetails).collect(Collectors.toList());
            size= collect.size();
            for (ExpenseConfigDetails e : collect) {
                ExpenseAccountExamine eae=new ExpenseAccountExamine(newEa.getId(),status);
                eae.setAuditor(e.getAid());
                eae.setAuditorname(e.getAdminName());
                eae.setStatus(0);
                Integer i = expenseAccountExamineMapper.selectYesByParam(newEa.getId(), e.getAid());
                if (Objects.equals(status, EAProcessStatus.CNSH.getCode())){
                    eae.setStatus(0);
                    count++;
                }else {
                    if (i>0||e.getAid().equals(TokenManager.getAdminId())) {
                        eae.setStatus(1);
                        count++;
                    }
                }
                expenseAccountExamineMapper.insertSelective(eae);
            }

        }else {
            size = expenseAccountExamines.size();
            for (ExpenseAccountExamine e : expenseAccountExamines) {
                if (e.getStatus()==1){
                    count++;
                }
            }
        }
        if (count==size){
            return true;
        }
        return false;
    }

    private int getFinalConfigDetails(Integer status) {
        int configDetails=0;
        if (Objects.equals(status, EAProcessStatus.CWFS.getCode()))configDetails=1;
        else if (Objects.equals(status, EAProcessStatus.DSZSH.getCode()))configDetails=2;
        else if (Objects.equals(status, EAProcessStatus.CWGGLYSH.getCode()))configDetails=3;
        else if (Objects.equals(status, EAProcessStatus.GSFSSH.getCode()))configDetails=4;
        else if (Objects.equals(status, EAProcessStatus.CNSH.getCode()))configDetails=5;
        return configDetails;
    }

    private void addNoticeAndEmail(ExpenseAccount in, Integer status, String str,String aname, List<Admin> aids) {
        Date date= new Date();

        Integer noticeStatus=NoticeStatus.EXPENSE_NOTICE.getCode();
        if (status==2){
            noticeStatus=NoticeStatus.EXPENSE_COMPLETE.getCode();
            str="您提交的费用报销单已通过审核，请及时查看。";
        }
        List<Notice> list=new ArrayList<>();
        if (aids!=null){
            for (Admin a : aids) {
                Notice notice=new Notice();
                notice.setId(UUID.randomUUID().toString());
                notice.setNoticeType(noticeStatus);
                notice.setType(NoticeTypes.getType(noticeStatus));
                notice.setAid(a.getId());
                notice.setCreateTime(date);
                notice.setContent(str);
                notice.setReaded(0);
                list.add(notice);
                EmailBo emailBo=new EmailBo(NoticeStatus.getValueByCode(noticeStatus),a.getEmail(),str);
                asyncUtils.send(emailBo);
                try {
                    weChatUtils.addExpenseAccountWeChatNotice(a.getExpenseOpenId(),status,in.getId(),date,aname,str);
                }catch (Exception e){
                    throw new BusinessException("网络通信异常，请稍后再试。");
                }
            }
        asyncUtils.addNoticeBatch(list);
        }
    }

    @Override
    public Pagination<?> pageList(InputPageListBo in) {
        Map<String ,Object> param=new HashMap<>();
        addParam(in, param);
        Pagination<?> pagePlus = findPagePlus("selectExpenseAccountList",
                "selectExpenseAccountCount",
                "selectExpenseAccountTotalAmount", param, in.getPageNo(), in.getPageSize());
        List<OutExpenseAccountBo> list = (List<OutExpenseAccountBo>) pagePlus.getList();
        for (OutExpenseAccountBo e : list) {
            //设定是否需要审核
            e.setExamine(pushExpenseAccountGetExamine(e.getId(), e.getProcessStatus()));
            //设定是否完成订单匹配
            List<ExpenseAccount> expenseAccounts = expenseAccountMapper.selectListByMainId(e.getId());
            int count = 0;
            if(!expenseAccounts.isEmpty()){
                for (ExpenseAccount e2 : expenseAccounts) {
                    if(e2!=null&& e2.getOrderNo()!=null){
                        count++;
                    }
                }
            }
            if (count==expenseAccounts.size()){
                e.setFullyAllocate(1);
            }else {
                e.setFullyAllocate(0);
            }
        }
        return pagePlus;
    }

    @Override
    public Object selectByPrid(InputExpenseAccount in) {

        in.setAid(TokenManager.getAdminId());
        OutExpenseAccount useEa = expenseAccountMapper.selectByaidAndPrid(in);
        if (useEa!=null){
            ExpenseRelationship er = expenseRelationshipMapper.selectSonId(useEa.getId());
            if (er!=null){
                throw new BusinessException("公出已经发起报销");
            }
        }
        return useEa;
    }

    @Override
    public Object selectById(Integer id) {
        OutExpenseAccount useEa = expenseAccountMapper.selectByid(id);
        //判断当前角色是否审核
        useEa.setExamine(pushExpenseAccountGetExamine( id, useEa.getProcessStatus()));
        if (useEa.getDebitId()!=null){
            pushExpenseAccountGetDebitAmount(useEa);

        }
        return useEa;
    }


    @Override
    public Object selectByCheckNo(String checkNo) {
        checkNo=checkNo.trim();
        OutExpenseAccount useEa = expenseAccountMapper.selectByCheckNo(checkNo);
        if (useEa==null){
            throw  new BusinessException("报销编号不存在，请客对后再查询。");
        }
        if (useEa.getProcessStatus()<3||useEa.getStatus()!=2){
            throw  new BusinessException("报销单财务未审核，不可以根据编号查看打印。");
        }
        return useEa;
    }

    @Override
    public Object pushMerge(List<ExpenseAccount> list) {
        //设置付款部门和支付部门
        InputExpenseAccount in =new InputExpenseAccount();
        in.setCheckNo(idGenerator.getNOgenerateId());
        if (in.getAid()==null){
            in.setAid(TokenManager.getAdminId());
        }
        Admin admin = adminMapper.selectById(in.getAid());
        in.setAname(admin.getName());
        in.setApplyDep(admin.getDepartmentId());
        in.setPayDep(admin.getDepartmentId());
        Integer eaaid = expenseAccountPrivateMapper.selectByaidDefault(TokenManager.getAdminId());
        in.setEaaid(eaaid);
        BigDecimal count = new BigDecimal(0);
        BigDecimal amount = new BigDecimal(0);
        BigDecimal realAmount = new BigDecimal(0);
        List<String> typeList = new ArrayList<>();
        for (ExpenseAccount e : list) {
            count=count.add(e.getTotalAmount());
            amount=amount.add(e.getAmount());
            realAmount=realAmount.add(e.getRealAmount());
            StringBuilder types=new StringBuilder();
            if (e.getType()!=0){
                types.append(EATypes.getDescByCode(e.getType()));
            }else {
                types.append(e.getTypeOther());
            }
            if (e.getType()==2){
                if (e.getSecondaryType()==0){
                    types.append("-").append(e.getSecondaryTypeOther());
                }else{
                    String descByCode = EAsecondaryTypes.getDescByCode(e.getSecondaryType());
                    types.append("-").append(descByCode);
                }
            }
            if (!typeList.contains(types.toString()))typeList.add(types.toString());
        }
        in.setTotalAmount(count);
        in.setAmount(amount);
        in.setRealAmount(realAmount);
        in.setExpenseMain(1);
        in.setTypeOther(String.join(",",typeList));
        expenseAccountMapper.insertSelective(in);
        List<OutExpenseAccountLog> newLog=new ArrayList<>();
        list.forEach(e -> {
            ExpenseRelationship er=new ExpenseRelationship();
            er.setId(in.getId());
            er.setEaId(e.getId());
            expenseRelationshipMapper.insertSelective(er);
            List<OutExpenseAccountLog> listLog = expenseAccountLogMapper.selectByEaid(e.getId());
            for (OutExpenseAccountLog log : listLog) {
                if (!newLog.contains(log)){
                    log.setEaid(in.getId());
                    log.setShowStatus(1);
                    newLog.add(log);
                }
            }
            expenseAccountLogMapper.deleteByeaid(e.getId());
        });
        if (!newLog.isEmpty())expenseAccountLogMapper.insertBatch(newLog);
        expenseAccountMapper.updateByIds(list,1);
        return in.getId();
    }

    @Override
    public MainExpenseAccount mainExpense(Integer id) {
        MainExpenseAccount res = expenseAccountMapper.selectByMainId(id);
        pushData(res);
        res.setExamine(0);
        if (res.getAid().equals(TokenManager.getAdminId())){
            res.setExamine(2);
        }else {
            res.setExamine(pushExpenseAccountGetExamine( id, res.getProcessStatus()));
        }
        return res;
    }

    private void pushData(MainExpenseAccount res) {
        List<SonExpenseAccount> sonList = res.getSonList();
        List<String> orderList = new ArrayList<>();
        List<SonExpenseAccount> orderSonList = new ArrayList<>();
        //用来去重
        List<String> strList = new ArrayList<>();
        sonList.forEach(e -> {
            if (e.getOrderNo()==null){
                orderSonList.add(e);
            }else if (!orderList.contains(e.getOrderNo())){
                orderList.add(e.getOrderNo());
                orderSonList.add(e);
            }
        });
        orderList.clear();
        for (SonExpenseAccount e : orderSonList) {
            if (StringUtils.isNotEmpty(e.getUserNames())){
                String userNames = e.getUserNames();
                String  districtName = e.getDistrictName();
                int start=userNames.indexOf("[");
                if (start>0){
                    userNames = userNames.substring(0,start);
                    districtName=districtName.replace(userNames,"");
                    e.setUserNames(userNames);
                    e.setDistrictName(districtName);
                }else {
                    districtName=districtName.replace(userNames,"");
                    e.setDistrictName(districtName);
                }
            }
            if (e.getOrderNo()!=null){
                List<ExpenseAccount> expenseAccounts = expenseAccountMapper.selectByOrderNo(e.getOrderNo());
                int year = LocalDate.now().getYear();
                BigDecimal count = BigDecimal.ZERO;
                BigDecimal yxyCount = BigDecimal.ZERO;
                BigDecimal zxsCount = BigDecimal.ZERO;
                BigDecimal qtCount = BigDecimal.ZERO;
                TOrderPublicReleaseCount out = tOrderPublicReleaseCountMapper.selectByOrderNo(e.getOrderNo());
                if(!expenseAccounts.isEmpty()){
                    for (ExpenseAccount ea : expenseAccounts) {
                        count=count.add(ea.getRealAmount());
                        Admin admin = adminMapper.selectById(ea.getAid());
                        if (admin.getPosition()!=null){
                            if (admin.getPosition().equals("1")){
                                yxyCount=yxyCount.add(ea.getRealAmount());
                            }else if (admin.getPosition().equals("0")){
                                zxsCount=zxsCount.add(ea.getRealAmount());
                            }else {
                                qtCount=qtCount.add(ea.getRealAmount());
                            }
                        }
                    }
                }
                List<OrderYearMaxDuration> yearMaxDurationList = orderYearMaxDurationMapper.selectByOrderNo(e.getOrderNo());
                OrderYearMaxDuration useOyd = null;
                for (int i = 0; i < yearMaxDurationList.size(); i++) {
                    OrderYearMaxDuration oyd = yearMaxDurationList.get(i);
                    //年份为0不是会员直接获取跳出循环，否则获取当前年份，获取不到获取最后一年
                    if (oyd.getYear()==0){
                        useOyd=oyd;
                        break;
                    }else {
                        if (oyd.getYear().equals(year)){
                            useOyd = oyd;
                            break;
                        }
                        if(i==yearMaxDurationList.size()-1&&useOyd==null){
                            useOyd = yearMaxDurationList.get(i);
                        }
                    }
                }
                //获取未归集的订单
                List<BigDecimal> orderNoNotCountAmount = expenseAccountMapper.getOrderNoNotCountAmount(e.getOrderNo());
                BigDecimal countAmount = orderNoNotCountAmount.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                String str = getString(e, out, count, yxyCount, zxsCount,qtCount,useOyd);
                str=str+"，未归集："+countAmount.stripTrailingZeros().toPlainString()+"元";
                orderList.add(str);
            }else if (e.getPrid() !=null){
                //获取未归集的订单
                List<PublicReleaseDetails> list = publicReleaseMapper.selectDetailsByPrid(e.getPrid());
                StringBuilder sb = new StringBuilder();
                for (PublicReleaseDetails e2 : list) {
                    //不重复就添加到strList并且计算归集
                    if (!strList.contains(e2.getUid())){
                        strList.add(e2.getUid());
                        User user = userMapper.queryById(e2.getUid());
                        List<BigDecimal> orderNoNotCountAmount = expenseAccountMapper.getUidNotCountAmount(e2.getUid());
                        BigDecimal countAmount = orderNoNotCountAmount.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                        sb.append("[").append(user.getNickname()).append("]未归集：").append(countAmount.stripTrailingZeros().toPlainString()).append("元,");
                    }
                }
                if (sb.length()>1){
                    orderList.add(sb.substring(0,sb.length()-1));
                }
            }
            res.setStrList(orderList);
        }




    }

    private String getString(SonExpenseAccount e, TOrderPublicReleaseCount out, BigDecimal count, BigDecimal yxyCount,
                             BigDecimal zxsCount, BigDecimal qtCount, OrderYearMaxDuration useOyd) {
        TOrderPublicReleaseCountBo bo = new TOrderPublicReleaseCountBo();
//        if (out==null)return String.format("订单%s统计信息不存在，请要营销员先将订单派单。",e.getOrderNo());
//        bo.setMaxDuration(out.getMaxDuration());
//        BigDecimal maxDuration = BigDecimal.valueOf(out.getMaxDuration());
//        bo.setExceedDuration(maxDuration.subtract(BigDecimal.valueOf(out.getActualDuration())).abs());
//        bo.setActualDuration(out.getActualDuration());
//        bo.setFrequency(out.getFrequency());
//        bo.setPeopleCount(out.getPeopleCount());
//        String maxString="";
//        String actualString=",超过" + bo.getExceedDuration() + "小时,";
//        if (out.getMaxType()==0){
//            maxString=":原则上不能公出";
//            maxString=":原则上不能公出";
//        }else if (out.getMaxType()==1){
//            maxString=":应公出" + bo.getMaxDuration() +"小时";
//        }else if (out.getMaxType()==2){
//            maxString=":无限制公出，预估" + bo.getMaxDuration() +"小时";
//        }
//        if (bo.getExceedDuration().compareTo(BigDecimal.ZERO) <1){
//            actualString="";
//        }
        if (out==null||useOyd==null){
            TOrderNewBo orderNewBo = orderNewService.getOrderNewDetail(e.getOrderNo());
            return String.format("%s+%s派单进行中（无法统计公出信息）",orderNewBo.getNickname(),orderNewBo.getContractNo());
        }
        bo.setMaxDuration(useOyd.getMaxDuration().doubleValue());
        bo.setExceedDuration(useOyd.getMaxDuration().subtract(BigDecimal.valueOf(out.getActualDuration())).abs());
        bo.setActualDuration(out.getActualDuration());
        bo.setFrequency(out.getFrequency());
        bo.setPeopleCount(out.getPeopleCount());
        String yearStr="";
        if (useOyd.getYear()>0){
            yearStr="["+useOyd.getYear()+"年]";
        }
        String maxString=":应公出" + bo.getMaxDuration() +"小时"+yearStr;
        String actualString=",超过" + bo.getExceedDuration() + "小时,";
        if (useOyd.getStatus()==1){
            maxString=":不限制公出小时"+yearStr;
            actualString="";
        }
        StringBuilder expenseString=new StringBuilder().append("报销").append(count.stripTrailingZeros().toPlainString()).append("元");
        if (yxyCount.compareTo(BigDecimal.ZERO)>0 || zxsCount.compareTo(BigDecimal.ZERO)>0||qtCount.compareTo(BigDecimal.ZERO)>0){
            expenseString.append("(");
            if (zxsCount.compareTo(BigDecimal.ZERO) > 0) {
                expenseString.append("技术").append(zxsCount.stripTrailingZeros().toPlainString()).append("元,");
            }
            if (yxyCount.compareTo(BigDecimal.ZERO) > 0){
                expenseString.append("营销").append(yxyCount.stripTrailingZeros().toPlainString()).append("元,");
            }
            if (qtCount.compareTo(BigDecimal.ZERO) > 0){
                expenseString.append("其他").append(qtCount.stripTrailingZeros().toPlainString()).append("元,");
            }
            expenseString.deleteCharAt(expenseString.length() - 1);
            expenseString.append(")");
        }
        if (BigDecimal.valueOf(bo.getMaxDuration()).compareTo(BigDecimal.valueOf(bo.getActualDuration()))>0){
            actualString="";
        }
        return e.getBuyerName() + e.getContractNo() +
                maxString +
                ",已" + out.getPeopleCount() + "人公出," +
                out.getFrequency() + "次" +
                "," + out.getActualDuration() + "小时" +
                actualString+expenseString;
    }


    @Override
    public Object updateMain(MainExpenseAccount in) {
        OutExpenseAccount outExpenseAccount = expenseAccountMapper.selectByid(in.getId());
        if (outExpenseAccount != null&&outExpenseAccount.getStatus()!=0&&outExpenseAccount.getStatus()!=3) {
            throw new BusinessException("主项目已发起，不可修改！");
        }
        ExpenseAccount update =new ExpenseAccount();
        if (in.getStatus()==null)in.setStatus(1);
        if (in.getStatus()==1){
            if (in.getPayDep()!=null)update.setPayDep(in.getPayDep());
            if (in.getApplyDep()!=null)update.setApplyDep(in.getApplyDep());
            if (in.getRemarks()!=null)update.setRemarks(in.getRemarks());
            if (in.getEaaid()!=null)update.setEaaid(in.getEaaid());
            addExpenseAccountExamine(in.getId(),update);
            if (outExpenseAccount != null && outExpenseAccount.getStatus() == 0) {
                addExpenseAccountLogAndNoticEmail(outExpenseAccount, outExpenseAccount.getStatus(), "发起报销审核");
            }
            update.setStatus(in.getStatus());
            if (outExpenseAccount != null) {
                update.setProcessStatus(outExpenseAccount.getProcessStatus());
            }
            update.setId(in.getId());
            if (outExpenseAccount != null) {
                update.setExamineName(outExpenseAccount.getExamineName());
            }
            expenseAccountMapper.updateByPrimaryKeySelective(update);
//            expenseAccountMapper.updateByOrderRemarks(in.getId());
        }else if (in.getStatus()==4){
            expenseAccountMapper.updateByMainId(in.getId(),0);
            addExpenseAccountLog(in.getId(),4,0,TokenManager.getAdminId(),"撤销报销审核");
            List<ExpenseRelationship> list = expenseRelationshipMapper.selectById(in.getId());
            //撤销日志处理，先将日志改在挂子编号上，在新发起的时候再从自编号加到新编号上
            if (list.size()==1){
                ExpenseRelationship e =list.get(0);
                expenseAccountLogMapper.updateUseLogByUseId(e.getEaId(),e.getId());
            }else {
                List<OutExpenseAccountLog> outExpenseAccountLogs = expenseAccountLogMapper.selectByEaid(in.getId());
                for (ExpenseRelationship e : list) {
                    for (OutExpenseAccountLog eal : outExpenseAccountLogs) {
                        eal.setEaid(e.getEaId());
                    }
                    expenseAccountLogMapper.insertBatch(outExpenseAccountLogs);
                }
            }
            expenseAccountLogMapper.deleteByeaid(in.getId());
            expenseRelationshipMapper.deleteByPrimaryKey(in.getId());
            expenseAccountMapper.deleteByPrimaryKey(in.getId());
        }
        return 1;
    }

    @Override
    public Object mainList(ExpenseMainListInput in) {
        Map<String,Object> params = new HashMap<>();
        if (in.getStatus()!=null) params.put("status",in.getStatus());
        if (in.getCheckNo()!=null) params.put("checkNo",in.getCheckNo());
        if (in.getListStatus()!=null) params.put("listStatus",in.getListStatus());
        if (in.getUsername() != null)params.put("username",in.getUsername());
        if (in.getStartTime()!=null && in.getEndTime() !=null) {
            params.put("startTime",in.getStartTime());
            params.put("endTime",in.getEndTime()+" 23:59:59");
        }
        //1-3s是不同人的审核
        if (in.getProcessStatus()!=null&&in.getProcessStatus()==1){
            // 财务审核，复审，出纳都是财务与经理
            if(TokenManager.hasRole(AFTConstants.FINANCE_ADMIN)){
                in.setProcessStatus(6);
            }else if (TokenManager.hasRole(AFTConstants.APPROVAL_DECISION)||TokenManager.hasRole(AFTConstants.APPROVAL_DECISION_ASSISTANT)){
                in.setProcessStatus(5);
            }else if(TokenManager.hasRole(AFTConstants.VICE_CEO)){
                //先不做财务查看权限
                in.setProcessStatus(7);
            }else if (TokenManager.hasRole(AFTConstants.SALESMAN_MANAGER)){
                 in.setProcessStatus(1);
             }else if (TokenManager.hasRole(AFTConstants.SALESMAN_ADMIN)){
                 in.setProcessStatus(2);
            }else if (TokenManager.hasRole(AFTConstants.FINANCE)||TokenManager.hasRole(AFTConstants.FINANCE_MANAGER)){
                in.setProcessStatus(3);
            }
        }else {
                in.setProcessStatus(0);
        }


        if (in.getProcessStatus()!=null){
            params.put("aid",TokenManager.getAdminId());
            params.put("roleType",in.getProcessStatus());
            if (in.getProcessStatus()==3){
                params.put("deps",departmentService.selectMyDeps());
            }
        }
        Pagination<?> page = findPage("ExpenseMainList", "ExpenseMainCount", params, in.getPageNo(), in.getPageSize());
        List<MainExpenseAccount> list = (List<MainExpenseAccount>) page.getList();
        if (!list.isEmpty()){
            List<Integer> collect = list.stream().map(MainExpenseAccount::getId).collect(Collectors.toList());
            List<MainExpenseAccount> mainExpenseAccounts = expenseAccountMapper.selectSonByMainIds(collect);
            for (MainExpenseAccount e : list) {
                for (MainExpenseAccount me : mainExpenseAccounts) {
                    if (e.getId().equals(me.getId())){
                        e.setSonList(me.getSonList());
                    }
                }
            }
        }
        return page;
    }

    @Override
    public Pagination<?> detailsList(InputDetailsListBo in) {
        Map<String,Object> params = new HashMap<>();
        if(in.getStatus()!=null)params.put("status",in.getStatus());
        if(in.getType()!=null)params.put("type",in.getType());
        if (in.getDepId()!=null){
            ArrayList<Object> deps = new ArrayList<>();
            deps.add(in.getDepId());
            params.put("deps",deps);
        }
        if (in.getContractNo()!=null)params.put("contractNo",in.getContractNo());
        if (in.getBuyerName()!=null)params.put("buyerName",in.getBuyerName());
        if (in.getAid()!=null)params.put("aid",in.getAid());
        if (in.getApplyDep()!=null)params.put("applyDep",in.getApplyDep());
        if (in.getPayDep() != null)params.put("payDep",in.getPayDep());
        if (in.getStartTime()!=null)params.put("startTime",in.getStartTime());
        if (in.getEndTime()!=null)params.put("endTime",in.getEndTime());
        if (in.getCheckNo()!=null)params.put("checkNo",in.getCheckNo());
        params.put("current",TokenManager.getAdminId());
        if (in.getMyType()!=null){
            params.put("myType",in.getMyType());
        }else {
            params.put("myType",0);
        }
        //0=自己 1=财务 2=财务经理 3=超管
        if (TokenManager.hasRole(AFTConstants.SUPERADMIN)||TokenManager.hasRole(AFTConstants.FINANCE_ADMIN)){
            params.put("roleType",3);
        }else if (TokenManager.hasRole(AFTConstants.FINANCE_MANAGER)){
            params.put("roleType",2);
            List<String> strings = adminMapper.selectBySuperId(TokenManager.getAdminId());
            strings.add(TokenManager.getAdminId());
            params.put("checkList",strings);

        }else if (TokenManager.hasRole(AFTConstants.FINANCE)){
            params.put("roleType",1);
            params.put("current",TokenManager.getAdminId());
        }else{
            params.put("roleType",0);
            params.put("aid",TokenManager.getAdminId());
        }
        return findPagePlus("ExpenseDetailsList","ExpenseDetailsCount","ExpenseDetailsTotalAmount",params,in.getPageNo(),in.getPageSize());
    }

    @Override
    public List<ExpenseAccount> selectByIds(List<String> list) {
        return expenseAccountMapper.selectByIds(list);
    }

    @Override
    public int selectCountByIdsAndType(List<String> list, Integer i) {
        return expenseAccountMapper.selectCountByIdsAndType(list,i);
    }

    @Override
    public Object updateRealAmount(Integer type, Integer id, BigDecimal amount) {
        ExpenseAccountDetails ea= new ExpenseAccountDetails();
        ea.setId(id);
        ea.setRealAmount(amount);
        String name= Objects.requireNonNull(TokenManager.getAdminToken()).getName();
        Integer eaid = expenseAccountDetailsMapper.selectExpenseAccountById(id);
        addExpenseAccountLog(eaid,5,3,TokenManager.getAdminId(),String.format("财务[%s]修改实际报销金额为%s。",name,amount));
        expenseAccountDetailsMapper.updateByPrimaryKeySelective(ea);
        expenseAccountMapper.updateSonByDetId(id);
        expenseAccountMapper.updateMainByDetId(id);
        return 1;
    }




    /**
     * 计算当前报销显示抵扣金额
     */
    private void pushExpenseAccountGetDebitAmount(OutExpenseAccount useEa) {
        ExpenseAccount debit = expenseAccountMapper.selectByPrimaryKey(useEa.getDebitId());
        List<ExpenseAccount> list1 = expenseAccountMapper.selectByDebitId(useEa.getDebitId());
        BigDecimal count =debit.getRealAmount();
        for (ExpenseAccount e : list1) {
            if (!e.getId().equals(useEa.getId())){
                count=count.subtract(e.getSettlementAmount());
            }
        }
        if (count.compareTo(new BigDecimal(0))<0)count=new BigDecimal(0);
        useEa.setDebitTotalAmount(count);
    }

    /**
     * 判断角色是否已经审核
     * @param id  编号
     * @param processStatus  审核状态
     * @return 0 无需审核 1 需要审核
     */
    private Integer pushExpenseAccountGetExamine(Integer id,Integer processStatus) {
        List<ExpenseAccountExamine> list = expenseAccountExamineMapper.selectByEaidAndProcessStatus(id, processStatus);
        boolean flag=false;
        for (ExpenseAccountExamine e : list) {
            if (e.getAuditor().equals(TokenManager.getAdminId())){
                if (e.getStatus()==0){
                    flag=true;
                    break;
                }
            }
        }
        if (flag){
            return 1;
        }else {
            return 0;
        }
    }

    @Override
    public Object updateType(InputExpenseAccount in) {
        expenseAccountDetailsMapper.deleteByeaid(in.getId());
        if (in.getType()!=2)in.setSecondaryType(0);
        in.setSettlementAmount(new BigDecimal(0));
        return expenseAccountMapper.updateByPrimaryKeySelective(in);
    }

    @Override
    public List<OutExpenseAccount> selectDebitOrder(String depId,Integer id) {
        String aid= TokenManager.getAdminId();
        return expenseAccountMapper.selectByaidAndType(aid, depId, 4,id);
    }



    private void addParam(InputPageListBo in, Map<String, Object> param) {
        if (in.getStartTime()!=null&&in.getEndTime()!=null){
            param.put("startTime",in.getStartTime());
            param.put("endTime",in.getEndTime()+" 23:59:59");
        }
        if (in.getStatus()!=null)param.put("status",in.getStatus());
        //如果前端未传 0自己 1审核的参数，默认自己，在审核列表的时候判断Aid,在
        param.put("auditor",TokenManager.getAdminId());
        if(in.getProcessStatus()==null){
            if(TokenManager.hasRole(AFTConstants.FINANCE_MANAGER)){
                //财务经理查看权限
                in.setProcessStatus(5);
                param.put("auditor",TokenManager.getAdminId());
            }else if(TokenManager.hasRole(AFTConstants.FINANCE_ADMIN)||TokenManager.hasRole(AFTConstants.SUPERADMIN)||
                    TokenManager.hasRole(AFTConstants.VICE_CEO)|| TokenManager.hasRole(AFTConstants.APPROVAL_DECISION)){
                //财务管理员和超级管理员查看全部
                in.setProcessStatus(6);
                param.put("auditor",TokenManager.getAdminId());
                if(StringUtils.isNotBlank(in.getAid()))param.put("aid",in.getAid());
            }else if (TokenManager.hasRole(AFTConstants.FINANCE)){
                //0 设置为财务查看
                param.put("auditor",TokenManager.getAdminId());
                //如果是空设置为
                if (in.getProcessStatus()==null)in.setProcessStatus(2);
            }else {
                in.setProcessStatus(0);
            }
        }
        param.put("processStatus",in.getProcessStatus());
         if (in.getRoleType()==null){
             param.put("roleType",0);
         }else {
             param.put("roleType",in.getRoleType());
         }
         if(in.getProcessStatus()==2||in.getProcessStatus()==1){
                if (in.getAid()!=null){
                    param.put("aid",in.getAid());
                }
        }
        if (in.getExamineStatus()!=null){
            param.put("examineStatus",in.getExamineStatus());
        }else {
            param.put("examineStatus",0);
        }
        if (in.getContractNo()!=null)param.put("contractNo",in.getContractNo());
        if (in.getDepId()!=null)param.put("depId",in.getDepId());
        if (in.getApplyDep()!=null)param.put("applyDep",in.getApplyDep());
        if (in.getPayDep()!=null)param.put("payDep",in.getPayDep());
        if (in.getType()!=null)param.put("type",in.getType());
        if (in.getUsername()!=null)param.put("username",in.getUsername());
        if (in.getCheckNo()!=null)param.put("checkNo",in.getCheckNo());
        if (in.getSecondaryType()!=null)param.put("secondaryType",in.getSecondaryType());
        //固定传0查看子列表
        if (in.getExpenseMain()==null)in.setExpenseMain(0);
        if (in.getExpenseMain()!=null)param.put("expenseMain",in.getExpenseMain());
    }

    @Override
    @Cacheable(value = "expenseAccountStatistics#300", key = "'page:'+#name+',depId'+#depId+',startTime'+#startTime+',endTime'+#endTime+'lander'+#lander")
    public List<OutExpenseAccountStatistics> statistics(String name,String depId,String startTime,String endTime,String lander) {
        if (endTime != null) endTime = endTime + " 23:59:59";
        List<OutMyEAStatistics> list = expenseAccountMapper.selectStatistics(name, depId, startTime, endTime, lander);
        List<OutExpenseAccountStatistics> results = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        for (OutMyEAStatistics e : list) {
            OutExpenseAccountStatistics out = new OutExpenseAccountStatistics(e.getAid());
            out.setAname(e.getAname());
            if (e.getStatus() == 2) {
                if (e.getType() < 4) {
                    out.setBx(1);
                    out.setBxAmount(e.getTotalAmount());
                    if (!e.getDebitId().equals(0)) {
                        out.setDk(1);
                        out.setDkAmount(e.getBxdkAmount());
                    }
                } else if (e.getType() == 4) {
                    out.setJz(1);
                    out.setJzAmount(e.getTotalAmount());
                } else if (e.getType() == 5) {
                    out.setDk(1);
                    out.setDkAmount(e.getTotalAmount());
                }

                if (e.getTargetType() == 0) {
                    out.setGdfyAmount(e.getTotalAmount());
                } else if (e.getTargetType() == 1) {
                    out.setBxddAmount(e.getTotalAmount());
                }

            } else if (e.getStatus() == 1) {
                out.setWshs(1);
            } else if (e.getStatus() == 3) {
                out.setBhs(1);
            }
            //如果不存在就新增
            if (!map.containsKey(out.getAid())) {
                List<OutExpenseAccountStatistics> list2 = new ArrayList<>();
                list2.add(out);
                map.put(out.getAid(), list2);
            } else {
                List<OutExpenseAccountStatistics> list2 = (List<OutExpenseAccountStatistics>) map.get(e.getAid());
                list2.add(out);
            }
        }
        for (String s : map.keySet()) {
            List<OutExpenseAccountStatistics> list3 = (List<OutExpenseAccountStatistics>) map.get(s);
            if (!list3.isEmpty()) {
                OutExpenseAccountStatistics out = new OutExpenseAccountStatistics(s);

                for (OutExpenseAccountStatistics e : list3) {
                    if (out.getAname() == null) out.setAname(e.getAname());
                    if (e.getBx() != null) out.setBx(out.getBx() + e.getBx());
                    if (e.getJz() != null) out.setJz(out.getJz() + e.getJz());
                    if (e.getDk() != null) out.setDk(out.getDk() + e.getDk());
                    if (e.getBxAmount() != null) out.setBxAmount(out.getBxAmount().add(e.getBxAmount()));
                    if (e.getJzAmount() != null) out.setJzAmount(out.getJzAmount().add(e.getJzAmount()));
                    if (e.getDkAmount() != null) out.setDkAmount(out.getDkAmount().add(e.getDkAmount()));
                    if (e.getGdfyAmount() != null) out.setGdfyAmount(out.getGdfyAmount().add(e.getGdfyAmount()));
                    if (e.getBxddAmount() != null) out.setBxddAmount(out.getBxddAmount().add(e.getBxddAmount()));
                    if (e.getWshs() != null) out.setWshs(out.getWshs() + e.getWshs());
                    if (e.getBhs() != null) out.setBhs(out.getBhs() + e.getBhs());
                }
                BigDecimal totalAmount = new BigDecimal(0);
                totalAmount = totalAmount.add(out.getBxAmount()).add(out.getJzAmount()).subtract(out.getDkAmount());
                out.setTotalAmount(totalAmount);
                results.add(out);
            }

        }
        return results;
    }

    @Override
    @CacheEvict(value = "expenseAccountStatistics#300", allEntries = true)
    public void statisticsEvict(String name, String depId, String startTime, String endTime) {
        LoggerUtils.debug(getClass(), "报销统计缓存清除。");
    }

    @Override
    public Object pushSupplementCheckNo() {
        List<ExpenseAccount> list = expenseAccountMapper.selectByCheckNoNot();
        if (!list.isEmpty()){
            list.forEach(e ->{
                ExpenseAccount ea=new ExpenseAccount();
                ea.setId(e.getId());
                ea.setCheckNo(idGenerator.getNOgenerateId());
                expenseAccountMapper.updateByPrimaryKeySelective(ea);
            });
            return 1;
        }
        return 0;
    }

    @Override
    public void pushAll() {
        List<ExpenseAccount> list = expenseAccountMapper.selectAllMain();
        for (ExpenseAccount ea : list) {
            List<ExpenseAccount> expenseAccounts = expenseAccountMapper.selectListByMainId(ea.getId());
            List<String> typeList=new ArrayList<>();
            for (ExpenseAccount e : expenseAccounts) {
                StringBuilder types=new StringBuilder();
                if (e.getType()!=0){
                    types.append(EATypes.getDescByCode(e.getType()));
                }else {
                    types.append(e.getTypeOther());
                }
                if (e.getType()==2){
                    if (e.getSecondaryType()==0){
                        types.append("-").append(e.getSecondaryTypeOther());
                    }else{
                        String descByCode = EAsecondaryTypes.getDescByCode(e.getSecondaryType());
                        types.append("-").append(descByCode);
                    }
                }
                if (!typeList.contains(types.toString()))typeList.add(types.toString());
            }
            String typeStr = String.join(",", typeList);
            ExpenseAccount update=new ExpenseAccount();
            update.setId(ea.getId());
            update.setTypeOther(typeStr);
            expenseAccountMapper.updateByPrimaryKeySelective(update);
        }

    }




}
