edit
This commit is contained in:
260
XLAB2/DocumentNumberDirectoryService.cs
Normal file
260
XLAB2/DocumentNumberDirectoryService.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user