diff --git a/Hcs.Client/Client/Api/Payload/Payments/ImportSupplierNotificationsOfOrderExecutionPayload.cs b/Hcs.Client/Client/Api/Payload/Payments/ImportSupplierNotificationsOfOrderExecutionPayload.cs new file mode 100644 index 0000000..af2398e --- /dev/null +++ b/Hcs.Client/Client/Api/Payload/Payments/ImportSupplierNotificationsOfOrderExecutionPayload.cs @@ -0,0 +1,38 @@ +using System; + +namespace Hcs.Client.Api.Payload.Payments +{ + // http://open-gkh.ru/Payment/importSupplierNotificationsOfOrderExecutionRequest/SupplierNotificationOfOrderExecution.html + public class ImportSupplierNotificationsOfOrderExecutionPayload + { + /// + /// Дата внесения платы (в случае отсутствия: дата поступления средств) + /// + public DateTime orderDate; + + /// + /// Необязательное. Месяц, за который вносится плата. Указывать совместно с . + /// + public int? month; + + /// + /// Необязательное. Год, за который вносится плата. Указывать совместно с . + /// + public short? year; + + /// + /// Идентификатор платежного документа + /// + public string paymentDocumentId; + + /// + /// Сумма + /// + public decimal amount; + + /// + /// Необязательное. Признак онлайн-оплаты. + /// + public bool? onlinePayment; + } +} diff --git a/Hcs.Client/Client/Api/PaymentsApi.cs b/Hcs.Client/Client/Api/PaymentsApi.cs new file mode 100644 index 0000000..ec114bb --- /dev/null +++ b/Hcs.Client/Client/Api/PaymentsApi.cs @@ -0,0 +1,23 @@ +using Hcs.Client.Api.Payload.Payments; +using Hcs.Client.Api.Request.Payments; +using System.Threading; +using System.Threading.Tasks; + +namespace Hcs.Client.Api +{ + // http://open-gkh.ru/PaymentsServiceAsync/ + public class PaymentsApi(ClientBase client) : ApiBase(client) + { + /// + /// Импорт пакета документов "Извещение о принятии к исполнению распоряжения", размещаемых исполнителем + /// + /// Пейлоад документа + /// Токен отмены + /// true, если операция выполнена успешно, иначе - false + public async Task ImportSupplierNotificationsOfOrderExecutionAsync(ImportSupplierNotificationsOfOrderExecutionPayload payload, CancellationToken token = default) + { + var request = new ImportSupplierNotificationsOfOrderExecutionRequest(client); + return await request.ExecuteAsync(payload, token); + } + } +} diff --git a/Hcs.Client/Client/Api/Request/Payments/ImportSupplierNotificationsOfOrderExecutionRequest.cs b/Hcs.Client/Client/Api/Request/Payments/ImportSupplierNotificationsOfOrderExecutionRequest.cs new file mode 100644 index 0000000..94fd933 --- /dev/null +++ b/Hcs.Client/Client/Api/Request/Payments/ImportSupplierNotificationsOfOrderExecutionRequest.cs @@ -0,0 +1,97 @@ +using Hcs.Client.Api.Payload.Payments; +using Hcs.Client.Api.Request.Exception; +using Hcs.Client.Internal; +using Hcs.Service.Async.Payments; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Hcs.Client.Api.Request.Payments +{ + internal class ImportSupplierNotificationsOfOrderExecutionRequest(ClientBase client) : PaymentsRequestBase(client) + { + protected override bool CanBeRestarted => false; + + internal async Task ExecuteAsync(ImportSupplierNotificationsOfOrderExecutionPayload payload, CancellationToken token) + { + ThrowIfPayloadIncorrect(payload); + + // http://open-gkh.ru/Payment/importSupplierNotificationsOfOrderExecutionRequest.html + var request = new importSupplierNotificationsOfOrderExecutionRequest + { + Id = Constants.SIGNED_XML_ELEMENT_ID, + version = "10.0.1.1", + SupplierNotificationOfOrderExecution = [GetNotificationFromPayload(payload)] + }; + + var result = await SendAndWaitResultAsync(request, async asyncClient => + { + var response = await asyncClient.importSupplierNotificationsOfOrderExecutionAsync(CreateRequestHeader(), request); + return response.AckRequest.Ack; + }, token); + + result.Items.OfType().ToList().ForEach(error => + { + throw RemoteException.CreateNew(error.ErrorCode, error.Description); + }); + + result.Items.OfType().ToList().ForEach(commonResult => + { + commonResult.Items.OfType().ToList().ForEach(error => + { + throw RemoteException.CreateNew(error.ErrorCode, error.Description); + }); + }); + + return true; + } + + private void ThrowIfPayloadIncorrect(ImportSupplierNotificationsOfOrderExecutionPayload payload) + { + if (payload.month.HasValue && !payload.year.HasValue) + { + throw new ArgumentException($"{nameof(payload.month)} has value but {nameof(payload.year)} has not"); + } + + if (!payload.month.HasValue && payload.year.HasValue) + { + throw new ArgumentException($"{nameof(payload.year)} has value but {nameof(payload.month)} has not"); + } + + if (string.IsNullOrEmpty(payload.paymentDocumentId)) + { + throw new ArgumentException($"{nameof(payload.paymentDocumentId)} is empty"); + } + } + + private importSupplierNotificationsOfOrderExecutionRequestSupplierNotificationOfOrderExecution GetNotificationFromPayload(ImportSupplierNotificationsOfOrderExecutionPayload payload) + { + var notification = new importSupplierNotificationsOfOrderExecutionRequestSupplierNotificationOfOrderExecution() + { + TransportGUID = Guid.NewGuid().ToString(), + OrderDate = payload.orderDate, + Item = payload.paymentDocumentId, + ItemElementName = ItemChoiceType1.PaymentDocumentID, + Amount = payload.amount + }; + + if (payload.month.HasValue) + { + notification.OrderPeriod = new SupplierNotificationOfOrderExecutionTypeOrderPeriod() + { + Month = payload.month.Value, + Year = payload.year.Value + }; + } + + if (payload.onlinePayment.HasValue && payload.onlinePayment.Value) + { + notification.OnlinePayment = true; + notification.OnlinePaymentSpecified = true; + } + + return notification; + } + } +} diff --git a/Hcs.Client/Client/Api/Request/Payments/PaymentsRequestBase.cs b/Hcs.Client/Client/Api/Request/Payments/PaymentsRequestBase.cs new file mode 100644 index 0000000..9bfdc94 --- /dev/null +++ b/Hcs.Client/Client/Api/Request/Payments/PaymentsRequestBase.cs @@ -0,0 +1,55 @@ +using Hcs.Client.Api.Request; +using Hcs.Client.Api.Request.Adapter; +using Hcs.Service.Async.Payments; +using System.Threading.Tasks; + +namespace Hcs.Service.Async.Payments +{ +#pragma warning disable IDE1006 + public partial class getStateResult : IGetStateResultMany { } +#pragma warning restore IDE1006 + + public partial class PaymentPortsTypeAsyncClient : IAsyncClient + { + public async Task GetStateAsync(RequestHeader header, IGetStateRequest request) + { + return await getStateAsync(header, (getStateRequest)request); + } + } + +#pragma warning disable IDE1006 + public partial class getStateResponse : IGetStateResponse +#pragma warning restore IDE1006 + { + public IGetStateResult GetStateResult => getStateResult; + } + + public partial class AckRequestAck : IAck { } + + public partial class ErrorMessageType : IErrorMessage { } + +#pragma warning disable IDE1006 + public partial class getStateRequest : IGetStateRequest { } +#pragma warning restore IDE1006 +} + +namespace Hcs.Client.Api.Request.Payments +{ + internal class PaymentsRequestBase(ClientBase client) : + RequestBase(client) + { + protected override EndPoint EndPoint => EndPoint.PaymentsAsync; + + protected override bool EnableMinimalResponseWaitDelay => true; + + protected override bool CanBeRestarted => true; + + protected override int RestartTimeoutMinutes => 20; + } +} diff --git a/Hcs.Client/Client/UniClient.cs b/Hcs.Client/Client/UniClient.cs index cf23e38..c1f11a3 100644 --- a/Hcs.Client/Client/UniClient.cs +++ b/Hcs.Client/Client/UniClient.cs @@ -23,6 +23,8 @@ namespace Hcs.Client public OrgRegistryCommonApi OrgRegistryCommon => new(this); + public PaymentsApi Payments => new(this); + public void SetSigningCertificate(X509Certificate2 cert, string pin = null) { pin ??= Constants.DEFAULT_CERTIFICATE_PIN; diff --git a/Hcs.Client/Hcs.Client.csproj b/Hcs.Client/Hcs.Client.csproj index 64965c5..ee607ae 100644 --- a/Hcs.Client/Hcs.Client.csproj +++ b/Hcs.Client/Hcs.Client.csproj @@ -79,6 +79,8 @@ + + @@ -126,6 +128,8 @@ + + diff --git a/Hcs.TestApp/Hcs.TestApp.csproj b/Hcs.TestApp/Hcs.TestApp.csproj index 792f1f2..5133fc2 100644 --- a/Hcs.TestApp/Hcs.TestApp.csproj +++ b/Hcs.TestApp/Hcs.TestApp.csproj @@ -76,6 +76,7 @@ + diff --git a/Hcs.TestApp/TestApp/Program.cs b/Hcs.TestApp/TestApp/Program.cs index 4946766..f2c7dd2 100644 --- a/Hcs.TestApp/TestApp/Program.cs +++ b/Hcs.TestApp/TestApp/Program.cs @@ -42,6 +42,7 @@ namespace Hcs.TestApp var nsiScenario = new NsiScenario(client); var nsiCommonScenario = new NsiCommonScenario(client); var orgRegistryCommonScenario = new OrgRegistryCommonScenario(client); + var paymentsScenario = new PaymentsScenario(client); try { //billsScenario.ImportPaymentDocument(); @@ -77,6 +78,8 @@ namespace Hcs.TestApp //nsiCommonScenario.ExportNsiItem276(); //orgRegistryCommonScenario.ExportOrgRegistry(); + + //paymentsScenario.ImportSupplierNotificationsOfOrderExecution(); } catch (Exception e) { diff --git a/Hcs.TestApp/TestApp/Scenario/PaymentsScenario.cs b/Hcs.TestApp/TestApp/Scenario/PaymentsScenario.cs new file mode 100644 index 0000000..3757127 --- /dev/null +++ b/Hcs.TestApp/TestApp/Scenario/PaymentsScenario.cs @@ -0,0 +1,27 @@ +using Hcs.Client; +using Hcs.Client.Api.Payload.Payments; +using System; + +namespace Hcs.TestApp.Scenario +{ + internal class PaymentsScenario(UniClient client) + { + private readonly UniClient client = client; + + internal void ImportSupplierNotificationsOfOrderExecution() + { + var payload = new ImportSupplierNotificationsOfOrderExecutionPayload() + { + orderDate = new DateTime(2025, 9, 5), + month = 8, + year = 2025, + paymentDocumentId = "00АА095186-02-5081", + amount = 4000M, + onlinePayment = true + }; + + var result = client.Payments.ImportSupplierNotificationsOfOrderExecutionAsync(payload).Result; + Console.WriteLine("Scenario execution " + (result ? "succeeded" : "failed")); + } + } +}