complete save order endpoint
This commit is contained in:
parent
7acf518892
commit
97a07bd216
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
@ -74,3 +83,47 @@ public class GetOrderPromotionResponse
|
|||
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;
|
||||
}
|
||||
|
|
@ -7,5 +7,5 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp;
|
|||
|
||||
public interface IOrderService
|
||||
{
|
||||
Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request);
|
||||
Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request, int userId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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();
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
catch(Exception)
|
||||
catch (Exception ie)
|
||||
{
|
||||
tc?.HandleError();
|
||||
throw DBCustomError.GenerateCustomError(ie);
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
||||
return response;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ namespace OnlineSalesAutoCrop.CoreAPI.Configuration.DI
|
|||
{
|
||||
if (services == null)
|
||||
return;
|
||||
|
||||
services.AddSingleton<IEaseCache, EaseCache>();
|
||||
services.AddTransient<IUserService, UserService>();
|
||||
services.AddTransient<IParamTypeService, ParamTypeService>();
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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<AppSettings> appSe
|
|||
SaveOrderResponse response = new();
|
||||
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));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user