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:
锦麟 王
2026-08-27 20:06:20 +08:00
17 changed files with 587 additions and 100 deletions
@@ -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>
+1
View File
@@ -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_v2IsDefault && !IsForClient && IsValid && 适用收益互换,ValueDate 最新)。
/// 模板须有效适用结构含收益互换且适用当前交易簿记账户
/// 3.全局默认 margin_template_v2IsDefault && !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; }
}
}
+62 -19
View File
@@ -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
}
}
}
+25 -2
View File
@@ -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>
+12 -2
View File
@@ -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();