443 lines
19 KiB
C#
443 lines
19 KiB
C#
using BaseOUDAL;
|
|
using Qdp.Pricing.Library.Common.Interfaces;
|
|
using System.ComponentModel;
|
|
using YLErp.BLL;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Model;
|
|
using YLErp.Modules.CalculationModule;
|
|
|
|
namespace YLErp.Modules.SalesModule
|
|
{
|
|
public class SalesCommissionDetailDataService : YLBaseService
|
|
{
|
|
public SalesCommissionDetailDataService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public SalesCommissionDetailDataService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
public SearchListResult<CommissionsDetailGroupByClient> FunDunCommissionDetailQueryByClient(BaseSearchReq req, List<int> salesmen, List<string> departments, List<int> clients)
|
|
{
|
|
// EF Core 不支持from group select g 用法
|
|
//var query = from s in DbContext.SalesCommissionDetail
|
|
// where s.IsValid && s.TradeCashID != 0
|
|
// group s by s.ClientID into g
|
|
// select g;
|
|
|
|
////根据客户查询
|
|
//if (clients != null && clients.Count > 0)
|
|
//{
|
|
// query = query.Where(o => clients.Contains(o.Key));
|
|
//}
|
|
|
|
//var temp = query.ToList();
|
|
|
|
var query = DbContext.SalesCommissionDetail.Where(b => b.IsValid && b.TradeCashID != 0);
|
|
if (clients != null && clients.Count > 0)
|
|
{
|
|
query = query.Where(o => clients.Contains(o.ClientID));
|
|
}
|
|
var temp = query?.ToList()?.GroupBy(g => g.ClientID)?.ToList();
|
|
|
|
var commissionGroups = new List<CommissionsDetailGroupByClient>();
|
|
foreach (var item in temp)
|
|
{
|
|
var commissionGroup = new CommissionsDetailGroupByClient
|
|
{
|
|
id = item.Key
|
|
};
|
|
item.OrderBy(o => o.TradeID);
|
|
foreach (var c in item)
|
|
{
|
|
double? tempCommission;
|
|
if (!c.CommissionFixed)
|
|
{
|
|
tempCommission = c.CommissionProportionAmount;
|
|
}
|
|
else
|
|
{
|
|
tempCommission = c.Commission;
|
|
}
|
|
commissionGroup.commissionCount++;
|
|
commissionGroup.commissionAmount = (commissionGroup.commissionAmount == null ? 0 : commissionGroup.commissionAmount) + tempCommission;
|
|
}
|
|
var tempSalesmen = new List<int>();
|
|
foreach (var com in item)
|
|
{
|
|
tempSalesmen.Add(com.SalesmanID);
|
|
|
|
}
|
|
//根据销售员查询
|
|
if (salesmen != null && salesmen.Count > 0)
|
|
{
|
|
if (tempSalesmen.Except(salesmen).Count() == tempSalesmen.Distinct().Count())
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
var men = new SalesmenDataService(OptUser).GetSalesmen(tempSalesmen);
|
|
//根据部门查询
|
|
if (departments != null && departments.Count > 0)
|
|
{
|
|
var departmentGroup = men.GroupBy(o => o.Department).Select(o => o.Key);
|
|
if (departmentGroup.Except(departments).Count() == (departmentGroup).Distinct().Count())
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
foreach (var man in men)
|
|
{
|
|
commissionGroup.salesman += man.Name + "、";
|
|
if (commissionGroup.department == null || !commissionGroup.department.Contains(man.Department))
|
|
{
|
|
commissionGroup.department += man.Department + "、";
|
|
}
|
|
}
|
|
commissionGroup.salesman = commissionGroup.salesman?.TrimEnd('、');
|
|
commissionGroup.department = commissionGroup.department?.TrimEnd('、');
|
|
commissionGroup.clientname = DataCacheProvider.GetClientDataSource().GetData(commissionGroup.id)?.Name;
|
|
|
|
commissionGroups.Add(commissionGroup);
|
|
}
|
|
var ret = commissionGroups.AsQueryable().ToSearchList(req);
|
|
ret.Sum = new
|
|
{
|
|
AmountSum = ret.rows.Sum(o => o.commissionAmount),
|
|
CountSum = ret.rows.Sum(o => o.commissionCount)
|
|
|
|
};
|
|
return ret;
|
|
}
|
|
public SearchListResult<CommissionDetailByTrade> CommissionDetailQuery(SalesCommissionDetailReq req)
|
|
{
|
|
var salesDic = new Lazy<Dictionary<int, string>>(() =>
|
|
{
|
|
return DbContext.salesmen.Select(n => new { n.id, n.Name }).ToDictionary(n => n.id, m => m.Name);
|
|
});
|
|
var fliterSalesNames = new List<string>();
|
|
var query = from s in DbContext.SalesCommissionDetail
|
|
where s.IsValid && s.TradeCashID != 0
|
|
join t in DbContext.trade
|
|
on s.TradeID equals t.id
|
|
select new CommissionDetailByTrade
|
|
{
|
|
id = s.id,
|
|
tradeid = t.id,
|
|
tradeNumber = t.TradeNumber,
|
|
tradeStatus = t.TradeStatus,
|
|
salesmanId = s.SalesmanID,
|
|
traderName = t.TraderName,
|
|
clientName = t.ClientName,
|
|
clientId = t.ClientId.ToString(),
|
|
buySell = t.BuySell,
|
|
tradeDate = t.TradeDate,
|
|
tradeType = t.TradeType,
|
|
structureType = t.StructureType,
|
|
StockEqvNotional = t.OriginalStockEqvNotional,
|
|
TradeSinglePrice = t.TradeType == "现金流交易" ? (t.TradePrice ?? 0) : (t.TradeSinglePrice ?? 0),
|
|
commissionAmout = s.CommissionFixed ? s.Commission : s.CommissionProportionAmount,
|
|
CommissionCalcu = s.CommissionFixed ? "" : s.CommissionCalcu
|
|
};
|
|
if (!string.IsNullOrWhiteSpace(req.salesmanIds))
|
|
{
|
|
var ids = req.salesmanIds.Split(',').Select(O => int.TryParse(O, out int id) ? id : 0).ToList();
|
|
query = query.Where(o => ids.Contains(o.salesmanId));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(req.clientIds))
|
|
{
|
|
var clients = req.clientIds.Split(',').ToList();
|
|
query = query.Where(o => clients.Contains(o.clientId));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(req.TradeNumber))
|
|
{
|
|
query = query.Where(o => o.tradeNumber.Contains(req.TradeNumber));
|
|
}
|
|
|
|
if (req.tradeDateStart != null)
|
|
{
|
|
query = query.Where(o => o.tradeDate >= req.tradeDateStart);
|
|
}
|
|
|
|
if (req.tradeDateEnd != null)
|
|
{
|
|
query = query.Where(o => o.tradeDate <= req.tradeDateEnd);
|
|
}
|
|
|
|
var temp = query.ToList().OrderBy(o => o.tradeid);
|
|
if (temp.Count() == 0)
|
|
{
|
|
return null;
|
|
}
|
|
var line = new CommissionDetailByTrade();
|
|
var result = new List<CommissionDetailByTrade>();
|
|
foreach (var item in temp)
|
|
{
|
|
line = new CommissionDetailByTrade()
|
|
{
|
|
id = item.id,
|
|
tradeid = item.tradeid,
|
|
buySell = item.buySell,
|
|
clientId = item.clientId,
|
|
clientName = item.clientName,
|
|
commissionAmout = item.commissionAmout,
|
|
StockEqvNotional = item.StockEqvNotional,
|
|
TradeSinglePrice = item.TradeSinglePrice,
|
|
structureType = item.structureType,
|
|
tradeDate = item.tradeDate,
|
|
tradeNumber = item.tradeNumber,
|
|
traderName = item.traderName,
|
|
tradeStatus = item.tradeStatus,
|
|
tradeType = item.tradeType,
|
|
CommissionCalcu = item.CommissionCalcu,
|
|
};
|
|
if (salesDic.Value.TryGetValue(item.salesmanId, out var name))
|
|
{
|
|
line.salesmanName = name;
|
|
}
|
|
result.Add(line);
|
|
//if (int.TryParse(item.salesmanName, out var id) && id > 0
|
|
// && salesDic.Value.TryGetValue(id, out var name) && !string.IsNullOrWhiteSpace(name))
|
|
//{
|
|
// line.salesmanName = name;
|
|
//}
|
|
}
|
|
//if (line != null && !string.IsNullOrWhiteSpace(line.salesmanName))
|
|
//{
|
|
// line.salesmanName = line.salesmanName.TrimEnd('、');
|
|
//}
|
|
//if (JudgeSalesmanName(line, fliterSalesNames))
|
|
//{
|
|
// result.Add(line);
|
|
//}
|
|
var ret = result.AsQueryable().ToSearchList(req);
|
|
return ret;
|
|
}
|
|
/// <summary>
|
|
/// 移除交易提成
|
|
/// </summary>
|
|
/// <param name="tradeId"></param>
|
|
/// <param name="useSave"></param>
|
|
public void RemoveTradeCommissionDetail(int tradeId, bool useSave = true)
|
|
{
|
|
//删除原来的数据
|
|
var oldCommissionsDetail = DbContext.SalesCommissionDetail.Where(o => o.TradeID == tradeId);
|
|
if (oldCommissionsDetail.Any())
|
|
oldCommissionsDetail.ToList().ForEach(l =>
|
|
{
|
|
l.IsValid = false;
|
|
l.TradeCashID = l.TradeCashID == 0 ? -1 : l.TradeCashID;//这个操作是防止计算提成的时候,查到这条记录;
|
|
});
|
|
if (useSave)
|
|
{
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 保存交易提成明细
|
|
/// </summary>
|
|
public int SaveTradeCommissionDetail(int tradeId, int? ClientId, double? Commission, bool? CommissionFixed, IEnumerable<int> SalesIds, bool isValid, bool delOldData = true)
|
|
{
|
|
if (tradeId < 0)
|
|
{
|
|
throw new ServiceException("参数错误:" + nameof(tradeId));
|
|
}
|
|
|
|
if (ClientId < 1)
|
|
{
|
|
throw new ServiceException("参数错误:" + nameof(ClientId));
|
|
}
|
|
|
|
if (Commission < 0)
|
|
{
|
|
throw new ServiceException("提成金额 必须大于等于0");
|
|
}
|
|
|
|
if (delOldData)
|
|
{
|
|
//删除原来的数据
|
|
RemoveTradeCommissionDetail(tradeId, false);
|
|
|
|
}
|
|
|
|
//如果销售员,销售提成没了,那也没必要新增了
|
|
if (!SalesIds.Any())
|
|
{
|
|
DbContext.SaveChanges();
|
|
return 0;
|
|
}
|
|
|
|
if (!Commission.HasValue || Commission == 0)
|
|
{
|
|
DbContext.SaveChanges();
|
|
return 0;
|
|
}
|
|
var saleCount = SalesIds.Count();
|
|
var commissions = SalesIds.Select(n => new SalesCommissionDetail
|
|
{
|
|
ClientID = (int)ClientId,
|
|
Commission = Commission.Value / saleCount,
|
|
CommissionFixed = (bool)CommissionFixed,
|
|
OptDate = OptDate,
|
|
SalesmanID = n,
|
|
TradeID = tradeId,
|
|
IsValid = isValid
|
|
}).ToArray();
|
|
|
|
DbContext.SalesCommissionDetail.AddRange(commissions);
|
|
return DbContext.SaveChanges();
|
|
}
|
|
public void CalcuSalesCommissionDetail(trade_cash trade_Cash)
|
|
{
|
|
var trade = DbContext.trade.Where(l => l.id == trade_Cash.TradeId).FirstOrDefault();
|
|
if (trade == null) return;
|
|
var tradeClone = trade.Clone();
|
|
var salesCommissions = DbContext.SalesCommissionDetail.Where(x => x.TradeID == trade.id && x.TradeCashID == 0 && !x.CommissionFixed).ToList();
|
|
if (!salesCommissions.Any()) return;
|
|
if (!ConsTrade.TradeTypesForHedge.Contains(trade.TradeType))
|
|
{
|
|
double? nowPrice = tradeClone.SpotPrice;
|
|
//计算全额payoff
|
|
var notional = tradeClone.Notional;
|
|
tradeClone.Notional = tradeClone.OriginalNotional.Value;//原始份额不应该为Null
|
|
FangDunGetPayoff(tradeClone, Convert.ToDouble(nowPrice), out var amount);
|
|
tradeClone.Notional = notional;
|
|
//计算全额时间价值
|
|
var timePrice = (tradeClone.TradeSinglePrice.GetValueOrDefault() * tradeClone.OriginalNotional - (amount <= 0 ? 0 : amount));
|
|
//把全额时间价值换成当前了结部分的时间价值
|
|
timePrice = timePrice < 0 ? 0 : timePrice * trade_Cash.UnwindPercentRate;
|
|
//实际存续天数
|
|
var ActualExistenceDate = trade_Cash.ValueDate.Subtract(Convert.ToDateTime(trade.TradeDate)).TotalDays;
|
|
ActualExistenceDate = ActualExistenceDate < 1 ? 1 : ((int)ActualExistenceDate);
|
|
// var ActualExistenceDate = QdpModule.QdpCalendarHelper.AllBizDays(Convert.ToDateTime(trade.TradeDate), Convert.ToDateTime(tc.ValueDate)).Count;
|
|
//约定存续天数
|
|
var AgreedExistenceDate = Convert.ToDateTime(trade.ExerciseDate).Subtract(Convert.ToDateTime(trade.TradeDate)).TotalDays;
|
|
AgreedExistenceDate = AgreedExistenceDate < 1 ? 1 : ((int)AgreedExistenceDate);
|
|
// var AgreedExistenceDate = QdpModule.QdpCalendarHelper.AllBizDays(Convert.ToDateTime(trade.TradeDate), Convert.ToDateTime(trade.ExerciseDate)).Count;
|
|
|
|
foreach (var commission in salesCommissions)
|
|
{
|
|
//计算 提成金额 公式
|
|
var commissionProportionAmount = timePrice * ActualExistenceDate / AgreedExistenceDate * commission.Commission;
|
|
var commissionCalcu = $"{timePrice}*{ActualExistenceDate}/{AgreedExistenceDate}*{commission.Commission}";
|
|
commission.TradeCashID = trade_Cash.id;
|
|
commission.CommissionCalcu = commissionCalcu;
|
|
commission.CommissionProportionAmount = commissionProportionAmount;
|
|
commission.IsValid = true;
|
|
}
|
|
if (!trade_Cash.IsLastAction)
|
|
{
|
|
SaveTradeCommissionDetail(trade.id, trade.ClientId, salesCommissions.Sum(O => O.Commission), false, salesCommissions.Select(O => O.SalesmanID), false, false);
|
|
}
|
|
DbContext.SaveChanges();
|
|
}
|
|
}
|
|
public static void FangDunGetPayoff(trade trade, double price, out double amount)
|
|
{
|
|
amount = 0;
|
|
string fixings = null;
|
|
DateTime? finalPriceSettleDate = null;
|
|
tradeBLL.SetFieldsByTradeType(trade);
|
|
|
|
var isAsianOption = trade.TradeType == "亚式期权";
|
|
if (isAsianOption && trade.trade_asian_option == null)
|
|
{
|
|
throw new ServiceException("缺少亚式期权交易信息");
|
|
}
|
|
if (isAsianOption)
|
|
{
|
|
var startDate = trade.trade_asian_option.AveragingPeriodStartDate ?? trade.TradeDate.Value;
|
|
var settleDate = finalPriceSettleDate ?? valuedateBLL.ValueDate;
|
|
//亚式浮动行权价并且行权价有输入值的情况下
|
|
if (trade.trade_asian_option.StrikeType == "Floating")
|
|
{
|
|
//均价起算日大于结算日的情况下取计值日
|
|
if (startDate > settleDate)
|
|
{
|
|
startDate = settleDate;
|
|
}
|
|
fixings = $"{startDate:yyyy-MM-dd},{price}";
|
|
}
|
|
else
|
|
{
|
|
//均价起算日大于结算日的情况下取不到fixing则直接赋值
|
|
if (startDate > settleDate)
|
|
{
|
|
fixings = $"{settleDate:yyyy-MM-dd},{price}";
|
|
}
|
|
else
|
|
{
|
|
fixings = AsianOptionFixingService.GetFixingString(settleDate, trade);
|
|
if (string.IsNullOrEmpty(fixings))
|
|
{
|
|
//取不到fixing则直接赋值
|
|
fixings = $"{settleDate:yyyy-MM-dd},{price}";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var QdpTrade = TradeCalcHelper.GetQdpTrade(trade, finalPriceSettleDate, fixings);
|
|
if (QdpTrade != null && QdpTrade.Instrument != null && QdpTrade.Instrument is IOption opt)
|
|
{
|
|
var value = 1;
|
|
if (trade.BuySell == "卖出")
|
|
{
|
|
value = -1;
|
|
}
|
|
var cashFlows = opt.GetPayoff(new double[] { price });
|
|
if (cashFlows != null && (cashFlows[0].PaymentAmount == 0 || double.IsNaN(cashFlows[0].PaymentAmount)))
|
|
{
|
|
amount = TradeHelper.GetAmountByPaymentAmount(0, trade.PrincipalSum(), trade.BuySell) * value;
|
|
}
|
|
else
|
|
{
|
|
//cashFlows[0].PaymentAmount包含了买卖方向的处理了
|
|
amount = TradeHelper.GetAmountByPaymentAmount(cashFlows[0].PaymentAmount, trade.PrincipalSum(), trade.BuySell) * value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public class SalesCommissionDetailReq : BaseSearchReq
|
|
{
|
|
/// <summary>
|
|
/// 销售员ID
|
|
/// </summary>
|
|
public string salesmanIds { get; set; }
|
|
/// <summary>
|
|
/// 客户ID
|
|
/// </summary>
|
|
public string clientIds { get; set; }
|
|
/// <summary>
|
|
/// 交易编号
|
|
/// </summary>
|
|
public string TradeNumber { get; set; }
|
|
/// <summary>
|
|
/// 交易日期始
|
|
/// </summary>
|
|
public DateTime? tradeDateStart { get; set; }
|
|
/// <summary>
|
|
/// 交易日期末
|
|
/// </summary>
|
|
public DateTime? tradeDateEnd { get; set; }
|
|
}
|
|
public class CommissionsDetailGroupByClient
|
|
{
|
|
public int id;
|
|
[DisplayName("销售员")]
|
|
public string salesman { get; set; }
|
|
[DisplayName("部门")]
|
|
public string department { get; set; }
|
|
[DisplayName("客户名称")]
|
|
public string clientname { get; set; }
|
|
[DisplayName("提成金额")]
|
|
public double? commissionAmount { get; set; }
|
|
[DisplayName("交易数目")]
|
|
public int commissionCount { get; set; }
|
|
}
|
|
}
|