complete save order endpoint

This commit is contained in:
dibakor 2026-07-16 18:30:18 +06:00
parent 7acf518892
commit 97a07bd216
7 changed files with 733 additions and 27 deletions

View File

@ -14,8 +14,9 @@ public class SaveOrderRequest
public string? OrderNo { get; set; } public string? OrderNo { get; set; }
public DateTime? OrderDate { get; set; } public DateTime? OrderDate { get; set; }
public DateTime? ExpectedDeliveryDate { get; set; } public DateTime? ExpectedDeliveryDate { get; set; }
public string UUID { get; set; } public Guid UUID { get; set; }
public string SalesOrgCode { get; set; } public string SalesOrgCode { get; set; }
public int? CustomerId { get; set; }
public string CustomerCode { get; set; } public string CustomerCode { get; set; }
public string CustomerName { get; set; } public string CustomerName { get; set; }
public int? EmployeeId { get; set; } public int? EmployeeId { get; set; }
@ -24,6 +25,7 @@ public class SaveOrderRequest
public string PaymentTermCode { get; set; } public string PaymentTermCode { get; set; }
public string DiscountCode { get; set; } public string DiscountCode { get; set; }
public string FocCode { get; set; } public string FocCode { get; set; }
public decimal FocValue { get; set; }
public decimal DiscountValue { get; set; } public decimal DiscountValue { get; set; }
public decimal GrossValue { get; set; } public decimal GrossValue { get; set; }
public decimal NetValue { get; set; } public decimal NetValue { get; set; }
@ -33,10 +35,14 @@ public class SaveOrderRequest
public class SaveOrderItemRequest 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 MaterialCode { get; set; }
public string MaterialName { get; set; } public string MaterialName { get; set; }
public decimal UnitPrice { get; set; } public decimal UnitPrice { get; set; }
public string SalesUnit { get; set; } public string SalesUnit { get; set; }
public decimal PromoQuantity { get; set; }
public decimal OrderQuantity { get; set; } public decimal OrderQuantity { get; set; }
public decimal ValidatedQuantity { get; set; } public decimal ValidatedQuantity { get; set; }
public decimal ApprovedQuantity { get; set; } public decimal ApprovedQuantity { get; set; }
@ -48,6 +54,7 @@ public class SaveOrderItemRequest
public class SaveOrderPromotionRequest public class SaveOrderPromotionRequest
{ {
public int ItemId { get; set; }
public int OrderId { get; set; } public int OrderId { get; set; }
public int MaterialId { get; set; } public int MaterialId { get; set; }
public string PromotionCode { get; set; } public string PromotionCode { get; set; }

View File

@ -7,11 +7,20 @@ public class SaveOrderResponse
{ {
public int OrderId { get; set; } public int OrderId { get; set; }
public string OrderNo { get; set; } public string OrderNo { get; set; }
public string UUID { get; set; }
public DateTime OrderDate { get; set; } public DateTime OrderDate { get; set; }
public Guid UUID { get; set; }
public bool IsNewOrder { 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 class GetOrderByUUIDResponse
{ {
public int OrderId { get; set; } public int OrderId { get; set; }
@ -74,3 +83,47 @@ public class GetOrderPromotionResponse
public decimal ApprovedFocQuantity { get; set; } public decimal ApprovedFocQuantity { get; set; }
public int PromotionType { 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;
}

View File

@ -7,5 +7,5 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp;
public interface IOrderService public interface IOrderService
{ {
Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request); Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request, int userId);
} }

View File

@ -1,7 +1,9 @@
using Ease.NetCore.DataAccess; using Ease.NetCore.DataAccess;
using Ease.NetCore.DataAccess.SQL; using Ease.NetCore.DataAccess.SQL;
using Microsoft.AspNetCore.Http;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
@ -11,6 +13,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp; namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp;
@ -28,36 +31,196 @@ public class OrderService : IOrderService
_masterService = masterService; _masterService = masterService;
} }
public async Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request) //public async Task<SaveOrderResponse> 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<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request, int userId)
{ {
SaveOrderResponse response = new(); SaveOrderResponse response = new();
try 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.");
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;
}
GetOrderByUUIDResponse order= await GetOrderByUUIDAsync(tc, request.UUID); // 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<string, MaterialLookupDto> materialsByCode = new(StringComparer.OrdinalIgnoreCase);
if (request.Items is { Count: > 0 })
{
List<string> 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<SaveOrderPromotionRequest> promotions = new List<SaveOrderPromotionRequest>();
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(); tc.End();
} }
catch(Exception) catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception)
{ {
throw; throw;
} }
return new SaveOrderResponse() return response;
{
OrderId = 12345,
OrderDate = DateTime.Now,
OrderNo = "Ord-000001-" + DateTime.Now.ToString("HH-mm-ss"),
UUID = request.UUID,
IsNewOrder =true
};
} }
private async Task<GetOrderByUUIDResponse> 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<GetOrderByUUIDResponse> GetOrderByUUIDAsync(TransactionContext tc, Guid uuid)
{ {
GetOrderByUUIDResponse response = null; GetOrderByUUIDResponse response = null;
@ -70,7 +233,6 @@ public class OrderService : IOrderService
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderDetailsByUUID", parameterValues: p)) using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderDetailsByUUID", parameterValues: p))
{ {
// Result Set 1: Order Header
if (dr.Read()) if (dr.Read())
{ {
response = new GetOrderByUUIDResponse response = new GetOrderByUUIDResponse
@ -127,7 +289,7 @@ public class OrderService : IOrderService
} }
} }
// Result Set 3: Promotions (attach to matching item)
if (response != null && dr.NextResult()) if (response != null && dr.NextResult())
{ {
while (dr.Read()) while (dr.Read())
@ -154,7 +316,6 @@ public class OrderService : IOrderService
dr.Close(); dr.Close();
} }
tc.End();
} }
catch (Exception) catch (Exception)
{ {
@ -164,4 +325,487 @@ public class OrderService : IOrderService
return response; return response;
} }
private async Task<UserRoleDto?> 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<OrderBasicResponse?> 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<CustomerLookupDto?> 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<PaymentTermLookupDto?> 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<DiscountLookupDto?> 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<bool> 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<Dictionary<string, MaterialLookupDto>> GetMaterialsAsync(TransactionContext tc, IEnumerable<string> materialCodes)
{
try
{
Dictionary<string, MaterialLookupDto> 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<List<FreeGoodsRuleDto>> GetFreeGoodsRulesAsync(TransactionContext tc, string salesOrgCode, string focCode, IEnumerable<int> materialIds, DateTime? asOfDate, int customerId)
{
try
{
List<FreeGoodsRuleDto> 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<SaveOrderPromotionRequest> BuildFocPromotions(SaveOrderRequest request,
Dictionary<string, MaterialLookupDto> materialsByCode,List<FreeGoodsRuleDto> focRules)
{
try
{
List<SaveOrderPromotionRequest> promotions = new();
Dictionary<int, FreeGoodsRuleDto> 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<SaveOrderItemRequest> items, Dictionary<string, MaterialLookupDto> 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<SaveOrderPromotionRequest> 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<SaveOrderResponse> InsertOrderAsync(TransactionContext tc, SaveOrderRequest request, CustomerLookupDto customer, PaymentTermLookupDto paymentTerm,
DiscountLookupDto discount, Dictionary<string, MaterialLookupDto> materialsByCode,
List<SaveOrderPromotionRequest> 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<string, MaterialLookupDto> materialsByCode,
List<SaveOrderPromotionRequest> 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;
}
}
} }

View File

@ -25,7 +25,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Configuration.DI
{ {
if (services == null) if (services == null)
return; return;
services.AddSingleton<IEaseCache, EaseCache>(); services.AddSingleton<IEaseCache, EaseCache>();
services.AddTransient<IUserService, UserService>(); services.AddTransient<IUserService, UserService>();
services.AddTransient<IParamTypeService, ParamTypeService>(); services.AddTransient<IParamTypeService, ParamTypeService>();

View File

@ -13,6 +13,7 @@ using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses; using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Systems;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using System; using System;
@ -130,7 +131,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
[ [
Helper.CreateClaim("LoginId", loginReponse.LoginId), Helper.CreateClaim("LoginId", loginReponse.LoginId),
Helper.CreateClaim("Email", loginReponse.Email), Helper.CreateClaim("Email", loginReponse.Email),
Helper.CreateClaim("Mobile", $"{loginReponse.MobileNumber}"), Helper.CreateClaim("UserId", $"{loginReponse.UserId}"),
Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) Helper.CreateClaim("HashKey", Guid.NewGuid().ToString())
]), ]),
Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration), Expires = DateTime.UtcNow.AddMinutes(_appSettings.AccessTokenDuration),
@ -208,7 +209,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
[ [
Helper.CreateClaim("LoginId", userRefreshToken.LoginId), Helper.CreateClaim("LoginId", userRefreshToken.LoginId),
Helper.CreateClaim("Email", userRefreshToken.EmailAddress), Helper.CreateClaim("Email", userRefreshToken.EmailAddress),
Helper.CreateClaim("AuthKey", $"{userRefreshToken.AuthKey}"), Helper.CreateClaim("UserId", $"{userRefreshToken.UserId}"),
Helper.CreateClaim("HashKey", Guid.NewGuid().ToString()) Helper.CreateClaim("HashKey", Guid.NewGuid().ToString())
]), ]),
Expires = DateTime.UtcNow.AddHours(12), Expires = DateTime.UtcNow.AddHours(12),

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses; using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
@ -54,7 +55,8 @@ public class OrdersController(IOrderService service, IOptions<AppSettings> appSe
SaveOrderResponse response = new(); SaveOrderResponse response = new();
try try
{ {
response = await _service.SaveOrderAsync(request); int userId = HttpContext.User.GetClaimValue<int>(Constants.UserId);
response = await _service.SaveOrderAsync(request, userId);
return Ok(MobileResponseBase<SaveOrderResponse>.Success(response)); return Ok(MobileResponseBase<SaveOrderResponse>.Success(response));
} }
catch (Exception ex) catch (Exception ex)