Merge branch 'release/2.19.1.2501.01' into release/个税版本

# Conflicts:
#	src/com/engine/salary/entity/taxdeclaration/param/TaxDeclarationSaveParam.java
#	src/com/engine/salary/maintainer/datacollection/AddUpSituationManager.java
#	src/com/engine/salary/service/impl/TaxDeclarationServiceImpl.java
#	src/com/engine/salary/wrapper/SalaryCommonWrapper.java
This commit is contained in:
钱涛 2025-02-08 15:50:39 +08:00
commit 9adafca446
25 changed files with 617 additions and 70 deletions

View File

@ -0,0 +1,3 @@
alter table hrsa_page_list_template add file_id int;
/

View File

@ -0,0 +1,3 @@
alter table hrsa_page_list_template add file_id int;
/

View File

@ -0,0 +1,3 @@
alter table hrsa_page_list_template add file_id int;
/

View File

@ -0,0 +1 @@
ALTER TABLE hrsa_page_list_template ADD COLUMN file_id int(0);

View File

@ -0,0 +1,2 @@
alter table hrsa_page_list_template add file_id int
/

View File

@ -0,0 +1 @@
alter table hrsa_page_list_template add file_id int;

View File

@ -0,0 +1,2 @@
alter table hrsa_page_list_template add file_id int
go

View File

@ -0,0 +1,3 @@
alter table hrsa_page_list_template add file_id int;
/

View File

@ -59,6 +59,11 @@ public class PageListTemplateDTO {
@TableTitle(title = "范围",dataIndex = "limits",key = "limits")
private String limits;
@TableTitle(title = "导出模板",dataIndex = "fileName",key = "fileName")
private String fileName;
private Integer fileId;
/**
* 限制
*/

View File

@ -0,0 +1,24 @@
package com.engine.salary.entity.setting.param;
import com.engine.salary.util.valid.DataCheck;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DownloadTemplateParam {
@DataCheck(require = true,message = "请选择表头")
private List<String> heads;
@DataCheck(require = true,message = "请选择设置")
private List<String> setting;
}

View File

@ -40,4 +40,9 @@ public class PageListTemplateSaveParam {
*/
@DataCheck(require = true,message = "请选择设置")
private List<String> setting;
/**
* 导出模板id
*/
private Integer fileId;
}

View File

@ -12,7 +12,7 @@ import java.util.Date;
import java.util.List;
/**
* 薪资帐套
* 页面模板
*/
@Data
@Builder
@ -64,6 +64,12 @@ public class PageListTemplatePO {
@ElogTransform(name = "限制")
private List<Long> limitIds;
/**
* excel模板文件id
*/
@ElogTransform(name = "模板文件id")
private Integer fileId;
/**
* 租户ID
*/

View File

@ -2,6 +2,7 @@ package com.engine.salary.entity.taxdeclaration.param;
import com.engine.salary.enums.taxdeclaration.TaxAgentRangeEnum;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -9,6 +10,8 @@ import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
import java.time.YearMonth;
import java.util.Date;
/**
* 生成个税申报表参数
@ -54,4 +57,12 @@ public class TaxDeclarationSaveParam {
* 备注
*/
private String description;
private String salaryMonthStr;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date taxCycle;
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date salaryDate;
}

View File

@ -49,7 +49,7 @@ public class AddUpSituationManager extends Service {
int delete = getTaxDeclarationMapper().deleteByIdZj(po.getId());
// 调用生成申报单接口
try {
getTaxDeclarationService().save(TaxDeclarationSaveParam.builder().salaryMonth(po.getSalaryMonth()).taxAgentId(po.getTaxAgentId()).build());
getTaxDeclarationService().save(TaxDeclarationSaveParam.builder().salaryMonth(po.getSalaryMonth()).salaryDate(po.getSalaryMonth()).taxCycle(po.getTaxCycle()).taxAgentId(po.getTaxAgentId()).build());
} catch (Exception e) {
bb.writeLog("错误:" + e);
result = false;

View File

@ -9,11 +9,12 @@
<result column="limit_ids" property="limitIds" typeHandler="com.engine.salary.handle.LongListTypeHandler"/>
<result column="name" property="name"/>
<result column="page" property="page"/>
<result column="setting" property="setting" typeHandler="com.engine.salary.handle.SalaryListTypeHandler" />
<result column="setting" property="setting" typeHandler="com.engine.salary.handle.SalaryListTypeHandler"/>
<result column="shared_type" property="sharedType"/>
<result column="system_type" property="systemType"/>
<result column="tenant_key" property="tenantKey"/>
<result column="update_time" property="updateTime"/>
<result column="file_id" property="fileId"/>
</resultMap>
<!-- 表字段 -->
@ -32,6 +33,7 @@
, t.system_type
, t.tenant_key
, t.update_time
, t.file_id
</sql>
<!-- 查询全部 -->
@ -75,13 +77,16 @@
<if test="page != null">
AND page = #{page}
</if>
<if test="fileId != null">
AND file_id = #{fileId}
</if>
<if test="setting != null">
AND setting = #{setting}
</if>
<if test="sharedType != null">
AND shared_type = #{sharedType}
</if>
<if test="systemType != null">
<if test="systemType != null">
AND system_type = #{systemType}
</if>
<if test="tenantKey != null">
@ -141,6 +146,9 @@
<if test="updateTime != null">
update_time,
</if>
<if test="fileId != null">
file_id,
</if>
</trim>
<trim prefix="VALUES (" suffix=")" suffixOverrides=",">
<if test="createTime != null">
@ -179,6 +187,9 @@
<if test="updateTime != null">
#{updateTime},
</if>
<if test="fileId != null">
#{fileId},
</if>
</trim>
</insert>
@ -197,6 +208,7 @@
system_type=#{systemType},
tenant_key=#{tenantKey},
update_time=#{updateTime},
file_id=#{fileId},
</set>
WHERE id = #{id} AND delete_type = 0
</update>
@ -239,6 +251,7 @@
<if test="updateTime != null">
update_time=#{updateTime},
</if>
file_id=#{fileId},
</set>
WHERE id = #{id} AND delete_type = 0
</update>

View File

@ -10,6 +10,8 @@ import com.engine.salary.component.WeaTableColumnGroup;
import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO;
import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveListDTO;
import com.engine.salary.entity.salaryitem.po.SalaryItemPO;
import com.engine.salary.entity.setting.param.PageListTemplateQueryParam;
import com.engine.salary.entity.setting.po.PageListTemplatePO;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.report.common.constant.SalaryConstant;
import com.engine.salary.report.entity.bo.SalaryStatisticsReportBO;
@ -29,6 +31,7 @@ import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.excel.ExcelFillUtils;
import com.engine.salary.util.excel.ExcelUtilPlus;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
@ -38,9 +41,11 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import weaver.file.ImageFileManager;
import weaver.general.Util;
import weaver.hrm.User;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@ -183,10 +188,12 @@ public class SalaryStatisticsEmployeeWrapper extends Service {
pageInfo.setTotal(salaryAcctEmployeePageInfo.getTotal());
List<WeaTableColumn> weaTableColumns = getSettingService(user).getPageListColumns(SALARY_DETAILS_REPORT.getValue());
List<PageListTemplatePO> pageListTemplates = getSettingService(user).getPageListTemplates(PageListTemplateQueryParam.builder().page(SALARY_DETAILS_REPORT.getValue()).build());
Integer fileId = pageListTemplates.stream().filter(PageListTemplatePO::getChecked).findFirst().map(PageListTemplatePO::getFileId).orElse(null);
// 结果
resultMap.put("columns", weaTableColumns);
resultMap.put("pageInfo", pageInfo);
resultMap.put("fileId", fileId);
if (queryParam.isExport()) {
Map<String, Object> countResultMap = Maps.newHashMap();
List<SalaryItemPO> salaryItems = salaryStatisticsEmployeeDetailResult.getSalaryItemList();
@ -270,8 +277,17 @@ public class SalaryStatisticsEmployeeWrapper extends Service {
Map<String, Object> resultMap = salaryList(queryParam);
List<WeaTableColumn> columns = (List<WeaTableColumn>) resultMap.get("columns");
List<Map<String, Object>> resultList = ((PageInfo<Map<String, Object>>) resultMap.get("pageInfo")).getList();
Map<String, Object> countResult = (Map<String, Object>) resultMap.get("countResult");
//根据上传的模板导出
Object fileId = resultMap.get("fileId");
if (fileId != null) {
InputStream inputStream = ImageFileManager.getInputStreamById((Integer) fileId);
XSSFWorkbook workbook = ExcelFillUtils.fillOneSheet(inputStream, 0, resultList);
return workbook;
}
//根据显示列表导出
Map<String, Object> countResult = (Map<String, Object>) resultMap.get("countResult");
Map<String, WeaTableColumn> columnMap = SalaryEntityUtil.convert2Map(columns, WeaTableColumn::getColumn);
// 获取薪资项目保留小数位数
List<SalaryItemPO> salaryItemPOList = getSalaryItemService(user).listAll();

View File

@ -6,6 +6,7 @@ import com.engine.salary.entity.setting.dto.PageListTemplateDetailDTO;
import com.engine.salary.entity.setting.param.*;
import com.engine.salary.entity.setting.po.PageListTemplatePO;
import com.engine.salary.util.page.PageInfo;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.util.List;
@ -45,6 +46,13 @@ public interface SettingService {
*/
PageListTemplatePO savePageListTemplate(PageListTemplateSaveParam param);
/**
* 下载导出模板文件
* @param param
* @return
*/
XSSFWorkbook downloadPageExportTemplateFile(DownloadTemplateParam param);
/**
* 更换模板
* @param param

View File

@ -5076,8 +5076,11 @@ public class SIAccountServiceImpl extends Service implements SIAccountService {
List<DataCollectionEmployee> employeeList = new ArrayList<>();
if (!isFirstFlag) {
// 不是首次核算需要把社保历史数据取出
historyDetailData.addAll(getInsuranceAccountDetailMapper().list(InsuranceAccountDetailParam.builder().billMonth(billMonth).paymentOrganization(paymentOrganization.toString()).employeeIds(ids).build()));
historyDetailData.addAll(getInsuranceAccountDetailMapper().extList(InsuranceAccountDetailParam.builder().billMonth(billMonth).paymentOrganization(paymentOrganization.toString()).employeeIds(ids).build()));
List<List<Long>> partition = Lists.partition(ids, 100);
partition.forEach(part -> {
historyDetailData.addAll(getInsuranceAccountDetailMapper().list(InsuranceAccountDetailParam.builder().billMonth(billMonth).paymentOrganization(paymentOrganization.toString()).employeeIds(part).build()));
historyDetailData.addAll(getInsuranceAccountDetailMapper().extList(InsuranceAccountDetailParam.builder().billMonth(billMonth).paymentOrganization(paymentOrganization.toString()).employeeIds(part).build()));
});
} else {
employeeList = getSalaryEmployeeService(user).listByIds(ids);
}

View File

@ -29,9 +29,12 @@ import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.IdGenerator;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.excel.ExcelUtilPlus;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.ValidUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import weaver.file.ImageFileManager;
import weaver.hrm.User;
import java.util.*;
@ -329,6 +332,7 @@ public class SettingServiceImpl extends Service implements SettingService {
.systemType(0)
.limitIds(param.getLimitIds())
.setting(param.getSetting())
.fileId(param.getFileId())
.creator((long) user.getUID())
.createTime(now)
.updateTime(now)
@ -344,6 +348,7 @@ public class SettingServiceImpl extends Service implements SettingService {
po.setSharedType(param.getSharedType());
po.setLimitIds(param.getLimitIds());
po.setSetting(param.getSetting());
po.setFileId(param.getFileId());
po.setUpdateTime(now);
getPageListTemplateMapper().updateIgnoreNull(po);
}
@ -351,6 +356,21 @@ public class SettingServiceImpl extends Service implements SettingService {
return po;
}
@Override
public XSSFWorkbook downloadPageExportTemplateFile(DownloadTemplateParam param) {
List<List<String>> rowList = new ArrayList<>();
List<String> heads = param.getHeads();
List<String> setting = param.getSetting();
if (CollUtil.isEmpty(heads)||CollUtil.isEmpty(setting)||!Objects.equals(heads.size(), setting.size())){
throw new SalaryRunTimeException("未选择字段");
}
setting = setting.stream().map(s -> String.format("{.%s}", s)).collect(Collectors.toList());
rowList.add(heads);
rowList.add(setting);
return ExcelUtilPlus.genWorkbook(rowList, "薪资明细");
}
@Override
public void changePageListTemplate(PageListTemplateChangeParam param) {
@ -400,19 +420,31 @@ public class SettingServiceImpl extends Service implements SettingService {
return CollectionUtil.isEmpty(limitIds) || CollectionUtil.intersection(limitIds, taxIds).size() != 0;
}
return true;
}).map(po -> PageListTemplateDTO.builder()
.id(po.getId())
.page(po.getPage())
.name(po.getName())
.sharedType(po.getSharedType())
.systemType(po.getSystemType())
.sharedTypeName(SharedTypeEnum.parseByValue(po.getSharedType()).getDefaultLabel())
.systemTypeName(po.getSystemType() == 1 ? "系统模板" : "自定义")
.limits(CollUtil.isEmpty(po.getLimitIds()) ? "所有" : Optional.ofNullable(po.getLimitIds()).orElse(new ArrayList<>()).stream().map(id -> idNameMap.getOrDefault(id, "")).collect(Collectors.joining(",")))
.limitIds(CollUtil.isEmpty(po.getLimitIds()) ? new ArrayList<>() : po.getLimitIds())
.setting(po.getSetting())
.canEdit(chief || (Objects.equals((long) user.getUID(), po.getCreator()) && po.getSystemType() == 0))
.build())
}).map(po -> {
Integer fileId = po.getFileId();
String fileName = "";
if (fileId != null) {
ImageFileManager imageFileManager = new ImageFileManager();
imageFileManager.getImageFileInfoById(fileId);
fileName = imageFileManager.getImageFileName();
}
return PageListTemplateDTO.builder()
.id(po.getId())
.page(po.getPage())
.name(po.getName())
.sharedType(po.getSharedType())
.systemType(po.getSystemType())
.sharedTypeName(SharedTypeEnum.parseByValue(po.getSharedType()).getDefaultLabel())
.systemTypeName(po.getSystemType() == 1 ? "系统模板" : "自定义")
.limits(CollUtil.isEmpty(po.getLimitIds()) ? "所有" : Optional.ofNullable(po.getLimitIds()).orElse(new ArrayList<>()).stream().map(id -> idNameMap.getOrDefault(id, "")).collect(Collectors.joining(",")))
.limitIds(CollUtil.isEmpty(po.getLimitIds()) ? new ArrayList<>() : po.getLimitIds())
.setting(po.getSetting())
.canEdit(chief || (Objects.equals((long) user.getUID(), po.getCreator()) && po.getSystemType() == 0))
.fileId(fileId)
.fileName(fileName)
.build();
})
.collect(Collectors.toList());
return SalaryPageUtil.buildPage(param.getCurrent(), param.getPageSize(), templateDTOS, PageListTemplateDTO.class);

View File

@ -2,6 +2,7 @@ package com.engine.salary.service.impl;
import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.hrmelog.entity.dto.LoggerContext;
import com.engine.salary.common.LocalDateRange;
import com.engine.salary.common.YearMonthRange;
import com.engine.salary.config.SalaryElogConfig;
@ -29,6 +30,8 @@ import com.engine.salary.mapper.datacollection.AddUpSituationMapper;
import com.engine.salary.mapper.salaryacct.SalaryAcctRecordMapper;
import com.engine.salary.mapper.taxdeclaration.TaxDeclarationMapper;
import com.engine.salary.service.*;
import com.engine.salary.sys.service.SalarySysConfService;
import com.engine.salary.sys.service.impl.SalarySysConfServiceImpl;
import com.engine.salary.util.SalaryDateUtil;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
@ -47,6 +50,8 @@ import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;
import static com.engine.salary.sys.constant.SalarySysConstant.TAX_DECLARATION_DATE_TYPE;
@Slf4j
public class TaxDeclarationServiceImpl extends Service implements TaxDeclarationService {
@ -96,6 +101,13 @@ public class TaxDeclarationServiceImpl extends Service implements TaxDeclaration
return ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user);
}
private SalarySysConfService getSalarySysConfService(User user) {
return ServiceUtil.getService(SalarySysConfServiceImpl.class, user);
}
//是否根据税款所属期进行申报
private final boolean isTaxDeclarationByTaxCycle = "1".equals(getSalarySysConfService(user).getValueByCode(TAX_DECLARATION_DATE_TYPE));
private TaxDeclareRecordService getTaxDeclareRecordService(User user) {
return ServiceUtil.getService(TaxDeclareRecordServiceImpl.class, user);
@ -218,51 +230,96 @@ public class TaxDeclarationServiceImpl extends Service implements TaxDeclaration
// 查询个税扣缴义务人
List<TaxAgentPO> taxAgentPOS = getTaxAgentService(user).listByIds(taxAgentIds);
Map<Long, String> taxAgentNameMap = SalaryEntityUtil.convert2Map(taxAgentPOS, TaxAgentPO::getId, TaxAgentPO::getName);
List<SalaryAcctRecordPO> salaryAcctRecordPOS;
Date taxCycle;
List<SalaryAcctResultPO> salaryAcctResultPOS;
Set<Long> salaryAcctRecordIds;
//根据税款所属期申报
if (isTaxDeclarationByTaxCycle) {
taxCycle = saveParam.getTaxCycle();
if (Objects.isNull(taxCycle)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "税款所属期参数错误"));
}
// 薪资所属月的日期范围
LocalDateRange salaryMonthDateRange = SalaryDateUtil.localDate2Range(saveParam.getSalaryMonth());
if (Objects.isNull(salaryMonthDateRange)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "薪资所属月参数错误"));
}
// 查询税款所属期个税扣缴义务人已经生成过的个税申报表
List<TaxDeclarationPO> taxDeclarationPOS = listByTaxCycle(TaxDeclarationPO.builder().taxCycle(taxCycle).taxAgentIds(taxAgentNameMap.keySet()).build());
// 已经生成过个税申报表不允许再次生成个税申报表
if (CollectionUtils.isNotEmpty(taxDeclarationPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(107986, "{0}在{1}已经生成过个税申报表,不允许再次生成")
.replace("{0}", taxAgentNameMap.get(taxDeclarationPOS.get(0).getTaxAgentId()))
.replace("{1}", SalaryDateUtil.getFormatYearMonth(taxCycle)));
}
// 查询薪资所属月的薪资核算记录
salaryAcctRecordPOS = listByTaxCycle(SalaryAcctRecordPO.builder().taxCycle(taxCycle).build());
// 无薪资核算记录不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctRecordPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98874, "{0}无申报数据").replace("{0}", SalaryDateUtil.getFormatYearMonth(taxCycle)));
}
// 查询薪资核算结果
salaryAcctResultPOS = getSalaryAcctResultService(user)
.listBySalaryAcctRecordIdsAndTaxAgentIds(SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getId), taxAgentIds);
// 查询薪资所属月个税扣缴义务人已经生成过的个税申报表
List<TaxDeclarationPO> taxDeclarationPOS = listBySalaryMonthTax(TaxDeclarationPO.builder().salaryMonths(salaryMonthDateRange).taxAgentIds(taxAgentNameMap.keySet()).build());
// 已经生成过个税申报表不允许再次生成个税申报表
if (CollectionUtils.isNotEmpty(taxDeclarationPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(107986, "{0}在{1}已经生成过个税申报表,不允许再次生成")
.replace("{0}", taxAgentNameMap.get(taxDeclarationPOS.get(0).getTaxAgentId()))
.replace("{1}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 查询薪资所属月的薪资核算记录
List<SalaryAcctRecordPO> salaryAcctRecordPOS = listBySalaryMonth(SalaryAcctRecordPO.builder().salaryMonths(salaryMonthDateRange).build());
// 无薪资核算记录不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctRecordPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98874, "{0}无申报数据").replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 查询薪资核算结果
List<SalaryAcctResultPO> salaryAcctResultPOS = getSalaryAcctResultService(user)
.listBySalaryAcctRecordIdsAndTaxAgentIds(SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getId), taxAgentIds);
// 无薪资核算结果不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctResultPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(110093, "{0}无可申报数据")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 无薪资核算结果不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctResultPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(110093, "{0}无可申报数据")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
salaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryAcctRecordId);
salaryAcctRecordPOS = salaryAcctRecordPOS.stream().filter(salaryAcctRecordPO -> salaryAcctRecordIds.contains(salaryAcctRecordPO.getId())).collect(Collectors.toList());
// 如果存在未归档的也不允许生成个税申报表
boolean notArchived = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue()));
if (notArchived) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98875, "{0}有未归档数据,请全部归档后再申报")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
} else {
//根据薪资所属月申报
// 薪资所属月的日期范围
LocalDateRange salaryMonthDateRange = SalaryDateUtil.localDate2Range(SalaryDateUtil.localDateToDate(saveParam.getSalaryMonth().atDay(1)));
if (Objects.isNull(salaryMonthDateRange)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(84026, "薪资所属月参数错误"));
}
Set<Long> salaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryAcctRecordId);
salaryAcctRecordPOS = salaryAcctRecordPOS.stream().filter(salaryAcctRecordPO -> salaryAcctRecordIds.contains(salaryAcctRecordPO.getId())).collect(Collectors.toList());
// 如果存在未归档的也不允许生成个税申报表
boolean notArchived = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue()));
if (notArchived) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98875, "{0}有未归档数据,请全部归档后再申报")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 如果当前薪资所属月下存在不同的税款所属期属于异常业务场景不允许生成个税申报表
Date taxCycle = salaryAcctRecordPOS.get(0).getTaxCycle();
boolean differentTaxCycle = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> salaryAcctRecordPO.getTaxCycle().compareTo(taxCycle) != 0);
if (differentTaxCycle) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98876, "{0}存在不同的税款所属期,无法正常生成个税申报表,请调整账套设置,重新核算后再生成个税申报表")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
// 查询薪资所属月个税扣缴义务人已经生成过的个税申报表
List<TaxDeclarationPO> taxDeclarationPOS = listBySalaryMonthTax(TaxDeclarationPO.builder().salaryMonths(salaryMonthDateRange).taxAgentIds(taxAgentNameMap.keySet()).build());
// 已经生成过个税申报表不允许再次生成个税申报表
if (CollectionUtils.isNotEmpty(taxDeclarationPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(107986, "{0}在{1}已经生成过个税申报表,不允许再次生成")
.replace("{0}", taxAgentNameMap.get(taxDeclarationPOS.get(0).getTaxAgentId()))
.replace("{1}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 查询薪资所属月的薪资核算记录
salaryAcctRecordPOS = listBySalaryMonth(SalaryAcctRecordPO.builder().salaryMonths(salaryMonthDateRange).build());
// 无薪资核算记录不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctRecordPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98874, "{0}无申报数据").replace("{0}", saveParam.getSalaryMonth().toString()));
}
// 查询薪资核算结果
salaryAcctResultPOS = getSalaryAcctResultService(user)
.listBySalaryAcctRecordIdsAndTaxAgentIds(SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getId), taxAgentIds);
// 无薪资核算结果不允许生成个税申报表
if (CollectionUtils.isEmpty(salaryAcctResultPOS)) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(110093, "{0}无可申报数据")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
salaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctResultPOS, SalaryAcctResultPO::getSalaryAcctRecordId);
salaryAcctRecordPOS = salaryAcctRecordPOS.stream().filter(salaryAcctRecordPO -> salaryAcctRecordIds.contains(salaryAcctRecordPO.getId())).collect(Collectors.toList());
// 如果存在未归档的也不允许生成个税申报表
boolean notArchived = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue()));
if (notArchived) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98875, "{0}有未归档数据,请全部归档后再申报")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
// 如果当前薪资所属月下存在不同的税款所属期属于异常业务场景不允许生成个税申报表
taxCycle = salaryAcctRecordPOS.get(0).getTaxCycle();
boolean differentTaxCycle = salaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> salaryAcctRecordPO.getTaxCycle().compareTo(taxCycle) != 0);
if (differentTaxCycle) {
throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98876, "{0}存在不同的税款所属期,无法正常生成个税申报表,请调整账套设置,重新核算后再生成个税申报表")
.replace("{0}", SalaryDateUtil.getFormatYearMonth(saveParam.getSalaryMonth())));
}
}
// 查询薪资账套
Set<Long> salarySobIds = SalaryEntityUtil.properties(salaryAcctRecordPOS, SalaryAcctRecordPO::getSalarySobId);
@ -311,8 +368,8 @@ public class TaxDeclarationServiceImpl extends Service implements TaxDeclaration
loggerContext.setTargetId(declare.getId().toString());
loggerContext.setTargetName(targetName);
loggerContext.setOperateType(OperateTypeEnum.ADD.getValue());
loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel( 0, "生成个税申报表"));
loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel( 0, "生成个税申报表"));
loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "生成个税申报表"));
loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "生成个税申报表"));
loggerContext.setNewValues(declare);
SalaryElogConfig.taxDeclarationLoggerTemplate.write(loggerContext);
});
@ -353,6 +410,14 @@ public class TaxDeclarationServiceImpl extends Service implements TaxDeclaration
return getSalaryAcctRecordMapper().listSome(po);
}
public List<TaxDeclarationPO> listByTaxCycle(TaxDeclarationPO build) {
return getTaxDeclarationMapper().listSome(build);
}
public List<SalaryAcctRecordPO> listByTaxCycle(SalaryAcctRecordPO po) {
return getSalaryAcctRecordMapper().listSome(po);
}
@Override
public boolean checkByAuthority(TaxDeclarationPO taxDeclarationPO, Long employeeId) {
// 判断是否开启了分权
@ -395,15 +460,15 @@ public class TaxDeclarationServiceImpl extends Service implements TaxDeclaration
// 查询个税扣缴义务人名称
String bar = "_";
TaxAgentPO taxAgentPO = getTaxAgentService(user).getById(po.getTaxAgentId());
String targetName = SalaryDateUtil.getFormatYearMonth(po.getSalaryMonth()) + bar + taxAgentPO.getName() + bar + IncomeCategoryEnum.parseByValue(po.getIncomeCategory()).getDefaultLabel();
String targetName = SalaryDateUtil.getFormatYearMonth(po.getSalaryMonth()) + bar + taxAgentPO.getName() + bar + IncomeCategoryEnum.parseByValue(po.getIncomeCategory()).getDefaultLabel();
// 记录日志
LoggerContext<TaxDeclarationPO> loggerContext = new LoggerContext<>();
loggerContext.setUser(user);
loggerContext.setTargetId(taxDeclarationId.toString());
loggerContext.setTargetName(targetName);
loggerContext.setOperateType(OperateTypeEnum.DELETE.getValue());
loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel( 0, "撤回个税申报表"));
loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel( 0, "撤回个税申报表"));
loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "撤回个税申报表"));
loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "撤回个税申报表"));
SalaryElogConfig.taxDeclarationLoggerTemplate.write(loggerContext);
}

View File

@ -79,7 +79,7 @@ public class SalarySysConstant {
/**
* 个税申报撤回
*/
public static final String WITHDRAW_TAX_DECLARATION ="WITHDRAW_TAX_DECLARATION";
public static final String WITHDRAW_TAX_DECLARATION = "WITHDRAW_TAX_DECLARATION";
/**
* 删除薪资档案
@ -124,7 +124,7 @@ public class SalarySysConstant {
/**
* 首次查看后多少分钟不能查看工资单
*/
public static final String SALARY_BILL_BURNING_AFTER_READING_MIN= "SALARY_BILL_BURNING_AFTER_READING_MIN";
public static final String SALARY_BILL_BURNING_AFTER_READING_MIN = "SALARY_BILL_BURNING_AFTER_READING_MIN";
/**
* 核算固定列头数
@ -215,6 +215,15 @@ public class SalarySysConstant {
*/
public static final String SHOT_EMP_TIME_TYPE = "SHOT_EMP_TIME_TYPE";
//是否采集考勤班次数据,0关闭 1开启
/**
* 是否采集考勤班次数据,0关闭 1开启
*/
public static final String ATTENDANCE_SERIAL_COLLECTION_BTN = "ATTENDANCE_SERIAL_COLLECTION_BTN";
/**
* 报税日期类型薪资所属月/税款所属期
* 0薪资所属月 1税款所属期
* 默认薪资所属月
*/
public static final String TAX_DECLARATION_DATE_TYPE = "TAX_DECLARATION_DATE_TYPE";
}

View File

@ -0,0 +1,88 @@
package com.engine.salary.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class FileUtils {
private static final String COPY_FILE_SUFFIX = "_副本";
/**
* 将输入流写入文件
* @param in 输入流
* @param path 输出文件的路径
* @param fileName 输出文件的名称
*
* */
public static void writeFile(InputStream in , String path , String fileName) throws FileNotFoundException {
FileOutputStream outputStream = new FileOutputStream("");
}
/**
* 给定一个目录和文件若该目录存在名称相同的文件则返回一个可用的副本文件名否则直接返回输入的文件名
* @param path 文件的目录
* @param fileName 文件名
* @return 可用的文件名
* */
public static String getAvailableName(String path ,String fileName){
File filePath = new File(path);
String suffix = fileName.substring(fileName.lastIndexOf("."));
File[] files = filePath.listFiles( f -> f.getName().endsWith(suffix) );
if ( Objects.isNull(files) ) return fileName;
Set<String> fileNameSet = new HashSet<>();
for (File file: files ) {
fileNameSet.add(file.getName().substring(0 , file.getName().lastIndexOf(".")));
}
boolean isExist = true;
int count = 1;
String tempFileName = fileName.substring(0,fileName.lastIndexOf("."));
String tempFileName1 = tempFileName;
while(isExist){
if (fileNameSet.contains(tempFileName)){
tempFileName = tempFileName1 + COPY_FILE_SUFFIX + count++;
}else{
isExist = false;
}
}
return tempFileName + suffix;
}
/**
* 给定一个文件的完整路径若该文件已经存在则返回一个可用的副本文件名否则直接返回输入的文件名
* @param fullPath 文件的完整路径
* @return 可用的文件名
* */
public static String getAvailableName( String fullPath ){
File file = new File(fullPath);
if ( !file.isFile() ) throw new RuntimeException( String.format("not a file [%s].",fullPath));
return getAvailableName( file.getParent() ,file.getName() );
}
/**
* 给定一个文件的完整路径若该文件已经存在则返回一个完整可用的副本文件名否则直接返回输入的文件名
* @param fullPath 文件的完整路径
* @return 完整可用的文件名
* */
public static String getAvailableFullName( String fullPath ){
File file = new File(fullPath);
if ( !file.isFile() ) throw new RuntimeException( String.format("not a file [%s].",fullPath));
return file.getParent() + File.separator + getAvailableName(fullPath);
}
}

View File

@ -0,0 +1,204 @@
package com.engine.salary.util.excel;
import cn.hutool.core.util.StrUtil;
import com.engine.salary.util.FileUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
public class ExcelFillUtils {
// public static final String FILL_EXPRESSION_REGEX = "\\{\\.\\w+\\}";
private static final String FILL_EXPRESSION_REGEX = "\\{\\.[\\p{L}\\p{M}\\S]+\\}";
/**
* 给定模板指定某个页签将数据填充到模板中的指定页签并将数据导入到指定文件上
*
* @param template 模板文件地址
* @param sheetIndex 页签下标
* @param data 待填充的数据,数据格式如下
* [
* {"colName1":v1 ,"colName2":v2...},
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ]
* @return 新生成的副本文件
*/
public static XSSFWorkbook fillOneSheet(String template, Integer sheetIndex, List<Map<String, Object>> data) {
try {
XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(template));
fill(workbook, sheetIndex, data);//填充数据
return workbook;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 给定模板指定某个页签将数据填充到模板中的指定页签并将数据导入到指定文件上
*
* @param template 模板文件流
* @param sheetIndex 页签名称
* @param data 待填充的数据,数据格式如下
* [
* {"colName1":v1 ,"colName2":v2...},
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ]
* @return 新生成的副本文件
*/
public static XSSFWorkbook fillOneSheet(InputStream template, Integer sheetIndex, List<Map<String, Object>> data) {
try{
XSSFWorkbook workbook = new XSSFWorkbook(template);
fill(workbook, sheetIndex, data);//填充数据
return workbook;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 给定模板,将数据填充到模板中的多个页签并在模板所在目录生成新的副本文件
*
* @param template 模板文件地址
* @param datas 待填充的数据集,数据格式如下
* {
* "SheetName1":[
* {"colName1":v1 ,"colName2":v2...},
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ],
* "SheetName2":[
* {"colName1":v1 ,"colName2":v2...}
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ],
* ...
* }
*/
public static String fillMultipleSheet(String template, Map<Integer, List<Map<String, Object>>> datas) {
return fillMultipleSheet(template, FileUtils.getAvailableFullName(template), datas);
}
/**
* 给定模板,将数据填充到模板中的多个页签并将数据导入到指定文件上
*
* @param template 模板文件地址
* @param datas 待填充的数据集,数据格式如下
* {
* "SheetName1":[
* {"colName1":v1 ,"colName2":v2...},
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ],
* "SheetName2":[
* {"colName1":v1 ,"colName2":v2...}
* {"colName1":v1 ,"colName2":v2...}
* ,...
* ],
* ...
* }
*/
public static String fillMultipleSheet(String template, String outputFile, Map<Integer, List<Map<String, Object>>> datas) {
try (Workbook workbook = new XSSFWorkbook(new FileInputStream(template))) {
datas.forEach((sheetIndex, data) -> fill(workbook, sheetIndex, data));//填充数据
refreshFormula(workbook);//刷新公式
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
workbook.write(outputStream);
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return outputFile;
}
private static Workbook fill(Workbook workbook, int sheetIndex, List<Map<String, Object>> data) {
Sheet sheet = workbook.getSheetAt(sheetIndex);
if (sheet == null) {
throw new RuntimeException(String.format("sheet [%s] does not exist.", sheetIndex));
}
//找到所有的表达式单元格
Map<String, Cell> expressionCellMap = new HashMap<>();
for (int i = 0; i < sheet.getPhysicalNumberOfRows(); i++) {
Row row = sheet.getRow(i);
for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {
Cell cell = row.getCell(j);
if (!Objects.isNull(cell) && isFillExpression(cell.getStringCellValue())) {//判断该单元格是否是填充公式
expressionCellMap.put(getColNameFromEx(cell.getStringCellValue()), cell);
}
}
}
//填充数据
for (int i = 0; i < data.size(); i++) {
Map<String, Object> dataRow = data.get(i);
for (Map.Entry<String, Object> entry : dataRow.entrySet()) {
String colName = entry.getKey();
Object value = entry.getValue();
if (expressionCellMap.containsKey(colName)) {
Cell cell = expressionCellMap.get(colName);//公式所在的单元格
int rowID = cell.getRowIndex() + i;
int colId = cell.getColumnIndex();
Row fillRow = sheet.getRow(rowID);
fillRow = Objects.isNull(fillRow) ? sheet.createRow(rowID) : fillRow;
Cell fillCell = fillRow.getCell(colId);
//创建的新单元格需要复制公式单元格的格式
fillCell = Objects.isNull(fillCell) ? CellUtil.createCell(fillRow, colId, "", cell.getCellStyle()) : fillCell;
if (value instanceof String) {
fillCell.setCellValue(String.valueOf(value));
} else if (value instanceof Number) {
fillCell.setCellValue(((Number) value).doubleValue());
} else {
throw new RuntimeException(String.format("Unsupported data type [%s].", value.getClass().toString()));
}
}
}
}
return workbook;
}
private static void refreshFormula(Workbook workbook) {
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
}
private static boolean isFillExpression(String ex) {
if (StrUtil.isBlank(ex)) {
return false;
}
;
return Pattern.matches(FILL_EXPRESSION_REGEX, ex);
}
private static String getColNameFromEx(String ex) {
if (!isFillExpression(ex)) throw new RuntimeException("Illegal expression " + ex);
return ex.substring(2, ex.length() - 1);
}
}

View File

@ -16,6 +16,7 @@ import com.engine.salary.util.page.PageInfo;
import com.engine.salary.wrapper.SalaryCommonWrapper;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import weaver.hrm.HrmUserVarify;
import weaver.hrm.User;
@ -24,6 +25,11 @@ import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
@ -157,6 +163,35 @@ public class SalaryCommonController {
return new ResponseResult<PageListTemplateSaveParam, PageListTemplatePO>(user).run(getSalaryCommonWrapper(user)::savePageListTemplate, param);
}
@POST
@Path("/pageList/template/file/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadPageExportTemplateFile(@Context HttpServletRequest request, @Context HttpServletResponse response,@RequestBody DownloadTemplateParam param) {
try {
User user = HrmUserVarify.getUser(request, response);
XSSFWorkbook workbook = getSalaryCommonWrapper(user).downloadPageExportTemplateFile(param);
String fileName = "薪资明细模板示例" + LocalDate.now();
try {
fileName = URLEncoder.encode(fileName + ".xlsx", "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
StreamingOutput output = outputStream -> {
workbook.write(outputStream);
outputStream.flush();
};
response.setContentType("application/octet-stream");
return Response.ok(output).header("Content-disposition", "attachment;filename=" + fileName).header("Cache-Control", "no-cache").build();
} catch (Exception e) {
log.error("导出薪资明细模板异常", e);
throw e;
}
}
@POST
@Path("/pageList/template/delete")
@Produces(MediaType.APPLICATION_JSON)

View File

@ -17,6 +17,7 @@ import com.engine.salary.service.impl.SalarySobItemServiceImpl;
import com.engine.salary.service.impl.SettingServiceImpl;
import com.engine.salary.util.JsonUtil;
import com.engine.salary.util.page.PageInfo;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.engine.salary.util.page.SalaryPageUtil;
import org.apache.commons.lang3.StringUtils;
import weaver.hrm.User;
@ -97,6 +98,10 @@ public class SalaryCommonWrapper extends Service {
return getSettingService(user).savePageListTemplate(param);
}
public XSSFWorkbook downloadPageExportTemplateFile(DownloadTemplateParam param) {
return getSettingService(user).downloadPageExportTemplateFile(param);
}
public void deletePageListTemplate(PageListTemplateDeleteParam param) {
getSettingService(user).deletePageListTemplate(param);
}