Files
zszq-trs/YLErpDAL/Modules/TradeModule/DocGenerateModule/GuolianContractNoGenerator.cs
T
tengyufan e1c5f72418 #EQD-6604 国联民生-确认书编码生成环节移到簿记(支持手工录入)
feat: 国联民生收益互换簿记后按确认书规则生成交易编号

- - 国联收益互换首次入库后复用原交易确认书编号规则生成交易编号,保留手工填写的交易编号
- 覆盖手工新增、系统流水自动簿记、普通交易导入、流水导入和日终合成今日交易流水
- 自动流水簿记增加本地事务,交易编号回写与基础簿记数据失败时统一回滚
- 交易确认书生成时直接取交易编号,不再重新生成确认书编号
- 结算管理-交易确认书页面将合约编号更名为交易确认书编号
- 上传确认书时将合约编号更名为交易确认书编号
- 历史交易编号不处理
2026-07-29 16:45:29 +08:00

127 lines
4.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using YLErp.BLL;
using YLErp.Configuration;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.TradeModule.DocGenerateModule
{
/// <summary>
/// 国贸交易确认书编号生成器
/// 规则:
/// - 对客交易(OpponentRole为"甲方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-FICC-{当日第N笔对客交易(数字递增)}-{标的代码},从01开始
/// 例:GLMS-IS-20260424-01-250210IB
/// - 非对客交易(OpponentRole为"乙方"):GLMS-{对手方代码}-{成交日期(yyyyMMdd)}-FICC-{当日第N笔非对客交易使用字母递增}-{标的代码},从A开始
/// 例:GLMS-IS-20260424-A-250210IB
/// </summary>
public class GuolianContractNoGenerator
{
private static readonly object _syncLock = new object();
public static bool IsGuolianSwapTrade(trade trade)
{
return PS.Config.Company == CompanyEnum.国联
&& trade?.TradeType == "收益互换";
}
/// <summary>
/// 交易首次入库后按国联交易确认书编号规则回写交易编号。
/// </summary>
/// <returns>是否生成了交易编号</returns>
public static bool TryGenerateTradeNumberAfterSave(YLContext dbContext, trade trade)
{
if (!IsGuolianSwapTrade(trade) || !string.IsNullOrWhiteSpace(trade.TradeNumber))
{
return false;
}
if (trade.id <= 0)
{
throw new InvalidOperationException("国联收益互换交易编号必须在交易入库后生成");
}
var clientCode = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId)?.Code;
if (string.IsNullOrWhiteSpace(clientCode))
{
throw new ServiceException("对手方代码缩写缺失,请联系运营组同事维护");
}
trade.TradeNumber = Generate(dbContext, trade, clientCode);
return true;
}
/// <summary>
/// 生成国贸交易确认书编号
/// </summary>
/// <param name="dbContext">数据库上下文</param>
/// <param name="trade">交易记录</param>
/// <param name="clientCode">客户代码</param>
/// <returns>确认书编号</returns>
public static string Generate(
YLContext dbContext,
trade trade,
string clientCode)
{
// 根据trade的OpponentRole判断是否为对客交易(甲方=对客)。glms客户规定甲方=对客
var isClientTrade = trade.OpponentRole == "甲方";
var underlyingCode = (trade.UnderlyingCode ?? "").Replace(".", ""); // 去掉标的代码中的点号
var tradeDate = trade.TradeDate ?? DateTime.MinValue;
var tradeDateStr = tradeDate.ToString("yyyyMMdd");
var prefix = $"GLMS-{clientCode}-{tradeDateStr}-FICC-";
lock (_syncLock)
{
// 幂等:如果已生成过确认书编号,直接返回
var existingForTrade = dbContext.trade_contract_r
.Where(r => r.TradeId == trade.id && r.Type == ContractTypeEnum.Trade && r.IsValid)
.Select(r => r.ContractCode)
.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(existingForTrade))
{
return existingForTrade;
}
// 通过当天同客户同类交易的 id 排序确定序号(幂等),每个客户独立递增
var tradeDateStart = tradeDate.Date;
var sameDayTradesCount = dbContext.trade
.Count(t => t.TradeDate >= tradeDateStart
&& t.TradeDate < tradeDateStart.AddDays(1)
&& t.ClientId == trade.ClientId
&& t.OpponentRole == trade.OpponentRole
&& t.id < trade.id);
var sequenceNo = sameDayTradesCount + 1;
if (isClientTrade)
{
return $"{prefix}{sequenceNo:D2}-{underlyingCode}";
}
else
{
return $"{prefix}{NumberToLetter(sequenceNo)}-{underlyingCode}";
}
}
}
/// <summary>
/// 将数字转换为字母序列(1=A, 2=B, 26=Z, 27=AA, 28=AB...
/// </summary>
private static string NumberToLetter(int number)
{
if (number <= 0)
{
return "A";
}
string result = "";
while (number > 0)
{
number--; // 调整为从0开始
result = (char)('A' + (number % 26)) + result;
number /= 26;
}
return result;
}
}
}