OnlineSalesAutoCrop/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/OrderService.cs

963 lines
41 KiB
C#

using Ease.NetCore.DataAccess;
using Ease.NetCore.DataAccess.SQL;
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;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp;
public class OrderService : IOrderService
{
private readonly AppSettings _settings;
private readonly IUserService _userService;
private readonly IMobileMasterDataService _masterService;
public OrderService(IOptions<AppSettings> options, IUserService userService, IMobileMasterDataService masterService)
{
_settings = options.Value;
_userService = userService;
_masterService = masterService;
}
public async Task<SaveOrderResponse> SaveOrderAsync(SaveOrderRequest request, int userId)
{
SaveOrderResponse response = new();
try
{
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;
if (existingOrder is not null && existingOrder.OrderId > 0 &&
(existingOrder.OrderStatus == OrderStatusEnum.Approved ||
existingOrder.OrderStatus == OrderStatusEnum.CancelledByApprover ||
existingOrder.OrderStatus == OrderStatusEnum.CancelledByValidator))
{
throw new Exception($"Order with UUID '{request.UUID}' has already been {existingOrder.OrderStatus.ToString()} and cannot be modified.");
}
foreach (var item in request.Items)
{
item.ItemId = 0;
item.OrderId = request.OrderId;
}
// 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;
if(dicountItem!= null)
{
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,
});
}
if(focItem != null)
{
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 = 2,
});
}
// 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)
{
throw;
}
return response;
}
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.InOrderApproval == orderStatus || OrderStatusEnum.CancelledByApprover == orderStatus || OrderStatusEnum.Approved == orderStatus))
{
return true;
}
return false;
}
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: "@DiscountCode", pType: SqlDbType.VarChar, pValue: request.DiscountCode),
SqlHelperExtension.CreateInParam(pName: "@FOCCode", pType: SqlDbType.VarChar, pValue: request.FocCode),
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),
SqlHelperExtension.CreateInParam(pName: "@UserId", pType: SqlDbType.Int, pValue: userId),
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: "@DiscountCode", pType: SqlDbType.VarChar, pValue: request.DiscountCode),
SqlHelperExtension.CreateInParam(pName: "@FOCCode", pType: SqlDbType.VarChar, pValue: request.FocCode),
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: "@UserId", pType: SqlDbType.Int, pValue: userId),
itemsParam,
promotionsParam
];
using IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.UpdateOrder", parameterValues: p);
dr.Close();
}
catch (Exception)
{
throw;
}
}
public async Task<PagedResult<GetSalesOrderResponse>> GetSalesOrdersSummary(GetOrderSummaryRequest request, int userId)
{
PagedResult<GetSalesOrderResponse?> response = null;
List<GetSalesOrderResponse> orders = new();
List<GetSalesOrderItemResponse> orderItems = new();
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@SalesOrgCode", pType: SqlDbType.VarChar, pValue:request.SalesOrgCode) ,
SqlHelperExtension.CreateInParam(pName: "@EmployeeCode", pType: SqlDbType.VarChar, pValue:request.EmployerCode) ,
SqlHelperExtension.CreateInParam(pName: "@OrderNo", pType: SqlDbType.VarChar, pValue: request.OrderNo ),
SqlHelperExtension.CreateInParam(pName: "@UUID", pType: SqlDbType.VarChar, pValue: request.UUID ),
SqlHelperExtension.CreateInParam(pName: "@CustomerCode", pType: SqlDbType.VarChar, pValue: request.CustomerCode ),
SqlHelperExtension.CreateInParam(pName: "@OrderStatus", pType: SqlDbType.Int, pValue: (int?)request.OrderStatus ),
SqlHelperExtension.CreateInParam(pName: "@StartDate", pType: SqlDbType.DateTime, pValue: request.StartDate ),
SqlHelperExtension.CreateInParam(pName: "@EndDate", pType: SqlDbType.DateTime, pValue: request.EndDate ),
SqlHelperExtension.CreateInParam(pName: "@PageNumber", pType: SqlDbType.Int, pValue: request.PageNumber ),
SqlHelperExtension.CreateInParam(pName: "@PageSize", pType: SqlDbType.Int, pValue: request.PageSize )
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderSummaryPaginated", parameterValues: p))
{
// --- Result set 1: order header ---
while (dr.Read())
{
orders.Add( new GetSalesOrderResponse
{
OrderId = dr.GetInt32(16),
Uuid = dr.GetGuid(0),
OrderNo = dr.GetString(1),
OrderDate = dr.GetDateTime(2),
ExpectedDeliveryDate = dr.IsDBNull(3) ? null : dr.GetDateTime(3),
SalesOrgCode = dr.GetString(4),
CustomerCode = dr.GetString(5),
CustomerName = dr.GetString(6),
ReferenceTxtNo = dr.IsDBNull(7) ? null : dr.GetString(7),
EmployeeCode = dr.GetString(8),
PaymentTermCode = dr.GetString(9),
DiscountCode = dr.IsDBNull(10) ? string.Empty : dr.GetString(10),
FocCode = dr.IsDBNull(11) ? string.Empty : dr.GetString(11),
DiscountValue = dr.GetDecimal(12),
GrossValue = dr.GetDecimal(13),
NetValue = dr.GetDecimal(14),
OrderStatus = dr.GetInt32(15)
});
}
// --- Result set 2: order items ---
if (orders.Count >0 && dr.NextResult())
{
while (dr.Read())
{
orderItems.Add(new GetSalesOrderItemResponse
{
OrderId = dr.GetInt32(0),
UUID = dr.GetGuid(1),
MaterialCode = dr.GetString(2),
MaterialName = dr.GetString(3),
UnitPrice = dr.GetDecimal(4),
SalesUnit = dr.GetString(5),
PromoQuantity =dr.IsDBNull(6)? null : dr.GetDecimal(6),
OrderQuantity = dr.IsDBNull(7)? null : dr.GetDecimal(7),
ValidatedQuantity = dr.IsDBNull(8) ? null : dr.GetDecimal(8),
ApprovedQuantity = dr.IsDBNull(9) ? null : dr.GetDecimal(9),
DiscountPercentage = dr.IsDBNull(10) ? null : dr.GetDecimal(10),
DiscountAmount = dr.IsDBNull(11) ? null : dr.GetDecimal(11),
IsFocItem = dr.GetInt32(12) > 0,
LineTotalValue = dr.GetDecimal(13)
});
}
}
dr.Close();
}
tc.End();
foreach (var order in orders)
{
order.Items = orderItems.Where(i => i.OrderId == order.OrderId).ToList();
}
response = new PagedResult<GetSalesOrderResponse>
{
Items = orders,
TotalCount = orders.Count,
PageNumber = request.PageNumber,
PageSize = request.PageSize
};
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
public async Task<IList<int>> GetAvailableOrderIdsForSAPAsync(SAPOrderStatusEnum orderStaus, CancellationToken cancellationToken = default)
{
List<int> response = new List<int>();
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@OrderStatus", pType: SqlDbType.Int, pValue: (int?)orderStaus ),
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetAvailableOrderIdsByStatus", parameterValues: p))
{
while (dr.Read())
{
response.Add(dr.GetInt32(0));
}
dr.Close();
}
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
public async Task<GetOrderForSAPResponse?> GetOrderDetailsForSapByIdAsync(int orderId, CancellationToken cancellationToken = default)
{
GetOrderForSAPResponse response = null;
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@OrderId", pType: SqlDbType.Int, pValue: orderId ),
];
using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetOrderDetailsById", parameterValues: p))
{
while (dr.Read())
{
response = new GetOrderForSAPResponse()
{
SalesOrderType = dr.GetString(0),
SalesOrganization = dr.GetString(1),
DistributionChannel = dr.GetString(2),
OrganizationDivision = dr.GetString(3),
SoldToParty = dr.GetString(4),
PurchaseOrderByCustomer = dr.GetString(5),
CustomerPurchaseOrderDate = ConvertOrderDateIntoMilisec( dr.GetDateTime(6)),
PurchaseOrderByShipToParty =dr.IsDBNull(7) ? null : dr.GetString(7),
CustomerPaymentTerms = dr.IsDBNull(8) ? null : dr.GetString(8),
CustomerGroup = dr.GetString(9),
CustomerPriceGroup = dr.GetString(10)
};
}
if (response is not null && dr.NextResult())
{
while (dr.Read())
{
response.to_Item.Add(new GetOrderItemForSAPResponse
{
Material = dr.GetString(0),
RequestedQuantity =Convert.ToString( dr.IsDBNull(1)? 0 : dr.GetDecimal(1))
});
}
}
dr.Close();
}
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
private string ConvertOrderDateIntoMilisec(DateTime orderDate)
{
long milliseconds = new DateTimeOffset(orderDate).ToUnixTimeMilliseconds();
string sapDate = $"/Date({milliseconds})/";
return sapDate;
}
public async Task<bool> UpdateSendingSapDetailsAsync(UpdateSapDetailsRequest request, CancellationToken cancellationToken = default)
{
bool response = false;
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam(pName: "@OrderId", pType: SqlDbType.Int, pValue: request.OrderId ),
SqlHelperExtension.CreateInParam(pName: "@Status", pType: SqlDbType.Int, pValue: request.Status ),
SqlHelperExtension.CreateInParam(pName: "@SentAt", pType: SqlDbType.DateTime, pValue: DateTime.Now ),
SqlHelperExtension.CreateInParam(pName: "@RetryCount", pType: SqlDbType.Int, pValue: request.RetryCount ),
SqlHelperExtension.CreateInParam(pName: "@LastError", pType: SqlDbType.Int, pValue: request.LastError ),
SqlHelperExtension.CreateInParam(pName: "@ReturnMessage", pType: SqlDbType.Int, pValue: request.ReturnMessage ),
];
await tc.ExecuteNonQuerySpAsync("dbo.UpdateSendingSapDetails", parameterValues: p);
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
}