using BaseOUDAL; using OfficeOpenXml; using YLErp.Commons; using YLErp.DBModels.Enums; using YLErp.Helpers; namespace YLErp.Modules.ClientModule { /// /// 客户资金导入服务 /// public class ClientCashImportService : ClientBaseService { readonly ErpBaseContext basedb = new(); public ClientCashImportService(OptUserInfo userInfo) : base(userInfo) { } /// /// 从文件导入银行流水信息 /// public string ImportBankRecords(string fileName, bool CheckPermission = false) { if (PS.Config.Company == Configuration.CompanyEnum.申万) { return ImportBankRecordsForSywg(fileName); } var clientCashList = new List(); var bankcardList = new List(); var directionList = new List { "出金", "入金", "其他收入", "其他支出" }; ExcelPackage.LicenseContext = LicenseContext.NonCommercial; //读取文件 using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { using var package = new ExcelPackage(fs); if (package.Workbook.Worksheets.Count > 0) { var sheet = package.Workbook.Worksheets[0]; if (null == sheet.Dimension) { return "导入的文件不能为空文档!"; } var serialNumList = new List(); //当低于20列时默认为是简洁版,超过20列默认为是财务版,走财务版的逻辑 if (sheet.Dimension.End.Column - sheet.Dimension.Start.Column <= 20) { for (int m = sheet.Dimension.Start.Row + 1, n = sheet.Dimension.End.Row; m <= n; m++) { int j = sheet.Dimension.Start.Column, k = sheet.Dimension.End.Column; DateTime happenDate; try { happenDate = sheet.GetValue(m, j); if (happenDate == DateTime.MinValue) { return "第" + m + "行,发生时间转换格式错误,请检查导入符合要求格式的文件!"; } if (happenDate > DateTime.Now) { return "第" + m + "行,发生时间不能大于当前时间!"; } } catch (Exception) { return "第" + m + "行,发生时间转换格式错误,请检查导入符合要求格式的文件!"; } j++; var clientName = sheet.GetValue(m, j)?.ToString(); if (clientName.IsNullOrWhiteSpace()) { return "第" + m + "行,客户名称不能为空,请检查导入符合要求格式的文件!"; } j++; var openBankCard = sheet.GetValue(m, j)?.ToString(); if (PS.Config.Company == Configuration.CompanyEnum.中粮) { if (openBankCard.IsNullOrWhiteSpace()) { return "第" + m + "行,银行账号不能为空,请检查导入符合要求格式的文件!"; } } j++; var direction = sheet.GetValue(m, j)?.ToString(); if (direction.IsNullOrWhiteSpace()) { return "第" + m + "行,方向不能为空,请检查导入符合要求格式的文件!"; } if (!directionList.Contains(direction)) { return "第" + m + "行,方向不满足可导入方向类型,请检查导入符合要求格式的文件!"; } j++; var money = sheet.GetValue(m, j)?.ToString(); if (money.IsNullOrWhiteSpace()) { return "第" + m + "行,金额不能为空,请检查导入符合要求格式的文件!"; } j++; var currencyCode = sheet.GetValue(m, j)?.ToString(); var Currency = DbContextFactory.GetYLDbContext().currency.Find(currencyCode); var defaultValues = new string[] { "CNY", "USD" }; if (Currency == null && !string.IsNullOrWhiteSpace(currencyCode)) { throw new Exception($"币种{currencyCode}不存在"); } if (PS.Config.Company == Configuration.CompanyEnum.中金) { if (!defaultValues.Contains(currencyCode)) { throw new Exception($"出入金币种只能为CNY/USD"); } } j++; var serialNum = sheet.GetValue(m, j)?.ToString(); j++; var remark = sheet.GetValue(m, j)?.ToString(); if ((yldb.ClientCashInCashOut.Any(x => x.SerialNumber == serialNum) || serialNumList.Any(x => x == serialNum)) && !string.IsNullOrWhiteSpace(serialNum)) { return string.Format("第{0}行,流水号已经存在,请检查后再导入!", m); } var clientCash = new ClientCashInCashOut { Direction = direction, Number = UniqueTimeId.GetStr(), ClientName = clientName, Action = "Excel导入", Money = direction == "其他支出" ? -NumberHelper.ToDouble(money) : NumberHelper.ToDouble(money), CurrencyCode = currencyCode, HappenDate = happenDate, State = "未确认", OpenBankCard = openBankCard, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, CreatorId = UserId, CreatorName = UserName, CreateDate = DateTime.Now, SerialNumber = serialNum, Comments = remark, ValidState = ConsGlobal.Valid, cash_type = CashTypeEnum.初保账户.ToString() }; //客户名称检查 var client = ClientDataQueryService.GetClient(clientName); if (client != null) { if (client.ProcessStatus == "已开户") { clientCash.ClientId = client.id; clientCash.ClientNumber = client.Number; } else { return string.Format("第{0}行,客户: {1} 尚未开户,请检查后再导入!", m, clientName); } } else { return string.Format("第{0}行,客户: {1} 在系统中不存在,请检查后再导入!", m, clientName); } if (!string.IsNullOrEmpty(openBankCard)) { var bankcardModel = DbContext.bankcard.FirstOrDefault(c => c.ApprovalOrder < 1 && client.id == c.ClientId && c.ValidState != "InValid" && c.Card == openBankCard); if (bankcardModel != null) { clientCash.Uses = bankcardModel.Use; } else { return string.Format("第{0}行,客户: {1} 在系统中不存在此银行卡信息,请检查后再导入!", m, clientName); } //中金不填银行卡,银行卡用客户的 if (PS.Config.Company == Configuration.CompanyEnum.中金 && string.IsNullOrEmpty(clientCash.OpenBankCard)) { var bankCard = DbContext.bankcard.FirstOrDefault(c => c.ApprovalOrder < 1 && client.id == c.ClientId && c.ValidState != "InValid"); if (bankCard != null) { openBankCard = bankCard.Card; clientCash.OpenBankCard = bankCard.Card; clientCash.OpenBankId = bankCard.id; clientCash.OpenBank = bankCard.Bank; } else { return string.Format("第{0}行:客户 {1} 未设置银行卡,请检查后再导入!", m, client.Name); } } //检查银行卡 var bankCardModel = DbContext.bankcard.Where(c => c.ApprovalOrder < 1 && openBankCard.Equals(c.Card) && client.id == c.ClientId && c.ValidState != "InValid"); //if (CheckPermission) //{ // var dictionaryName = CheckCashAccountTypePermission(clientCash.Direction); // if (!bankCardModel.Where(b => dictionaryName.Contains(b.Use) && b.Card == openBankCard).Any()) // { // return string.Format("第{0}行,出入金方向和银行卡账号类型不匹配!", m); // } //} if ("出金".Equals(clientCash.Direction)) { var bcModel = bankCardModel.FirstOrDefault(); if (bankCardModel.FirstOrDefault() != null) { clientCash.OpenBankId = bcModel.id; clientCash.OpenBank = bcModel.Bank; } else { return string.Format("第{0}行,银行卡号:{1} 非系统有效银行卡号,请检查后再导入!", m, openBankCard); } } } else { clientCash.Uses = "全部"; var bankcardModel = DbContext.bankcard.FirstOrDefault(c => c.ApprovalOrder < 1 && client.id == c.ClientId && c.ValidState != "InValid" && c.Use=="全部"); if (bankcardModel != null) { clientCash.OpenBankCard = bankcardModel.Card; } } serialNumList.Add(serialNum); clientCashList.Add(clientCash); } } else { for (int m = sheet.Dimension.Start.Row + 1, n = sheet.Dimension.End.Row; m <= n; m++) { int j = sheet.Dimension.Start.Column, k = sheet.Dimension.End.Column; var tradeDate = sheet.GetValue(m, j) != null ? sheet.GetValue(m, j).ToString() : ""; DateTime? happenDate = null; try { happenDate = DateTime.ParseExact(tradeDate, "yyyy-MM-dd", System.Globalization.CultureInfo.CurrentCulture); } catch (Exception) { return "第" + m + "行,交易日转换格式错误,支持格式:yyyy-MM-dd,请检查导入符合要求格式的文件!"; } var tradeTime = tradeDate + " " + (sheet.GetValue(m, ++j) != null ? sheet.GetValue(m, j).ToString() : ""); try { happenDate = DateTime.ParseExact(tradeTime, "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.CurrentCulture); if (happenDate > DateTime.Now) { return "第" + m + "行,发生时间不能大于当前时间!"; } } catch (Exception) { return "第" + m + "行,交易时间转换格式错误,支持格式:HH:mm:ss,请检查导入符合要求格式的文件!"; } j += 2; var direction = string.IsNullOrEmpty(sheet.GetValue(m, ++j) != null ? sheet.GetValue(m, j).ToString() : null) ? "入金" : "出金"; var money = 0.0; if (sheet.GetValue(m, j) == null && sheet.GetValue(m, j + 1) == null) { return "第" + m + "行,借方金额、贷方金额不能同时为空!"; } else { money = double.Parse(sheet.GetValue(m, j) != null ? sheet.GetValue(m, j).ToString() : sheet.GetValue(m, j + 1).ToString()); } if (money < 0) { return "第" + m + "行,借方金额、贷方金额不能小于0!"; } j += 4; var SerialNumber = sheet.GetValue(m, j) != null ? sheet.GetValue(m, j).ToString() : ""; j += 3; var uses = sheet.GetValue(m, j)?.ToString(); j += 4; var clientName = sheet.GetValue(m, ++j) != null ? sheet.GetValue(m, j).ToString() : ""; var openBankCard = sheet.GetValue(m, ++j) != null ? sheet.GetValue(m, j).ToString() : ""; j++; var OpenBankName = sheet.GetValue(m, ++j) != null ? sheet.GetValue(m, j).ToString() : ""; if ((yldb.ClientCashInCashOut.Any(x => x.SerialNumber == SerialNumber) || serialNumList.Any(x => x == SerialNumber)) && !string.IsNullOrWhiteSpace(SerialNumber)) { return string.Format("第{0}行,流水号已经存在,请检查后再导入!", m); } var clientCash = new ClientCashInCashOut { Direction = direction, Number = UniqueTimeId.GetStr(), ClientName = clientName, Action = "Excel导入", Money = money, HappenDate = happenDate, State = "未确认", OpenBankCard = openBankCard, OpenBank = OpenBankName, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, CreatorId = UserId, CreatorName = UserName, CreateDate = DateTime.Now, SerialNumber = SerialNumber, Uses = uses, ValidState = ConsGlobal.Valid, cash_type = CashTypeEnum.初保账户.ToString() }; //客户名称检查 var client = ClientDataQueryService.GetClient(clientName); if (client != null) { if (client.ProcessStatus == "已开户") { clientCash.ClientId = client.id; clientCash.ClientNumber = client.Number; } else { return string.Format("第{0}行,客户: {1} 尚未开户,请检查后再导入!", m, clientName); } } else { return string.Format("第{0}行,客户: {1} 在系统中不存在,请检查后再导入!", m, clientName); } if (openBankCard.IsNullOrWhiteSpace()) { return string.Format("第{0}行,银行卡号不能为空,请检查后再导入!", m); } //检查银行卡 var bankCard = DbContext.bankcard.Where(c => c.ApprovalOrder < 1 && openBankCard.Equals(c.Card) && client.id == c.ClientId && c.ValidState != "InValid"); //if (CheckPermission) //{ // var dictionaryName = CheckCashAccountTypePermission(clientCash.Direction); // if (!bankCard.Where(b => dictionaryName.Contains(b.Use) && b.Card == openBankCard).Any()) // { // return string.Format("第{0}行,出入金方向和银行卡账号类型不匹配!", m); // } //} if ("出金".Equals(clientCash.Direction)) { var bcModel = bankCard.FirstOrDefault(); if (bankCard != null) { clientCash.OpenBankId = bcModel.id; clientCash.OpenBank = bcModel.Bank; } else { return string.Format("第{0}行,银行卡号:{1} 非系统有效银行卡号,请检查后再导入!", m, openBankCard, clientName); } } //if ("出金".Equals(clientCash.Direction)) //{ // //当前抵押且确认的抵押品记录(包含抵押确认状态的记录和赎回待确认或者赎回拒绝的记录) // var entrysExecutedProduct = db.clientcashincashout_product.Where(t => // t.ClientId == clientCash.ClientId && // (ClientCashInCashOut.已确认.Equals(t.OptStatus) && (Clientcashincashout_productStatusEnum.抵押 // .ToString().Equals(t.Status)) || // (t.OptStatus != ClientCashInCashOut.已确认 && (Clientcashincashout_productStatusEnum.赎回.ToString() // .Equals(t.Status))))).ToList(); // //获取标的信息 // var underlyingIds = entrysExecutedProduct.Select(x => x.UnderlyingId).ToList(); // var underlyings = db.underlying_manager.Where(x => underlyingIds.Contains(x.id)); // //当前客户总的抵押品的金额(赎回的不算) // var executedMoneyProduct = entrysExecutedProduct.Sum(t => // (underlyings.FirstOrDefault(x => x.id == t.UnderlyingId) == null // ? 0 // : underlyings.FirstOrDefault(x => x.id == t.UnderlyingId).Price) * // t.ProductAmount * t.Rate); // //查看当前客户可用资金是否符合出金条件 // var clientbalance = ClientBalanceUtility // .GetClientBanlances(new List { clientCash.ClientId.Value }, // DateTime.MinValue, DateTime.Now.Date).FirstOrDefault(); // //当日可用资金(除去抵押品部分的价值,默认客户先消费现金,再消费抵押品,所以出金时可用资金要除去抵押品的价值) // var availableFund = (clientbalance == null ? 0 : clientbalance.AvailableAmount) - // (executedMoneyProduct ?? 0.0); // //已存在的该客户的导入出金数据 // var existMoney = clientCashList.Where(x => // x.ClientId == clientCash.ClientId && "出金".Equals(x.Direction)) // .Sum(x => x.Money); // //如果出金大于可用资金则抛出错误 // if ((clientCash.Money ?? 0.0) + (existMoney ?? 0.0) > availableFund) // { // return "添加出金失败,客户:" + clientCash.ClientName + " 实际可取出资金:" + // availableFund.ToString("0.00") + ",当前导入的出金总额超过了该客户可取金额,请检查确认后再进行导入;"; // } //} serialNumList.Add(SerialNumber); clientCashList.Add(clientCash); } } } else { return "表格sheet为空!"; } } //保存 if (clientCashList.Count > 0) { yldb.ClientCashInCashOut.AddRange(clientCashList); yldb.SaveChanges(); foreach (var item in clientCashList) { var ClientCashLogdata = ClientCashLog(item.id, "导入资金记录"); yldb.clientcash_log.Add(ClientCashLogdata); } yldb.SaveChanges(); } return null; } /// /// 检测出金(收款账户、全部),入金(付款账户、全部) /// public List CheckCashAccountTypePermission(string direction) { var dictionaryName = (from dicItem in basedb.DictionaryItems join dic in basedb.Dictionaries on dicItem.DictId equals dic.Id where dic.Name == "资金用途" && !(direction.Contains("出金") ? "付款账户" : "收款账户").Contains(dicItem.ShortName) select dicItem.Name).ToList(); return dictionaryName; } /// /// 资金记录导入日志 /// /// /// public ClientCashLog ClientCashLog(int CashId, string Type) { var clientcashlog = new ClientCashLog() { CashId = CashId, OptType = Type, OptId = UserId, OptName = UserName, OptDate = DateTime.Now }; return clientcashlog; } /// /// 从文件导入银行流水信息 /// public string ImportBankRecordsForSywg(string fileName) { var errorMessage = string.Empty; var clientCashList = new List(); //读取文件 using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { using var package = new ExcelPackage(fs); if (package.Workbook.Worksheets.Count > 0) { var sheet = package.Workbook.Worksheets[1]; if (null == sheet.Dimension) { return "导入的文件不能为空文档!"; } if (sheet.Dimension.Start.Row == sheet.Dimension.End.Row) { return "导入文件只有标题行!"; } for (int m = sheet.Dimension.Start.Row + 1, n = sheet.Dimension.End.Row; m <= n; m++) { int j = sheet.Dimension.Start.Column, k = sheet.Dimension.End.Column; var serialNumber = sheet.GetValue(m, j) != null ? sheet.GetValue(m, j).ToString() : ""; j += 6; var debit = NumberHelper.ToDouble(sheet.GetValue(m, j)?.ToString()); j++; var credit = NumberHelper.ToDouble(sheet.GetValue(m, j)?.ToString()); if (debit == 0 && credit == 0) { errorMessage += "第" + m + "行,借方金额、贷方金额不能同时为0!"; continue; } if (debit < 0 || credit < 0) { errorMessage += "第" + m + "行,借方金额或贷方金额不能小于0!"; continue; } var money = debit == 0 ? credit : debit; var direction = credit > 0 ? "入金" : "出金"; j++; var accountBalance = NumberHelper.ToDouble(sheet.GetValue(m, j)?.ToString()); j += 2; var openBankCard = sheet.GetValue(m, j)?.ToString(); j++; var clientName = sheet.GetValue(m, j)?.ToString(); j++; var openBankName = sheet.GetValue(m, j)?.ToString(); j += 2; var tradeDate = sheet.GetValue(m, j)?.ToString(); DateTime? happenDate = null; try { happenDate = DateTime.ParseExact(tradeDate, "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.CurrentCulture); //happenDate = happenDate.Value.Date; } catch (Exception) { errorMessage += "第" + m + "行,交易日期转换格式错误,请检查导入符合要求格式的文件!"; continue; } j++; var uses = sheet.GetValue(m, j)?.ToString(); var clientCash = new ClientCashInCashOut { Direction = direction, Number = UniqueTimeId.GetStr(), ClientName = clientName, Action = "Excel导入", Money = money, HappenDate = happenDate, // 特殊处理:如果是通过资金监控程序读取的申万资金流水文件,State默认为"已确认" State = UserName == "申万服务出入金操作" ? ClientCashInCashOut.已确认 : ClientCashInCashOut.未确认, OpenBank = openBankName, OptId = UserId, OptName = UserName, OptDate = DateTime.Now, CreatorId = UserId, CreatorName = UserName, CreateDate = DateTime.Now, SerialNumber = serialNumber, AccountBalance = accountBalance, Uses = uses }; //客户名称检查 var client = ClientDataQueryService.GetClient(clientName); if (client != null) { if (client.ProcessStatus == "已开户") { clientCash.ClientId = client.id; clientCash.ClientNumber = client.Number; } else { errorMessage += string.Format("第{0}行,客户: {1} 尚未开户,请检查后再导入!", m, clientName); continue; } } else { errorMessage += string.Format("第{0}行,客户: {1} 在系统中不存在,请检查后再导入!", m, clientName); continue; } //检查银行卡 var bankCard = DbContext.bankcard.FirstOrDefault(c => c.ApprovalOrder < 1 && openBankCard.Equals(c.Card)); if (bankCard != null) { //银行卡是否与绑定客户对应 if (bankCard.ClientId != client.id) { errorMessage += string.Format("第{0}行,银行卡号:{1} 与客户:{2} 绑定关系不对应,请检查后再导入!", m, openBankCard, clientName); continue; } else if (bankCard.ValidState == "InValid") { errorMessage += string.Format("第{0}行,客户:{1} 银行卡号:{2} 该银行卡状态为无效,请检查后再导入!", m, clientName, openBankCard); continue; } else { clientCash.OpenBankId = bankCard.id; clientCash.OpenBank = bankCard.Bank; clientCash.OpenBankCard = bankCard.Card; } } else { errorMessage += string.Format("第{0}行,银行卡号:{1} 未在系统中登记,请检查后再导入!", m, openBankCard, clientName); continue; } clientCashList.Add(clientCash); } } else { return "表格sheet为空!"; } } clientCashList = FilterHappnedRecords(clientCashList); //保存 if (clientCashList.Count > 0) { yldb.ClientCashInCashOut.AddRange(clientCashList); yldb.SaveChanges(); } return errorMessage; } /// /// 将数据库里已存在的流水记录过滤掉。 /// 过滤逻辑是按待导入的记录的日期,查询数据库里对应日期的所有流水记录, /// 如果数据库里已经存在某一流水号,则将该条记录过滤掉 /// /// 待导入记录 /// 数据库 /// 过滤后的记录 private List FilterHappnedRecords(List records) { if (records == null || records.Count == 0) { return records; } var happenDates = records.Select(x => x.HappenDate).Distinct().ToList(); var happenedSerialNumbers = yldb.ClientCashInCashOut.Where(x => x.HappenDate.HasValue && happenDates.Contains(x.HappenDate.Value)).Select(x => x.SerialNumber).ToList(); return records.Where(x => !happenedSerialNumbers.Contains(x.SerialNumber)).ToList(); } } }