diff --git a/Hcs.Client/Client/Api/DeviceMeteringApi.cs b/Hcs.Client/Client/Api/DeviceMeteringApi.cs
index 73b7423..ffc3fed 100644
--- a/Hcs.Client/Client/Api/DeviceMeteringApi.cs
+++ b/Hcs.Client/Client/Api/DeviceMeteringApi.cs
@@ -1,5 +1,7 @@
-using Hcs.Client.Api.Request.DeviceMetering;
+using Hcs.Client.Api.Payload.DeviceMetering;
+using Hcs.Client.Api.Request.DeviceMetering;
using Hcs.Service.Async.DeviceMetering;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -8,6 +10,18 @@ namespace Hcs.Client.Api
// http://open-gkh.ru/DeviceMeteringServiceAsync/
public class DeviceMeteringApi(ClientBase client) : ApiBase(client)
{
+ ///
+ /// Экспорт истории показаний и поверок приборов учета пользователя, установленных в указанном доме
+ ///
+ /// Пейлоад выборки ПУ
+ /// Токен отмены
+ /// Лицевые счета
+ public async Task> ExportMeteringDeviceHistoryAsync(ExportMeteringDeviceHistoryPayload payload, CancellationToken token = default)
+ {
+ var request = new ExportMeteringDeviceHistory(client);
+ return await request.ExecuteAsync(payload, token);
+ }
+
///
/// Импорт показаний приборов учета
///
diff --git a/Hcs.Client/Client/Api/Payload/DeviceMetering/ExportMeteringDeviceHistoryPayload.cs b/Hcs.Client/Client/Api/Payload/DeviceMetering/ExportMeteringDeviceHistoryPayload.cs
new file mode 100644
index 0000000..8431d1b
--- /dev/null
+++ b/Hcs.Client/Client/Api/Payload/DeviceMetering/ExportMeteringDeviceHistoryPayload.cs
@@ -0,0 +1,91 @@
+using Hcs.Client.Api.Registry;
+using System;
+
+namespace Hcs.Client.Api.Payload.DeviceMetering
+{
+ // http://open-gkh.ru/DeviceMetering/exportMeteringDeviceHistoryRequest.html
+ public class ExportMeteringDeviceHistoryPayload
+ {
+ ///
+ /// Необязательное. Список из уникальных идентификаторов домов по ФИАС, в которых установлены ПУ
+ /// пользователей. Если не указано, то будут экспортироваться данные по всем ПУ пользователей.
+ ///
+ public string[] fiasHouseGuid;
+
+ ///
+ /// Выборочное. Выбор между , и
+ /// . Тип прибора учета (НСИ 27). Максимум 100 по выбранным.
+ ///
+ public RegistryElement[] meteringDeviceType;
+
+ ///
+ /// Выборочное. Выбор между , и
+ /// . Вид коммунального ресурса (НСИ 2). Максимум 100 по выбранным.
+ ///
+ public RegistryElement[] municipalResource;
+
+ ///
+ /// Выборочное. Выбор между , и
+ /// . Идентификатор ПУ. Максимум 100 по выбранным.
+ ///
+ public string[] meteringDeviceRootGUID;
+
+ ///
+ /// Необязательное. Дата ввода в эксплуатацию "С".
+ ///
+ public DateTime? commissioningDateFrom;
+
+ ///
+ /// Необязательное. Дата ввода в эксплуатацию "П".
+ ///
+ public DateTime? сommissioningDateTo;
+
+ ///
+ /// Необязательное. Выгружать архивированные или нет.
+ ///
+ public bool? serchArchived;
+
+ ///
+ /// Необязательное. Дата архивации "С".
+ ///
+ public DateTime? archiveDateFrom;
+
+ ///
+ /// Необязательное. Дата архивации "По".
+ ///
+ public DateTime? archiveDateTo;
+
+ ///
+ /// Необязательное. Дата начала периода, за который выгружаются показания и поверки ПУ (по дате
+ /// снятия показаний). Период выгрузки показаний ПУ (определяемый элементами
+ /// и ) не должен выходить за пределы двух последовательных календарных месяцев.
+ ///
+ public DateTime? inputDateFrom;
+
+ ///
+ /// Необязательное. Дата окончания периода, за который выгружаются показания и поверки ПУ (по дате
+ /// снятия показаний). Период выгрузки показаний ПУ (определяемый элементами
+ /// и ) не должен выходить за пределы двух последовательных календарных месяцев.
+ ///
+ public DateTime? inputDateTo;
+
+ ///
+ /// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему гражданином,
+ /// включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку не включаются.
+ ///
+ public bool? excludePersonAsDataSource;
+
+ ///
+ /// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему текущей
+ /// организацией, включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку не включаются.
+ ///
+ public bool? excludeCurrentOrgAsDataSource;
+
+ ///
+ /// Необязательное. Если флаг сброшен или отсутствует, то показания, введенные в систему организациями
+ /// отличной от текущей, включаются в выгрузку. Если флаг установлен, то такие показания в выгрузку
+ /// не включаются.
+ ///
+ public bool? excludeOtherOrgAsDataSource;
+ }
+}
diff --git a/Hcs.Client/Client/Api/Registry/Registry27.cs b/Hcs.Client/Client/Api/Registry/Registry27.cs
new file mode 100644
index 0000000..c945f83
--- /dev/null
+++ b/Hcs.Client/Client/Api/Registry/Registry27.cs
@@ -0,0 +1,16 @@
+namespace Hcs.Client.Api.Registry
+{
+ ///
+ /// НСИ "Тип прибора учета" (реестровый номер 27).
+ /// Взято из https://dom.gosuslugi.ru/opendataapi/nsi-27/v1.
+ ///
+ public static class Registry27
+ {
+ ///
+ /// Индивидуальный
+ ///
+ public static RegistryElement Element1 => new(
+ "1",
+ "3e86b303-62be-4837-91c1-ed2475702c65");
+ }
+}
diff --git a/Hcs.Client/Client/Api/Request/DeviceMetering/ExportMeteringDeviceHistory.cs b/Hcs.Client/Client/Api/Request/DeviceMetering/ExportMeteringDeviceHistory.cs
new file mode 100644
index 0000000..c7f67d0
--- /dev/null
+++ b/Hcs.Client/Client/Api/Request/DeviceMetering/ExportMeteringDeviceHistory.cs
@@ -0,0 +1,170 @@
+using Hcs.Client.Api.Payload.DeviceMetering;
+using Hcs.Client.Internal;
+using Hcs.Service.Async.DeviceMetering;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Hcs.Client.Api.Request.DeviceMetering
+{
+ internal class ExportMeteringDeviceHistory(ClientBase client) : DeviceMeteringRequestBase(client)
+ {
+ protected override bool EnableMinimalResponseWaitDelay => false;
+
+ internal async Task> 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();
+ }
+
+ 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