using Edge.Core.Processor; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Gateway.POS.Models; using Edge.Core.UniversalApi; using AutoMapper; using Edge.Core.Processor.Dispatcher.Attributes; using Gateway.Payment.Shared; namespace Gateway.POS { [MetaPartsDescriptor( "lang-zh-cn:轻简POSlang-en-us:Lite POS", "lang-zh-cn:轻简POSlang-en-us:Lite POS", new[] { "lang-zh-cn:轻简POSlang-en-us:LitePOS" })] public partial class App : IAppProcessor { private bool enableTestingMode = false; private Gateway.Payment.App paymentApp; private ClassicCpuCardApp.App classicCpuCardApp; private ILogger logger = NullLogger.Instance; private IServiceProvider services; public string MetaConfigName { get; set; } /// /// must be 6 digits /// private string siteId = "abcdef"; public void Init(IEnumerable processors) { this.paymentApp = processors.WithHandlerOrApp().SelectHandlerOrAppThenCast().FirstOrDefault(); if (this.paymentApp == null) this.logger.LogInformation("There's no Gateway.Payment.App defined, so only support cash payment."); this.classicCpuCardApp = processors.WithHandlerOrApp().SelectHandlerOrAppThenCast().FirstOrDefault(); if (this.classicCpuCardApp == null) this.logger.LogInformation("There's no ClassicCpuCardApp.App defined, so no support for cpu card."); } public App(bool enableTestingMode, IServiceProvider services) { this.services = services; var loggerFactory = services.GetRequiredService(); this.logger = loggerFactory.CreateLogger("DynamicPrivate_Gateway.POS"); this.enableTestingMode = enableTestingMode; } public Task Start() { this.logger.LogInformation("Migrating database..."); var migrateDbContext = new PosAppDbContext(); try { migrateDbContext.Database.Migrate(); } catch (Exception exx) { string migrationsStr = ""; string pendingMigrationsStr = ""; string appliedMigrationsStr = ""; try { migrationsStr = migrateDbContext.Database?.GetMigrations()?.Aggregate("", (acc, n) => acc + ", " + n) ?? ""; pendingMigrationsStr = migrateDbContext.Database?.GetPendingMigrations()?.Aggregate("", (acc, n) => acc + ", " + n) ?? ""; appliedMigrationsStr = migrateDbContext.Database?.GetAppliedMigrations()?.Aggregate("", (acc, n) => acc + ", " + n) ?? ""; } catch { } this.logger.LogError("Gateway.POS App Exceptioned when Migrating the database, detail: " + exx + System.Environment.NewLine + "migrations are: " + migrationsStr + System.Environment.NewLine + ", pendingMigrations are: " + pendingMigrationsStr + System.Environment.NewLine + ", appliedMigrations are: " + appliedMigrationsStr); throw new InvalidOperationException("failed for migrating the database"); } this.logger.LogInformation(" Migrate database finished."); return Task.FromResult(true); } //Task Stop() { return Task.FromResult(true); } [UniversalApi] public async Task Commit(TransactionInputDto input) { var mapper = this.services.GetRequiredService(); var trx = mapper.Map(input); trx.ServerSideTimestamp = DateTime.Now; try { var dbContext = new PosAppDbContext(); var foundOp = await dbContext.Operators.FindAsync(new object[] { input.OperatorId }); if (foundOp == null) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Input OperatorId does not map to existed operator.", }; return output; } trx.Operator = foundOp; if (trx.TransactionType == TransactionTypeEnum.Sale && (trx.Payments == null || !trx.Payments.Any())) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Must provide at least one Payment.", }; return output; } if (trx.TransactionType == TransactionTypeEnum.Sale && input.RefundTrxId != null) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Trx with type Sale should not providing RefundTrxId.", }; return output; } if (trx.TransactionType == TransactionTypeEnum.Sale && trx.Payments.Select(p => p.Method).Except(new[] { PaymentMethodEnum.Cash, PaymentMethodEnum.ClassicCpuCard, PaymentMethodEnum.DirectMembershipProfitAccountFundRedeem }).Any() && this.paymentApp == null) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Only support Cash due to no other Mop enabled.", }; return output; } if (trx.TransactionType == TransactionTypeEnum.Sale && trx.Payments.Count >= 2) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Only support single payment for a trx.", }; return output; } if (trx.TransactionType == TransactionTypeEnum.Sale && trx.Payments.Sum(p => p.ExpectAmount) != trx.NetAmount) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Payment(s) expect amount(s) must exactly equals Trx.NetAmount since split payment is not support yet.", }; return output; } if (trx.TransactionType == TransactionTypeEnum.Sale) { trx.ReceiptId = DateTime.Now.ToString("yyMMddHHmmssfff" + (trx.Operator?.Id.ToString() ?? "a")); if (trx.Payments.First().Method == PaymentMethodEnum.Cash) { trx.Payments.First().PaidAmount = trx.Payments.First().ExpectAmount; trx.Payments.First().TradeStatus = TradeStatusEnum.SUCCESS; } else { if (this.enableTestingMode) trx.NetAmount = new decimal(0.02); this.logger.LogDebug($"Committing with TrxType.Sale with MOP: {trx.Payments.First().Method }, netAmount: { trx.NetAmount.Value}, authCode: {trx.Payments.First().AuthCode}"); if (trx.Payments.First().Method == PaymentMethodEnum.ClassicCpuCard) { var unitOfWorkDb = dbContext; var redeemInput = new Gateway.POS.Models.MembershipProfitAccountRedeemRequest(); redeemInput.SourceTrx = trx; redeemInput.CreateByOperatorId = trx.OperatorId; redeemInput.Description = trx.Comment; redeemInput.ProfitAccountType = Gateway.POS.Models.MembershipProfitAccountProfitTypeEnum.Fund; redeemInput.Purpose = trx.Comment; redeemInput.RedeemAuthCode = trx.Payments.First().AuthCode; redeemInput.RedeemProfitAmount = trx.NetAmount; var redeemResult = await this.InternalRedeemMembershipProfitByOpenClassicCpuCardReaderAndReadCard(trx.Payments.First().CardReaderName, redeemInput, dbContext); if (redeemResult.OverallResultCode == 200) { this.logger.LogDebug(" Succeed for Commit with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value); trx.Payments.First().PaidAmount = trx.NetAmount.Value;// trx.Payments.First().ExpectAmount; trx.Payments.First().TradeStatus = TradeStatusEnum.SUCCESS; trx.MembershipProfitAccountRedeem = redeemResult.Data as MembershipProfitAccountRedeem; } else { this.logger.LogDebug($" Failed for Commit with TrxType.Sale with MOP: {trx.Payments.First().Method}, netAmount: {trx.NetAmount.Value }: {redeemResult.Message.ToString()}"); var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"CPU卡支付失败: {redeemResult.Message}" }; return output; } } else if (trx.Payments.First().Method == PaymentMethodEnum.DirectMembershipProfitAccountFundRedeem) { if (!trx.Payments.First().TargetMembershipSubAccountId.HasValue) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = $"DirectMembershipProfitAccountFundRedeem must specify entry TargetMembershipSubAccountId in Payment" }; return output; } var unitOfWorkDb = dbContext; var redeemInput = new Gateway.POS.Models.MembershipProfitAccountRedeemRequest(); redeemInput.SourceTrx = trx; redeemInput.CreateByOperatorId = trx.OperatorId; redeemInput.Description = trx.Comment; redeemInput.ProfitAccountType = Gateway.POS.Models.MembershipProfitAccountProfitTypeEnum.Fund; redeemInput.Purpose = trx.Comment; redeemInput.RedeemAuthCode = trx.Payments.First().AuthCode; redeemInput.RedeemProfitAmount = trx.NetAmount; var redeemResult = await this.InternalRedeemMembershipProfit(trx.Payments.First().TargetMembershipSubAccountId.Value, redeemInput, dbContext); if (redeemResult.OverallResultCode == 200) { this.logger.LogDebug(" Succeed for Commit with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value); trx.Payments.First().PaidAmount = trx.NetAmount.Value;// trx.Payments.First().ExpectAmount; trx.Payments.First().TradeStatus = TradeStatusEnum.SUCCESS; trx.MembershipProfitAccountRedeem = redeemResult.Data as MembershipProfitAccountRedeem; } else { this.logger.LogDebug($" Failed for Commit with TrxType.Sale with MOP: {trx.Payments.First().Method}, netAmount: {trx.NetAmount.Value }: {redeemResult.Message.ToString()}"); var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"DirectMembershipProfitAccountFundRedeem Payment failed with: {redeemResult.Message}" }; return output; } } else { var po = await this.paymentApp.StartPayment(new PaymentOrderDto() { AuthCode = trx.Payments.First().AuthCode, PaymentMethod = trx.Payments.First().Method.ToString(), NetAmount = trx.NetAmount.Value, TotalAmount = trx.TotalAmount ?? trx.NetAmount.Value, OperatorId = trx.OperatorId.ToString(), SiteId = trx.SiteId, TerminalId = trx.TerminalId, Title = "sale from local fusion at " + DateTime.Now.ToLongTimeString(), IsForRefund = false }); if (po.TradeStatus == TradeStatusEnum.SUCCESS) { this.logger.LogDebug(" Succeed for Commit with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value + ", billNumber: " + po.BillNumber); trx.Payments.First().PaidAmount = trx.Payments.First().ExpectAmount; trx.Payments.First().BillNumber = po.BillNumber; trx.Payments.First().TradeStatus = TradeStatusEnum.SUCCESS; } else { this.logger.LogDebug($" Failed for Commit with TrxType.Sale with MOP: {trx.Payments.First().Method}, netAmount: {trx.NetAmount.Value }, Payment failed with PaymentResultMessage: {po.PayResults.FirstOrDefault()?.PaymentResultMessage ?? ""}, PaymentResultCode: { po.PayResults.FirstOrDefault()?.PaymentResultCode ?? ""}, PaymentResultErrorDetail: { po.PayResults.FirstOrDefault()?.PaymentResultErrorDetail ?? ""}, PaymentResultRawResult: { po.PayResults.FirstOrDefault()?.PaymentResultRawResult ?? ""}"); var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Payment failed with PaymentResultMessage: {po.PayResults.FirstOrDefault()?.PaymentResultMessage ?? ""}, PaymentResultCode: {po.PayResults.FirstOrDefault()?.PaymentResultCode ?? ""}, PaymentResultErrorDetail: {po.PayResults.FirstOrDefault()?.PaymentResultErrorDetail ?? ""}, PaymentResultRawResult: {po.PayResults.FirstOrDefault()?.PaymentResultRawResult ?? ""}" }; return output; } } } if (trx.TransactionSubType == TransactionSubTypeEnum.MembershipProfitAccount_Recharge_Fund_ToAccount_ById) { if (trx.Payments.First().TradeStatus != TradeStatusEnum.SUCCESS) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToAccount failed at Payment stage" }; return output; } if ((trx.Payments.First().TargetMembershipAccountId ?? 0) == 0) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToAccount failed at Recharge stage, must specify the Target Membership Account Id, and a refund may requried since the real payment has done." }; return output; } var unitOfWorkDb = dbContext; var recharge = new Gateway.POS.Models.MembershipProfitAccountRechargeRequest(); recharge.SourceTrx = trx; recharge.CreateByOperatorId = trx.OperatorId; recharge.Description = trx.Comment; recharge.RechargeSource = ProfitAccountRechargeSourceEnum.BySystemTrx; recharge.RechargeAmount = trx.Payments.First().PaidAmount; var rechargeResult = await this.InternalRechargeMembershipAccount( trx.Payments.First().TargetMembershipAccountId.Value, MembershipProfitAccountProfitTypeEnum.Fund, recharge, unitOfWorkDb); if (rechargeResult.OverallResultCode == 200) { this.logger.LogDebug(" Succeed for Recharge with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value); trx.MembershipProfitAccountRecharge = rechargeResult.Data as MembershipProfitAccountRecharge; } else { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToAccount failed at Recharge stage, and a refund may requried since the real payment has done: {rechargeResult.Message ?? ""}" }; return output; } } else if (trx.TransactionSubType == TransactionSubTypeEnum.MembershipProfitAccount_Recharge_Fund_ToSubAccount_ById) { if (trx.Payments.First().TradeStatus != TradeStatusEnum.SUCCESS) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToSubAccount failed at Payment stage" }; return output; } if ((trx.Payments.First().TargetMembershipSubAccountId ?? 0) == 0) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToSubAccount failed at Recharge stage, must specify the Target Membership SubAccount Id, and a refund may requried since the real payment has done." }; return output; } var unitOfWorkDb = dbContext; var recharge = new Gateway.POS.Models.MembershipProfitAccountRechargeRequest(); recharge.SourceTrx = trx; recharge.CreateByOperatorId = trx.OperatorId; recharge.Description = trx.Comment; recharge.RechargeSource = ProfitAccountRechargeSourceEnum.BySystemTrx; recharge.RechargeAmount = trx.Payments.First().PaidAmount; var rechargeResult = await this.InternalRechargeMembershipSubAccount( trx.Payments.First().TargetMembershipSubAccountId.Value, MembershipProfitAccountProfitTypeEnum.Fund, recharge, unitOfWorkDb); if (rechargeResult.OverallResultCode == 200) { this.logger.LogDebug(" Succeed for Recharge with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value); trx.MembershipProfitAccountRecharge = rechargeResult.Data as MembershipProfitAccountRecharge; } else { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToSubAccount failed at Recharge stage, and a refund may requried since the real payment has done: {rechargeResult.Message ?? ""}" }; return output; } } else if (trx.TransactionSubType == TransactionSubTypeEnum.MembershipProfitAccount_Recharge_Fund_ToSubAccount_ByCard) { if (trx.Payments.First().TradeStatus != TradeStatusEnum.SUCCESS) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToSubAccount failed at Payment stage" }; return output; } var unitOfWorkDb = dbContext; var recharge = new Gateway.POS.Models.MembershipProfitAccountRechargeRequest(); recharge.SourceTrx = trx; recharge.CreateByOperatorId = trx.OperatorId; recharge.Description = trx.Comment; recharge.RechargeSource = ProfitAccountRechargeSourceEnum.BySystemTrx; recharge.RechargeAmount = trx.Payments.First().PaidAmount; var rechargeResult = await this.InternalRechargeMembershipSubAccountByOpenClassicCpuCardReaderAndReadCard( trx.Payments.First().CardReaderName, MembershipProfitAccountProfitTypeEnum.Fund, recharge, unitOfWorkDb); if (rechargeResult.OverallResultCode == 200) { this.logger.LogDebug(" Succeed for Recharge with TrxType.Sale with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value); trx.MembershipProfitAccountRecharge = rechargeResult.Data as MembershipProfitAccountRecharge; } else { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Recharge_Fund_ToSubAccount failed at Recharge stage, and a refund may requried since the real payment has done: {rechargeResult.Message ?? ""}" }; return output; } } } else if (trx.TransactionType == TransactionTypeEnum.Refund) { if (input.RefundTrxId == null) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = "Must provide original trx id for refund.", }; return output; } var db = new PosAppDbContext(); var originalTrx = await db.Transactions.Include(t => t.Payments).FirstOrDefaultAsync(t => t.Id == input.RefundTrxId); if (originalTrx == null) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = $"Could not find original trx with id: {input.RefundTrxId.Value} for refund.", }; return output; } if (originalTrx.TransactionType != TransactionTypeEnum.Sale) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = $"Only can refund a previous exists Sale type trx.", }; return output; } trx.ReceiptId = "R" + (originalTrx.ReceiptId ?? ""); PaymentOrder paymentOrder = null; if (originalTrx.Payments.First().Method != PaymentMethodEnum.Cash) { if (string.IsNullOrEmpty(originalTrx.Payments.First().BillNumber)) { var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 400, Reason = $"Original trx does not contains the BillNumber in payment info which is the critical info for refund operation, contact administrator.", }; return output; } this.logger.LogDebug("Committing with TrxType.Refund with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value + ", billNumber: " + originalTrx.Payments.First().BillNumber); paymentOrder = await this.paymentApp.StartPayment(new PaymentOrderDto() { AuthCode = originalTrx.Payments.First().AuthCode, PaymentMethod = originalTrx.Payments.First().Method.ToString(), BillNumber = originalTrx.Payments.First().BillNumber, NetAmount = originalTrx.Payments.First().PaidAmount, TotalAmount = originalTrx.TotalAmount ?? originalTrx.NetAmount.Value, OperatorId = originalTrx.OperatorId.ToString(), SiteId = originalTrx.SiteId, TerminalId = originalTrx.TerminalId, Title = "refund from local fusion at " + DateTime.Now.ToLongTimeString(), IsForRefund = true }); } else { //cash always let it succeed. paymentOrder = new PaymentOrder(); paymentOrder.TradeStatus = TradeStatusEnum.SUCCESS; } if (paymentOrder.TradeStatus == TradeStatusEnum.SUCCESS) { this.logger.LogDebug(" Succeed Committing with TrxType.Refund with MOP: " + trx.Payments.First().Method + ", netAmount: " + trx.NetAmount.Value + ", billNumber: " + originalTrx.Payments.First().BillNumber); trx.TotalAmount = -originalTrx.TotalAmount; trx.NetAmount = -originalTrx.NetAmount; trx.Payments.First().TradeStatus = TradeStatusEnum.SUCCESS; trx.Payments.First().ExpectAmount = -originalTrx.Payments.First().ExpectAmount; trx.Payments.First().PaidAmount = -originalTrx.Payments.First().PaidAmount; } else { this.logger.LogDebug($" Failed for Commit with TrxType.Refund with MOP: {trx.Payments.First().Method}, netAmount: {trx.NetAmount.Value }, billNumber: {originalTrx.Payments.First().BillNumber}, Payment failed with PaymentResultMessage: {paymentOrder.PayResults.FirstOrDefault()?.PaymentResultMessage ?? ""}, PaymentResultCode: { paymentOrder.PayResults.FirstOrDefault()?.PaymentResultCode ?? ""}, PaymentResultErrorDetail: { paymentOrder.PayResults.FirstOrDefault()?.PaymentResultErrorDetail ?? ""}, PaymentResultRawResult: { paymentOrder.PayResults.FirstOrDefault()?.PaymentResultRawResult ?? ""}"); var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Payment(refund) failed with PaymentResultMessage: {paymentOrder.PayResults.FirstOrDefault()?.PaymentResultMessage ?? ""}, PaymentResultCode: {paymentOrder.PayResults.FirstOrDefault()?.PaymentResultCode ?? ""}, PaymentResultErrorDetail: {paymentOrder.PayResults.FirstOrDefault()?.PaymentResultErrorDetail ?? ""}, PaymentResultRawResult: {paymentOrder.PayResults.FirstOrDefault()?.PaymentResultRawResult ?? ""}" }; return output; } } dbContext.Transactions.Add(trx); dbContext.Entry(trx.Operator).State = EntityState.Unchanged; await dbContext.SaveChangesAsync(); var finalOutput = mapper.Map(trx); finalOutput.CommitState = new TransactionCommitState() { StateCode = 200, Reason = "", }; return finalOutput; } catch (Exception eeee) { this.logger.LogInformation($"Commit(trx) exceptioned: {eeee}"); var output = mapper.Map(trx); output.CommitState = new TransactionCommitState() { StateCode = 500, Reason = $"Generic exception in Commit(trx), detail: {eeee}", }; return output; } } [UniversalApi] public async Task> GetTransactionsByTimeRange(DateTime start, DateTime end, int pageRowCount, int pageIndex) { using (var db = new PosAppDbContext()) { var results = await db.Transactions.Include(t => t.Payments).Include(t => t.Operator).Include(t => t.FuelItems).Include(t => t.AppliedDiscounts) .Include(t => t.MembershipProfitAccountRecharge) .Include(t => t.MembershipProfitAccountRedeem) .Where(t => t.ServerSideTimestamp >= start && t.ServerSideTimestamp <= end) .OrderByDescending(t => t.ServerSideTimestamp) .Skip(pageRowCount * pageIndex).Take(pageRowCount).ToListAsync(); var mapper = this.services.GetRequiredService(); var dtos = mapper.Map>(results); return dtos; } } [UniversalApi] public async Task ValidateOperatorLogon(string operatorName, string password) { using (var db = new PosAppDbContext()) { var result = await db.Operators.FirstOrDefaultAsync(o => o.Name == operatorName && o.Password == password); if (result == null) return null; var mapper = this.services.GetRequiredService(); var op = mapper.Map(result); return op; } } [UniversalApi] public async Task> GetOperators() { using (var db = new PosAppDbContext()) { var results = await db.Operators.ToListAsync(); if (results == null) return null; var mapper = this.services.GetRequiredService(); var ops = mapper.Map>(results); return ops; } } [UniversalApi] public async Task CreateOperatorByAuthorizeCode(string authorizeCode, string newOpName, string newOpPassword, string newOpDescription) { if (authorizeCode != "666888") return null; if (string.IsNullOrEmpty(newOpName) || string.IsNullOrEmpty(newOpPassword)) return null; using (var db = new PosAppDbContext()) { var newOp = new Operator() { Name = newOpName, Password = newOpPassword, Description = newOpDescription, CreatedTimestamp = DateTime.Now }; db.Operators.Add(newOp); var c = await db.SaveChangesAsync(); if (c != 1) { this.logger.LogInformation($"Create operator with name: {newOpName ?? ""}, pwd: {newOpPassword ?? ""} failed due to effected db row count is not 1."); return null; } var mapper = this.services.GetRequiredService(); var op = mapper.Map(newOp); return op; } } [UniversalApi] public async Task ResetOperatorPasswordByAuthorizeCode(string authorizeCode, string opName, string newOpPassword) { if (authorizeCode != "666888") return null; if (string.IsNullOrEmpty(opName) || string.IsNullOrEmpty(newOpPassword)) return null; using (var db = new PosAppDbContext()) { var updatedOp = await db.Operators.FirstOrDefaultAsync(op => op.Name == opName); if (updatedOp == null) return null; updatedOp.Password = newOpPassword; var c = await db.SaveChangesAsync(); if (c != 1) { this.logger.LogInformation($"Reset operator pwd with name: {opName ?? ""}, newPwd: {newOpPassword ?? ""} failed due to effected db row count is not 1."); return null; } var mapper = this.services.GetRequiredService(); var op = mapper.Map(updatedOp); return op; } } [UniversalApi(Description = "Update or insert a discount, providing the non-zero Id property for update, otherwise for insert.")] public async Task UpsertDiscount(DiscountDto input) { var dbContext = new PosAppDbContext(); var mapper = this.services.GetRequiredService(); var discount = mapper.Map(input); if (input.Id == null || input.Id == 0) { /*create one in db*/ discount.CreatedTime = DateTime.Now; dbContext.Discounts.Add(discount); await dbContext.SaveChangesAsync(); } else { /*udpate one in db*/ discount.ModifiedTime = DateTime.Now; dbContext.Entry(discount).State = Microsoft.EntityFrameworkCore.EntityState.Modified; dbContext.Entry(discount).Property(c => c.CreatedTime).IsModified = false; //dbContext.Entry(inputTankOverallConfig).Property(c => c.ModifiedTimeStamp).IsModified = true; await dbContext.SaveChangesAsync(); } return mapper.Map(discount); } [UniversalApi(Description = "Get all discounts")] public async Task> GetDiscounts() { var dbContext = new PosAppDbContext(); var results = await dbContext.Discounts.ToListAsync(); var mapper = this.services.GetRequiredService(); return mapper.Map>(results); } } }