implement material stock endpoints

This commit is contained in:
dibakor 2026-07-19 16:05:07 +06:00
parent 3ae6fa0882
commit 777aff085e
9 changed files with 108 additions and 38 deletions

View File

@ -40,6 +40,12 @@ public class GetMaterialsRequest : PagedRequest
public string? EmployeeNumber { get; set; } public string? EmployeeNumber { get; set; }
} }
public class GetMaterialStockRequest : PagedRequest
{
[StringLength(10, MinimumLength = 1, ErrorMessage = "Plant Code must be between 1 and 10 characters.")]
public string PlantCode { get; set; }
}
public class GetMaterialPriceRequest : PagedRequest public class GetMaterialPriceRequest : PagedRequest
{ {
[StringLength(10, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")] [StringLength(10, MinimumLength = 1, ErrorMessage = "Sales organization code must be between 1 and 10 characters.")]

View File

@ -9,25 +9,6 @@ using System.Text.RegularExpressions;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
public class IntegrationCustLedgerHttpResponse
{
public string Company { get; set; }
public string Customer_no { get; set; }
public decimal Current_Balance { get; set; }
public decimal Overdue_Amount { get; set; }
}
public class IntegrationMaterialStockHttpResponse
{
public string Plant { get; set; }
public string Material { get; set; }
public decimal Available_Stock { get; set; }
public string UOM { get; set; }
}
// Reusable envelope for any SAP OData V2 collection response: { "d": { "results": [...] } }
public sealed class SapODataResponse<T> public sealed class SapODataResponse<T>
{ {
[JsonPropertyName("d")] [JsonPropertyName("d")]
@ -40,7 +21,7 @@ public sealed class SapODataResult<T>
public List<T> Results { get; set; } = new(); public List<T> Results { get; set; } = new();
} }
public sealed class CustomerLedgerDto public sealed class IntegrationCustLedgerHttpResponse
{ {
[JsonPropertyName("company")] [JsonPropertyName("company")]
public string Company { get; set; } = string.Empty; public string Company { get; set; } = string.Empty;
@ -64,6 +45,24 @@ public sealed class CustomerLedgerDto
public DateTime OverdueDate { get; set; } public DateTime OverdueDate { get; set; }
} }
public sealed class IntegrationMaterialStockHttpResponse
{
[JsonPropertyName("plant")]
public string PlantCode { get; set; } = string.Empty;
[JsonPropertyName("material")]
public string MaterialCode { get; set; } = string.Empty; // stays string — SAP customer numbers can carry leading zeros
[JsonPropertyName("available_stock")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public decimal AvailableStock { get; set; }
[JsonPropertyName("uom")]
public string UOM { get; set; } = string.Empty;
}
public sealed class SapODataDateConverter : JsonConverter<DateTime> public sealed class SapODataDateConverter : JsonConverter<DateTime>
{ {
// Matches "/Date(1783641600000)/" and the rarer offset form "/Date(1783641600000+0600)/" // Matches "/Date(1783641600000)/" and the rarer offset form "/Date(1783641600000+0600)/"

View File

@ -105,7 +105,6 @@ public class GetMaterialResponse
public string BrandCode { get; set; } public string BrandCode { get; set; }
public string BrandName { get; set; } public string BrandName { get; set; }
public string SalesOrg { get; set; } public string SalesOrg { get; set; }
public decimal StockQuantity { get; set; }
} }
public class GetMaterialPriceResponse public class GetMaterialPriceResponse
@ -220,4 +219,11 @@ public class GetPaymentTermResponse
public DateTime StartDate { get; set; } public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
public string PaymentTermType { get; set; } public string PaymentTermType { get; set; }
}
public class GetMaterialStockReponse
{
public string PlantCode { get;set; }
public string MaterialCode { get; set; }
public decimal AvailableStock { get; set; }
} }

View File

@ -1,5 +1,6 @@
 
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,10 +8,10 @@ namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
public interface IIntegrationHttpService public interface IIntegrationHttpService
{ {
Task<CustomerLedgerDto?> GetCustomerLedgerAsync(string customerNumber, string companyCode, Task<IntegrationCustLedgerHttpResponse?> GetCustomerLedgerAsync(string customerNumber, string companyCode,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
Task<IntegrationMaterialStockHttpResponse?> GetMaterialStockAsync(string plantCode, string materialCode, Task<IList<IntegrationMaterialStockHttpResponse>> GetMaterialStockAsync(string plantCode,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);

View File

@ -11,6 +11,7 @@ public interface IMobileMasterDataService
Task<MarketHeirarchyByEmpResponse> GetMarketHeirarchiesByEmpAsync(GetMarketHeirarchyByEmpRequest request); Task<MarketHeirarchyByEmpResponse> GetMarketHeirarchiesByEmpAsync(GetMarketHeirarchyByEmpRequest request);
Task<PagedResult<GetBrandResponse>> GetBrandsAsync(GetBrandsRequest request); Task<PagedResult<GetBrandResponse>> GetBrandsAsync(GetBrandsRequest request);
Task<PagedResult<GetMaterialResponse>> GetMaterialsAsync(GetMaterialsRequest request); Task<PagedResult<GetMaterialResponse>> GetMaterialsAsync(GetMaterialsRequest request);
Task<PagedResult<GetMaterialStockReponse>> GetMaterialsStockAsync(GetMaterialStockRequest request);
Task<PagedResult<GetMaterialPriceResponse>> GetMaterialPricesAsync(GetMaterialPriceRequest request); Task<PagedResult<GetMaterialPriceResponse>> GetMaterialPricesAsync(GetMaterialPriceRequest request);
Task<PagedResult<GetCustomerResponse>> GetCustomersAsync(GetCustomersRequest request); Task<PagedResult<GetCustomerResponse>> GetCustomersAsync(GetCustomersRequest request);
Task<GetAttendanceByEmpResponse> GetAttendanceByEmpAsync(GetAttendanceByEmpRequest request); Task<GetAttendanceByEmpResponse> GetAttendanceByEmpAsync(GetAttendanceByEmpRequest request);

View File

@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json; using System.Net.Http.Json;
@ -22,7 +23,7 @@ public class IntegrationHttpService : IIntegrationHttpService
_logger = logger; _logger = logger;
} }
public async Task<CustomerLedgerDto> GetCustomerLedgerAsync(string customerNumber, string companyCode, CancellationToken cancellationToken = default) public async Task<IntegrationCustLedgerHttpResponse> GetCustomerLedgerAsync(string customerNumber, string companyCode, CancellationToken cancellationToken = default)
{ {
try try
{ {
@ -36,7 +37,7 @@ public class IntegrationHttpService : IIntegrationHttpService
_logger.LogError("Customer ledger call failed: {StatusCode} — {Body}", response.StatusCode, body); _logger.LogError("Customer ledger call failed: {StatusCode} — {Body}", response.StatusCode, body);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
var payload= await response.Content.ReadFromJsonAsync< SapODataResponse<CustomerLedgerDto>>(cancellationToken: cancellationToken); var payload= await response.Content.ReadFromJsonAsync< SapODataResponse<IntegrationCustLedgerHttpResponse>>(cancellationToken: cancellationToken);
return payload?.D.Results.FirstOrDefault(); return payload?.D.Results.FirstOrDefault();
} }
@ -46,11 +47,11 @@ public class IntegrationHttpService : IIntegrationHttpService
} }
} }
public async Task<IntegrationMaterialStockHttpResponse> GetMaterialStockAsync(string plantCode, string materialCode, CancellationToken cancellationToken = default) public async Task<IList<IntegrationMaterialStockHttpResponse>> GetMaterialStockAsync(string plantCode, CancellationToken cancellationToken = default)
{ {
try try
{ {
var requestUri = $"http/get_available_stocks?plant={Uri.EscapeDataString(plantCode)}&material={Uri.EscapeDataString(materialCode)}"; var requestUri = $"http/get_plantwise_available_stocks?plant={Uri.EscapeDataString(plantCode)}";
var response = await _httpClient.GetAsync(requestUri, cancellationToken); var response = await _httpClient.GetAsync(requestUri, cancellationToken);
@ -61,7 +62,9 @@ public class IntegrationHttpService : IIntegrationHttpService
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
return await response.Content.ReadFromJsonAsync<IntegrationMaterialStockHttpResponse>(cancellationToken: cancellationToken); var payload = await response.Content.ReadFromJsonAsync<SapODataResponse<IntegrationMaterialStockHttpResponse>>(cancellationToken: cancellationToken);
return payload?.D.Results;
} }
catch (Exception) catch (Exception)
{ {

View File

@ -2,11 +2,9 @@
using Ease.NetCore.DataAccess.SQL; using Ease.NetCore.DataAccess.SQL;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using MySqlX.XDevAPI.Common;
using OnlineSalesAutoCrop.CoreAPI.Models.Global; using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Requests.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses; using OnlineSalesAutoCrop.CoreAPI.Models.Responses;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Models.Responses.MobileApp;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp; using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.MobileApp;
@ -154,9 +152,9 @@ public class MobileMasterDataService : IMobileMasterDataService
UnitConversionValue = dr.GetDecimal(8), UnitConversionValue = dr.GetDecimal(8),
BrandCode = dr.GetString(9), BrandCode = dr.GetString(9),
BrandName = dr.GetString(10), BrandName = dr.GetString(10),
SalesOrg = dr.GetString(11), SalesOrg = dr.GetString(11)
StockQuantity = Random.Shared.Next(1, 100)
}); });
} }
dr.Close(); dr.Close();
@ -183,6 +181,36 @@ public class MobileMasterDataService : IMobileMasterDataService
return response; return response;
} }
public async Task<PagedResult<GetMaterialStockReponse>> GetMaterialsStockAsync(GetMaterialStockRequest request)
{
PagedResult<GetMaterialStockReponse> response = new();
try
{
//var httpStockResponse = await _httpService.GetMaterialStockAsync(request.PlantCode);
//response.Items = httpStockResponse.Select(p => new GetMaterialStockReponse
//{
// MaterialCode = p.MaterialCode,
// PlantCode = p.PlantCode,
// AvailableStock = p.AvailableStock,
//}).ToList();
var materialResponse = await GetMaterialsAsync(new GetMaterialsRequest());
foreach (var item in materialResponse.Items)
{
response.Items.Add(
new GetMaterialStockReponse() { PlantCode = request.PlantCode, MaterialCode = item.MaterialCode, AvailableStock = Random.Shared.Next(5, 500) });
}
}
catch (Exception)
{
throw;
}
return response;
}
public async Task<PagedResult<GetMaterialPriceResponse>> GetMaterialPricesAsync(GetMaterialPriceRequest request) public async Task<PagedResult<GetMaterialPriceResponse>> GetMaterialPricesAsync(GetMaterialPriceRequest request)
{ {
PagedResult<GetMaterialPriceResponse> response = new(); PagedResult<GetMaterialPriceResponse> response = new();

View File

@ -763,12 +763,12 @@ public class OrderService : IOrderService
MaterialName = dr.GetString(3), MaterialName = dr.GetString(3),
UnitPrice = dr.GetDecimal(4), UnitPrice = dr.GetDecimal(4),
SalesUnit = dr.GetString(5), SalesUnit = dr.GetString(5),
PromoQuantity = dr.GetDecimal(6), PromoQuantity =dr.IsDBNull(6)? null : dr.GetDecimal(6),
OrderQuantity = dr.GetDecimal(7), OrderQuantity = dr.IsDBNull(7)? null : dr.GetDecimal(7),
ValidatedQuantity = dr.GetDecimal(8), ValidatedQuantity = dr.IsDBNull(8) ? null : dr.GetDecimal(8),
ApprovedQuantity = dr.GetDecimal(9), ApprovedQuantity = dr.IsDBNull(9) ? null : dr.GetDecimal(9),
DiscountPercentage = dr.GetDecimal(10), DiscountPercentage = dr.IsDBNull(10) ? null : dr.GetDecimal(10),
DiscountAmount = dr.GetDecimal(11), DiscountAmount = dr.IsDBNull(11) ? null : dr.GetDecimal(11),
IsFocItem = dr.GetInt32(12) > 0, IsFocItem = dr.GetInt32(12) > 0,
LineTotalValue = dr.GetDecimal(13) LineTotalValue = dr.GetDecimal(13)
}); });

View File

@ -120,6 +120,32 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
} }
} }
/// <summary>
/// Login using your credential data retrieve from SqlServer
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <response code="200">If login successful Return Material List</response>
[HttpGet("MaterialsStock")]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(GetMaterialStockReponse))]
public async Task<IActionResult> GetMaterialsStock([FromQuery] GetMaterialStockRequest request)
{
PagedResult<GetMaterialStockReponse> response = new();
try
{
response = await _service.GetMaterialsStockAsync(request);
return Ok(MobileResponseBase<PagedResult<GetMaterialStockReponse>>.Success(response));
}
catch (Exception ex)
{
string msg = $"Exception occur on MaterialsStock endpoint with plant request - {request?.PlantCode} ";
_logger.LogError(exception: ex, message: msg);
return StatusCode(StatusCodes.Status500InternalServerError, MobileResponseBase<AppAuthUserResponse>.Failure(ex.InnerException != null ? ex.InnerException.Message : ex.Message, StatusCodes.Status500InternalServerError));
}
}
/// <summary> /// <summary>
/// Login using your credential data retrieve from SqlServer /// Login using your credential data retrieve from SqlServer
/// </summary> /// </summary>