Merge branch 'glms/feature/1.4.2' of http://git.yiliantech.com/gitlab/otc-dev/zszq-trs into glms/feature/1.4.2
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Globalization;
|
||||
using YLErp.Enums;
|
||||
|
||||
namespace YLErp.DBModels
|
||||
@@ -17,6 +18,109 @@ namespace YLErp.DBModels
|
||||
[DisplayName("模板名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 适用簿记账户ID列表,逗号分隔;为空表示适用全部簿记账户。
|
||||
/// </summary>
|
||||
[DisplayName("适用簿记账户组")]
|
||||
[Column("book_id")]
|
||||
public string BookIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 将簿记账户ID列表规范化为去空格、去重的逗号分隔字符串。
|
||||
/// </summary>
|
||||
public static string NormalizeBookIds(string bookIds)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(bookIds))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ids = new List<int>();
|
||||
foreach (var rawId in bookIds.Split(new[] { ',' }, StringSplitOptions.None))
|
||||
{
|
||||
var token = rawId.Trim();
|
||||
if (token.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("适用簿记账户组包含空账户ID。", nameof(bookIds));
|
||||
}
|
||||
if (!int.TryParse(token, NumberStyles.None, CultureInfo.InvariantCulture, out var id) || id <= 0)
|
||||
{
|
||||
throw new ArgumentException("适用簿记账户组ID必须为正整数:" + token, nameof(bookIds));
|
||||
}
|
||||
if (!ids.Contains(id))
|
||||
{
|
||||
ids.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
return ids.Count == 0 ? null : string.Join(",", ids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析簿记账户ID列表。结果使用完整token,避免"1"误命中"11"。
|
||||
/// </summary>
|
||||
public static HashSet<string> ParseBookIds(string bookIds)
|
||||
{
|
||||
var ids = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (string.IsNullOrWhiteSpace(bookIds))
|
||||
{
|
||||
return ids;
|
||||
}
|
||||
|
||||
foreach (var rawId in bookIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var token = rawId.Trim();
|
||||
if (int.TryParse(token, NumberStyles.None, CultureInfo.InvariantCulture, out var id) && id > 0)
|
||||
{
|
||||
ids.Add(id.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断模板是否适用于指定簿记账户;空范围表示全部。
|
||||
/// </summary>
|
||||
public bool IsApplicableToBook(int? bookId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BookIds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return bookId.HasValue && ParseBookIds(BookIds).Contains(bookId.Value.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断两个簿记账户范围是否重叠;任一空范围均视为全部。
|
||||
/// </summary>
|
||||
public static bool AreBookScopesOverlapping(string firstBookIds, string secondBookIds)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(firstBookIds) || string.IsNullOrWhiteSpace(secondBookIds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var first = ParseBookIds(firstBookIds);
|
||||
var second = ParseBookIds(secondBookIds);
|
||||
return first.Count > 0 && second.Count > 0 && first.Overlaps(second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 收集与新模板簿记账户范围重叠的所有模板适用结构。
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetOverlappingTradeTypes(IEnumerable<margin_template_v2> candidates, string bookIds)
|
||||
{
|
||||
if (candidates == null)
|
||||
{
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
return candidates
|
||||
.Where(x => x != null && AreBookScopesOverlapping(x.BookIds, bookIds))
|
||||
.SelectMany(x => (x.TradeTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单腿/多腿
|
||||
/// </summary>
|
||||
|
||||
@@ -171,6 +171,7 @@ namespace YLErp
|
||||
"yladmin" => _configuration.GetConnectionString("yladmin"),
|
||||
"ylclient" => _configuration.GetConnectionString("ylclient"),
|
||||
"bondoms" => _configuration.GetConnectionString("bondoms"),
|
||||
"glms_bigdata" => _configuration.GetConnectionString("glms_bigdata"),
|
||||
"apex_oracle"=> _configuration.GetConnectionString("apex_oracle"),
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
@@ -13,12 +13,20 @@ namespace YLErp.Modules.MarginModule
|
||||
/// <summary>
|
||||
/// 预付金模板V2 取数帮助类:按交易绑定的模板(trade_margin_template → margin_template_v2 → margin_template_detail)
|
||||
/// 取 初始预付金率x / 维持预付金率y,供交易确认书、预付金计算等消费点统一使用。
|
||||
/// 匹配维度:生效日期(ValueDate 最新)→ 利率债期限档(SpanConfig.BondTerm,四档,空=全部兜底)→ 标的资产类型(UnderlyingType 标志位)。
|
||||
/// 匹配维度:簿记账户范围(BookIds,空=全部)→ 生效日期(ValueDate 最新)→
|
||||
/// 利率债期限档(SpanConfig.BondTerm,四档,空=全部兜底)→ 标的资产类型(UnderlyingType 标志位)。
|
||||
/// </summary>
|
||||
public static class MarginTemplateV2RateHelper
|
||||
{
|
||||
private static readonly IYcLogger logger = LogFactory.GetLogger("预付金模板取数");
|
||||
|
||||
private sealed class ClientTemplateCandidate
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
public string ClientLevel { get; set; }
|
||||
public margin_template_v2 Template { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取数结果。率为小数(如 0.1 表示 10%,与数据库一致),调用方直接用。
|
||||
/// </summary>
|
||||
@@ -45,6 +53,19 @@ namespace YLErp.Modules.MarginModule
|
||||
public decimal? MaintainRate { get; set; }
|
||||
}
|
||||
|
||||
internal static int? GetTradeAssetId(int? tradeId, YLContext db)
|
||||
{
|
||||
if (!tradeId.HasValue || tradeId.Value <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return db.trade.AsNoTracking()
|
||||
.Where(x => x.id == tradeId.Value)
|
||||
.Select(x => (int?)x.AssetId)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按交易绑定的模板取 初始/维持预付金率。
|
||||
/// </summary>
|
||||
@@ -182,11 +203,13 @@ namespace YLErp.Modules.MarginModule
|
||||
/// 绑定指向已失效模板时同样停止(不向下回退,避免"改绑定失效后悄悄换成别的规则"),返回 null 并留日志;
|
||||
/// 2.客户默认 client_margin_template(按客户 ClientId 或按客户等级 ClientId=0+ClientLevel,
|
||||
/// 客户等级经 client.LevelId → clientlevel.LevelName 解析;ValueDate 最新 ≤ 业务日),
|
||||
/// 模板须有效且适用结构含收益互换;
|
||||
/// 3.全局默认 margin_template_v2(IsDefault && !IsForClient && IsValid && 适用收益互换,ValueDate 最新)。
|
||||
/// 模板须有效、适用结构含收益互换且适用当前交易簿记账户;
|
||||
/// 3.全局默认 margin_template_v2(IsDefault && !IsForClient && IsValid && 适用收益互换,且适用当前簿记账户,ValueDate 最新)。
|
||||
/// </summary>
|
||||
public static margin_template_v2 ResolveTieredTemplate(int? tradeId, int clientId, DateTime valueDate, YLContext db)
|
||||
{
|
||||
var tradeAssetId = GetTradeAssetId(tradeId, db);
|
||||
|
||||
//1.交易绑定(找到即停)
|
||||
if (tradeId > 0)
|
||||
{
|
||||
@@ -199,10 +222,15 @@ namespace YLErp.Modules.MarginModule
|
||||
if (bindingTemplateId > 0)
|
||||
{
|
||||
var bound = db.margin_template_v2.AsNoTracking().FirstOrDefault(x => x.id == bindingTemplateId.Value && x.IsValid);
|
||||
if (bound != null)
|
||||
if (bound != null && bound.IsApplicableToBook(tradeAssetId))
|
||||
{
|
||||
return bound;
|
||||
}
|
||||
if (bound != null)
|
||||
{
|
||||
logger.Info($"【警告】预付金模板取数:交易{tradeId}绑定的模板{bindingTemplateId}不适用簿记账户{tradeAssetId},按找到即停不向下回退");
|
||||
return null;
|
||||
}
|
||||
logger.Info($"【警告】预付金模板取数:交易{tradeId}绑定的模板{bindingTemplateId}已失效或不存在,按找到即停不向下回退");
|
||||
return null;
|
||||
}
|
||||
@@ -229,10 +257,12 @@ namespace YLErp.Modules.MarginModule
|
||||
&& (cmt.ClientId == clientId || (cmt.ClientId == 0 && cmt.ClientLevel != "" && cmt.ClientLevel == levelName))
|
||||
&& mt.IsValid && mt.TradeTypes.Contains("收益互换")
|
||||
orderby cmt.ValueDate descending, cmt.id descending
|
||||
select mt).FirstOrDefault();
|
||||
if (clientTemplate != null)
|
||||
select mt).ToList();
|
||||
|
||||
var ret = clientTemplate.FirstOrDefault(x => x.IsApplicableToBook(tradeAssetId));
|
||||
if (ret != null)
|
||||
{
|
||||
return clientTemplate;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +271,8 @@ namespace YLErp.Modules.MarginModule
|
||||
.Where(x => x.IsDefault && !x.IsForClient && x.IsValid && x.TradeTypes.Contains("收益互换") && x.ValueDate <= valueDate)
|
||||
.OrderByDescending(x => x.ValueDate)
|
||||
.ThenByDescending(x => x.id)
|
||||
.FirstOrDefault();
|
||||
.ToList()
|
||||
.FirstOrDefault(x => x.IsApplicableToBook(tradeAssetId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -249,8 +280,8 @@ namespace YLErp.Modules.MarginModule
|
||||
/// 只配了客户默认/全局默认模板的交易与交易级绑定同权生效):
|
||||
/// 语义与单笔 ResolveTieredTemplate 一致——
|
||||
/// 1.交易绑定(ValueDate 最新 ≤ 业务日,同日多条按 id 最新兜底)找到即停,绑定指向失效模板同样停止(不回退);
|
||||
/// 2.客户默认(client_margin_template 按客户或按客户等级,模板须有效且适用结构含收益互换,先过滤有效再取绑定最新);
|
||||
/// 3.全局默认(IsDefault&&!IsForClient&&IsValid&&适用收益互换,ValueDate/id 最新)。
|
||||
/// 2.客户默认(client_margin_template 按客户或按客户等级,模板须有效、适用结构含收益互换且适用当前簿记账户,先过滤有效再取绑定最新);
|
||||
/// 3.全局默认(IsDefault&&!IsForClient&&IsValid&&适用收益互换,按每笔交易簿记账户取ValueDate/id 最新)。
|
||||
/// 返回 tradeId → 命中模板;未命中或因失效绑定停止的交易不在结果中。
|
||||
/// </summary>
|
||||
public static Dictionary<int, margin_template_v2> ResolveTieredTemplates(List<trade> trades, DateTime valueDate, YLContext db)
|
||||
@@ -282,10 +313,10 @@ namespace YLErp.Modules.MarginModule
|
||||
|
||||
//2.客户默认(client_margin_template:按客户 或 按客户等级;客户表在独立客户库,等级名批量解析)
|
||||
var clientIds = trades.Select(t => t.ClientId).Where(c => c > 0).Distinct().ToList();
|
||||
var clientTemplateByClient = new Dictionary<int, margin_template_v2>();
|
||||
var levelNames = new Dictionary<int, string>();
|
||||
var clientTemplateCandidates = new List<ClientTemplateCandidate>();
|
||||
if (clientIds.Any())
|
||||
{
|
||||
var levelNames = new Dictionary<int, string>();
|
||||
using (var clientDb = DbContextFactory.GetClientDbContext(OptUserInfo.SystemUser))
|
||||
{
|
||||
var clientLevels = clientDb.client.AsNoTracking()
|
||||
@@ -305,33 +336,28 @@ namespace YLErp.Modules.MarginModule
|
||||
}
|
||||
|
||||
//先按 有效+适用收益互换 过滤再按绑定取最新(与单笔解析一致,同日互换/期权并存不误穿透)
|
||||
var clientBindingRows = (from cmt in db.client_margin_template.AsNoTracking()
|
||||
join mt in db.margin_template_v2.AsNoTracking() on cmt.MarginTemplateId equals mt.id
|
||||
where cmt.ValueDate <= valueDate && cmt.MarginTemplateId > 0
|
||||
&& (clientIds.Contains(cmt.ClientId) || (cmt.ClientId == 0 && cmt.ClientLevel != ""))
|
||||
&& mt.IsValid && mt.TradeTypes.Contains("收益互换")
|
||||
orderby cmt.ValueDate descending, cmt.id descending
|
||||
select new { cmt.ClientId, cmt.ClientLevel, Template = mt }).ToList();
|
||||
foreach (var clientId in clientIds)
|
||||
{
|
||||
var levelName = levelNames.TryGetValue(clientId, out var name) ? name : null;
|
||||
//每客户找到即停:按客户 或 按客户等级 同查询取绑定最新(与单笔解析一致;
|
||||
//直接绑定与等级绑定并存的优先级方案未定义(BUG-16),维持"绑定日期最新"现状不改语义)
|
||||
var hit = clientBindingRows
|
||||
.FirstOrDefault(r => r.ClientId == clientId || (r.ClientId == 0 && r.ClientLevel == levelName));
|
||||
if (hit != null)
|
||||
clientTemplateCandidates = (from cmt in db.client_margin_template.AsNoTracking()
|
||||
join mt in db.margin_template_v2.AsNoTracking() on cmt.MarginTemplateId equals mt.id
|
||||
where cmt.ValueDate <= valueDate && cmt.MarginTemplateId > 0
|
||||
&& (clientIds.Contains(cmt.ClientId) || (cmt.ClientId == 0 && cmt.ClientLevel != ""))
|
||||
&& mt.IsValid && mt.TradeTypes.Contains("收益互换")
|
||||
orderby cmt.ValueDate descending, cmt.id descending
|
||||
select new { cmt.ClientId, cmt.ClientLevel, Template = mt }).ToList()
|
||||
.Select(x => new ClientTemplateCandidate
|
||||
{
|
||||
clientTemplateByClient[clientId] = hit.Template;
|
||||
}
|
||||
}
|
||||
ClientId = x.ClientId,
|
||||
ClientLevel = x.ClientLevel,
|
||||
Template = x.Template
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
//3.全局默认(全部交易共享,一次查询)
|
||||
var globalDefault = db.margin_template_v2.AsNoTracking()
|
||||
//3.全局默认(候选模板一次查询,每笔交易按簿记账户取最新适用模板)
|
||||
var globalDefaults = db.margin_template_v2.AsNoTracking()
|
||||
.Where(x => x.IsDefault && !x.IsForClient && x.IsValid && x.TradeTypes.Contains("收益互换") && x.ValueDate <= valueDate)
|
||||
.OrderByDescending(x => x.ValueDate)
|
||||
.ThenByDescending(x => x.id)
|
||||
.FirstOrDefault();
|
||||
.ToList();
|
||||
|
||||
foreach (var trade in trades)
|
||||
{
|
||||
@@ -342,21 +368,30 @@ namespace YLErp.Modules.MarginModule
|
||||
if (bindingByTrade.TryGetValue(trade.id, out var templateId))
|
||||
{
|
||||
//一级找到即停:绑定指向失效模板时不向下回退(与单笔解析一致,仅留日志)
|
||||
if (boundTemplates.TryGetValue(templateId, out var bound))
|
||||
if (boundTemplates.TryGetValue(templateId, out var bound) && bound.IsApplicableToBook(trade.AssetId))
|
||||
{
|
||||
result[trade.id] = bound;
|
||||
}
|
||||
else if (boundTemplates.ContainsKey(templateId))
|
||||
{
|
||||
logger.Info($"【警告】预付金模板取数:交易{trade.id}绑定的模板{templateId}不适用簿记账户{trade.AssetId},按找到即停不向下回退");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info($"【警告】预付金模板取数:交易{trade.id}绑定的模板{templateId}已失效或不存在,按找到即停不向下回退");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (clientTemplateByClient.TryGetValue(trade.ClientId, out var clientTemplate))
|
||||
var levelName = levelNames.TryGetValue(trade.ClientId, out var clientLevelName) ? clientLevelName : null;
|
||||
var clientTemplate = clientTemplateCandidates.FirstOrDefault(x =>
|
||||
(x.ClientId == trade.ClientId || (x.ClientId == 0 && x.ClientLevel == levelName))
|
||||
&& x.Template.IsApplicableToBook(trade.AssetId));
|
||||
if (clientTemplate != null)
|
||||
{
|
||||
result[trade.id] = clientTemplate;
|
||||
result[trade.id] = clientTemplate.Template;
|
||||
continue;
|
||||
}
|
||||
var globalDefault = globalDefaults.FirstOrDefault(x => x.IsApplicableToBook(trade.AssetId));
|
||||
if (globalDefault != null)
|
||||
{
|
||||
result[trade.id] = globalDefault;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
public static readonly string[] InitialTemplateNames = { "现金保证金", "授信保证金" };
|
||||
|
||||
public static SwapMarginTemplateConfig GetConfig()
|
||||
public static SwapMarginTemplateConfig GetConfig(int? assetId = null)
|
||||
{
|
||||
//保证金模板V2迁移:交易页"保证金模板"下拉的选项改为"预付金模板V2"页面维护的模板(自定义+全局默认,存模板名称),不再使用数据字典
|
||||
//互换交易只显示"适用结构"包含 收益互换 的模板
|
||||
@@ -17,7 +17,9 @@ namespace YLErp.Modules.SwapModule
|
||||
var items = db.margin_template_v2
|
||||
.Where(item => !item.IsForClient && item.IsValid && item.TradeTypes.Contains("收益互换"))
|
||||
.OrderBy(item => item.IsDefault).ThenBy(item => item.id)
|
||||
.Select(item => new SelectItem { Text = item.Name, Value = item.Name })
|
||||
.ToList()
|
||||
.Where(item => !assetId.HasValue || assetId.Value <= 0 || item.IsApplicableToBook(assetId.Value))
|
||||
.Select(item => new SwapMarginTemplateOption { Text = item.Name, Value = item.Name, BookIds = item.BookIds })
|
||||
.ToArray();
|
||||
|
||||
return new SwapMarginTemplateConfig
|
||||
@@ -31,8 +33,15 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
public class SwapMarginTemplateConfig
|
||||
{
|
||||
public IEnumerable<SelectItem> options { get; set; }
|
||||
public IEnumerable<SwapMarginTemplateOption> options { get; set; }
|
||||
|
||||
public string defaultValue { get; set; }
|
||||
}
|
||||
|
||||
public class SwapMarginTemplateOption : SelectItem
|
||||
{
|
||||
public string BookIds { get; set; }
|
||||
|
||||
public bool IsLegacy { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using BaseOUDAL;
|
||||
using BaseOUDAL;
|
||||
using ClosedXML.Report.Options;
|
||||
using Confluent.Kafka;
|
||||
using CsvHelper;
|
||||
@@ -118,6 +118,12 @@ namespace YLErp.Modules.SwapModule
|
||||
try
|
||||
{
|
||||
var isAddNew = req.id == 0;
|
||||
var originalMarginTemplateName = isAddNew
|
||||
? null
|
||||
: DbContext.trade
|
||||
.Where(x => x.id == req.id)
|
||||
.Select(x => x.MarginTemplateName)
|
||||
.FirstOrDefault();
|
||||
dbTrade = isAddNew ? InnerSaveNewTrade(req, tradeNumberGenerated) : InnerSaveEditTrade(req, out var changeConfirmStatus);
|
||||
if (req.SalesCommission != null)
|
||||
{
|
||||
@@ -139,7 +145,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
}
|
||||
//保证金模板V2迁移:按交易页选择的保证金模板(MarginTemplateName 存模板V2名称)维护交易模板绑定
|
||||
SyncTradeMarginTemplate(dbTrade);
|
||||
SyncTradeMarginTemplate(dbTrade, originalMarginTemplateName, isAddNew);
|
||||
trans.Commit();
|
||||
}
|
||||
finally
|
||||
@@ -151,35 +157,72 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
/// <summary>
|
||||
/// 保证金模板V2迁移:按交易上选择的保证金模板(MarginTemplateName 存预付金模板V2的名称)维护 trade_margin_template 绑定。
|
||||
/// 未选择或名称匹配不到有效自定义模板时仅清除旧绑定,不新增(保持无预付金占用的现状)。
|
||||
/// 未选择或存量交易原样保留的历史名称仅清除旧绑定,不新增(保持无预付金占用的现状)。
|
||||
/// </summary>
|
||||
private void SyncTradeMarginTemplate(trade dbTrade)
|
||||
private void SyncTradeMarginTemplate(trade dbTrade, string originalMarginTemplateName, bool isNewTrade)
|
||||
{
|
||||
var olds = DbContext.trade_margin_template.Where(x => x.TradeId == dbTrade.id).ToList();
|
||||
DbContext.trade_margin_template.RemoveRange(olds);
|
||||
margin_template_v2 template = null;
|
||||
if (!string.IsNullOrWhiteSpace(dbTrade.MarginTemplateName))
|
||||
{
|
||||
var allTemplatesByName = DbContext.margin_template_v2
|
||||
.Where(x => x.Name == dbTrade.MarginTemplateName)
|
||||
.ToList();
|
||||
var hasV2TemplateWithName = allTemplatesByName.Any();
|
||||
//匹配自定义+全局默认模板(重名时优先自定义),模板"适用结构"需包含 收益互换
|
||||
var template = DbContext.margin_template_v2
|
||||
.Where(x => x.Name == dbTrade.MarginTemplateName && !x.IsForClient && x.IsValid && x.TradeTypes.Contains("收益互换"))
|
||||
var templatesByName = allTemplatesByName
|
||||
.Where(x => !x.IsForClient && x.IsValid && (x.TradeTypes ?? string.Empty).Contains("收益互换"))
|
||||
.OrderBy(x => x.IsDefault)
|
||||
.FirstOrDefault();
|
||||
if (template != null)
|
||||
.ToList();
|
||||
template = templatesByName.FirstOrDefault(x => x.IsApplicableToBook(dbTrade.AssetId));
|
||||
if (template == null)
|
||||
{
|
||||
DbContext.trade_margin_template.Add(new trade_margin_template()
|
||||
//存量交易未修改的非V2历史名称只保留在 trade.MarginTemplateName,不创建模板绑定。
|
||||
if (!CanKeepLegacyMarginTemplateName(
|
||||
isNewTrade,
|
||||
originalMarginTemplateName,
|
||||
dbTrade.MarginTemplateName,
|
||||
hasV2TemplateWithName))
|
||||
{
|
||||
TradeId = dbTrade.id,
|
||||
ValueDate = new DateTime(2000, 1, 1),
|
||||
MarginTemplateId = template.id,
|
||||
IsLatest = true,
|
||||
OptId = UserId,
|
||||
OptName = UserName,
|
||||
OptDate = DateTime.Now
|
||||
});
|
||||
throw new ServiceException(templatesByName.Any()
|
||||
? $"所选预付金模板不适用当前簿记账户:{dbTrade.MarginTemplateName}"
|
||||
: $"所选预付金模板不存在或已失效:{dbTrade.MarginTemplateName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var olds = DbContext.trade_margin_template.Where(x => x.TradeId == dbTrade.id).ToList();
|
||||
DbContext.trade_margin_template.RemoveRange(olds);
|
||||
if (template != null)
|
||||
{
|
||||
DbContext.trade_margin_template.Add(new trade_margin_template()
|
||||
{
|
||||
TradeId = dbTrade.id,
|
||||
ValueDate = new DateTime(2000, 1, 1),
|
||||
MarginTemplateId = template.id,
|
||||
IsLatest = true,
|
||||
OptId = UserId,
|
||||
OptName = UserName,
|
||||
OptDate = DateTime.Now
|
||||
});
|
||||
}
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否允许存量交易原样保留未迁移的历史保证金模板名称。
|
||||
/// </summary>
|
||||
internal static bool CanKeepLegacyMarginTemplateName(
|
||||
bool isNewTrade,
|
||||
string originalName,
|
||||
string currentName,
|
||||
bool currentNameIsV2Template)
|
||||
{
|
||||
return !isNewTrade
|
||||
&& !currentNameIsV2Template
|
||||
&& !string.IsNullOrWhiteSpace(originalName)
|
||||
&& !string.IsNullOrWhiteSpace(currentName)
|
||||
&& string.Equals(originalName, currentName, StringComparison.Ordinal);
|
||||
}
|
||||
/// <summary>
|
||||
/// 单标的生成开仓事件
|
||||
/// </summary>
|
||||
|
||||
@@ -656,7 +656,10 @@ namespace YLErp.Modules.TradeModule.OrderModule
|
||||
subTrade.InitialMargin = 0;
|
||||
trade.InitialMargin = 0;
|
||||
|
||||
var marginTemplate = DbContext.margin_template_v2.FirstOrDefault(x => x.Name == "无预付金");
|
||||
var marginTemplate = DbContext.margin_template_v2
|
||||
.Where(x => x.Name == "无预付金")
|
||||
.ToList()
|
||||
.FirstOrDefault(x => x.IsApplicableToBook(subTrade.AssetId));
|
||||
if (marginTemplate == null)
|
||||
{
|
||||
marginTemplate = new margin_template_v2()
|
||||
@@ -1492,4 +1495,4 @@ namespace YLErp.Modules.TradeModule.OrderModule
|
||||
// dbTrade.ProcessOptDate = DateTime.Now;
|
||||
// dbTrade.ProcessStatus = "未审批";
|
||||
// }
|
||||
//}
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using Dapper;
|
||||
using MySqlConnector;
|
||||
using YieldChain.Helpers;
|
||||
using YLErp.BLL;
|
||||
|
||||
namespace YLErp.Modules.UnderlyingModule
|
||||
{
|
||||
public enum FundManagerLookupStatus
|
||||
{
|
||||
NotFound,
|
||||
Unique,
|
||||
Multiple,
|
||||
Unavailable
|
||||
}
|
||||
|
||||
public sealed class FundManagerLookupResult
|
||||
{
|
||||
public FundManagerLookupStatus Status { get; init; }
|
||||
public string InvestAdvisorName { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询上游基金档案中的基金管理人。上游不可用时返回降级结果,不阻断页面编辑。
|
||||
/// </summary>
|
||||
public sealed class FundManagerLookupService
|
||||
{
|
||||
private sealed class FundManagerRow
|
||||
{
|
||||
public string InvestAdvisorCode { get; set; }
|
||||
public string InvestAdvisorName { get; set; }
|
||||
}
|
||||
|
||||
private const string LookupSql = @"
|
||||
SELECT
|
||||
ia.investadvisorcode AS InvestAdvisorCode,
|
||||
ia.investadvisorname AS InvestAdvisorName
|
||||
FROM glms_bigdata.mf_fundarchives AS fa
|
||||
INNER JOIN glms_bigdata.mf_investadvisoroutline AS ia
|
||||
ON CONVERT(fa.investadvisorcode USING utf8mb4) COLLATE utf8mb4_unicode_ci =
|
||||
CONVERT(ia.investadvisorcode USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
||||
WHERE CONVERT(fa.secucode USING utf8mb4) COLLATE utf8mb4_unicode_ci =
|
||||
CONVERT(TRIM(SUBSTRING_INDEX(@UnderlyingCode, '.', 1)) USING utf8mb4) COLLATE utf8mb4_unicode_ci";
|
||||
|
||||
public FundManagerLookupResult Lookup(string underlyingCode)
|
||||
{
|
||||
var normalizedCode = NormalizeCode(underlyingCode);
|
||||
if (string.IsNullOrEmpty(normalizedCode))
|
||||
{
|
||||
return new FundManagerLookupResult { Status = FundManagerLookupStatus.NotFound };
|
||||
}
|
||||
|
||||
var connectionString = AppManager.GetConnectionString("glms_bigdata");
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
return new FundManagerLookupResult { Status = FundManagerLookupStatus.Unavailable };
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var connection = new MySqlConnection(connectionString);
|
||||
var matches = connection.Query<FundManagerRow>(LookupSql, new { UnderlyingCode = normalizedCode }, commandTimeout: 10)
|
||||
.Where(row => !string.IsNullOrWhiteSpace(row.InvestAdvisorName))
|
||||
.GroupBy(row => (row.InvestAdvisorCode ?? string.Empty).Trim(), StringComparer.OrdinalIgnoreCase)
|
||||
.Select(group => group.Select(row => row.InvestAdvisorName.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToArray())
|
||||
.Where(names => names.Length > 0)
|
||||
.ToArray();
|
||||
|
||||
return matches.Length switch
|
||||
{
|
||||
0 => new FundManagerLookupResult { Status = FundManagerLookupStatus.NotFound },
|
||||
1 when matches[0].Length == 1 => new FundManagerLookupResult { Status = FundManagerLookupStatus.Unique, InvestAdvisorName = matches[0][0] },
|
||||
_ => new FundManagerLookupResult { Status = FundManagerLookupStatus.Multiple }
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFactory.GetLogger<FundManagerLookupService>().Error("查询基金管理人失败,代码:" + normalizedCode, ex);
|
||||
return new FundManagerLookupResult { Status = FundManagerLookupStatus.Unavailable };
|
||||
}
|
||||
}
|
||||
|
||||
public static string NormalizeCode(string underlyingCode)
|
||||
{
|
||||
var trimmed = underlyingCode?.Trim();
|
||||
if (string.IsNullOrEmpty(trimmed))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dotIndex = trimmed.IndexOf('.');
|
||||
return (dotIndex < 0 ? trimmed : trimmed.Substring(0, dotIndex)).TrimToNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,10 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
|
||||
var marginTemplate = yldb.margin_template_v2.Find(clientMarginTemplate.MarginTemplateId);
|
||||
if (marginTemplate == null)
|
||||
{
|
||||
throw new Exception("找不到有效的预付金模板");
|
||||
}
|
||||
|
||||
var marginDetail = yldb.margin_template_detail.Where(x => x.MarginTemplateId == marginTemplate.id && x.ValueDate <= clientMarginTemplate.ValueDate).OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
||||
if (marginDetail == null && (marginTemplate.RuleType != (int)MarginRuleTypeEnum.无预付金 && marginTemplate.RuleType != (int)MarginRuleTypeEnum.自动赎回买入预付金规则 && marginTemplate.RuleType != (int)MarginRuleTypeEnum.含赔付预付金计算规则 && marginTemplate.RuleType != (int)MarginRuleTypeEnum.保底预付金规则))
|
||||
@@ -57,8 +61,14 @@ namespace YLErp.Web.Controllers
|
||||
|
||||
//一套规则多套默认参数的互斥验证
|
||||
var existClientMarginTemplateOther = yldb.client_margin_template.Where(x => ((x.ClientId != 0 && x.ClientId == clientMarginTemplate.ClientId) || (x.ClientLevel != "" && x.ClientLevel == clientMarginTemplate.ClientLevel)) && x.ValueDate == clientMarginTemplate.ValueDate && x.id != clientMarginTemplate.id);
|
||||
var existMarginTemplateIdsOther = existClientMarginTemplateOther.Select(x => x.MarginTemplateId).ToArray();
|
||||
|
||||
var existingTemplateIds = existClientMarginTemplateOther
|
||||
.Select(x => x.MarginTemplateId)
|
||||
.ToArray();
|
||||
var existingMarginTemplates = yldb.margin_template_v2
|
||||
.Where(x => existingTemplateIds.Contains(x.id))
|
||||
.ToList()
|
||||
.Where(x => margin_template_v2.AreBookScopesOverlapping(x.BookIds, marginTemplate.BookIds))
|
||||
.ToList();
|
||||
var msg = "";
|
||||
if(clientMarginTemplate.ClientLevel == "")
|
||||
{
|
||||
@@ -72,17 +82,16 @@ namespace YLErp.Web.Controllers
|
||||
if (marginTemplate.DefaultParameterType != 0)
|
||||
{
|
||||
//验证不同规则是否已经存在该交易类型
|
||||
var marginTemplateDefault = yldb.margin_template_v2.Where(x => existMarginTemplateIdsOther.Contains(x.id) && x.RuleType != marginTemplate.RuleType);
|
||||
var existTradeTypes = marginTemplateDefault.Select(x => x.TradeTypes).ToList();
|
||||
var tradeTypes = new List<string>();
|
||||
existTradeTypes.ForEach(x => tradeTypes.AddRange(x.Split(',')));
|
||||
var marginTemplateDefault = existingMarginTemplates
|
||||
.Where(x => x.RuleType != marginTemplate.RuleType)
|
||||
.ToList();
|
||||
var tradeTypes = margin_template_v2.GetOverlappingTradeTypes(marginTemplateDefault, marginTemplate.BookIds).ToList();
|
||||
|
||||
//验证相同规则相同的默认参数类型,是否包含相同的适用结构
|
||||
var sameRuleParameterRecord = yldb.margin_template_v2.FirstOrDefault(x => existMarginTemplateIdsOther.Contains(x.id) && x.RuleType == marginTemplate.RuleType && x.DefaultParameterType == marginTemplate.DefaultParameterType);
|
||||
if (sameRuleParameterRecord != null)
|
||||
{
|
||||
tradeTypes.AddRange(sameRuleParameterRecord.TradeTypes.Split(','));
|
||||
}
|
||||
var sameRuleParameterRecords = existingMarginTemplates
|
||||
.Where(x => x.RuleType == marginTemplate.RuleType && x.DefaultParameterType == marginTemplate.DefaultParameterType)
|
||||
.ToList();
|
||||
tradeTypes.AddRange(margin_template_v2.GetOverlappingTradeTypes(sameRuleParameterRecords, marginTemplate.BookIds));
|
||||
|
||||
tradeTypes.ForEach(x =>
|
||||
{
|
||||
@@ -94,10 +103,8 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
var marginTemplateDefault = yldb.margin_template_v2.Where(x => existMarginTemplateIdsOther.Contains(x.id));
|
||||
var existTradeTypes = marginTemplateDefault.Select(x => x.TradeTypes).ToList();
|
||||
List<string> tradeTypes = new List<string>();
|
||||
existTradeTypes.ForEach(x => tradeTypes.AddRange(x.Split(',')));
|
||||
var marginTemplateDefault = existingMarginTemplates;
|
||||
var tradeTypes = margin_template_v2.GetOverlappingTradeTypes(marginTemplateDefault, marginTemplate.BookIds).ToList();
|
||||
tradeTypes.ForEach(x =>
|
||||
{
|
||||
if (marginTemplate.TradeTypes.Contains(x))
|
||||
@@ -202,4 +209,4 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +247,9 @@ namespace YLErp.Web.Controllers
|
||||
{
|
||||
try
|
||||
{
|
||||
marginTemplate.BookIds = margin_template_v2.NormalizeBookIds(marginTemplate.BookIds);
|
||||
ValidateBookIds(marginTemplate.BookIds);
|
||||
|
||||
if (string.IsNullOrEmpty(marginTemplate.TradeTypes))
|
||||
{
|
||||
throw new Exception("默认规则的适用结构不能为空");
|
||||
@@ -263,17 +266,18 @@ namespace YLErp.Web.Controllers
|
||||
if (marginTemplate.DefaultParameterType != 0)
|
||||
{
|
||||
//验证不同规则是否已经存在该交易类型
|
||||
var marginTemplateDefault = yldb.margin_template_v2.Where(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && x.RuleType != marginTemplate.RuleType && x.ValueDate == marginTemplate.ValueDate);
|
||||
var existTradeTypes = marginTemplateDefault.Select(x => x.TradeTypes).ToList();
|
||||
var tradeTypes = new List<string>();
|
||||
existTradeTypes.ForEach(x => tradeTypes.AddRange(x.Split(',')));
|
||||
var marginTemplateDefault = yldb.margin_template_v2
|
||||
.Where(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && x.RuleType != marginTemplate.RuleType && x.ValueDate == marginTemplate.ValueDate)
|
||||
.ToList()
|
||||
.Where(x => margin_template_v2.AreBookScopesOverlapping(x.BookIds, marginTemplate.BookIds));
|
||||
var tradeTypes = margin_template_v2.GetOverlappingTradeTypes(marginTemplateDefault, marginTemplate.BookIds).ToList();
|
||||
|
||||
//验证相同规则相同的默认参数类型,是否包含相同的适用结构
|
||||
var sameRuleParameterRecord = yldb.margin_template_v2.FirstOrDefault(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && x.RuleType == marginTemplate.RuleType && x.DefaultParameterType == marginTemplate.DefaultParameterType && (x.BuySellType & marginTemplate.BuySellType) > 0 && (x.MarginScenarios & marginTemplate.MarginScenarios) > 0 && x.ValueDate == marginTemplate.ValueDate);
|
||||
if (sameRuleParameterRecord != null)
|
||||
{
|
||||
tradeTypes.AddRange(sameRuleParameterRecord.TradeTypes.Split(','));
|
||||
}
|
||||
var sameRuleParameterRecords = yldb.margin_template_v2
|
||||
.Where(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && x.RuleType == marginTemplate.RuleType && x.DefaultParameterType == marginTemplate.DefaultParameterType && (x.BuySellType & marginTemplate.BuySellType) > 0 && (x.MarginScenarios & marginTemplate.MarginScenarios) > 0 && x.ValueDate == marginTemplate.ValueDate)
|
||||
.ToList()
|
||||
.Where(x => margin_template_v2.AreBookScopesOverlapping(x.BookIds, marginTemplate.BookIds));
|
||||
tradeTypes.AddRange(margin_template_v2.GetOverlappingTradeTypes(sameRuleParameterRecords, marginTemplate.BookIds));
|
||||
|
||||
tradeTypes.ForEach(x =>
|
||||
{
|
||||
@@ -285,10 +289,11 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
var marginTemplateDefault = yldb.margin_template_v2.Where(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && (x.BuySellType & marginTemplate.BuySellType) > 0 && (x.MarginScenarios & marginTemplate.MarginScenarios) > 0 && x.ValueDate == marginTemplate.ValueDate);
|
||||
var existTradeTypes = marginTemplateDefault.Select(x => x.TradeTypes).ToList();
|
||||
var tradeTypes = new List<string>();
|
||||
existTradeTypes.ForEach(x => tradeTypes.AddRange(x.Split(',')));
|
||||
var marginTemplateDefault = yldb.margin_template_v2
|
||||
.Where(x => x.id != marginTemplate.id && x.IsDefault && x.IsValid && !x.IsForClient && (x.BuySellType & marginTemplate.BuySellType) > 0 && (x.MarginScenarios & marginTemplate.MarginScenarios) > 0 && x.ValueDate == marginTemplate.ValueDate)
|
||||
.ToList()
|
||||
.Where(x => margin_template_v2.AreBookScopesOverlapping(x.BookIds, marginTemplate.BookIds));
|
||||
var tradeTypes = margin_template_v2.GetOverlappingTradeTypes(marginTemplateDefault, marginTemplate.BookIds).ToList();
|
||||
tradeTypes.ForEach(x =>
|
||||
{
|
||||
if (marginTemplate.TradeTypes.Contains(x))
|
||||
@@ -471,6 +476,7 @@ namespace YLErp.Web.Controllers
|
||||
r.Name = marginTemplate.Name;
|
||||
r.IsDefault = marginTemplate.IsDefault;
|
||||
r.IsForClient = marginTemplate.IsForClient;
|
||||
r.BookIds = marginTemplate.BookIds;
|
||||
r.Comments = marginTemplate.Comments;
|
||||
r.RuleType = marginTemplate.RuleType;
|
||||
r.TradeTypes = marginTemplate.TradeTypes;
|
||||
@@ -595,6 +601,27 @@ namespace YLErp.Web.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateBookIds(string bookIds)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(bookIds))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = margin_template_v2.ParseBookIds(bookIds)
|
||||
.Select(x => int.Parse(x))
|
||||
.ToList();
|
||||
var existingIds = yldb.assetunit
|
||||
.Where(x => ids.Contains(x.id))
|
||||
.Select(x => x.id)
|
||||
.ToList();
|
||||
var missingIds = ids.Except(existingIds).ToList();
|
||||
if (missingIds.Any())
|
||||
{
|
||||
throw new Exception("适用簿记账户组包含不存在的簿记账户ID:" + string.Join(",", missingIds));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public JsonResult deleteEditmargin_template_v2(string enid)
|
||||
{
|
||||
@@ -732,4 +759,4 @@ namespace YLErp.Web.Controllers
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7980,7 +7980,11 @@ namespace YLErp.Web.Controllers
|
||||
var trade = yldb.trade.Find(tradeId);
|
||||
//R1 三层级(2026-08-24 配合修改):候选只列有效模板——已作废模板一旦绑定,
|
||||
//解析一级"找到即停"会让该交易直接视为无预付金(仅留日志),必须从源头挡住
|
||||
var marginTemplates = yldb.margin_template_v2.Where(x => x.IsValid && x.TradeTypes.Contains(trade.TradeType) && !x.IsDefault).ToList();
|
||||
var marginTemplates = yldb.margin_template_v2
|
||||
.Where(x => x.IsValid && x.TradeTypes.Contains(trade.TradeType) && !x.IsDefault)
|
||||
.ToList()
|
||||
.Where(x => x.IsApplicableToBook(trade.AssetId))
|
||||
.ToList();
|
||||
marginTemplates.ForEach(x => x.Details = yldb.margin_template_detail.Where(y => y.MarginTemplateId == x.id).ToList());
|
||||
|
||||
var latestTradeMarginTemplate = yldb.trade_margin_template.Where(x => x.TradeId == tradeId).OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
||||
@@ -8025,6 +8029,26 @@ namespace YLErp.Web.Controllers
|
||||
throw new Exception("请选择有效的预付金模板");
|
||||
}
|
||||
|
||||
var trade = yldb.trade.Find(tradeMarginTemplate.TradeId);
|
||||
if (trade == null)
|
||||
{
|
||||
throw new Exception("交易不存在");
|
||||
}
|
||||
|
||||
var marginTemplate = yldb.margin_template_v2
|
||||
.FirstOrDefault(x => x.id == tradeMarginTemplate.MarginTemplateId
|
||||
&& x.IsValid
|
||||
&& !x.IsDefault
|
||||
&& x.TradeTypes.Contains(trade.TradeType));
|
||||
if (marginTemplate == null)
|
||||
{
|
||||
throw new Exception("请选择有效的预付金模板");
|
||||
}
|
||||
if (!marginTemplate.IsApplicableToBook(trade.AssetId))
|
||||
{
|
||||
throw new Exception("所选预付金模板不适用当前簿记账户");
|
||||
}
|
||||
|
||||
var deletedRecords = yldb.trade_margin_template.Where(x => x.TradeId == tradeMarginTemplate.TradeId && x.ValueDate >= tradeMarginTemplate.ValueDate).ToArray();
|
||||
yldb.trade_margin_template.RemoveRange(deletedRecords);
|
||||
var updatedRecords = yldb.trade_margin_template.Where(x => x.TradeId == tradeMarginTemplate.TradeId && x.ValueDate < tradeMarginTemplate.ValueDate).ToList();
|
||||
@@ -8040,7 +8064,6 @@ namespace YLErp.Web.Controllers
|
||||
|
||||
#region 重置初始预付金
|
||||
|
||||
var trade = yldb.trade.Find(tradeMarginTemplate.TradeId);
|
||||
//R1 三层级(2026-08-24 配合修改):收益互换跳过系统重算——互换初始预付金为手工录入/预付金腿承载
|
||||
//(方案B 引擎回落链消费 trade.InitialMargin),且试算链路(GetInitialMarginWithPreHandle 克隆 id=0)
|
||||
//对互换取不到模板绑定恒得 0,无条件重置会把手工录入值清零;与 TradeSaveService 保存守卫语义对齐
|
||||
|
||||
@@ -525,6 +525,25 @@ namespace YLErp.Web.Controllers
|
||||
return JsonSuccess("", underlying);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询上游基金档案中的基金管理人。查询失败或结果不唯一时返回可降级结果。
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public JsonResult GetFundManager(string code, string instrumentType)
|
||||
{
|
||||
if (!string.Equals(instrumentType, ConsGlobal.InstrumentType.Fund, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return JsonSuccess("", new FundManagerLookupResult { Status = FundManagerLookupStatus.NotFound });
|
||||
}
|
||||
|
||||
var result = new FundManagerLookupService().Lookup(code);
|
||||
return JsonSuccess("", new
|
||||
{
|
||||
result.InvestAdvisorName,
|
||||
IsUnique = result.Status == FundManagerLookupStatus.Unique
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预付金参数
|
||||
/// </summary>
|
||||
|
||||
@@ -15,11 +15,20 @@
|
||||
var jsClient = canChangeClient && Model.ClientId > 0 ? jsClients.FirstOrDefault(n => n.id == Model.ClientId) : null;
|
||||
var jsAssetUnits = JsDataModel.GetAssetUnits(CurUser);
|
||||
var tradeMarginTemplates = new tradeController().GetMarginTemplates();
|
||||
var swapMarginTemplateItems = SwapMarginTemplateConfigService.GetConfig().options.ToList();
|
||||
var allSwapMarginTemplateItems = SwapMarginTemplateConfigService.GetConfig().options.ToList();
|
||||
var swapMarginTemplateItems = allSwapMarginTemplateItems.ToList();
|
||||
//保证金模板V2迁移:存量交易的保证金模板可能是历史文本值(如字典配置的"现金保证金"),不在模板V2选项中时追加显示,避免编辑保存时被静默清空
|
||||
if (!string.IsNullOrWhiteSpace(Model.MarginTemplateName) && !swapMarginTemplateItems.Any(n => n.Value == Model.MarginTemplateName))
|
||||
{
|
||||
swapMarginTemplateItems.Add(new YLErp.Models.SelectItem { Text = Model.MarginTemplateName + "(历史值)", Value = Model.MarginTemplateName });
|
||||
var legacyMarginTemplateItem = new SwapMarginTemplateOption
|
||||
{
|
||||
Text = Model.MarginTemplateName + "(历史值)",
|
||||
Value = Model.MarginTemplateName,
|
||||
BookIds = null,
|
||||
IsLegacy = true
|
||||
};
|
||||
allSwapMarginTemplateItems.Add(legacyMarginTemplateItem);
|
||||
swapMarginTemplateItems.Add(legacyMarginTemplateItem);
|
||||
}
|
||||
var tradeMarginTemplateItems = new tradeController().GetMarginTemplateItems();
|
||||
var jsAssetUnit = Model.AssetId > 0 ? jsAssetUnits.FirstOrDefault(n => n.id == Model.AssetId) : null;
|
||||
@@ -75,6 +84,7 @@
|
||||
jsTrader,
|
||||
jsTraders,
|
||||
tradeMarginTemplates = tradeMarginTemplates,
|
||||
allSwapMarginTemplateItems = allSwapMarginTemplateItems,
|
||||
swapMarginTemplateItems = swapMarginTemplateItems,
|
||||
tradeMarginTemplateItems = tradeMarginTemplateItems,
|
||||
needRemark = !isAdd && valuedateBLL.SystemDate.EditTradeNeedRemark,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
isGFSM = PS.Config.Company == CompanyEnum.广发商贸,
|
||||
etfSubtypeItems = YLErp.BLL.DictionaryBLL.GetDictionaryItems("ETF 子类").Select(x => x.Name).ToList()
|
||||
};
|
||||
var selectedBookIds = margin_template_v2.ParseBookIds(Model?.BookIds);
|
||||
}
|
||||
|
||||
@section CSS{
|
||||
@@ -63,6 +64,16 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="formlabel">适用簿记账户</label>
|
||||
<select class="chosen-select" id="BookIds" multiple data-placeholder="请选择簿记账户">
|
||||
@foreach (var item in AssetunitController.GetAllassetunit())
|
||||
{
|
||||
<option value="@item.Value" @(selectedBookIds.Contains(item.Value?.Trim() ?? "") ? "selected" : "")>@item.Text</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@if (PS.Config.Company != CompanyEnum.广发商贸)
|
||||
{
|
||||
<div class="form-group">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@using YLErp.Enums;
|
||||
@using YLErp.Enums;
|
||||
@model margin_template_v2
|
||||
|
||||
@{
|
||||
@@ -12,6 +12,7 @@
|
||||
detail = new margin_template_detail() { ValueDate = new DateTime(2020, 1, 1), ComparedInitialMarginRate = 1 },
|
||||
etfSubtypeItems = YLErp.BLL.DictionaryBLL.GetDictionaryItems("ETF 子类").Select(x => x.Name).ToList()
|
||||
};
|
||||
var selectedBookIds = margin_template_v2.ParseBookIds(Model?.BookIds);
|
||||
}
|
||||
|
||||
@section CSS{
|
||||
@@ -63,6 +64,16 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="formlabel">适用簿记账户</label>
|
||||
<select class="chosen-select" id="BookIds" multiple data-placeholder="请选择簿记账户">
|
||||
@foreach (var item in AssetunitController.GetAllassetunit())
|
||||
{
|
||||
<option value="@item.Value" @(selectedBookIds.Contains(item.Value?.Trim() ?? "") ? "selected" : "")>@item.Text</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="formlabel">适用买卖方向</label>
|
||||
<select class="chosen-select" id="BuySellType" multiple>
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
//定价格式化(来自配置)
|
||||
//定价格式化(来自配置)
|
||||
const inputFormatPercent = Object.freeze({ precision: 2, append: '%' });
|
||||
const inputFormatPercentNegative = Object.freeze({ precision: 2, append: '%', negative: true });
|
||||
const inputFormatNegative = Object.freeze({ precision: 2, negative: true });
|
||||
|
||||
function normalizeBookIds(values) {
|
||||
var ids = Array.isArray(values) ? values : (values ? [values] : []);
|
||||
var normalized = [];
|
||||
ids.forEach(function (id) {
|
||||
id = (id || '').toString().trim();
|
||||
if (id && normalized.indexOf(id) < 0) {
|
||||
normalized.push(id);
|
||||
}
|
||||
});
|
||||
return normalized.length ? normalized.join(',') : null;
|
||||
}
|
||||
|
||||
const MarginRuleTypeEnum = {
|
||||
无预付金: "0",
|
||||
按固定利率: "1",
|
||||
@@ -271,6 +283,7 @@ const vue = new Vue({
|
||||
return;
|
||||
}
|
||||
this.marginTemplate.TradeTypes = $("#tradeTypes").val().join(",");
|
||||
this.marginTemplate.BookIds = normalizeBookIds($("#BookIds").val());
|
||||
this.marginTemplate.IsDefault = true;
|
||||
this.marginTemplate.IsForClient = isForClient;
|
||||
this.marginTemplate.BuySellType = this.sum($("#BuySellType").val());
|
||||
@@ -619,4 +632,4 @@ $(function () {
|
||||
});
|
||||
|
||||
SetAceDropDown();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -264,6 +264,23 @@ const vue = new Vue({
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
refreshMarginTemplateOptions() {
|
||||
var assetId = Number(this.trade.AssetId) || 0;
|
||||
var assetToken = assetId.toString();
|
||||
var allItems = this.page.allSwapMarginTemplateItems || [];
|
||||
this.page.swapMarginTemplateItems = allItems.filter(function (item) {
|
||||
if (!assetId || item.IsLegacy || !item.BookIds) {
|
||||
return true;
|
||||
}
|
||||
return ("," + item.BookIds + ",").split(",").some(function (token) {
|
||||
return token.trim() === assetToken;
|
||||
});
|
||||
});
|
||||
if (this.trade.MarginTemplateName
|
||||
&& !this.page.swapMarginTemplateItems.some(function (item) { return item.Value === this.trade.MarginTemplateName; }, this)) {
|
||||
this.trade.MarginTemplateName = "";
|
||||
}
|
||||
},
|
||||
getQuantityInputFormat(item) {
|
||||
return swapPricePrecision.getCommonInputFormat(
|
||||
'quantity',
|
||||
@@ -1914,7 +1931,7 @@ function __init(vue) {
|
||||
});
|
||||
let traderId = page.Trade.TraderId;
|
||||
let trader = traderId ? consTraders.find(x => x.id === traderId) : null;
|
||||
if (!trader && page.IsNew) {
|
||||
if (!trader && page.isAdd) {
|
||||
trader = consTraders[0];
|
||||
trader && (vue.trade.TraderId = trader.id);
|
||||
}
|
||||
@@ -1926,15 +1943,17 @@ function __init(vue) {
|
||||
nameField: 'Name', valueField: 'id', searchField: ['Name', 'PinYin'], lookup: consAssetUnits,
|
||||
onSelect(data) {
|
||||
vue.trade.AssetId = data.id;
|
||||
vue.refreshMarginTemplateOptions();
|
||||
}
|
||||
});
|
||||
let assetId = page.Trade.AssetId;
|
||||
let asset = assetId ? consAssetUnits.find(x => x.id === assetId) : null;
|
||||
if (!asset && page.IsNew) {
|
||||
if (!asset && page.isAdd) {
|
||||
asset = consAssetUnits[0];
|
||||
asset && (vue.trade.AssetId = asset.id);
|
||||
}
|
||||
autoAssetUnit.setData(asset);
|
||||
vue.refreshMarginTemplateOptions();
|
||||
|
||||
//客户名称
|
||||
if (page.canChangeClient) {
|
||||
@@ -1948,7 +1967,7 @@ function __init(vue) {
|
||||
});
|
||||
let clientId = page.Trade.ClientId;
|
||||
let client = clientId ? consClients.find(x => x.id === clientId) : null;
|
||||
!client && page.IsNew && (client = consClients[0]);
|
||||
!client && page.isAdd && (client = consClients[0]);
|
||||
autoClient.setData(client);
|
||||
_getMainProtocolCode(client);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
const consSelect = ['MarketCode', 'UnderlyingInstrumentType', 'UnderlyingState', 'UnderlyingStatus', 'UpDownLimitType', 'EtfSubType'];
|
||||
|
||||
var autoUpDownLimit, autoVariety;
|
||||
var fundManagerLookupSeq = 0;
|
||||
var fundManagerLookupTimer = null;
|
||||
var fundManagerLookupXhr = null;
|
||||
var fundManagerManualEdit = false;
|
||||
|
||||
$(function () {
|
||||
|
||||
@@ -46,6 +50,12 @@ $(function () {
|
||||
lookup: ylotc.varieties
|
||||
});
|
||||
|
||||
$('#InvestAdvisorName').on('input', function () {
|
||||
fundManagerManualEdit = true;
|
||||
});
|
||||
|
||||
$('#UnderlyingCode').on('input', refreshFundManagerLookup);
|
||||
|
||||
for (var i = 1; i <= 5; i++) {
|
||||
let datas = ylotc.underlyingBlocks.filter(x => x.Group === i);
|
||||
let autoBlock = FastVue.autocomplete(document.getElementById('inputBlock' + i), {
|
||||
@@ -112,9 +122,57 @@ $(function () {
|
||||
break;
|
||||
}
|
||||
$('#editForm').removeClass("form-None form-Stock form-CommodityFutures form-CommoditySpot form-Bonds form-Fund").addClass("form-" + classType);
|
||||
refreshFundManagerLookup();
|
||||
}).trigger('change');
|
||||
});
|
||||
|
||||
function refreshFundManagerLookup() {
|
||||
var requestSeq = ++fundManagerLookupSeq;
|
||||
if (fundManagerLookupTimer) {
|
||||
clearTimeout(fundManagerLookupTimer);
|
||||
fundManagerLookupTimer = null;
|
||||
}
|
||||
if (fundManagerLookupXhr) {
|
||||
fundManagerLookupXhr.abort();
|
||||
fundManagerLookupXhr = null;
|
||||
}
|
||||
|
||||
var codeInput = $('#UnderlyingCode');
|
||||
var typeInput = $('#UnderlyingInstrumentType');
|
||||
var managerInput = $('#InvestAdvisorName');
|
||||
if (page.Model.id > 0 || !codeInput.length || !typeInput.length ||
|
||||
typeInput.val() !== 'Fund' || !codeInput.val() || !managerInput.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var codeAtRequest = codeInput.val();
|
||||
var managerAtRequest = managerInput.val() || '';
|
||||
var manualAtRequest = fundManagerManualEdit;
|
||||
fundManagerLookupTimer = setTimeout(function () {
|
||||
fundManagerLookupTimer = null;
|
||||
fundManagerLookupXhr = $.ajax({
|
||||
url: '/underlying_manager/GetFundManager',
|
||||
method: 'GET',
|
||||
data: { code: codeAtRequest, instrumentType: 'Fund' }
|
||||
}).done(function (resp) {
|
||||
if (requestSeq !== fundManagerLookupSeq || page.Model.id > 0 ||
|
||||
$('#UnderlyingCode').val() !== codeAtRequest ||
|
||||
$('#UnderlyingInstrumentType').val() !== 'Fund' ||
|
||||
manualAtRequest || fundManagerManualEdit) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = resp && resp.obj;
|
||||
if (result && result.IsUnique && result.InvestAdvisorName) {
|
||||
managerInput.val(result.InvestAdvisorName);
|
||||
fundManagerManualEdit = false;
|
||||
}
|
||||
}).always(function () {
|
||||
fundManagerLookupXhr = null;
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
|
||||
function saveData() {
|
||||
|
||||
var data = $('#editForm').serializeObject();
|
||||
|
||||
Reference in New Issue
Block a user