diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs
index 4a2a508..9d50f91 100644
--- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs
+++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs
@@ -266,11 +266,13 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global
///
[Required]
public ApiSettings API { get; set; }
+
+ public AutoCropSAPApi AutoCropSAPApi { get; set; }
- ///
- /// Swagger Setting
- ///
- [Required]
+ ///
+ /// Swagger Setting
+ ///
+ [Required]
public Swagger Swagger { get; set; }
///
@@ -346,10 +348,20 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global
public string Url { get; set; }
}
- ///
- ///
- ///
- public class ApiLicense
+ ///
+ /// AutoCrop Integration API Details
+ ///
+ public class AutoCropSAPApi
+ {
+ public string BaseUrl { get; set; }
+ public string UserName { get; set; }
+ public string Password { get; set; }
+ }
+
+ ///
+ ///
+ ///
+ public class ApiLicense
{
public string Name { get; set; }
public string Url { get; set; }
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationHttpResponse.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationHttpResponse.cs
new file mode 100644
index 0000000..eff3d37
--- /dev/null
+++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Responses/Integrations/IntegrationHttpResponse.cs
@@ -0,0 +1,91 @@
+
+
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+
+
+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
+{
+ [JsonPropertyName("d")]
+ public SapODataResult D { get; set; } = default!;
+}
+
+public sealed class SapODataResult
+{
+ [JsonPropertyName("results")]
+ public List Results { get; set; } = new();
+}
+
+public sealed class CustomerLedgerDto
+{
+ [JsonPropertyName("company")]
+ public string Company { get; set; } = string.Empty;
+
+ [JsonPropertyName("customer_no")]
+ public string CustomerNo { get; set; } = string.Empty; // stays string — SAP customer numbers can carry leading zeros
+
+ [JsonPropertyName("current_balance")]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
+ public decimal CurrentBalance { get; set; }
+
+ [JsonPropertyName("overdue_amount")]
+ [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
+ public decimal OverdueAmount { get; set; }
+
+ [JsonPropertyName("currency")]
+ public string Currency { get; set; } = string.Empty;
+
+ [JsonPropertyName("overdue_date")]
+ [JsonConverter(typeof(SapODataDateConverter))]
+ public DateTime OverdueDate { get; set; }
+}
+
+public sealed class SapODataDateConverter : JsonConverter
+{
+ // Matches "/Date(1783641600000)/" and the rarer offset form "/Date(1783641600000+0600)/"
+ private static readonly Regex Pattern = new(@"/Date\((-?\d+)(?:[+-]\d{4})?\)/", RegexOptions.Compiled);
+
+ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var raw = reader.GetString();
+ if (string.IsNullOrEmpty(raw))
+ return default;
+
+ var match = Pattern.Match(raw);
+ if (!match.Success)
+ throw new JsonException($"Unexpected SAP OData date format: '{raw}'");
+
+ var ms = long.Parse(match.Groups[1].Value);
+ return DateTimeOffset.FromUnixTimeMilliseconds(ms).UtcDateTime;
+ }
+
+ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
+ {
+ var ms = new DateTimeOffset(value, TimeSpan.Zero).ToUnixTimeMilliseconds();
+ writer.WriteStringValue($"/Date({ms})/");
+ }
+}
\ No newline at end of file
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Integrations/IIntegrationHttpService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Integrations/IIntegrationHttpService.cs
new file mode 100644
index 0000000..19f6939
--- /dev/null
+++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Contracts/Integrations/IIntegrationHttpService.cs
@@ -0,0 +1,17 @@
+
+using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
+
+public interface IIntegrationHttpService
+{
+ Task GetCustomerLedgerAsync(string customerNumber, string companyCode,
+ CancellationToken cancellationToken = default);
+
+ Task GetMaterialStockAsync(string plantCode, string materialCode,
+ CancellationToken cancellationToken = default);
+
+
+}
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationHttpService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationHttpService.cs
new file mode 100644
index 0000000..e7dc82f
--- /dev/null
+++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationHttpService.cs
@@ -0,0 +1,71 @@
+
+using Microsoft.Extensions.Logging;
+using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
+using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations;
+
+public class IntegrationHttpService : IIntegrationHttpService
+{
+ private readonly HttpClient _httpClient;
+ private readonly ILogger _logger;
+
+ public IntegrationHttpService(HttpClient httpClient, ILogger logger)
+ {
+ _httpClient = httpClient;
+ _logger = logger;
+ }
+
+ public async Task GetCustomerLedgerAsync(string customerNumber, string companyCode, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ var requestUri = $"http/get_customer_ledger?customer={Uri.EscapeDataString(customerNumber)}&company={Uri.EscapeDataString(companyCode)}";
+
+ var response = await _httpClient.GetAsync(requestUri, cancellationToken);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ var body = await response.Content.ReadAsStringAsync(cancellationToken);
+ _logger.LogError("Customer ledger call failed: {StatusCode} — {Body}", response.StatusCode, body);
+ response.EnsureSuccessStatusCode();
+ }
+ var payload= await response.Content.ReadFromJsonAsync< SapODataResponse>(cancellationToken: cancellationToken);
+
+ return payload?.D.Results.FirstOrDefault();
+ }
+ catch(Exception)
+ {
+ throw;
+ }
+ }
+
+ public async Task GetMaterialStockAsync(string plantCode, string materialCode, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ var requestUri = $"http/get_available_stocks?plant={Uri.EscapeDataString(plantCode)}&material={Uri.EscapeDataString(materialCode)}";
+
+ var response = await _httpClient.GetAsync(requestUri, cancellationToken);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ var body = await response.Content.ReadAsStringAsync(cancellationToken);
+ _logger.LogError("Material Stock call failed: {StatusCode} — {Body}", response.StatusCode, body);
+ response.EnsureSuccessStatusCode();
+ }
+
+ return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken);
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ }
+}
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs
index 3f84fb1..74a7512 100644
--- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs
+++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/MobileApp/MobileMasterDataService.cs
@@ -8,11 +8,13 @@ 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.Integrations;
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.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.MobileApp;
@@ -21,11 +23,13 @@ public class MobileMasterDataService : IMobileMasterDataService
{
private readonly AppSettings _settings;
private readonly IUserService _userService;
+ private readonly IIntegrationHttpService _httpService;
- public MobileMasterDataService(IOptions options, IUserService userService)
+ public MobileMasterDataService(IOptions options, IUserService userService, IIntegrationHttpService httpService)
{
_settings = options.Value;
_userService = userService;
+ _httpService = httpService;
}
public async Task GetMarketHeirarchiesByEmpAsync(GetMarketHeirarchyByEmpRequest request)
@@ -236,6 +240,7 @@ public class MobileMasterDataService : IMobileMasterDataService
public async Task> GetCustomersAsync(GetCustomersRequest request)
{
+ string companyCode = string.Empty;
PagedResult response = new();
try
{
@@ -284,6 +289,7 @@ public class MobileMasterDataService : IMobileMasterDataService
CreditBalance = dr.GetDecimal(23),
CreditOverdue = dr.GetDecimal(24),
});
+ companyCode = dr.GetString(25);
}
dr.Close();
@@ -292,6 +298,18 @@ public class MobileMasterDataService : IMobileMasterDataService
response.PageSize = request.PageSize;
}
tc.End();
+
+
+ if (!string.IsNullOrWhiteSpace(request.CustomerCode))
+ {
+ var customerLedger = await _httpService.GetCustomerLedgerAsync(request.CustomerCode, companyCode);
+ response.Items = response.Items.Select(x =>
+ {
+ x.CreditBalance = customerLedger.CurrentBalance;
+ x.CreditOverdue = customerLedger.OverdueAmount;
+ return x;
+ }).ToList();
+ }
}
catch (Exception ie)
{
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj b/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj
index 99aee17..47984ff 100644
--- a/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj
+++ b/Api/OnlineSalesAutoCrop.CoreAPI/OnlineSalesAutoCrop.CoreAPI.csproj
@@ -171,6 +171,8 @@
+
+
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs
index 472a207..2a650a6 100644
--- a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs
+++ b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs
@@ -8,7 +8,6 @@ using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@@ -23,18 +22,22 @@ 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.Services.Contracts.Integrations;
+using OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations;
using OnlineSalesAutoCrop.CoreAPI.SignalRHub;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.RateLimiting;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.HttpOverrides;
+using System.Net.Http.Headers;
+using System.Net.Http;
+using System.Net;
namespace OnlineSalesAutoCrop.CoreAPI
{
@@ -341,9 +344,29 @@ namespace OnlineSalesAutoCrop.CoreAPI
services.ConfigureBusinessServices();
- #endregion
- }
- }
+ #endregion
+
+
+ #region AutoCrop Integration Service
+
+ services.AddHttpClient((sp, client) =>
+ {
+ var opt = _appSettings.AutoCropSAPApi;
+
+ client.BaseAddress = new Uri(opt.BaseUrl);
+
+ var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{opt.UserName}:{opt.Password}"));
+ client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", basicAuth);
+
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+
+ client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
+ })
+ .AddStandardResilienceHandler(); // retry + timeout + circuit breaker, bundled
+
+ #endregion
+ }
+ }
catch
{
//Nothimg to do here as we are handling all exceptions in Configure method using global exception handler
diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json
index 3b3c47c..cf9d690 100644
--- a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json
+++ b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json
@@ -85,7 +85,12 @@
"WaMsgSvcSid": "MG8401d33a9a3b2aea95619bda3e5757b5",
"WaSenderId": "+8801326755660",
"RefreshTokenDuration": "15",
- "AccessTokenDuration": "60"
+ "AccessTokenDuration": "60",
+ "AutoCropSAPApi": {
+ "BaseUrl": "https://accl-test-pad-l06vsvh7.it-cpi004-rt.cfapps.ap11.hana.ondemand.com/",
+ "UserName": "sb-1635ae0e-9941-4998-962f-e23f592d29b5!b45657|it-rt-accl-test-pad-l06vsvh7!b68",
+ "Password": "01b3f9b9-8dee-4aed-9cb9-bbefeffc1db8$eB6J_oUJZDGuJnpzAzLUxI9lAha_hljim2cfH1niJa8="
+ }
},
"MenuSettings": {