Files
tnsu-approval-route/backend/Tnsu.ApprovalRoute.Application/Reminders/ApprovalRow.cs
T
gorbenko.pavel 48ede1420d
Tests / backend-test (push) Successful in 1m38s
Tests / frontend-test (push) Successful in 47s
Обходной лист (#12)
Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/12
2026-08-17 05:43:52 +00:00

114 lines
4.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Globalization;
using System.Text.Json;
namespace Tnsu.ApprovalRoute.Application.Reminders;
/// <summary>
/// Строка списка «… — согласование», разобранная из ответа SharePoint.
/// Имена колонок — как в request-portal (FIELDS в approvalList.service.ts):
/// именно он эти списки и создаёт.
/// </summary>
internal sealed record ApprovalRow(
int Id,
int TicketId,
string Status,
string Phase,
DateTimeOffset? Date,
string? ApproverEmail,
int? SlaHours,
bool? AskOverdueReason)
{
public static ApprovalRow? TryParse(JsonElement item)
{
// TicketId — Lookup на заявку; без него строку не с чем связать.
if (!TryGetInt(item, "TicketId", out var ticketId)) return null;
var approver = item.TryGetProperty("TansuSiteApprover", out var a) && a.ValueKind == JsonValueKind.Object
? GetString(a, "EMail")
: null;
return new ApprovalRow(
TryGetInt(item, "Id", out var id) ? id : 0,
ticketId,
GetString(item, "ApprovalStatus") ?? string.Empty,
// Пустая фаза — старые строки, заведённые до появления колонки;
// трактуем как «Согласование» (так же поступает request-portal).
GetString(item, "Phase") is { Length: > 0 } phase ? phase : ApprovalPhases.Approval,
TryGetDate(item, "Date"),
approver,
TryGetInt(item, "SLAhours", out var sla) ? sla : null,
TryGetBool(item, "AskOverdueReason", out var ask) ? ask : null);
}
private static string? GetString(JsonElement e, string name) =>
e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
/// <summary>
/// Целое из JSON — с поправкой на то, КАК SharePoint отдаёт Number-колонки.
///
/// Одного TryGetInt32 здесь мало: у колонки типа Number значение приходит
/// дробным («1.0», а не «1»), а TryGetInt32 на числе с десятичной точкой
/// возвращает false — и поле молча становилось null. На фронтенде этой
/// проблемы нет вовсе: в JavaScript 1.0 и 1 — одно и то же число, поэтому
/// там те же данные читались верно, а на бэкенде терялись.
///
/// Строку тоже принимаем: с odata=verbose (и у части полей) SharePoint
/// отдаёт числа строками.
/// </summary>
private static bool TryGetInt(JsonElement e, string name, out int value)
{
value = 0;
if (!e.TryGetProperty(name, out var v)) return false;
if (v.ValueKind == JsonValueKind.Number)
{
if (v.TryGetInt32(out value)) return true;
if (!v.TryGetDouble(out var d) || d is < int.MinValue or > int.MaxValue) return false;
value = (int)Math.Round(d);
return true;
}
if (v.ValueKind != JsonValueKind.String) return false;
if (!double.TryParse(v.GetString(), NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed))
return false;
if (parsed is < int.MinValue or > int.MaxValue) return false;
value = (int)Math.Round(parsed);
return true;
}
private static bool TryGetBool(JsonElement e, string name, out bool value)
{
value = false;
if (!e.TryGetProperty(name, out var v)) return false;
if (v.ValueKind is JsonValueKind.True or JsonValueKind.False)
{
value = v.GetBoolean();
return true;
}
// Да/Нет-колонка тоже может прийти строкой («true»/«false») — см. TryGetInt.
return v.ValueKind == JsonValueKind.String && bool.TryParse(v.GetString(), out value);
}
private static DateTimeOffset? TryGetDate(JsonElement e, string name) =>
e.TryGetProperty(name, out var v) &&
v.ValueKind == JsonValueKind.String &&
DateTimeOffset.TryParse(v.GetString(), out var parsed)
? parsed
: null;
}
internal static class ApprovalPhases
{
public const string Approval = "Согласование";
public const string Execution = "Исполнение";
}
internal static class ApprovalStatuses
{
public const string OnApproval = "На согласовании";
public const string OnExecution = "На исполнении";
}