From 18891cd584431a4fa1b51897c0bd6afc0cf754bf Mon Sep 17 00:00:00 2001 From: dibakor Date: Tue, 7 Jul 2026 18:14:18 +0600 Subject: [PATCH] implement brand, material and customer and change the response format when got bad request --- .../Enums.cs | 12 +- .../Requests/BaseRequest.cs | 9 + .../MobileApp/MobileMasterDataRequest.cs | 18 + .../MobileApp/MobileMasterDataResponse.cs | 129 ++++- .../Responses/ResponseBase.cs | 13 + .../MobileApp/IMobileMasterDataService.cs | 14 + .../MobileApp/MobileMasterDataService.cs | 505 ++++++++++++++++++ .../Services/Systems/UserService.cs | 8 +- .../DI/ServiceCollectionExtensions.cs | 3 + .../Mobile/MobileAuthController.cs | 13 +- .../Mobile/MobileMasterDataController.cs | 161 ++++++ .../OnlineSalesAutoCrop.CoreAPI.csproj.user | 2 +- Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs | 48 +- .../dotnet-tools.json | 13 + 14 files changed, 917 insertions(+), 31 deletions(-) create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IMobileMasterDataService.cs create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileMasterDataController.cs create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/dotnet-tools.json diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Enums.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Enums.cs index ecdfd70..0db2715 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Enums.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Enums.cs @@ -727,13 +727,13 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models public enum UserRoleTypeEnum { - [Description("SAP User")] - SapUser = 1, [Description("Order Collector")] - OrderCollector = 2, + OrderCollector = 1, [Description("OrderValidator")] - OrderValidator = 3, + OrderValidator = 2, [Description("OrderApprover")] - OrderApprover =4 - } + OrderApprover = 3 , + [Description("SAP User")] + SapUser = 4 , + } } \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/BaseRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/BaseRequest.cs index 03897b5..20af7cf 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/BaseRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/BaseRequest.cs @@ -27,4 +27,13 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests { public string AuthenticationId { get; set; } } + + public abstract class PagedRequest + { + [Range(1, int.MaxValue, ErrorMessage = "PageNumber must be at least 1.")] + public int PageNumber { get; set; } = 1; + + [Range(1, 1000, ErrorMessage = "PageSize must be between 1 and 1000.")] + public int PageSize { get; set; } = 1000; + } } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/MobileMasterDataRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/MobileMasterDataRequest.cs index 4e78a88..d2a6067 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/MobileMasterDataRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/MobileApp/MobileMasterDataRequest.cs @@ -9,3 +9,21 @@ public class GetMarketHeirarchyByEmpRequest [Required, NotNull, StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "EmployeeNumber must be between 3 and 10 characters.")] public string EmployeeNumber { get; set; } } + +public class GetBrandsRequest : PagedRequest +{ + public string? BrandCode { get; set; } +} + + +public class GetMaterialsRequest : PagedRequest +{ + public string? BrandCode { get; set; } + public string? MaterialCode { get; set; } +} + +public class GetCustomersRequest : PagedRequest +{ + public string? TeritoryCode { get; set; } + public string? CustomerCode { get; set; } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/MobileMasterDataResponse.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/MobileMasterDataResponse.cs index f073ef4..2b4be76 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/MobileMasterDataResponse.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/MobileApp/MobileMasterDataResponse.cs @@ -1,6 +1,133 @@ -namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; +using System; +using System.Collections.Generic; + +namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; public class MarketHeirarchyByEmpResponse { + public MarketHeirarchyByEmpResponse() + { + Companies = new List(); + SalesOrgs = new List(); + DistributionChannels = new List(); + Regions = new List(); + Areas = new List(); + Teritories = new List(); + Plants = new List(); + } + public IList Companies { get; set; } + public IList SalesOrgs { get; set; } + public IList DistributionChannels { get; set; } + public IList Regions { get; set; } + public IList Areas { get; set; } + public IList Teritories { get; set; } + public IList Plants { get; set; } } + +public class EmpCompanyResponse +{ + public string Code { get; set; } + public string Name { get; set; } +} + +public class SalesOrgResponse +{ + public string CompanyCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + +public class DistibutionChannleResponse +{ + public string CompanyCode { get; set; } + public string SalesOrgCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + +public class RegionResponse +{ + public string CompanyCode { get; set; } + public string SalesOrgCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + +public class AreaResponse +{ + public string CompanyCode { get; set; } + public string SalesOrgCode { get; set; } + public string RegionCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + +public class TeritoryResponse +{ + public string CompanyCode { get; set; } + public string SalesOrgCode { get; set; } + public string RegionCode { get; set; } + public string AreaCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + +public class PlantResponse +{ + public string CompanyCode { get; set; } + public string SalesOrgCode { get; set; } + public string Code { get; set; } + public string Name { get; set; } +} + + + +public class GetBrandResponse +{ + public string BrandCode { get; set; } = string.Empty; + public string BrandName { get; set; } = string.Empty; +} + + +public class GetMaterialResponse +{ + public string Name { get; set; } + public string Code { get; set; } + public string TypeCode { get; set; } + public string TypeName { get; set; } + public string GroupCode { get; set; } + public string GroupName { get; set; } + public string BaseUnitMeasurement { get; set; } + public string SalesUnitMeasurement { get; set; } + public decimal UnitConversionValue { get; set; } + public string BrandCode { get; set; } + public string BrandName { get; set; } + public string SalesOrg { get; set; } + public decimal BasePrice { get; set; } +} + + +public class GetCustomerResponse +{ + public string Code { get; set; } + public string Name { get; set; } + public string AccountGroupCode { get; set; } + public string AccountGroupDescription { get; set; } + public string DistributionChannelCode { get; set; } + public string DistributionChannelName { get; set; } + public string DivisionCode { get; set; } + public string DivisionName { get; set; } + public string MobileNumber { get; set; } + public string EmailAddress { get; set; } + public string TaxNumber { get; set; } + public string SalesGroupCode { get; set; } + public string SalesGroupName { get; set; } + public string CustomerGroupCode { get; set; } + public string CustomerGroupName { get; set; } + public string CustomerPriceGroupCode { get; set; } + public string CustomerPriceGroupName { get; set; } + public string PaymentTermCode { get; set; } + public string TeritoryCode { get; set; } + public string PlantCode { get; set; } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/ResponseBase.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/ResponseBase.cs index e830304..1b3145f 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/ResponseBase.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/ResponseBase.cs @@ -1,7 +1,9 @@ using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Objects; +using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses { @@ -64,4 +66,15 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses { public List FoundKeywords { get; set; } } + + public class PagedResult + { + public IList Items { get; set; } = []; + public int TotalCount { get; set; } + public int PageNumber { get; set; } = 1; + public int PageSize { get; set; } = 10000; + public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize); + public bool HasNextPage => PageNumber < TotalPages; + public bool HasPreviousPage => PageNumber > 1; + } } \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IMobileMasterDataService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IMobileMasterDataService.cs new file mode 100644 index 0000000..29cf963 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/MobileApp/IMobileMasterDataService.cs @@ -0,0 +1,14 @@ +using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; +using OnlineSalesAutoCrop.CoreAPI.Models.Responses; +using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; +using System.Threading.Tasks; + +namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp; + +public interface IMobileMasterDataService +{ + Task GetMarketHeirarchiesByEmpAsync(GetMarketHeirarchyByEmpRequest request); + Task> GetBrandsAsync(GetBrandsRequest request); + Task> GetMaterialsAsync(GetMaterialsRequest request); + Task> GetCustomersAsync(GetCustomersRequest request); +} diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs new file mode 100644 index 0000000..e2b0664 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs @@ -0,0 +1,505 @@ +using Ease.NetCore.DataAccess; +using Ease.NetCore.DataAccess.SQL; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Options; +using MySqlX.XDevAPI.Common; +using OnlineSalesAutoCrop.CoreAPI.Models.Global; +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.Services.Contracts.MobileApp; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; +using System; +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; + +namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp; + +public class MobileMasterDataService : IMobileMasterDataService +{ + private readonly AppSettings _settings; + private readonly IUserService _userService; + + public MobileMasterDataService(IOptions options, IUserService userService) + { + _settings = options.Value; + _userService = userService; + } + + public async Task GetMarketHeirarchiesByEmpAsync(GetMarketHeirarchyByEmpRequest request) + { + + MarketHeirarchyByEmpResponse response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + string employeeNumber = request.EmployeeNumber; + + response.Companies = await GetCompaniesByEmployeeNumberAsync(tc, employeeNumber); + response.SalesOrgs = await GetSalesOrgsByEmployeeNumberAsync(tc, employeeNumber); + response.DistributionChannels = await GetDistributionChannelsByEmployeeNumberAsync(tc, employeeNumber); + response.Regions = await GetRegionsByEmployeeNumberAsync(tc, employeeNumber); + response.Areas = await GetAreasByEmployeeNumberAsync(tc, employeeNumber); + response.Teritories = await GetTerritoriesByEmployeeNumberAsync(tc, employeeNumber); + response.Plants = await GetPlantsByEmployeeNumberAsync(tc, employeeNumber); + + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception ex) + { + throw; + } + + return response; + } + + public async Task> GetBrandsAsync(GetBrandsRequest request) + { + PagedResult response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@BrandCode", pType: SqlDbType.VarChar, pValue: request.BrandCode), + 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.GetBrandsPaginated", parameterValues: p)) + { + if (dr.Read()) + { + response.TotalCount = dr.GetInt32(0); + response.PageNumber = request.PageNumber; + response.PageSize = request.PageSize; + } + + // Second result set: paginated brand data + dr.NextResult(); + + + while (dr.Read()) + { + response.Items.Add(new GetBrandResponse + { + BrandCode = dr.GetString(0), + BrandName = dr.GetString(1) + }); + } + dr.Close(); + + } + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception ex) + { + throw; + } + + return response; + } + + + public async Task> GetMaterialsAsync(GetMaterialsRequest request) + { + PagedResult response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@BrandCode", pType: SqlDbType.VarChar, pValue: request.BrandCode), + SqlHelperExtension.CreateInParam(pName: "@MaterialCode", pType: SqlDbType.VarChar, pValue: request.MaterialCode), + 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.GetMaterialsPaginated", parameterValues: p)) + { + if (dr.Read()) + { + response.TotalCount = dr.GetInt32(0); + response.PageNumber = request.PageNumber; + response.PageSize = request.PageSize; + } + + // Second result set: paginated brand data + dr.NextResult(); + + + while (dr.Read()) + { + response.Items.Add(new GetMaterialResponse + { + Code = dr.GetString(0), + Name = dr.GetString(1), + TypeCode = dr.GetString(2), + TypeName = dr.GetString(3), + GroupCode = dr.GetString(4), + GroupName = dr.GetString(5), + BaseUnitMeasurement = dr.GetString(6), + SalesUnitMeasurement = dr.GetString(7), + UnitConversionValue = dr.GetDecimal(8), + BrandCode = dr.GetString(9), + BrandName = dr.GetString(10), + SalesOrg = dr.GetString(11), + BasePrice = dr.GetDecimal(12) + }); + } + dr.Close(); + + } + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception ex) + { + throw; + } + + return response; + } + + public async Task> GetCustomersAsync(GetCustomersRequest request) + { + PagedResult response = new(); + try + { + using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode); + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@TeritoryCode", pType: SqlDbType.VarChar, pValue: request.TeritoryCode), + SqlHelperExtension.CreateInParam(pName: "@CustomerCode", pType: SqlDbType.VarChar, pValue: request.CustomerCode), + 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.GetCustomersPaginated", parameterValues: p)) + { + if (dr.Read()) + { + response.TotalCount = dr.GetInt32(0); + response.PageNumber = request.PageNumber; + response.PageSize = request.PageSize; + } + + // Second result set: paginated brand data + dr.NextResult(); + + + while (dr.Read()) + { + response.Items.Add(new GetCustomerResponse + { + Code = dr.GetString(0), + Name = dr.GetString(1), + AccountGroupCode = dr.GetString(2), + AccountGroupDescription = dr.GetString(3), + DistributionChannelCode = dr.GetString(4), + DistributionChannelName = dr.GetString(5), + DivisionCode = dr.GetString(6), + DivisionName = dr.GetString(7), + MobileNumber = dr.GetString(8), + EmailAddress = dr.GetString(9), + TaxNumber = dr.GetString(10), + SalesGroupCode = dr.GetString(11), + SalesGroupName = dr.GetString(12), + CustomerGroupCode = dr.GetString(13), + CustomerGroupName = dr.GetString(14), + CustomerPriceGroupCode = dr.GetString(15), + CustomerPriceGroupName = dr.GetString(16), + PaymentTermCode = dr.GetString(17), + TeritoryCode = dr.GetString(18), + PlantCode = dr.GetString(19) + }); + } + dr.Close(); + + } + tc.End(); + } + catch (Exception ie) + { + tc?.HandleError(); + + throw DBCustomError.GenerateCustomError(ie); + } + } + catch (Exception ex) + { + throw; + } + + return response; + } + + private async Task> GetCompaniesByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetCompaniesByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new EmpCompanyResponse + { + Code = dr.GetString(0), + Name = dr.GetString(1) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetSalesOrgsByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetSalesOrgsByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new SalesOrgResponse + { + CompanyCode = dr.GetString(0), + Code = dr.GetString(1), + Name = dr.GetString(2) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetDistributionChannelsByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetDistributionChannelsByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new DistibutionChannleResponse + { + CompanyCode = dr.GetString(0), + SalesOrgCode = dr.GetString(1), + Code = dr.GetString(2), + Name = dr.GetString(3) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetRegionsByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetRegionsByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new RegionResponse + { + CompanyCode = dr.GetString(0), + SalesOrgCode = dr.GetString(1), + Code = dr.GetString(2), + Name = dr.GetString(3) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetAreasByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetAreasByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new AreaResponse + { + CompanyCode = dr.GetString(0), + SalesOrgCode = dr.GetString(1), + RegionCode = dr.GetString(2), + Code = dr.GetString(3), + Name = dr.GetString(4) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetTerritoriesByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetTerritoriesByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new TeritoryResponse + { + CompanyCode = dr.GetString(0), + SalesOrgCode = dr.GetString(1), + RegionCode = dr.GetString(2), + AreaCode = dr.GetString(3), + Code = dr.GetString(4), + Name = dr.GetString(5) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + + private async Task> GetPlantsByEmployeeNumberAsync(TransactionContext tc, string employeeNumber) + { + List response = []; + + try + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam(pName: "@EmployeeNumber", pType: SqlDbType.NVarChar, pValue: employeeNumber) + ]; + + using (IDataReader dr = await tc.ExecuteReaderSpAsync("dbo.GetPlantsByEmployeeNumber", parameterValues: p)) + { + while (dr.Read()) + { + response.Add(new PlantResponse + { + CompanyCode = dr.GetString(0), + SalesOrgCode = dr.GetString(1), + Code = dr.GetString(2), + Name = dr.GetString(3) + }); + } + dr.Close(); + } + } + catch (Exception ex) + { + throw DBCustomError.GenerateCustomError(ex); + } + + return response; + } + +} diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs index d6c860d..8a8eb99 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Systems/UserService.cs @@ -406,16 +406,18 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems if (user is null) { tc.End(); - throw new InvalidOperationException($"User not found with login: {request.LoginId}"); + response.IsAuthorized = true; + return response; } if (!password.Equals(user.Password)) { tc.End(); - throw new InvalidOperationException($"Password mismatch for login: {request.LoginId}"); + response.IsAuthorized = false; + response.LoginStatus = EnumLoginStatus.Unsuccessful; + return response; } - #endregion #region If the user was locked, try set set unlock if Time expired diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs index 7d78214..eaeb5ca 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Configurations/DI/ServiceCollectionExtensions.cs @@ -1,10 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Setups; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; using OnlineSalesAutoCrop.CoreAPI.Services.Services.Auth; using OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations; +using OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Services.Services.Setups; using OnlineSalesAutoCrop.CoreAPI.Services.Services.Systems; @@ -31,6 +33,7 @@ namespace OnlineSalesAutoCrop.CoreAPI.Configuration.DI services.AddTransient(); services.AddTransient(); services.AddScoped(); + services.AddScoped(); } } } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs index 35bb635..1bb5df6 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileAuthController.cs @@ -76,13 +76,13 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers if (string.IsNullOrEmpty(request.LoginId)) { string message = "Login ID is required."; - return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status417ExpectationFailed)); + return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status400BadRequest)); } if (string.IsNullOrEmpty(request.Password)) { string message = "Password is required."; - return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status417ExpectationFailed)); + return BadRequest(MobileResponseBase.Failure(message, StatusCodes.Status400BadRequest)); } @@ -104,26 +104,25 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers if (loginReponse == null || loginReponse.UserId == 0) { string message = "Login ID/Password is invalid.\" : \"You are not Authorized to login into the System."; - return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status403Forbidden)); + return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } - if (loginReponse.LoginStatus == EnumLoginStatus.Unsuccessful) { string message = checkPwd ? $"Login ID/Password is invalid for {request.LoginId}." : "You are not Authorized to login into the System"; - return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status403Forbidden)); + return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } if (loginReponse.IsLocked) { string message =$"Your Account is locked . Please try again after few minutes"; - return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status403Forbidden)); + return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } if (!loginReponse.IsAuthorized ) { string message = $"You are not Authorized to Login into the System, Please contact with System Administrator."; - return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status403Forbidden)); + return Unauthorized(MobileResponseBase.Failure(message, StatusCodes.Status401Unauthorized)); } string pwdSecretKey = GlobalFunctions.ConvertFromBase64String(_appSettings.PwdSecretKey); diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileMasterDataController.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileMasterDataController.cs new file mode 100644 index 0000000..e5ac2be --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Controllers/Mobile/MobileMasterDataController.cs @@ -0,0 +1,161 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using OnlineSalesAutoCrop.CoreAPI; +using OnlineSalesAutoCrop.CoreAPI.Models; +using OnlineSalesAutoCrop.CoreAPI.Models.Global; +using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems; +using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Common; +using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations; +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.MobileApp; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; +using System; +using System.DirectoryServices; +using System.IdentityModel.Tokens.Jwt; +using System.Runtime.Versioning; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace OnlineSalesAutoCrop.CoreAPI.Controllers +{ + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + [Authorize] + [ApiController] + [ApiVersion("1.0")] + [ValidateAntiForgeryToken] + [Route("api/mobile/v{version:apiVersion}/masterdata")] + public class MobileMasterDataController(IMobileMasterDataService service, IOptions appSettings, IEaseCache cache, ILogger logger) : ControllerBase + { + private readonly ILogger _logger = logger; + private readonly IEaseCache _cache = cache; + private readonly IMobileMasterDataService _service = service; + private readonly AppSettings _appSettings = appSettings?.Value; + private readonly DateTimeOffset _options = Helper.CreateEaseCacheOptions(); + + + /// + /// Login using your credential data retrieve from SqlServer + /// + /// + /// + /// + /// If login successful Return MarketHierarchies List + [HttpGet("MarketHierarchies")] + [IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MarketHeirarchyByEmpResponse))] + public async Task EmployeeMarketHierarchies([FromQuery] GetMarketHeirarchyByEmpRequest request) + { + MarketHeirarchyByEmpResponse response = new(); + try + { + response = await _service.GetMarketHeirarchiesByEmpAsync(request); + return Ok(MobileResponseBase.Success(response)); + } + catch (Exception ex) + { + string msg = $"Exception occur on MarketHeirarchys endpoint with employee request - {request?.EmployeeNumber}"; + _logger.LogError(exception: ex, message: msg); + return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); + } + } + + /// + /// Login using your credential data retrieve from SqlServer + /// + /// + /// + /// + /// If login successful Return Brands List + [HttpGet("Brands")] + [IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(MarketHeirarchyByEmpResponse))] + public async Task Brands([FromQuery] GetBrandsRequest request) + { + PagedResult response = new(); + try + { + response = await _service.GetBrandsAsync(request); + return Ok(MobileResponseBase>.Success(response)); + } + catch (Exception ex) + { + string msg = $"Exception occur on brands endpoint with employee request - {request?.BrandCode}"; + _logger.LogError(exception: ex, message: msg); + return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); + } + } + + + /// + /// Login using your credential data retrieve from SqlServer + /// + /// + /// + /// + /// If login successful Return Material List + [HttpGet("Materials")] + [IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(GetMaterialResponse))] + public async Task Materials([FromQuery] GetMaterialsRequest request) + { + PagedResult response = new(); + try + { + response = await _service.GetMaterialsAsync(request); + return Ok(MobileResponseBase>.Success(response)); + } + catch (Exception ex) + { + string msg = $"Exception occur on Materials endpoint with employee request - {request?.BrandCode} -{request?.MaterialCode}"; + _logger.LogError(exception: ex, message: msg); + return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); + } + } + + /// + /// Login using your credential data retrieve from SqlServer + /// + /// + /// + /// + /// If login successful Return Material List + [HttpGet("Customers")] + [IgnoreAntiforgeryToken] + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(GetCustomerResponse))] + public async Task Customers([FromQuery] GetCustomersRequest request) + { + PagedResult response = new(); + try + { + response = await _service.GetCustomersAsync(request); + return Ok(MobileResponseBase>.Success(response)); + } + catch (Exception ex) + { + string msg = $"Exception occur on Materials endpoint with employee request - {request?.TeritoryCode} -{request?.CustomerCode}"; + _logger.LogError(exception: ex, message: msg); + return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError)); + } + } + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj.user b/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj.user index f756d77..65537a9 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj.user +++ b/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj.user @@ -1,7 +1,7 @@  - D:\Local\OnlineSalesAutoCrop\Api\OnlineSalesAutoCrop.CoreAPI\Properties\PublishProfiles\FolderProfile.pubxml + FolderProfile IIS Express ApiControllerEmptyScaffolder root/Common/Api diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs index 823438c..54a76e9 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs @@ -1,12 +1,5 @@ using Asp.Versioning; using Asp.Versioning.ApiExplorer; -using OnlineSalesAutoCrop.CoreAPI.API.Swagger; -using OnlineSalesAutoCrop.CoreAPI.Configuration.DI; -using OnlineSalesAutoCrop.CoreAPI.Configurations; -using OnlineSalesAutoCrop.CoreAPI.Models; -using OnlineSalesAutoCrop.CoreAPI.Models.Global; -using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; -using OnlineSalesAutoCrop.CoreAPI.SignalRHub; using Hangfire; using Hangfire.SqlServer; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -24,9 +17,18 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; +using OnlineSalesAutoCrop.CoreAPI.API.Swagger; +using OnlineSalesAutoCrop.CoreAPI.Configuration.DI; +using OnlineSalesAutoCrop.CoreAPI.Configurations; +using OnlineSalesAutoCrop.CoreAPI.Models; +using OnlineSalesAutoCrop.CoreAPI.Models.Global; +using OnlineSalesAutoCrop.CoreAPI.Models.Responses; +using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems; +using OnlineSalesAutoCrop.CoreAPI.SignalRHub; using Swashbuckle.AspNetCore.SwaggerGen; using System; using System.IO; +using System.Linq; using System.Net; using System.Reflection; using System.Text; @@ -73,20 +75,40 @@ namespace OnlineSalesAutoCrop.CoreAPI services.Configure(Configuration.GetSection("AppSettings")); services.Configure(Configuration.GetSection("MenuSettings")); - #endregion + #endregion - #region Controllers + #region Controllers - services.AddControllers(options => + services.AddControllers(options => { options.Filters.Add(new ProducesAttribute("application/json")); }); - #endregion + services.Configure(options => + { + options.InvalidModelStateResponseFactory = context => + { + var errors = context.ModelState + .Where(kvp => kvp.Value?.Errors.Count > 0) + .SelectMany(kvp => kvp.Value!.Errors.Select(e => e.ErrorMessage)) + .ToList(); - #region API versioning + var response = new MobileResponseBase + { + ReturnStatus = StatusCodes.Status400BadRequest, + ReturnMessage = errors, + Data = null + }; - services.AddApiVersioning(options => + return new BadRequestObjectResult(response); + }; + }); + + #endregion + + #region API versioning + + services.AddApiVersioning(options => { options.ReportApiVersions = true; options.DefaultApiVersion = new ApiVersion(1, 0); diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/dotnet-tools.json b/Api/OnlineSalesAutoCrop.CoreAPI/dotnet-tools.json new file mode 100644 index 0000000..807729e --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "10.0.9", + "commands": [ + "dotnet-ef" + ], + "rollForward": false + } + } +} \ No newline at end of file