Add migrated to .NET 8.0 variant of Hcs.Client
This commit is contained in:
7
Hcs.ClientNet/Client/Api/ApiBase.cs
Normal file
7
Hcs.ClientNet/Client/Api/ApiBase.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
public abstract class ApiBase(ClientBase client)
|
||||
{
|
||||
protected ClientBase client = client;
|
||||
}
|
||||
}
|
||||
68
Hcs.ClientNet/Client/Api/BillsApi.cs
Normal file
68
Hcs.ClientNet/Client/Api/BillsApi.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using Hcs.ClientNet.Api.Payload.Bills;
|
||||
using Hcs.ClientNet.Api.Request.Bills;
|
||||
using Hcs.Service.Async.Bills;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/BillsServiceAsync/
|
||||
public class BillsApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Экспорт платежных документов
|
||||
/// </summary>
|
||||
/// <param name="paymentDocumentID">Идентификатор платежного документа</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Платежные документы</returns>
|
||||
public async Task<IEnumerable<exportPaymentDocumentResultType>> ExportPaymentDocumentDataByPaymentDocumentIDAsync(string paymentDocumentID, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportPaymentDocumentDataRequest(client);
|
||||
return await request.ExecuteByPaymentDocumentIDAsync(paymentDocumentID, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Экспорт платежных документов
|
||||
/// </summary>
|
||||
/// <param name="year">Год</param>
|
||||
/// <param name="month">Месяц</param>
|
||||
/// <param name="fiasHouseGuid">Глобальный уникальный идентификатор дома по ФИАС</param>
|
||||
/// <param name="accountNumber">Номер лицевого счета/иной идентификатор плательщика</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Платежные документы</returns>
|
||||
public async Task<IEnumerable<exportPaymentDocumentResultType>> ExportPaymentDocumentDataByAccountNumberAsync(short year, int month, string fiasHouseGuid, string accountNumber, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportPaymentDocumentDataRequest(client);
|
||||
return await request.ExecuteByAccountNumberAsync(year, month, fiasHouseGuid, accountNumber, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Экспорт платежных документов
|
||||
/// </summary>
|
||||
/// <param name="year">Год</param>
|
||||
/// <param name="month">Месяц</param>
|
||||
/// <param name="fiasHouseGuid">Глобальный уникальный идентификатор дома по ФИАС</param>
|
||||
/// <param name="paymentDocumentNumber">Номер платежного документа, по которому внесена плата,
|
||||
/// присвоенный такому документу исполнителем в целях осуществления расчетов по внесению платы</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Платежные документы</returns>
|
||||
public async Task<IEnumerable<exportPaymentDocumentResultType>> ExportPaymentDocumentDataByPaymentDocumentNumberAsync(short year, int month, string fiasHouseGuid, string paymentDocumentNumber, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportPaymentDocumentDataRequest(client);
|
||||
return await request.ExecuteByPaymentDocumentNumberAsync(year, month, fiasHouseGuid, paymentDocumentNumber, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт сведений о платежных документах
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад сведений о платежных документах</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportPaymentDocumentDataAsync(ImportPaymentDocumentDataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportPaymentDocumentDataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Hcs.ClientNet/Client/Api/DeviceMeteringApi.cs
Normal file
37
Hcs.ClientNet/Client/Api/DeviceMeteringApi.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Hcs.ClientNet.Api.Payload.DeviceMetering;
|
||||
using Hcs.ClientNet.Api.Request.DeviceMetering;
|
||||
using Hcs.Service.Async.DeviceMetering;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/DeviceMeteringServiceAsync/
|
||||
public class DeviceMeteringApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Экспорт истории показаний и поверок приборов учета пользователя, установленных в указанном доме
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад выборки ПУ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Лицевые счета</returns>
|
||||
public async Task<IEnumerable<exportMeteringDeviceHistoryResultType>> ExportMeteringDeviceHistoryAsync(ExportMeteringDeviceHistoryPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportMeteringDeviceHistoryRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт показаний приборов учета
|
||||
/// </summary>
|
||||
/// <param name="values">Показания прибора учета</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportMeteringDeviceValuesAsync(importMeteringDeviceValuesRequestMeteringDevicesValues values, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportMeteringDeviceValuesRequest(client);
|
||||
return await request.ExecuteAsync(values, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
169
Hcs.ClientNet/Client/Api/HouseManagementApi.cs
Normal file
169
Hcs.ClientNet/Client/Api/HouseManagementApi.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.HouseManagement;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagementServiceAsync/
|
||||
public class HouseManagementApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Экспорт лицевых счетов
|
||||
/// </summary>
|
||||
/// <param name="fiasHouseGuid">Глобальный уникальный идентификатор дома по ФИАС</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Лицевые счета</returns>
|
||||
public async Task<IEnumerable<exportAccountResultType>> ExportAccountAsync(string fiasHouseGuid, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportAccountRequest(client);
|
||||
return await request.ExecuteAsync(fiasHouseGuid, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает информацию о доме
|
||||
/// </summary>
|
||||
/// <param name="fiasHouseGuid">Глобальный уникальный идентификатор дома по ФИАС</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Информация о доме</returns>
|
||||
public async Task<IEnumerable<exportHouseResultType>> ExportHouseAsync(string fiasHouseGuid, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportHouseRequest(client);
|
||||
return await request.ExecuteAsync(fiasHouseGuid, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает все договора ресурсоснабжения
|
||||
/// </summary>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Договора ресурсоснабжения</returns>
|
||||
public async Task<IEnumerable<exportSupplyResourceContractResultType>> ExportSupplyResourceContractDataAsync(CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportSupplyResourceContractDataRequest(client);
|
||||
return await request.ExecuteAsync(token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает договор ресурсоснабжения по его идентификатору в ГИС ЖКХ
|
||||
/// </summary>
|
||||
/// <param name="contractRootGuid">Идентификатор договора ресурсоснабжения в ГИС ЖКХ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Договор ресурсоснабжения</returns>
|
||||
public async Task<exportSupplyResourceContractResultType> ExportSupplyResourceContractDataAsync(Guid contractRootGuid, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportSupplyResourceContractDataRequest(client);
|
||||
return await request.ExecuteAsync(contractRootGuid, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает договор ресурсоснабжения по номеру договора в ГИС ЖКХ
|
||||
/// </summary>
|
||||
/// <param name="contractNumber">Номер договора ресурсоснабжения в ГИС ЖКХ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Договор ресурсоснабжения</returns>
|
||||
public async Task<exportSupplyResourceContractResultType> ExportSupplyResourceContractDataAsync(string contractNumber, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportSupplyResourceContractDataRequest(client);
|
||||
return await request.ExecuteAsync(contractNumber, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает объекты жилищного фонда из договора ресурсоснабжения по его идентификатору
|
||||
/// </summary>
|
||||
/// <param name="contractRootGuid">Идентификатор договора ресурсоснабжения в ГИС ЖКХ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Объекты жилищного фонда</returns>
|
||||
public async Task<IEnumerable<exportSupplyResourceContractObjectAddressResultType>> ExportSupplyResourceContractObjectAddressDataAsync(Guid contractRootGuid, CancellationToken token = default)
|
||||
{
|
||||
var request = new ExportSupplyResourceContractObjectAddressDataRequest(client);
|
||||
return await request.ExecuteAsync(contractRootGuid, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт лицевого счета
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад лицевого счета</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportAccountDataAsync(ImportAccountDataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportAccountDataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт сведений о ДУ (создание ДУ)
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад ДУ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Импортированный договор</returns>
|
||||
public async Task<importContractResultType> ImportContractDataAsync(ImportContractDataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportContractDataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт данных дома
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад данных дома</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportHouseUODataAsync(ImportLivingHouseUODataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportHouseUODataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт прибора учета
|
||||
/// </summary>
|
||||
/// <param name="meteringDevice">Прибор учета</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportMeteringDeviceDataAsync(MeteringDeviceFullInformationType meteringDevice, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportMeteringDeviceDataRequest(client);
|
||||
return await request.ExecuteAsync(meteringDevice, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт новости для информирования граждан
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад новости</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportNotificationDataAsync(ImportNotificationDataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportNotificationDataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт договора ресурсоснабжения с РСО
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад договора ресурсоснабжения</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Импортированный договор</returns>
|
||||
public async Task<getStateResultImportResultCommonResultImportSupplyResourceContract> ImportSupplyResourceContractDataAsync(ImportSupplyResourceContractDataPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportSupplyResourceContractDataRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт проекта договора ресурсоснабжения с РСО
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад проекта договора ресурсоснабжения</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Импортированный проект договора</returns>
|
||||
public async Task<getStateResultImportResultCommonResultImportSupplyResourceContractProject> ImportSupplyResourceContractProjectAsync(ImportSupplyResourceContractProjectPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportSupplyResourceContractProjectRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Hcs.ClientNet/Client/Api/NsiApi.cs
Normal file
32
Hcs.ClientNet/Client/Api/NsiApi.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Api.Request.Nsi;
|
||||
using Hcs.Service.Async.Nsi;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/NsiService/
|
||||
public class NsiApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Возвращает данные справочника поставщика информации
|
||||
/// </summary>
|
||||
/// <param name="registryNumber">Реестровый номер справочника</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Данные справочника</returns>
|
||||
public async Task<IEnumerable<NsiItemType>> ExportDataProviderNsiItemAsync(exportDataProviderNsiItemRequestRegistryNumber registryNumber, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new ExportDataProviderNsiItemRequest(client);
|
||||
return await request.ExecuteAsync(registryNumber, token);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Hcs.ClientNet/Client/Api/NsiCommonApi.cs
Normal file
51
Hcs.ClientNet/Client/Api/NsiCommonApi.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Api.Request.NsiCommon;
|
||||
using Hcs.Service.Async.NsiCommon;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/NsiCommonService/
|
||||
public class NsiCommonApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Возвращает данные общесистемного справочника
|
||||
/// </summary>
|
||||
/// <param name="registryNumber">Реестровый номер справочника</param>
|
||||
/// <param name="listGroup">Группа справочников, где NSI - общесистемный, а NSIRAO - ОЖФ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Данные общесистемного справочника</returns>
|
||||
public async Task<NsiItemType> ExportNsiItemAsync(int registryNumber, ListGroup listGroup, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new ExportNsiItemRequest(client);
|
||||
return await request.ExecuteAsync(registryNumber, listGroup, token);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает перечень общесистемных справочников с указанием даты последнего изменения каждого из них
|
||||
/// </summary>
|
||||
/// <param name="listGroup">Группа справочников, где NSI - общесистемный, а NSIRAO - ОЖФ</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Перечень общесистемных справочников</returns>
|
||||
public async Task<NsiListType> ExportNsiListAsync(ListGroup listGroup, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new ExportNsiListRequest(client);
|
||||
return await request.ExecuteAsync(listGroup, token);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Hcs.ClientNet/Client/Api/OrgRegistryCommonApi.cs
Normal file
52
Hcs.ClientNet/Client/Api/OrgRegistryCommonApi.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Api.Request.OrgRegistryCommon;
|
||||
using Hcs.Service.Async.OrgRegistryCommon;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/OrganizationsRegistryCommonAsyncService/
|
||||
public class OrgRegistryCommonApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// Экспорт сведений о поставщиках информации ИС
|
||||
/// </summary>
|
||||
/// <param name="isActual">Выгрузить только активных поставщиков данных</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Сведения о поставщиках данных</returns>
|
||||
public async Task<IEnumerable<exportDataProviderResultType>> ExportDataProviderAsync(bool isActual, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new ExportDataProviderRequest(client);
|
||||
return await request.ExecuteAsync(isActual, token);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Экспорт сведений из реестра организаций
|
||||
/// </summary>
|
||||
/// <param name="ogrn">ОГРН</param>
|
||||
/// <param name="kpp">КПП</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>Сведения из реестра организаций</returns>
|
||||
public async Task<IEnumerable<exportOrgRegistryResultType>> ExportOrgRegistryAsync(string ogrn, string kpp, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new ExportOrgRegistryRequest(client);
|
||||
return await request.ExecuteAsync(ogrn, kpp, token);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
using Hcs.ClientNet.Api.Registry;
|
||||
using Hcs.ClientNet.Api.Type;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.Bills
|
||||
{
|
||||
// http://open-gkh.ru/Bills/importPaymentDocumentRequest.html
|
||||
public class ImportPaymentDocumentDataPayload
|
||||
{
|
||||
// http://open-gkh.ru/Bills/importPaymentDocumentRequest/PaymentInformation.html
|
||||
public class PaymentInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// БИК банка получателя
|
||||
/// </summary>
|
||||
public string bankBIK;
|
||||
|
||||
/// <summary>
|
||||
/// Номер расчетного счета
|
||||
/// </summary>
|
||||
public string operatingAccountNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Начисление по услуге
|
||||
/// </summary>
|
||||
// http://open-gkh.ru/Bills/PaymentDocumentType/ChargeInfo.html
|
||||
public interface IChargeInfo { }
|
||||
|
||||
/// <summary>
|
||||
/// Главная коммунальная услуга
|
||||
/// </summary>
|
||||
// http://open-gkh.ru/Bills/PDServiceChargeType/MunicipalService.html
|
||||
public class MunicipalService : IChargeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Необязательное. Перерасчеты, корректировки, руб.
|
||||
/// </summary>
|
||||
public decimal? moneyRecalculation;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Льготы, субсидии, скидки, руб.
|
||||
/// </summary>
|
||||
public decimal? moneyDiscount;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Норматив потребления коммунальных ресурсов в целях использования и содержания
|
||||
/// общего имущества в многоквартирном доме.
|
||||
/// </summary>
|
||||
public decimal? houseOverallNeedsNorm;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Норматив потребления коммунальных услуг.
|
||||
/// </summary>
|
||||
public decimal? individualConsumptionNorm;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Текущие показания приборов учёта коммунальных ресурсов - индивидуальных
|
||||
/// (квартирных).
|
||||
/// </summary>
|
||||
public decimal? individualConsumptionCurrentValue;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Текущие показания приборов учёта коммунальных ресурсов - коллективных (общедомовых).
|
||||
/// </summary>
|
||||
public decimal? houseOverallNeedsCurrentValue;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Суммарный объём коммунальных ресурсов в многоквартирном доме - в помещениях дома.
|
||||
/// </summary>
|
||||
public decimal? houseTotalIndividualConsumption;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Суммарный объём коммунальных ресурсов в многоквартирном доме - в целях содержания
|
||||
/// общего имущества в многоквартирном доме.
|
||||
/// </summary>
|
||||
public decimal? houseTotalHouseOverallNeeds;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Способ определения объема коммунальных ресурсов при индивидуальном потреблении.
|
||||
/// </summary>
|
||||
public MunicipalServiceVolumeDeterminingMethod? individualConsumptionVolumeDeterminingMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Объем/площадь/кол-во коммунальных ресурсов при индивидуальном потреблении.
|
||||
/// </summary>
|
||||
public decimal? individualConsumptionVolumeValue;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Способ определения объема коммунальных ресурсов при содержании общего имущества.
|
||||
/// </summary>
|
||||
public MunicipalServiceVolumeDeterminingMethod? overallConsumptionVolumeDeterminingMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Объем/площадь/кол-во коммунальных ресурсов при содержании общего имущества.
|
||||
/// </summary>
|
||||
public decimal? overallConsumptionVolumeValue;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Размер повышающего коэффициента.
|
||||
/// </summary>
|
||||
public decimal? multiplyingFactorRatio;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Размер превышения платы, рассчитанной с применением повышающего коэффициента над
|
||||
/// размером платы, рассчитанной без учета повышающего коэффициента, руб.
|
||||
/// </summary>
|
||||
public decimal? amountOfExcessFees;
|
||||
|
||||
/// <summary>
|
||||
/// К оплате за индивидуальное потребление коммунальной услуги, руб.
|
||||
/// </summary>
|
||||
public decimal? municipalServiceIndividualConsumptionPayable;
|
||||
|
||||
/// <summary>
|
||||
/// К оплате за общедомовое потребление коммунальной услуги, руб.
|
||||
/// </summary>
|
||||
public decimal? municipalServiceCommunalConsumptionPayable;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Размер платы за коммунальные услуги, индивидуальное потребление.
|
||||
/// </summary>
|
||||
public decimal? amountOfPaymentMunicipalServiceIndividualConsumption;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Размер платы за коммунальные услуги, общедомовые нужды.
|
||||
/// </summary>
|
||||
public decimal? amountOfPaymentMunicipalServiceCommunalConsumption;
|
||||
|
||||
/// <summary>
|
||||
/// Код услуги из справочника "Вид коммунальной услуги" НСИ 3
|
||||
/// </summary>
|
||||
public RegistryElement serviceType;
|
||||
|
||||
/// <summary>
|
||||
/// Тариф/Размер платы на кв.м, руб./Размер взноса на кв.м, руб.
|
||||
/// </summary>
|
||||
public decimal rate;
|
||||
|
||||
/// <summary>
|
||||
/// К оплате за расчетный период, руб.
|
||||
/// </summary>
|
||||
public decimal totalPayable;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Начислено за расчетный период (без перерасчетов и льгот), руб.
|
||||
/// </summary>
|
||||
public decimal? accountingPeriodTotal;
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/Bills/importPaymentDocumentRequest/PaymentDocument.html
|
||||
public class PaymentDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Платежный реквизит
|
||||
/// </summary>
|
||||
public PaymentInformation paymentInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор лицевого счета
|
||||
/// </summary>
|
||||
public string accountGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Номер платежного документа, по которому внесена плата, присвоенный такому
|
||||
/// документу исполнителем в целях осуществления расчетов по внесению платы
|
||||
/// </summary>
|
||||
public string paymentDocumentNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Начисления по услугам
|
||||
/// </summary>
|
||||
public List<IChargeInfo> chargeInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Если true, то выставлен на оплату, иначе - отозван
|
||||
/// </summary>
|
||||
public bool exposeNotWithdraw;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Задолженность за предыдущие периоды, руб.
|
||||
/// </summary>
|
||||
public decimal? debtPreviousPeriods;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Аванс на начало расчетного периода, руб.
|
||||
/// </summary>
|
||||
public decimal? advanceBllingPeriod;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Итого к оплате за расчетный период c учетом задолженности/переплаты, руб.
|
||||
/// (по всему платежному документу)
|
||||
/// </summary>
|
||||
public decimal? totalPayableByPDWithDebtAndAdvance;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Сумма к оплате за расчетный период, руб. (по всему платежному документу).
|
||||
/// </summary>
|
||||
public decimal? totalPayableByPD;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Оплачено денежных средств, руб.
|
||||
/// </summary>
|
||||
public decimal? paidCash;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата последней поступившей оплаты
|
||||
/// </summary>
|
||||
public DateTime? dateOfLastReceivedPayment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Если true, то передаваемые данные платежных документов, следует считать верными,
|
||||
/// даже если они отличаются от автоматически рассчитанных системой значений. В том случае, если параметр
|
||||
/// не заполнен, то ГИС ЖХК будет проводить автоматическую проверку рассчитываемых сумм по ПД.
|
||||
/// </summary>
|
||||
public bool confirmAmountsCorrect;
|
||||
|
||||
/// <summary>
|
||||
/// Месяц расчетного периода платежного документа
|
||||
/// </summary>
|
||||
public int month;
|
||||
|
||||
/// <summary>
|
||||
/// Год расчетного периода платежного документа
|
||||
/// </summary>
|
||||
public short year;
|
||||
|
||||
/// <summary>
|
||||
/// Сведения о платежных реквизитах получателя платежа - бизнес-ключ поиска размещенных платежных
|
||||
/// реквизитов в ГИС ЖКХ
|
||||
/// </summary>
|
||||
public PaymentInformation[] paymentInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Размещаемый платежный документ. Максимум 500.
|
||||
/// </summary>
|
||||
public PaymentDocument[] paymentDocument;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
using Hcs.ClientNet.Api.Registry;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.DeviceMetering
|
||||
{
|
||||
// http://open-gkh.ru/DeviceMetering/exportMeteringDeviceHistoryRequest.html
|
||||
public class ExportMeteringDeviceHistoryPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Необязательное. Список из уникальных идентификаторов домов по ФИАС, в которых установлены ПУ
|
||||
/// пользователей. Если не указано, то будут экспортироваться данные по всем ПУ пользователей.
|
||||
/// </summary>
|
||||
public string[] fiasHouseGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное. Выбор между <see cref="meteringDeviceType"/>, <see cref="municipalResource"/> и
|
||||
/// <see cref="meteringDeviceRootGUID"/>. Тип прибора учета (НСИ 27). Максимум 100 по выбранным.
|
||||
/// </summary>
|
||||
public RegistryElement[] meteringDeviceType;
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное. Выбор между <see cref="meteringDeviceType"/>, <see cref="municipalResource"/> и
|
||||
/// <see cref="meteringDeviceRootGUID"/>. Вид коммунального ресурса (НСИ 2). Максимум 100 по выбранным.
|
||||
/// </summary>
|
||||
public RegistryElement[] municipalResource;
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное. Выбор между <see cref="meteringDeviceType"/>, <see cref="municipalResource"/> и
|
||||
/// <see cref="meteringDeviceRootGUID"/>. Идентификатор ПУ. Максимум 100 по выбранным.
|
||||
/// </summary>
|
||||
public string[] meteringDeviceRootGUID;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата ввода в эксплуатацию "С".
|
||||
/// </summary>
|
||||
public DateTime? commissioningDateFrom;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата ввода в эксплуатацию "П".
|
||||
/// </summary>
|
||||
public DateTime? сommissioningDateTo;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Выгружать архивированные или нет.
|
||||
/// </summary>
|
||||
public bool? serchArchived;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата архивации "С".
|
||||
/// </summary>
|
||||
public DateTime? archiveDateFrom;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата архивации "По".
|
||||
/// </summary>
|
||||
public DateTime? archiveDateTo;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата начала периода, за который выгружаются показания и поверки ПУ (по дате
|
||||
/// снятия показаний). Период выгрузки показаний ПУ (определяемый элементами <see cref="inputDateFrom"/>
|
||||
/// и <see cref="inputDateTo"/>) не должен выходить за пределы двух последовательных календарных месяцев.
|
||||
/// </summary>
|
||||
public DateTime? inputDateFrom;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дата окончания периода, за который выгружаются показания и поверки ПУ (по дате
|
||||
/// снятия показаний). Период выгрузки показаний ПУ (определяемый элементами <see cref="inputDateFrom"/>
|
||||
/// и <see cref="inputDateTo"/>) не должен выходить за пределы двух последовательных календарных месяцев.
|
||||
/// </summary>
|
||||
public DateTime? inputDateTo;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему гражданином,
|
||||
/// включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку не включаются.
|
||||
/// </summary>
|
||||
public bool? excludePersonAsDataSource;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему текущей
|
||||
/// организацией, включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку не включаются.
|
||||
/// </summary>
|
||||
public bool? excludeCurrentOrgAsDataSource;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему организациями
|
||||
/// отличной от текущей, включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку
|
||||
/// не включаются.
|
||||
/// </summary>
|
||||
public bool? excludeOtherOrgAsDataSource;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/importAccountRequest/Account.html
|
||||
public class ImportAccountDataPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип лицевого счета
|
||||
/// </summary>
|
||||
public enum AccountType
|
||||
{
|
||||
/// <summary>
|
||||
/// Лицевой счет для оплаты за жилое помещение и коммунальные услуги
|
||||
/// </summary>
|
||||
UO,
|
||||
|
||||
/// <summary>
|
||||
/// Лицевой счет для оплаты за коммунальные услуги
|
||||
/// </summary>
|
||||
RSO,
|
||||
|
||||
/// <summary>
|
||||
/// Лицевой счет для оплаты капитального ремонта
|
||||
/// </summary>
|
||||
CR,
|
||||
|
||||
/// <summary>
|
||||
/// Лицевой счет РКЦ
|
||||
/// </summary>
|
||||
RC,
|
||||
|
||||
/// <summary>
|
||||
/// Лицевой счет ОГВ/ОМС
|
||||
/// </summary>
|
||||
OGVorOMS,
|
||||
|
||||
/// <summary>
|
||||
/// Лицевой счет ТКО
|
||||
/// </summary>
|
||||
TKO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Номер лицевого счета или иной идентификатор плательщика. Максимум 30 символов.
|
||||
/// </summary>
|
||||
public string accountNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Идентификатор ЛС в ГИС ЖКХ (при обновлении данных ЛС).
|
||||
/// </summary>
|
||||
public string accountGUID;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Конкретизация оснований ЛС (договоров ресурсоснабжения, договоров социального найма,
|
||||
/// договоров по обращению с ТКО).
|
||||
/// </summary>
|
||||
public AccountReasonsImportType accountReasons;
|
||||
|
||||
/// <summary>
|
||||
/// Тип лицевого счета
|
||||
/// </summary>
|
||||
public AccountType accountType;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Количество проживающих, не больше 9999.
|
||||
/// </summary>
|
||||
public uint? livingPersonsNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Общая площадь для ЛС. Не более 4 цифр после целой.
|
||||
/// </summary>
|
||||
public decimal? totalSquare;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Жилая площадь. Не более 4 цифр после целой.
|
||||
/// </summary>
|
||||
public decimal? residentialSquare;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Отапливаемая площадь. Не более 4 цифр после целой.
|
||||
/// </summary>
|
||||
public decimal? heatedArea;
|
||||
|
||||
// TODO: Добавить причину закрытия лицевого счета
|
||||
|
||||
/// <summary>
|
||||
/// Помещения
|
||||
/// </summary>
|
||||
public AccountTypeAccommodation[] accomodations;
|
||||
|
||||
/// <summary>
|
||||
/// Сведения о платильщике
|
||||
/// </summary>
|
||||
public AccountTypePayerInfo payerInfo;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
using Hcs.ClientNet.Api.Registry;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
public class ImportContractDataPayload
|
||||
{
|
||||
// TODO: LicenseRequest
|
||||
|
||||
/// <summary>
|
||||
/// Объекты управления
|
||||
/// </summary>
|
||||
public importContractRequestContractPlacingContractContractObject[] contractObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Номер документа
|
||||
/// </summary>
|
||||
public string docNum;
|
||||
|
||||
/// <summary>
|
||||
/// Дата заключения
|
||||
/// </summary>
|
||||
public DateTime signingDate;
|
||||
|
||||
/// <summary>
|
||||
/// Дата вступления в силу
|
||||
/// </summary>
|
||||
public DateTime effectiveDate;
|
||||
|
||||
/// <summary>
|
||||
/// Планируемая дата окончания
|
||||
/// </summary>
|
||||
public DateTime planDateComptetion;
|
||||
|
||||
// TODO: Вторая сторона договора
|
||||
|
||||
// TODO: Protocol
|
||||
|
||||
/// <summary>
|
||||
/// Ссылка на НСИ "Основание заключения договора" (реестровый номер 58)
|
||||
/// </summary>
|
||||
public RegistryElement contractBase;
|
||||
|
||||
/// <summary>
|
||||
/// Сведения о сроках
|
||||
/// </summary>
|
||||
public DateDetailsType dateDetailsType;
|
||||
|
||||
/// <summary>
|
||||
/// Договор на управление и приложения
|
||||
/// </summary>
|
||||
public AttachmentType[] contractAttachment;
|
||||
|
||||
// TODO: AgreementAttachment
|
||||
|
||||
// TODO: SignedOwners
|
||||
|
||||
// TODO: CommissioningPermitAgreement
|
||||
|
||||
// TODO: Charter
|
||||
|
||||
// TODO: LocalGovernmentDecision
|
||||
|
||||
// TODO: RegistryDecisionID
|
||||
|
||||
// TODO: AutomaticRollOverOneYear
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
using Hcs.ClientNet.Api.Registry;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/importHouseUORequest/LivingHouse/LivingHouseToCreate.html
|
||||
// http://open-gkh.ru/HouseManagement/HouseBasicUOType.html
|
||||
public class ImportLivingHouseUODataPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Глобальный уникальный идентификатор дома по ФИАС
|
||||
/// </summary>
|
||||
public Guid fiasHouseGuid;
|
||||
|
||||
/// <summary>
|
||||
/// Общая площадь здания
|
||||
/// </summary>
|
||||
public decimal totalSquare;
|
||||
|
||||
/// <summary>
|
||||
/// Состояние (НСИ 24)
|
||||
/// </summary>
|
||||
public RegistryElement state;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Стадия жизненного цикла (НСИ 338).
|
||||
/// </summary>
|
||||
public RegistryElement lifeCycleStage;
|
||||
|
||||
/// <summary>
|
||||
/// Год ввода в эксплуатацию. До 2215 включительно.
|
||||
/// </summary>
|
||||
public short usedYear;
|
||||
|
||||
/// <summary>
|
||||
/// Количество этажей. До 999 включительно.
|
||||
/// </summary>
|
||||
public int floorCount;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. ОКТМО (обязательное для всех территорий, за исключением города и космодрома
|
||||
/// "Байконур"). Значение из ФИАС при наличии.
|
||||
/// </summary>
|
||||
public OKTMORefType oktmo;
|
||||
|
||||
/// <summary>
|
||||
/// Часовая зона. Справочник 32.
|
||||
/// </summary>
|
||||
public RegistryElement olsonTZ;
|
||||
|
||||
/// <summary>
|
||||
/// Наличие у дома статуса объекта культурного наследия
|
||||
/// </summary>
|
||||
public bool culturalHeritage;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Данные ОЖФ
|
||||
/// </summary>
|
||||
public OGFData[] ogfData;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дом находится в муниципальной собственности и в полном объеме используется
|
||||
/// в качестве общежития. Принимает только false.
|
||||
/// </summary>
|
||||
public bool isMunicipalProperty;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Дом находится в собственности субъекта Российской Федерации и в полном объеме
|
||||
/// используется в качестве общежития. Принимает только false.
|
||||
/// </summary>
|
||||
public bool isRegionProperty;
|
||||
|
||||
/// <summary>
|
||||
/// Кадастровый номер
|
||||
/// </summary>
|
||||
public string cadastralNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Условный номер. При указании в ГИС ЖКХ осуществляется привязка к ЕГРП (поиск в ЕГРП выполняется
|
||||
/// по условному номеру).
|
||||
/// </summary>
|
||||
public string conditionalNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Жилой дом блокированной застройки (если не указан - аналог false).
|
||||
/// </summary>
|
||||
public bool hasBlocks;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Несколько жилых домов с одинаковым адресом (если не указан - аналог false)
|
||||
/// </summary>
|
||||
public bool hasMultipleHousesWithSameAddress;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/importNotificationRequest/notification/Create.html
|
||||
public class ImportNotificationDataPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Выборочное. Строковое представление темы, вместо ссылки на справочник. Максимальная длина
|
||||
/// текста равно 200 символам.
|
||||
/// </summary>
|
||||
public string topic;
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное. Тема из справочника 364, заместо строкового представления темы.
|
||||
/// </summary>
|
||||
public nsiRef topicFromRegistry;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Показывает высокую важность новости.
|
||||
/// </summary>
|
||||
public bool isImportant;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Текст новости с максимальной длиной в 5000 символов.
|
||||
/// </summary>
|
||||
public string content;
|
||||
|
||||
/// <summary>
|
||||
/// Адресаты. Подходящие типы для значения:
|
||||
/// <see cref="string"/>, если это глобальный уникальный идентификатор дома по ФИАС,
|
||||
/// <see cref="importNotificationRequestNotificationCreateRoomOwners"/>,
|
||||
/// <see cref="RegOrgType"/> либо true, если все дома.
|
||||
/// </summary>
|
||||
public List<Tuple<ItemsChoiceType29, object>> destinations;
|
||||
|
||||
/// <summary>
|
||||
/// Выборочное. Если true, то новость всегда актуальна. Иначе период актуальности берется из
|
||||
/// <see cref="startDate"/> и <see cref="endDate"/>.
|
||||
/// </summary>
|
||||
public bool isNotLimit;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Период актуальности "С". Обязательно задается в случае <see cref="isNotLimit"/> = false.
|
||||
/// </summary>
|
||||
public DateTime? startDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Период актуальности "ДО". Обязательно задается в случае <see cref="isNotLimit"/> = false.
|
||||
/// </summary>
|
||||
public DateTime? endDate;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Документы новостей.
|
||||
/// </summary>
|
||||
public AttachmentType[] attachment;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Если true, то новость отправляется адресатам.
|
||||
/// </summary>
|
||||
public bool isShipOff;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Признак "Для публикации в мобильном приложении".
|
||||
/// </summary>
|
||||
public bool isForPublishToMobileApp;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация для новости, публикуемой в мобильном приложении.
|
||||
/// </summary>
|
||||
public importNotificationRequestNotificationCreateMobileAppData mobileAppData;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/SupplyResourceContractType.html
|
||||
public class ImportSupplyResourceContractDataPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Если договор не является публичным и/или присутствует заключенный на бумажном носителе
|
||||
/// (электронной форме) и/или не заключен в отношении нежилых помещений в многоквартирных домах,
|
||||
/// то равно true, иначе - false
|
||||
/// </summary>
|
||||
public bool isContract;
|
||||
|
||||
/// <summary>
|
||||
/// Номер договора
|
||||
/// </summary>
|
||||
public string contractNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Дата заключения
|
||||
/// </summary>
|
||||
public DateTime signingDate;
|
||||
|
||||
/// <summary>
|
||||
/// Дата вступления в силу
|
||||
/// </summary>
|
||||
public DateTime effectiveDate;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Договор заключен на неопределенный срок или нет.
|
||||
/// </summary>
|
||||
public bool indefiniteTerm;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Автоматически пролонгировать договор на один год при наступлении
|
||||
/// даты окончания действия или нет.
|
||||
/// </summary>
|
||||
public bool automaticRollOverOneYear;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Дата окончания действия. Обязательно для заполнения, если
|
||||
/// <see cref="automaticRollOverOneYear"/> = true.
|
||||
/// </summary>
|
||||
public DateTime? comptetionDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Период передачи текущих показаний по индивидуальным приборам учета. Обязателен для
|
||||
/// заполнения, если поле <see cref="volumeDepends"/> = true ИЛИ если поле
|
||||
/// <see cref="meteringDeviceInformation"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypePeriod period;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Показывает, разрешена ли гражданам передача текущих показаний по
|
||||
/// индивидуальным приборам учета в любой день месяца. Заполнение возможно только если: в настройках
|
||||
/// организации установлена настройка "Разрешить передачу гражданам показаний индивидуальных или общих
|
||||
/// (квартирных) приборов учета в любой день месяца" ИЛИ в настройках организации установлена настройка
|
||||
/// "Разрешить передачу гражданам показаний индивидуальных или общих (квартирных) приборов учета только
|
||||
/// в сроки, установленные в договоре, или в любой день месяца, если в договоре установлен признак
|
||||
/// "Разрешить передачу показаний приборов учета в любой день месяца" И заполнен <see cref="period"/>.
|
||||
/// </summary>
|
||||
public bool indicationsAnyDay;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Ссылка на НСИ "Основание заключения договора" (реестровый номер 58). Значения
|
||||
/// брать из <see cref="Registry.Registry58"/>.
|
||||
/// </summary>
|
||||
public nsiRef[] contractBase;
|
||||
|
||||
/// <summary>
|
||||
/// Вторая сторона договора. Подходящие типы:
|
||||
/// <see cref="SupplyResourceContractTypeApartmentBuildingOwner"/>,
|
||||
/// <see cref="SupplyResourceContractTypeApartmentBuildingRepresentativeOwner"/>,
|
||||
/// <see cref="SupplyResourceContractTypeApartmentBuildingSoleOwner"/>,
|
||||
/// <see cref="SupplyResourceContractTypeLivingHouseOwner"/>,
|
||||
/// <see cref="SupplyResourceContractTypeOrganization"/> либо true, если это договор оферты.
|
||||
/// </summary>
|
||||
public object counterparty;
|
||||
|
||||
/// <summary>
|
||||
/// Если в договоре в наличии плановый объем и режим подачи поставки ресурсов то true, иначе - false
|
||||
/// </summary>
|
||||
public bool isPlannedVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Тип ведения планового объема и режима подачи: D - в разрезе договора,
|
||||
/// O - в разрезе объектов жилищного фонда. Заполняется при наличии в договоре планового объема и
|
||||
/// режима поставки ресурсов.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypePlannedVolumeType? plannedVolumeType;
|
||||
|
||||
/// <summary>
|
||||
/// Предмет договора. Максимум 100 записей.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeContractSubject[] contractSubject;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Размещение информации о начислениях за коммунальные услуги осуществляет: R(SO)- РСО,
|
||||
/// P(roprietor) - исполнитель коммунальных услуг. Заполняется, если порядок размещения информации
|
||||
/// о начислениях за коммунальные услуги ведется в разрезе договора.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeCountingResource? countingResource;
|
||||
|
||||
/// <summary>
|
||||
/// Показатели качества коммунальных ресурсов и температурный график ведутся: D - в разрезе договора,
|
||||
/// O - в разрезе объектов жилищного фонда
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeSpecifyingQualityIndicators specifyingQualityIndicators;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Признак "Отсутствие присоединения сетей объектов жилищного фонда к централизованной
|
||||
/// системе водоснабжения". Может быть указан, только если показатели качества коммунальных ресурсов
|
||||
/// ведутся в разрезе договора и предмет договора включает коммунальную услугу "Холодное водоснабжение"
|
||||
/// И/ИЛИ "Горячее водоснабжение".
|
||||
/// </summary>
|
||||
public bool noConnectionToWaterSupply;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Данные об объекте жилищного фонда. При импорте договора должен быть добавлен как минимум
|
||||
/// один адрес объекта жилищного фонда.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeObjectAddress[] objectAddress;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Показатель качества (содержащийся в справочнике показателей качества). Если
|
||||
/// показатели указываются в разрезе договора, то ссылка на ОЖФ не заполняется. Если показатели
|
||||
/// указываются в разрезе ОЖФ, то ссылка на ОЖФ обязательна.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeQuality[] quality;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Иной показатель качества коммунального ресурса (не содержащийся в справочнике
|
||||
/// показателей качества). Если показатели указываются в разрезе договора, то ссылка на ОЖФ
|
||||
/// не заполняется. Если показатели указываются в разрезе ОЖФ, то ссылка на ОЖФ обязательна.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeOtherQualityIndicator[] otherQualityIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о температурном графике. Если показатели качества указываются в разрезе
|
||||
/// договора, то ссылка на ОЖФ в данном элементе не заполняется и элемент может заполняться только если
|
||||
/// в предмете договора хотя бы раз встречается ресурс "Тепловая энергия". Если показатели качества
|
||||
/// указываются в разрезе ОЖФ, то ссылка на ОЖФ обязательна и элемент заполняется только если
|
||||
/// в рамках ОЖФ встречается ресурс "Тепловая энергия".
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeTemperatureChart[] temperatureChart;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок представления (выставления) платежных документов, не позднее. Является
|
||||
/// обязательным, если вторая сторона договора отличается от "Управляющая организация" ИЛИ если
|
||||
/// заполнено поле <see cref="meteringDeviceInformation"/>. Не заполняется, если
|
||||
/// <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeBillingDate billingDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок внесения платы, не позднее. Является обязательным, если вторая сторона договора
|
||||
/// отличается от "Управляющая организация" И договор не является публичным и/или присутствует
|
||||
/// заключенный на бумажном носителе или в электронной форме И в поле
|
||||
/// <see cref="oneTimePayment"/> = false. Не заполняется, если <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypePaymentDate paymentDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок предоставления информации о поступивших платежах, не позднее. Является
|
||||
/// обязательным, если второй стороной договора является "Управляющая организация",
|
||||
/// "Размещение информации о начислениях за коммунальные услуги осуществляет" = "РСО" И
|
||||
/// договор не является публичным и/или присутствует заключенный на бумажном носителе или в
|
||||
/// электронной форме.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeProvidingInformationDate providingInformationDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Указывает на то, что размещение информации об индивидуальных приборах учета и их
|
||||
/// показаниях осуществляет ресурсоснабжающая организация или нет. Обязательно для заполнения,
|
||||
/// если в <see cref="countingResource"/> указано "РСО". В остальных случаях не заполняется.
|
||||
/// </summary>
|
||||
public bool? meteringDeviceInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Указывает на то, что объем поставки ресурса(ов) определяется на основании прибора
|
||||
/// учета или нет. Поле не заполняется, если вторая сторона договора "Управляющая организация"
|
||||
/// ИЛИ поле <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public bool? volumeDepends;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Указывает на то, что оплата предоставленных услуг осуществляется ли единоразово
|
||||
/// при отгрузке указанных ресурсов без заведения лицевых счетов для потребителей. Доступно
|
||||
/// для заполнения, только если вторая сторона договора отлична от "Управляющая организация".
|
||||
/// </summary>
|
||||
public bool? oneTimePayment;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Порядок размещения информации о начислениях за коммунальные услуги ведется: D - в
|
||||
/// разрезе договора, O - в разрезе объектов жилищного фонда. Заполняется, если второй стороной договора
|
||||
/// является исполнитель коммунальных услуг.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeAccrualProcedure? accrualProcedure;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о применяемом тарифе.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeTariff[] tariff;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о нормативе потребления коммунальной услуги.
|
||||
/// </summary>
|
||||
public SupplyResourceContractTypeNorm[] norm;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.HouseManagement
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/SupplyResourceContractType.html
|
||||
public class ImportSupplyResourceContractProjectPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Если договор не является публичным и/или присутствует заключенный на бумажном носителе
|
||||
/// (электронной форме) и/или не заключен в отношении нежилых помещений в многоквартирных домах,
|
||||
/// то равно true, иначе - false
|
||||
/// </summary>
|
||||
public bool isContract;
|
||||
|
||||
/// <summary>
|
||||
/// Номер договора
|
||||
/// </summary>
|
||||
public string contractNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Дата заключения
|
||||
/// </summary>
|
||||
public DateTime signingDate;
|
||||
|
||||
/// <summary>
|
||||
/// Дата вступления в силу
|
||||
/// </summary>
|
||||
public DateTime effectiveDate;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Договор заключен на неопределенный срок или нет.
|
||||
/// </summary>
|
||||
public bool indefiniteTerm;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Автоматически пролонгировать договор на один год при наступлении
|
||||
/// даты окончания действия или нет.
|
||||
/// </summary>
|
||||
public bool automaticRollOverOneYear;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Дата окончания действия. Обязательно для заполнения, если
|
||||
/// <see cref="automaticRollOverOneYear"/> = true.
|
||||
/// </summary>
|
||||
public DateTime? comptetionDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Период передачи текущих показаний по индивидуальным приборам учета. Обязателен для
|
||||
/// заполнения, если поле <see cref="volumeDepends"/> = true ИЛИ если поле
|
||||
/// <see cref="meteringDeviceInformation"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypePeriod period;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Показывает, разрешена ли гражданам передача текущих показаний по
|
||||
/// индивидуальным приборам учета в любой день месяца. Заполнение возможно только если: в настройках
|
||||
/// организации установлена настройка "Разрешить передачу гражданам показаний индивидуальных или общих
|
||||
/// (квартирных) приборов учета в любой день месяца" ИЛИ в настройках организации установлена настройка
|
||||
/// "Разрешить передачу гражданам показаний индивидуальных или общих (квартирных) приборов учета только
|
||||
/// в сроки, установленные в договоре, или в любой день месяца, если в договоре установлен признак
|
||||
/// "Разрешить передачу показаний приборов учета в любой день месяца" И заполнен <see cref="period"/>.
|
||||
/// </summary>
|
||||
public bool indicationsAnyDay;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Ссылка на НСИ "Основание заключения договора" (реестровый номер 58). Значения
|
||||
/// брать из <see cref="Registry.Registry58"/>.
|
||||
/// </summary>
|
||||
public nsiRef[] contractBase;
|
||||
|
||||
/// <summary>
|
||||
/// Вторая сторона договора. Подходящие типы:
|
||||
/// <see cref="SupplyResourceContractProjectTypeApartmentBuildingOwner"/>,
|
||||
/// <see cref="SupplyResourceContractProjectTypeApartmentBuildingRepresentativeOwner"/>,
|
||||
/// <see cref="SupplyResourceContractProjectTypeApartmentBuildingSoleOwner"/>,
|
||||
/// <see cref="SupplyResourceContractProjectTypeLivingHouseOwner"/>,
|
||||
/// <see cref="SupplyResourceContractProjectTypeOrganization"/> либо true, если это договор оферты.
|
||||
/// </summary>
|
||||
public object counterparty;
|
||||
|
||||
/// <summary>
|
||||
/// Если в договоре в наличии плановый объем и режим подачи поставки ресурсов то true, иначе - false
|
||||
/// </summary>
|
||||
public bool isPlannedVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Тип ведения планового объема и режима подачи: D - в разрезе договора,
|
||||
/// O - в разрезе объектов жилищного фонда. Заполняется при наличии в договоре планового объема и
|
||||
/// режима поставки ресурсов.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypePlannedVolumeType? plannedVolumeType;
|
||||
|
||||
/// <summary>
|
||||
/// Предмет договора. Максимум 100 записей.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeContractSubject[] contractSubject;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Размещение информации о начислениях за коммунальные услуги осуществляет: R(SO)- РСО,
|
||||
/// P(roprietor) - исполнитель коммунальных услуг. Заполняется, если порядок размещения информации
|
||||
/// о начислениях за коммунальные услуги ведется в разрезе договора.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeCountingResource? countingResource;
|
||||
|
||||
/// <summary>
|
||||
/// Показатели качества коммунальных ресурсов и температурный график ведутся: D - в разрезе договора,
|
||||
/// O - в разрезе объектов жилищного фонда
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeSpecifyingQualityIndicators specifyingQualityIndicators;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Признак "Отсутствие присоединения сетей объектов жилищного фонда к централизованной
|
||||
/// системе водоснабжения". Может быть указан, только если показатели качества коммунальных ресурсов
|
||||
/// ведутся в разрезе договора и предмет договора включает коммунальную услугу "Холодное водоснабжение"
|
||||
/// И/ИЛИ "Горячее водоснабжение".
|
||||
/// </summary>
|
||||
public bool noConnectionToWaterSupply;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Показатель качества (содержащийся в справочнике показателей качества). Обязательно
|
||||
/// для заполнения, если показатели качества указываются в разрезе договора. Для пары КУ и КР
|
||||
/// "Горячее водоснабжение" и "Питьевая вода" указываются актуальные показатели, определенные для КР
|
||||
/// "Горячая вода" в справочнике показателей качества коммунальных ресурсов. Для пары КУ и КР
|
||||
/// "Горячее водоснабжение" и "Тепловая энергия" информация о показателях качества не заполняется
|
||||
/// только в том случае, если для договора (если показатели ведутся в разрезе договора) или
|
||||
/// ОЖФ в договоре (если показатели ведутся в разрезе ОЖФ) также указана пара КУ и КР
|
||||
/// "Горячее водоснабжение" и "Питьевая вода".
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeQuality[] quality;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Иной показатель качества коммунального ресурса (не содержащийся в справочнике
|
||||
/// показателей качества).
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeOtherQualityIndicator[] otherQualityIndicator;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о температурном графике. Доступно для заполнения только если в
|
||||
/// предмете договора хотя бы раз встречается ресурс "Тепловая энергия".
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeTemperatureChart[] temperatureChart;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок представления (выставления) платежных документов, не позднее. Является
|
||||
/// обязательным, если вторая сторона договора отличается от "Управляющая организация" ИЛИ если
|
||||
/// заполнено поле <see cref="meteringDeviceInformation"/>. Не заполняется, если
|
||||
/// <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeBillingDate billingDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок внесения платы, не позднее. Является обязательным, если вторая сторона договора
|
||||
/// отличается от "Управляющая организация" И договор не является публичным и/или присутствует
|
||||
/// заключенный на бумажном носителе или в электронной форме И в поле
|
||||
/// <see cref="oneTimePayment"/> = false. Не заполняется, если <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypePaymentDate paymentDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Срок предоставления информации о поступивших платежах, не позднее. Является
|
||||
/// обязательным, если второй стороной договора является "Управляющая организация",
|
||||
/// "Размещение информации о начислениях за коммунальные услуги осуществляет" = "РСО" И
|
||||
/// договор не является публичным и/или присутствует заключенный на бумажном носителе или в
|
||||
/// электронной форме.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeProvidingInformationDate providingInformationDate;
|
||||
|
||||
/// <summary>
|
||||
/// Условное. Указывает на то, что размещение информации об индивидуальных приборах учета и их
|
||||
/// показаниях осуществляет ресурсоснабжающая организация или нет. Обязательно для заполнения,
|
||||
/// если в <see cref="countingResource"/> указано "РСО". В остальных случаях не заполняется.
|
||||
/// </summary>
|
||||
public bool? meteringDeviceInformation;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Указывает на то, что объем поставки ресурса(ов) определяется на основании прибора
|
||||
/// учета или нет. Поле не заполняется, если вторая сторона договора "Управляющая организация"
|
||||
/// ИЛИ поле <see cref="oneTimePayment"/> = true.
|
||||
/// </summary>
|
||||
public bool? volumeDepends;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Указывает на то, что оплата предоставленных услуг осуществляется ли единоразово
|
||||
/// при отгрузке указанных ресурсов без заведения лицевых счетов для потребителей. Доступно
|
||||
/// для заполнения, только если вторая сторона договора отлична от "Управляющая организация".
|
||||
/// </summary>
|
||||
public bool? oneTimePayment;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Порядок размещения информации о начислениях за коммунальные услуги ведется: D - в
|
||||
/// разрезе договора, O - в разрезе объектов жилищного фонда. Заполняется, если второй стороной договора
|
||||
/// является исполнитель коммунальных услуг.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeAccrualProcedure? accrualProcedure;
|
||||
|
||||
/// <summary>
|
||||
/// Кода ФИАС региона РФ, в котором применяются тарифы и/или нормативы потребления КУ (у поставщика
|
||||
/// данных должна быть подтвержденная функция РСО в этом Субъекте РФ).
|
||||
/// </summary>
|
||||
public string regionCodeFias;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о применяемом тарифе. Заполнется при указании <see cref="regionCodeFias"/>.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeRegionalSettingsTariff[] tariff;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Информация о нормативе потребления коммунальной услуги. Заполнется при
|
||||
/// указании <see cref="regionCodeFias"/>.
|
||||
/// </summary>
|
||||
public SupplyResourceContractProjectTypeRegionalSettingsNorm[] norm;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.Payments
|
||||
{
|
||||
// http://open-gkh.ru/Payment/importNotificationsOfOrderExecutionRequest/NotificationOfOrderExecution139Type.html
|
||||
public class ImportNotificationsOfOrderExecutionPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Уникальный номер платежа (идентификатор операции)
|
||||
/// </summary>
|
||||
public string orderId;
|
||||
|
||||
/// <summary>
|
||||
/// Дата
|
||||
/// </summary>
|
||||
public DateTime orderDate;
|
||||
|
||||
/// <summary>
|
||||
/// Сумма оплаты (в копейках)
|
||||
/// </summary>
|
||||
public decimal amount;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Признак онлайн-оплаты.
|
||||
/// </summary>
|
||||
public bool? onlinePayment;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Год. Указывать совместно с <see cref="month"/>.
|
||||
/// </summary>
|
||||
public short? year;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Месяц. Указывать совместно с <see cref="year"/>.
|
||||
/// </summary>
|
||||
public int? month;
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор платежного документа
|
||||
/// </summary>
|
||||
public string paymentDocumentId;
|
||||
|
||||
/// <summary>
|
||||
/// GUID платежного документа
|
||||
/// </summary>
|
||||
public string paymentDocumentGUID;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Payload.Payments
|
||||
{
|
||||
// http://open-gkh.ru/Payment/importSupplierNotificationsOfOrderExecutionRequest/SupplierNotificationOfOrderExecution.html
|
||||
public class ImportSupplierNotificationsOfOrderExecutionPayload
|
||||
{
|
||||
/// <summary>
|
||||
/// Дата внесения платы (в случае отсутствия: дата поступления средств)
|
||||
/// </summary>
|
||||
public DateTime orderDate;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Месяц, за который вносится плата. Указывать совместно с <see cref="year"/>.
|
||||
/// </summary>
|
||||
public int? month;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Год, за который вносится плата. Указывать совместно с <see cref="month"/>.
|
||||
/// </summary>
|
||||
public short? year;
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор платежного документа
|
||||
/// </summary>
|
||||
public string paymentDocumentId;
|
||||
|
||||
/// <summary>
|
||||
/// Сумма
|
||||
/// </summary>
|
||||
public decimal amount;
|
||||
|
||||
/// <summary>
|
||||
/// Необязательное. Признак онлайн-оплаты.
|
||||
/// </summary>
|
||||
public bool? onlinePayment;
|
||||
}
|
||||
}
|
||||
35
Hcs.ClientNet/Client/Api/PaymentsApi.cs
Normal file
35
Hcs.ClientNet/Client/Api/PaymentsApi.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Hcs.ClientNet.Api.Payload.Payments;
|
||||
using Hcs.ClientNet.Api.Request.Payments;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api
|
||||
{
|
||||
// http://open-gkh.ru/PaymentsServiceAsync/
|
||||
public class PaymentsApi(ClientBase client) : ApiBase(client)
|
||||
{
|
||||
/// <summary>
|
||||
/// ВИ_ОПЛАТА_ИЗВ. Передать перечень документов "Извещение о принятии к исполнению распоряжения".
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад документа</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportNotificationsOfOrderExecutionAsync(ImportNotificationsOfOrderExecutionPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportNotificationsOfOrderExecutionRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Импорт пакета документов "Извещение о принятии к исполнению распоряжения", размещаемых исполнителем
|
||||
/// </summary>
|
||||
/// <param name="payload">Пейлоад документа</param>
|
||||
/// <param name="token">Токен отмены</param>
|
||||
/// <returns>true, если операция выполнена успешно, иначе - false</returns>
|
||||
public async Task<bool> ImportSupplierNotificationsOfOrderExecutionAsync(ImportSupplierNotificationsOfOrderExecutionPayload payload, CancellationToken token = default)
|
||||
{
|
||||
var request = new ImportSupplierNotificationsOfOrderExecutionRequest(client);
|
||||
return await request.ExecuteAsync(payload, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry16.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry16.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Межповерочный интервал" (реестровый номер 16).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-16/v1.
|
||||
/// </summary>
|
||||
public static class Registry16
|
||||
{
|
||||
/// <summary>
|
||||
/// 4 года
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"e5e3288d-2994-41f7-8e44-f329b09ab77f");
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry2.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry2.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Коммунальный ресурс" (реестровый номер 2).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-2/v1.
|
||||
/// </summary>
|
||||
public static class Registry2
|
||||
{
|
||||
/// <summary>
|
||||
/// Тепловая энергия
|
||||
/// </summary>
|
||||
public static RegistryElement Element5 => new(
|
||||
"5",
|
||||
"44af905f-09c3-4cc3-b749-70048a53d8cf");
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry239.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry239.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Тарифицируемый ресурс" (реестровый номер 239).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-239/v1.
|
||||
/// </summary>
|
||||
public static class Registry239
|
||||
{
|
||||
/// <summary>
|
||||
/// Тепловая энергия
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"eec6e4b8-76c8-4fce-99b7-c95718edad19");
|
||||
}
|
||||
}
|
||||
37
Hcs.ClientNet/Client/Api/Registry/Registry24.cs
Normal file
37
Hcs.ClientNet/Client/Api/Registry/Registry24.cs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Состояние дома" (реестровый номер 24).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-24/v1.
|
||||
/// </summary>
|
||||
public static class Registry24
|
||||
{
|
||||
/// <summary>
|
||||
/// Аварийный
|
||||
/// </summary>
|
||||
public static RegistryElement Element1 => new(
|
||||
"1",
|
||||
"cbe05853-a91b-43cc-a2cb-06cdfa97d492");
|
||||
|
||||
/// <summary>
|
||||
/// Исправный
|
||||
/// </summary>
|
||||
public static RegistryElement Element2 => new(
|
||||
"2",
|
||||
"2d3ae73e-6c72-4740-9122-9c632d1893a7");
|
||||
|
||||
/// <summary>
|
||||
/// Ветхий
|
||||
/// </summary>
|
||||
public static RegistryElement Element3 => new(
|
||||
"3",
|
||||
"bf083ae4-e4ec-4ace-b190-4d009e5cd1a1");
|
||||
|
||||
/// <summary>
|
||||
/// Не выбран
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"4ee07c0b-82d6-41f4-a8c5-2cff784bbd9c");
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry27.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry27.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Тип прибора учета" (реестровый номер 27).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-27/v1.
|
||||
/// </summary>
|
||||
public static class Registry27
|
||||
{
|
||||
/// <summary>
|
||||
/// Индивидуальный
|
||||
/// </summary>
|
||||
public static RegistryElement Element1 => new(
|
||||
"1",
|
||||
"3a9687b5-caed-4ec6-8a08-f4d3d012f2c7");
|
||||
}
|
||||
}
|
||||
23
Hcs.ClientNet/Client/Api/Registry/Registry276.cs
Normal file
23
Hcs.ClientNet/Client/Api/Registry/Registry276.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Показатели качества коммунальных ресурсов" (реестровый номер 276).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-276/v1.
|
||||
/// </summary>
|
||||
public static class Registry276
|
||||
{
|
||||
/// <summary>
|
||||
/// Величина тепловой нагрузки
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"51dd6edc-83fe-4810-8b62-4dc85a75e9a3");
|
||||
|
||||
/// <summary>
|
||||
/// Диапазон давления теплоносителя в подающем трубопроводе
|
||||
/// </summary>
|
||||
public static RegistryElement Element10 => new(
|
||||
"10",
|
||||
"a5a17c90-cc4b-4f32-a22b-6e06cd42a68c");
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry3.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry3.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Вид коммунальной услуги" (реестровый номер 3).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-3/v1.
|
||||
/// </summary>
|
||||
public static class Registry3
|
||||
{
|
||||
/// <summary>
|
||||
/// Отопление
|
||||
/// </summary>
|
||||
public static RegistryElement Element6 => new(
|
||||
"6",
|
||||
"74925764-ddf3-4b4b-b18d-85994187c13a");
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Registry/Registry32.cs
Normal file
16
Hcs.ClientNet/Client/Api/Registry/Registry32.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Часовые зоны по Olson" (реестровый номер 32).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-32/v1.
|
||||
/// </summary>
|
||||
public static class Registry32
|
||||
{
|
||||
/// <summary>
|
||||
/// Иркутск
|
||||
/// </summary>
|
||||
public static RegistryElement Element11 => new(
|
||||
"11",
|
||||
"244ae392-0b96-46f2-80ea-4dac32e7326a");
|
||||
}
|
||||
}
|
||||
58
Hcs.ClientNet/Client/Api/Registry/Registry338.cs
Normal file
58
Hcs.ClientNet/Client/Api/Registry/Registry338.cs
Normal file
@ -0,0 +1,58 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Стадия жизненного цикла" (реестровый номер 338).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-338/v1.
|
||||
/// </summary>
|
||||
public static class Registry338
|
||||
{
|
||||
/// <summary>
|
||||
/// Эксплуатация
|
||||
/// </summary>
|
||||
public static RegistryElement Element1 => new(
|
||||
"1",
|
||||
"29b18683-5195-4ef4-83fc-71bf45597d46");
|
||||
|
||||
/// <summary>
|
||||
/// Реконструкция
|
||||
/// </summary>
|
||||
public static RegistryElement Element2 => new(
|
||||
"2",
|
||||
"75764145-f181-47e5-bff1-1306a46eb20e");
|
||||
|
||||
/// <summary>
|
||||
/// Капитальный ремонт с отселением
|
||||
/// </summary>
|
||||
public static RegistryElement Element3 => new(
|
||||
"3",
|
||||
"dee170df-db42-4cd6-9e5e-b62be91b3663");
|
||||
|
||||
/// <summary>
|
||||
/// Капитальный ремонт без отселения
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"91dc91e2-6883-4c84-b711-53f57f28dbe2");
|
||||
|
||||
/// <summary>
|
||||
/// Снос
|
||||
/// </summary>
|
||||
public static RegistryElement Element5 => new(
|
||||
"5",
|
||||
"cc358aa9-10b3-4d6a-bbec-c5f6b14950f6");
|
||||
|
||||
/// <summary>
|
||||
/// Не эксплуатируется, расселен
|
||||
/// </summary>
|
||||
public static RegistryElement Element6 => new(
|
||||
"6",
|
||||
"4bed3d7e-6015-428e-b4b4-7b7aec171c0d");
|
||||
|
||||
/// <summary>
|
||||
/// Выведен из эксплуатации
|
||||
/// </summary>
|
||||
public static RegistryElement Element7 => new(
|
||||
"7",
|
||||
"f3edc065-c1a1-4110-96fa-03313ae7a039");
|
||||
}
|
||||
}
|
||||
15
Hcs.ClientNet/Client/Api/Registry/Registry51.cs
Normal file
15
Hcs.ClientNet/Client/Api/Registry/Registry51.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Вид коммунальной услуги" (реестровый номер 51)
|
||||
/// </summary>
|
||||
public static class Registry51
|
||||
{
|
||||
/// <summary>
|
||||
/// Отопление
|
||||
/// </summary>
|
||||
public static RegistryElement Element6_1 => new(
|
||||
"6.1",
|
||||
"14ad13a3-45ce-408b-b641-6fc59554f803");
|
||||
}
|
||||
}
|
||||
79
Hcs.ClientNet/Client/Api/Registry/Registry58.cs
Normal file
79
Hcs.ClientNet/Client/Api/Registry/Registry58.cs
Normal file
@ -0,0 +1,79 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
/// <summary>
|
||||
/// НСИ "Основание заключения договора" (реестровый номер 58).
|
||||
/// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-58/v1.
|
||||
/// </summary>
|
||||
public static class Registry58
|
||||
{
|
||||
/// <summary>
|
||||
/// Решение собрания собственников
|
||||
/// </summary>
|
||||
public static RegistryElement Element1 => new(
|
||||
"1",
|
||||
"110d48b2-32a9-4a44-939c-b784d9794621");
|
||||
|
||||
/// <summary>
|
||||
/// Открытый конкурс
|
||||
/// </summary>
|
||||
public static RegistryElement Element2 => new(
|
||||
"2",
|
||||
"a9dc59c3-d53f-42eb-ba98-cf8c74d88d36");
|
||||
|
||||
/// <summary>
|
||||
/// Договор управления
|
||||
/// </summary>
|
||||
public static RegistryElement Element3 => new(
|
||||
"3",
|
||||
"11efe618-79f8-4f53-bfd6-11620e8e9e1e");
|
||||
|
||||
/// <summary>
|
||||
/// Устав
|
||||
/// </summary>
|
||||
public static RegistryElement Element4 => new(
|
||||
"4",
|
||||
"a2eb920c-8163-4958-812a-ad153a5dfde6");
|
||||
|
||||
/// <summary>
|
||||
/// Решение правления
|
||||
/// </summary>
|
||||
public static RegistryElement Element5 => new(
|
||||
"5",
|
||||
"58639715-2708-4b8e-a5e6-7cae4ddbf03b");
|
||||
|
||||
/// <summary>
|
||||
/// Решение органа управления застройщика
|
||||
/// </summary>
|
||||
public static RegistryElement Element6 => new(
|
||||
"6",
|
||||
"9b606ef5-7701-4a12-a837-d81b50939160");
|
||||
|
||||
/// <summary>
|
||||
/// Заявление потребителя
|
||||
/// </summary>
|
||||
public static RegistryElement Element7 => new(
|
||||
"7",
|
||||
"93cd9d85-91b8-4bf9-ae48-c5f1e691949f");
|
||||
|
||||
/// <summary>
|
||||
/// Нормативный правовой акт
|
||||
/// </summary>
|
||||
public static RegistryElement Element8 => new(
|
||||
"8",
|
||||
"8b8ee37b-fa79-40cc-b98d-0e51f0c38d03");
|
||||
|
||||
/// <summary>
|
||||
/// Разрешение на ввод в эксплуатацию
|
||||
/// </summary>
|
||||
public static RegistryElement Element9 => new(
|
||||
"9",
|
||||
"16331000-d96e-4a33-a6c7-3cb9eacf4927");
|
||||
|
||||
/// <summary>
|
||||
/// Устав
|
||||
/// </summary>
|
||||
public static RegistryElement Element10 => new(
|
||||
"10",
|
||||
"555638ae-a207-46fa-99bd-88bdb297c45a");
|
||||
}
|
||||
}
|
||||
9
Hcs.ClientNet/Client/Api/Registry/RegistryElement.cs
Normal file
9
Hcs.ClientNet/Client/Api/Registry/RegistryElement.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Hcs.ClientNet.Api.Registry
|
||||
{
|
||||
public class RegistryElement(string code, string guid)
|
||||
{
|
||||
public string Code { get; } = code;
|
||||
|
||||
public string GUID { get; } = guid;
|
||||
}
|
||||
}
|
||||
9
Hcs.ClientNet/Client/Api/Request/Adapter/IAck.cs
Normal file
9
Hcs.ClientNet/Client/Api/Request/Adapter/IAck.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
public interface IAck
|
||||
{
|
||||
string MessageGUID { get; set; }
|
||||
|
||||
string RequesterMessageGUID { get; set; }
|
||||
}
|
||||
}
|
||||
9
Hcs.ClientNet/Client/Api/Request/Adapter/IAsyncClient.cs
Normal file
9
Hcs.ClientNet/Client/Api/Request/Adapter/IAsyncClient.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Adapter
|
||||
{
|
||||
public interface IAsyncClient<TRequestHeader> where TRequestHeader : class
|
||||
{
|
||||
Task<IGetStateResponse> GetStateAsync(TRequestHeader header, IGetStateRequest request);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
public interface IErrorMessage
|
||||
{
|
||||
string ErrorCode { get; }
|
||||
|
||||
string Description { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
public interface IGetStateRequest
|
||||
{
|
||||
string MessageGUID { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Adapter
|
||||
{
|
||||
public interface IGetStateResponse
|
||||
{
|
||||
IGetStateResult GetStateResult { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Adapter
|
||||
{
|
||||
public interface IGetStateResult
|
||||
{
|
||||
sbyte RequestState { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Adapter
|
||||
{
|
||||
public interface IGetStateResultMany : IGetStateResult
|
||||
{
|
||||
object[] Items { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Adapter
|
||||
{
|
||||
public interface IGetStateResultOne : IGetStateResult
|
||||
{
|
||||
object Item { get; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal enum AsyncRequestStateType
|
||||
{
|
||||
Received = 1,
|
||||
InProgress,
|
||||
Ready
|
||||
}
|
||||
}
|
||||
55
Hcs.ClientNet/Client/Api/Request/Bills/BillsRequestBase.cs
Normal file
55
Hcs.ClientNet/Client/Api/Request/Bills/BillsRequestBase.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.Bills;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.Bills
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class BillsPortsTypeAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> 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.ClientNet.Api.Request.Bills
|
||||
{
|
||||
internal class BillsRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
BillsPortsTypeAsyncClient,
|
||||
BillsPortsTypeAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.BillsAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.Bills;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Bills
|
||||
{
|
||||
internal class ExportPaymentDocumentDataRequest(ClientBase client) : BillsRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
internal async Task<IEnumerable<exportPaymentDocumentResultType>> ExecuteByPaymentDocumentIDAsync(string paymentDocumentID, CancellationToken token)
|
||||
{
|
||||
var request = new exportPaymentDocumentRequest()
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.0.1",
|
||||
Items = [paymentDocumentID],
|
||||
ItemsElementName = [ItemsChoiceType7.PaymentDocumentID]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportPaymentDocumentDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportPaymentDocumentResultType>();
|
||||
}
|
||||
|
||||
internal async Task<IEnumerable<exportPaymentDocumentResultType>> ExecuteByAccountNumberAsync(short year, int month, string fiasHouseGuid, string accountNumber, CancellationToken token)
|
||||
{
|
||||
var request = new exportPaymentDocumentRequest()
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.0.1",
|
||||
Items = [year, month, fiasHouseGuid, accountNumber],
|
||||
ItemsElementName = [ItemsChoiceType7.Year, ItemsChoiceType7.Month, ItemsChoiceType7.FIASHouseGuid, ItemsChoiceType7.AccountNumber]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportPaymentDocumentDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportPaymentDocumentResultType>();
|
||||
}
|
||||
|
||||
internal async Task<IEnumerable<exportPaymentDocumentResultType>> ExecuteByPaymentDocumentNumberAsync(short year, int month, string fiasHouseGuid, string paymentDocumentNumber, CancellationToken token)
|
||||
{
|
||||
var request = new exportPaymentDocumentRequest()
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.0.1",
|
||||
Items = [year, month, fiasHouseGuid, paymentDocumentNumber],
|
||||
ItemsElementName = [ItemsChoiceType7.Year, ItemsChoiceType7.Month, ItemsChoiceType7.FIASHouseGuid, ItemsChoiceType7.PaymentDocumentNumber]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportPaymentDocumentDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportPaymentDocumentResultType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,276 @@
|
||||
using Hcs.ClientNet.Api.Payload.Bills;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Api.Type;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.Bills;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Bills
|
||||
{
|
||||
internal class ImportPaymentDocumentDataRequest(ClientBase client) : BillsRequestBase(client)
|
||||
{
|
||||
internal async Task<bool> ExecuteAsync(ImportPaymentDocumentDataPayload payload, CancellationToken token)
|
||||
{
|
||||
// TODO: Добавить проверку пейлоада
|
||||
|
||||
var request = GetRequestFromPayload(payload);
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importPaymentDocumentDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
result.Items.OfType<CommonResultType>().ToList().ForEach(commonResult =>
|
||||
{
|
||||
commonResult.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private importPaymentDocumentRequest GetRequestFromPayload(ImportPaymentDocumentDataPayload payload)
|
||||
{
|
||||
var items = new List<object>();
|
||||
if (payload.confirmAmountsCorrect)
|
||||
{
|
||||
items.Add(true);
|
||||
}
|
||||
items.Add(payload.month);
|
||||
items.Add(payload.year);
|
||||
|
||||
var paymentInformations = new Dictionary<ImportPaymentDocumentDataPayload.PaymentInformation, importPaymentDocumentRequestPaymentInformation>();
|
||||
foreach (var entry in payload.paymentInformation)
|
||||
{
|
||||
var paymentInformation = new importPaymentDocumentRequestPaymentInformation()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
BankBIK = entry.bankBIK,
|
||||
operatingAccountNumber = entry.operatingAccountNumber
|
||||
};
|
||||
paymentInformations.Add(entry, paymentInformation);
|
||||
|
||||
items.Add(paymentInformation);
|
||||
}
|
||||
|
||||
foreach (var entry in payload.paymentDocument)
|
||||
{
|
||||
var chargeInfo = new List<object>();
|
||||
foreach (var subEntry in entry.chargeInfo)
|
||||
{
|
||||
if (subEntry is ImportPaymentDocumentDataPayload.MunicipalService municipalService)
|
||||
{
|
||||
var item = new PDServiceChargeTypeMunicipalService()
|
||||
{
|
||||
ServiceType = new nsiRef()
|
||||
{
|
||||
Code = municipalService.serviceType.Code,
|
||||
GUID = municipalService.serviceType.GUID
|
||||
},
|
||||
Rate = municipalService.rate,
|
||||
TotalPayable = municipalService.totalPayable
|
||||
};
|
||||
|
||||
if (municipalService.moneyRecalculation.HasValue)
|
||||
{
|
||||
item.ServiceCharge = new ServiceChargeImportType()
|
||||
{
|
||||
MoneyRecalculation = municipalService.moneyRecalculation.Value,
|
||||
MoneyRecalculationSpecified = true
|
||||
};
|
||||
}
|
||||
if (municipalService.moneyDiscount.HasValue)
|
||||
{
|
||||
item.ServiceCharge ??= new ServiceChargeImportType();
|
||||
item.ServiceCharge.MoneyDiscount = municipalService.moneyDiscount.Value;
|
||||
item.ServiceCharge.MoneyDiscountSpecified = true;
|
||||
}
|
||||
|
||||
if (municipalService.houseOverallNeedsNorm.HasValue)
|
||||
{
|
||||
item.ServiceInformation = new ServiceInformation()
|
||||
{
|
||||
houseOverallNeedsNorm = municipalService.houseOverallNeedsNorm.Value,
|
||||
houseOverallNeedsNormSpecified = true
|
||||
};
|
||||
}
|
||||
if (municipalService.individualConsumptionNorm.HasValue)
|
||||
{
|
||||
item.ServiceInformation ??= new ServiceInformation();
|
||||
item.ServiceInformation.individualConsumptionNorm = municipalService.individualConsumptionNorm.Value;
|
||||
item.ServiceInformation.individualConsumptionNormSpecified = true;
|
||||
}
|
||||
if (municipalService.individualConsumptionCurrentValue.HasValue)
|
||||
{
|
||||
item.ServiceInformation ??= new ServiceInformation();
|
||||
item.ServiceInformation.individualConsumptionCurrentValue = municipalService.individualConsumptionCurrentValue.Value;
|
||||
item.ServiceInformation.individualConsumptionCurrentValueSpecified = true;
|
||||
}
|
||||
if (municipalService.houseOverallNeedsCurrentValue.HasValue)
|
||||
{
|
||||
item.ServiceInformation ??= new ServiceInformation();
|
||||
item.ServiceInformation.houseOverallNeedsCurrentValue = municipalService.houseOverallNeedsCurrentValue.Value;
|
||||
item.ServiceInformation.houseOverallNeedsCurrentValueSpecified = true;
|
||||
}
|
||||
if (municipalService.houseTotalIndividualConsumption.HasValue)
|
||||
{
|
||||
item.ServiceInformation ??= new ServiceInformation();
|
||||
item.ServiceInformation.houseTotalIndividualConsumption = municipalService.houseTotalIndividualConsumption.Value;
|
||||
item.ServiceInformation.houseTotalIndividualConsumptionSpecified = true;
|
||||
}
|
||||
if (municipalService.houseTotalHouseOverallNeeds.HasValue)
|
||||
{
|
||||
item.ServiceInformation ??= new ServiceInformation();
|
||||
item.ServiceInformation.houseTotalHouseOverallNeeds = municipalService.houseTotalHouseOverallNeeds.Value;
|
||||
item.ServiceInformation.houseTotalHouseOverallNeedsSpecified = true;
|
||||
}
|
||||
|
||||
var consumption = new List<PDServiceChargeTypeMunicipalServiceVolume>();
|
||||
if (municipalService.individualConsumptionVolumeDeterminingMethod.HasValue)
|
||||
{
|
||||
consumption.Add(new PDServiceChargeTypeMunicipalServiceVolume()
|
||||
{
|
||||
determiningMethod = municipalService.individualConsumptionVolumeDeterminingMethod.Value.ToServiceType(),
|
||||
determiningMethodSpecified = true,
|
||||
type = PDServiceChargeTypeMunicipalServiceVolumeType.I,
|
||||
typeSpecified = true,
|
||||
Value = municipalService.individualConsumptionVolumeValue.Value
|
||||
});
|
||||
}
|
||||
if (municipalService.overallConsumptionVolumeDeterminingMethod.HasValue)
|
||||
{
|
||||
consumption.Add(new PDServiceChargeTypeMunicipalServiceVolume()
|
||||
{
|
||||
determiningMethod = municipalService.overallConsumptionVolumeDeterminingMethod.Value.ToServiceType(),
|
||||
determiningMethodSpecified = true,
|
||||
type = PDServiceChargeTypeMunicipalServiceVolumeType.O,
|
||||
typeSpecified = true,
|
||||
Value = municipalService.overallConsumptionVolumeValue.Value
|
||||
});
|
||||
}
|
||||
item.Consumption = [.. consumption];
|
||||
|
||||
if (municipalService.multiplyingFactorRatio.HasValue)
|
||||
{
|
||||
item.MultiplyingFactor = new PDServiceChargeTypeMunicipalServiceMultiplyingFactor()
|
||||
{
|
||||
Ratio = municipalService.multiplyingFactorRatio.Value
|
||||
};
|
||||
|
||||
if (municipalService.amountOfExcessFees.HasValue)
|
||||
{
|
||||
item.MultiplyingFactor.AmountOfExcessFees = municipalService.amountOfExcessFees.Value;
|
||||
item.MultiplyingFactor.AmountOfExcessFeesSpecified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (municipalService.municipalServiceIndividualConsumptionPayable.HasValue)
|
||||
{
|
||||
item.MunicipalServiceIndividualConsumptionPayable = municipalService.municipalServiceIndividualConsumptionPayable.Value;
|
||||
item.MunicipalServiceIndividualConsumptionPayableSpecified = true;
|
||||
}
|
||||
|
||||
if (municipalService.municipalServiceCommunalConsumptionPayable.HasValue)
|
||||
{
|
||||
item.MunicipalServiceCommunalConsumptionPayable = municipalService.municipalServiceCommunalConsumptionPayable.Value;
|
||||
item.MunicipalServiceCommunalConsumptionPayableSpecified = true;
|
||||
}
|
||||
|
||||
if (municipalService.amountOfPaymentMunicipalServiceIndividualConsumption.HasValue)
|
||||
{
|
||||
item.AmountOfPaymentMunicipalServiceIndividualConsumption = municipalService.amountOfPaymentMunicipalServiceIndividualConsumption.Value;
|
||||
item.AmountOfPaymentMunicipalServiceIndividualConsumptionSpecified = true;
|
||||
}
|
||||
|
||||
if (municipalService.amountOfPaymentMunicipalServiceCommunalConsumption.HasValue)
|
||||
{
|
||||
item.AmountOfPaymentMunicipalServiceCommunalConsumption = municipalService.amountOfPaymentMunicipalServiceCommunalConsumption.Value;
|
||||
item.AmountOfPaymentMunicipalServiceCommunalConsumptionSpecified = true;
|
||||
}
|
||||
|
||||
if (municipalService.accountingPeriodTotal.HasValue)
|
||||
{
|
||||
item.AccountingPeriodTotal = municipalService.accountingPeriodTotal.Value;
|
||||
item.AccountingPeriodTotalSpecified = true;
|
||||
}
|
||||
|
||||
chargeInfo.Add(new PaymentDocumentTypeChargeInfo()
|
||||
{
|
||||
Item = item
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Обработать ошибку
|
||||
}
|
||||
|
||||
var paymentDocument = new importPaymentDocumentRequestPaymentDocument()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Items1 = [paymentInformations[entry.paymentInformation].TransportGUID],
|
||||
AccountGuid = entry.accountGuid,
|
||||
PaymentDocumentNumber = entry.paymentDocumentNumber,
|
||||
Items = [.. chargeInfo],
|
||||
Item = true,
|
||||
ItemElementName = entry.exposeNotWithdraw ? ItemChoiceType5.Expose : ItemChoiceType5.Withdraw
|
||||
};
|
||||
|
||||
if (entry.debtPreviousPeriods.HasValue)
|
||||
{
|
||||
paymentDocument.DebtPreviousPeriods = entry.debtPreviousPeriods.Value;
|
||||
paymentDocument.DebtPreviousPeriodsSpecified = true;
|
||||
}
|
||||
|
||||
if (entry.advanceBllingPeriod.HasValue)
|
||||
{
|
||||
paymentDocument.AdvanceBllingPeriod = entry.advanceBllingPeriod.Value;
|
||||
paymentDocument.AdvanceBllingPeriodSpecified = true;
|
||||
}
|
||||
|
||||
if (entry.totalPayableByPDWithDebtAndAdvance.HasValue)
|
||||
{
|
||||
paymentDocument.TotalPayableByPDWithDebtAndAdvance = entry.totalPayableByPDWithDebtAndAdvance.Value;
|
||||
paymentDocument.TotalPayableByPDWithDebtAndAdvanceSpecified = true;
|
||||
}
|
||||
|
||||
if (entry.totalPayableByPD.HasValue)
|
||||
{
|
||||
paymentDocument.TotalPayableByPD = entry.totalPayableByPD.Value;
|
||||
paymentDocument.TotalPayableByPDSpecified = true;
|
||||
}
|
||||
|
||||
if (entry.paidCash.HasValue)
|
||||
{
|
||||
paymentDocument.PaidCash = entry.paidCash.Value;
|
||||
paymentDocument.PaidCashSpecified = true;
|
||||
}
|
||||
|
||||
if (entry.dateOfLastReceivedPayment.HasValue)
|
||||
{
|
||||
paymentDocument.DateOfLastReceivedPayment = entry.dateOfLastReceivedPayment.Value;
|
||||
paymentDocument.DateOfLastReceivedPaymentSpecified = true;
|
||||
}
|
||||
|
||||
items.Add(paymentDocument);
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/Bills/importPaymentDocumentRequest.html
|
||||
return new importPaymentDocumentRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "11.2.0.16",
|
||||
Items = [.. items]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.DeviceMetering;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.DeviceMetering
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class DeviceMeteringPortTypesAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> 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.ClientNet.Api.Request.DeviceMetering
|
||||
{
|
||||
internal class DeviceMeteringRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
DeviceMeteringPortTypesAsyncClient,
|
||||
DeviceMeteringPortTypesAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.DeviceMeteringAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,179 @@
|
||||
using Hcs.ClientNet.Api.Payload.DeviceMetering;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.DeviceMetering;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.DeviceMetering
|
||||
{
|
||||
internal class ExportMeteringDeviceHistoryRequest(ClientBase client) : DeviceMeteringRequestBase(client)
|
||||
{
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
internal async Task<IEnumerable<exportMeteringDeviceHistoryResultType>> ExecuteAsync(ExportMeteringDeviceHistoryPayload payload, CancellationToken token)
|
||||
{
|
||||
ThrowIfPayloadIncorrect(payload);
|
||||
|
||||
var request = GetRequestFromPayload(payload);
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportMeteringDeviceHistoryAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportMeteringDeviceHistoryResultType>();
|
||||
}
|
||||
|
||||
private void ThrowIfPayloadIncorrect(ExportMeteringDeviceHistoryPayload payload)
|
||||
{
|
||||
if (payload.meteringDeviceType?.Length <= 0 && payload.municipalResource?.Length <= 0 &&
|
||||
payload.meteringDeviceRootGUID?.Length <= 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.meteringDeviceType)}/{nameof(payload.municipalResource)}/{nameof(payload.meteringDeviceRootGUID)} are empty");
|
||||
}
|
||||
|
||||
if (payload.meteringDeviceType?.Length + payload.municipalResource?.Length +
|
||||
payload.meteringDeviceRootGUID?.Length > 100)
|
||||
{
|
||||
throw new ArgumentException($"Too much {nameof(payload.meteringDeviceType)}/{nameof(payload.municipalResource)}/{nameof(payload.meteringDeviceRootGUID)} values");
|
||||
}
|
||||
|
||||
if (payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue)
|
||||
{
|
||||
if (payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue &&
|
||||
payload.inputDateFrom.Value > payload.inputDateTo.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.inputDateFrom)} must be earlier than {nameof(payload.inputDateTo)}");
|
||||
}
|
||||
|
||||
if (payload.inputDateTo.Value - payload.inputDateFrom.Value > TimeSpan.FromDays(
|
||||
DateTime.DaysInMonth(payload.inputDateTo.Value.Year, payload.inputDateTo.Value.Month) +
|
||||
DateTime.DaysInMonth(payload.inputDateFrom.Value.Year, payload.inputDateFrom.Value.Month)))
|
||||
{
|
||||
throw new ArgumentException($"Too big range from {nameof(payload.inputDateFrom)} to {nameof(payload.inputDateTo)}");
|
||||
}
|
||||
}
|
||||
else if (payload.inputDateFrom.HasValue && !payload.inputDateTo.HasValue)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.inputDateTo)} is null");
|
||||
}
|
||||
else if (!payload.inputDateFrom.HasValue && payload.inputDateTo.HasValue)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.inputDateFrom)} is null");
|
||||
}
|
||||
}
|
||||
|
||||
private exportMeteringDeviceHistoryRequest GetRequestFromPayload(ExportMeteringDeviceHistoryPayload payload)
|
||||
{
|
||||
var items = new List<object>();
|
||||
var itemsElementName = new List<ItemsChoiceType4>();
|
||||
if (payload.meteringDeviceType != null)
|
||||
{
|
||||
foreach (var meteringDeviceType in payload.meteringDeviceType)
|
||||
{
|
||||
items.Add(new nsiRef()
|
||||
{
|
||||
Code = meteringDeviceType.Code,
|
||||
GUID = meteringDeviceType.GUID
|
||||
});
|
||||
itemsElementName.Add(ItemsChoiceType4.MeteringDeviceType);
|
||||
}
|
||||
}
|
||||
if (payload.municipalResource != null)
|
||||
{
|
||||
foreach (var municipalResource in payload.municipalResource)
|
||||
{
|
||||
items.Add(new nsiRef()
|
||||
{
|
||||
Code = municipalResource.Code,
|
||||
GUID = municipalResource.GUID
|
||||
});
|
||||
itemsElementName.Add(ItemsChoiceType4.MunicipalResource);
|
||||
}
|
||||
}
|
||||
if (payload.meteringDeviceRootGUID != null)
|
||||
{
|
||||
foreach (var meteringDeviceRootGUID in payload.meteringDeviceRootGUID)
|
||||
{
|
||||
items.Add(meteringDeviceRootGUID);
|
||||
itemsElementName.Add(ItemsChoiceType4.MeteringDeviceRootGUID);
|
||||
}
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/DeviceMetering/exportMeteringDeviceHistoryRequest.html
|
||||
var request = new exportMeteringDeviceHistoryRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.3.1",
|
||||
FIASHouseGuid = payload.fiasHouseGuid,
|
||||
Items = [.. items],
|
||||
ItemsElementName = [.. itemsElementName]
|
||||
};
|
||||
|
||||
if (payload.commissioningDateFrom.HasValue)
|
||||
{
|
||||
request.CommissioningDateFrom = payload.commissioningDateFrom.Value;
|
||||
request.CommissioningDateFromSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.сommissioningDateTo.HasValue)
|
||||
{
|
||||
request.CommissioningDateTo = payload.сommissioningDateTo.Value;
|
||||
request.CommissioningDateToSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.serchArchived.HasValue)
|
||||
{
|
||||
request.SerchArchived = payload.serchArchived.Value;
|
||||
request.SerchArchivedSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.archiveDateFrom.HasValue)
|
||||
{
|
||||
request.ArchiveDateFrom = payload.archiveDateFrom.Value;
|
||||
request.ArchiveDateFromSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.archiveDateTo.HasValue)
|
||||
{
|
||||
request.ArchiveDateTo = payload.archiveDateTo.Value;
|
||||
request.ArchiveDateToSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.inputDateFrom.HasValue)
|
||||
{
|
||||
request.inputDateFrom = payload.inputDateFrom.Value;
|
||||
request.inputDateFromSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.inputDateTo.HasValue)
|
||||
{
|
||||
request.inputDateTo = payload.inputDateTo.Value;
|
||||
request.inputDateToSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.excludePersonAsDataSource.HasValue)
|
||||
{
|
||||
request.ExcludePersonAsDataSource = payload.excludePersonAsDataSource.Value;
|
||||
request.ExcludePersonAsDataSourceSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.excludeCurrentOrgAsDataSource.HasValue)
|
||||
{
|
||||
request.ExcludeCurrentOrgAsDataSource = payload.excludeCurrentOrgAsDataSource.Value;
|
||||
request.ExcludeCurrentOrgAsDataSourceSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.excludeOtherOrgAsDataSource.HasValue)
|
||||
{
|
||||
request.ExcludeOtherOrgAsDataSource = payload.excludeOtherOrgAsDataSource.Value;
|
||||
request.ExcludeOtherOrgAsDataSourceSpecified = true;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.DeviceMetering;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.DeviceMetering
|
||||
{
|
||||
internal class ImportMeteringDeviceValuesRequest(ClientBase client) : DeviceMeteringRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(importMeteringDeviceValuesRequestMeteringDevicesValues values, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/DeviceMetering/importMeteringDeviceValuesRequest.html
|
||||
var request = new importMeteringDeviceValuesRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.1",
|
||||
MeteringDevicesValues = [values]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importMeteringDeviceValuesAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
result.Items.OfType<CommonResultType>().ToList().ForEach(commonResult =>
|
||||
{
|
||||
commonResult.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Hcs.ClientNet/Client/Api/Request/EndPoint.cs
Normal file
16
Hcs.ClientNet/Client/Api/Request/EndPoint.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal enum EndPoint
|
||||
{
|
||||
BillsAsync,
|
||||
DebtRequestsAsync,
|
||||
DeviceMeteringAsync,
|
||||
HomeManagementAsync,
|
||||
LicensesAsync,
|
||||
NsiAsync,
|
||||
NsiCommonAsync,
|
||||
OrgRegistryAsync,
|
||||
OrgRegistryCommonAsync,
|
||||
PaymentsAsync
|
||||
}
|
||||
}
|
||||
30
Hcs.ClientNet/Client/Api/Request/EndPointLocator.cs
Normal file
30
Hcs.ClientNet/Client/Api/Request/EndPointLocator.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal class EndPointLocator
|
||||
{
|
||||
private static readonly Dictionary<EndPoint, string> endPoints;
|
||||
|
||||
static EndPointLocator()
|
||||
{
|
||||
endPoints ??= [];
|
||||
|
||||
endPoints.Add(EndPoint.BillsAsync, "ext-bus-bills-service/services/BillsAsync");
|
||||
endPoints.Add(EndPoint.DebtRequestsAsync, "ext-bus-debtreq-service/services/DebtRequestsAsync");
|
||||
endPoints.Add(EndPoint.DeviceMeteringAsync, "ext-bus-device-metering-service/services/DeviceMeteringAsync");
|
||||
endPoints.Add(EndPoint.HomeManagementAsync, "ext-bus-home-management-service/services/HomeManagementAsync");
|
||||
endPoints.Add(EndPoint.LicensesAsync, "ext-bus-licenses-service/services/LicensesAsync");
|
||||
endPoints.Add(EndPoint.NsiAsync, "ext-bus-nsi-service/services/NsiAsync");
|
||||
endPoints.Add(EndPoint.NsiCommonAsync, "ext-bus-nsi-common-service/services/NsiCommonAsync");
|
||||
endPoints.Add(EndPoint.OrgRegistryAsync, "ext-bus-org-registry-service/services/OrgRegistryAsync");
|
||||
endPoints.Add(EndPoint.OrgRegistryCommonAsync, "ext-bus-org-registry-common-service/services/OrgRegistryCommonAsync");
|
||||
endPoints.Add(EndPoint.PaymentsAsync, "ext-bus-payment-service/services/PaymentAsync");
|
||||
}
|
||||
|
||||
internal static string GetPath(EndPoint endPoint)
|
||||
{
|
||||
return endPoints[endPoint];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Исключение указывает на то, что сервер обнаружил что у
|
||||
/// него нет объектов для выдачи по условию
|
||||
/// </summary>
|
||||
internal class NoResultsRemoteException : RemoteException
|
||||
{
|
||||
public NoResultsRemoteException(string description) : base(NO_OBJECTS_FOR_EXPORT, description) { }
|
||||
|
||||
public NoResultsRemoteException(string errorCode, string description) :
|
||||
base(errorCode, description) { }
|
||||
|
||||
public NoResultsRemoteException(string errorCode, string description, System.Exception nested) :
|
||||
base(errorCode, description, nested) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Exception
|
||||
{
|
||||
internal class RemoteException : System.Exception
|
||||
{
|
||||
internal const string NO_OBJECTS_FOR_EXPORT = "INT002012";
|
||||
internal const string MISSING_IN_REGISTRY = "INT002000";
|
||||
internal const string ACCESS_DENIED = "AUT011003";
|
||||
|
||||
internal string ErrorCode { get; private set; }
|
||||
internal string Description { get; private set; }
|
||||
|
||||
public RemoteException(string errorCode, string description)
|
||||
: base(Combine(errorCode, description))
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public RemoteException(string errorCode, string description, System.Exception nestedException)
|
||||
: base(Combine(errorCode, description), nestedException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
private static string Combine(string errorCode, string description)
|
||||
{
|
||||
return $"Remote server returned an error: [{errorCode}] {description}";
|
||||
}
|
||||
|
||||
internal static RemoteException CreateNew(string errorCode, string description, System.Exception nested = null)
|
||||
{
|
||||
if (string.Compare(errorCode, NO_OBJECTS_FOR_EXPORT) == 0)
|
||||
{
|
||||
return new NoResultsRemoteException(errorCode, description, nested);
|
||||
}
|
||||
return new RemoteException(errorCode, description);
|
||||
}
|
||||
|
||||
internal static RemoteException CreateNew(RemoteException nested)
|
||||
{
|
||||
if (nested == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(nested));
|
||||
}
|
||||
return CreateNew(nested.ErrorCode, nested.Description, nested);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает true, если ошибка @e или ее вложенные ошибки содержат @errorCode
|
||||
/// </summary>
|
||||
internal static bool ContainsErrorCode(SystemException e, string errorCode)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Util.EnumerateInnerExceptions(e).OfType<RemoteException>().Where(x => x.ErrorCode == errorCode).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace Hcs.ClientNet.Api.Request.Exception
|
||||
{
|
||||
internal class RestartTimeoutException : System.Exception
|
||||
{
|
||||
public RestartTimeoutException(string message) : base(message) { }
|
||||
|
||||
public RestartTimeoutException(string message, System.Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal class GostSigningEndpointBehavior(ClientBase client) : IEndpointBehavior
|
||||
{
|
||||
private readonly ClientBase client = client;
|
||||
|
||||
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
|
||||
|
||||
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
|
||||
{
|
||||
clientRuntime.ClientMessageInspectors.Add(
|
||||
new GostSigningMessageInspector(client));
|
||||
}
|
||||
|
||||
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
|
||||
|
||||
public void Validate(ServiceEndpoint endpoint) { }
|
||||
}
|
||||
}
|
||||
116
Hcs.ClientNet/Client/Api/Request/GostSigningMessageInspector.cs
Normal file
116
Hcs.ClientNet/Client/Api/Request/GostSigningMessageInspector.cs
Normal file
@ -0,0 +1,116 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using GostXades;
|
||||
using System.IO;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Dispatcher;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
/// <summary>
|
||||
/// Фильтр сообщений добавляет в XML-сообщение электронную подпись XADES/GOST
|
||||
/// </summary>
|
||||
internal class GostSigningMessageInspector(ClientBase client) : IClientMessageInspector
|
||||
{
|
||||
private readonly ClientBase client = client;
|
||||
|
||||
public object BeforeSendRequest(ref Message request, IClientChannel channel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filterHeader = "[Message Inspector]";
|
||||
|
||||
PurgeDebuggerHeaders(ref request);
|
||||
|
||||
var messageBody = GetMessageBodyString(ref request, Encoding.UTF8);
|
||||
if (!messageBody.Contains(Constants.SIGNED_XML_ELEMENT_ID))
|
||||
{
|
||||
client.TryCaptureMessage(true, messageBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
var certInfo = X509Tools.GetFullnameWithExpirationDateStr(client.Certificate);
|
||||
client.TryLog($"{filterHeader} signing message with key [{certInfo}]...");
|
||||
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
var service = new GostXadesBesService(client.CryptoProviderType);
|
||||
var signedXml = service.Sign(messageBody,
|
||||
Constants.SIGNED_XML_ELEMENT_ID,
|
||||
client.CertificateThumbprint,
|
||||
client.CertificatePassword);
|
||||
stopwatch.Stop();
|
||||
|
||||
client.TryLog($"{filterHeader} message signed in {stopwatch.ElapsedMilliseconds} ms");
|
||||
client.TryCaptureMessage(true, signedXml);
|
||||
|
||||
request = Message.CreateMessage(
|
||||
XmlReaderFromString(signedXml), int.MaxValue, request.Version);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new System.Exception($"Exception occured in {GetType().Name}", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void PurgeDebuggerHeaders(ref Message request)
|
||||
{
|
||||
var limit = request.Headers.Count;
|
||||
for (var i = 0; i < limit; ++i)
|
||||
{
|
||||
if (request.Headers[i].Name.Equals("VsDebuggerCausalityData"))
|
||||
{
|
||||
request.Headers.RemoveAt(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetMessageBodyString(ref Message request, Encoding encoding)
|
||||
{
|
||||
var mb = request.CreateBufferedCopy(int.MaxValue);
|
||||
request = mb.CreateMessage();
|
||||
|
||||
var s = new MemoryStream();
|
||||
var xw = XmlWriter.Create(s);
|
||||
mb.CreateMessage().WriteMessage(xw);
|
||||
xw.Flush();
|
||||
|
||||
s.Position = 0;
|
||||
|
||||
var bXML = new byte[s.Length];
|
||||
s.Read(bXML, 0, (int)s.Length);
|
||||
|
||||
if (bXML[0] != (byte)'<')
|
||||
{
|
||||
return encoding.GetString(bXML, 3, bXML.Length - 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
return encoding.GetString(bXML, 0, bXML.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private XmlReader XmlReaderFromString(string xml)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.Write(xml);
|
||||
writer.Flush();
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
return XmlReader.Create(stream);
|
||||
}
|
||||
|
||||
public void AfterReceiveReply(ref Message reply, object correlationState)
|
||||
{
|
||||
client.TryCaptureMessage(false, reply.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ExportAccountRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
internal async Task<IEnumerable<exportAccountResultType>> ExecuteAsync(string fiasHouseGuid, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/exportAccountRequest.html
|
||||
var request = new exportAccountRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.1",
|
||||
Items = [fiasHouseGuid],
|
||||
ItemsElementName = [ItemsChoiceType26.FIASHouseGuid]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportAccountDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportAccountResultType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ExportHouseRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
internal async Task<IEnumerable<exportHouseResultType>> ExecuteAsync(string fiasHouseGuid, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/exportHouseRequest.html
|
||||
var request = new exportHouseRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "15.6.0.1",
|
||||
FIASHouseGuid = fiasHouseGuid
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportHouseDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportHouseResultType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ExportSupplyResourceContractDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool EnableMinimalResponseWaitDelay => false;
|
||||
|
||||
internal async Task<IEnumerable<exportSupplyResourceContractResultType>> ExecuteAsync(CancellationToken token)
|
||||
{
|
||||
var result = new List<exportSupplyResourceContractResultType>();
|
||||
|
||||
void OnResultReceived(exportSupplyResourceContractResultType[] contracts)
|
||||
{
|
||||
if (contracts?.Length > 0)
|
||||
{
|
||||
result.AddRange(contracts);
|
||||
}
|
||||
}
|
||||
|
||||
var pageNum = 0;
|
||||
Guid? exportContractRootGuid = null;
|
||||
while (true)
|
||||
{
|
||||
pageNum++;
|
||||
|
||||
client.TryLog($"Querying page #{pageNum}...");
|
||||
|
||||
var data = await QueryBatchAsync(null, null, exportContractRootGuid, OnResultReceived, token);
|
||||
if (data.IsLastPage)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
exportContractRootGuid = data.NextGuid;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<exportSupplyResourceContractResultType> ExecuteAsync(Guid contractRootGuid, CancellationToken token)
|
||||
{
|
||||
exportSupplyResourceContractResultType result = null;
|
||||
|
||||
void OnResultReceived(exportSupplyResourceContractResultType[] contracts)
|
||||
{
|
||||
result = contracts?.Length > 0 ? contracts[0] : null;
|
||||
}
|
||||
|
||||
await QueryBatchAsync(contractRootGuid, null, null, OnResultReceived, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<exportSupplyResourceContractResultType> ExecuteAsync(string contractNumber, CancellationToken token)
|
||||
{
|
||||
exportSupplyResourceContractResultType result = null;
|
||||
|
||||
void OnResultReceived(exportSupplyResourceContractResultType[] contracts)
|
||||
{
|
||||
result = contracts?.Length > 0 ? contracts[0] : null;
|
||||
}
|
||||
|
||||
await QueryBatchAsync(null, contractNumber, null, OnResultReceived, token);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<PaginationData> QueryBatchAsync(
|
||||
Guid? contractRootGuid, string contractNumber, Guid? exportContractRootGuid,
|
||||
Action<exportSupplyResourceContractResultType[]> onResultReceived,
|
||||
CancellationToken token)
|
||||
{
|
||||
var itemsElementName = new List<ItemsChoiceType32>();
|
||||
var items = new List<object>();
|
||||
|
||||
if (contractRootGuid.HasValue)
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ContractRootGUID);
|
||||
items.Add(contractRootGuid.ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(contractNumber))
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ContractNumber);
|
||||
items.Add(contractNumber);
|
||||
}
|
||||
|
||||
if (exportContractRootGuid.HasValue)
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType32.ExportContractRootGUID);
|
||||
items.Add(exportContractRootGuid.ToString());
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/exportSupplyResourceContractRequest.html
|
||||
var request = new exportSupplyResourceContractRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.1.1",
|
||||
ItemsElementName = [.. itemsElementName],
|
||||
Items = [.. items]
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var ackResponse = await asyncClient.exportSupplyResourceContractDataAsync(
|
||||
CreateRequestHeader(), request);
|
||||
return ackResponse.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
var contractResult = result.Items.OfType<getStateResultExportSupplyResourceContractResult>().First();
|
||||
onResultReceived?.Invoke(contractResult.Contract);
|
||||
|
||||
return new PaginationData(contractResult.Item);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return PaginationData.CreateLastPageData();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ExportSupplyResourceContractObjectAddressDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
internal async Task<IEnumerable<exportSupplyResourceContractObjectAddressResultType>> ExecuteAsync(Guid contractRootGuid, CancellationToken token)
|
||||
{
|
||||
var result = new List<exportSupplyResourceContractObjectAddressResultType>();
|
||||
|
||||
void OnResultReceived(exportSupplyResourceContractObjectAddressResultType[] addresses)
|
||||
{
|
||||
if (addresses?.Length > 0)
|
||||
{
|
||||
result.AddRange(addresses);
|
||||
}
|
||||
}
|
||||
|
||||
var pageNum = 0;
|
||||
Guid? exportObjectGuid = null;
|
||||
while (true)
|
||||
{
|
||||
pageNum++;
|
||||
|
||||
client.TryLog($"Querying page #{pageNum}...");
|
||||
|
||||
var data = await QueryBatchAsync(contractRootGuid, exportObjectGuid, OnResultReceived, token);
|
||||
if (data.IsLastPage)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
exportObjectGuid = data.NextGuid;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<PaginationData> QueryBatchAsync(
|
||||
Guid contractRootGuid, Guid? exportObjectGuid,
|
||||
Action<exportSupplyResourceContractObjectAddressResultType[]> onResultReceived,
|
||||
CancellationToken token)
|
||||
{
|
||||
var itemsElementName = new List<ItemsChoiceType34>();
|
||||
var items = new List<string>();
|
||||
|
||||
itemsElementName.Add(ItemsChoiceType34.ContractRootGUID);
|
||||
items.Add(contractRootGuid.ToString());
|
||||
|
||||
if (exportObjectGuid.HasValue)
|
||||
{
|
||||
itemsElementName.Add(ItemsChoiceType34.ExportObjectGUID);
|
||||
items.Add(exportObjectGuid.ToString());
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/exportSupplyResourceContractObjectAddressRequest.html
|
||||
var request = new exportSupplyResourceContractObjectAddressRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.1.1.1",
|
||||
ItemsElementName = [.. itemsElementName],
|
||||
Items = [.. items]
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var ackResponse = await asyncClient.exportSupplyResourceContractObjectAddressDataAsync(
|
||||
CreateRequestHeader(), request);
|
||||
return ackResponse.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
var contractResult = result.Items.OfType<getStateResultExportSupplyResourceContractObjectAddress>().First();
|
||||
onResultReceived?.Invoke(contractResult.ObjectAddress);
|
||||
|
||||
return new PaginationData(contractResult.Item);
|
||||
}
|
||||
catch (NoResultsRemoteException)
|
||||
{
|
||||
return PaginationData.CreateLastPageData();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.HouseManagement
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class HouseManagementPortsTypeAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> 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.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class HouseManagementRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
HouseManagementPortsTypeAsyncClient,
|
||||
HouseManagementPortsTypeAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.HomeManagementAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
|
||||
protected getStateResultImportResultCommonResult[] GetCommonResults(IEnumerable<getStateResultImportResult> importResults)
|
||||
{
|
||||
var result = new List<getStateResultImportResultCommonResult>();
|
||||
foreach (var importResult in importResults)
|
||||
{
|
||||
importResult.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var commonResults = importResult.Items.OfType<getStateResultImportResultCommonResult>();
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
commonResult.Items.OfType<CommonResultTypeError>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
}
|
||||
|
||||
result.AddRange(commonResults);
|
||||
}
|
||||
return [.. result];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportAccountDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(ImportAccountDataPayload payload, CancellationToken token)
|
||||
{
|
||||
// TODO: Добавить проверку пейлоада
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importAccountRequest.html
|
||||
var request = new importAccountRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.1",
|
||||
Account = [GetAccountFromPayload(payload)]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importAccountDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.ImportAccount)
|
||||
{
|
||||
return commonResult.Item is getStateResultImportResultCommonResultImportAccount;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private importAccountRequestAccount GetAccountFromPayload(ImportAccountDataPayload payload)
|
||||
{
|
||||
var account = new importAccountRequestAccount()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
AccountNumber = payload.accountNumber,
|
||||
AccountGUID = payload.accountGUID,
|
||||
AccountReasons = payload.accountReasons,
|
||||
Item = true,
|
||||
Accommodation = payload.accomodations,
|
||||
PayerInfo = payload.payerInfo
|
||||
};
|
||||
|
||||
switch (payload.accountType)
|
||||
{
|
||||
case ImportAccountDataPayload.AccountType.UO:
|
||||
account.ItemElementName = ItemChoiceType8.isUOAccount;
|
||||
break;
|
||||
|
||||
case ImportAccountDataPayload.AccountType.RSO:
|
||||
account.ItemElementName = ItemChoiceType8.isRSOAccount;
|
||||
break;
|
||||
|
||||
case ImportAccountDataPayload.AccountType.CR:
|
||||
account.ItemElementName = ItemChoiceType8.isCRAccount;
|
||||
break;
|
||||
|
||||
case ImportAccountDataPayload.AccountType.RC:
|
||||
account.ItemElementName = ItemChoiceType8.isRCAccount;
|
||||
break;
|
||||
|
||||
case ImportAccountDataPayload.AccountType.OGVorOMS:
|
||||
account.ItemElementName = ItemChoiceType8.isOGVorOMSAccount;
|
||||
break;
|
||||
|
||||
case ImportAccountDataPayload.AccountType.TKO:
|
||||
account.ItemElementName = ItemChoiceType8.isTKOAccount;
|
||||
break;
|
||||
}
|
||||
|
||||
if (payload.livingPersonsNumber.HasValue)
|
||||
{
|
||||
account.LivingPersonsNumber = payload.livingPersonsNumber.Value.ToString();
|
||||
}
|
||||
|
||||
if (payload.totalSquare.HasValue)
|
||||
{
|
||||
account.TotalSquare = payload.totalSquare.Value;
|
||||
account.TotalSquareSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.residentialSquare.HasValue)
|
||||
{
|
||||
account.ResidentialSquare = payload.residentialSquare.Value;
|
||||
account.ResidentialSquareSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.heatedArea.HasValue)
|
||||
{
|
||||
account.HeatedArea = payload.heatedArea.Value;
|
||||
account.HeatedAreaSpecified = true;
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportContractDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
internal async Task<importContractResultType> ExecuteAsync(ImportContractDataPayload payload, CancellationToken token)
|
||||
{
|
||||
// TODO: Добавить проверку пейлоада
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importContractRequest/Contract.html
|
||||
var contract = new importContractRequestContract
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Item = GetContractFromPayload(payload)
|
||||
};
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importContractRequest.html
|
||||
var request = new importContractRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "11.9.0.1",
|
||||
Contract = [contract]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importContractDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.importContract)
|
||||
{
|
||||
if (commonResult.Item is importContractResultType importedContract)
|
||||
{
|
||||
return importedContract;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private importContractRequestContractPlacingContract GetContractFromPayload(ImportContractDataPayload payload)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/importContractRequest/Contract/PlacingContract.html
|
||||
var contract = new importContractRequestContractPlacingContract()
|
||||
{
|
||||
ContractObject = payload.contractObjects,
|
||||
DocNum = payload.docNum,
|
||||
SigningDate = payload.signingDate,
|
||||
EffectiveDate = payload.effectiveDate,
|
||||
PlanDateComptetion = payload.planDateComptetion,
|
||||
Item = true,
|
||||
ItemElementName = ItemChoiceType13.Owners,
|
||||
ContractBase = new nsiRef()
|
||||
{
|
||||
Code = payload.contractBase.Code,
|
||||
GUID = payload.contractBase.GUID
|
||||
},
|
||||
DateDetails = payload.dateDetailsType,
|
||||
ContractAttachment = payload.contractAttachment
|
||||
};
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportHouseUODataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(ImportLivingHouseUODataPayload payload, CancellationToken token)
|
||||
{
|
||||
// TODO: Добавить проверку пейлоада
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importHouseUORequest.html
|
||||
var request = new importHouseUORequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.2.3.2",
|
||||
Item = GetLivingHouseFromPayload(payload)
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importHouseUODataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.ImportHouseUO)
|
||||
{
|
||||
return commonResult.Item is OGFImportStatusType;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private importHouseUORequestLivingHouse GetLivingHouseFromPayload(ImportLivingHouseUODataPayload payload)
|
||||
{
|
||||
var livingHouse = new importHouseUORequestLivingHouseLivingHouseToCreate()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
BasicCharacteristicts = new HouseBasicUOType()
|
||||
{
|
||||
FIASHouseGuid = payload.fiasHouseGuid.ToString(),
|
||||
TotalSquare = payload.totalSquare,
|
||||
State = new nsiRef()
|
||||
{
|
||||
Code = payload.state.Code,
|
||||
GUID = payload.state.GUID
|
||||
},
|
||||
LifeCycleStage = new nsiRef()
|
||||
{
|
||||
Code = payload.lifeCycleStage.Code,
|
||||
GUID = payload.lifeCycleStage.GUID
|
||||
},
|
||||
UsedYear = payload.usedYear,
|
||||
FloorCount = payload.floorCount,
|
||||
OKTMO = payload.oktmo,
|
||||
OlsonTZ = new nsiRef()
|
||||
{
|
||||
Code = payload.olsonTZ.Code,
|
||||
GUID = payload.olsonTZ.GUID
|
||||
},
|
||||
CulturalHeritage = payload.culturalHeritage,
|
||||
OGFData = payload.ogfData,
|
||||
// TODO: Разобраться с кадастровым номером
|
||||
Items = [true, payload.conditionalNumber],
|
||||
ItemsElementName = [ItemsChoiceType3.NoCadastralNumber, ItemsChoiceType3.ConditionalNumber]
|
||||
}
|
||||
};
|
||||
|
||||
if (!payload.isMunicipalProperty)
|
||||
{
|
||||
livingHouse.BasicCharacteristicts.IsMunicipalProperty = false;
|
||||
livingHouse.BasicCharacteristicts.IsMunicipalPropertySpecified = true;
|
||||
}
|
||||
|
||||
if (!payload.isRegionProperty)
|
||||
{
|
||||
livingHouse.BasicCharacteristicts.IsRegionProperty = false;
|
||||
livingHouse.BasicCharacteristicts.IsRegionPropertySpecified = true;
|
||||
}
|
||||
|
||||
if (payload.hasBlocks)
|
||||
{
|
||||
livingHouse.HasBlocks = true;
|
||||
livingHouse.HasBlocksSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.hasMultipleHousesWithSameAddress)
|
||||
{
|
||||
livingHouse.HasMultipleHousesWithSameAddress = true;
|
||||
livingHouse.HasMultipleHousesWithSameAddressSpecified = true;
|
||||
}
|
||||
|
||||
return new importHouseUORequestLivingHouse()
|
||||
{
|
||||
Item = livingHouse
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportMeteringDeviceDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(MeteringDeviceFullInformationType meteringDevice, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/importMeteringDeviceDataRequest.html
|
||||
var request = new importMeteringDeviceDataRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "11.1.0.8",
|
||||
MeteringDevice =
|
||||
[
|
||||
new importMeteringDeviceDataRequestMeteringDevice()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Item = meteringDevice
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importMeteringDeviceDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.importMeteringDevice)
|
||||
{
|
||||
return commonResult.Item is getStateResultImportResultCommonResultImportMeteringDevice;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportNotificationDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(ImportNotificationDataPayload payload, CancellationToken token)
|
||||
{
|
||||
// TODO: Добавить проверку пейлоада
|
||||
|
||||
var notification = new importNotificationRequestNotification()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Item = GetNotificationFromPayload(payload)
|
||||
};
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importNotificationRequest.html
|
||||
var request = new importNotificationRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "13.2.2.0",
|
||||
notification = [notification]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importNotificationDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private importNotificationRequestNotificationCreate GetNotificationFromPayload(ImportNotificationDataPayload payload)
|
||||
{
|
||||
var notification = new importNotificationRequestNotificationCreate();
|
||||
|
||||
if (!string.IsNullOrEmpty(payload.topic))
|
||||
{
|
||||
notification.Item = payload.topic;
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.Item = payload.topicFromRegistry;
|
||||
}
|
||||
|
||||
if (payload.isImportant)
|
||||
{
|
||||
notification.IsImportant = true;
|
||||
notification.IsImportantSpecified = true;
|
||||
}
|
||||
|
||||
notification.content = payload.content;
|
||||
|
||||
var items = new List<object>();
|
||||
var itemsElementName = new List<ItemsChoiceType29>();
|
||||
foreach (var tuple in payload.destinations)
|
||||
{
|
||||
items.Add(tuple.Item2);
|
||||
itemsElementName.Add(tuple.Item1);
|
||||
}
|
||||
notification.Items = [.. items];
|
||||
notification.ItemsElementName = [.. itemsElementName];
|
||||
|
||||
if (payload.isNotLimit)
|
||||
{
|
||||
notification.Items1 = [true];
|
||||
notification.Items1ElementName = [Items1ChoiceType.IsNotLimit];
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.Items1 = [payload.startDate.Value, payload.endDate.Value];
|
||||
notification.Items1ElementName = [Items1ChoiceType.StartDate, Items1ChoiceType.EndDate];
|
||||
}
|
||||
|
||||
// TODO: Добавить добавление аттачмента
|
||||
|
||||
if (payload.isShipOff)
|
||||
{
|
||||
notification.IsShipOff = true;
|
||||
notification.IsShipOffSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.isForPublishToMobileApp)
|
||||
{
|
||||
notification.IsForPublishToMobileApp = true;
|
||||
notification.IsForPublishToMobileAppSpecified = true;
|
||||
}
|
||||
|
||||
notification.MobileAppData = payload.mobileAppData;
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,302 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportSupplyResourceContractDataRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<getStateResultImportResultCommonResultImportSupplyResourceContract> ExecuteAsync(ImportSupplyResourceContractDataPayload payload, CancellationToken token)
|
||||
{
|
||||
ThrowIfPayloadIncorrect(payload);
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/SupplyResourceContractType.html
|
||||
var contract = new importSupplyResourceContractRequestContract
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Item1 = GetContractFromPayload(payload)
|
||||
};
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importSupplyResourceContractRequest.html
|
||||
var request = new importSupplyResourceContractRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "11.3.0.5",
|
||||
Contract = [contract]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importSupplyResourceContractDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.ImportSupplyResourceContract)
|
||||
{
|
||||
if (commonResult.Item is getStateResultImportResultCommonResultImportSupplyResourceContract importedContract)
|
||||
{
|
||||
return importedContract;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ThrowIfPayloadIncorrect(ImportSupplyResourceContractDataPayload payload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(payload.contractNumber))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractNumber)} is empty");
|
||||
}
|
||||
|
||||
if (payload.signingDate.Equals(default) || payload.effectiveDate.Equals(default))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.signingDate)} OR/AND {nameof(payload.effectiveDate)} are default");
|
||||
}
|
||||
|
||||
if (!payload.comptetionDate.HasValue && payload.automaticRollOverOneYear)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.comptetionDate)} is null but {nameof(payload.automaticRollOverOneYear)} has value");
|
||||
}
|
||||
|
||||
if (payload.period == null && (payload.volumeDepends.HasValue && payload.volumeDepends.Value
|
||||
|| payload.meteringDeviceInformation.HasValue && payload.meteringDeviceInformation.Value))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.period)} is null but {nameof(payload.volumeDepends)} OR/AND {nameof(payload.meteringDeviceInformation)} have value");
|
||||
}
|
||||
|
||||
if (payload.indicationsAnyDay && payload.period == null)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.indicationsAnyDay)} has value but {nameof(payload.period)} is null");
|
||||
}
|
||||
|
||||
// TODO: Add counterparty check
|
||||
|
||||
if (payload.plannedVolumeType.HasValue && !payload.isPlannedVolume)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.plannedVolumeType)} has value but {nameof(payload.isPlannedVolume)} is false");
|
||||
}
|
||||
|
||||
if (payload.contractSubject == null || payload.contractSubject.Length == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractSubject)} is empty");
|
||||
}
|
||||
|
||||
if (payload.contractSubject != null && payload.contractSubject.Length > 100)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractSubject)} exceeds its limit ({payload.contractSubject.Length} of 100)");
|
||||
}
|
||||
|
||||
if (payload.countingResource.HasValue && (!payload.accrualProcedure.HasValue ||
|
||||
payload.accrualProcedure.Value == SupplyResourceContractTypeAccrualProcedure.O))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.countingResource)} has value but {nameof(payload.accrualProcedure)} is null OR has inappropriate value");
|
||||
}
|
||||
|
||||
// TODO: Add noConnectionToWaterSupply check
|
||||
|
||||
if (payload.objectAddress == null || payload.objectAddress.Length == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.objectAddress)} is empty");
|
||||
}
|
||||
|
||||
// TODO: Add quality check
|
||||
// TODO: Add otherQualityIndicator check
|
||||
// TODO: Add temperatureChart check
|
||||
|
||||
if (payload.billingDate == null && (payload.counterparty is not SupplyResourceContractTypeOrganization ||
|
||||
payload.meteringDeviceInformation.HasValue))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.billingDate)} is null but {nameof(payload.meteringDeviceInformation)} has value");
|
||||
}
|
||||
|
||||
if (payload.billingDate != null && payload.oneTimePayment.HasValue && payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.billingDate)} has value but {nameof(payload.oneTimePayment)} is true");
|
||||
}
|
||||
|
||||
if (payload.paymentDate == null && payload.counterparty is not SupplyResourceContractTypeOrganization &&
|
||||
payload.isContract && payload.oneTimePayment.HasValue && !payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.paymentDate)} is null but should have value");
|
||||
}
|
||||
|
||||
if (payload.paymentDate != null && payload.oneTimePayment.HasValue && payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.paymentDate)} has value but {nameof(payload.oneTimePayment)} is true");
|
||||
}
|
||||
|
||||
if (payload.providingInformationDate == null && payload.counterparty is SupplyResourceContractTypeOrganization &&
|
||||
payload.countingResource.HasValue && payload.countingResource.Value == SupplyResourceContractTypeCountingResource.R &&
|
||||
payload.isContract)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.providingInformationDate)} is null but should have value");
|
||||
}
|
||||
|
||||
if (!payload.meteringDeviceInformation.HasValue &&
|
||||
payload.countingResource.HasValue && payload.countingResource == SupplyResourceContractTypeCountingResource.R)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.meteringDeviceInformation)} is null but should have value");
|
||||
}
|
||||
|
||||
if (payload.volumeDepends.HasValue && (payload.counterparty is SupplyResourceContractTypeOrganization ||
|
||||
payload.oneTimePayment.HasValue && payload.oneTimePayment.Value))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.volumeDepends)} has value but should have not one");
|
||||
}
|
||||
|
||||
if (payload.oneTimePayment.HasValue && payload.counterparty is SupplyResourceContractTypeOrganization)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.oneTimePayment)} has value but {nameof(payload.counterparty)} has inappropriate value");
|
||||
}
|
||||
|
||||
// TODO: Add accrualProcedure check
|
||||
}
|
||||
|
||||
private SupplyResourceContractType GetContractFromPayload(ImportSupplyResourceContractDataPayload payload)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/SupplyResourceContractType.html
|
||||
var contract = new SupplyResourceContractType();
|
||||
|
||||
if (payload.isContract)
|
||||
{
|
||||
var isContract = new SupplyResourceContractTypeIsContract()
|
||||
{
|
||||
ContractNumber = payload.contractNumber,
|
||||
SigningDate = payload.signingDate,
|
||||
EffectiveDate = payload.effectiveDate
|
||||
};
|
||||
contract.Item = isContract;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isNotContract = new SupplyResourceContractTypeIsNotContract()
|
||||
{
|
||||
ContractNumber = payload.contractNumber,
|
||||
SigningDate = payload.signingDate,
|
||||
SigningDateSpecified = true,
|
||||
EffectiveDate = payload.effectiveDate,
|
||||
EffectiveDateSpecified = true
|
||||
};
|
||||
contract.Item = isNotContract;
|
||||
}
|
||||
|
||||
var items = new List<object>();
|
||||
var itemsElementName = new List<ItemsChoiceType9>();
|
||||
if (payload.indefiniteTerm)
|
||||
{
|
||||
items.Add(payload.indefiniteTerm);
|
||||
itemsElementName.Add(ItemsChoiceType9.IndefiniteTerm);
|
||||
}
|
||||
if (payload.automaticRollOverOneYear)
|
||||
{
|
||||
items.Add(payload.automaticRollOverOneYear);
|
||||
itemsElementName.Add(ItemsChoiceType9.AutomaticRollOverOneYear);
|
||||
|
||||
items.Add(payload.comptetionDate.Value);
|
||||
itemsElementName.Add(ItemsChoiceType9.ComptetionDate);
|
||||
}
|
||||
if (items.Count > 0 && itemsElementName.Count > 0)
|
||||
{
|
||||
contract.Items = [.. items];
|
||||
contract.ItemsElementName = [.. itemsElementName];
|
||||
}
|
||||
|
||||
if (payload.period != null)
|
||||
{
|
||||
contract.Period = payload.period;
|
||||
}
|
||||
|
||||
if (payload.indicationsAnyDay)
|
||||
{
|
||||
contract.IndicationsAnyDay = true;
|
||||
contract.IndicationsAnyDaySpecified = true;
|
||||
}
|
||||
|
||||
if (payload.contractBase != null && payload.contractBase.Length > 0)
|
||||
{
|
||||
contract.ContractBase = payload.contractBase;
|
||||
}
|
||||
|
||||
contract.Item1 = payload.counterparty;
|
||||
contract.IsPlannedVolume = payload.isPlannedVolume;
|
||||
|
||||
if (payload.plannedVolumeType.HasValue)
|
||||
{
|
||||
contract.PlannedVolumeType = payload.plannedVolumeType.Value;
|
||||
contract.PlannedVolumeTypeSpecified = true;
|
||||
}
|
||||
|
||||
contract.ContractSubject = payload.contractSubject;
|
||||
|
||||
if (payload.countingResource.HasValue)
|
||||
{
|
||||
contract.CountingResource = payload.countingResource.Value;
|
||||
contract.CountingResourceSpecified = true;
|
||||
}
|
||||
|
||||
contract.SpecifyingQualityIndicators = payload.specifyingQualityIndicators;
|
||||
|
||||
if (payload.noConnectionToWaterSupply)
|
||||
{
|
||||
contract.NoConnectionToWaterSupply = true;
|
||||
contract.NoConnectionToWaterSupplySpecified = true;
|
||||
}
|
||||
|
||||
contract.ObjectAddress = payload.objectAddress;
|
||||
contract.Quality = payload.quality;
|
||||
contract.OtherQualityIndicator = payload.otherQualityIndicator;
|
||||
contract.TemperatureChart = payload.temperatureChart;
|
||||
contract.BillingDate = payload.billingDate;
|
||||
contract.PaymentDate = payload.paymentDate;
|
||||
contract.ProvidingInformationDate = payload.providingInformationDate;
|
||||
|
||||
if (payload.meteringDeviceInformation.HasValue)
|
||||
{
|
||||
contract.MeteringDeviceInformation = payload.meteringDeviceInformation.Value;
|
||||
contract.MeteringDeviceInformationSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.volumeDepends.HasValue)
|
||||
{
|
||||
contract.VolumeDepends = payload.volumeDepends.Value;
|
||||
contract.VolumeDependsSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.oneTimePayment.HasValue)
|
||||
{
|
||||
contract.OneTimePayment = payload.oneTimePayment.Value;
|
||||
contract.OneTimePaymentSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.accrualProcedure.HasValue)
|
||||
{
|
||||
contract.AccrualProcedure = payload.accrualProcedure.Value;
|
||||
contract.AccrualProcedureSpecified = true;
|
||||
}
|
||||
|
||||
contract.Tariff = payload.tariff;
|
||||
contract.Norm = payload.norm;
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,306 @@
|
||||
using Hcs.ClientNet.Api.Payload.HouseManagement;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.HouseManagement;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.HouseManagement
|
||||
{
|
||||
internal class ImportSupplyResourceContractProjectRequest(ClientBase client) : HouseManagementRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<getStateResultImportResultCommonResultImportSupplyResourceContractProject> ExecuteAsync(ImportSupplyResourceContractProjectPayload payload, CancellationToken token)
|
||||
{
|
||||
ThrowIfPayloadIncorrect(payload);
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importSupplyResourceContractProjectRequest/Contract.html
|
||||
var contract = new importSupplyResourceContractProjectRequestContract
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
Item1 = GetContractFromPayload(payload)
|
||||
};
|
||||
|
||||
// http://open-gkh.ru/HouseManagement/importSupplyResourceContractProjectRequest.html
|
||||
var request = new importSupplyResourceContractProjectRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "11.7.0.3",
|
||||
Contract = [contract]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importSupplyResourceContractProjectDataAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
var importResults = result.Items.OfType<getStateResultImportResult>();
|
||||
var commonResults = GetCommonResults(importResults);
|
||||
foreach (var commonResult in commonResults)
|
||||
{
|
||||
if (commonResult.ItemElementName == ItemChoiceType26.ImportSupplyResourceContractProject)
|
||||
{
|
||||
if (commonResult.Item is getStateResultImportResultCommonResultImportSupplyResourceContractProject importedContract)
|
||||
{
|
||||
return importedContract;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Дополнить проверки
|
||||
private void ThrowIfPayloadIncorrect(ImportSupplyResourceContractProjectPayload payload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(payload.contractNumber))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractNumber)} is empty");
|
||||
}
|
||||
|
||||
if (payload.signingDate.Equals(default) || payload.effectiveDate.Equals(default))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.signingDate)} OR/AND {nameof(payload.effectiveDate)} are default");
|
||||
}
|
||||
|
||||
if (!payload.comptetionDate.HasValue && payload.automaticRollOverOneYear)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.comptetionDate)} is null but {nameof(payload.automaticRollOverOneYear)} has value");
|
||||
}
|
||||
|
||||
if (payload.period == null && (payload.volumeDepends.HasValue && payload.volumeDepends.Value
|
||||
|| payload.meteringDeviceInformation.HasValue && payload.meteringDeviceInformation.Value))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.period)} is null but {nameof(payload.volumeDepends)} OR/AND {nameof(payload.meteringDeviceInformation)} have value");
|
||||
}
|
||||
|
||||
if (payload.indicationsAnyDay && payload.period == null)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.indicationsAnyDay)} has value but {nameof(payload.period)} is null");
|
||||
}
|
||||
|
||||
// TODO: Add counterparty check
|
||||
|
||||
if (payload.plannedVolumeType.HasValue && !payload.isPlannedVolume)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.plannedVolumeType)} has value but {nameof(payload.isPlannedVolume)} is false");
|
||||
}
|
||||
|
||||
if (payload.contractSubject == null || payload.contractSubject.Length == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractSubject)} is empty");
|
||||
}
|
||||
|
||||
if (payload.contractSubject != null && payload.contractSubject.Length > 100)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.contractSubject)} exceeds its limit ({payload.contractSubject.Length} of 100)");
|
||||
}
|
||||
|
||||
if (payload.countingResource.HasValue && (!payload.accrualProcedure.HasValue ||
|
||||
payload.accrualProcedure.Value == SupplyResourceContractProjectTypeAccrualProcedure.O))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.countingResource)} has value but {nameof(payload.accrualProcedure)} is null OR has inappropriate value");
|
||||
}
|
||||
|
||||
// TODO: Add noConnectionToWaterSupply check
|
||||
// TODO: Add quality check
|
||||
// TODO: Add otherQualityIndicator check
|
||||
// TODO: Add temperatureChart check
|
||||
|
||||
if (payload.billingDate == null && (payload.counterparty is not SupplyResourceContractTypeOrganization ||
|
||||
payload.meteringDeviceInformation.HasValue))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.billingDate)} is null but {nameof(payload.meteringDeviceInformation)} has value");
|
||||
}
|
||||
|
||||
if (payload.billingDate != null && payload.oneTimePayment.HasValue && payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.billingDate)} has value but {nameof(payload.oneTimePayment)} is true");
|
||||
}
|
||||
|
||||
if (payload.paymentDate == null && payload.counterparty is not SupplyResourceContractTypeOrganization &&
|
||||
payload.isContract && payload.oneTimePayment.HasValue && !payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.paymentDate)} is null but should have value");
|
||||
}
|
||||
|
||||
if (payload.paymentDate != null && payload.oneTimePayment.HasValue && payload.oneTimePayment.Value)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.paymentDate)} has value but {nameof(payload.oneTimePayment)} is true");
|
||||
}
|
||||
|
||||
if (payload.providingInformationDate == null && payload.counterparty is SupplyResourceContractTypeOrganization &&
|
||||
payload.countingResource.HasValue && payload.countingResource.Value == SupplyResourceContractProjectTypeCountingResource.R &&
|
||||
payload.isContract)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.providingInformationDate)} is null but should have value");
|
||||
}
|
||||
|
||||
if (!payload.meteringDeviceInformation.HasValue &&
|
||||
payload.countingResource.HasValue && payload.countingResource == SupplyResourceContractProjectTypeCountingResource.R)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.meteringDeviceInformation)} is null but should have value");
|
||||
}
|
||||
|
||||
if (payload.volumeDepends.HasValue && (payload.counterparty is SupplyResourceContractTypeOrganization ||
|
||||
payload.oneTimePayment.HasValue && payload.oneTimePayment.Value))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.volumeDepends)} has value but should have not one");
|
||||
}
|
||||
|
||||
if (payload.oneTimePayment.HasValue && payload.counterparty is SupplyResourceContractTypeOrganization)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.oneTimePayment)} has value but {nameof(payload.counterparty)} has inappropriate value");
|
||||
}
|
||||
|
||||
// TODO: Add accrualProcedure check
|
||||
}
|
||||
|
||||
private SupplyResourceContractProjectType GetContractFromPayload(ImportSupplyResourceContractProjectPayload payload)
|
||||
{
|
||||
// http://open-gkh.ru/HouseManagement/SupplyResourceContractProjectType.html
|
||||
var contract = new SupplyResourceContractProjectType();
|
||||
|
||||
if (payload.isContract)
|
||||
{
|
||||
var isContract = new SupplyResourceContractProjectTypeIsContract()
|
||||
{
|
||||
ContractNumber = payload.contractNumber,
|
||||
SigningDate = payload.signingDate,
|
||||
EffectiveDate = payload.effectiveDate
|
||||
};
|
||||
contract.Item = isContract;
|
||||
}
|
||||
else
|
||||
{
|
||||
var isNotContract = new SupplyResourceContractProjectTypeIsNotContract()
|
||||
{
|
||||
ContractNumber = payload.contractNumber,
|
||||
SigningDate = payload.signingDate,
|
||||
SigningDateSpecified = true,
|
||||
EffectiveDate = payload.effectiveDate,
|
||||
EffectiveDateSpecified = true
|
||||
};
|
||||
contract.Item = isNotContract;
|
||||
}
|
||||
|
||||
var items = new List<object>();
|
||||
var itemsElementName = new List<ItemsChoiceType14>();
|
||||
if (payload.indefiniteTerm)
|
||||
{
|
||||
items.Add(payload.indefiniteTerm);
|
||||
itemsElementName.Add(ItemsChoiceType14.IndefiniteTerm);
|
||||
}
|
||||
if (payload.automaticRollOverOneYear)
|
||||
{
|
||||
items.Add(payload.automaticRollOverOneYear);
|
||||
itemsElementName.Add(ItemsChoiceType14.AutomaticRollOverOneYear);
|
||||
|
||||
items.Add(payload.comptetionDate.Value);
|
||||
itemsElementName.Add(ItemsChoiceType14.ComptetionDate);
|
||||
}
|
||||
if (items.Count > 0 && itemsElementName.Count > 0)
|
||||
{
|
||||
contract.Items = [.. items];
|
||||
contract.ItemsElementName = [.. itemsElementName];
|
||||
}
|
||||
|
||||
if (payload.period != null)
|
||||
{
|
||||
contract.Period = payload.period;
|
||||
}
|
||||
|
||||
if (payload.indicationsAnyDay)
|
||||
{
|
||||
contract.IndicationsAnyDay = true;
|
||||
contract.IndicationsAnyDaySpecified = true;
|
||||
}
|
||||
|
||||
if (payload.contractBase != null && payload.contractBase.Length > 0)
|
||||
{
|
||||
contract.ContractBase = payload.contractBase;
|
||||
}
|
||||
|
||||
contract.Item1 = payload.counterparty;
|
||||
contract.IsPlannedVolume = payload.isPlannedVolume;
|
||||
|
||||
if (payload.plannedVolumeType.HasValue)
|
||||
{
|
||||
contract.PlannedVolumeType = payload.plannedVolumeType.Value;
|
||||
contract.PlannedVolumeTypeSpecified = true;
|
||||
}
|
||||
|
||||
contract.ContractSubject = payload.contractSubject;
|
||||
|
||||
if (payload.countingResource.HasValue)
|
||||
{
|
||||
contract.CountingResource = payload.countingResource.Value;
|
||||
contract.CountingResourceSpecified = true;
|
||||
}
|
||||
|
||||
contract.SpecifyingQualityIndicators = payload.specifyingQualityIndicators;
|
||||
|
||||
if (payload.noConnectionToWaterSupply)
|
||||
{
|
||||
contract.NoConnectionToWaterSupply = true;
|
||||
contract.NoConnectionToWaterSupplySpecified = true;
|
||||
}
|
||||
|
||||
contract.Quality = payload.quality;
|
||||
contract.OtherQualityIndicator = payload.otherQualityIndicator;
|
||||
contract.TemperatureChart = payload.temperatureChart;
|
||||
contract.BillingDate = payload.billingDate;
|
||||
contract.PaymentDate = payload.paymentDate;
|
||||
contract.ProvidingInformationDate = payload.providingInformationDate;
|
||||
|
||||
if (payload.meteringDeviceInformation.HasValue)
|
||||
{
|
||||
contract.MeteringDeviceInformation = payload.meteringDeviceInformation.Value;
|
||||
contract.MeteringDeviceInformationSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.volumeDepends.HasValue)
|
||||
{
|
||||
contract.VolumeDepends = payload.volumeDepends.Value;
|
||||
contract.VolumeDependsSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.oneTimePayment.HasValue)
|
||||
{
|
||||
contract.OneTimePayment = payload.oneTimePayment.Value;
|
||||
contract.OneTimePaymentSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.accrualProcedure.HasValue)
|
||||
{
|
||||
contract.AccrualProcedure = payload.accrualProcedure.Value;
|
||||
contract.AccrualProcedureSpecified = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(payload.regionCodeFias))
|
||||
{
|
||||
contract.RegionalSettings = new SupplyResourceContractProjectTypeRegionalSettings()
|
||||
{
|
||||
Region = new RegionType()
|
||||
{
|
||||
code = payload.regionCodeFias
|
||||
},
|
||||
Tariff = payload.tariff,
|
||||
Norm = payload.norm
|
||||
};
|
||||
}
|
||||
|
||||
return contract;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.Nsi;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Nsi
|
||||
{
|
||||
internal class ExportDataProviderNsiItemRequest(ClientBase client) : NsiRequestBase(client)
|
||||
{
|
||||
internal async Task<IEnumerable<NsiItemType>> ExecuteAsync(exportDataProviderNsiItemRequestRegistryNumber registryNumber, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/Nsi/exportDataProviderNsiItemRequest.html
|
||||
var request = new exportDataProviderNsiItemRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.2",
|
||||
RegistryNumber = registryNumber
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportDataProviderNsiItemAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<NsiItemType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Hcs.ClientNet/Client/Api/Request/Nsi/NsiRequestBase.cs
Normal file
55
Hcs.ClientNet/Client/Api/Request/Nsi/NsiRequestBase.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.Nsi;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.Nsi
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class NsiPortsTypeAsyncClient : IAsyncClient<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> 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.ClientNet.Api.Request.Nsi
|
||||
{
|
||||
internal class NsiRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
NsiPortsTypeAsyncClient,
|
||||
NsiPortsTypeAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.NsiAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.NsiCommon;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.NsiCommon
|
||||
{
|
||||
internal class ExportNsiItemRequest(ClientBase client) : NsiCommonRequestBase(client)
|
||||
{
|
||||
internal async Task<NsiItemType> ExecuteAsync(int registryNumber, ListGroup listGroup, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/NsiCommon/exportNsiItemRequest.html
|
||||
var request = new exportNsiItemRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.2",
|
||||
RegistryNumber = registryNumber.ToString(),
|
||||
ListGroup = listGroup
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportNsiItemAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Item as NsiItemType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.NsiCommon;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.NsiCommon
|
||||
{
|
||||
internal class ExportNsiListRequest(ClientBase client) : NsiCommonRequestBase(client)
|
||||
{
|
||||
internal async Task<NsiListType> ExecuteAsync(ListGroup listGroup, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/NsiCommon/exportNsiListRequest.html
|
||||
var request = new exportNsiListRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.2",
|
||||
ListGroup = listGroup
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportNsiListAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Item as NsiListType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.NsiCommon;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.NsiCommon
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultOne { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class NsiPortsTypeAsyncClient : IAsyncClient<ISRequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> GetStateAsync(ISRequestHeader 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.ClientNet.Api.Request.NsiCommon
|
||||
{
|
||||
internal class NsiCommonRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
NsiPortsTypeAsyncClient,
|
||||
NsiPortsTypeAsync,
|
||||
ISRequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.NsiCommonAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.OrgRegistryCommon;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.OrgRegistryCommon
|
||||
{
|
||||
internal class ExportDataProviderRequest(ClientBase client) : OrgRegistryCommonRequestBase(client)
|
||||
{
|
||||
internal async Task<IEnumerable<exportDataProviderResultType>> ExecuteAsync(bool isActual, CancellationToken token)
|
||||
{
|
||||
// http://open-gkh.ru/OrganizationsRegistryCommon/exportDataProviderRequest.html
|
||||
var request = new exportDataProviderRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.2.1"
|
||||
};
|
||||
|
||||
if (isActual)
|
||||
{
|
||||
request.IsActual = true;
|
||||
request.IsActualSpecified = true;
|
||||
}
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportDataProviderAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportDataProviderResultType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.OrgRegistryCommon;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.OrgRegistryCommon
|
||||
{
|
||||
internal class ExportOrgRegistryRequest(ClientBase client) : OrgRegistryCommonRequestBase(client)
|
||||
{
|
||||
private const int OGRN_LENGTH = 13;
|
||||
|
||||
internal async Task<IEnumerable<exportOrgRegistryResultType>> ExecuteAsync(string ogrn, string kpp, CancellationToken token)
|
||||
{
|
||||
if (ogrn.Length != OGRN_LENGTH)
|
||||
{
|
||||
throw new System.ArgumentException($"The length of {ogrn} is incorrect");
|
||||
}
|
||||
|
||||
var criteria = new exportOrgRegistryRequestSearchCriteria();
|
||||
if (!string.IsNullOrEmpty(kpp))
|
||||
{
|
||||
criteria.Items = [ogrn, kpp];
|
||||
criteria.ItemsElementName = [ItemsChoiceType3.OGRN, ItemsChoiceType3.KPP];
|
||||
}
|
||||
else
|
||||
{
|
||||
criteria.Items = [ogrn];
|
||||
criteria.ItemsElementName = [ItemsChoiceType3.OGRN];
|
||||
}
|
||||
|
||||
// http://open-gkh.ru/OrganizationsRegistryCommon/exportOrgRegistryRequest.html
|
||||
var request = new exportOrgRegistryRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.2.1",
|
||||
SearchCriteria = [criteria]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.exportOrgRegistryAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
return result.Items.OfType<exportOrgRegistryResultType>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.Service.Async.OrgRegistryCommon;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.Service.Async.OrgRegistryCommon
|
||||
{
|
||||
#pragma warning disable IDE1006
|
||||
public partial class getStateResult : IGetStateResultMany { }
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
public partial class RegOrgPortsTypeAsyncClient : IAsyncClient<ISRequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> GetStateAsync(ISRequestHeader 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.ClientNet.Api.Request.OrgRegistryCommon
|
||||
{
|
||||
internal class OrgRegistryCommonRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
RegOrgPortsTypeAsyncClient,
|
||||
RegOrgPortsTypeAsync,
|
||||
ISRequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.OrgRegistryCommonAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
54
Hcs.ClientNet/Client/Api/Request/PaginationData.cs
Normal file
54
Hcs.ClientNet/Client/Api/Request/PaginationData.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal class PaginationData
|
||||
{
|
||||
/// <summary>
|
||||
/// Состояние, указывающее на то, что это последняя страница
|
||||
/// </summary>
|
||||
internal bool IsLastPage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор следующей страницы
|
||||
/// </summary>
|
||||
internal Guid NextGuid { get; private set; }
|
||||
|
||||
public PaginationData(object item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] item is null");
|
||||
}
|
||||
else if (item is bool boolItem)
|
||||
{
|
||||
if (boolItem == false)
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] item is false");
|
||||
}
|
||||
|
||||
IsLastPage = true;
|
||||
}
|
||||
else if (item is string stringItem)
|
||||
{
|
||||
IsLastPage = false;
|
||||
NextGuid = Guid.Parse(stringItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception($"[{nameof(PaginationData)}] failed to handle item of {item.GetType().FullName} type");
|
||||
}
|
||||
}
|
||||
|
||||
internal static PaginationData CreateLastPageData()
|
||||
{
|
||||
return new PaginationData(true);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{nameof(PaginationData)}] {nameof(IsLastPage)} = {IsLastPage}" +
|
||||
(IsLastPage ? "" : $", {nameof(NextGuid)} = {NextGuid}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
using Hcs.ClientNet.Api.Payload.Payments;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.Payments;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Payments
|
||||
{
|
||||
internal class ImportNotificationsOfOrderExecutionRequest(ClientBase client) : PaymentsRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> ExecuteAsync(ImportNotificationsOfOrderExecutionPayload payload, CancellationToken token)
|
||||
{
|
||||
ThrowIfPayloadIncorrect(payload);
|
||||
|
||||
// http://open-gkh.ru/Payment/importNotificationsOfOrderExecutionRequest.html
|
||||
var request = new importNotificationsOfOrderExecutionRequest
|
||||
{
|
||||
Id = Constants.SIGNED_XML_ELEMENT_ID,
|
||||
version = "10.0.1.1",
|
||||
Items = [GetNotificationFromPayload(payload)]
|
||||
};
|
||||
|
||||
var result = await SendAndWaitResultAsync(request, async asyncClient =>
|
||||
{
|
||||
var response = await asyncClient.importNotificationsOfOrderExecutionAsync(CreateRequestHeader(), request);
|
||||
return response.AckRequest.Ack;
|
||||
}, token);
|
||||
|
||||
result.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
result.Items.OfType<CommonResultType>().ToList().ForEach(commonResult =>
|
||||
{
|
||||
commonResult.Items.OfType<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ThrowIfPayloadIncorrect(ImportNotificationsOfOrderExecutionPayload payload)
|
||||
{
|
||||
if (string.IsNullOrEmpty(payload.orderId))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.orderId)} is empty");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(payload.paymentDocumentGUID))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(payload.paymentDocumentGUID)} is empty");
|
||||
}
|
||||
}
|
||||
|
||||
private importNotificationsOfOrderExecutionRequestNotificationOfOrderExecution139Type GetNotificationFromPayload(ImportNotificationsOfOrderExecutionPayload payload)
|
||||
{
|
||||
var notification = new importNotificationsOfOrderExecutionRequestNotificationOfOrderExecution139Type()
|
||||
{
|
||||
TransportGUID = Guid.NewGuid().ToString(),
|
||||
OrderInfo = new NotificationOfOrderExecution139TypeOrderInfo()
|
||||
{
|
||||
OrderID = payload.orderId,
|
||||
OrderDate = payload.orderDate,
|
||||
Amount = payload.amount,
|
||||
Items = [payload.paymentDocumentId, payload.paymentDocumentGUID],
|
||||
ItemsElementName = [ItemsChoiceType4.PaymentDocumentID, ItemsChoiceType4.PaymentDocumentGUID]
|
||||
}
|
||||
};
|
||||
|
||||
if (payload.onlinePayment.HasValue && payload.onlinePayment.Value)
|
||||
{
|
||||
notification.OrderInfo.OnlinePayment = true;
|
||||
notification.OrderInfo.OnlinePaymentSpecified = true;
|
||||
}
|
||||
|
||||
if (payload.month.HasValue)
|
||||
{
|
||||
notification.OrderInfo.MonthAndYear = new NotificationOfOrderExecution139TypeOrderInfoMonthAndYear()
|
||||
{
|
||||
Year = payload.year.Value,
|
||||
Month = payload.month.Value
|
||||
};
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
using Hcs.ClientNet.Api.Payload.Payments;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.Service.Async.Payments;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request.Payments
|
||||
{
|
||||
internal class ImportSupplierNotificationsOfOrderExecutionRequest(ClientBase client) : PaymentsRequestBase(client)
|
||||
{
|
||||
protected override bool CanBeRestarted => false;
|
||||
|
||||
internal async Task<bool> 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<ErrorMessageType>().ToList().ForEach(error =>
|
||||
{
|
||||
throw RemoteException.CreateNew(error.ErrorCode, error.Description);
|
||||
});
|
||||
|
||||
result.Items.OfType<CommonResultType>().ToList().ForEach(commonResult =>
|
||||
{
|
||||
commonResult.Items.OfType<ErrorMessageType>().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
using Hcs.ClientNet.Api.Request;
|
||||
using Hcs.ClientNet.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<RequestHeader>
|
||||
{
|
||||
public async Task<IGetStateResponse> 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.ClientNet.Api.Request.Payments
|
||||
{
|
||||
internal class PaymentsRequestBase(ClientBase client) :
|
||||
RequestBase<getStateResult,
|
||||
PaymentPortsTypeAsyncClient,
|
||||
PaymentPortsTypeAsync,
|
||||
RequestHeader,
|
||||
AckRequestAck,
|
||||
ErrorMessageType,
|
||||
getStateRequest>(client)
|
||||
{
|
||||
protected override EndPoint EndPoint => EndPoint.PaymentsAsync;
|
||||
|
||||
protected override bool EnableMinimalResponseWaitDelay => true;
|
||||
|
||||
protected override bool CanBeRestarted => true;
|
||||
|
||||
protected override int RestartTimeoutMinutes => 20;
|
||||
}
|
||||
}
|
||||
423
Hcs.ClientNet/Client/Api/Request/RequestBase.cs
Normal file
423
Hcs.ClientNet/Client/Api/Request/RequestBase.cs
Normal file
@ -0,0 +1,423 @@
|
||||
using Hcs.ClientNet.Api.Request.Adapter;
|
||||
using Hcs.ClientNet.Api.Request.Exception;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
using System.ServiceModel.Description;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal abstract class RequestBase<TResult, TAsyncClient, TChannel, TRequestHeader, TAck, TErrorMessage, TGetStateRequest>
|
||||
where TResult : IGetStateResult
|
||||
where TAsyncClient : ClientBase<TChannel>, TChannel, IAsyncClient<TRequestHeader>
|
||||
where TChannel : class
|
||||
where TRequestHeader : class
|
||||
where TAck : IAck
|
||||
where TErrorMessage : IErrorMessage
|
||||
where TGetStateRequest : IGetStateRequest, new()
|
||||
{
|
||||
private const int RESPONSE_WAIT_DELAY_MIN = 2;
|
||||
private const int RESPONSE_WAIT_DELAY_MAX = 5;
|
||||
|
||||
// "[EXP001000] Произошла ошибка при передаче данных. Попробуйте осуществить передачу данных повторно".
|
||||
// Видимо, эту ошибку нельзя включать здесь. Предположительно это маркер DDOS защиты и если отправлять
|
||||
// точно такой же пакет повторно, то ошибка входит в бесконечный цикл - необходимо заново
|
||||
// собирать пакет с новыми кодами и временем и новой подписью. Такую ошибку надо обнаруживать
|
||||
// на более высоком уровне и заново отправлять запрос новым пакетом.
|
||||
private static readonly string[] ignorableSystemErrorMarkers = [
|
||||
"Истекло время ожидания шлюза",
|
||||
"Базовое соединение закрыто: Соединение, которое должно было работать, было разорвано сервером",
|
||||
"Попробуйте осуществить передачу данных повторно",
|
||||
"(502) Недопустимый шлюз",
|
||||
"(503) Сервер не доступен"
|
||||
];
|
||||
|
||||
protected ClientBase client;
|
||||
protected CustomBinding binding;
|
||||
|
||||
protected abstract EndPoint EndPoint { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Для запросов, возвращающих мало данных, можно попробовать сократить
|
||||
/// начальный период ожидания подготовки ответа
|
||||
/// </summary>
|
||||
protected abstract bool EnableMinimalResponseWaitDelay { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Указывает на то, что можно ли этот метод перезапускать в случае зависания
|
||||
/// ожидания или в случае сбоя на сервере
|
||||
/// </summary>
|
||||
protected abstract bool CanBeRestarted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Для противодействия зависанию ожидания вводится предел ожидания в минутах
|
||||
/// для запросов, которые можно перезапустить заново с теми же параметрами
|
||||
/// </summary>
|
||||
protected abstract int RestartTimeoutMinutes { get; }
|
||||
|
||||
private EndpointAddress RemoteAddress => new(client.ComposeEndpointUri(EndPointLocator.GetPath(EndPoint)));
|
||||
|
||||
private string ThreadIdText => $"(Thread #{ThreadId})";
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает идентификатор текущего исполняемого потока
|
||||
/// </summary>
|
||||
private int ThreadId => Environment.CurrentManagedThreadId;
|
||||
|
||||
public RequestBase(ClientBase client)
|
||||
{
|
||||
this.client = client;
|
||||
|
||||
ConfigureBinding();
|
||||
}
|
||||
|
||||
private void ConfigureBinding()
|
||||
{
|
||||
binding = new CustomBinding
|
||||
{
|
||||
CloseTimeout = TimeSpan.FromSeconds(180),
|
||||
OpenTimeout = TimeSpan.FromSeconds(180),
|
||||
ReceiveTimeout = TimeSpan.FromSeconds(180),
|
||||
SendTimeout = TimeSpan.FromSeconds(180)
|
||||
};
|
||||
|
||||
binding.Elements.Add(new TextMessageEncodingBindingElement
|
||||
{
|
||||
MessageVersion = MessageVersion.Soap11,
|
||||
WriteEncoding = Encoding.UTF8
|
||||
});
|
||||
|
||||
if (client.UseTunnel)
|
||||
{
|
||||
if (!System.Diagnostics.Process.GetProcessesByName("stunnel").Any())
|
||||
{
|
||||
throw new System.Exception("stunnel is not running");
|
||||
}
|
||||
|
||||
binding.Elements.Add(new HttpTransportBindingElement
|
||||
{
|
||||
AuthenticationScheme = (client.IsPPAK ? System.Net.AuthenticationSchemes.Digest : System.Net.AuthenticationSchemes.Basic),
|
||||
MaxReceivedMessageSize = int.MaxValue,
|
||||
UseDefaultWebProxy = false
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
binding.Elements.Add(new HttpsTransportBindingElement
|
||||
{
|
||||
AuthenticationScheme = (client.IsPPAK ? System.Net.AuthenticationSchemes.Digest : System.Net.AuthenticationSchemes.Basic),
|
||||
MaxReceivedMessageSize = int.MaxValue,
|
||||
RequireClientCertificate = true,
|
||||
UseDefaultWebProxy = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task<TResult> SendAndWaitResultAsync(
|
||||
object request,
|
||||
Func<TAsyncClient, Task<TAck>> sender,
|
||||
CancellationToken token)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CanBeRestarted)
|
||||
{
|
||||
return await RunRepeatableTaskInsistentlyAsync(
|
||||
async () => await ExecuteSendAndWaitResultAsync(request, sender, token), token);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await ExecuteSendAndWaitResultAsync(request, sender, token);
|
||||
}
|
||||
}
|
||||
catch (RestartTimeoutException e)
|
||||
{
|
||||
if (!CanBeRestarted)
|
||||
{
|
||||
throw new System.Exception("Cannot restart request execution on timeout", e);
|
||||
}
|
||||
|
||||
client.TryLog($"Restarting {request.GetType().Name} request execution...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Для запросов к серверу которые можно направлять несколько раз, разрешаем
|
||||
/// серверу аномально отказаться. Предполагается, что здесь мы игнорируем
|
||||
/// только жесткие отказы серверной инфраструктуры, которые указывают
|
||||
/// что запрос даже не был принят в обработку. Также все запросы на
|
||||
/// чтение можно повторять в случае их серверных системных ошибок.
|
||||
/// </summary>
|
||||
protected async Task<TRepeatableResult> RunRepeatableTaskInsistentlyAsync<TRepeatableResult>(
|
||||
Func<Task<TRepeatableResult>> func, CancellationToken token)
|
||||
{
|
||||
var afterErrorDelaySec = 120;
|
||||
for (var attempt = 1; ; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await func();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
if (CanIgnoreSuchException(e, out string marker))
|
||||
{
|
||||
client.TryLog($"Ignoring error of attempt #{attempt} with type [{marker}]");
|
||||
client.TryLog($"Waiting {afterErrorDelaySec} sec until next attempt...");
|
||||
|
||||
await Task.Delay(afterErrorDelaySec * 1000, token);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e is RestartTimeoutException)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (e is RemoteException)
|
||||
{
|
||||
throw RemoteException.CreateNew(e as RemoteException);
|
||||
}
|
||||
|
||||
throw new System.Exception("Cannot ignore this exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanIgnoreSuchException(System.Exception e, out string resultMarker)
|
||||
{
|
||||
foreach (var marker in ignorableSystemErrorMarkers)
|
||||
{
|
||||
var found = Util.EnumerateInnerExceptions(e).Find(
|
||||
x => x.Message != null && x.Message.Contains(marker));
|
||||
if (found != null)
|
||||
{
|
||||
resultMarker = marker;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
resultMarker = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<TResult> ExecuteSendAndWaitResultAsync(
|
||||
object request,
|
||||
Func<TAsyncClient, Task<TAck>> sender,
|
||||
CancellationToken token)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
var version = RequestHelper.GetRequestVersionString(request);
|
||||
client.TryLog($"Executing request {RemoteAddress.Uri}/{request.GetType().Name} of version {version}...");
|
||||
|
||||
TAck ack;
|
||||
|
||||
var stopWatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
using (var asyncClient = CreateAsyncClient())
|
||||
{
|
||||
ack = await sender(asyncClient);
|
||||
}
|
||||
stopWatch.Stop();
|
||||
|
||||
client.TryLog($"Request executed in {stopWatch.ElapsedMilliseconds} ms, result GUID is {ack.MessageGUID}");
|
||||
|
||||
var result = await WaitForResultAsync(ack, true, token);
|
||||
if (result is IQueryable queryableResult)
|
||||
{
|
||||
queryableResult.OfType<TErrorMessage>().ToList().ForEach(x =>
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
});
|
||||
}
|
||||
else if (result is TErrorMessage x)
|
||||
{
|
||||
throw RemoteException.CreateNew(x.ErrorCode, x.Description);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TAsyncClient CreateAsyncClient()
|
||||
{
|
||||
var asyncClient = (TAsyncClient)Activator.CreateInstance(typeof(TAsyncClient), binding, RemoteAddress);
|
||||
ConfigureEndpointCredentials(asyncClient.Endpoint, asyncClient.ClientCredentials);
|
||||
return asyncClient;
|
||||
}
|
||||
|
||||
private void ConfigureEndpointCredentials(
|
||||
ServiceEndpoint serviceEndpoint, ClientCredentials clientCredentials)
|
||||
{
|
||||
serviceEndpoint.EndpointBehaviors.Add(new GostSigningEndpointBehavior(client));
|
||||
|
||||
if (!client.IsPPAK)
|
||||
{
|
||||
clientCredentials.UserName.UserName = Constants.NAME_SIT;
|
||||
clientCredentials.UserName.Password = Constants.PASSWORD_SIT;
|
||||
}
|
||||
|
||||
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate (
|
||||
object sender, X509Certificate serverCertificate, X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!client.UseTunnel)
|
||||
{
|
||||
clientCredentials.ClientCertificate.SetCertificate(
|
||||
StoreLocation.CurrentUser,
|
||||
StoreName.My,
|
||||
X509FindType.FindByThumbprint,
|
||||
client.CertificateThumbprint);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Основной алгоритм ожидания ответа на асинхронный запрос.
|
||||
/// Из документации ГИС ЖКХ:
|
||||
/// Также рекомендуем придерживаться следующего алгоритма отправки запросов на получение статуса обработки пакета в случае использования асинхронных сервисов ГИС ЖКХ (в рамках одного MessageGUID):
|
||||
/// - первый запрос getState направлять не ранее чем через 10 секунд, после получения квитанции о приеме пакета с бизнес-данными от сервиса ГИС КЖХ;
|
||||
/// - в случае, если на первый запрос getSate получен результат с RequestState равным "1" или "2", то следующий запрос getState необходимо направлять не ранее чем через 60 секунд после отправки предыдущего запроса;
|
||||
/// - в случае, если на второй запрос getSate получен результат с RequestState равным "1" или "2", то следующий запрос getState необходимо направлять не ранее чем через 300 секунд после отправки предыдущего запроса;
|
||||
/// - в случае, если на третий запрос getSate получен результат с RequestState равным "1" или "2", то следующий запрос getState необходимо направлять не ранее чем через 900 секунд после отправки предыдущего запроса;
|
||||
/// - в случае, если на четвертый(и все последующие запросы) getState получен результат с RequestState равным "1" или "2", то следующий запрос getState необходимо направлять не ранее чем через 1800 секунд после отправки предыдущего запроса.
|
||||
/// </summary>
|
||||
private async Task<TResult> WaitForResultAsync(
|
||||
TAck ack, bool withInitialDelay, CancellationToken token)
|
||||
{
|
||||
TResult result;
|
||||
|
||||
var startTime = DateTime.Now;
|
||||
for (var attempts = 1; ; attempts++)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var delaySec = EnableMinimalResponseWaitDelay ? RESPONSE_WAIT_DELAY_MIN : RESPONSE_WAIT_DELAY_MAX;
|
||||
if (attempts >= 2)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX;
|
||||
}
|
||||
if (attempts >= 3)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX * 2;
|
||||
}
|
||||
if (attempts >= 5)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX * 4;
|
||||
}
|
||||
if (attempts >= 7)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX * 8;
|
||||
}
|
||||
if (attempts >= 9)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX * 16;
|
||||
}
|
||||
if (attempts >= 12)
|
||||
{
|
||||
delaySec = RESPONSE_WAIT_DELAY_MAX * 60;
|
||||
}
|
||||
|
||||
if (attempts > 1 || withInitialDelay)
|
||||
{
|
||||
var minutesElapsed = (int)(DateTime.Now - startTime).TotalMinutes;
|
||||
if (CanBeRestarted && minutesElapsed > RestartTimeoutMinutes)
|
||||
{
|
||||
throw new RestartTimeoutException($"{RestartTimeoutMinutes} minute(s) wait time exceeded");
|
||||
}
|
||||
|
||||
client.TryLog($"Waiting {delaySec} sec for attempt #{attempts}" +
|
||||
$" to get response ({minutesElapsed} minute(s) elapsed)...");
|
||||
|
||||
await Task.Delay(delaySec * 1000, token);
|
||||
}
|
||||
|
||||
client.TryLog($"Requesting response, attempt #{attempts} in {ThreadIdText}...");
|
||||
|
||||
result = await TryGetResultAsync(ack);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
client.TryLog($"Response received!");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет однократную проверку наличия результата.
|
||||
/// Возвращает default если результата еще нет.
|
||||
/// </summary>
|
||||
private async Task<TResult> TryGetResultAsync(TAck ack)
|
||||
{
|
||||
using var asyncClient = CreateAsyncClient();
|
||||
var requestHeader = RequestHelper.CreateHeader<TRequestHeader>(client);
|
||||
var requestBody = new TGetStateRequest
|
||||
{
|
||||
MessageGUID = ack.MessageGUID
|
||||
};
|
||||
var response = await asyncClient.GetStateAsync(requestHeader, requestBody);
|
||||
var result = response.GetStateResult;
|
||||
if (result.RequestState == (int)AsyncRequestStateType.Ready)
|
||||
{
|
||||
return (TResult)result;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
protected TRequestHeader CreateRequestHeader()
|
||||
{
|
||||
return RequestHelper.CreateHeader<TRequestHeader>(client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Исполнение повторяемой операции некоторое допустимое число ошибок
|
||||
/// </summary>
|
||||
protected async Task<TRepeatableResult> RunRepeatableTaskAsync<TRepeatableResult>(
|
||||
Func<Task<TRepeatableResult>> taskFunc, Func<System.Exception, bool> canIgnoreFunc, int maxAttempts)
|
||||
{
|
||||
for (var attempts = 1; ; attempts++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await taskFunc();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
if (canIgnoreFunc(e))
|
||||
{
|
||||
if (attempts < maxAttempts)
|
||||
{
|
||||
client.TryLog($"Ignoring error of attempt #{attempts} of {maxAttempts} attempts");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new System.Exception("Too much attempts with error");
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Hcs.ClientNet/Client/Api/Request/RequestHelper.cs
Normal file
101
Hcs.ClientNet/Client/Api/Request/RequestHelper.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal static class RequestHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Подготовка заголовка сообщения отправляемого в ГИС ЖКХ с обязательными атрибутами.
|
||||
/// Заголовки могут быть разного типа для разных типов сообщений, но имена полей одинаковые.
|
||||
/// </summary>
|
||||
internal static THeader CreateHeader<THeader>(ClientBase client) where THeader : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var instance = Activator.CreateInstance(typeof(THeader));
|
||||
foreach (var prop in instance.GetType().GetProperties())
|
||||
{
|
||||
switch (prop.Name)
|
||||
{
|
||||
case "Item":
|
||||
prop.SetValue(instance, client.OrgPPAGUID);
|
||||
break;
|
||||
|
||||
case "ItemElementName":
|
||||
prop.SetValue(instance, Enum.Parse(prop.PropertyType, "orgPPAGUID"));
|
||||
break;
|
||||
|
||||
case "MessageGUID":
|
||||
prop.SetValue(instance, Guid.NewGuid().ToString());
|
||||
break;
|
||||
|
||||
case "Date":
|
||||
prop.SetValue(instance, DateTime.Now);
|
||||
break;
|
||||
|
||||
case "IsOperatorSignatureSpecified":
|
||||
if (client.Role == OrganizationRole.RC || client.Role == OrganizationRole.RSO)
|
||||
{
|
||||
prop.SetValue(instance, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case "IsOperatorSignature":
|
||||
if (client.Role == OrganizationRole.RC || client.Role == OrganizationRole.RSO)
|
||||
{
|
||||
prop.SetValue(instance, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return instance as THeader;
|
||||
}
|
||||
catch (ArgumentNullException e)
|
||||
{
|
||||
throw new ApplicationException($"Error occured while building request header: {e.Message}");
|
||||
}
|
||||
catch (SystemException e)
|
||||
{
|
||||
throw new ApplicationException($"Error occured while building request header: {e.GetBaseException().Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Для объекта запроса возвращает значение строки свойства version
|
||||
/// </summary>
|
||||
internal static string GetRequestVersionString(object requestObject)
|
||||
{
|
||||
if (requestObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var versionHost = requestObject;
|
||||
if (versionHost != null)
|
||||
{
|
||||
var versionProperty = versionHost.GetType().GetProperties().FirstOrDefault(x => x.Name == "version");
|
||||
if (versionProperty != null)
|
||||
{
|
||||
return versionProperty.GetValue(versionHost) as string;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var field in requestObject.GetType().GetFields())
|
||||
{
|
||||
versionHost = field.GetValue(requestObject);
|
||||
if (versionHost != null)
|
||||
{
|
||||
var versionProperty = versionHost.GetType().GetProperties().FirstOrDefault(x => x.Name == "version");
|
||||
if (versionProperty != null)
|
||||
{
|
||||
return versionProperty.GetValue(versionHost) as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Hcs.ClientNet/Client/Api/Request/X509Tools.cs
Normal file
172
Hcs.ClientNet/Client/Api/Request/X509Tools.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Request
|
||||
{
|
||||
internal class X509Tools
|
||||
{
|
||||
private const string PRIVATE_KEY_USAGE_PERIOD = "2.5.29.16";
|
||||
|
||||
public static string GetFullnameWithExpirationDateStr(X509Certificate2 x509cert)
|
||||
{
|
||||
var (фамилия, имя, отчество) = GetFullname(x509cert);
|
||||
return (string.IsNullOrEmpty(фамилия) ? "" : $"{фамилия} ") + (string.IsNullOrEmpty(имя) ? "" : $"{имя} ") +
|
||||
(string.IsNullOrEmpty(отчество) ? "" : $"{отчество} ") + "until " + GetNotAfterDate(x509cert).ToString("dd.MM.yyyy");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает массив из трех строк, содержащих соответственно Фамилию, Имя и Отчество
|
||||
/// полученных из данных сертификата. Если сертификат не содержит ФИО, то возвращается массив
|
||||
/// из трех пустых строк. Это не точный метод определять имя, он предполагает, что
|
||||
/// поля SN, G, CN содержат ФИО в определенном порядке, что правдоподобно но не обязательно.
|
||||
/// </summary>
|
||||
private static (string Фамилия, string Имя, string Отчество) GetFullname(X509Certificate2 x509cert)
|
||||
{
|
||||
string фам = "", имя = "", отч = "";
|
||||
|
||||
// Сначала ищем поля surname (SN) и given-name (G)
|
||||
var sn = DecodeSubjectField(x509cert, "SN");
|
||||
var g = DecodeSubjectField(x509cert, "G");
|
||||
if (!string.IsNullOrEmpty(sn) && !string.IsNullOrEmpty(g))
|
||||
{
|
||||
фам = sn;
|
||||
|
||||
var gParts = g.Split(' ');
|
||||
if (gParts != null && gParts.Length >= 1)
|
||||
{
|
||||
имя = gParts[0];
|
||||
}
|
||||
if (gParts != null && gParts.Length >= 2)
|
||||
{
|
||||
отч = gParts[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Иначе берем три первых слова из common name (CN), игнорируя кавычки
|
||||
var cn = DecodeSubjectField(x509cert, "CN");
|
||||
if (!string.IsNullOrEmpty(cn))
|
||||
{
|
||||
cn = new StringBuilder(cn).Replace("\"", "").ToString();
|
||||
|
||||
char[] separators = [' ', ';'];
|
||||
var cnParts = cn.Split(separators);
|
||||
if (cnParts != null && cnParts.Length >= 1)
|
||||
{
|
||||
фам = cnParts[0];
|
||||
}
|
||||
if (cnParts != null && cnParts.Length >= 2)
|
||||
{
|
||||
имя = cnParts[1];
|
||||
}
|
||||
if (cnParts != null && cnParts.Length >= 3)
|
||||
{
|
||||
отч = cnParts[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (фам, имя, отч);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает значение поля с именем @subName включенного в различимое имя Subject
|
||||
/// </summary>
|
||||
private static string DecodeSubjectField(X509Certificate2 x509cert, string subName)
|
||||
{
|
||||
// Чтобы посмотреть все поля сертификата
|
||||
//System.Diagnostics.Trace.WriteLine("x509decode = " + x509cert.SubjectName.Decode(
|
||||
//X500DistinguishedNameFlags.UseNewLines));
|
||||
|
||||
// Декодируем различимое имя на отдельные строки через переводы строк для надежности разбора
|
||||
var decoded = x509cert.SubjectName.Decode(X500DistinguishedNameFlags.UseNewLines);
|
||||
char[] separators = ['\n', '\r'];
|
||||
var parts = decoded.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Каждая часть начинается с имени и отделяется от значения символом равно
|
||||
foreach (var part in parts)
|
||||
{
|
||||
if (part.Length <= subName.Length + 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part.StartsWith(subName) && part[subName.Length] == '=')
|
||||
{
|
||||
return part.Substring(subName.Length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Возвращает дату окончания действия сертификата
|
||||
/// </summary>
|
||||
private static DateTime GetNotAfterDate(X509Certificate2 x509cert)
|
||||
{
|
||||
// Сначала пытаемся определить срок первичного ключа, а затем уже самого сертификата
|
||||
var датаОкончания = GetPrivateKeyUsageEndDate(x509cert);
|
||||
if (датаОкончания != null)
|
||||
{
|
||||
return (DateTime)датаОкончания;
|
||||
}
|
||||
return x509cert.NotAfter;
|
||||
}
|
||||
|
||||
private static DateTime? GetPrivateKeyUsageEndDate(X509Certificate2 x509cert)
|
||||
{
|
||||
foreach (var ext in x509cert.Extensions)
|
||||
{
|
||||
if (ext.Oid.Value == PRIVATE_KEY_USAGE_PERIOD)
|
||||
{
|
||||
// Дата начала с индексом 0, дата окончания с индексом 1
|
||||
return ParseAsn1Datetime(ext, 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Разбирает значение типа дата из серии значений ASN1 присоединенных к расширению
|
||||
/// </summary>
|
||||
private static DateTime? ParseAsn1Datetime(X509Extension ext, int valueIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var asnObject = (new Asn1InputStream(ext.RawData)).ReadObject();
|
||||
if (asnObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var asnSequence = Asn1Sequence.GetInstance(asnObject);
|
||||
if (asnSequence.Count <= valueIndex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var asn = (Asn1TaggedObject)asnSequence[valueIndex];
|
||||
var asnStr = Asn1OctetString.GetInstance(asn, false);
|
||||
var s = Encoding.UTF8.GetString(asnStr.GetOctets());
|
||||
var year = int.Parse(s.Substring(0, 4));
|
||||
var month = int.Parse(s.Substring(4, 2));
|
||||
var day = int.Parse(s.Substring(6, 2));
|
||||
var hour = int.Parse(s.Substring(8, 2));
|
||||
var minute = int.Parse(s.Substring(10, 2));
|
||||
var second = int.Parse(s.Substring(12, 2));
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
using Hcs.Service.Async.Bills;
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Api.Type
|
||||
{
|
||||
// http://open-gkh.ru/Bills/PDServiceChargeType/MunicipalService/Consumption/Volume/determiningMethod.html
|
||||
public enum MunicipalServiceVolumeDeterminingMethod
|
||||
{
|
||||
Norm,
|
||||
MeteringDevice,
|
||||
Other
|
||||
}
|
||||
|
||||
internal static class MunicipalServiceVolumeDeterminingMethodExtensions
|
||||
{
|
||||
internal static PDServiceChargeTypeMunicipalServiceVolumeDeterminingMethod ToServiceType(this MunicipalServiceVolumeDeterminingMethod type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MunicipalServiceVolumeDeterminingMethod.Norm:
|
||||
return PDServiceChargeTypeMunicipalServiceVolumeDeterminingMethod.N;
|
||||
|
||||
case MunicipalServiceVolumeDeterminingMethod.MeteringDevice:
|
||||
return PDServiceChargeTypeMunicipalServiceVolumeDeterminingMethod.M;
|
||||
|
||||
case MunicipalServiceVolumeDeterminingMethod.Other:
|
||||
return PDServiceChargeTypeMunicipalServiceVolumeDeterminingMethod.O;
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"Cannot convert {type} to service type");
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Hcs.ClientNet/Client/ClientBase.cs
Normal file
97
Hcs.ClientNet/Client/ClientBase.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using Hcs.ClientNet.Internal;
|
||||
using Hcs.ClientNet.Logger;
|
||||
using Hcs.ClientNet.MessageCapturer;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Hcs.ClientNet
|
||||
{
|
||||
public abstract class ClientBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Идентификатор поставщика данных ГИС ЖКХ
|
||||
/// </summary>
|
||||
public string OrgPPAGUID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Исполнитель/сотрудник ГИС ЖКХ, от которого будут регистрироваться ответы
|
||||
/// </summary>
|
||||
public string ExecutorGUID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Признак, указывающий на то, что используется ли внешний туннель (stunnel)
|
||||
/// </summary>
|
||||
public bool UseTunnel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Если true, то запросы будут выполняться на промышленном стенде, иначе - на тестовом
|
||||
/// </summary>
|
||||
public bool IsPPAK { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Роль
|
||||
/// </summary>
|
||||
public OrganizationRole Role { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливаемый пользователем приемник отладочных сообщений
|
||||
/// </summary>
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливаемый пользователем механизм перехвата содержимого отправляемых
|
||||
/// и принимаемых пакетов
|
||||
/// </summary>
|
||||
public IMessageCapturer MessageCapturer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Сертификат клиента для применения при формировании запросов
|
||||
/// </summary>
|
||||
internal X509Certificate2 Certificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Тип криптопровайдера, полученный из сертификата
|
||||
/// </summary>
|
||||
internal GostXades.CryptoProviderTypeEnum CryptoProviderType { get; set; }
|
||||
|
||||
internal GostCryptography.Base.ProviderType GostCryptoProviderType =>
|
||||
(GostCryptography.Base.ProviderType)CryptoProviderType;
|
||||
|
||||
/// <summary>
|
||||
/// Отпечаток сертификата
|
||||
/// </summary>
|
||||
internal string CertificateThumbprint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Пароль доступа к сертификату
|
||||
/// </summary>
|
||||
internal string CertificatePassword { get; set; }
|
||||
|
||||
internal string ComposeEndpointUri(string endpointName)
|
||||
{
|
||||
if (UseTunnel)
|
||||
{
|
||||
return $"http://{Constants.URI_TUNNEL}/{endpointName}";
|
||||
}
|
||||
|
||||
return IsPPAK
|
||||
? $"https://{Constants.URI_PPAK}/{endpointName}"
|
||||
: $"https://{Constants.URI_SIT_02}/{endpointName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Пробует вывести сообщение в установленный приемник отладочных сообщений
|
||||
/// </summary>
|
||||
internal void TryLog(string message)
|
||||
{
|
||||
Logger?.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Пробует отправить тело сообщения в установленный перехватчик
|
||||
/// </summary>
|
||||
internal void TryCaptureMessage(bool sent, string messageBody)
|
||||
{
|
||||
MessageCapturer?.CaptureMessage(sent, messageBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
Hcs.ClientNet/Client/Internal/CertificateHelper.cs
Normal file
81
Hcs.ClientNet/Client/Internal/CertificateHelper.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using GostCryptography.Base;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Hcs.ClientNet.Internal
|
||||
{
|
||||
internal static class CertificateHelper
|
||||
{
|
||||
internal static bool IsGostPrivateKey(this X509Certificate2 certificate)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (certificate.HasPrivateKey)
|
||||
{
|
||||
var cspInfo = certificate.GetPrivateKeyInfo();
|
||||
if (cspInfo.ProviderType == (int)ProviderType.CryptoPro ||
|
||||
cspInfo.ProviderType == (int)ProviderType.VipNet ||
|
||||
cspInfo.ProviderType == (int)ProviderType.CryptoPro_2012_512 ||
|
||||
cspInfo.ProviderType == (int)ProviderType.CryptoPro_2012_1024)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static GostXades.CryptoProviderTypeEnum GetProviderType(this X509Certificate2 certificate)
|
||||
{
|
||||
return (GostXades.CryptoProviderTypeEnum)GetProviderInfo(certificate).Item1;
|
||||
}
|
||||
|
||||
internal static Tuple<int, string> GetProviderInfo(this X509Certificate2 certificate)
|
||||
{
|
||||
if (certificate.HasPrivateKey)
|
||||
{
|
||||
var cspInfo = certificate.GetPrivateKeyInfo();
|
||||
return new Tuple<int, string>(cspInfo.ProviderType, cspInfo.ProviderName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Certificate has no private key");
|
||||
}
|
||||
}
|
||||
|
||||
internal static X509Certificate2 FindCertificate(Func<X509Certificate2, bool> predicate)
|
||||
{
|
||||
if (predicate == null)
|
||||
{
|
||||
throw new ArgumentException("Null subject predicate");
|
||||
}
|
||||
|
||||
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
|
||||
try
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
|
||||
|
||||
var collection = store.Certificates
|
||||
.OfType<X509Certificate2>()
|
||||
.Where(x => x.HasPrivateKey && x.IsGostPrivateKey());
|
||||
|
||||
var now = DateTime.Now;
|
||||
return collection.First(
|
||||
x => now >= x.NotBefore && now <= x.NotAfter && predicate(x));
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Hcs.ClientNet/Client/Internal/Constants.cs
Normal file
25
Hcs.ClientNet/Client/Internal/Constants.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Hcs.ClientNet.Internal
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Имя XML-элемента в сообщении, которое будет подписываться в фильтре
|
||||
/// отправки подписывающем XML
|
||||
/// </summary>
|
||||
internal const string SIGNED_XML_ELEMENT_ID = "signed-data-container";
|
||||
|
||||
/// <summary>
|
||||
/// Если PIN сертификата не указан пользователем, применяется это значение
|
||||
/// по умолчанию для сертификатов RuToken
|
||||
/// </summary>
|
||||
internal const string DEFAULT_CERTIFICATE_PIN = "12345678";
|
||||
|
||||
internal const string URI_PPAK = "api.dom.gosuslugi.ru";
|
||||
internal const string URI_SIT_01 = "sit01.dom.test.gosuslugi.ru:10081";
|
||||
internal const string URI_SIT_02 = "sit02.dom.test.gosuslugi.ru:10081";
|
||||
internal const string URI_TUNNEL = "127.0.0.1:8080";
|
||||
|
||||
internal const string NAME_SIT = "sit";
|
||||
internal const string PASSWORD_SIT = "xw{p&&Ee3b9r8?amJv*]";
|
||||
}
|
||||
}
|
||||
63
Hcs.ClientNet/Client/Internal/Util.cs
Normal file
63
Hcs.ClientNet/Client/Internal/Util.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Hcs.ClientNet.Internal
|
||||
{
|
||||
internal static class Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Возвращает список все вложенных исключений для данного исключения
|
||||
/// </summary>
|
||||
internal static List<Exception> EnumerateInnerExceptions(Exception e)
|
||||
{
|
||||
var list = new List<Exception>();
|
||||
WalkInnerExceptionsRecurse(e, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static void WalkInnerExceptionsRecurse(Exception e, List<Exception> list)
|
||||
{
|
||||
if (e == null || list.Contains(e))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(e);
|
||||
|
||||
WalkInnerExceptionsRecurse(e.InnerException, list);
|
||||
|
||||
if (e is AggregateException)
|
||||
{
|
||||
var aggregate = e as AggregateException;
|
||||
foreach (var inner in aggregate.InnerExceptions)
|
||||
{
|
||||
WalkInnerExceptionsRecurse(inner, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static string FormatDate(DateTime date)
|
||||
{
|
||||
return date.ToString("yyyyMMdd");
|
||||
}
|
||||
|
||||
internal static string FormatDate(DateTime? date)
|
||||
{
|
||||
return (date == null) ? string.Empty : FormatDate((DateTime)date);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Преобразует массиб байтов в строку в формате binhex
|
||||
/// </summary>
|
||||
internal static string ConvertToHexString(byte[] ba)
|
||||
{
|
||||
var buf = new StringBuilder(ba.Length * 2);
|
||||
foreach (byte b in ba)
|
||||
{
|
||||
buf.AppendFormat("{0:x2}", b);
|
||||
}
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
Hcs.ClientNet/Client/Logger/ActionLogger.cs
Normal file
14
Hcs.ClientNet/Client/Logger/ActionLogger.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Logger
|
||||
{
|
||||
public class ActionLogger : ILogger
|
||||
{
|
||||
public Action<string> OnWriteLine;
|
||||
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
OnWriteLine?.Invoke(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Hcs.ClientNet/Client/Logger/ConsoleLogger.cs
Normal file
12
Hcs.ClientNet/Client/Logger/ConsoleLogger.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Hcs.ClientNet.Logger
|
||||
{
|
||||
public class ConsoleLogger : ILogger
|
||||
{
|
||||
public void WriteLine(string message)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Hcs.ClientNet/Client/Logger/ILogger.cs
Normal file
10
Hcs.ClientNet/Client/Logger/ILogger.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Hcs.ClientNet.Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для механизма вывода отладочных сообщений для обработки вызывающей системой
|
||||
/// </summary>
|
||||
public interface ILogger
|
||||
{
|
||||
internal void WriteLine(string message);
|
||||
}
|
||||
}
|
||||
43
Hcs.ClientNet/Client/MessageCapturer/FileMessageCapturer.cs
Normal file
43
Hcs.ClientNet/Client/MessageCapturer/FileMessageCapturer.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Hcs.ClientNet.Logger;
|
||||
using System.Text;
|
||||
|
||||
namespace Hcs.ClientNet.MessageCapturer
|
||||
{
|
||||
/// <summary>
|
||||
/// Реализация механизма захвата содержимого сообщений SOAP, записывающая
|
||||
/// каждое сообщение в отдельный файл на диске
|
||||
/// </summary>
|
||||
public class FileMessageCapturer(string directory, ILogger logger) : IMessageCapturer
|
||||
{
|
||||
private readonly string directory = directory;
|
||||
private readonly ILogger logger = logger;
|
||||
|
||||
public void CaptureMessage(bool sent, string messageBody)
|
||||
{
|
||||
var index = 0;
|
||||
var maxIndex = 1000000;
|
||||
string fileName;
|
||||
do
|
||||
{
|
||||
index += 1;
|
||||
|
||||
if (index > maxIndex)
|
||||
{
|
||||
throw new System.Exception("index value exceeds maxIndex value");
|
||||
}
|
||||
|
||||
fileName = index.ToString("D3") + "_" + (sent ? "message" : "response") + ".xml";
|
||||
|
||||
if (!string.IsNullOrEmpty(directory))
|
||||
{
|
||||
fileName = System.IO.Path.Combine(directory, fileName);
|
||||
}
|
||||
}
|
||||
while (System.IO.File.Exists(fileName));
|
||||
|
||||
logger?.WriteLine($"Capturing message to file {fileName}...");
|
||||
|
||||
System.IO.File.WriteAllText(fileName, messageBody, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Hcs.ClientNet/Client/MessageCapturer/IMessageCapturer.cs
Normal file
11
Hcs.ClientNet/Client/MessageCapturer/IMessageCapturer.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Hcs.ClientNet.MessageCapturer
|
||||
{
|
||||
/// <summary>
|
||||
/// Интерфейс для механизма захвата отправляемых и принимаемых
|
||||
/// SOAP сообщений в ходе коммуникации с ГИС ЖКХ
|
||||
/// </summary>
|
||||
public interface IMessageCapturer
|
||||
{
|
||||
internal void CaptureMessage(bool sent, string messageBody);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Hcs.ClientNet.MessageCapturer
|
||||
{
|
||||
/// <summary>
|
||||
/// Реализация захвата содержимого отправляемых и принимаемых SOAP сообщений,
|
||||
/// которая хранит данные в памяти
|
||||
/// </summary>
|
||||
public class MemoryMessageCapturer : IMessageCapturer
|
||||
{
|
||||
private readonly MemoryStream messageCaptureStream;
|
||||
private readonly StreamWriter messageCaptureWriter;
|
||||
|
||||
public MemoryMessageCapturer()
|
||||
{
|
||||
messageCaptureStream = new MemoryStream();
|
||||
messageCaptureWriter = new StreamWriter(messageCaptureStream, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public void CaptureMessage(bool sent, string messageBody)
|
||||
{
|
||||
if (messageCaptureStream.Position > 0)
|
||||
{
|
||||
messageCaptureWriter.WriteLine("");
|
||||
}
|
||||
|
||||
messageCaptureWriter.Write("<!--");
|
||||
messageCaptureWriter.Write(sent ? "SENT " : "RECEIVED ");
|
||||
messageCaptureWriter.Write(DateTime.Now.ToString());
|
||||
messageCaptureWriter.WriteLine("-->");
|
||||
messageCaptureWriter.Write(messageBody);
|
||||
messageCaptureWriter.Flush();
|
||||
}
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
var buf = messageCaptureStream.GetBuffer();
|
||||
var size = (int)messageCaptureStream.Length;
|
||||
var data = new byte[size];
|
||||
Buffer.BlockCopy(buf, 0, data, 0, size);
|
||||
return data;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Encoding.UTF8.GetString(GetData());
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Hcs.ClientNet/Client/OrganizationRole.cs
Normal file
20
Hcs.ClientNet/Client/OrganizationRole.cs
Normal file
@ -0,0 +1,20 @@
|
||||
namespace Hcs.ClientNet
|
||||
{
|
||||
public enum OrganizationRole
|
||||
{
|
||||
/// <summary>
|
||||
/// УК/ТСЖ/ЖСК
|
||||
/// </summary>
|
||||
UK,
|
||||
|
||||
/// <summary>
|
||||
/// Ресурсоснабжающая организация
|
||||
/// </summary>
|
||||
RSO,
|
||||
|
||||
/// <summary>
|
||||
/// Расчетный центр
|
||||
/// </summary>
|
||||
RC
|
||||
}
|
||||
}
|
||||
59
Hcs.ClientNet/Client/UniClient.cs
Normal file
59
Hcs.ClientNet/Client/UniClient.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using GostCryptography.Gost_R3411;
|
||||
using Hcs.ClientNet.Api;
|
||||
using Hcs.ClientNet.Internal;
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Hcs.ClientNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Универсальный клиент для вызова всех реализованных функций интеграции с ГИС ЖКХ
|
||||
/// </summary>
|
||||
public class UniClient : ClientBase
|
||||
{
|
||||
public BillsApi Bills => new(this);
|
||||
|
||||
public DeviceMeteringApi DeviceMetering => new(this);
|
||||
|
||||
public HouseManagementApi HouseManagement => new(this);
|
||||
|
||||
public NsiApi Nsi => new(this);
|
||||
|
||||
public NsiCommonApi NsiCommon => new(this);
|
||||
|
||||
public OrgRegistryCommonApi OrgRegistryCommon => new(this);
|
||||
|
||||
public PaymentsApi Payments => new(this);
|
||||
|
||||
public void SetSigningCertificate(X509Certificate2 cert, string pin = null)
|
||||
{
|
||||
pin ??= Constants.DEFAULT_CERTIFICATE_PIN;
|
||||
|
||||
Certificate = cert ?? throw new ArgumentNullException("Certificate not specified");
|
||||
CryptoProviderType = cert.GetProviderType();
|
||||
CertificateThumbprint = cert.Thumbprint;
|
||||
CertificatePassword = pin;
|
||||
}
|
||||
|
||||
public X509Certificate2 FindCertificate(Func<X509Certificate2, bool> predicate)
|
||||
{
|
||||
return CertificateHelper.FindCertificate(predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Производит для потока хэш по алгоритму "ГОСТ Р 34.11-94" в строке binhex
|
||||
/// </summary>
|
||||
public string ComputeGost94Hash(System.IO.Stream stream)
|
||||
{
|
||||
// API HouseManagement указывает, что файлы приложенные к договору должны размещаться
|
||||
// с AttachmentHASH по стандарту ГОСТ. Оказывается, ГИС ЖКХ требует применения устаревшего
|
||||
// алгоритма ГОСТ Р 34.11-94 (соответствует `rhash --gost94-cryptopro file` в linux).
|
||||
using var algorithm = new Gost_R3411_94_HashAlgorithm(GostCryptoProviderType);
|
||||
var savedPosition = stream.Position;
|
||||
stream.Position = 0;
|
||||
var hashValue = Util.ConvertToHexString(algorithm.ComputeHash(stream));
|
||||
stream.Position = savedPosition;
|
||||
return hashValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user