From 97a07bd216e89dfdf2b689757081132a25bf33b3 Mon Sep 17 00:00:00 2001 From: dibakor Date: Thu, 16 Jul 2026 18:30:18 +0600 Subject: [PATCH] complete save order endpoint --- .../Requests/MobileApp/OrderRequest.cs | 9 +- .../Responses/MobileApp/OrderResponse.cs | 55 +- .../Contracts/MobileApp/IOrderService.cs | 2 +- .../Services/MobileApp/OrderService.cs | 682 +++++++++++++++++- .../DI/ServiceCollectionExtensions.cs | 3 +- .../Mobile/MobileAuthController.cs | 5 +- .../Controllers/Mobile/OrdersController.cs | 4 +- 7 files changed, 733 insertions(+), 27 deletions(-) diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/OrderRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/OrderRequest.cs index 158b2a2..7967da3 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/OrderRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/OrderRequest.cs @@ -14,8 +14,9 @@ public class SaveOrderRequest public string? OrderNo { get; set; } public DateTime? OrderDate { get; set; } public DateTime? ExpectedDeliveryDate { get; set; } - public string UUID { get; set; } + public Guid UUID { get; set; } public string SalesOrgCode { get; set; } + public int? CustomerId { get; set; } public string CustomerCode { get; set; } public string CustomerName { get; set; } public int? EmployeeId { get; set; } @@ -24,6 +25,7 @@ public class SaveOrderRequest public string PaymentTermCode { get; set; } public string DiscountCode { get; set; } public string FocCode { get; set; } + public decimal FocValue { get; set; } public decimal DiscountValue { get; set; } public decimal GrossValue { get; set; } public decimal NetValue { get; set; } @@ -33,10 +35,14 @@ public class SaveOrderRequest public class SaveOrderItemRequest { + public int? OrderId { get; set; } + public int? ItemId { get; set; } + public int? MaterialId { get; set; } public string MaterialCode { get; set; } public string MaterialName { get; set; } public decimal UnitPrice { get; set; } public string SalesUnit { get; set; } + public decimal PromoQuantity { get; set; } public decimal OrderQuantity { get; set; } public decimal ValidatedQuantity { get; set; } public decimal ApprovedQuantity { get; set; } @@ -48,6 +54,7 @@ public class SaveOrderItemRequest public class SaveOrderPromotionRequest { + public int ItemId { get; set; } public int OrderId { get; set; } public int MaterialId { get; set; } public string PromotionCode { get; set; } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/OrderResponse.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/OrderResponse.cs index 8208d23..d2c0901 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/OrderResponse.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/OrderResponse.cs @@ -7,11 +7,20 @@ public class SaveOrderResponse { public int OrderId { get; set; } public string OrderNo { get; set; } - public string UUID { get; set; } public DateTime OrderDate { get; set; } + public Guid UUID { get; set; } public bool IsNewOrder { get; set; } } +public class OrderBasicResponse +{ + public int OrderId { get; set; } + public string OrderNo { get; set; } + public DateTime OrderDate { get; set; } + public Guid UUID { get; set; } + public OrderStatusEnum OrderStatus { get; set; } +} + public class GetOrderByUUIDResponse { public int OrderId { get; set; } @@ -73,4 +82,48 @@ public class GetOrderPromotionResponse public decimal ValidatedFocQuantity { get; set; } public decimal ApprovedFocQuantity { get; set; } public int PromotionType { get; set; } +} + + +public sealed class UserRoleDto +{ + public int UserId { get; set; } + public UserRoleTypeEnum RoleId { get; set; } +} + +public sealed class CustomerLookupDto +{ + public int CustomerId { get; set; } + public string CustomerCode { get; set; } = string.Empty; + public string CustomerName { get; set; } = string.Empty; +} + +public sealed class PaymentTermLookupDto +{ + public int PaymentTermId { get; set; } + public string PaymentTermCode { get; set; } = string.Empty; +} + +public sealed class DiscountLookupDto +{ + public int DiscountId { get; set; } + public string DiscountCode { get; set; } = string.Empty; +} + +public sealed class MaterialLookupDto +{ + public int MaterialId { get; set; } + public string MaterialCode { get; set; } = string.Empty; + public string MaterialName { get; set; } = string.Empty; +} + +public sealed class FreeGoodsRuleDto +{ + public int FreeGoodsId { get; set; } + public int MaterialId { get; set; } + public decimal MinOrderQuantity { get; set; } + public decimal ForQuantity { get; set; } + public decimal FreeQuantity { get; set; } + public string UnitOfMeasure { get; set; } = string.Empty; + public string FocUnitOfMeasure { get; set; } = string.Empty; } \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IOrderService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IOrderService.cs index beb0c58..2199480 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IOrderService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IOrderService.cs @@ -7,5 +7,5 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp; public interface IOrderService { - Task SaveOrderAsync(SaveOrderRequest request); + Task SaveOrderAsync(SaveOrderRequest request, int userId); } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/OrderService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/OrderService.cs index eab4ca1..1767b4f 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/OrderService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/OrderService.cs @@ -1,7 +1,9 @@ using Ease.NetCore.DataAccess; using Ease.NetCore.DataAccess.SQL; +using Microsoft.AspNetCore.Http; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Options; +using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; @@ -11,6 +13,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp; @@ -28,36 +31,196 @@ public class OrderService : IOrderService _masterService = masterService; } - public async Task SaveOrderAsync(SaveOrderRequest request) + //public async Task SaveOrderAsync(SaveOrderRequest request) + //{ + // SaveOrderResponse response = new(); + // try + // { + // using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode,true); + + + // GetOrderByUUIDResponse order= await GetOrderByUUIDAsync(tc, request.UUID); + + + // tc.End(); + + // } + // catch(Exception) + // { + // throw; + // } + + // return new SaveOrderResponse() + // { + // OrderId = 12345, + // OrderDate = DateTime.Now, + // OrderNo = "Ord-000001-" + DateTime.Now.ToString("HH-mm-ss"), + // UUID = request.UUID, + // IsNewOrder =true + // }; + //} + + + public async Task SaveOrderAsync(SaveOrderRequest request, int userId) { SaveOrderResponse response = new(); + try { - using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode,true); + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true); + try + { + // 1. Role - fetched using the claims-resolved UserId + UserRoleDto? userRole = await GetUserRoleAsync(tc, userId); + if (userRole is null) + throw new Exception($"User id '{userId}' was not found."); - - GetOrderByUUIDResponse order= await GetOrderByUUIDAsync(tc, request.UUID); + if (!IsPermissiontoModify(request.OrderStatus ,userRole.RoleId )) + { + throw new Exception("You are not permitted to submit of modify this order"); + } + + var existingOrder = await GetExistingOrderIdAsync(tc, request.UUID); + request.OrderId = (existingOrder != null && existingOrder.OrderId > 0)? existingOrder.OrderId : 0; + + foreach (var item in request.Items) + { + item.ItemId = 0; + item.OrderId = request.OrderId; + } - tc.End(); + // 2. Insert vs update decision + bool isUpdate = existingOrder != null && existingOrder.OrderId > 0 ; + // 3. Reference data - SPs only fetch, every pass/fail call happens here + CustomerLookupDto? customer = await GetCustomerAsync(tc, request.CustomerCode); + if (customer is null) + throw new Exception($"Customer code '{request.CustomerCode}' does not exist."); + + request.CustomerId = customer.CustomerId; + + PaymentTermLookupDto? paymentTerm = await GetPaymentTermAsync(tc, request.PaymentTermCode, request.SalesOrgCode); + if (paymentTerm is null) + throw new Exception($"Payment term code '{request.PaymentTermCode}' does not exist for sales org '{request.SalesOrgCode}'."); + + + DiscountLookupDto? discount = await GetDiscountAsync(tc, request.DiscountCode, request.SalesOrgCode); + if (discount is null) + throw new Exception($"Discount code '{request.DiscountCode}' does not exist."); + + bool focCodeExists = await CheckFocCodeExistsAsync(tc, request.FocCode, request.SalesOrgCode); + if (!focCodeExists) + throw new Exception($"FOC code '{request.FocCode}' does not exist."); + + if (request.Items is null || request.Items.Count == 0) + throw new Exception("Order must contain at least one item."); + + Dictionary materialsByCode = new(StringComparer.OrdinalIgnoreCase); + if (request.Items is { Count: > 0 }) + { + List distinctCodes = request.Items + .Select(i => i.MaterialCode) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + materialsByCode = await GetMaterialsAsync(tc, distinctCodes); + + foreach (string code in distinctCodes) + { + if (!materialsByCode.ContainsKey(code)) + throw new Exception($"Material code '{code}' does not exist."); + } + } + + var focItem = request.Items.FirstOrDefault(x => x.IsFocItem == true); + var dicountItem = request.Items.FirstOrDefault(x => x.DiscountAmount > 0); + List promotions = new List(); + + request.FocValue = 0; + + promotions.Add(new SaveOrderPromotionRequest() + { + OrderId = isUpdate ? existingOrder.OrderId : 0, + MaterialId = materialsByCode[dicountItem.MaterialCode].MaterialId, + PromotionCode =request.DiscountCode , + DiscountPercentage = dicountItem.DiscountPercentage, + DiscountValue = dicountItem.DiscountAmount, + ForQuantity = dicountItem.PromoQuantity, + OrderFocQty = 0, + ValidatedFocQty = 0, + ApprovedFocQty = 0, + PromotionType = 1, + }); + + promotions.Add(new SaveOrderPromotionRequest() + { + OrderId = isUpdate ? existingOrder.OrderId : 0, + MaterialId = materialsByCode[focItem.MaterialCode].MaterialId, + PromotionCode = request.FocCode, + DiscountPercentage = 0, + DiscountValue = 0, + ForQuantity = focItem.PromoQuantity, + OrderFocQty = (request.OrderStatus == OrderStatusEnum.Draft || request.OrderStatus == OrderStatusEnum.InOrderValidation) ? focItem.OrderQuantity : 0, + ValidatedFocQty = (request.OrderStatus == OrderStatusEnum.CancelledByValidator || request.OrderStatus == OrderStatusEnum.InOrderApproval) ? focItem.OrderQuantity : 0, + ApprovedFocQty = (request.OrderStatus == OrderStatusEnum.CancelledByApprover || request.OrderStatus == OrderStatusEnum.Approved) ? focItem.OrderQuantity : 0, + PromotionType = 1, + }); + + // 5. Insert or update + int orderId; + if (isUpdate) + { + orderId = existingOrder.OrderId; + await UpdateOrderAsync(tc, orderId, request, customer!, paymentTerm!, discount!, materialsByCode, promotions, userId,(int) userRole!.RoleId); + response.IsNewOrder = !isUpdate; + response.UUID = request.UUID; + response.OrderId = orderId; + response.OrderDate = existingOrder.OrderDate; + } + else + { + response = await InsertOrderAsync(tc, request, customer!, paymentTerm!, discount!, materialsByCode, promotions, userId,(int) userRole!.RoleId); + } + + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + throw DBCustomError.GenerateCustomError(ie); + } } - catch(Exception) + catch (Exception) { - throw; + throw; } - return new SaveOrderResponse() - { - OrderId = 12345, - OrderDate = DateTime.Now, - OrderNo = "Ord-000001-" + DateTime.Now.ToString("HH-mm-ss"), - UUID = request.UUID, - IsNewOrder =true - }; + return response; } - private async Task GetOrderByUUIDAsync(TransactionContext tc, string uuid) + private bool IsPermissiontoModify(OrderStatusEnum orderStatus, UserRoleTypeEnum roleId) + { + if( UserRoleTypeEnum.OrderCollector == roleId && ( OrderStatusEnum.Draft == orderStatus || OrderStatusEnum.InOrderValidation == orderStatus)) + { + return true; + } + + if (UserRoleTypeEnum.OrderValidator == roleId && (OrderStatusEnum.CancelledByValidator == orderStatus || OrderStatusEnum.InOrderApproval == orderStatus)) + { + return true; + } + + if (UserRoleTypeEnum.OrderApprover == roleId && (OrderStatusEnum.CancelledByApprover == orderStatus || OrderStatusEnum.Approved == orderStatus)) + { + return true; + } + + return false; + } + + + private async Task GetOrderByUUIDAsync(TransactionContext tc, Guid uuid) { GetOrderByUUIDResponse response = null; @@ -70,7 +233,6 @@ public class OrderService : IOrderService using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderDetailsByUUID", parameterValues: p)) { - // Result Set 1: Order Header if (dr.Read()) { response = new GetOrderByUUIDResponse @@ -127,7 +289,7 @@ public class OrderService : IOrderService } } - // Result Set 3: Promotions (attach to matching item) + if (response != null && dr.NextResult()) { while (dr.Read()) @@ -154,7 +316,6 @@ public class OrderService : IOrderService dr.Close(); } - tc.End(); } catch (Exception) { @@ -164,4 +325,487 @@ public class OrderService : IOrderService return response; } + private async Task GetUserRoleAsync(TransactionContext tc, int userId) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetUserRoleByUserId", parameterValues: p); + + if (dr.Read()) + { + return new UserRoleDto { UserId = dr.GetInt32(0), RoleId =(UserRoleTypeEnum) dr.GetInt32(1) }; + } + + return null; + } + catch (Exception) + { + throw; + } + + } + + private async Task GetExistingOrderIdAsync(TransactionContext tc, Guid uuid) + { + OrderBasicResponse response = null; + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UUID", pType: SqlDbType.UniqueIdentifier, pValue: uuid) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderHeaderByUUID", parameterValues: p); + + if( dr.Read()) + { + response = new OrderBasicResponse() + { + OrderId = dr.GetInt32(0), + OrderNo = dr.GetString(1), + OrderDate = dr.GetDateTime(2), + UUID = uuid, + OrderStatus = (OrderStatusEnum)dr.GetInt32(3) + }; + } + dr.Close(); + } + catch (Exception) + { + throw; + } + return response; + } + + private async Task GetCustomerAsync(TransactionContext tc, string customerCode) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@CustomerCode", pType: SqlDbType.VarChar, pValue: customerCode) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetCustomerByCode", parameterValues: p); + + if (dr.Read()) + { + return new CustomerLookupDto + { + CustomerId = dr.GetInt32(0), + CustomerCode = dr.GetString(1), + CustomerName = dr.GetString(2) + }; + } + + return null; + } + catch (Exception) + { + throw; + } + } + + private async Task GetPaymentTermAsync(TransactionContext tc, string paymentTermCode, string salesOrgCode) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@PaymentTermCode", pType: SqlDbType.VarChar, pValue: paymentTermCode), + SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue: salesOrgCode) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetPaymentTermByCode", parameterValues: p); + + if (dr.Read()) + { + return new PaymentTermLookupDto { PaymentTermId = dr.GetInt32(0), PaymentTermCode = dr.GetString(1) }; + } + + return null; + } + catch (Exception) + { + throw; + } + } + + private async Task GetDiscountAsync(TransactionContext tc, string discountCode, string salesOrgCode) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@DiscountCode", pType: SqlDbType.VarChar, pValue: discountCode), + SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue: salesOrgCode) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetDiscountByCode", parameterValues: p); + + if (dr.Read()) + { + return new DiscountLookupDto { DiscountId = dr.GetInt32(0), DiscountCode = dr.GetString(1) }; + } + + return null; + } + catch (Exception) + { + throw; + } + } + + private async Task CheckFocCodeExistsAsync(TransactionContext tc, string focCode, string salesOrgCode) + { + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue: salesOrgCode), + SqlHelperExtension.CreateInParam(pName: "@FocCode", pType: SqlDbType.VarChar, pValue: focCode) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.CheckFocCodeExists", parameterValues: p); + + return dr.Read(); + } + catch (Exception) + { + throw; + } + } + + private async Task> GetMaterialsAsync(TransactionContext tc, IEnumerable materialCodes) + { + try + { + Dictionary result = new(StringComparer.OrdinalIgnoreCase); + + DataTable codesTable = new(); + codesTable.Columns.Add("Code", typeof(string)); + foreach (string code in materialCodes) + codesTable.Rows.Add(code); + + SqlParameter codesParam = SqlHelperExtension.CreateInParam(pName: "@Codes", pType: SqlDbType.Structured, pValue: codesTable); + codesParam.TypeName = "dbo.CodeListTableType"; // CreateInParam has no TypeName slot, so it's set separately + + SqlParameter[] p = [codesParam]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetMaterialsByCodes", parameterValues: p); + + while (dr.Read()) + { + MaterialLookupDto dto = new() + { + MaterialId = dr.GetInt32(0), + MaterialCode = dr.GetString(1), + MaterialName = dr.GetString(2) + }; + + result[dto.MaterialCode] = dto; + } + + dr.Close(); + return result; + } + catch (Exception) + { + throw; + } + } + + private async Task> GetFreeGoodsRulesAsync(TransactionContext tc, string salesOrgCode, string focCode, IEnumerable materialIds, DateTime? asOfDate, int customerId) + { + try + { + List rules = new(); + + DataTable idsTable = new(); + idsTable.Columns.Add("Id", typeof(int)); + foreach (int id in materialIds) + idsTable.Rows.Add(id); + + SqlParameter idsParam = SqlHelperExtension.CreateInParam(pName: "@MaterialIds", pType: SqlDbType.Structured, pValue: idsTable); + idsParam.TypeName = "dbo.IntListTableType"; + + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue: salesOrgCode), + SqlHelperExtension.CreateInParam(pName: "@CustomerId", pType: SqlDbType.VarChar, pValue: customerId), + SqlHelperExtension.CreateInParam(pName: "@FocCode", pType: SqlDbType.VarChar, pValue: focCode),idsParam, + SqlHelperExtension.CreateInParam(pName: "@AsOfDate", pType: SqlDbType.DateTime, pValue: DateTime.Now) + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetFreeGoodsRules", parameterValues: p); + + while (dr.Read()) + { + rules.Add(new FreeGoodsRuleDto + { + FreeGoodsId = dr.GetInt32(0), + MaterialId = dr.GetInt32(1), + MinOrderQuantity = dr.GetDecimal(2), + ForQuantity = dr.GetDecimal(3), + FreeQuantity = dr.GetDecimal(4), + UnitOfMeasure = dr.IsDBNull(5) ? string.Empty : dr.GetString(5), + FocUnitOfMeasure = dr.IsDBNull(6) ? string.Empty : dr.GetString(6) + }); + } + + dr.Close(); + return rules; + } + catch (Exception) + { + throw; + } + } + + // Once OrderQuantity >= MinOrderQuantity, the material earns FreeQuantity for every + // ForQuantity units ordered (e.g. Min 10 / For 10 / Free 1 -> ordering 25 = floor(25/10)*1 = 2 free). + // This is my read of "calculate based on order quantity" - swap the formula below if yours differs. + private static List BuildFocPromotions(SaveOrderRequest request, + Dictionary materialsByCode,List focRules) + { + try + { + List promotions = new(); + + Dictionary rulesByMaterialId = focRules + .GroupBy(r => r.MaterialId) + .ToDictionary(g => g.Key, g => g.First()); // first active rule wins if more than one overlaps + + foreach (SaveOrderItemRequest item in request.Items) + { + if (item.IsFocItem) + continue; // a free item doesn't itself trigger more free goods + + if (!materialsByCode.TryGetValue(item.MaterialCode, out MaterialLookupDto? material)) + continue; // already caught by validation - guarded here defensively + + if (!rulesByMaterialId.TryGetValue(material.MaterialId, out FreeGoodsRuleDto? rule)) + continue; // no free-goods rule configured for this material under this FOC code + + if (rule.ForQuantity <= 0 || item.OrderQuantity < rule.MinOrderQuantity) + continue; + + decimal eligibleSets = Math.Floor(item.OrderQuantity / rule.ForQuantity); + decimal focQuantity = eligibleSets * rule.FreeQuantity; + + if (focQuantity <= 0) + continue; + + promotions.Add(new SaveOrderPromotionRequest + { + MaterialId = material.MaterialId, + PromotionCode = request.FocCode, + DiscountPercentage = 0, + DiscountValue = 0, + ForQuantity = rule.ForQuantity, + OrderFocQty = focQuantity, + ValidatedFocQty = 0, + ApprovedFocQty = 0, + PromotionType = 1 // TODO: swap for your real "free goods" PromotionType constant + }); + } + + return promotions; + } + catch (Exception) + { + throw; + } + } + + private static DataTable ToOrderItemTable(IEnumerable items, Dictionary materialsByCode, int orderStatus ) + { + try + { + DataTable table = new(); + table.Columns.Add("OrderId", typeof(int)); + table.Columns.Add("ItemId", typeof(int)); + table.Columns.Add("MaterialId", typeof(int)); + table.Columns.Add("MaterialCode", typeof(string)); + table.Columns.Add("MaterialName", typeof(string)); + table.Columns.Add("UnitPrice", typeof(decimal)); + table.Columns.Add("SalesUnit", typeof(string)); + table.Columns.Add("PromoQty", typeof(decimal)); + table.Columns.Add("OrderQuantity", typeof(decimal)); + table.Columns.Add("ValidatedQuantity", typeof(decimal)); + table.Columns.Add("ApprovedQuantity", typeof(decimal)); + table.Columns.Add("DiscountPercentage", typeof(decimal)); + table.Columns.Add("DiscountAmount", typeof(decimal)); + table.Columns.Add("IsFocItem", typeof(bool)); + table.Columns.Add("LineTotalValue", typeof(decimal)); + table.Columns.Add("OrderStatus", typeof(int)); + + foreach (SaveOrderItemRequest item in items) + { + MaterialLookupDto material = materialsByCode[item.MaterialCode]; + + table.Rows.Add( + item.ItemId, + item.OrderId, + material.MaterialId, + item.MaterialCode, + item.MaterialName ?? material.MaterialName, + item.UnitPrice, + item.SalesUnit, + item.PromoQuantity, + item.OrderQuantity, + item.ValidatedQuantity, + item.ApprovedQuantity, + item.DiscountPercentage, + item.DiscountAmount, + item.IsFocItem, + item.LineTotalValue, + orderStatus); + } + + return table; + } + catch (Exception) + { + throw; + } + } + + private static DataTable ToOrderPromotionTable(IEnumerable promotions) + { + try + { + DataTable table = new(); + table.Columns.Add("ItemId", typeof(int)); + table.Columns.Add("OrderId", typeof(int)); + table.Columns.Add("MaterialId", typeof(int)); + table.Columns.Add("PromotionCode", typeof(string)); + table.Columns.Add("DiscountPercentage", typeof(decimal)); + table.Columns.Add("DiscountValue", typeof(decimal)); + table.Columns.Add("ForQuantity", typeof(decimal)); + table.Columns.Add("OrderFocQty", typeof(decimal)); + table.Columns.Add("ValidatedFocQty", typeof(decimal)); + table.Columns.Add("ApprovedFocQty", typeof(decimal)); + table.Columns.Add("PromotionType", typeof(int)); + + foreach (SaveOrderPromotionRequest promo in promotions) + { + table.Rows.Add( + promo.ItemId, + promo.OrderId, + promo.MaterialId, + promo.PromotionCode , + promo.DiscountPercentage, + promo.DiscountValue, + promo.ForQuantity, + promo.OrderFocQty, + promo.ValidatedFocQty, + promo.ApprovedFocQty, + promo.PromotionType); + } + + return table; + } + catch (Exception) + { + throw; + } + } + + private async Task InsertOrderAsync(TransactionContext tc, SaveOrderRequest request, CustomerLookupDto customer, PaymentTermLookupDto paymentTerm, + DiscountLookupDto discount, Dictionary materialsByCode, + List promotions, int userId, int role) + { + try + { + SqlParameter itemsParam = SqlHelperExtension.CreateInParam(pName: "@Items", pType: SqlDbType.Structured, pValue: ToOrderItemTable(request.Items, materialsByCode, (int)request.OrderStatus)); + itemsParam.TypeName = "dbo.OrderItemTableType"; + + SqlParameter promotionsParam = SqlHelperExtension.CreateInParam(pName: "@Promotions", pType: SqlDbType.Structured, pValue: ToOrderPromotionTable(promotions)); + promotionsParam.TypeName = "dbo.OrderPromotionTableType"; + + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@UUID", pType: SqlDbType.UniqueIdentifier, pValue: request.UUID), + SqlHelperExtension.CreateInParam(pName: "@OrderNo", pType: SqlDbType.VarChar, pValue: request.OrderNo), + SqlHelperExtension.CreateInParam(pName: "@OrderDate", pType: SqlDbType.DateTime, pValue: request.OrderDate ?? DateTime.Now ), + SqlHelperExtension.CreateInParam(pName: "@ExpectedDeliveryDate", pType: SqlDbType.DateTime, pValue: request.ExpectedDeliveryDate ), + SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue: request.SalesOrgCode), + SqlHelperExtension.CreateInParam(pName: "@CustomerId", pType: SqlDbType.Int, pValue: customer.CustomerId), + SqlHelperExtension.CreateInParam(pName: "@CustomerCode", pType: SqlDbType.VarChar, pValue: request.CustomerCode), + SqlHelperExtension.CreateInParam(pName: "@CustomerName", pType: SqlDbType.VarChar, pValue: request.CustomerName), + SqlHelperExtension.CreateInParam(pName: "@EmployeeId", pType: SqlDbType.Int, pValue: request.EmployeeId ), + SqlHelperExtension.CreateInParam(pName: "@EmployeeCode", pType: SqlDbType.VarChar, pValue: request.EmployeeCode ), + SqlHelperExtension.CreateInParam(pName: "@EmployeeName", pType: SqlDbType.VarChar, pValue: request.EmployeeCode ), + SqlHelperExtension.CreateInParam(pName: "@PaymentTermCode", pType: SqlDbType.VarChar, pValue: request.PaymentTermCode), + SqlHelperExtension.CreateInParam(pName: "@ReferenceTxtNo", pType: SqlDbType.VarChar, pValue: request.ReferenceTxtNo ), + SqlHelperExtension.CreateInParam(pName: "@GrossValue", pType: SqlDbType.Decimal, pValue: request.GrossValue), + SqlHelperExtension.CreateInParam(pName: "@DiscountValue", pType: SqlDbType.Decimal, pValue: request.DiscountValue), + SqlHelperExtension.CreateInParam(pName: "@FOCValue", pType: SqlDbType.Decimal, pValue: request.FocCode), + SqlHelperExtension.CreateInParam(pName: "@NetValue", pType: SqlDbType.Decimal, pValue: request.NetValue), + SqlHelperExtension.CreateInParam(pName: "@OrderStatus", pType: SqlDbType.Int, pValue: (int)request.OrderStatus), + SqlHelperExtension.CreateInParam(pName: "@SapRefferenceOrderNo", pType: SqlDbType.NVarChar, pValue: null), + itemsParam, + promotionsParam + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.InsertOrder", parameterValues: p); + + int newOrderId = 0; + string orderNo = string.Empty; + if (dr.Read()) + { + newOrderId = dr.GetInt32(0); + orderNo = dr.GetString(1); + } + + dr.Close(); + return new SaveOrderResponse() { IsNewOrder=true, OrderDate= DateTime.Now, OrderId= newOrderId,OrderNo =orderNo,UUID = request.UUID}; + } + catch (Exception) + { + throw; + } + } + + private async Task UpdateOrderAsync(TransactionContext tc, int orderId, SaveOrderRequest request, CustomerLookupDto customer, PaymentTermLookupDto paymentTerm, + DiscountLookupDto discount, Dictionary materialsByCode, + List promotions, int userId, int role) + { + try + { + SqlParameter itemsParam = SqlHelperExtension.CreateInParam(pName: "@Items", pType: SqlDbType.Structured, pValue: ToOrderItemTable(request.Items, materialsByCode, (int)request.OrderStatus)); + itemsParam.TypeName = "dbo.OrderItemTableType"; + + SqlParameter promotionsParam = SqlHelperExtension.CreateInParam(pName: "@Promotions", pType: SqlDbType.Structured, pValue: ToOrderPromotionTable(promotions)); + promotionsParam.TypeName = "dbo.OrderPromotionTableType"; + + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@OrderId", pType: SqlDbType.Int, pValue: orderId), + SqlHelperExtension.CreateInParam(pName: "@ExpectedDeliveryDate", pType: SqlDbType.DateTime, pValue: request.ExpectedDeliveryDate ), + SqlHelperExtension.CreateInParam(pName: "@PaymentTermCode", pType: SqlDbType.VarChar, pValue: request.PaymentTermCode), + SqlHelperExtension.CreateInParam(pName: "@ReferenceTxtNo", pType: SqlDbType.VarChar, pValue: request.ReferenceTxtNo ), + SqlHelperExtension.CreateInParam(pName: "@GrossValue", pType: SqlDbType.Decimal, pValue: request.GrossValue), + SqlHelperExtension.CreateInParam(pName: "@DiscountValue", pType: SqlDbType.Decimal, pValue: request.DiscountValue), + SqlHelperExtension.CreateInParam(pName: "@FOCValue", pType: SqlDbType.Decimal, pValue: request.FocCode), + SqlHelperExtension.CreateInParam(pName: "@NetValue", pType: SqlDbType.Decimal, pValue: request.NetValue), + SqlHelperExtension.CreateInParam(pName: "@OrderStatus", pType: SqlDbType.Int, pValue: (int)request.OrderStatus), + itemsParam, + promotionsParam + ]; + + using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.UpdateOrder", parameterValues: p); + dr.Close(); + } + catch (Exception) + { + throw; + } + } } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs index dd16f20..977360b 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs @@ -25,8 +25,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Configuration.DI { if (services == null) return; - - services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs index 6b096f4..206ac8e 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs @@ -13,6 +13,7 @@ using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; +using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; using System; @@ -130,7 +131,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers [ Helper.CreateClaim("LoginId", loginReponse.LoginId), Helper.CreateClaim("Email", loginReponse.Email), - Helper.CreateClaim("Mobile", $"{loginReponse.MobileNumber}"), + Helper.CreateClaim("UserId", $"{loginReponse.UserId}"), Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) ]), Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration), @@ -208,7 +209,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers [ Helper.CreateClaim("LoginId", userRefreshToken.LoginId), Helper.CreateClaim("Email", userRefreshToken.EmailAddress), - Helper.CreateClaim("AuthKey", $"{userRefreshToken.AuthKey}"), + Helper.CreateClaim("UserId", $"{userRefreshToken.UserId}"), Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) ]), Expires = DateTime.UtcNow.AddHours(12), diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/OrdersController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/OrdersController.cs index f9b6ecb..2170a46 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/OrdersController.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/OrdersController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using OnlineSalesAutoCrop.CoreAPI.Models; using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses; @@ -54,7 +55,8 @@ public class OrdersController(IOrderService service, IOptions appSe SaveOrderResponse response = new(); try { - response = await _service.SaveOrderAsync(request); + int userId = HttpContext.User.GetClaimValue(Constants.UserId); + response = await _service.SaveOrderAsync(request, userId); return Ok(MobileResponseBase.Success(response)); } catch (Exception ex)