package cn.iocoder.yudao.module.finance.controller.admin.voucher;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherEntryRespVO;
|
import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherPageReqVO;
|
import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherRespVO;
|
import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherSaveReqVO;
|
import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherDO;
|
import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherEntryDO;
|
import cn.iocoder.yudao.module.finance.service.voucher.FinVoucherService;
|
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
|
import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
import jakarta.annotation.Resource;
|
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.validation.Valid;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.validation.annotation.Validated;
|
import org.springframework.web.bind.annotation.*;
|
|
import java.io.IOException;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.stream.Stream;
|
|
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
|
|
@Tag(name = "管理后台 - 记账凭证")
|
@RestController
|
@RequestMapping("/finance/voucher")
|
@Validated
|
public class FinVoucherController {
|
|
/**
|
* 附件的业务记录类型
|
*/
|
private static final String ATTACHMENT_RECORD_TYPE = "fin_voucher";
|
|
@Resource
|
private FinVoucherService voucherService;
|
@Resource
|
private StorageAttachmentApi storageAttachmentApi;
|
@Resource
|
private AdminUserApi adminUserApi;
|
|
@PostMapping("/create")
|
@Operation(summary = "创建记账凭证")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:create')")
|
public CommonResult<Long> createVoucher(@Valid @RequestBody FinVoucherSaveReqVO createReqVO) {
|
return success(voucherService.createVoucher(createReqVO));
|
}
|
|
@PutMapping("/update")
|
@Operation(summary = "更新记账凭证", description = "仅未过账的凭证允许修改")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:update')")
|
public CommonResult<Boolean> updateVoucher(@Valid @RequestBody FinVoucherSaveReqVO updateReqVO) {
|
voucherService.updateVoucher(updateReqVO);
|
return success(true);
|
}
|
|
@DeleteMapping("/delete")
|
@Operation(summary = "删除记账凭证")
|
@Parameter(name = "id", description = "编号", required = true)
|
@PreAuthorize("@ss.hasPermission('finance:voucher:delete')")
|
public CommonResult<Boolean> deleteVoucher(@RequestParam("id") Long id) {
|
voucherService.deleteVoucher(id);
|
return success(true);
|
}
|
|
@DeleteMapping("/delete-list")
|
@Operation(summary = "批量删除记账凭证")
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
@PreAuthorize("@ss.hasPermission('finance:voucher:delete')")
|
public CommonResult<Boolean> deleteVoucherList(@RequestParam("ids") List<Long> ids) {
|
voucherService.deleteVoucherList(ids);
|
return success(true);
|
}
|
|
@PutMapping("/post")
|
@Operation(summary = "凭证过账", description = "未过账 -> 已过账,过账后凭证不可再修改")
|
@Parameter(name = "id", description = "编号", required = true)
|
@PreAuthorize("@ss.hasPermission('finance:voucher:update-status')")
|
public CommonResult<Boolean> postVoucher(@RequestParam("id") Long id) {
|
voucherService.postVoucher(id);
|
return success(true);
|
}
|
|
@PutMapping("/cancel")
|
@Operation(summary = "凭证作废", description = "未过账 -> 已作废")
|
@Parameter(name = "id", description = "编号", required = true)
|
@PreAuthorize("@ss.hasPermission('finance:voucher:update-status')")
|
public CommonResult<Boolean> cancelVoucher(@RequestParam("id") Long id) {
|
voucherService.cancelVoucher(id);
|
return success(true);
|
}
|
|
@GetMapping("/get")
|
@Operation(summary = "获得记账凭证", description = "包含凭证分录与附件列表")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:query')")
|
public CommonResult<FinVoucherRespVO> getVoucher(@RequestParam("id") Long id) {
|
FinVoucherDO voucher = voucherService.getVoucher(id);
|
if (voucher == null) {
|
return success(null);
|
}
|
FinVoucherRespVO respVO = BeanUtils.toBean(voucher, FinVoucherRespVO.class);
|
respVO.setEntries(BeanUtils.toBean(voucherService.getVoucherEntryListByVoucherId(id), FinVoucherEntryRespVO.class));
|
// 注意:StorageAttachmentApi.listAttachments 在「该记录没有任何附件」时返回 null,而不是空集合。
|
// 直接 .isEmpty() 会 NPE:新增的凭证还没传附件时,一查详情就 500,必须先兜底成空列表。
|
List<StorageBlobRespDTO> attachments = storageAttachmentApi.listAttachments(ATTACHMENT_RECORD_TYPE, id);
|
if (attachments == null) {
|
attachments = List.of();
|
}
|
respVO.setAttachmentList(attachments);
|
respVO.setHasAttachment(!attachments.isEmpty());
|
return success(respVO);
|
}
|
|
@GetMapping("/page")
|
@Operation(summary = "获得记账凭证分页")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:query')")
|
public CommonResult<PageResult<FinVoucherRespVO>> getVoucherPage(@Valid FinVoucherPageReqVO pageReqVO) {
|
PageResult<FinVoucherDO> pageResult = voucherService.getVoucherPage(pageReqVO);
|
PageResult<FinVoucherRespVO> result = BeanUtils.toBean(pageResult, FinVoucherRespVO.class);
|
fillCreatorName(result.getList());
|
return success(result);
|
}
|
|
@GetMapping("/page-with-entry")
|
@Operation(summary = "获得记账凭证分页(含分录)", description = "一次性返回本页所有凭证的分录,避免前端逐条查询")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:query')")
|
public CommonResult<PageResult<FinVoucherRespVO>> getVoucherPageWithEntry(@Valid FinVoucherPageReqVO pageReqVO) {
|
PageResult<FinVoucherDO> pageResult = voucherService.getVoucherPage(pageReqVO);
|
PageResult<FinVoucherRespVO> result = BeanUtils.toBean(pageResult, FinVoucherRespVO.class);
|
if (result.getList().isEmpty()) {
|
return success(result);
|
}
|
fillCreatorName(result.getList());
|
// 批量查询本页凭证的分录
|
Map<Long, List<FinVoucherEntryDO>> entryMap = convertMultiMap(
|
voucherService.getVoucherEntryListByVoucherIds(
|
convertList(result.getList(), FinVoucherRespVO::getId)),
|
FinVoucherEntryDO::getVoucherId);
|
result.getList().forEach(voucher -> {
|
voucher.setEntries(BeanUtils.toBean(
|
entryMap.getOrDefault(voucher.getId(), List.of()), FinVoucherEntryRespVO.class));
|
// 列表页也需要展示附件标记;附件表按 recordId 单条查询,本页最多 20 条,成本可接受
|
// 同上:没有附件时 listAttachments 返回 null,必须兜底,否则列表里只要有一条没附件的凭证就整页 500
|
List<StorageBlobRespDTO> attachments = storageAttachmentApi.listAttachments(ATTACHMENT_RECORD_TYPE, voucher.getId());
|
if (attachments == null) {
|
attachments = List.of();
|
}
|
voucher.setAttachmentList(attachments);
|
voucher.setHasAttachment(!attachments.isEmpty());
|
});
|
return success(result);
|
}
|
|
@GetMapping("/export-excel")
|
@Operation(summary = "导出记账凭证 Excel")
|
@PreAuthorize("@ss.hasPermission('finance:voucher:export')")
|
@ApiAccessLog(operateType = EXPORT)
|
public void exportVoucherExcel(@Valid FinVoucherPageReqVO pageReqVO,
|
HttpServletResponse response) throws IOException {
|
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
List<FinVoucherDO> list = voucherService.getVoucherPage(pageReqVO).getList();
|
List<FinVoucherRespVO> voList = BeanUtils.toBean(list, FinVoucherRespVO.class);
|
fillCreatorName(voList);
|
// 导出 Excel
|
ExcelUtils.write(response, "记账凭证.xls", "数据", FinVoucherRespVO.class, voList);
|
}
|
|
/**
|
* 填充制单人昵称。
|
* BaseDO.creator 存的是用户 ID,列表与导出表里直接展示就是一串数字,
|
* 所以按 ERP 收款单/付款单的既有口径,统一在这里把用户信息批量查一次再回填。
|
*/
|
private void fillCreatorName(List<FinVoucherRespVO> list) {
|
if (CollUtil.isEmpty(list)) {
|
return;
|
}
|
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
|
voucher -> Stream.of(NumberUtils.parseLong(voucher.getCreator()))));
|
list.forEach(voucher -> MapUtils.findAndThen(userMap, NumberUtils.parseLong(voucher.getCreator()),
|
user -> voucher.setCreatorName(user.getNickname())));
|
}
|
|
}
|