From a44497f92cd96e4517454108308bb7d4dc993a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=98=8A=E5=A4=A9?= <15984976+n2319_0@user.noreply.gitee.com> Date: Tue, 26 May 2026 12:04:16 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E8=B4=A2=E5=8A=A1=E7=8A=B6?= =?UTF-8?q?=E6=80=81=EF=BC=8C=E8=AE=A2=E5=8D=95=E5=BC=82=E8=AE=AE=E6=B1=87?= =?UTF-8?q?=E6=80=BB=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GearReturnExchangeController.java | 9 + .../java/com/gear/oa/domain/GearJournal.java | 10 + .../com/gear/oa/domain/GearStockIoOrder.java | 2 + .../com/gear/oa/domain/bo/GearJournalBo.java | 20 + .../oa/domain/bo/GearReturnExchangeBo.java | 11 + .../gear/oa/domain/bo/GearStockIoOrderBo.java | 2 + .../bo/GearStockIoOrderWithDetailBo.java | 5 +- .../com/gear/oa/domain/vo/GearJournalVo.java | 10 + .../gear/oa/domain/vo/GearStockIoOrderVo.java | 3 + .../oa/mapper/GearReturnExchangeMapper.java | 9 + .../service/IGearReturnExchangeService.java | 6 + .../service/impl/GearJournalServiceImpl.java | 3 + .../impl/GearReceivableServiceImpl.java | 7 + .../impl/GearReturnExchangeServiceImpl.java | 25 + .../impl/GearStockIoOrderServiceImpl.java | 24 +- .../mapper/oa/GearReturnExchangeMapper.xml | 34 + gear-ui3/src/api/oa/returnExchange.js | 9 + gear-ui3/src/router/index.js | 26 + gear-ui3/src/views/finance/summary/index.vue | 611 ++++++++++++++++++ gear-ui3/src/views/oms/customer/index.vue | 235 +++++-- .../oms/returnExchange/summary/index.vue | 321 +++++++++ gear-ui3/src/views/oms/salesman/index.vue | 218 +++++-- .../stockIoOrder/panels/stockIoOrderPage.vue | 29 +- 23 files changed, 1543 insertions(+), 86 deletions(-) create mode 100644 gear-ui3/src/views/finance/summary/index.vue create mode 100644 gear-ui3/src/views/oms/returnExchange/summary/index.vue diff --git a/gear-oa/src/main/java/com/gear/oa/controller/GearReturnExchangeController.java b/gear-oa/src/main/java/com/gear/oa/controller/GearReturnExchangeController.java index 7a14631..d480073 100644 --- a/gear-oa/src/main/java/com/gear/oa/controller/GearReturnExchangeController.java +++ b/gear-oa/src/main/java/com/gear/oa/controller/GearReturnExchangeController.java @@ -24,6 +24,7 @@ import com.gear.oa.domain.vo.GearReturnExchangeVo; import com.gear.oa.domain.bo.GearReturnExchangeBo; import com.gear.oa.service.IGearReturnExchangeService; import com.gear.common.core.page.TableDataInfo; +import java.util.Map; /** * 退换货管理 @@ -47,6 +48,14 @@ public class GearReturnExchangeController extends BaseController { return iGearReturnExchangeService.queryPageList(bo, pageQuery); } + /** + * 汇总(总数/总金额/按状态/按类型) + */ + @GetMapping("/summary") + public R> summary(GearReturnExchangeBo bo) { + return R.ok(iGearReturnExchangeService.summary(bo)); + } + /** * 导出退换货管理列表 */ diff --git a/gear-oa/src/main/java/com/gear/oa/domain/GearJournal.java b/gear-oa/src/main/java/com/gear/oa/domain/GearJournal.java index 0128ece..cf92dff 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/GearJournal.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/GearJournal.java @@ -46,6 +46,16 @@ public class GearJournal extends BaseEntity { * 对方户名 */ private String counterpart; + /** + * 客户ID(用于按客户过滤收款记录) + */ + @TableField("customer_id") + private Long customerId; + /** + * 销售员ID(用于按销售员过滤收款记录) + */ + @TableField("salesman_id") + private Long salesmanId; /** * 收入金额 */ diff --git a/gear-oa/src/main/java/com/gear/oa/domain/GearStockIoOrder.java b/gear-oa/src/main/java/com/gear/oa/domain/GearStockIoOrder.java index 665c790..aa20c63 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/GearStockIoOrder.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/GearStockIoOrder.java @@ -36,6 +36,8 @@ public class GearStockIoOrder extends BaseEntity { private String responsibleName; + private Date ioTime; + private Date planArrivalTime; private Date actualArrivalTime; diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearJournalBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearJournalBo.java index 4f76bd3..be9ec66 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearJournalBo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearJournalBo.java @@ -52,6 +52,16 @@ public class GearJournalBo extends BaseEntity { */ private String counterpart; + /** + * 客户ID(用于按客户过滤收款记录) + */ + private Long customerId; + + /** + * 销售员ID(用于按销售员过滤收款记录) + */ + private Long salesmanId; + /** * 收入金额 */ @@ -72,5 +82,15 @@ public class GearJournalBo extends BaseEntity { */ private String remark; + /** + * 时间范围筛选(按 journalDate) + */ + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date startTime; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date endTime; } diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearReturnExchangeBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearReturnExchangeBo.java index 4d0dbec..9af58fc 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearReturnExchangeBo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearReturnExchangeBo.java @@ -66,5 +66,16 @@ public class GearReturnExchangeBo extends BaseEntity { //订单id private Long orderId; + //时间范围筛选(按创建时间) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + /** + * 汇总维度:status,type(逗号分隔),为空则不返回分组汇总 + */ + private String summaryDims; } diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderBo.java index ce47891..c2bf2ff 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderBo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderBo.java @@ -35,6 +35,8 @@ public class GearStockIoOrderBo extends BaseEntity { private String responsibleName; + private Date ioTime; + private Date planArrivalTime; private Date actualArrivalTime; diff --git a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderWithDetailBo.java b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderWithDetailBo.java index 254c725..2e41e7e 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderWithDetailBo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/bo/GearStockIoOrderWithDetailBo.java @@ -32,8 +32,12 @@ public class GearStockIoOrderWithDetailBo { private String responsibleName; + private Date ioTime; + private Date planArrivalTime; + private Date actualArrivalTime; + private Date planFinishTime; private Long warehouseId; @@ -48,4 +52,3 @@ public class GearStockIoOrderWithDetailBo { private List details; } - diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/GearJournalVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearJournalVo.java index 491710f..e194bd5 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/vo/GearJournalVo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearJournalVo.java @@ -58,6 +58,16 @@ public class GearJournalVo { @ExcelProperty(value = "对方户名") private String counterpart; + /** + * 客户ID(用于按客户过滤收款记录) + */ + private Long customerId; + + /** + * 销售员ID(用于按销售员过滤收款记录) + */ + private Long salesmanId; + /** * 收入金额 */ diff --git a/gear-oa/src/main/java/com/gear/oa/domain/vo/GearStockIoOrderVo.java b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearStockIoOrderVo.java index 163829c..09b1203 100644 --- a/gear-oa/src/main/java/com/gear/oa/domain/vo/GearStockIoOrderVo.java +++ b/gear-oa/src/main/java/com/gear/oa/domain/vo/GearStockIoOrderVo.java @@ -36,6 +36,9 @@ public class GearStockIoOrderVo extends BaseEntity { private String responsibleName; + @ExcelProperty(value = "出入库时间") + private Date ioTime; + private Date planArrivalTime; private Date actualArrivalTime; diff --git a/gear-oa/src/main/java/com/gear/oa/mapper/GearReturnExchangeMapper.java b/gear-oa/src/main/java/com/gear/oa/mapper/GearReturnExchangeMapper.java index 698ae93..026c69f 100644 --- a/gear-oa/src/main/java/com/gear/oa/mapper/GearReturnExchangeMapper.java +++ b/gear-oa/src/main/java/com/gear/oa/mapper/GearReturnExchangeMapper.java @@ -7,6 +7,9 @@ import com.gear.oa.domain.vo.GearReturnExchangeVo; import com.gear.common.core.mapper.BaseMapperPlus; import org.apache.ibatis.annotations.Param; +import java.util.List; +import java.util.Map; + /** * 退换货管理Mapper接口 * @@ -16,4 +19,10 @@ import org.apache.ibatis.annotations.Param; public interface GearReturnExchangeMapper extends BaseMapperPlus { Page selectVoPagePlus(Page build,@Param("ew") QueryWrapper lqw); + + Map selectSummary(@Param("ew") QueryWrapper lqw); + + List> selectStatusStats(@Param("ew") QueryWrapper lqw); + + List> selectTypeStats(@Param("ew") QueryWrapper lqw); } diff --git a/gear-oa/src/main/java/com/gear/oa/service/IGearReturnExchangeService.java b/gear-oa/src/main/java/com/gear/oa/service/IGearReturnExchangeService.java index 6e36b14..da0c0f7 100644 --- a/gear-oa/src/main/java/com/gear/oa/service/IGearReturnExchangeService.java +++ b/gear-oa/src/main/java/com/gear/oa/service/IGearReturnExchangeService.java @@ -8,6 +8,7 @@ import com.gear.common.core.domain.PageQuery; import java.util.Collection; import java.util.List; +import java.util.Map; /** * 退换货管理Service接口 @@ -46,4 +47,9 @@ public interface IGearReturnExchangeService { * 校验并批量删除退换货管理信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 汇总(总数/总金额/按状态/按类型) + */ + Map summary(GearReturnExchangeBo bo); } diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/GearJournalServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/GearJournalServiceImpl.java index a7875b5..5b0be1c 100644 --- a/gear-oa/src/main/java/com/gear/oa/service/impl/GearJournalServiceImpl.java +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/GearJournalServiceImpl.java @@ -67,9 +67,12 @@ public class GearJournalServiceImpl implements IGearJournalService { lqw.eq(StringUtils.isNotBlank(bo.getSummary()), GearJournal::getSummary, bo.getSummary()); lqw.eq(StringUtils.isNotBlank(bo.getTransType()), GearJournal::getTransType, bo.getTransType()); lqw.eq(StringUtils.isNotBlank(bo.getCounterpart()), GearJournal::getCounterpart, bo.getCounterpart()); + lqw.eq(bo.getCustomerId() != null, GearJournal::getCustomerId, bo.getCustomerId()); + lqw.eq(bo.getSalesmanId() != null, GearJournal::getSalesmanId, bo.getSalesmanId()); lqw.eq(bo.getIncomeAmount() != null, GearJournal::getIncomeAmount, bo.getIncomeAmount()); lqw.eq(bo.getExpenseAmount() != null, GearJournal::getExpenseAmount, bo.getExpenseAmount()); lqw.eq(bo.getBalanceAmount() != null, GearJournal::getBalanceAmount, bo.getBalanceAmount()); + lqw.between(bo.getStartTime() != null && bo.getEndTime() != null, GearJournal::getJournalDate, bo.getStartTime(), bo.getEndTime()); //根据这个创建时间进行一个倒叙 lqw.orderByDesc(GearJournal::getCreateTime); return lqw; diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/GearReceivableServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/GearReceivableServiceImpl.java index ef71f3b..bae7545 100644 --- a/gear-oa/src/main/java/com/gear/oa/service/impl/GearReceivableServiceImpl.java +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/GearReceivableServiceImpl.java @@ -198,6 +198,13 @@ public class GearReceivableServiceImpl implements IGearReceivableService { journal.setSummary("客户付款"); journal.setTransType("收入"); journal.setCounterpart(customerMapper.selectById(receivable.getCustomerId()).getName()); + journal.setCustomerId(receivable.getCustomerId()); + if (receivable.getOrderId() != null) { + GearOrder order = orderMapper.selectById(receivable.getOrderId()); + if (order != null) { + journal.setSalesmanId(order.getSalesmanId()); + } + } journal.setIncomeAmount(changePaidAmount); journal.setExpenseAmount(BigDecimal.ZERO); gearJournalService.computeBalance(journal); diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/GearReturnExchangeServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/GearReturnExchangeServiceImpl.java index 137cbd7..5fe2350 100644 --- a/gear-oa/src/main/java/com/gear/oa/service/impl/GearReturnExchangeServiceImpl.java +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/GearReturnExchangeServiceImpl.java @@ -75,6 +75,10 @@ public class GearReturnExchangeServiceImpl implements IGearReturnExchangeService queryWrapper.eq(StringUtils.isNotBlank(bo.getReason()), "gre.reason", bo.getReason()); queryWrapper.eq(StringUtils.isNotBlank(bo.getStatus()), "gre.status", bo.getStatus()); queryWrapper.eq(bo.getAmount() != null, "gre.amount", bo.getAmount()); + queryWrapper.and(bo.getSalesmanId() != null, + w -> w.eq("o.salesman_id", bo.getSalesmanId()).or().eq("gre.salesman_id", bo.getSalesmanId())); + queryWrapper.between(bo.getStartTime() != null && bo.getEndTime() != null, + "gre.create_time", bo.getStartTime(), bo.getEndTime()); //逻辑删除 queryWrapper.eq("gre.del_flag", 0); @@ -101,9 +105,30 @@ public class GearReturnExchangeServiceImpl implements IGearReturnExchangeService lqw.eq(StringUtils.isNotBlank(bo.getReason()), GearReturnExchange::getReason, bo.getReason()); lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GearReturnExchange::getStatus, bo.getStatus()); lqw.eq(bo.getAmount() != null, GearReturnExchange::getAmount, bo.getAmount()); + lqw.eq(bo.getSalesmanId() != null, GearReturnExchange::getSalesmanId, bo.getSalesmanId()); + lqw.between(bo.getStartTime() != null && bo.getEndTime() != null, GearReturnExchange::getCreateTime, bo.getStartTime(), bo.getEndTime()); return lqw; } + @Override + public Map summary(GearReturnExchangeBo bo) { + QueryWrapper lqw = buildQueryWrapperPlus(bo); + Map summary = baseMapper.selectSummary(lqw); + String dims = bo.getSummaryDims() == null ? "" : bo.getSummaryDims().trim(); + boolean needStatus = dims.contains("status"); + boolean needType = dims.contains("type"); + List> statusStats = needStatus ? baseMapper.selectStatusStats(lqw) : java.util.Collections.emptyList(); + List> typeStats = needType ? baseMapper.selectTypeStats(lqw) : java.util.Collections.emptyList(); + if (summary == null) { + summary = new java.util.HashMap<>(); + summary.put("totalCount", 0); + summary.put("totalAmount", java.math.BigDecimal.ZERO); + } + summary.put("statusStats", statusStats); + summary.put("typeStats", typeStats); + return summary; + } + /** * 新增退换货管理 */ diff --git a/gear-oa/src/main/java/com/gear/oa/service/impl/GearStockIoOrderServiceImpl.java b/gear-oa/src/main/java/com/gear/oa/service/impl/GearStockIoOrderServiceImpl.java index 5e3076f..209c670 100644 --- a/gear-oa/src/main/java/com/gear/oa/service/impl/GearStockIoOrderServiceImpl.java +++ b/gear-oa/src/main/java/com/gear/oa/service/impl/GearStockIoOrderServiceImpl.java @@ -143,6 +143,9 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService { } GearStockIoOrder order = BeanUtil.toBean(bo, GearStockIoOrder.class); + Date ioTime = bo.getIoTime() != null ? bo.getIoTime() : new Date(); + ensureIoTimeNotAfterToday(ioTime); + order.setIoTime(ioTime); if (StringUtils.isBlank(order.getOrderCode())) { order.setOrderCode("SIOO_" + IdUtil.getSnowflakeNextIdStr()); } @@ -197,6 +200,9 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService { throw new ServiceException("单据明细不能为空"); } + if (bo.getIoTime() != null) { + ensureIoTimeNotAfterToday(bo.getIoTime()); + } GearStockIoOrder update = BeanUtil.toBean(bo, GearStockIoOrder.class); update.setStatus(null); update.setExecFlag(null); @@ -268,6 +274,22 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService { } } + private void ensureIoTimeNotAfterToday(Date ioTime) { + if (ioTime == null) { + return; + } + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + cal.set(Calendar.HOUR_OF_DAY, 23); + cal.set(Calendar.MINUTE, 59); + cal.set(Calendar.SECOND, 59); + cal.set(Calendar.MILLISECOND, 999); + Date endOfToday = cal.getTime(); + if (ioTime.after(endOfToday)) { + throw new ServiceException("出入库时间不能晚于当天"); + } + } + @Override @Transactional(rollbackFor = Exception.class) public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { @@ -326,7 +348,7 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService { update.setConfirmInFlag("1"); update.setConfirmInBy(LoginHelper.getNickName()); update.setConfirmInTime(new Date()); - update.setActualArrivalTime(new Date()); + update.setActualArrivalTime(order.getActualArrivalTime() != null ? order.getActualArrivalTime() : new Date()); update.setExecFlag("1"); baseMapper.updateById(update); } diff --git a/gear-oa/src/main/resources/mapper/oa/GearReturnExchangeMapper.xml b/gear-oa/src/main/resources/mapper/oa/GearReturnExchangeMapper.xml index e8f7b80..103cbcb 100644 --- a/gear-oa/src/main/resources/mapper/oa/GearReturnExchangeMapper.xml +++ b/gear-oa/src/main/resources/mapper/oa/GearReturnExchangeMapper.xml @@ -57,6 +57,40 @@ ${ew.customSqlSegment} + + + + + + diff --git a/gear-ui3/src/api/oa/returnExchange.js b/gear-ui3/src/api/oa/returnExchange.js index 0b86617..a8c17a6 100644 --- a/gear-ui3/src/api/oa/returnExchange.js +++ b/gear-ui3/src/api/oa/returnExchange.js @@ -42,3 +42,12 @@ export function delReturnExchange(returnExchangeId) { method: 'delete' }) } + +// 订单异议汇总 +export function getReturnExchangeSummary(query) { + return request({ + url: '/oa/returnExchange/summary', + method: 'get', + params: query + }) +} diff --git a/gear-ui3/src/router/index.js b/gear-ui3/src/router/index.js index 38d6233..dc79199 100644 --- a/gear-ui3/src/router/index.js +++ b/gear-ui3/src/router/index.js @@ -70,6 +70,32 @@ export const constantRoutes = [ } ] }, + { + path: '/finance', + component: Layout, + hidden: true, + children: [ + { + path: 'summary', + component: () => import('@/views/finance/summary/index.vue'), + name: 'FinanceSummary', + meta: { title: '财务汇总', icon: 'money', noCache: true } + } + ] + }, + { + path: '/oms', + component: Layout, + hidden: true, + children: [ + { + path: 'returnExchangeSummary', + component: () => import('@/views/oms/returnExchange/summary/index.vue'), + name: 'ReturnExchangeSummary', + meta: { title: '订单异议汇总', icon: 'list', noCache: true } + } + ] + }, { path: '/user', component: Layout, hidden: true, redirect: 'noredirect', children: [ { path: 'profile/:activeTab?', component: () => import('@/views/system/user/profile/index'), name: 'Profile', meta: { title: '个人中心', icon: 'user' } } ] }, { path: '/mat/product', component: Layout, hidden: true, children: [ { path: 'detail/:id(\\d+)', component: () => import('@/views/mat/product/detail'), name: 'ProductDetail', meta: { title: '产品详情', activeMenu: '/mat/product' } } ] } ] diff --git a/gear-ui3/src/views/finance/summary/index.vue b/gear-ui3/src/views/finance/summary/index.vue new file mode 100644 index 0000000..b04c3bf --- /dev/null +++ b/gear-ui3/src/views/finance/summary/index.vue @@ -0,0 +1,611 @@ + + + + + diff --git a/gear-ui3/src/views/oms/customer/index.vue b/gear-ui3/src/views/oms/customer/index.vue index bda0681..55b0091 100644 --- a/gear-ui3/src/views/oms/customer/index.vue +++ b/gear-ui3/src/views/oms/customer/index.vue @@ -194,7 +194,16 @@
财务状态
- 刷新 +
+ 新增收款 + 刷新 + + + + + + 导出 +
@@ -214,20 +223,40 @@
收款明细
- - - - + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + +
@@ -285,7 +314,9 @@ import { listOrder } from "@/api/oms/order"; import { listShippingOrder } from "@/api/oms/shippingOrder"; import { listOrderDetail } from "@/api/oms/orderDetail"; import { listReceivable } from "@/api/finance/receivable"; +import request from "@/utils/request"; import ReturnExchange from "@/views/oms/order/panels/return.vue"; +import * as XLSX from "xlsx"; export default { name: "Customer", @@ -341,12 +372,19 @@ export default { // 财务状态(按客户维度汇总应收/已收/未收,并展示收款明细) financeLoading: false, financeList: [], + financeReceiveAddOpen: false, + financeReceiveAddLoading: false, + financeReceiveAddForm: { + journalDate: "", + incomeAmount: 0, + remark: "" + }, + financeExportGroupBy: "", financeSummary: { receivableAmount: 0, receivedAmount: 0, unreceivedAmount: 0 }, - financeOrderCodeMap: {}, // 订单异议:按客户维度展示订单列表,点“查看”进入订单异议管理(ReturnExchange) disputeLoading: false, disputeOrderList: [], @@ -392,7 +430,10 @@ export default { this.shippingList = []; this.financeList = []; this.financeSummary = { receivableAmount: 0, receivedAmount: 0, unreceivedAmount: 0 }; - this.financeOrderCodeMap = {}; + this.financeReceiveAddOpen = false; + this.financeReceiveAddLoading = false; + this.financeReceiveAddForm = { journalDate: "", incomeAmount: 0, remark: "" }; + this.financeExportGroupBy = ""; this.disputeOrderList = []; this.disputeOpen = false; this.disputeOrderId = null; @@ -501,42 +542,90 @@ export default { return Number.isFinite(n) ? n.toFixed(2) : "0.00"; }, + openReceiveAdd() { + if (!this.selectedCustomerId) return; + this.financeReceiveAddForm = { + journalDate: this.parseNow(), + incomeAmount: 0, + remark: "" + }; + this.financeReceiveAddOpen = true; + }, + + parseNow() { + const d = new Date(); + if (typeof this.parseTime === "function") { + return this.parseTime(d, "{y}-{m}-{d} {h}:{i}:{s}"); + } + return ""; + }, + + submitReceiveAdd() { + if (!this.selectedCustomerId) return; + const amount = Number(this.financeReceiveAddForm && this.financeReceiveAddForm.incomeAmount != null ? this.financeReceiveAddForm.incomeAmount : 0); + if (!Number.isFinite(amount) || amount <= 0) { + this.$modal.msgError("请输入收款金额"); + return; + } + this.financeReceiveAddLoading = true; + const payload = { + journalDate: this.financeReceiveAddForm.journalDate || undefined, + summary: "客户收款", + transType: "收入", + customerId: this.selectedCustomerId, + counterpart: (this.selectedCustomer && this.selectedCustomer.name) ? String(this.selectedCustomer.name) : "", + incomeAmount: amount, + expenseAmount: 0, + remark: this.financeReceiveAddForm.remark != null ? String(this.financeReceiveAddForm.remark) : "" + }; + request({ + url: "/oa/journal", + method: "post", + data: payload + }).then(() => { + this.$modal.msgSuccess("已新增收款"); + this.financeReceiveAddOpen = false; + this.loadFinance(); + }).finally(() => { + this.financeReceiveAddLoading = false; + }); + }, + loadFinance() { if (!this.selectedCustomerId) return; this.financeLoading = true; + const customerName = (this.selectedCustomer && this.selectedCustomer.name) ? String(this.selectedCustomer.name) : ""; Promise.all([ listReceivable({ customerId: this.selectedCustomerId, pageNum: 1, pageSize: 9999 }), - listOrder({ customerId: this.selectedCustomerId, pageNum: 1, pageSize: 9999 }) + request({ + url: "/oa/journal/list", + method: "get", + params: { customerId: this.selectedCustomerId, transType: "收入", pageNum: 1, pageSize: 9999 } + }), + customerName + ? request({ + url: "/oa/journal/list", + method: "get", + params: { counterpart: customerName, transType: "收入", pageNum: 1, pageSize: 9999 } + }) + : Promise.resolve({ rows: [] }) ]) - .then(([recRes, orderRes]) => { + .then(([recRes, j1, j2]) => { const receivables = (recRes && recRes.rows) ? recRes.rows : []; - const orders = (orderRes && orderRes.rows) ? orderRes.rows : []; + const receivableAmount = receivables.reduce((sum, r) => sum + (Number(r && r.amount != null ? r.amount : 0) || 0), 0); - const codeMap = {}; - orders.forEach(o => { - if (!o || o.orderId == null) return; - codeMap[o.orderId] = o.orderCode || String(o.orderId); + const jRows1 = (j1 && j1.rows) ? j1.rows : []; + const jRows2 = (j2 && j2.rows) ? j2.rows : []; + const journalMap = new Map(); + [...jRows1, ...jRows2].forEach(r => { + if (!r || r.journalId == null) return; + journalMap.set(r.journalId, r); }); - this.financeOrderCodeMap = codeMap; + const journals = Array.from(journalMap.values()); + this.financeList = journals; - const rows = receivables.map(r => { - const orderId = r && r.orderId != null ? r.orderId : null; - return { - ...r, - orderCode: orderId != null ? (codeMap[orderId] || String(orderId)) : "-" - }; - }); - this.financeList = rows; - - const receivableAmount = rows.reduce((sum, r) => sum + (Number(r && r.amount != null ? r.amount : 0) || 0), 0); - const receivedAmount = rows.reduce((sum, r) => sum + (Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0), 0); - const unreceivedAmount = rows.reduce((sum, r) => { - const bal = r && r.balanceAmount != null ? Number(r.balanceAmount) : NaN; - if (Number.isFinite(bal)) return sum + bal; - const a = Number(r && r.amount != null ? r.amount : 0) || 0; - const p = Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0; - return sum + Math.max(0, a - p); - }, 0); + const receivedAmount = journals.reduce((sum, r) => sum + (Number(r && r.incomeAmount != null ? r.incomeAmount : 0) || 0), 0); + const unreceivedAmount = Math.max(0, receivableAmount - receivedAmount); this.financeSummary = { receivableAmount, receivedAmount, unreceivedAmount }; }) @@ -545,6 +634,70 @@ export default { }); }, + exportFinance() { + const list = this.financeList || []; + const groupBy = this.financeExportGroupBy ? String(this.financeExportGroupBy) : ""; + const rowMap = (r) => ({ + 收款时间: r && r.journalDate != null ? r.journalDate : "", + 已收金额: r && r.incomeAmount != null ? r.incomeAmount : 0, + 摘要: r && r.summary != null ? r.summary : "", + 备注: r && r.remark != null ? r.remark : "" + }); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(list.map(rowMap)), "全部"); + if (groupBy) { + const groups = new Map(); + list.forEach((r) => { + const key = this.financeExportKey(r, groupBy); + if (!groups.has(key)) groups.set(key, []); + groups.get(key).push(r); + }); + const used = new Set(["全部"]); + Array.from(groups.entries()).forEach(([k, items]) => { + const sheetName = this.financeUniqueSheetName(k, used); + XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(items.map(rowMap)), sheetName); + }); + } + const cid = this.selectedCustomerId != null ? String(this.selectedCustomerId) : "customer"; + XLSX.writeFile(wb, `customer_finance_${cid}_${new Date().getTime()}.xlsx`); + }, + financeExportKey(r, groupBy) { + const raw = r && r[groupBy] != null ? r[groupBy] : ""; + const s = raw == null ? "" : String(raw); + const toDay = (val) => (val && String(val).length >= 10 ? String(val).slice(0, 10) : String(val || "")); + const toMonth = (val) => (val && String(val).length >= 7 ? String(val).slice(0, 7) : String(val || "")); + let key = ""; + if (groupBy === "journalDateDay") key = toDay(r && r.journalDate); + else if (groupBy === "journalDateMonth") key = toMonth(r && r.journalDate); + else key = s.trim(); + return key ? key : "未填写"; + }, + financeSanitizeSheetName(name) { + const n = String(name || "").replace(/[\[\]\*\/\\\?\:]/g, "_").replace(/\s+/g, " ").trim(); + const cut = n.length > 31 ? n.slice(0, 31) : n; + return cut || "未命名"; + }, + financeUniqueSheetName(base, used) { + const set = used || new Set(); + let name = this.financeSanitizeSheetName(base); + if (!set.has(name)) { + set.add(name); + return name; + } + for (let i = 2; i < 999; i++) { + const suffix = `_${i}`; + const max = 31 - suffix.length; + const next = this.financeSanitizeSheetName(name.slice(0, Math.max(1, max)) + suffix); + if (!set.has(next)) { + set.add(next); + return next; + } + } + const fallback = this.financeSanitizeSheetName(`${name}_${Date.now()}`); + set.add(fallback); + return fallback; + }, + loadDisputeOrders() { if (!this.selectedCustomerId) return; this.disputeLoading = true; diff --git a/gear-ui3/src/views/oms/returnExchange/summary/index.vue b/gear-ui3/src/views/oms/returnExchange/summary/index.vue new file mode 100644 index 0000000..8f6cc5a --- /dev/null +++ b/gear-ui3/src/views/oms/returnExchange/summary/index.vue @@ -0,0 +1,321 @@ + + + diff --git a/gear-ui3/src/views/oms/salesman/index.vue b/gear-ui3/src/views/oms/salesman/index.vue index 3ac90ed..4660c46 100644 --- a/gear-ui3/src/views/oms/salesman/index.vue +++ b/gear-ui3/src/views/oms/salesman/index.vue @@ -200,7 +200,16 @@
财务状态
- 刷新 +
+ 新增收款 + 刷新 + + + + + + 导出 +
@@ -220,21 +229,40 @@
收款明细
- - - - - + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + +
@@ -378,7 +406,9 @@ import { listOrder } from "@/api/oms/order"; import { listShippingOrder } from "@/api/oms/shippingOrder"; import { listOrderDetail } from "@/api/oms/orderDetail"; import { listReceivable } from "@/api/finance/receivable"; +import request from "@/utils/request"; import ReturnExchange from "@/views/oms/order/panels/return.vue"; +import * as XLSX from "xlsx"; export default { name: "Salesman", @@ -430,12 +460,19 @@ export default { financeLoading: false, financeList: [], + financeReceiveAddOpen: false, + financeReceiveAddLoading: false, + financeReceiveAddForm: { + journalDate: "", + incomeAmount: 0, + remark: "" + }, + financeExportGroupBy: "", financeSummary: { receivableAmount: 0, receivedAmount: 0, unreceivedAmount: 0 }, - financeOrderCodeMap: {}, disputeLoading: false, disputeOrderList: [], @@ -702,42 +739,75 @@ export default { return Number.isFinite(n) ? n.toFixed(2) : "0.00"; }, + openReceiveAdd() { + if (!this.selectedSalesmanId) return; + this.financeReceiveAddForm = { + journalDate: this.parseNow(), + incomeAmount: 0, + remark: "" + }; + this.financeReceiveAddOpen = true; + }, + + parseNow() { + const d = new Date(); + if (typeof this.parseTime === "function") { + return this.parseTime(d, "{y}-{m}-{d} {h}:{i}:{s}"); + } + return ""; + }, + + submitReceiveAdd() { + if (!this.selectedSalesmanId) return; + const amount = Number(this.financeReceiveAddForm && this.financeReceiveAddForm.incomeAmount != null ? this.financeReceiveAddForm.incomeAmount : 0); + if (!Number.isFinite(amount) || amount <= 0) { + this.$modal.msgError("请输入收款金额"); + return; + } + this.financeReceiveAddLoading = true; + const payload = { + journalDate: this.financeReceiveAddForm.journalDate || undefined, + summary: "客户收款", + transType: "收入", + salesmanId: this.selectedSalesmanId, + counterpart: this.selectedSalesmanName || "", + incomeAmount: amount, + expenseAmount: 0, + remark: this.financeReceiveAddForm.remark != null ? String(this.financeReceiveAddForm.remark) : "" + }; + request({ + url: "/oa/journal", + method: "post", + data: payload + }).then(() => { + this.$modal.msgSuccess("已新增收款"); + this.financeReceiveAddOpen = false; + this.loadFinance(); + }).finally(() => { + this.financeReceiveAddLoading = false; + }); + }, + loadFinance() { if (!this.selectedSalesmanId) return; this.financeLoading = true; Promise.all([ listReceivable({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 }), - listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 }) + request({ + url: "/oa/journal/list", + method: "get", + params: { salesmanId: this.selectedSalesmanId, transType: "收入", pageNum: 1, pageSize: 9999 } + }) ]) - .then(([recRes, orderRes]) => { + .then(([recRes, journalRes]) => { const receivables = (recRes && recRes.rows) ? recRes.rows : []; - const orders = (orderRes && orderRes.rows) ? orderRes.rows : []; + const receivableAmount = receivables.reduce((sum, r) => sum + (Number(r && r.amount != null ? r.amount : 0) || 0), 0); - const codeMap = {}; - orders.forEach(o => { - if (!o || o.orderId == null) return; - codeMap[o.orderId] = o.orderCode || String(o.orderId); - }); - this.financeOrderCodeMap = codeMap; + const journals = (journalRes && journalRes.rows) ? journalRes.rows : []; + this.financeList = journals; - const rows = receivables.map(r => { - const orderId = r && r.orderId != null ? r.orderId : null; - return { - ...r, - orderCode: orderId != null ? (codeMap[orderId] || String(orderId)) : "-" - }; - }); - this.financeList = rows; - - const receivableAmount = rows.reduce((sum, r) => sum + (Number(r && r.amount != null ? r.amount : 0) || 0), 0); - const receivedAmount = rows.reduce((sum, r) => sum + (Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0), 0); - const unreceivedAmount = rows.reduce((sum, r) => { - const bal = r && r.balanceAmount != null ? Number(r.balanceAmount) : NaN; - if (Number.isFinite(bal)) return sum + bal; - const a = Number(r && r.amount != null ? r.amount : 0) || 0; - const p = Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0; - return sum + Math.max(0, a - p); - }, 0); + const receivedAmount = journals.reduce((sum, r) => sum + (Number(r && r.incomeAmount != null ? r.incomeAmount : 0) || 0), 0); + const unreceivedAmount = Math.max(0, receivableAmount - receivedAmount); this.financeSummary = { receivableAmount, receivedAmount, unreceivedAmount }; }) @@ -746,6 +816,70 @@ export default { }); }, + exportFinance() { + const list = this.financeList || []; + const groupBy = this.financeExportGroupBy ? String(this.financeExportGroupBy) : ""; + const rowMap = (r) => ({ + 收款时间: r && r.journalDate != null ? r.journalDate : "", + 已收金额: r && r.incomeAmount != null ? r.incomeAmount : 0, + 摘要: r && r.summary != null ? r.summary : "", + 备注: r && r.remark != null ? r.remark : "" + }); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(list.map(rowMap)), "全部"); + if (groupBy) { + const groups = new Map(); + list.forEach((r) => { + const key = this.financeExportKey(r, groupBy); + if (!groups.has(key)) groups.set(key, []); + groups.get(key).push(r); + }); + const used = new Set(["全部"]); + Array.from(groups.entries()).forEach(([k, items]) => { + const sheetName = this.financeUniqueSheetName(k, used); + XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(items.map(rowMap)), sheetName); + }); + } + const sid = this.selectedSalesmanId != null ? String(this.selectedSalesmanId) : "salesman"; + XLSX.writeFile(wb, `salesman_finance_${sid}_${new Date().getTime()}.xlsx`); + }, + financeExportKey(r, groupBy) { + const raw = r && r[groupBy] != null ? r[groupBy] : ""; + const s = raw == null ? "" : String(raw); + const toDay = (val) => (val && String(val).length >= 10 ? String(val).slice(0, 10) : String(val || "")); + const toMonth = (val) => (val && String(val).length >= 7 ? String(val).slice(0, 7) : String(val || "")); + let key = ""; + if (groupBy === "journalDateDay") key = toDay(r && r.journalDate); + else if (groupBy === "journalDateMonth") key = toMonth(r && r.journalDate); + else key = s.trim(); + return key ? key : "未填写"; + }, + financeSanitizeSheetName(name) { + const n = String(name || "").replace(/[\[\]\*\/\\\?\:]/g, "_").replace(/\s+/g, " ").trim(); + const cut = n.length > 31 ? n.slice(0, 31) : n; + return cut || "未命名"; + }, + financeUniqueSheetName(base, used) { + const set = used || new Set(); + let name = this.financeSanitizeSheetName(base); + if (!set.has(name)) { + set.add(name); + return name; + } + for (let i = 2; i < 999; i++) { + const suffix = `_${i}`; + const max = 31 - suffix.length; + const next = this.financeSanitizeSheetName(name.slice(0, Math.max(1, max)) + suffix); + if (!set.has(next)) { + set.add(next); + return next; + } + } + const fallback = this.financeSanitizeSheetName(`${name}_${Date.now()}`); + set.add(fallback); + return fallback; + }, + loadPlanShipping() { if (!this.selectedSalesmanId) return; this.planDetailLoading = true; diff --git a/gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue b/gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue index fa8bb7a..eef6e1e 100644 --- a/gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue +++ b/gear-ui3/src/views/wms/stockIoOrder/panels/stockIoOrderPage.vue @@ -59,6 +59,7 @@ +