-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 查询
- 重置
- 展开
- 收起
-
-
-
-
-
-
-
- 新增
- 导出
- 批量删除
-
-
-
-
-
-
-
-
-
-
-
- {{ scope.row.djrq ? (new Date(scope.row.djrq).toLocaleDateString('zh-CN', {year:'numeric',month:'2-digit',day:'2-digit'}).replace(/\//g,'-')) : '' }}
-
-
-
-
-
- {{ scope.row.djzt || scope.row.shzt || '未审核' }}
-
-
-
-
- {{ wldwText(scope.row.wldw || scope.row.kh) }}
-
-
-
-
-
-
-
-
-
-
-
- 查看
- 编辑
- 删除
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 查询
+ 重置
+ 展开
+ 收起
+
+
+
+
+
+
+
+ 新增
+ 导出
+ 批量删除
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+ {{
+ scope.row.djrq
+ ? new Date(scope.row.djrq)
+ .toLocaleDateString("zh-CN", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit"
+ })
+ .replace(/\//g, "-")
+ : ""
+ }}
+
+
+
+
+
+ 草稿
+ 待审核
+ 一级已审
+ 已审核
+ 审核不通过
+ {{
+ cellText(auditStatus(scope.row) || "未审核")
+ }}
+
+
+
+
+ {{ showWldw(scope.row) }}
+
+
+
+
+
+
+
+
+
+
+
+ 查看
+ 审核信息
+ 编辑
+ 提交审核
+ 撤回
+ 一级审核
+ 二级审核
+ 审核不通过
+ 删除
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
+ }
+};
+
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdCrInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdCrInput.cs
index 2becf43..dfb58fe 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdCrInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdCrInput.cs
@@ -77,6 +77,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
/// 付款单位
///
public string fkdw { get; set; }
+
+ ///
+ /// 往来单位(wt_wldw.F_Id);与 fkdw 同步保存
+ ///
+ public string wldw { get; set; }
+
+ ///
+ /// 兼容请求:客户主键,落库时与 wldw/fkdw 对齐
+ ///
+ public string kh { get; set; }
+
+ ///
+ /// 兼容请求:供应商主键,落库时与 wldw/fkdw 对齐
+ ///
+ public string gys { get; set; }
+
+ ///
+ /// 单据状态(一般仅查询回显;写入以服务端为准)
+ ///
+ public string djzt { get; set; }
///
/// 费用收入单明细
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdInfoOutput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdInfoOutput.cs
index 4316cba..30fe63e 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdInfoOutput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdInfoOutput.cs
@@ -77,6 +77,36 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
/// 付款单位
///
public string fkdw { get; set; }
+
+ ///
+ /// 往来单位主键(与 fkdw 一致)
+ ///
+ public string wldw { get; set; }
+
+ ///
+ /// 往来单位名称
+ ///
+ public string wldwmc { get; set; }
+
+ ///
+ /// 单据状态
+ ///
+ public string djzt { get; set; }
+
+ ///
+ /// 审批备注
+ ///
+ public string spbz { get; set; }
+
+ ///
+ /// 一级审核人
+ ///
+ public string shr1 { get; set; }
+
+ ///
+ /// 二级审核人
+ ///
+ public string shr2 { get; set; }
///
/// 费用收入单明细
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListOutput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListOutput.cs
index 18faae0..1c67d8a 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListOutput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListOutput.cs
@@ -71,6 +71,26 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
/// 单据类型
///
public string djlx { get; set; }
+
+ ///
+ /// 付款单位
+ ///
+ public string fkdw { get; set; }
+
+ ///
+ /// 往来单位主键
+ ///
+ public string wldw { get; set; }
+
+ ///
+ /// 往来单位名称
+ ///
+ public string wldwmc { get; set; }
+
+ ///
+ /// 单据状态
+ ///
+ public string djzt { get; set; }
}
}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListQueryInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListQueryInput.cs
index 20a8400..586df88 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListQueryInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtFysrdListQueryInput.cs
@@ -83,6 +83,16 @@ namespace NCC.Extend.Entitys.Dto.WtFysrd
/// 单据类型
///
public string djlx { get; set; }
+
+ ///
+ /// 付款单位
+ ///
+ public string fkdw { get; set; }
+
+ ///
+ /// 往来单位(与 fkdw 任一匹配)
+ ///
+ public string wldw { get; set; }
}
}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtPurchaseSummaryQueryInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtPurchaseSummaryQueryInput.cs
index 04bcae5..6c63263 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtPurchaseSummaryQueryInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtPurchaseSummaryQueryInput.cs
@@ -36,11 +36,21 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
public string Warehouse { get; set; }
///
- /// 单据类型(支持逗号分隔多值)
+ /// 单据类型(逗号分隔,仅允许采购/销售/预售/委托代销共 9 类;空或 all 表示默认包含全部 9 类)
///
public string BillType { get; set; }
///
+ /// 排序列键(白名单):分类/品牌/商品聚合支持「分类名称」「采购金额」等;明细/线性列表支持「单据日期」「数量」等;非法则走各接口默认排序。
+ ///
+ public string SortField { get; set; }
+
+ ///
+ /// 排序方向:asc / desc(默认 desc)
+ ///
+ public string SortOrder { get; set; }
+
+ ///
/// 下钻:商品分类主键(wt_pl.F_Id),与明细接口联用
///
public string CategoryId { get; set; }
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtStockSummaryQueryInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtStockSummaryQueryInput.cs
index f534977..2916245 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtStockSummaryQueryInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtStockSummaryQueryInput.cs
@@ -24,5 +24,10 @@ namespace NCC.Extend.Entitys.Dto.WtXsckd
/// 商品关键字(匹配 wt_sp 名称或编码)
///
public string Product { get; set; }
+
+ ///
+ /// 商品主键(wt_sp.F_Id);非空时按精确商品筛选(优先级高于 )
+ ///
+ public string ProductSpId { get; set; }
}
}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsCrInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsCrInput.cs
index ec28e7f..6c78ca5 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsCrInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsCrInput.cs
@@ -29,6 +29,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
public string jsr { get; set; }
///
+ /// 往来单位(主表 wldw,与明细 kh 对应)
+ ///
+ public string wldw { get; set; }
+
+ ///
/// 审核状态
///
public string djzt { get; set; }
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsInfoOutput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsInfoOutput.cs
index eae5032..e64f672 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsInfoOutput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsInfoOutput.cs
@@ -27,6 +27,11 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
/// 往来单位
///
public string wldw { get; set; }
+
+ ///
+ /// 往来单位名称(详情展示,服务端解析)
+ ///
+ public string wldwmc { get; set; }
///
/// 经手人
@@ -34,6 +39,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
public string jsr { get; set; }
///
+ /// 审批备注
+ ///
+ public string spbz { get; set; }
+
+ ///
+ /// 审核人(终审)
+ ///
+ public string shr { get; set; }
+
+ ///
+ /// 一级审核人
+ ///
+ public string shr1 { get; set; }
+
+ ///
+ /// 二级审核人
+ ///
+ public string shr2 { get; set; }
+
+ ///
/// 审核状态
///
public string djzt { get; set; }
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsListOutput.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsListOutput.cs
index d1a6a52..43d259c 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsListOutput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/WtYskzjjsListOutput.cs
@@ -86,6 +86,26 @@ namespace NCC.Extend.Entitys.Dto.WtYskzjjs
/// 往来单位名称(按往来单位/会员/供应商自动解析)
///
public string wldwmc { get; set; }
+
+ ///
+ /// 审批备注
+ ///
+ public string spbz { get; set; }
+
+ ///
+ /// 审核人(终审)
+ ///
+ public string shr { get; set; }
+
+ ///
+ /// 一级审核人
+ ///
+ public string shr1 { get; set; }
+
+ ///
+ /// 二级审核人
+ ///
+ public string shr2 { get; set; }
}
}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtFysrdEntity.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtFysrdEntity.cs
index bcd1a05..88defcc 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtFysrdEntity.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtFysrdEntity.cs
@@ -94,6 +94,36 @@ namespace NCC.Extend.Entitys
///
[SugarColumn(ColumnName = "fkdw")]
public string Fkdw { get; set; }
+
+ ///
+ /// 往来单位(与 fkdw 存同一 wt_wldw.F_Id,便于与 kh/gys/wldw 统一口径)
+ ///
+ [SugarColumn(ColumnName = "wldw", IsNullable = true)]
+ public string Wldw { get; set; }
+
+ ///
+ /// 单据状态(草稿/待审核/一级已审/已审核/审核不通过)
+ ///
+ [SugarColumn(ColumnName = "djzt", IsNullable = true)]
+ public string Djzt { get; set; }
+
+ ///
+ /// 审批备注(与业务备注 bz 分离)
+ ///
+ [SugarColumn(ColumnName = "spbz", IsNullable = true)]
+ public string Spbz { get; set; }
+
+ ///
+ /// 一级审核人
+ ///
+ [SugarColumn(ColumnName = "shr1", IsNullable = true)]
+ public string Shr1 { get; set; }
+
+ ///
+ /// 二级审核人
+ ///
+ [SugarColumn(ColumnName = "shr2", IsNullable = true)]
+ public string Shr2 { get; set; }
}
}
\ No newline at end of file
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtYskzjjsEntity.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtYskzjjsEntity.cs
index 107e8de..c928fd5 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtYskzjjsEntity.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/WtYskzjjsEntity.cs
@@ -42,6 +42,30 @@ namespace NCC.Extend.Entitys
public string Jsr { get; set; }
///
+ /// 审批备注(与业务备注分离;库表须含列 spbz)
+ ///
+ [SugarColumn(ColumnName = "spbz", IsNullable = true, ColumnDataType = "text")]
+ public string Spbz { get; set; }
+
+ ///
+ /// 审核人(终审)
+ ///
+ [SugarColumn(ColumnName = "shr", IsNullable = true)]
+ public string Shr { get; set; }
+
+ ///
+ /// 一级审核人
+ ///
+ [SugarColumn(ColumnName = "shr1", IsNullable = true)]
+ public string Shr1 { get; set; }
+
+ ///
+ /// 二级审核人
+ ///
+ [SugarColumn(ColumnName = "shr2", IsNullable = true)]
+ public string Shr2 { get; set; }
+
+ ///
/// 审核状态
///
[SugarColumn(ColumnName = "djzt")]
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdService.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdService.cs
index eb72ab8..09bf578 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdService.cs
@@ -36,6 +36,7 @@ namespace NCC.Extend.WtFysrd
private readonly ISqlSugarRepository
_wtFysrdMxRepository;
private readonly SqlSugarScope _db;
private readonly IUserManager _userManager;
+ private readonly WtFysrdWorkflowHelper _fysrdWorkflow;
///
/// 初始化一个类型的新实例
@@ -43,12 +44,71 @@ namespace NCC.Extend.WtFysrd
public WtFysrdService(
ISqlSugarRepository wtFysrdRepository,
ISqlSugarRepository wtFysrdMxRepository,
- IUserManager userManager)
+ IUserManager userManager,
+ WtFysrdWorkflowHelper fysrdWorkflow)
{
_wtFysrdRepository = wtFysrdRepository;
_db = _wtFysrdRepository.Context;
_wtFysrdMxRepository = wtFysrdMxRepository;
_userManager = userManager;
+ _fysrdWorkflow = fysrdWorkflow;
+ }
+
+ private static bool IsLockedForEdit(string djzt)
+ {
+ var s = djzt?.Trim() ?? "";
+ return s == "待审核" || s == "已审核" || s == "一级已审" || s == "待二级" || s == "一级已审/待二级";
+ }
+
+ private static bool IsDeletableState(string djzt)
+ {
+ var s = djzt?.Trim() ?? "";
+ return string.IsNullOrEmpty(s) || s == "草稿" || s == "审核不通过";
+ }
+
+ private void NormalizeHeaderFromInput(WtFysrdCrInput input, WtFysrdEntity entity)
+ {
+ if (input == null || entity == null) return;
+ if (!string.IsNullOrWhiteSpace(input.djlx))
+ entity.Djlx = WtFysrdWorkflowHelper.NormalizeOtherIncomeDjlx(input.djlx) ?? input.djlx;
+ if (!string.IsNullOrWhiteSpace(WtFysrdWorkflowHelper.ResolveCounterpartyId(input.wldw, input.fkdw, input.kh, input.gys)))
+ WtFysrdWorkflowHelper.ApplyCounterpartyToEntity(entity, input.wldw, input.fkdw, input.kh, input.gys);
+ }
+
+ private async Task EnrichWldwForListAsync(IEnumerable rows)
+ {
+ if (rows == null) return;
+ var rowList = rows as IList ?? rows.ToList();
+ if (rowList.Count == 0) return;
+ foreach (var r in rowList)
+ {
+ if (string.IsNullOrWhiteSpace(r.wldw))
+ r.wldw = r.fkdw;
+ if (string.IsNullOrWhiteSpace(r.fkdw))
+ r.fkdw = r.wldw;
+ }
+ var ids = rowList.Select(x => x.wldw).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
+ if (ids.Count == 0) return;
+ var wldwRows = await _db.Queryable().In(w => w.Id, ids).ToListAsync();
+ var map = wldwRows.ToDictionary(x => x.Id, x => x.Dwmc, StringComparer.OrdinalIgnoreCase);
+ foreach (var r in rowList)
+ {
+ if (!string.IsNullOrWhiteSpace(r.wldw) && map.TryGetValue(r.wldw, out var mc))
+ r.wldwmc = mc;
+ }
+ }
+
+ ///
+ /// 提交审核:草稿/审核不通过 → 待审核(其他收入单;除备注外必填校验)
+ ///
+ /// 单据编号
+ [HttpPost("Actions/SubmitForAudit/{id}")]
+ public async Task SubmitForAudit(string id)
+ {
+ dynamic r = await _fysrdWorkflow.SubmitForAuditAsync(id);
+ if (r.success == true)
+ return new { msg = (string)(r.message ?? "已提交") };
+ throw NCCException.Bah((string)(r.message ?? "提交失败"));
}
///
@@ -59,9 +119,22 @@ namespace NCC.Extend.WtFysrd
[HttpGet("{id}")]
public async Task GetInfo(string id)
{
+ _fysrdWorkflow.EnsureFysrdAuditColumns();
var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
var output = entity.Adapt();
-
+ output.djlx = WtFysrdWorkflowHelper.NormalizeOtherIncomeDjlx(entity.Djlx) ?? entity.Djlx;
+ output.wldw = string.IsNullOrWhiteSpace(entity.Wldw) ? entity.Fkdw : entity.Wldw;
+ output.fkdw = string.IsNullOrWhiteSpace(entity.Fkdw) ? entity.Wldw : entity.Fkdw;
+ output.djzt = entity.Djzt;
+ output.spbz = entity.Spbz;
+ output.shr1 = entity.Shr1;
+ output.shr2 = entity.Shr2;
+ var wldwId = output.wldw ?? output.fkdw;
+ if (!string.IsNullOrWhiteSpace(wldwId))
+ {
+ var w = await _db.Queryable().FirstAsync(x => x.Id == wldwId);
+ output.wldwmc = w?.Dwmc;
+ }
var wtFysrdMxList = await _db.Queryable().Where(w => w.Djbh == entity.Id).ToListAsync();
output.wtFysrdMxList = wtFysrdMxList.Adapt>();
return output;
@@ -75,6 +148,7 @@ namespace NCC.Extend.WtFysrd
[HttpGet("")]
public async Task GetList([FromQuery] WtFysrdListQueryInput input)
{
+ _fysrdWorkflow.EnsureFysrdAuditColumns();
var sidx = input.sidx == null ? "id" : input.sidx;
List queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject>() : null;
DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -94,6 +168,8 @@ namespace NCC.Extend.WtFysrd
.WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz))
.WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy))
.WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx))
+ .WhereIF(!string.IsNullOrEmpty(input.fkdw), p => p.Fkdw == input.fkdw || p.Wldw == input.fkdw)
+ .WhereIF(!string.IsNullOrEmpty(input.wldw), p => p.Wldw == input.wldw || p.Fkdw == input.wldw)
.Select(it=> new WtFysrdListOutput
{
id = it.Id,
@@ -109,7 +185,11 @@ namespace NCC.Extend.WtFysrd
bz=it.Bz,
zy=it.Zy,
djlx=it.Djlx,
+ fkdw = it.Fkdw,
+ wldw = it.Wldw,
+ djzt = it.Djzt,
}).MergeTable().OrderBy(sidx+" "+input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
+ await EnrichWldwForListAsync(data.list);
return PageResult.SqlSugarPageResult(data);
}
@@ -121,15 +201,18 @@ namespace NCC.Extend.WtFysrd
[HttpPost("")]
public async Task Create([FromBody] WtFysrdCrInput input)
{
+ _fysrdWorkflow.EnsureFysrdAuditColumns();
var userInfo = await _userManager.GetUserInfo();
var entity = input.Adapt();
+ NormalizeHeaderFromInput(input, entity);
+ entity.Djzt = "草稿";
// 生成每日递增单号
var today = DateTime.Now.ToString("yyyyMMdd");
var prefix = "FY";
// 根据单据类型设置前缀
- if (!string.IsNullOrEmpty(input.djlx)) {
- if (input.djlx.Contains("现金费用单")) prefix = "XF";
- else if (input.djlx.Contains("其他收入单")) prefix = "QT";
+ if (!string.IsNullOrEmpty(entity.Djlx)) {
+ if (entity.Djlx.Contains("现金费用单")) prefix = "XF";
+ else if (WtFysrdWorkflowHelper.IsOtherIncomeDjlx(entity.Djlx)) prefix = "QT";
else prefix = "FY"; // 默认费用单前缀
}
var maxId = await _db.Queryable()
@@ -199,6 +282,7 @@ namespace NCC.Extend.WtFysrd
[NonAction]
public async Task GetNoPagingList([FromQuery] WtFysrdListQueryInput input)
{
+ _fysrdWorkflow.EnsureFysrdAuditColumns();
var sidx = input.sidx == null ? "id" : input.sidx;
List queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject>() : null;
DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -218,6 +302,8 @@ namespace NCC.Extend.WtFysrd
.WhereIF(!string.IsNullOrEmpty(input.bz), p => p.Bz.Contains(input.bz))
.WhereIF(!string.IsNullOrEmpty(input.zy), p => p.Zy.Contains(input.zy))
.WhereIF(!string.IsNullOrEmpty(input.djlx), p => p.Djlx.Contains(input.djlx))
+ .WhereIF(!string.IsNullOrEmpty(input.fkdw), p => p.Fkdw == input.fkdw || p.Wldw == input.fkdw)
+ .WhereIF(!string.IsNullOrEmpty(input.wldw), p => p.Wldw == input.wldw || p.Fkdw == input.wldw)
.Select(it=> new WtFysrdListOutput
{
id = it.Id,
@@ -233,7 +319,11 @@ namespace NCC.Extend.WtFysrd
bz=it.Bz,
zy=it.Zy,
djlx=it.Djlx,
+ fkdw = it.Fkdw,
+ wldw = it.Wldw,
+ djzt = it.Djzt,
}).MergeTable().OrderBy(sidx+" "+input.sort).ToListAsync();
+ await EnrichWldwForListAsync(data);
return data;
}
@@ -256,7 +346,7 @@ namespace NCC.Extend.WtFysrd
{
exportData = await this.GetNoPagingList(input);
}
- List paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"分支机构\",\"field\":\"fzjg\"},{\"value\":\"部门\",\"field\":\"bm\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"结算账户\",\"field\":\"jszh\"},{\"value\":\"金额\",\"field\":\"je\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"摘要\",\"field\":\"zy\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},]".ToList();
+ List paramList = "[{\"value\":\"单据编号\",\"field\":\"id\"},{\"value\":\"单据日期\",\"field\":\"djrq\"},{\"value\":\"分支机构\",\"field\":\"fzjg\"},{\"value\":\"部门\",\"field\":\"bm\"},{\"value\":\"经手人\",\"field\":\"jsr\"},{\"value\":\"结算账户\",\"field\":\"jszh\"},{\"value\":\"金额\",\"field\":\"je\"},{\"value\":\"制单人\",\"field\":\"zdr\"},{\"value\":\"审核人\",\"field\":\"shr\"},{\"value\":\"过账人\",\"field\":\"gzr\"},{\"value\":\"备注\",\"field\":\"bz\"},{\"value\":\"摘要\",\"field\":\"zy\"},{\"value\":\"单据类型\",\"field\":\"djlx\"},{\"value\":\"往来单位\",\"field\":\"wldw\"},{\"value\":\"往来单位名称\",\"field\":\"wldwmc\"},{\"value\":\"付款单位\",\"field\":\"fkdw\"},{\"value\":\"单据状态\",\"field\":\"djzt\"},]".ToList();
ExcelConfig excelconfig = new ExcelConfig();
excelconfig.FileName = "费用单.xls";
excelconfig.HeadFont = "微软雅黑";
@@ -292,6 +382,11 @@ namespace NCC.Extend.WtFysrd
public async Task BatchRemove([FromBody] List ids)
{
var entitys = await _db.Queryable().In(it => it.Id, ids).ToListAsync();
+ foreach (var e in entitys)
+ {
+ if (!IsDeletableState(e.Djzt))
+ throw NCCException.Bah($"单据 {e.Id} 当前状态不允许删除");
+ }
if (entitys.Count > 0)
{
try
@@ -324,7 +419,37 @@ namespace NCC.Extend.WtFysrd
[HttpPut("{id}")]
public async Task Update(string id, [FromBody] WtFysrdUpInput input)
{
+ _fysrdWorkflow.EnsureFysrdAuditColumns();
+ var existing = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (IsLockedForEdit(existing.Djzt))
+ throw NCCException.Bah("当前单据状态不允许修改");
+
var entity = input.Adapt();
+ entity.Id = id;
+ entity.Djzt = existing.Djzt;
+ if (string.IsNullOrWhiteSpace(entity.Djlx))
+ entity.Djlx = existing.Djlx;
+ NormalizeHeaderFromInput(input, entity);
+ if (string.IsNullOrWhiteSpace(WtFysrdWorkflowHelper.ResolveCounterpartyId(entity.Wldw, entity.Fkdw, null, null)))
+ {
+ entity.Wldw = existing.Wldw;
+ entity.Fkdw = existing.Fkdw;
+ }
+
+ if (WtFysrdWorkflowHelper.IsOtherIncomeDjlx(entity.Djlx)
+ && !string.Equals(existing.Djzt?.Trim(), "草稿", StringComparison.Ordinal))
+ {
+ var mxForVal = input.wtFysrdMxList.Adapt>();
+ try
+ {
+ WtFysrdWorkflowHelper.ValidateOtherIncomeForSubmit(entity, mxForVal);
+ }
+ catch (InvalidOperationException ex)
+ {
+ throw NCCException.Bah(ex.Message);
+ }
+ }
+
try
{
//开启事务
@@ -367,6 +492,8 @@ namespace NCC.Extend.WtFysrd
{
var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
_ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
+ if (!IsDeletableState(entity.Djzt))
+ throw NCCException.Bah("当前单据状态不允许删除");
try
{
//开启事务
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdWorkflowHelper.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdWorkflowHelper.cs
new file mode 100644
index 0000000..3844ef8
--- /dev/null
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtFysrdWorkflowHelper.cs
@@ -0,0 +1,446 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using NCC.Common.Core.Manager;
+using NCC.Dependency;
+using NCC.Extend.Entitys;
+using SqlSugar;
+
+namespace NCC.Extend.WtFysrd
+{
+ ///
+ /// 其他收入单(wt_fysrd)审批流:与待办中心、ApproveGeneric/RejectGeneric/ReverseApproval 对接。
+ ///
+ public class WtFysrdWorkflowHelper : ITransient
+ {
+ /// 与 wt_shrysz.djmc、待办 billType 一致
+ public const string BillName = "其他收入单";
+
+ private readonly SqlSugarScope _db;
+ private readonly IUserManager _userManager;
+ private static bool _auditColumnsChecked;
+
+ public WtFysrdWorkflowHelper(ISqlSugarRepository fysrdRepository, IUserManager userManager)
+ {
+ _db = fysrdRepository.Context;
+ _userManager = userManager;
+ }
+
+ ///
+ /// 确保 wt_fysrd 具备审批与往来单位统一字段(djzt、spbz、shr、shr1、shr2、wldw)
+ ///
+ public void EnsureFysrdAuditColumns()
+ {
+ if (_auditColumnsChecked) return;
+ lock (typeof(WtFysrdWorkflowHelper))
+ {
+ if (_auditColumnsChecked) return;
+ try
+ {
+ if (!_db.DbMaintenance.IsAnyTable("wt_fysrd")) { _auditColumnsChecked = true; return; }
+ var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_fysrd");
+ var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
+ if (!names.Contains("djzt"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
+ if (!names.Contains("spbz"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
+ if (!names.Contains("shr"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
+ if (!names.Contains("shr1"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
+ if (!names.Contains("shr2"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
+ if (!names.Contains("wldw"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位(wt_wldw.F_Id),与 fkdw 同步'");
+
+ _db.Ado.ExecuteCommand(
+ "UPDATE `wt_fysrd` SET `djzt`='草稿' WHERE `djzt` IS NULL OR TRIM(IFNULL(`djzt`,''))=''");
+ _db.Ado.ExecuteCommand(
+ "UPDATE `wt_fysrd` SET `wldw`=`fkdw` WHERE (`wldw` IS NULL OR TRIM(IFNULL(`wldw`,''))='') AND `fkdw` IS NOT NULL AND TRIM(`fkdw`)<>''");
+ }
+ catch (Exception ex) { Console.WriteLine($"EnsureFysrdAuditColumns: {ex.Message}"); }
+ _auditColumnsChecked = true;
+ }
+ }
+
+ /// 是否按「其他收入单」口径处理的单据类型(含历史前端传值)
+ public static bool IsOtherIncomeDjlx(string djlx)
+ {
+ var n = NormalizeOtherIncomeDjlx(djlx);
+ return string.Equals(n, BillName, StringComparison.Ordinal);
+ }
+
+ /// 统一单据类型展示与审核配置匹配用字符串
+ public static string NormalizeOtherIncomeDjlx(string djlx)
+ {
+ if (string.IsNullOrWhiteSpace(djlx)) return djlx;
+ var t = djlx.Trim().Replace("其它", "其他");
+ if (t == "其他收入" || t == "其他收入单" || t.Contains("其他收入单"))
+ return BillName;
+ if (t.Contains("其他收入"))
+ return BillName;
+ return djlx.Trim();
+ }
+
+ /// 往来单位主键:优先 wldw,其次 fkdw、kh、gys(请求兼容)
+ public static string ResolveCounterpartyId(string wldw, string fkdw, string kh, string gys)
+ {
+ var v = !string.IsNullOrWhiteSpace(wldw) ? wldw
+ : !string.IsNullOrWhiteSpace(fkdw) ? fkdw
+ : !string.IsNullOrWhiteSpace(kh) ? kh
+ : gys;
+ return string.IsNullOrWhiteSpace(v) ? null : v.Trim();
+ }
+
+ public static void ApplyCounterpartyToEntity(WtFysrdEntity entity, string wldw, string fkdw, string kh, string gys)
+ {
+ var id = ResolveCounterpartyId(wldw, fkdw, kh, gys);
+ if (string.IsNullOrEmpty(id)) return;
+ entity.Wldw = id;
+ entity.Fkdw = id;
+ }
+
+ private static void AppendSpbz(WtFysrdEntity entity, string actionTag, string remark)
+ {
+ var r = (remark ?? "").Trim();
+ if (string.IsNullOrEmpty(r)) return;
+ var line = string.IsNullOrEmpty(actionTag) ? r : $"[{actionTag}] {r}";
+ var existing = entity.Spbz?.TrimEnd() ?? "";
+ entity.Spbz = string.IsNullOrWhiteSpace(existing) ? line : existing + "\n" + line;
+ }
+
+ private static bool IsUserInAuditConfig(string configValue, string userId, string userAccount)
+ {
+ if (string.IsNullOrWhiteSpace(configValue)) return false;
+ if (string.IsNullOrWhiteSpace(userId) && string.IsNullOrWhiteSpace(userAccount)) return false;
+ var users = configValue
+ .Split(new[] { ',', ',', ';', ';', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim())
+ .Where(x => !string.IsNullOrEmpty(x))
+ .ToList();
+ return users.Any(x =>
+ (!string.IsNullOrWhiteSpace(userId) && string.Equals(x, userId.Trim(), StringComparison.OrdinalIgnoreCase))
+ || (!string.IsNullOrWhiteSpace(userAccount) && string.Equals(x, userAccount.Trim(), StringComparison.OrdinalIgnoreCase)));
+ }
+
+ ///
+ /// 提交审核前校验:除备注外主表及明细必填(仅其他收入单)
+ ///
+ public static void ValidateOtherIncomeForSubmit(WtFysrdEntity h, List mxList)
+ {
+ if (h == null) throw new ArgumentNullException(nameof(h));
+ if (!IsOtherIncomeDjlx(h.Djlx))
+ return;
+
+ if (h.Djrq == null)
+ throw new InvalidOperationException("单据日期不能为空");
+ if (string.IsNullOrWhiteSpace(h.Fzjg))
+ throw new InvalidOperationException("分支机构不能为空");
+ if (string.IsNullOrWhiteSpace(h.Bm))
+ throw new InvalidOperationException("部门不能为空");
+ if (string.IsNullOrWhiteSpace(h.Jsr))
+ throw new InvalidOperationException("经手人不能为空");
+ if (string.IsNullOrWhiteSpace(h.Jszh))
+ throw new InvalidOperationException("结算账户不能为空");
+ if (h.Je <= 0)
+ throw new InvalidOperationException("金额必须大于 0");
+ if (string.IsNullOrWhiteSpace(h.Zdr))
+ throw new InvalidOperationException("制单人不能为空");
+ if (string.IsNullOrWhiteSpace(h.Zy))
+ throw new InvalidOperationException("摘要不能为空");
+ var cp = ResolveCounterpartyId(h.Wldw, h.Fkdw, null, null);
+ if (string.IsNullOrWhiteSpace(cp))
+ throw new InvalidOperationException("往来单位不能为空");
+
+ if (mxList == null || mxList.Count == 0)
+ throw new InvalidOperationException("请先维护收入明细");
+
+ foreach (var mx in mxList)
+ {
+ if (string.IsNullOrWhiteSpace(mx.Km))
+ throw new InvalidOperationException("明细科目不能为空");
+ if (mx.Je <= 0)
+ throw new InvalidOperationException("明细金额必须大于 0");
+ if (string.IsNullOrWhiteSpace(mx.Mxlx))
+ throw new InvalidOperationException("明细类型不能为空");
+ }
+ }
+
+ ///
+ /// 草稿 / 审核不通过 → 待审核
+ ///
+ public async Task SubmitForAuditAsync(string id)
+ {
+ EnsureFysrdAuditColumns();
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ return new { success = false, message = "单据不存在" };
+ if (!IsOtherIncomeDjlx(entity.Djlx))
+ return new { success = false, message = "该接口仅支持其他收入单" };
+
+ var st = entity.Djzt?.Trim() ?? "";
+ if (st != "草稿" && st != "审核不通过")
+ return new { success = false, message = "仅草稿或审核不通过状态可提交审核" };
+
+ var mxList = await _db.Queryable().Where(w => w.Djbh == id).ToListAsync();
+ try
+ {
+ ValidateOtherIncomeForSubmit(entity, mxList);
+ }
+ catch (InvalidOperationException ex)
+ {
+ return new { success = false, message = ex.Message };
+ }
+
+ entity.Djzt = "待审核";
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt }).ExecuteCommandAsync();
+ return new { success = true, message = "已提交审核,请在待办中心处理" };
+ }
+
+ ///
+ /// 与 对接
+ ///
+ public async Task ApproveAsync(string id, string remark)
+ {
+ EnsureFysrdAuditColumns();
+ try
+ {
+ _db.BeginTran();
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "单据不存在" };
+ }
+ if (!IsOtherIncomeDjlx(entity.Djlx))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "该单据不是其他收入单" };
+ }
+
+ if (entity.Djzt == "已审核")
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "该单据已经审核,无需重复审核" };
+ }
+
+ var userInfo = await _userManager.GetUserInfo();
+ var userId = userInfo?.userId ?? "";
+ var userAccount = userInfo?.userAccount?.Trim();
+ if (string.IsNullOrEmpty(userAccount))
+ userAccount = _userManager.Account?.Trim();
+
+ var approvalConfig = await _db.Queryable()
+ .Where(c => c.Djmc == BillName)
+ .FirstAsync();
+
+ var configShr1 = approvalConfig?.Shr1;
+ var configShr2 = approvalConfig?.Shr2;
+ var hasTwoLevel = !string.IsNullOrWhiteSpace(configShr1) && !string.IsNullOrWhiteSpace(configShr2);
+
+ if (string.IsNullOrWhiteSpace(configShr1))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "未配置其他收入单一级审核人员,请先在审核人员设置中维护" };
+ }
+
+ var djztTrim = entity.Djzt?.Trim() ?? "";
+ if (entity.Djzt == "待审核" || string.IsNullOrEmpty(djztTrim))
+ {
+ if (!IsUserInAuditConfig(configShr1, userId, userAccount))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "当前用户不在其他收入单一级审核人员范围内" };
+ }
+
+ if (hasTwoLevel)
+ {
+ entity.Djzt = "一级已审";
+ entity.Shr1 = userId;
+ AppendSpbz(entity, "一级通过", remark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "一级审核通过,等待二级审核" };
+ }
+
+ entity.Djzt = "已审核";
+ entity.Shr = userId;
+ entity.Shr1 = userId;
+ AppendSpbz(entity, "审核通过", remark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "审核通过" };
+ }
+
+ if (entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级")
+ {
+ if (!hasTwoLevel)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "当前为单级审核配置,单据状态异常" };
+ }
+ if (string.IsNullOrWhiteSpace(configShr2))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "未配置其他收入单二级审核人员,请先在审核人员设置中维护" };
+ }
+ if (!IsUserInAuditConfig(configShr2, userId, userAccount))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "当前用户不在其他收入单二级审核人员范围内" };
+ }
+
+ entity.Djzt = "已审核";
+ entity.Shr2 = userId;
+ entity.Shr = userId;
+ AppendSpbz(entity, "二级通过", remark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr2, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "二级审核通过,审核完成" };
+ }
+
+ _db.RollbackTran();
+ return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核" };
+ }
+ catch (Exception ex)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = $"审核失败: {ex.Message}" };
+ }
+ }
+
+ ///
+ /// 与 RejectGeneric 对接
+ ///
+ public async Task RejectAsync(string id, string remark)
+ {
+ EnsureFysrdAuditColumns();
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ return new { success = false, message = "单据不存在" };
+ if (!IsOtherIncomeDjlx(entity.Djlx))
+ return new { success = false, message = "该单据不是其他收入单" };
+ if (entity.Djzt == "已审核")
+ return new { success = false, message = "该单据已审核通过" };
+ if (entity.Djzt == "审核不通过")
+ return new { success = false, message = "该单据已是审核不通过状态" };
+ if (entity.Djzt == "草稿")
+ return new { success = false, message = "草稿状态请直接修改或删除单据" };
+
+ var userInfo = await _userManager.GetUserInfo();
+ var userId = userInfo?.userId ?? "";
+ var userAccount = userInfo?.userAccount?.Trim();
+ if (string.IsNullOrEmpty(userAccount))
+ userAccount = _userManager.Account?.Trim();
+
+ var approvalConfig = await _db.Queryable()
+ .Where(c => c.Djmc == BillName)
+ .FirstAsync();
+ var configShr1 = approvalConfig?.Shr1;
+ var configShr2 = approvalConfig?.Shr2;
+
+ var djztTrim = entity.Djzt?.Trim() ?? "";
+ var level1 = djztTrim == "待审核" || string.IsNullOrEmpty(djztTrim);
+ var level2 = entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级";
+
+ if (!level1 && !level2)
+ return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核不通过" };
+
+ if (level1)
+ {
+ if (string.IsNullOrWhiteSpace(configShr1))
+ return new { success = false, message = "未配置其他收入单一级审核人员,请先在审核人员设置中维护" };
+ if (!IsUserInAuditConfig(configShr1, userId, userAccount))
+ return new { success = false, message = "当前用户不在其他收入单一级审核人员范围内" };
+ }
+ else
+ {
+ if (string.IsNullOrWhiteSpace(configShr2))
+ return new { success = false, message = "未配置其他收入单二级审核人员,请先在审核人员设置中维护" };
+ if (!IsUserInAuditConfig(configShr2, userId, userAccount))
+ return new { success = false, message = "当前用户不在其他收入单二级审核人员范围内" };
+ }
+
+ _db.BeginTran();
+ try
+ {
+ var row = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (row == null)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "单据不存在" };
+ }
+ AppendSpbz(row, "审核不通过", remark);
+ row.Djzt = "审核不通过";
+ row.Shr = userId;
+ row.Shr1 = null;
+ row.Shr2 = null;
+ await _db.Updateable(row).UpdateColumns(x => new { x.Djzt, x.Spbz, x.Shr, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "已标记为审核不通过" };
+ }
+ catch (Exception ex)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = $"操作失败: {ex.Message}" };
+ }
+ }
+
+ ///
+ /// 与 ReverseApproval 对接:已审核或一级已审 → 待审核
+ ///
+ public async Task ReverseAsync(string id)
+ {
+ EnsureFysrdAuditColumns();
+ try
+ {
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ return new { success = false, message = "单据不存在" };
+ if (!IsOtherIncomeDjlx(entity.Djlx))
+ return new { success = false, message = "该单据不是其他收入单" };
+
+ if (entity.Djzt != "已审核" && entity.Djzt != "一级已审" && entity.Djzt != "一级已审/待二级" && entity.Djzt != "待二级")
+ return new { success = false, message = "当前单据状态不允许反审" };
+
+ var userInfo = await _userManager.GetUserInfo();
+ var userId = userInfo?.userId ?? "";
+ var userAccount = userInfo?.userAccount?.Trim();
+ if (string.IsNullOrEmpty(userAccount))
+ userAccount = _userManager.Account?.Trim();
+
+ var approvalConfig = await _db.Queryable()
+ .Where(c => c.Djmc == BillName)
+ .FirstAsync();
+
+ var configShr1 = approvalConfig?.Shr1;
+ var configShr2 = approvalConfig?.Shr2;
+ var bothUnset = string.IsNullOrWhiteSpace(configShr1) && string.IsNullOrWhiteSpace(configShr2);
+ var isAuthorized = bothUnset
+ || IsUserInAuditConfig(configShr1 ?? "", userId, userAccount)
+ || IsUserInAuditConfig(configShr2 ?? "", userId, userAccount);
+
+ if (!isAuthorized)
+ return new { success = false, message = "您没有反审权限,只有一级或二级审核人可以反审" };
+
+ entity.Djzt = "待审核";
+ entity.Shr = null;
+ entity.Shr1 = null;
+ entity.Shr2 = null;
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
+ return new { success = true, message = "反审成功,单据已恢复为待审核状态" };
+ }
+ catch (Exception ex)
+ {
+ return new { success = false, message = $"反审失败: {ex.Message}" };
+ }
+ }
+ }
+}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
index 101fe05..dc56895 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtXsckdService.cs
@@ -50,6 +50,7 @@ namespace NCC.Extend.WtXsckd
private readonly WtBillSummaryService _billSummaryService;
private readonly WtDjzyService _wtDjzyService;
private readonly WtTjd.WtTjdWorkflowHelper _tjdWorkflowHelper;
+ private readonly WtFysrd.WtFysrdWorkflowHelper _fysrdWorkflowHelper;
// 序列号生成信号量,确保线程安全
private static readonly SemaphoreSlim _serialNumberSemaphore = new SemaphoreSlim(1, 1);
@@ -1691,7 +1692,8 @@ WHERE d.djlx IN ('销售退货单','预售退货单','委托代销退货单')
IUserManager userManager,
WtBillSummaryService billSummaryService,
WtDjzyService wtDjzyService,
- WtTjd.WtTjdWorkflowHelper tjdWorkflowHelper)
+ WtTjd.WtTjdWorkflowHelper tjdWorkflowHelper,
+ WtFysrd.WtFysrdWorkflowHelper fysrdWorkflowHelper)
{
_wtXsckdRepository = wtXsckdRepository;
_db = _wtXsckdRepository.Context;
@@ -1700,6 +1702,7 @@ WHERE d.djlx IN ('销售退货单','预售退货单','委托代销退货单')
_billSummaryService = billSummaryService;
_wtDjzyService = wtDjzyService;
_tjdWorkflowHelper = tjdWorkflowHelper;
+ _fysrdWorkflowHelper = fysrdWorkflowHelper;
}
///
@@ -4453,7 +4456,12 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), ''), IFNULL(MAX(s.F_Spbm), '')";
AddInCondition("c.ck", input.Warehouse, "stockWh");
AddInCondition("s.F_Pp", input.Brand, "stockPp");
- if (!string.IsNullOrWhiteSpace(input.Product))
+ if (!string.IsNullOrWhiteSpace(input.ProductSpId))
+ {
+ whereList.Add("s.F_Id = @stockProdId");
+ paramList.Add(new SugarParameter("@stockProdId", input.ProductSpId.Trim()));
+ }
+ else if (!string.IsNullOrWhiteSpace(input.Product))
{
whereList.Add("(s.F_Spmc LIKE @stockProdKw OR s.F_Spbm LIKE @stockProdKw)");
paramList.Add(new SugarParameter("@stockProdKw", $"%{input.Product.Trim()}%"));
@@ -4464,6 +4472,51 @@ ORDER BY IFNULL(MAX(pp.F_Ppmc), ''), IFNULL(MAX(s.F_Spbm), '')";
}
///
+ /// 商品库存汇总(按仓库聚合):用于“选择某个商品后,展示该商品在各仓数量/成本”。
+ ///
+ /// 必须提供 ProductSpId;可选 Warehouse(限定仓库范围)
+ [HttpGet("Actions/GetStockSummaryByWarehouse")]
+ public async Task GetStockSummaryByWarehouse([FromQuery] WtStockSummaryQueryInput input)
+ {
+ input ??= new WtStockSummaryQueryInput();
+ if (string.IsNullOrWhiteSpace(input.ProductSpId))
+ {
+ throw NCCException.Oh("商品 productSpId 不能为空");
+ }
+
+ // 仓库维度不需要分类/品牌筛选,但允许传 warehouse 限定仓库范围
+ var (whereSql, paramList) = BuildWtStockCostSummaryWhere(
+ new WtStockSummaryQueryInput
+ {
+ Warehouse = input.Warehouse,
+ ProductSpId = input.ProductSpId
+ },
+ null);
+
+ var sql = $@"
+SELECT
+ c.ck AS `仓库Id`,
+ IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), CONCAT('未知仓库(', c.ck, ')')) AS `仓库名称`,
+ IFNULL(NULLIF(TRIM(MAX(s.F_Spbm)), ''), '无') AS `商品编码`,
+ IFNULL(NULLIF(TRIM(MAX(s.F_Spmc)), ''), '无') AS `商品名称`,
+ SUM(CAST(c.sl AS DECIMAL(18,4))) AS `数量`,
+ CASE WHEN SUM(CAST(c.sl AS DECIMAL(18,4))) = 0 THEN 0
+ ELSE SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) / SUM(CAST(c.sl AS DECIMAL(18,4))) END AS `成本均价`,
+ SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) AS `总成本`
+FROM wt_sp_cost c
+INNER JOIN wt_sp s ON s.F_Id = c.spbh
+LEFT JOIN wt_ck ck ON ck.F_Id = c.ck
+WHERE c.sl <> 0
+ AND c.ck IS NOT NULL AND TRIM(c.ck) <> ''
+ {whereSql}
+GROUP BY c.ck, ck.F_mdmc
+ORDER BY SUM(CAST(c.sl AS DECIMAL(18,4)) * c.cbj) DESC";
+
+ var list = await _db.Ado.GetDataTableAsync(sql, paramList);
+ return list;
+ }
+
+ ///
/// 获取指定商品在指定门店/仓库范围内的库存数量(与商品库存汇总口径一致:仅汇总 wt_sp_cost.sl,不按序列号条数统计;大量无序列号商品亦适用)。
///
/// 商品ID
@@ -4580,11 +4633,135 @@ INNER JOIN wt_xsckd d ON d.F_Id = mx.djbh
LEFT JOIN wt_sp sp ON sp.F_Id = mx.spbh
LEFT JOIN wt_pl pl ON pl.F_Id = sp.F_Pl
LEFT JOIN wt_pp pp ON pp.F_Id = sp.F_Pp
-LEFT JOIN wt_wldw w ON w.F_Id = d.gys
-LEFT JOIN BASE_USER u ON u.F_Id = d.jsr
+LEFT JOIN wt_wldw w ON w.F_Id = IFNULL(NULLIF(TRIM(d.gys), ''), d.kh)
+LEFT JOIN BASE_USER u ON u.F_Id = IFNULL(NULLIF(TRIM(d.jsr), ''), d.zdr)
LEFT JOIN wt_ck ck ON ck.F_Id = IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)
WHERE 1 = 1";
+ /// 采购汇总限定单据类型(默认全集)。
+ private static readonly string[] PurSumAllowedDjlx =
+ {
+ "采购入库单", "采购退货单", "销售出库单", "销售退货单", "预售出库单", "预售退货单",
+ "委托代销发货单", "委托代销退货单", "委托代销结算单"
+ };
+
+ /// 主表 d.djlx 加减符号(与明细 mx 解耦)。
+ private const string PurSumBizSignCase =
+ "CASE WHEN d.djlx IN ('采购入库单','销售退货单','预售退货单','委托代销退货单') THEN 1 "
+ + "WHEN d.djlx IN ('采购退货单','销售出库单','预售出库单','委托代销发货单','委托代销结算单') THEN -1 ELSE 0 END";
+
+ // 注意:部分退货/冲销类单据明细 mx.sl/mx.je 可能已为负数;若再按单据类型乘 -1 会出现“双重取反”导致统计方向错误。
+ // 统一口径:先取 ABS,再按单据类型决定正负。
+ private static string PurSumSignedSlExpr => $"(ABS(CAST(mx.sl AS DECIMAL(18,4))) * ({PurSumBizSignCase}))";
+
+ private static string PurSumSignedJeExpr => $"(ABS(CAST(mx.je AS DECIMAL(18,4))) * ({PurSumBizSignCase}))";
+
+ // 单价保持正数展示(方向由数量/金额体现),避免出现负单价
+ private static string PurSumSignedDjExpr => "(ABS(CAST(mx.dj AS DECIMAL(18,6))))";
+
+ private static string NormalizePurSumDjlx(string raw)
+ {
+ if (string.IsNullOrWhiteSpace(raw))
+ {
+ return raw;
+ }
+
+ var t = raw.Trim();
+ return t == "委托代销结算" ? "委托代销结算单" : t;
+ }
+
+ ///
+ /// 采购汇总聚合 ORDER BY(白名单)。
+ ///
+ private static string BuildPurchaseSummaryAggOrderBy(string kind, string sortField, string sortOrder)
+ {
+ var dir = string.Equals(sortOrder?.Trim(), "asc", StringComparison.OrdinalIgnoreCase)
+ ? "ASC"
+ : "DESC";
+
+ var sumJe = $"SUM({PurSumSignedJeExpr})";
+ var sumSl = $"SUM({PurSumSignedSlExpr})";
+ var avgDj = $"CASE WHEN ABS({sumSl}) < 0.00000001 THEN 0 ELSE {sumJe} / {sumSl} END";
+
+ if (string.Equals(kind, "category", StringComparison.OrdinalIgnoreCase))
+ {
+ var col = (sortField?.Trim()) switch
+ {
+ "分类Id" or "categoryId" => "IFNULL(NULLIF(TRIM(sp.F_Pl), ''), '__NONE__')",
+ "分类名称" or "categoryName" => "IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无')",
+ "数量" or "qty" => sumSl,
+ "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
+ "采购金额" or "purchaseAmount" => sumJe,
+ _ => sumJe
+ };
+ return $"{col} {dir}";
+ }
+
+ if (string.Equals(kind, "brand", StringComparison.OrdinalIgnoreCase))
+ {
+ var col = (sortField?.Trim()) switch
+ {
+ "品牌Id" or "brandId" => "IFNULL(NULLIF(TRIM(sp.F_Pp), ''), '')",
+ "品牌名称" or "brandName" => "IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无')",
+ "数量" or "qty" => sumSl,
+ "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
+ "采购金额" or "purchaseAmount" => sumJe,
+ _ => sumJe
+ };
+ return $"{col} {dir}";
+ }
+
+ if (string.Equals(kind, "productAgg", StringComparison.OrdinalIgnoreCase))
+ {
+ var col = (sortField?.Trim()) switch
+ {
+ "商品Id" or "productId" => "sp.F_Id",
+ "商品编号" or "productCode" => "IFNULL(NULLIF(TRIM(MAX(sp.F_Spbm)), ''), '无')",
+ "商品名称" or "productName" => "IFNULL(NULLIF(TRIM(MAX(sp.F_Spmc)), ''), '无')",
+ "明细分类" or "detailCategory" =>
+ "IFNULL(NULLIF(TRIM(CONCAT_WS(' / ', NULLIF(TRIM(sp.F_Splx1), ''), NULLIF(TRIM(sp.F_Splx2), ''))), ''), '无')",
+ "数量" or "qty" => sumSl,
+ "入库单价" or "inboundPrice" or "unitPrice" => avgDj,
+ "采购金额" or "purchaseAmount" => sumJe,
+ _ => sumJe
+ };
+ return $"{col} {dir}";
+ }
+
+ return $"{sumJe} DESC";
+ }
+
+ ///
+ /// 采购明细 / 线性列表 ORDER BY(仅允许白名单列,防注入);数量金额单价按采购汇总符号口径排序。
+ ///
+ private static string BuildPurchaseSummaryDetailOrderBy(string sortField, string sortOrder)
+ {
+ var dir = string.Equals(sortOrder?.Trim(), "asc", StringComparison.OrdinalIgnoreCase)
+ ? "ASC"
+ : "DESC";
+
+ var agentExpr = "IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无')";
+
+ var col = sortField?.Trim() switch
+ {
+ "单据日期" or "billDate" => "d.djrq",
+ "单据编号" or "billId" => "d.F_Id",
+ "单据类型" or "billType" => "d.djlx",
+ "往来单位" or "contactName" => "IFNULL(NULLIF(TRIM(w.dwmc), ''), '无')",
+ "经手人" or "agentName" => agentExpr,
+ "仓库名称" or "warehouseName" => "IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无')",
+ "商品名称" or "productName" => "IFNULL(NULLIF(TRIM(mx.spmc), ''), '无')",
+ "数量" or "qty" => PurSumSignedSlExpr,
+ "入库单价" or "inboundPrice" or "unitPrice" => PurSumSignedDjExpr,
+ "采购金额" or "purchaseAmount" => PurSumSignedJeExpr,
+ _ => null
+ };
+
+ return string.IsNullOrEmpty(col)
+ ? "d.djrq DESC, d.F_Id DESC, mx.F_Id"
+ : $"{col} {dir}, d.F_Id DESC, mx.F_Id";
+ }
+
///
/// 采购汇总公共 WHERE(需配合含 sp/pl/pp 的 PurchaseSummaryJoinFromSql 使用)。
///
@@ -4644,22 +4821,56 @@ WHERE 1 = 1";
paramList.Add(new SugarParameter("@purSumEndDate", endDate.Date.AddDays(1)));
}
- AddInCondition("d.gys", input.ContactUnit, "purSumContact");
-
- // 经手人:主表 wt_xsckd.jsr 存 BASE_USER.F_Id,按用户主键 IN 筛选(与前端下拉 value 一致)
- AddInCondition("d.jsr", input.Agent, "purSumJsr");
+ whereList.Add("d.djzt = @purSumDjzt");
+ paramList.Add(new SugarParameter("@purSumDjzt", "已审核"));
- AddInCondition("IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)", input.Warehouse, "purSumWh");
-
- var billTypeValues = SplitCsv(input.BillType);
+ var allowedSet = new HashSet(PurSumAllowedDjlx, StringComparer.Ordinal);
+ var billRaw = input.BillType?.Trim();
+ var billAll = string.IsNullOrEmpty(billRaw) || billRaw.Equals("all", StringComparison.OrdinalIgnoreCase);
+ var billRequested = SplitCsv(input.BillType).Select(NormalizePurSumDjlx).Where(s => !string.IsNullOrWhiteSpace(s)).ToList();
+ var billTypeValues = billAll || billRequested.Count == 0
+ ? PurSumAllowedDjlx.ToList()
+ : billRequested.Where(allowedSet.Contains).Distinct().ToList();
if (billTypeValues.Count == 0)
{
- billTypeValues.Add("采购入库单");
- billTypeValues.Add("采购退货单");
+ billTypeValues = PurSumAllowedDjlx.ToList();
}
AddInCondition("d.djlx", string.Join(",", billTypeValues), "purSumDjlx");
+ var contactVals = SplitCsv(input.ContactUnit);
+ if (contactVals.Count > 0)
+ {
+ var gysParts = new List();
+ var khParts = new List();
+ for (var i = 0; i < contactVals.Count; i++)
+ {
+ var pn = $"@purSumContact{i}";
+ gysParts.Add($"d.gys = {pn}");
+ khParts.Add($"d.kh = {pn}");
+ paramList.Add(new SugarParameter(pn, contactVals[i]));
+ }
+
+ whereList.Add($"(({string.Join(" OR ", gysParts)}) OR ({string.Join(" OR ", khParts)}))");
+ }
+
+ var agentVals = SplitCsv(input.Agent);
+ if (agentVals.Count > 0)
+ {
+ var agentParts = new List();
+ for (var i = 0; i < agentVals.Count; i++)
+ {
+ var pn = $"@purSumJsr{i}";
+ agentParts.Add(
+ $"IFNULL(NULLIF(TRIM(d.jsr), ''), IFNULL(NULLIF(TRIM(d.zdr), ''), '')) = {pn}");
+ paramList.Add(new SugarParameter(pn, agentVals[i]));
+ }
+
+ whereList.Add($"({string.Join(" OR ", agentParts)})");
+ }
+
+ AddInCondition("IFNULL(NULLIF(TRIM(mx.rkck), ''), d.rkck)", input.Warehouse, "purSumWh");
+
var productValues = SplitCsv(input.Product);
if (productValues.Count > 0)
{
@@ -4668,7 +4879,8 @@ WHERE 1 = 1";
{
var pEq = $"@purProdEq{i}";
var pLike = $"@purProdLike{i}";
- productConditions.Add($"(mx.spbh = {pEq} OR sp.F_Spbm = {pEq} OR mx.spmc LIKE {pLike} OR IFNULL(sp.F_Spmc, '') LIKE {pLike})");
+ productConditions.Add(
+ $"(mx.spbh = {pEq} OR sp.F_Id = {pEq} OR sp.F_Spbm = {pEq} OR mx.spmc LIKE {pLike} OR IFNULL(sp.F_Spmc, '') LIKE {pLike})");
paramList.Add(new SugarParameter(pEq, productValues[i]));
paramList.Add(new SugarParameter(pLike, $"%{productValues[i]}%"));
}
@@ -4697,7 +4909,7 @@ WHERE 1 = 1";
var spId = !string.IsNullOrWhiteSpace(scopeProductSpId) ? scopeProductSpId.Trim() : (input.ProductSpId ?? string.Empty).Trim();
if (!string.IsNullOrEmpty(spId))
{
- whereList.Add("mx.spbh = @purSumScopeSp");
+ whereList.Add("(mx.spbh = @purSumScopeSp OR sp.F_Id = @purSumScopeSp)");
paramList.Add(new SugarParameter("@purSumScopeSp", spId));
}
@@ -4713,20 +4925,21 @@ WHERE 1 = 1";
{
input ??= new WtPurchaseSummaryQueryInput();
var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, null, null, null);
+ var orderBy = BuildPurchaseSummaryAggOrderBy("category", input.SortField, input.SortOrder);
var sql = $@"
SELECT
IFNULL(NULLIF(TRIM(sp.F_Pl), ''), '__NONE__') AS `分类Id`,
IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `分类名称`,
'无' AS `商品编号`,
IFNULL(NULLIF(TRIM(MAX(pl.F_Plmc)), ''), '无') AS `商品名称`,
- SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,
- CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0
- ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,
- SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额`
+ SUM({PurSumSignedSlExpr}) AS `数量`,
+ CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
+ ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
+ SUM({PurSumSignedJeExpr}) AS `采购金额`
{PurchaseSummaryJoinFromSql}
{whereSql}
GROUP BY sp.F_Pl, pl.F_Plmc
-ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC";
+ORDER BY {orderBy}";
var list = await _db.Ado.GetDataTableAsync(sql, paramList);
return list;
}
@@ -4744,20 +4957,21 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC";
input ??= new WtPurchaseSummaryQueryInput();
var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null);
+ var orderBy = BuildPurchaseSummaryAggOrderBy("brand", input.SortField, input.SortOrder);
var sql = $@"
SELECT
IFNULL(NULLIF(TRIM(sp.F_Pp), ''), '') AS `品牌Id`,
IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `品牌名称`,
'无' AS `商品编号`,
IFNULL(NULLIF(TRIM(MAX(pp.F_Ppmc)), ''), '无') AS `商品名称`,
- SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,
- CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0
- ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,
- SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额`
+ SUM({PurSumSignedSlExpr}) AS `数量`,
+ CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
+ ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
+ SUM({PurSumSignedJeExpr}) AS `采购金额`
{PurchaseSummaryJoinFromSql}
{whereSql}
GROUP BY sp.F_Pp, pp.F_Ppmc
-ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC";
+ORDER BY {orderBy}";
var list = await _db.Ado.GetDataTableAsync(sql, paramList);
return list;
}
@@ -4780,26 +4994,27 @@ ORDER BY SUM(CAST(mx.je AS DECIMAL(18,4))) DESC";
input ??= new WtPurchaseSummaryQueryInput();
var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, brandId, null);
+ var orderBy = BuildPurchaseSummaryAggOrderBy("productAgg", input.SortField, input.SortOrder);
var sql = $@"
SELECT
sp.F_Id AS `商品Id`,
IFNULL(NULLIF(TRIM(MAX(sp.F_Spbm)), ''), '无') AS `商品编号`,
IFNULL(NULLIF(TRIM(MAX(sp.F_Spmc)), ''), '无') AS `商品名称`,
IFNULL(NULLIF(TRIM(CONCAT_WS(' / ', NULLIF(TRIM(sp.F_Splx1), ''), NULLIF(TRIM(sp.F_Splx2), ''))), ''), '无') AS `明细分类`,
- SUM(CAST(mx.sl AS DECIMAL(18,4))) AS `数量`,
- CASE WHEN ABS(SUM(CAST(mx.sl AS DECIMAL(18,4)))) < 0.00000001 THEN 0
- ELSE SUM(CAST(mx.je AS DECIMAL(18,4))) / SUM(CAST(mx.sl AS DECIMAL(18,4))) END AS `入库单价`,
- SUM(CAST(mx.je AS DECIMAL(18,4))) AS `采购金额`
+ SUM({PurSumSignedSlExpr}) AS `数量`,
+ CASE WHEN ABS(SUM({PurSumSignedSlExpr})) < 0.00000001 THEN 0
+ ELSE SUM({PurSumSignedJeExpr}) / SUM({PurSumSignedSlExpr}) END AS `入库单价`,
+ SUM({PurSumSignedJeExpr}) AS `采购金额`
{PurchaseSummaryJoinFromSql}
{whereSql}
GROUP BY sp.F_Id, sp.F_Splx1, sp.F_Splx2
-ORDER BY IFNULL(MAX(sp.F_Spbm), ''), IFNULL(MAX(sp.F_Spmc), '')";
+ORDER BY {orderBy}";
var list = await _db.Ado.GetDataTableAsync(sql, paramList);
return list;
}
///
- /// 商品采购「线性列表」:指定分类下全部采购明细行(最多 2000 条),与明细列表列一致。
+ /// 商品采购「线性列表」:指定分类下全部相关单据明细(分页,与 GetPurchaseSummary 筛选及加减口径一致)。
///
[HttpGet("Actions/GetPurchaseSummaryLinear")]
public async Task GetPurchaseSummaryLinear([FromQuery] string categoryId, [FromQuery] WtPurchaseSummaryQueryInput input = null)
@@ -4811,25 +5026,50 @@ ORDER BY IFNULL(MAX(sp.F_Spbm), ''), IFNULL(MAX(sp.F_Spmc), '')";
input ??= new WtPurchaseSummaryQueryInput();
var (whereSql, paramList) = BuildPurchaseSummaryWhere(input, categoryId, null, null);
- const int maxRows = 2000;
+
+ var page = input.CurrentPage.GetValueOrDefault(1);
+ if (page < 1)
+ {
+ page = 1;
+ }
+
+ // 线性列表单次可拉较大窗口(默认 2000),与历史「最多 2000 条」一致;仍支持分页防一次过大。
+ var pageSize = input.PageSize.GetValueOrDefault(2000);
+ if (pageSize < 1)
+ {
+ pageSize = 2000;
+ }
+
+ if (pageSize > 2000)
+ {
+ pageSize = 2000;
+ }
+
+ var offset = (page - 1) * pageSize;
+
+ var countSql = "SELECT COUNT(*) " + PurchaseSummaryJoinFromSql + whereSql;
+ var totalObj = await _db.Ado.GetScalarAsync(countSql, paramList);
+ var total = Convert.ToInt32(totalObj ?? 0);
+
+ var orderByLin = BuildPurchaseSummaryDetailOrderBy(input.SortField, input.SortOrder);
var sql = $@"
SELECT
DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`,
d.F_Id AS `单据编号`,
d.djlx AS `单据类型`,
IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`,
- IFNULL(NULLIF(TRIM(u.F_RealName), ''), IFNULL(NULLIF(TRIM(d.jsr), ''), '无')) AS `经手人`,
+ IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无') AS `经手人`,
IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`,
IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`,
- CAST(mx.sl AS DECIMAL(18,4)) AS `数量`,
- mx.dj AS `入库单价`,
- mx.je AS `采购金额`
+ {PurSumSignedSlExpr} AS `数量`,
+ {PurSumSignedDjExpr} AS `入库单价`,
+ {PurSumSignedJeExpr} AS `采购金额`
{PurchaseSummaryJoinFromSql}
{whereSql}
-ORDER BY d.djrq DESC, d.F_Id, mx.F_Id
-LIMIT {maxRows}";
+ORDER BY {orderByLin}
+LIMIT {offset}, {pageSize}";
var list = await _db.Ado.GetDataTableAsync(sql, paramList);
- return list;
+ return new { list, total };
}
///
@@ -4866,21 +5106,22 @@ LIMIT {maxRows}";
var totalObj = await _db.Ado.GetScalarAsync(countSql, paramList);
var total = Convert.ToInt32(totalObj ?? 0);
+ var orderByDetail = BuildPurchaseSummaryDetailOrderBy(input.SortField, input.SortOrder);
var listSql = $@"
SELECT
DATE_FORMAT(d.djrq, '%Y-%m-%d') AS `单据日期`,
d.F_Id AS `单据编号`,
d.djlx AS `单据类型`,
IFNULL(NULLIF(TRIM(w.dwmc), ''), '无') AS `往来单位`,
- IFNULL(NULLIF(TRIM(u.F_RealName), ''), IFNULL(NULLIF(TRIM(d.jsr), ''), '无')) AS `经手人`,
+ IFNULL(NULLIF(TRIM(u.F_RealName), ''), '无') AS `经手人`,
IFNULL(NULLIF(TRIM(ck.F_mdmc), ''), '无') AS `仓库名称`,
IFNULL(NULLIF(TRIM(mx.spmc), ''), '无') AS `商品名称`,
- CAST(mx.sl AS DECIMAL(18,4)) AS `数量`,
- mx.dj AS `入库单价`,
- mx.je AS `采购金额`
+ {PurSumSignedSlExpr} AS `数量`,
+ {PurSumSignedDjExpr} AS `入库单价`,
+ {PurSumSignedJeExpr} AS `采购金额`
{PurchaseSummaryJoinFromSql}
{whereSql}
-ORDER BY d.djrq DESC, d.F_Id, mx.F_Id
+ORDER BY {orderByDetail}
LIMIT {offset}, {pageSize}";
var list = await _db.Ado.GetDataTableAsync(listSql, paramList);
return new { list, total };
@@ -5744,11 +5985,13 @@ LIMIT {offset}, {pageSize}";
}
///
- /// 通用审核接口:自动根据单据的 djlx 匹配审核配置;编号以 TJD 开头时优先判断是否为 wt_xsckd 同价调拨单,否则走商品调价单审批流
+ /// 通用审核接口:自动根据单据的 djlx 匹配审核配置;编号以 TJD 开头时优先判断是否为 wt_xsckd 同价调拨单,否则走商品调价单审批流;wt_fysrd 其他收入单走费用收入单审批流
///
[HttpPost("ApproveGeneric/{id}")]
public async Task ApproveGeneric(string id, [FromBody] WtApprovalRemarkInput input)
{
+ if (!string.IsNullOrEmpty(id) && await _db.Queryable().AnyAsync(x => x.Id == id))
+ return await _fysrdWorkflowHelper.ApproveAsync(id, input?.remark);
if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal))
{
var isSamePriceTransfer = await _db.Queryable()
@@ -5768,6 +6011,8 @@ LIMIT {offset}, {pageSize}";
[HttpPost("RejectGeneric/{id}")]
public async Task RejectGeneric(string id, [FromBody] WtApprovalRemarkInput input)
{
+ if (!string.IsNullOrEmpty(id) && await _db.Queryable().AnyAsync(x => x.Id == id))
+ return await _fysrdWorkflowHelper.RejectAsync(id, input?.remark);
if (!string.IsNullOrEmpty(id) && id.StartsWith("TJD", StringComparison.Ordinal))
{
var isSamePriceTransfer = await _db.Queryable()
@@ -5942,6 +6187,9 @@ LIMIT {offset}, {pageSize}";
{
try
{
+ if (!string.IsNullOrEmpty(id) && await _db.Queryable().AnyAsync(x => x.Id == id))
+ return await _fysrdWorkflowHelper.ReverseAsync(id);
+
var entity = await _db.Queryable().Where(x => x.Id == id).FirstAsync();
if (entity == null)
return new { success = false, message = "单据不存在" };
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtYskzjjsService.cs b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtYskzjjsService.cs
index ff67f4a..d0cc931 100644
--- a/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtYskzjjsService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/Extend/NCC.Extend/WtYskzjjsService.cs
@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NCC.Extend.Entitys;
+using NCC.Extend.Entitys.Dto;
using NCC.Extend.Entitys.Dto.WtYskzjjs;
using Yitter.IdGenerator;
using NCC.Common.Helper;
@@ -40,6 +41,7 @@ namespace NCC.Extend.WtYskzjjs
private readonly IUserManager _userManager;
private readonly WtDjzyService _wtDjzyService;
private readonly WtBillSummaryService _billSummaryService;
+ private static bool _yskAuditColsEnsured;
///
/// 初始化一个类型的新实例
@@ -60,6 +62,39 @@ namespace NCC.Extend.WtYskzjjs
}
///
+ /// 确保 wt_yskzjjs 具备审批相关列(避免线上旧库缺列导致 Unknown column)。
+ ///
+ private void EnsureYskzjjsAuditColumns()
+ {
+ if (_yskAuditColsEnsured) return;
+ lock (typeof(WtYskzjjsService))
+ {
+ if (_yskAuditColsEnsured) return;
+ try
+ {
+ if (!_db.DbMaintenance.IsAnyTable("wt_yskzjjs")) { _yskAuditColsEnsured = true; return; }
+ var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_yskzjjs");
+ var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
+ if (!names.Contains("djzt"))
+ _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
+ if (!names.Contains("spbz"))
+ _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
+ if (!names.Contains("shr"))
+ _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
+ if (!names.Contains("shr1"))
+ _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
+ if (!names.Contains("shr2"))
+ _db.Ado.ExecuteCommand("ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"EnsureYskzjjsAuditColumns: {ex.Message}");
+ }
+ _yskAuditColsEnsured = true;
+ }
+ }
+
+ ///
/// 获取应收款增加减少
///
/// 参数
@@ -67,12 +102,14 @@ namespace NCC.Extend.WtYskzjjs
[HttpGet("{id}")]
public async Task GetInfo(string id)
{
+ EnsureYskzjjsAuditColumns();
var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
var output = entity.Adapt();
var wtYskzjjsMxList = await _db.Queryable().Where(w => w.Djbh == entity.Id).ToListAsync();
output.wtYskzjjsMxList = wtYskzjjsMxList.Adapt>();
output.billZy = await _billSummaryService.ComputeWtYskzjjsZyByBillIdAsync(entity.Id);
+ output.wldwmc = await ResolveWldwMcAsync(output.wldw);
return output;
}
@@ -84,6 +121,7 @@ namespace NCC.Extend.WtYskzjjs
[HttpGet("")]
public async Task GetList([FromQuery] WtYskzjjsListQueryInput input)
{
+ EnsureYskzjjsAuditColumns();
var sidx = input.sidx == null ? "id" : input.sidx;
List queryDjrq = input.djrq != null ? input.djrq.Split(',').ToObeject>() : null;
DateTime? startDjrq = queryDjrq != null ? Ext.GetDateTime(queryDjrq.First()) : null;
@@ -113,6 +151,10 @@ namespace NCC.Extend.WtYskzjjs
djrq = d.Djrq,
jsr = d.Jsr,
djzt = d.Djzt,
+ spbz = d.Spbz,
+ shr = d.Shr,
+ shr1 = d.Shr1,
+ shr2 = d.Shr2,
dyddh = d.Dyddh,
hysjh = d.Hysjh,
fkzh = d.Fkzh,
@@ -132,6 +174,10 @@ namespace NCC.Extend.WtYskzjjs
djrq = it.djrq,
jsr = it.jsr,
djzt = it.djzt,
+ spbz = it.spbz,
+ shr = it.shr,
+ shr1 = it.shr1,
+ shr2 = it.shr2,
dyddh = it.dyddh,
hysjh = it.hysjh,
fkzh = it.fkzh,
@@ -158,9 +204,12 @@ namespace NCC.Extend.WtYskzjjs
[HttpPost("")]
public async Task Create([FromBody] WtYskzjjsCrInput input)
{
+ EnsureYskzjjsAuditColumns();
var userInfo = await _userManager.GetUserInfo();
NormalizeDjlxForQt(input);
NormalizeDjlxForTransfer(input);
+ SyncMxKhFromWldw(input);
+ ThrowIfInvalidForSubmit(input, input?.djzt);
var entity = input.Adapt();
entity.Id = await BuildCreateBillId(input);
try
@@ -264,6 +313,10 @@ namespace NCC.Extend.WtYskzjjs
djrq = d.Djrq,
jsr = d.Jsr,
djzt = d.Djzt,
+ spbz = d.Spbz,
+ shr = d.Shr,
+ shr1 = d.Shr1,
+ shr2 = d.Shr2,
dyddh = d.Dyddh,
hysjh = d.Hysjh,
fkzh = d.Fkzh,
@@ -283,6 +336,10 @@ namespace NCC.Extend.WtYskzjjs
djrq = it.djrq,
jsr = it.jsr,
djzt = it.djzt,
+ spbz = it.spbz,
+ shr = it.shr,
+ shr1 = it.shr1,
+ shr2 = it.shr2,
dyddh = it.dyddh,
hysjh = it.hysjh,
fkzh = it.fkzh,
@@ -389,8 +446,11 @@ namespace NCC.Extend.WtYskzjjs
[HttpPut("{id}")]
public async Task Update(string id, [FromBody] WtYskzjjsUpInput input)
{
+ EnsureYskzjjsAuditColumns();
NormalizeDjlxForQt(input);
NormalizeDjlxForTransfer(input);
+ SyncMxKhFromWldw(input);
+ ThrowIfInvalidForSubmit(input, input?.djzt);
var entity = input.Adapt();
try
{
@@ -461,7 +521,7 @@ namespace NCC.Extend.WtYskzjjs
}
///
- /// 兼容 qt 页面对 djlx 的历史传值:当明细为“其他收入单”时统一落库为“其他应收款”。
+ /// 兼容 qt 页面对 djlx 的历史传值:当明细为“其他收入单”时统一落库为“其他应收单”。
/// 仅修正 qt 场景,不影响其它单据类型。
///
/// 新增/更新入参
@@ -478,9 +538,10 @@ namespace NCC.Extend.WtYskzjjs
if (string.IsNullOrWhiteSpace(input.djlx) ||
input.djlx.Trim().Equals("应收款减少", StringComparison.Ordinal) ||
- input.djlx.Trim().Equals("其他收入单", StringComparison.Ordinal))
+ input.djlx.Trim().Equals("其他收入单", StringComparison.Ordinal) ||
+ input.djlx.Trim().Equals("其他应收款", StringComparison.Ordinal))
{
- input.djlx = "其他应收款";
+ input.djlx = "其他应收单";
}
}
@@ -598,5 +659,326 @@ namespace NCC.Extend.WtYskzjjs
await _billSummaryService.EnrichWtYskzjjsListBillZyAsync(rowList);
}
+
+ ///
+ /// 主表往来单位写入明细客户字段,便于列表按 kh 筛选与详情列展示。
+ ///
+ private static void SyncMxKhFromWldw(WtYskzjjsCrInput input)
+ {
+ if (input == null || string.IsNullOrWhiteSpace(input.wldw) || input.wtYskzjjsMxList == null)
+ return;
+ var w = input.wldw.Trim();
+ foreach (var mx in input.wtYskzjjsMxList.Where(m => m != null))
+ {
+ if (string.IsNullOrWhiteSpace(mx.kh))
+ mx.kh = w;
+ }
+ }
+
+ private async Task ResolveWldwMcAsync(string wldwId)
+ {
+ if (string.IsNullOrWhiteSpace(wldwId))
+ return "无";
+ var dwmc = (await _db.Queryable().Where(x => x.Id == wldwId).Select(x => x.Dwmc).ToListAsync()).FirstOrDefault();
+ if (!string.IsNullOrWhiteSpace(dwmc))
+ return dwmc;
+ var xm = (await _db.Queryable().Where(x => x.Id == wldwId).Select(x => x.Xm).ToListAsync()).FirstOrDefault();
+ if (!string.IsNullOrWhiteSpace(xm))
+ return xm;
+ var gysmc = (await _db.Queryable().Where(x => x.Id == wldwId).Select(x => x.Gysmc).ToListAsync()).FirstOrDefault();
+ if (!string.IsNullOrWhiteSpace(gysmc))
+ return gysmc;
+ return "无";
+ }
+
+ private static void AppendApprovalSpbzLineYsk(WtYskzjjsEntity entity, string actionTag, string remark)
+ {
+ var r = (remark ?? "").Trim();
+ if (string.IsNullOrEmpty(r)) return;
+ var line = string.IsNullOrEmpty(actionTag) ? r : $"[{actionTag}] {r}";
+ var existing = entity.Spbz?.TrimEnd() ?? "";
+ entity.Spbz = string.IsNullOrWhiteSpace(existing) ? line : existing + "\n" + line;
+ }
+
+ private static bool IsUserInAuditConfigYsk(string configValue, string userId, string userAccount = null)
+ {
+ if (string.IsNullOrWhiteSpace(configValue))
+ return false;
+ if (string.IsNullOrWhiteSpace(userId) && string.IsNullOrWhiteSpace(userAccount))
+ return false;
+
+ var users = configValue
+ .Split(new[] { ',', ',', ';', ';', '|', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim())
+ .Where(x => !string.IsNullOrEmpty(x))
+ .ToList();
+
+ return users.Any(x =>
+ (!string.IsNullOrWhiteSpace(userId) && string.Equals(x, userId.Trim(), StringComparison.OrdinalIgnoreCase))
+ || (!string.IsNullOrWhiteSpace(userAccount) && string.Equals(x, userAccount.Trim(), StringComparison.OrdinalIgnoreCase)));
+ }
+
+ ///
+ /// 提交审核:草稿 / 空 / 审核不通过 → 待审核
+ ///
+ [HttpPost("Actions/SubmitForAudit/{id}")]
+ public async Task SubmitForAudit(string id)
+ {
+ EnsureYskzjjsAuditColumns();
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ return new { success = false, message = "单据不存在" };
+ var z = entity.Djzt?.Trim() ?? "";
+ if (z != "草稿" && z != "审核不通过" && !string.IsNullOrEmpty(z))
+ return new { success = false, message = $"当前状态「{entity.Djzt}」不可提交审核" };
+
+ // 提交审核前做必填校验(备注除外)
+ var mxList = await _db.Queryable().Where(x => x.Djbh == entity.Id).ToListAsync();
+ var inputLike = new WtYskzjjsCrInput
+ {
+ id = entity.Id,
+ djlx = entity.Djlx,
+ djrq = entity.Djrq,
+ jsr = entity.Jsr,
+ wldw = entity.Wldw,
+ djzt = "待审核",
+ fkzh = entity.Fkzh,
+ fkje = entity.Fkje,
+ skzh = entity.Skzh,
+ skje = entity.Skje,
+ zy = entity.Zy,
+ wtYskzjjsMxList = mxList?.Select(m => new WtYskzjjsMxCrInput
+ {
+ srxmmc = m.Srxmmc,
+ je = m.Je,
+ bz = m.Bz,
+ mxlx = m.Mxlx,
+ kh = m.Kh,
+ zhbh = m.Zhbh
+ }).ToList()
+ };
+ try
+ {
+ ThrowIfInvalidForSubmit(inputLike, "待审核");
+ }
+ catch (Exception ex)
+ {
+ return new { success = false, message = ex.Message };
+ }
+
+ entity.Djzt = "待审核";
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt }).ExecuteCommandAsync();
+ return new { success = true, message = "已提交审核" };
+ }
+
+ ///
+ /// 除备注外必填:仅在提交审核/非草稿状态强制校验;草稿可放宽。
+ ///
+ private static void ThrowIfInvalidForSubmit(WtYskzjjsCrInput input, string djzt)
+ {
+ if (input == null) throw NCCException.Oh("参数不能为空");
+ var z = (djzt ?? input.djzt ?? "").Trim();
+ if (string.IsNullOrEmpty(z) || z == "草稿") return;
+
+ if (input.djrq == null) throw NCCException.Oh("请选择单据日期");
+ if (string.IsNullOrWhiteSpace(input.jsr)) throw NCCException.Oh("请选择经手人");
+ if (string.IsNullOrWhiteSpace(input.wldw)) throw NCCException.Oh("请选择往来单位");
+ if (string.IsNullOrWhiteSpace(input.skzh)) throw NCCException.Oh("请选择收款账户");
+ if (input.skje <= 0) throw NCCException.Oh("请填写明细金额,收款金额须大于 0");
+ if (input.wtYskzjjsMxList == null || input.wtYskzjjsMxList.Count == 0) throw NCCException.Oh("请至少添加一条明细");
+
+ for (var i = 0; i < input.wtYskzjjsMxList.Count; i++)
+ {
+ var row = input.wtYskzjjsMxList[i];
+ if (row == null) throw NCCException.Oh($"第 {i + 1} 行明细不能为空");
+ if (string.IsNullOrWhiteSpace(row.srxmmc)) throw NCCException.Oh($"第 {i + 1} 行收入项目为必填");
+ if (row.je <= 0) throw NCCException.Oh($"第 {i + 1} 行原币金额为必填且须大于 0");
+ }
+ }
+
+ ///
+ /// 撤回:待审核或一级已审 → 草稿
+ ///
+ [HttpPost("Actions/WithdrawAudit/{id}")]
+ public async Task WithdrawAudit(string id)
+ {
+ EnsureYskzjjsAuditColumns();
+ var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
+ if (entity == null)
+ return new { success = false, message = "单据不存在" };
+ var z = entity.Djzt?.Trim() ?? "";
+ if (z != "待审核" && z != "一级已审" && z != "待二级" && z != "一级已审/待二级")
+ return new { success = false, message = $"当前状态「{entity.Djzt}」不可撤回" };
+
+ entity.Djzt = "草稿";
+ entity.Shr1 = null;
+ entity.Shr2 = null;
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Shr2 }).ExecuteCommandAsync();
+ return new { success = true, message = "已撤回为草稿" };
+ }
+
+ ///
+ /// 审核通过(按 wt_shrysz.djmc 与主表 djlx 匹配,支持一级/两级)
+ ///
+ [HttpPost("Actions/Approve/{id}")]
+ public async Task Approve(string id, [FromBody] WtApprovalRemarkInput input = null)
+ {
+ EnsureYskzjjsAuditColumns();
+ return await ApproveYskzjjsDocumentAsync(id, input?.remark);
+ }
+
+ ///
+ /// 审核不通过(审批备注写入 spbz)
+ ///
+ [HttpPost("Actions/Reject/{id}")]
+ public async Task Reject(string id, [FromBody] WtApprovalRemarkInput input = null)
+ {
+ EnsureYskzjjsAuditColumns();
+ return await RejectYskzjjsDocumentAsync(id, input?.remark);
+ }
+
+ private async Task ApproveYskzjjsDocumentAsync(string id, string approvalRemark)
+ {
+ try
+ {
+ _db.BeginTran();
+ var entity = await _db.Queryable().Where(x => x.Id == id).FirstAsync();
+ if (entity == null)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "单据不存在" };
+ }
+
+ if (entity.Djzt == "已审核")
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "该单据已经审核,无需重复审核" };
+ }
+
+ var userInfo = await _userManager.GetUserInfo();
+ var userId = userInfo?.userId ?? "";
+ var userAccount = userInfo?.userAccount?.Trim();
+ if (string.IsNullOrEmpty(userAccount))
+ userAccount = _userManager.Account?.Trim();
+
+ var djmc = entity.Djlx?.Trim() ?? "";
+ var approvalConfig = await _db.Queryable()
+ .Where(c => c.Djmc == djmc)
+ .FirstAsync();
+
+ var configShr1 = approvalConfig?.Shr1;
+ var configShr2 = approvalConfig?.Shr2;
+ var hasTwoLevel = !string.IsNullOrEmpty(configShr1) && !string.IsNullOrEmpty(configShr2);
+
+ if (entity.Djzt == "待审核" || string.IsNullOrEmpty(entity.Djzt))
+ {
+ if (hasTwoLevel)
+ {
+ entity.Djzt = "一级已审";
+ entity.Shr1 = userId;
+ AppendApprovalSpbzLineYsk(entity, "一级通过", approvalRemark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr1, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "一级审核通过,等待二级审核" };
+ }
+
+ entity.Djzt = "已审核";
+ entity.Shr = userId;
+ entity.Shr1 = userId;
+ AppendApprovalSpbzLineYsk(entity, "审核通过", approvalRemark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr1, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "审核通过" };
+ }
+
+ if (entity.Djzt == "一级已审" || entity.Djzt == "一级已审/待二级" || entity.Djzt == "待二级")
+ {
+ if (!hasTwoLevel)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "未配置两级审核,当前状态不允许二级操作" };
+ }
+ if (string.IsNullOrWhiteSpace(configShr2))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "未配置二级审核人员" };
+ }
+ if (!IsUserInAuditConfigYsk(configShr2, userId, userAccount))
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "当前用户不在二级审核人员范围内" };
+ }
+
+ entity.Djzt = "已审核";
+ entity.Shr2 = userId;
+ entity.Shr = userId;
+ AppendApprovalSpbzLineYsk(entity, "二级通过", approvalRemark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Shr, x.Shr2, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "二级审核通过,审核完成" };
+ }
+
+ _db.RollbackTran();
+ return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核" };
+ }
+ catch (Exception ex)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = $"审核失败: {ex.Message}" };
+ }
+ }
+
+ private async Task RejectYskzjjsDocumentAsync(string id, string approvalRemark)
+ {
+ try
+ {
+ _db.BeginTran();
+ var entity = await _db.Queryable().Where(x => x.Id == id).FirstAsync();
+ if (entity == null)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "单据不存在" };
+ }
+
+ if (entity.Djzt == "已审核")
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "该单据已审核通过" };
+ }
+
+ if (entity.Djzt == "审核不通过")
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "该单据已是审核不通过状态" };
+ }
+
+ if (entity.Djzt == "草稿")
+ {
+ _db.RollbackTran();
+ return new { success = false, message = "草稿状态请直接修改或删除单据" };
+ }
+
+ var djztTrim = entity.Djzt?.Trim() ?? "";
+ var level1 = djztTrim == "待审核" || string.IsNullOrEmpty(djztTrim);
+ var level2 = entity.Djzt == "一级已审" || entity.Djzt == "待二级" || entity.Djzt == "一级已审/待二级";
+
+ if (!level1 && !level2)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = $"当前单据状态「{entity.Djzt}」不允许审核不通过" };
+ }
+
+ entity.Djzt = "审核不通过";
+ AppendApprovalSpbzLineYsk(entity, "审核不通过", approvalRemark);
+ await _db.Updateable(entity).UpdateColumns(x => new { x.Djzt, x.Spbz }).ExecuteCommandAsync();
+ _db.CommitTran();
+ return new { success = true, message = "已标记审核不通过" };
+ }
+ catch (Exception ex)
+ {
+ _db.RollbackTran();
+ return new { success = false, message = $"操作失败: {ex.Message}" };
+ }
+ }
}
}
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/OAuth/NCC.OAuth/Service/OAuthService.cs b/Antis.Erp.Plat/netcore/src/Modularity/OAuth/NCC.OAuth/Service/OAuthService.cs
index 77b6147..0f1b8b4 100755
--- a/Antis.Erp.Plat/netcore/src/Modularity/OAuth/NCC.OAuth/Service/OAuthService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/OAuth/NCC.OAuth/Service/OAuthService.cs
@@ -148,7 +148,11 @@ namespace NCC.OAuth.Service
bool isMoble = false;
if (input.account == user.MobilePhone) isMoble = true;
//获取加密后的密码
- var encryptPasswod = MD5Encryption.Encrypt(input.password + user.Secretkey);
+ var pwd = (input.password ?? "").Trim();
+ // 兼容:前端既可能传 MD5(明文),也可能直接传明文(如收银台)。
+ // 系统入库口径为:MD5(MD5(明文) + Secretkey)
+ var md5Once = NormalizePasswordToMd5Once(pwd);
+ var encryptPasswod = MD5Encryption.Encrypt(md5Once + user.Secretkey);
var userAnyPwd = await _userService.GetInfoByLogin(input.account, encryptPasswod, isMoble);
_ = userAnyPwd ?? throw NCCException.Oh(ErrorCode.D1000);
@@ -597,7 +601,9 @@ namespace NCC.OAuth.Service
var secretkey = (await _userService.GetInfoByAccount(input.account)).Secretkey;
//获取加密后的密码
- var encryptPasswod = MD5Encryption.Encrypt(input.password + secretkey);
+ var pwd = (input.password ?? "").Trim();
+ var md5Once = NormalizePasswordToMd5Once(pwd);
+ var encryptPasswod = MD5Encryption.Encrypt(md5Once + secretkey);
bool isMoble = false;
if (input.account == users.MobilePhone) isMoble = true;
@@ -606,6 +612,25 @@ namespace NCC.OAuth.Service
_ = user ?? throw NCCException.Oh(ErrorCode.D1000);
}
+ private static bool LooksLikeMd5(string s)
+ {
+ if (string.IsNullOrEmpty(s) || s.Length != 32) return false;
+ foreach (var ch in s)
+ {
+ var isHex = (ch >= '0' && ch <= '9')
+ || (ch >= 'a' && ch <= 'f')
+ || (ch >= 'A' && ch <= 'F');
+ if (!isHex) return false;
+ }
+ return true;
+ }
+
+ private static string NormalizePasswordToMd5Once(string passwordOrMd5)
+ {
+ var p = (passwordOrMd5 ?? "").Trim();
+ return LooksLikeMd5(p) ? p : MD5Encryption.Encrypt(p);
+ }
+
///
/// 获取当前登录用户信息
///
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System.Entitys/Dto/Permission/User/UserCrInput.cs b/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System.Entitys/Dto/Permission/User/UserCrInput.cs
index 2e8ee2c..ebac9b9 100755
--- a/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System.Entitys/Dto/Permission/User/UserCrInput.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System.Entitys/Dto/Permission/User/UserCrInput.cs
@@ -133,8 +133,16 @@ namespace NCC.System.Entitys.Dto.Permission.User
/// 排序
///
public long? sortCode { get; set; }
-
-
+
+ ///
+ /// 初始密码(明文,可选)。为空或未传时仍按系统默认密码规则生成(与历史行为一致)。
+ ///
+ ///
+ /// 非空时按与登录校验一致的口径入库:MD5(MD5(明文) + 用户 Secretkey)。
+ /// (登录请求中的 password 通常为 MD5(明文),服务端再拼接 Secretkey 后做最终 MD5)
+ ///
+ public string password { get; set; }
+
///
/// 门店信息
///
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersService.cs b/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersService.cs
index e8a6f28..862005d 100755
--- a/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/System/NCC.System/Service/Permission/UsersService.cs
@@ -262,10 +262,31 @@ namespace NCC.System.Service.Permission
}
///
- /// 新建
+ /// 新建用户
///
- /// 参数
- ///
+ ///
+ /// 可选传入 password(明文)。非空时按与「重置密码」相同的规则写入库,便于新建用户后直接以明文登录;
+ /// 未传或为空时仍使用系统默认密码哈希(兼容旧前端)。
+ ///
+ /// 示例请求:
+ /// ```json
+ /// {
+ /// "account": "zhangsan",
+ /// "password": "PlainTextPwd",
+ /// "realName": "张三",
+ /// "organizeId": "orgId",
+ /// "headIcon": "/api/file/Image/userAvatar/001.png"
+ /// }
+ /// ```
+ ///
+ /// 参数说明:
+ /// - password: 可选,非空时为初始明文密码
+ ///
+ /// 创建用户参数
+ /// 无返回体,成功即完成创建
+ /// 创建成功
+ /// 参数或业务校验失败
+ /// 服务器内部错误
[HttpPost("")]
public async Task Create([FromBody] UserCrInput input)
{
@@ -294,7 +315,12 @@ namespace NCC.System.Service.Permission
entity.Birthday = input.birthday.IsNullOrEmpty() ? DateTime.Now : Ext.GetDateTime(input.birthday.ToString());
entity.QuickQuery = PinyinUtil.PinyinString(input.realName);
entity.Secretkey = Guid.NewGuid().ToString();
- entity.Password = MD5Encryption.Encrypt(MD5Encryption.Encrypt(CommonConst.DEFAULT_PASSWORD) + entity.Secretkey);
+ if (string.IsNullOrWhiteSpace(input.password))
+ {
+ throw NCCException.Oh("请输入密码");
+ }
+ entity.Password = HashPasswordForStorage(input.password.Trim(), entity.Secretkey);
+
entity.Mdxx = input.mdxx;
var headIcon = input.headIcon.Split('/').ToList().Last();
if (string.IsNullOrEmpty(headIcon))
@@ -680,7 +706,7 @@ namespace NCC.System.Service.Permission
_ = entity ?? throw NCCException.Oh(ErrorCode.D1002);
- var password = MD5Encryption.Encrypt(input.userPassword + entity.Secretkey);
+ var password = HashPasswordForStorage(input.userPassword, entity.Secretkey);
var isOk = await _userRepository.Context.Updateable().SetColumns(it => new UserEntity()
{
@@ -798,7 +824,9 @@ namespace NCC.System.Service.Permission
[NonAction]
public async Task GetInfoByAccount(string account)
{
- return await _userRepository.FirstOrDefaultAsync(u => u.Account == account || u.MobilePhone == account && u.DeleteMark == null);
+ // 注意:必须同时过滤 DeleteMark,否则同账号存在“已删除历史记录”时会取错 Secretkey,导致登录永远校验失败
+ return await _userRepository.FirstOrDefaultAsync(u =>
+ (u.Account == account || u.MobilePhone == account) && u.DeleteMark == null);
}
///
@@ -930,6 +958,38 @@ namespace NCC.System.Service.Permission
var ids = PositionIds.Split(",");
return await _positionRepository.Entities.In(it => it.Id, ids).Select(it => new { id = it.Id, name = it.FullName }).MergeTable().Select().ToListAsync();
}
+
+ ///
+ /// 用户密码入库哈希(与重置密码、个人修改密码、OAuth 登录一致)。
+ ///
+ /// 明文密码
+ /// 用户秘钥
+ /// 可写入 的哈希值
+ private static string HashPasswordForStorage(string passwordOrMd5, string secretkey)
+ {
+ // 系统密码入库口径(与 OAuth 登录一致):
+ // - 登录/改密/重置密码:前端通常传 MD5(明文),服务端再做 MD5(MD5值 + Secretkey)
+ // - 新建用户:本次新增支持传“明文 password”,这里自动先做一层 MD5,再按同口径入库
+ var p = (passwordOrMd5 ?? "").Trim();
+ if (string.IsNullOrEmpty(p)) return MD5Encryption.Encrypt(MD5Encryption.Encrypt(CommonConst.DEFAULT_PASSWORD) + secretkey);
+
+ static bool LooksLikeMd5(string s)
+ {
+ if (s.Length != 32) return false;
+ foreach (var ch in s)
+ {
+ var isHex = (ch >= '0' && ch <= '9')
+ || (ch >= 'a' && ch <= 'f')
+ || (ch >= 'A' && ch <= 'F');
+ if (!isHex) return false;
+ }
+ return true;
+ }
+
+ var md5Once = LooksLikeMd5(p) ? p : MD5Encryption.Encrypt(p);
+ return MD5Encryption.Encrypt(md5Once + secretkey);
+ }
+
#endregion
}
}
\ No newline at end of file
diff --git a/Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs b/Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs
index 1ae2c10..6c3b6b9 100755
--- a/Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs
+++ b/Antis.Erp.Plat/netcore/src/Modularity/VisualDev/NCC.VisualDev/DashboardService.cs
@@ -26,6 +26,7 @@ namespace NCC.VisualDev
private readonly IUserManager _userManager;
private readonly IDictionaryDataService _dictionaryDataService;
private readonly SqlSugarScope _db;
+ private static bool _wtFysrdAuditColsEnsured;
///
/// 初始化一个类型的新实例
@@ -344,6 +345,8 @@ namespace NCC.VisualDev
///
private async Task> GetExtendBillTodoListAsync(string userId, string userAccount)
{
+ EnsureWtFysrdAuditColumnsForTodo();
+ EnsureWtYskzjjsAuditColumnsForTodo();
var list = new List();
list.AddRange(await QueryWtXsckdAuditTodosAsync("同价调拨单", userId, userAccount, null));
// 仅单据来源为「后台」或未标记(ly 空)的销售出库单进待办;备注「抖音订单:」同后端免审规则
@@ -361,12 +364,160 @@ namespace NCC.VisualDev
list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销退货单", userId, userAccount, null));
list.AddRange(await QueryWtXsckdAuditTodosAsync("委托代销结算单", userId, userAccount, null));
list.AddRange(await QueryWtTjdAuditTodosAsync(userId, userAccount));
+ list.AddRange(await QueryWtFysrdQtsrAuditTodosAsync(userId, userAccount));
+ list.AddRange(await QueryWtYskzjjsQtAuditTodosAsync(userId, userAccount));
return list.OrderByDescending(x => x.creatorTime).ToList();
}
+ /// 与 wt_shrysz.djmc、WtFysrdWorkflowHelper.BillName 一致
+ private const string FysrdQtsrTodoBillName = "其他收入单";
+
+ ///
+ /// 待办 SQL 依赖列存在;与 Extend 内 WtFysrdWorkflowHelper.EnsureFysrdAuditColumns 保持一致
+ ///
+ private void EnsureWtFysrdAuditColumnsForTodo()
+ {
+ if (_wtFysrdAuditColsEnsured) return;
+ lock (typeof(DashboardService))
+ {
+ if (_wtFysrdAuditColsEnsured) return;
+ try
+ {
+ if (!_db.DbMaintenance.IsAnyTable("wt_fysrd")) { _wtFysrdAuditColsEnsured = true; return; }
+ var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_fysrd");
+ var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
+ if (!names.Contains("djzt"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
+ if (!names.Contains("spbz"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
+ if (!names.Contains("shr"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
+ if (!names.Contains("shr1"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
+ if (!names.Contains("shr2"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
+ if (!names.Contains("wldw"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_fysrd` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位'");
+ }
+ catch (Exception ex) { Console.WriteLine($"EnsureWtFysrdAuditColumnsForTodo: {ex.Message}"); }
+ _wtFysrdAuditColsEnsured = true;
+ }
+ }
+
/// 与 wt_shrysz.djmc、WtTjdWorkflowHelper.BillName 一致
private const string TjdTodoBillName = "商品调价单";
+ /// 与 wt_shrysz.djmc、其他应收单 djlx 一致
+ private const string YskzjjsQtTodoBillName = "其他应收单";
+
+ private static bool _wtYskzjjsAuditColsEnsured;
+
+ ///
+ /// 待办 SQL 依赖列存在;与 Extend 内 WtYskzjjsService 的列映射保持一致
+ ///
+ private void EnsureWtYskzjjsAuditColumnsForTodo()
+ {
+ if (_wtYskzjjsAuditColsEnsured) return;
+ lock (typeof(DashboardService))
+ {
+ if (_wtYskzjjsAuditColsEnsured) return;
+ try
+ {
+ if (!_db.DbMaintenance.IsAnyTable("wt_yskzjjs")) { _wtYskzjjsAuditColsEnsured = true; return; }
+ var cols = _db.DbMaintenance.GetColumnInfosByTableName("wt_yskzjjs");
+ var names = cols.Select(c => c.DbColumnName.ToLowerInvariant()).ToHashSet();
+ if (!names.Contains("djzt"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djzt` varchar(32) NULL COMMENT '单据状态'");
+ if (!names.Contains("spbz"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `spbz` text NULL COMMENT '审批备注'");
+ if (!names.Contains("shr"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr` varchar(64) NULL COMMENT '审核人'");
+ if (!names.Contains("shr1"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr1` varchar(500) NULL COMMENT '一级审核人'");
+ if (!names.Contains("shr2"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `shr2` varchar(500) NULL COMMENT '二级审核人'");
+ if (!names.Contains("wldw"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `wldw` varchar(64) NULL COMMENT '往来单位'");
+ if (!names.Contains("djlx"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djlx` varchar(64) NULL COMMENT '单据类型'");
+ if (!names.Contains("djrq"))
+ _db.Ado.ExecuteCommand(
+ "ALTER TABLE `wt_yskzjjs` ADD COLUMN `djrq` datetime NULL COMMENT '单据日期'");
+ }
+ catch (Exception ex) { Console.WriteLine($"EnsureWtYskzjjsAuditColumnsForTodo: {ex.Message}"); }
+ _wtYskzjjsAuditColsEnsured = true;
+ }
+ }
+
+ ///
+ /// 其他收入单待办(wt_fysrd + wt_shrysz)
+ ///
+ private async Task> QueryWtFysrdQtsrAuditTodosAsync(string userId, string userAccount)
+ {
+ var result = new List();
+ const string sql = @"
+SELECT d.F_Id AS id, d.djrq, d.djzt,
+ IFNULL(NULLIF(TRIM(s.shr1), ''), s.shr) AS shr1_eff,
+ IFNULL(NULLIF(TRIM(s.shr2), ''), s.shr) AS shr2_eff
+FROM wt_fysrd d
+LEFT JOIN wt_shrysz s ON TRIM(s.djmc) = @billName
+WHERE (d.F_Id LIKE 'QT%' OR TRIM(IFNULL(d.djlx,'')) IN ('其他收入单','其它收入单','其他收入','其它收入'))
+ AND IFNULL(d.djzt, '') <> '草稿'
+ AND IFNULL(d.djzt, '') <> '已审核'
+ AND IFNULL(d.djzt, '') <> '审核不通过'
+ AND (
+ d.djzt IN ('待审核', '一级已审', '待二级', '一级已审/待二级')
+ OR d.djzt IS NULL
+ OR TRIM(IFNULL(d.djzt, '')) = ''
+ )
+ORDER BY d.djrq DESC";
+
+ var rows = await _db.Ado.SqlQueryAsync(sql, new { billName = FysrdQtsrTodoBillName });
+ if (rows == null || rows.Count == 0) return result;
+
+ foreach (var row in rows)
+ {
+ var status = Convert.ToString(row.djzt)?.Trim() ?? string.Empty;
+ var level1 = status == "待审核" || string.IsNullOrEmpty(status);
+ var level2 = status == "一级已审" || status == "待二级" || status == "一级已审/待二级";
+ if (!level1 && !level2) continue;
+
+ var configUsers = level1 ? Convert.ToString(row.shr1_eff) : Convert.ToString(row.shr2_eff);
+ if (!string.IsNullOrWhiteSpace(configUsers) && !IsInAuditUsers(configUsers, userId, userAccount))
+ continue;
+
+ DateTime? createTime = null;
+ var rawTime = Convert.ToString(row.djrq);
+ if (DateTime.TryParse(rawTime, out DateTime dt))
+ createTime = dt;
+
+ var id = Convert.ToString(row.id);
+ var levelText = level1 ? "一级审核待办" : "二级审核待办";
+ result.Add(new FlowTodoOutput
+ {
+ id = id,
+ fullName = $"{FysrdQtsrTodoBillName} {id} - {levelText}",
+ creatorTime = createTime,
+ billType = FysrdQtsrTodoBillName
+ });
+ }
+
+ return result;
+ }
+
///
/// 商品调价单待办(wt_tjd + wt_shrysz)
///
@@ -424,6 +575,62 @@ ORDER BY d.djrq DESC";
}
///
+ /// 其他应收单待办(wt_yskzjjs + wt_shrysz)
+ ///
+ private async Task> QueryWtYskzjjsQtAuditTodosAsync(string userId, string userAccount)
+ {
+ var result = new List();
+ const string sql = @"
+SELECT d.F_Id AS id, d.djrq, d.djzt,
+ IFNULL(NULLIF(TRIM(s.shr1), ''), s.shr) AS shr1_eff,
+ IFNULL(NULLIF(TRIM(s.shr2), ''), s.shr) AS shr2_eff
+FROM wt_yskzjjs d
+LEFT JOIN wt_shrysz s ON TRIM(s.djmc) = @billName
+WHERE TRIM(IFNULL(d.djlx,'')) IN ('其他应收单','其他应收款')
+ AND IFNULL(d.djzt, '') <> '草稿'
+ AND IFNULL(d.djzt, '') <> '已审核'
+ AND IFNULL(d.djzt, '') <> '审核不通过'
+ AND (
+ d.djzt IN ('待审核', '一级已审', '待二级', '一级已审/待二级')
+ OR d.djzt IS NULL
+ OR TRIM(IFNULL(d.djzt, '')) = ''
+ )
+ORDER BY d.djrq DESC";
+
+ var rows = await _db.Ado.SqlQueryAsync(sql, new { billName = YskzjjsQtTodoBillName });
+ if (rows == null || rows.Count == 0) return result;
+
+ foreach (var row in rows)
+ {
+ var status = Convert.ToString(row.djzt)?.Trim() ?? string.Empty;
+ var level1 = status == "待审核" || string.IsNullOrEmpty(status);
+ var level2 = status == "一级已审" || status == "待二级" || status == "一级已审/待二级";
+ if (!level1 && !level2) continue;
+
+ var configUsers = level1 ? Convert.ToString(row.shr1_eff) : Convert.ToString(row.shr2_eff);
+ if (!string.IsNullOrWhiteSpace(configUsers) && !IsInAuditUsers(configUsers, userId, userAccount))
+ continue;
+
+ DateTime? createTime = null;
+ var rawTime = Convert.ToString(row.djrq);
+ if (DateTime.TryParse(rawTime, out DateTime dt))
+ createTime = dt;
+
+ var id = Convert.ToString(row.id);
+ var levelText = level1 ? "一级审核待办" : "二级审核待办";
+ result.Add(new FlowTodoOutput
+ {
+ id = id,
+ fullName = $"{YskzjjsQtTodoBillName} {id} - {levelText}",
+ creatorTime = createTime,
+ billType = YskzjjsQtTodoBillName
+ });
+ }
+
+ return result;
+ }
+
+ ///
/// 按单据类型查询待审核/待二级单据,并按 shr1/shr2(或 shr)过滤当前用户
///
/// 与 wt_xsckd.djlx、wt_shrysz.djmc 一致
diff --git a/Antis.Erp.Plat/sy/home.html b/Antis.Erp.Plat/sy/home.html
index dc3dd0f..6421c2b 100755
--- a/Antis.Erp.Plat/sy/home.html
+++ b/Antis.Erp.Plat/sy/home.html
@@ -475,7 +475,7 @@
- 收银台
+ {{ posCashierHeaderTitle }}