Files
XLAB/XLAB2/DocumentNumberDirectoryService.cs
Курнат Андрей a2b4762702 edit
2026-04-03 21:06:10 +03:00

261 lines
9.2 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;
using System.Collections.Generic;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace XLAB2
{
internal sealed class DocumentNumberDirectory
{
public DocumentNumberDirectory(IReadOnlyList<GroupOption> accountingBooks, IReadOnlyList<GroupOption> documentTypes)
{
AccountingBooks = accountingBooks ?? Array.Empty<GroupOption>();
DocumentTypes = documentTypes ?? Array.Empty<GroupOption>();
}
public IReadOnlyList<GroupOption> AccountingBooks { get; private set; }
public IReadOnlyList<GroupOption> DocumentTypes { get; private set; }
}
internal sealed class DocumentNumberDirectoryService
{
private readonly string _filePath;
public DocumentNumberDirectoryService(string filePath)
{
_filePath = filePath;
}
public DocumentNumberDirectory LoadDirectory()
{
var configuration = LoadConfiguration();
return new DocumentNumberDirectory(
NormalizeEntries(configuration.AccountingBooks, "книг учета"),
NormalizeEntries(configuration.DocumentTypes, "типов документов"));
}
public IReadOnlyList<GroupOption> LoadAccountingBooks()
{
return LoadDirectory().AccountingBooks;
}
public void SaveAccountingBooks(IReadOnlyList<GroupOption> accountingBooks)
{
var configuration = LoadConfiguration();
var normalizedAccountingBooks = NormalizeWritableEntries(accountingBooks, "книг учета");
var normalizedDocumentTypes = NormalizeEntries(configuration.DocumentTypes, "типов документов");
configuration.AccountingBooks = ToConfigurationEntries(normalizedAccountingBooks);
configuration.DocumentTypes = ToConfigurationEntries(normalizedDocumentTypes);
SaveConfiguration(configuration);
}
private static IReadOnlyList<GroupOption> NormalizeEntries(IEnumerable<DocumentNumberDirectoryEntry> entries, string sectionName)
{
var items = new List<GroupOption>();
var seenKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in entries ?? Array.Empty<DocumentNumberDirectoryEntry>())
{
if (entry == null)
{
continue;
}
var key = DocumentNumberFormatter.NormalizePart(entry.Key);
var title = DocumentNumberFormatter.NormalizePart(entry.Title);
if (key == null || title == null || !seenKeys.Add(key))
{
continue;
}
items.Add(new GroupOption
{
Key = key,
Title = title
});
}
if (items.Count == 0)
{
throw new InvalidOperationException(string.Format("Справочник номеров документов не содержит {0}.", sectionName));
}
return items;
}
private static IReadOnlyList<GroupOption> NormalizeWritableEntries(IEnumerable<GroupOption> entries, string sectionName)
{
var items = new List<GroupOption>();
var seenKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var seenTitles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in entries ?? Array.Empty<GroupOption>())
{
if (entry == null)
{
continue;
}
var key = DocumentNumberFormatter.NormalizePart(entry.Key);
if (key == null)
{
throw new InvalidOperationException("Не задан ключ книги учета.");
}
var title = DocumentNumberFormatter.NormalizePart(entry.Title);
if (title == null)
{
throw new InvalidOperationException("Не задан номер книги учета.");
}
if (!seenKeys.Add(key))
{
throw new InvalidOperationException(string.Format("Ключ книги учета \"{0}\" повторяется.", key));
}
if (!seenTitles.Add(title))
{
throw new InvalidOperationException(string.Format("Номер книги учета \"{0}\" повторяется.", title));
}
items.Add(new GroupOption
{
Key = key,
Title = title
});
}
if (items.Count == 0)
{
throw new InvalidOperationException(string.Format("Справочник номеров документов не содержит {0}.", sectionName));
}
return items;
}
private DocumentNumberDirectoryConfiguration LoadConfiguration()
{
if (string.IsNullOrWhiteSpace(_filePath))
{
throw new InvalidOperationException("Не задан путь к справочнику номеров документов.");
}
if (!File.Exists(_filePath))
{
throw new InvalidOperationException(string.Format("Не найден справочник номеров документов: {0}.", _filePath));
}
using (var stream = File.OpenRead(_filePath))
{
var configuration = JsonSerializer.Deserialize<DocumentNumberDirectoryConfiguration>(stream, CreateReadSerializerOptions());
if (configuration == null)
{
throw new InvalidOperationException("Справочник номеров документов пуст или поврежден.");
}
return configuration;
}
}
private void SaveConfiguration(DocumentNumberDirectoryConfiguration configuration)
{
var directoryPath = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrWhiteSpace(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
var json = JsonSerializer.Serialize(configuration, CreateWriteSerializerOptions());
File.WriteAllText(_filePath, json);
}
private static List<DocumentNumberDirectoryEntry> ToConfigurationEntries(IEnumerable<GroupOption> entries)
{
var items = new List<DocumentNumberDirectoryEntry>();
foreach (var entry in entries ?? Array.Empty<GroupOption>())
{
if (entry == null)
{
continue;
}
items.Add(new DocumentNumberDirectoryEntry
{
Key = entry.Key,
Title = entry.Title
});
}
return items;
}
private static JsonSerializerOptions CreateReadSerializerOptions()
{
return new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
}
private static JsonSerializerOptions CreateWriteSerializerOptions()
{
return new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
}
}
internal static class DocumentNumberFormatter
{
public const int MaxLength = 60;
public static string Build(string documentTypeTitle, string accountingBookTitle, string documentSequenceNumber)
{
var normalizedDocumentTypeTitle = NormalizePart(documentTypeTitle);
var normalizedAccountingBookTitle = NormalizePart(accountingBookTitle);
var normalizedDocumentSequenceNumber = NormalizePart(documentSequenceNumber);
if (normalizedDocumentTypeTitle == null
|| normalizedAccountingBookTitle == null
|| normalizedDocumentSequenceNumber == null)
{
return string.Empty;
}
return string.Format("{0} № {1}-{2}", normalizedDocumentTypeTitle, normalizedAccountingBookTitle, normalizedDocumentSequenceNumber);
}
public static string NormalizePart(string value)
{
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
}
internal sealed class DocumentNumberDirectoryConfiguration
{
public List<DocumentNumberDirectoryEntry> AccountingBooks { get; set; }
public List<DocumentNumberDirectoryEntry> DocumentTypes { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> ExtensionData { get; set; }
}
internal sealed class DocumentNumberDirectoryEntry
{
public string Key { get; set; }
public string Title { get; set; }
}
}