complete billing and delivey endpoint for integration

This commit is contained in:
dibakor 2026-07-21 17:34:40 +06:00
parent 827795661a
commit 4990ac7a4a
6 changed files with 269 additions and 55 deletions

View File

@ -159,3 +159,4 @@ public class CustomerByCompanyCodeRequest
[StringLength(4, MinimumLength = 1, ErrorMessage = "Company Code must be between 1 and 4 characters")]
public string CompanyCode { get; set; } = string.Empty;
}

View File

@ -1,50 +1,87 @@
using System;

using DocumentFormat.OpenXml.Office2010.ExcelAc;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
public class IntegrationInvoiceRequest
public class IntegrationBillingDocumentRequest
{
[Required(ErrorMessage = "Invoice document number is required.")]
[StringLength(10, ErrorMessage = "Invoice document number cannot exceed 10 characters.")]
public string InvoiceDocumentNo { get; set; }
[Required(ErrorMessage = "Invoice/Billing document number is required.")]
[StringLength(50, ErrorMessage = "Invoice/Billing document number cannot exceed 50 characters.")]
public string InvoiceBillingDocumentNumber { get; set; } = string.Empty;
[Required(ErrorMessage = "Invoice date is required.")]
public DateTime InvoiceDate { get; set; }
[Required(ErrorMessage = "Billing date is required.")]
[DataType(DataType.Date)]
public DateTime BillingDate { get; set; }
[Required(ErrorMessage = "Sales order reference number is required.")]
[StringLength(13, ErrorMessage = "Sales order reference number cannot exceed 13 characters.")]
public string SalesOrderRefNo { get; set; }
[StringLength(50, ErrorMessage = "Sales order reference number cannot exceed 50 characters.")]
public string SalesOrderReferenceNumber { get; set; } = string.Empty;
[Required(ErrorMessage = "Delivery reference number is required.")]
[StringLength(10, ErrorMessage = "Delivery reference number cannot exceed 10 characters.")]
public string DeliveryRefNo { get; set; }
[StringLength(50, ErrorMessage = "Delivery reference number cannot exceed 50 characters.")]
public string DeliveryReferenceNumber { get; set; } = string.Empty;
[Required(ErrorMessage = "Invoice amount is required.")]
[Range(typeof(decimal), "0.01", "999999999999.99",
ErrorMessage = "Invoice amount must be greater than 0.")]
public decimal InvoiceAmount { get; set; }
[Required(ErrorMessage = "Invoice items are required.")]
[MinLength(1, ErrorMessage = "At least one invoice item is required.")]
public List<IntegrationInvoiceItemRequest> Items { get; set; } = new();
public IList<IntegrationBillingDocumentItemRequest> Items { get; set; } = new List<IntegrationBillingDocumentItemRequest>();
}
public class IntegrationInvoiceItemRequest
public class IntegrationBillingDocumentItemRequest
{
[Required(ErrorMessage = "Material code is required.")]
[RegularExpression(@".*\S.*", ErrorMessage = "Material code cannot be empty or whitespace.")]
[StringLength(10, ErrorMessage = "Material code cannot exceed 10 characters.")]
public string MaterialCode { get; set; }
[Required(ErrorMessage = "Item code is required.")]
[StringLength(100, ErrorMessage = "Item code cannot exceed 100 characters.")]
public string ItemCode { get; set; } = string.Empty;
[Required(ErrorMessage = "Quantity is required.")]
[Range(typeof(decimal), "0.01", "999999999999.99",
ErrorMessage = "Quantity must be greater than 0.")]
[Range(0.000001, double.MaxValue, ErrorMessage = "Quantity must be greater than zero.")]
public decimal Quantity { get; set; }
[Required(ErrorMessage = "Unit of measurement is required.")]
[StringLength(10, ErrorMessage = "Unit of measurement cannot exceed 10 characters.")]
public string UnitOfMeasurement { get; set; }
[Required(ErrorMessage = "Unit of Measure is required.")]
[StringLength(20, ErrorMessage = "Unit of Measure cannot exceed 20 characters.")]
public string UnitOfMeasure { get; set; } = string.Empty;
[Required(ErrorMessage = "Billing amount is required.")]
[Range(0.01, double.MaxValue, ErrorMessage = "Billing amount must be greater than zero.")]
[DataType(DataType.Currency)]
public decimal BillingAmount { get; set; }
}
public class IntegrationDeliveryDocumentRequest
{
[Required(ErrorMessage = "Delivery Document number is required.")]
[StringLength(50, ErrorMessage = "Delivery Document number cannot exceed 50 characters.")]
public string DeliveryDocumentNo{ get; set; } = string.Empty;
[Required(ErrorMessage = "Delivery date is required.")]
[DataType(DataType.Date)]
public DateTime DeliveryDate { get; set; }
[Required(ErrorMessage = "Sales order reference number is required.")]
[StringLength(50, ErrorMessage = "Sales order reference number cannot exceed 50 characters.")]
public string SalesOrderReferenceNumber { get; set; } = string.Empty;
[Required(ErrorMessage = "Delivery Status is required.")]
[StringLength(5, ErrorMessage = "Delivery Status cannot exceed 5 characters.")]
public string DeliveryStatus { get; set; } = string.Empty;
public IList<IntegrationDeliveryDocumentItemRequest> Items { get; set; } = new List<IntegrationDeliveryDocumentItemRequest>();
}
public class IntegrationDeliveryDocumentItemRequest
{
[Required(ErrorMessage = "Item code is required.")]
[StringLength(100, ErrorMessage = "Item code cannot exceed 100 characters.")]
public string ItemCode { get; set; } = string.Empty;
[Required(ErrorMessage = "Quantity is required.")]
[Range(0.000001, double.MaxValue, ErrorMessage = "Quantity must be greater than zero.")]
public decimal Quantity { get; set; }
[Required(ErrorMessage = "Unit of Measure is required.")]
[StringLength(20, ErrorMessage = "Unit of Measure cannot exceed 20 characters.")]
public string UnitOfMeasure { get; set; } = string.Empty;
}

View File

@ -1,13 +1,18 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
public class IntegrationInvoiceReqResponse : ResponseBase
public class IntegrationBillingReqResponse : ResponseBase
{
public string SalesOrderRefNo { get; set; }
public string InvoiceDocumnentNo { get; set; }
public DateTime InvoiceDate { get; set; }
public string DeliveryRefNo { get; set; }
public int NumberOfMaterial { get; set; }
public string InvoiceBillingDocumentNumber { get; set; } = string.Empty;
public DateTime BillingDate { get; set; }
}
public class IntegrationDeliveryReqResponse : ResponseBase
{
public string DeliveryDocumentNo{ get; set; } = string.Empty;
public DateTime DeliveryDate { get; set; }
}

View File

@ -33,5 +33,6 @@ public interface IIntegrationService
Task<IntegrationPaymentTermResponse> GetPaymentTermsByFilterAsync(GetIntegrationPaymentTermRequest request);
//Invoices
Task<IntegrationInvoiceReqResponse> SaveInvoiceAsync(IntegrationInvoiceRequest request);
Task<IntegrationBillingReqResponse> SaveBillingDocumentAsync(IntegrationBillingDocumentRequest request);
Task<IntegrationDeliveryReqResponse> SaveDeliveryInvoiceDocumentAsync(IntegrationDeliveryDocumentRequest request);
}

View File

@ -5,16 +5,13 @@ using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options;
using OnlineSalesAutoCrop.CoreAPI.Models;
using OnlineSalesAutoCrop.CoreAPI.Models.Global;
using OnlineSalesAutoCrop.CoreAPI.Models.Objects.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Models.Requests.Systems;
using OnlineSalesAutoCrop.CoreAPI.Models.Responses.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Integrations;
using OnlineSalesAutoCrop.CoreAPI.Services.Contracts.Systems;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
namespace OnlineSalesAutoCrop.CoreAPI.Services.Services.Integrations;
@ -488,10 +485,6 @@ public class IntegrationService : IIntegrationService
return response;
}
public async Task<IntegrationInvoiceReqResponse> SaveInvoiceAsync(IntegrationInvoiceRequest request)
{
throw new NotImplementedException();
}
#region Customer Private Functions
@ -1461,6 +1454,150 @@ public class IntegrationService : IIntegrationService
return returnValue;
}
#endregion
#region Billing and Delivery Endpoints
public async Task<IntegrationBillingReqResponse> SaveBillingDocumentAsync(IntegrationBillingDocumentRequest request)
{
IntegrationBillingReqResponse response = new();
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
CreateBillingDocument(tc, request);
response.InvoiceBillingDocumentNumber = request.InvoiceBillingDocumentNumber;
response.BillingDate = request.BillingDate;
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
public async Task<IntegrationDeliveryReqResponse> SaveDeliveryInvoiceDocumentAsync(IntegrationDeliveryDocumentRequest request)
{
IntegrationDeliveryReqResponse response = new();
try
{
using TransactionContext tc = await TransactionContext.BeginAsync(_settings.DefaultConnection.ConnectionNode, true);
try
{
CreateDeliveryInovoiceDocument(tc, request);
response.DeliveryDocumentNo = request.DeliveryDocumentNo;
response.DeliveryDate = request.DeliveryDate;
tc.End();
}
catch (Exception ie)
{
tc?.HandleError();
throw DBCustomError.GenerateCustomError(ie);
}
}
catch (Exception ex)
{
throw;
}
return response;
}
private bool CreateBillingDocument(TransactionContext tc, IntegrationBillingDocumentRequest request)
{
bool returnValue = false;
try
{
DataTable itemTable = new DataTable();
itemTable.Columns.Add("MaterialCode", typeof(string));
itemTable.Columns.Add("Quantity", typeof(decimal));
itemTable.Columns.Add("UnitOfMeasure", typeof(string));
itemTable.Columns.Add("BillingAmount", typeof(decimal));
foreach (var item in request.Items)
{
DataRow dr = itemTable.NewRow();
dr["MaterialCode"] = item.ItemCode;
dr["Quantity"] = item.Quantity;
dr["UnitOfMeasure"] = item.UnitOfMeasure;
dr["BillingAmount"] = item.BillingAmount;
itemTable.Rows.Add(dr);
}
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam("@BillingNumber", SqlDbType.NVarChar, request.InvoiceBillingDocumentNumber),
SqlHelperExtension.CreateInParam("@SalesOrderNo", SqlDbType.NVarChar, request.SalesOrderReferenceNumber),
SqlHelperExtension.CreateInParam("@BillingDate", SqlDbType.DateTime, request.BillingDate),
SqlHelperExtension.CreateInParam("@DeliveryNumber", SqlDbType.NVarChar, request.DeliveryReferenceNumber),
SqlHelperExtension.CreateInParam("@BillingItems", SqlDbType.Structured, itemTable)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.InsertBillingDocument", parameterValues: p);
returnValue = true;
}
catch (Exception)
{
throw;
}
return returnValue;
}
private bool CreateDeliveryInovoiceDocument(TransactionContext tc, IntegrationDeliveryDocumentRequest request)
{
bool returnValue = false;
try
{
DataTable itemTable = new DataTable();
itemTable.Columns.Add("MaterialCode", typeof(string));
itemTable.Columns.Add("Quantity", typeof(decimal));
itemTable.Columns.Add("UnitOfMeasure", typeof(string));
foreach (var item in request.Items)
{
DataRow dr = itemTable.NewRow();
dr["MaterialCode"] = item.ItemCode;
dr["Quantity"] = item.Quantity;
dr["UnitOfMeasure"] = item.UnitOfMeasure;
itemTable.Rows.Add(dr);
}
SqlParameter[] p =
[
SqlHelperExtension.CreateInParam("@DeliveryDocumentNo", SqlDbType.NVarChar, request.DeliveryDocumentNo),
SqlHelperExtension.CreateInParam("@SalesOrderReferenceNumber", SqlDbType.NVarChar, request.SalesOrderReferenceNumber ),
SqlHelperExtension.CreateInParam("@DeliveryDate", SqlDbType.DateTime, request.DeliveryDate),
SqlHelperExtension.CreateInParam("@DeliveryStatus", SqlDbType.NVarChar, request.DeliveryStatus),
SqlHelperExtension.CreateInParam("@InvoiceItems", SqlDbType.Structured, itemTable)
];
_ = tc.ExecuteNonQuerySp(spName: "dbo.InsertPrimarySalesInvoice", parameterValues: p);
returnValue = true;
}
catch (Exception)
{
throw;
}
return returnValue;
}
#endregion
}

View File

@ -284,31 +284,64 @@ namespace OnlineSalesAutoCrop.CoreAPI.Controllers
}
/// <summary>
/// Insert Invoices for SAP
/// Insert Deliveries for SAP
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>if created return 201 or if updated return 200 true</returns>
[HttpPost("Invoices")]
[HttpPost("Deliveries")]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IntegrationInvoiceReqResponse))]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(IntegrationInvoiceReqResponse))]
public async Task<IActionResult> SaveInvoices(IntegrationInvoiceRequest request)
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IntegrationDeliveryReqResponse))]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(IntegrationDeliveryReqResponse))]
public async Task<IActionResult> SaveDeliveries(IntegrationDeliveryDocumentRequest request)
{
IntegrationInvoiceReqResponse response = new IntegrationInvoiceReqResponse();
IntegrationDeliveryReqResponse response = new IntegrationDeliveryReqResponse();
try
{
response = await _integrationService.SaveInvoiceAsync(request);
response.ReturnMessage.Add($"Successfully Save the Invoices for InvoiceNo :{request.InvoiceDocumentNo} " +
$"And InvoiceDate : {request.InvoiceDate} ");
response = await _integrationService.SaveDeliveryInvoiceDocumentAsync(request);
response.ReturnMessage.Add($"Successfully Save the Deliveries for Delivery Documnet No :{request.DeliveryDocumentNo} " +
$"And DeliveryDate : {request.DeliveryDate} ");
response.ReturnStatus = StatusCodes.Status201Created;
return StatusCode(StatusCodes.Status201Created, response);
}
catch (Exception ex)
{
string msg = $"Exception Occur in Invoices for InvoiceNo :{request.InvoiceDocumentNo} " +
$"And InvoiceDate : {request.InvoiceDate} " ;
string msg = $"Exception Occur in Deliveries for Delivery Documnet No :{request.DeliveryDocumentNo} " +
$"And DeliveryDate : {request.DeliveryDate} " ;
_logger.LogError(exception: ex, msg);
response.ReturnStatus = StatusCodes.Status500InternalServerError;
response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, response);
}
}
/// <summary>
/// Insert Deliveries for SAP
/// </summary>
/// <remarks>
/// </remarks>
/// <param name="request"></param>
/// <returns>if created return 201 or if updated return 200 true</returns>
[HttpPost("Billing")]
[IgnoreAntiforgeryToken]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IntegrationBillingReqResponse))]
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(IntegrationBillingReqResponse))]
public async Task<IActionResult> SaveBilling(IntegrationBillingDocumentRequest request)
{
IntegrationBillingReqResponse response = new IntegrationBillingReqResponse();
try
{
response = await _integrationService.SaveBillingDocumentAsync(request);
response.ReturnMessage.Add($"Successfully Save the Billing for Billing Documnet No :{request.InvoiceBillingDocumentNumber} " +
$"And BillingDate : {request.BillingDate} ");
response.ReturnStatus = StatusCodes.Status201Created;
return StatusCode(StatusCodes.Status201Created, response);
}
catch (Exception ex)
{
string msg = $"Exception Occur in Billing for Billing Documnet No :{request.InvoiceBillingDocumentNumber} " +
$"And BillingDate : {request.BillingDate} ";
_logger.LogError(exception: ex, msg);
response.ReturnStatus = StatusCodes.Status500InternalServerError;
response.ReturnMessage.Add(ex.InnerException != null ? ex.InnerException.Message : ex.Message);