File size: 6,046 Bytes
13240d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using BakeryErp.Infrastructure.Database;

namespace BakeryErp.Infrastructure.Repositories;

public sealed class OcrDocumentRepository
{
    private readonly DatabaseOptions _options;

    public OcrDocumentRepository(DatabaseOptions options)
    {
        _options = options;
    }

    public IReadOnlyList<LocalOcrDocument> List()
    {
        return LocalDataStoreFile.Load(_options)
            .OcrDocuments
            .OrderByDescending(static document => document.CreatedAt)
            .ToList();
    }

    public LocalOcrDocument? Find(Guid id)
    {
        return LocalDataStoreFile.Load(_options)
            .OcrDocuments
            .FirstOrDefault(document => document.Id == id);
    }

    public OcrReport GetReport()
    {
        var documents = LocalDataStoreFile.Load(_options).OcrDocuments;
        return new OcrReport(
            documents.Count,
            documents.Count(static document => document.Status == LocalOcrStatus.Uploaded),
            documents.Count(static document => document.Status == LocalOcrStatus.Recognized),
            documents.Count(static document => document.Status == LocalOcrStatus.MappingRequired),
            documents.Count(static document => document.Status == LocalOcrStatus.Confirmed),
            documents.Count(static document => document.Status == LocalOcrStatus.ConvertedToReceipt),
            documents.Count(static document => document.Status == LocalOcrStatus.Failed),
            documents.Sum(static document => document.TotalAmount ?? 0));
    }

    public SaveOcrDocumentResult Save(LocalOcrDocument document)
    {
        var store = LocalDataStoreFile.Load(_options);
        var documentNumber = document.DocumentNumber.Trim().ToUpperInvariant();

        if (string.IsNullOrWhiteSpace(documentNumber))
        {
            return SaveOcrDocumentResult.ValidationError("單據編號為必填。");
        }

        var duplicatedNumber = store.OcrDocuments.Any(existing =>
            existing.Id != document.Id &&
            string.Equals(existing.DocumentNumber, documentNumber, StringComparison.OrdinalIgnoreCase));
        if (duplicatedNumber)
        {
            return SaveOcrDocumentResult.ValidationError("單據編號已存在,請改用其他編號。");
        }

        var existingDocument = store.OcrDocuments.FirstOrDefault(existing => existing.Id == document.Id);
        if (existingDocument is null)
        {
            document.Id = document.Id == Guid.Empty ? Guid.NewGuid() : document.Id;
            document.DocumentNumber = documentNumber;
            document.FileName = BlankToEmpty(document.FileName);
            document.SupplierName = BlankToNull(document.SupplierName);
            document.RawText = BlankToNull(document.RawText);
            document.RecognizedItems = BlankToNull(document.RecognizedItems);
            document.ReviewNote = BlankToNull(document.ReviewNote);
            document.CreatedAt = DateTime.UtcNow;
            store.OcrDocuments.Add(document);
        }
        else
        {
            existingDocument.DocumentNumber = documentNumber;
            existingDocument.FileName = BlankToEmpty(document.FileName);
            existingDocument.SupplierName = BlankToNull(document.SupplierName);
            existingDocument.DocumentDate = document.DocumentDate;
            existingDocument.Status = document.Status;
            existingDocument.TotalAmount = document.TotalAmount;
            existingDocument.RawText = BlankToNull(document.RawText);
            existingDocument.RecognizedItems = BlankToNull(document.RecognizedItems);
            existingDocument.ReviewNote = BlankToNull(document.ReviewNote);
            existingDocument.UpdatedAt = DateTime.UtcNow;
        }

        LocalDataStoreFile.Save(_options, store);
        return SaveOcrDocumentResult.Success();
    }

    public void Review(Guid id, bool approved)
    {
        var store = LocalDataStoreFile.Load(_options);
        var document = store.OcrDocuments.FirstOrDefault(existing => existing.Id == id);
        if (document is null)
        {
            return;
        }

        document.Status = approved ? LocalOcrStatus.Confirmed : LocalOcrStatus.MappingRequired;
        document.ReviewedAt = DateTime.UtcNow;
        document.UpdatedAt = DateTime.UtcNow;
        LocalDataStoreFile.Save(_options, store);
    }

    public void ConvertToReceipt(Guid id)
    {
        var store = LocalDataStoreFile.Load(_options);
        var document = store.OcrDocuments.FirstOrDefault(existing => existing.Id == id);
        if (document is null)
        {
            return;
        }

        document.Status = LocalOcrStatus.ConvertedToReceipt;
        document.UpdatedAt = DateTime.UtcNow;
        LocalDataStoreFile.Save(_options, store);
    }

    public static string GetStatusName(LocalOcrStatus status)
    {
        return status switch
        {
            LocalOcrStatus.Uploaded => "已上傳",
            LocalOcrStatus.Recognized => "已辨識",
            LocalOcrStatus.MappingRequired => "待對應",
            LocalOcrStatus.Confirmed => "已確認",
            LocalOcrStatus.ConvertedToReceipt => "已轉進貨",
            LocalOcrStatus.Failed => "失敗",
            _ => status.ToString()
        };
    }

    private static string BlankToEmpty(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
    }

    private static string? BlankToNull(string? value)
    {
        return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
    }
}

public sealed record OcrReport(
    int TotalCount,
    int UploadedCount,
    int RecognizedCount,
    int MappingRequiredCount,
    int ConfirmedCount,
    int ConvertedToReceiptCount,
    int FailedCount,
    decimal TotalAmount);

public sealed record SaveOcrDocumentResult(bool IsSuccess, string? ErrorMessage)
{
    public static SaveOcrDocumentResult Success() => new(true, null);
    public static SaveOcrDocumentResult ValidationError(string message) => new(false, message);
}