Files
zszq-trs/YLErpDAL/Modules/ExchangeTradeModule/ExchangeTradeSaveService.cs
tengyufan 864a29d6a8 #EQD-6605 国联民生-确认书编号规则调整(不基于甲方乙方判定)
feat: 国联民生收益互换按簿记分类生成交易编号

簿记账户新增“簿记分类”字段,支持代客、融券,新增账户默认代客,历史账户统一初始化为代客
簿记账户分类设为必填,列表、编辑及详情页面同步展示
修改已被交易引用的簿记账户分类时增加二次确认,并提示关联交易数量
国联民生收益互换交易编号不再根据甲乙方判断,改为根据簿记账户分类生成
代客使用数字序号,融券使用字母序号,分别从已有同客户同日编号的最大序号继续递增
保留手工交易编号优先逻辑,标准格式的手工编号参与后续最大序号计算
生成候选编号后检查完整交易编号是否已存在,存在则继续递增
新增簿记分类编号格式、最大序号计算及数字字母序列隔离专项单测
2026-08-05 15:55:01 +08:00

263 lines
8.4 KiB
C#

using BaseOUDAL;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Model.Enum;
namespace YLErp.Modules.ExchangeTradeModule
{
/// <summary>
/// 标的交易保存服务
/// </summary>
public class ExchangeTradeSaveService : ExchangeTradeSaveServiceBase
{
public ExchangeTradeSaveService(YLBaseService baseService) : base(baseService)
{
}
public ExchangeTradeSaveService(OptUserInfo userInfo) : base(userInfo)
{
}
public ExchangeTrade Save(ExchangeTrade req, TradeSourceEnum source)
{
var isAddNew = req.id == 0;
Prepare(req, source, isAddNew);
if (isAddNew)
{
DbContext.ExchangeTrade.Add(req);
SaveExchangeTradePosition(req, null);
DbContext.SaveChanges();
return req;
}
else
{
var dbTrade = DbContext.ExchangeTrade.Find(req.id);
if (dbTrade == null)
{
throw new ServiceException("保存失败,数据不存在");
}
req.CreateTime = dbTrade.CreateTime;
req.TradeNumber = dbTrade.TradeNumber;
if (req.IsValid)
{
SaveExchangeTradePosition(req, dbTrade);
}
else
{
InValidTrade(req.id);
}
UpdateEntity(dbTrade, req);
DbContext.SaveChanges();
return dbTrade;
}
}
private void Prepare(ExchangeTrade req, TradeSourceEnum source, bool isAddNew)
{
if (string.IsNullOrWhiteSpace(req.TradeType))
{
throw new ServiceException("交易类型 必须填写");
}
if (!ConsTrade.TradeTypesForHedge.Contains(req.TradeType)&&!ConsTrade.BondTypeList.Contains(req.TradeType))
{
throw new ServiceException("程序错误,标的交易类型不支持:" + req.TradeType);
}
//准备交易保存
PrepareSave(req);
req.OptId = UserId;
req.OptName = UserName;
req.OptDate = OptDate;
if (isAddNew)
{
req.TradeSource = source.ToString();
if (string.IsNullOrWhiteSpace(req.TradeNumber))
{
req.TradeNumber = DateTime.Now.ToString("yyyyMMddHHmmssfff");
}
req.IsValid = true;
req.CreateTime = DateTime.Now;
}
}
//准备交易基础信息(标的、簿记、交易日期)
private void PrepareSave(ExchangeTrade req)
{
if (req.TradeDate.Year < 2000)
{
throw new ServiceException("交易日期 填写错误:" + req.TradeDate.ToString("yyyy-MM-dd"));
}
ExchangeListOption exOption = null;
if (req.TradeType == "场内期权")
{
SetTradeOptionInfo(req, req.OptionCode, out exOption);
}
//标的信息
SetTradeUnderlyingInfo(req, req.UnderlyingCode, out var um);
//--------------------------------------
// 簿记帐户
//--------------------------------------
if (req.AssetBookId < 1)
{
throw new ServiceException("请选择簿记账户");
}
var assetUnit = DbContext.assetunit.Where(n => n.id == req.AssetBookId)
.Select(n => new AssetUnitDto { id = n.id, Name = n.Name, BookCategory = n.BookCategory, TraderIds = n.TraderIds, UserGroup = n.UserGroup }).FirstOrDefault();
if (assetUnit == null)
{
throw new ServiceException("簿记账户不存在");
}
//--------------------------------------
// 交易员信息
//--------------------------------------
//有交易员id,通过id查名字
if (req.TraderId > 0)
{
req.TraderName = UserBLL.GetNameById(req.TraderId);
}
else
{
//当前登录用户作为交易员
req.TraderId = UserId;
req.TraderName = UserName;
}
//--------------------------------------
// 对冲帐户
//--------------------------------------
if (req.ExchangeAccountId > 0)
{
req.ExchangeAccountCode = DbContext.exchange_account.Where(n => n.id == req.ExchangeAccountId).Select(n => n.AccountCode).FirstOrDefault();
}
//--------------------------------------
// 检查交易权限
//--------------------------------------
if (req.TraderId < 1)
{
throw new ServiceException("请选择交易员");
}
//不检查:必须是交易员角色
//不检查:交易用户组必须和簿记账户一致
//不检查:交易员不在所选簿记中,请重新选择簿记
//不检查:交易员具有交易标的的交易权限
//--------------------------------------
// 手续费
//--------------------------------------
switch (req.CommissionType)
{
case CommissionType.不收取:
case CommissionType.系统计算:
req.Commission = 0;
break;
default:
if (req.Commission < 0)
{
throw new ServiceException("请填写有效的交易手续费");
}
break;
}
//2020-09-11 改为以TradeAmount为主
if (req.TradeAmount < 1e-5)
{
throw new ServiceException("交易数量必须大于0");
}
if (req.TradeType != "场内期权")
{
req.Notional = req.TradeAmount * um.CountRatio;
req.TradeLots = req.Notional / um.ContractSize;
}
else
{
req.Notional = req.TradeAmount;
req.TradeLots = req.Notional / (exOption.ContractSize < 1 ? um.ContractSize : exOption.ContractSize);
}
//验证股票结构类型,只能股票标的,商品期货只能期货
switch (req.TradeType)
{
case "股票":
if (!um.CalcTypeIsStock())
{
throw new ServiceException("股票交易类型必须使用股票标的");
}
break;
case "商品期货":
if (!um.IsFutures())
{
throw new ServiceException("商品期货交易类型必须填写期货标的");
}
break;
case "商品现货":
if (!um.IsCommoditySpot())
{
throw new ServiceException("商品现货交易类型必须填写商品现货标的");
}
break;
case "场内期权":
break;
}
if (req.TradeType != "场内期权")
{
req.OptionType = string.Empty;
}
}
/// <summary>
/// 删除交易
/// </summary>
public void InValidTrade(int id)
{
var dbTrade = DbContext.ExchangeTrade.Find(id);
if (dbTrade == null)
{
throw new ServiceException("找不到交易信息");
}
dbTrade.IsValid = false;
//清除对冲交易持仓
var para = PrepareTradePositionParam(dbTrade);
var dbPosition = DbContext.TradePosition.FirstOrDefault(para.predicate);
if (dbPosition != null)
{
var dbNotional = TradeHelper.GetPositionNotional(para.tradeSide, dbTrade.Notional);
dbPosition.Position -= dbNotional;
dbPosition.PositionCost -= dbNotional * dbTrade.TradeSinglePrice;
dbPosition.UpdateTime = OptDate;
}
DbContext.SaveChanges();
}
}
}