• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

b1f6c1c4 / ProfessionalAccounting / 324

09 Dec 2024 01:34AM UTC coverage: 54.325% (-0.7%) from 55.047%
324

push

appveyor

b1f6c1c4
pvt prefix

6318 of 11630 relevant lines covered (54.33%)

122.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.58
/AccountingServer.Shell/Serializer/ExprSerializer.cs
1
/* Copyright (C) 2020-2024 b1f6c1c4
2
 *
3
 * This file is part of ProfessionalAccounting.
4
 *
5
 * ProfessionalAccounting is free software: you can redistribute it and/or
6
 * modify it under the terms of the GNU Affero General Public License as
7
 * published by the Free Software Foundation, version 3.
8
 *
9
 * ProfessionalAccounting is distributed in the hope that it will be useful, but
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License
12
 * for more details.
13
 *
14
 * You should have received a copy of the GNU Affero General Public License
15
 * along with ProfessionalAccounting.  If not, see
16
 * <https://www.gnu.org/licenses/>.
17
 */
18

19
using System;
20
using System.Collections.Generic;
21
using System.Linq;
22
using System.Text;
23
using System.Text.RegularExpressions;
24
using AccountingServer.BLL;
25
using AccountingServer.BLL.Util;
26
using AccountingServer.Entities;
27
using AccountingServer.Shell.Util;
28
using static AccountingServer.BLL.Parsing.Facade;
29
using static AccountingServer.BLL.Parsing.FacadeF;
30

31
namespace AccountingServer.Shell.Serializer;
32

33
/// <summary>
34
///     实体表达式
35
/// </summary>
36
public class ExprSerializer : IClientDependable, IEntitySerializer
37
{
38
    private const string TheToken = "new Voucher {";
39

40
    public Client Client { private get; set; }
41

42
    /// <inheritdoc />
43
    public string PresentVoucher(Voucher voucher)
44
        => PresentVoucher(voucher, null);
29✔
45

46
    /// <inheritdoc />
47
    public string PresentVoucher(Voucher voucher, string inject)
48
    {
29✔
49
        var sb = new StringBuilder();
29✔
50
        sb.Append(TheToken);
29✔
51
        if (voucher == null)
29✔
52
        {
3✔
53
            sb.Append("\n");
3✔
54
            sb.Append("\n");
3✔
55
        }
3✔
56
        else
57
        {
26✔
58
            sb.Append($"{voucher.ID?.Quotation('^')}\n");
26✔
59
            if (voucher.Date.HasValue || inject == null)
26✔
60
                sb.Append($"{voucher.Date.AsDate()}\n");
26✔
61
            if (voucher.Remark != null)
26✔
62
                sb.Append($"{voucher.Remark.Quotation('%')}\n");
14✔
63
            if (voucher.Type.HasValue &&
26✔
64
                voucher.Type != VoucherType.Ordinary)
65
                sb.Append($"{voucher.Type}\n");
12✔
66

67
            if (inject != null)
26✔
68
                sb.Append($"{inject}\n");
×
69

70
            foreach (var d in voucher.Details)
114✔
71
                sb.Append(PresentVoucherDetail(d));
88✔
72
        }
26✔
73

74
        sb.Append('}');
29✔
75
        return sb.ToString();
29✔
76
    }
29✔
77

78
    /// <inheritdoc />
79
    public string PresentVoucherDetail(VoucherDetail detail)
80
    {
90✔
81
        var sb = new StringBuilder();
90✔
82
        var t = TitleManager.GetTitleName(detail.Title);
90✔
83
        sb.Append(
90✔
84
            detail.SubTitle.HasValue
85
                ? $"// {t}-{TitleManager.GetTitleName(detail.Title, detail.SubTitle)}\n"
86
                : $"// {t}\n");
87
        if (detail.User != Client.User)
90✔
88
            sb.Append($"{detail.User.AsUser()} ");
25✔
89
        if (detail.Currency != BaseCurrency.Now)
90✔
90
            sb.Append($"{detail.Currency.AsCurrency()} ");
42✔
91
        sb.Append($"{detail.Title.AsTitle()}{detail.SubTitle.AsSubTitle()} ");
90✔
92
        if (detail.Content == null &&
90✔
93
            detail.Remark != null)
94
            sb.Append("''");
39✔
95
        else
96
            sb.Append(detail.Content?.Quotation('\''));
51✔
97
        sb.Append($" {detail.Remark?.Quotation('\"')} {detail.Fund}\n");
90✔
98
        return sb.ToString();
90✔
99
    }
90✔
100

101
    /// <inheritdoc />
102
    public string PresentVoucherDetail(VoucherDetailR detail)
103
        => $"{detail.Voucher.Date.AsDate()} {PresentVoucherDetail((VoucherDetail)detail)}";
2✔
104

105
    /// <inheritdoc />
106
    public Voucher ParseVoucher(string expr)
107
    {
32✔
108
        if (!expr.StartsWith(TheToken, StringComparison.Ordinal))
32✔
109
            throw new FormatException("格式错误");
2✔
110

111
        expr = expr[TheToken.Length..];
30✔
112
        var v = GetVoucher(ref expr);
30✔
113
        Parsing.TrimStartComment(ref expr);
30✔
114
        if (Parsing.Token(ref expr, false) != "}")
30✔
115
            throw new FormatException("格式错误" + expr);
2✔
116

117
        Parsing.Eof(expr);
28✔
118
        return v;
28✔
119
    }
28✔
120

121
    /// <inheritdoc />
122
    public virtual VoucherDetail ParseVoucherDetail(string expr)
123
    {
×
124
        var res = ParseVoucherDetail(ref expr);
×
125
        Parsing.Eof(expr);
×
126
        return res;
×
127
    }
×
128

129
    public string PresentAsset(Asset asset) => throw new NotImplementedException();
2✔
130
    public Asset ParseAsset(string str) => throw new NotImplementedException();
2✔
131
    public string PresentAmort(Amortization amort) => throw new NotImplementedException();
2✔
132
    public Amortization ParseAmort(string str) => throw new NotImplementedException();
2✔
133

134
    /// <summary>
135
    ///     解析记账凭证表达式
136
    /// </summary>
137
    /// <param name="expr">表达式</param>
138
    /// <returns>记账凭证</returns>
139
    private Voucher GetVoucher(ref string expr)
140
    {
30✔
141
        Parsing.TrimStartComment(ref expr);
30✔
142
        var id = Parsing.Quoted(ref expr, '^');
30✔
143
        Parsing.TrimStartComment(ref expr);
30✔
144
        DateTime? date = Client.Today;
30✔
145
        try
146
        {
30✔
147
            date = ParsingF.UniqueTime(ref expr, Client);
30✔
148
        }
14✔
149
        catch (Exception)
16✔
150
        {
16✔
151
            // ignore
152
        }
16✔
153

154
        Parsing.TrimStartComment(ref expr);
30✔
155
        var remark = Parsing.Quoted(ref expr, '%');
30✔
156
        Parsing.TrimStartComment(ref expr);
30✔
157
        var typeT = VoucherType.Ordinary;
30✔
158
        var type = Parsing.Token(ref expr, false, t => TryParse(t, out typeT)) != null ? (VoucherType?)typeT : null;
28✔
159
        Parsing.TrimStartComment(ref expr);
30✔
160

161
        var lst = new List<VoucherDetail>();
30✔
162
        VoucherDetail d;
163
        while ((d = ParseVoucherDetail(ref expr)) != null)
95✔
164
            lst.Add(d);
65✔
165

166
        return new()
30✔
167
            {
168
                ID = id,
169
                Remark = remark,
170
                Type = type,
171
                Date = date,
172
                Details = lst,
173
            };
174
    }
30✔
175

176
    /// <summary>
177
    ///     解析记账凭证类别表达式
178
    /// </summary>
179
    /// <param name="s">表达式</param>
180
    /// <param name="typeT">记账凭证类别</param>
181
    /// <returns>是否解析成功</returns>
182
    private static bool TryParse(string s, out VoucherType typeT)
183
    {
28✔
184
        // Don't use Enum.TryParse here:
185
        // Enum.TryParse("1001", out _) gives true
186
        switch (s)
28✔
187
        {
188
            case "Ordinary":
189
                typeT = VoucherType.Ordinary;
×
190
                return true;
×
191
            case "Carry":
192
                typeT = VoucherType.Carry;
2✔
193
                return true;
2✔
194
            case "AnnualCarry":
195
                typeT = VoucherType.AnnualCarry;
2✔
196
                return true;
2✔
197
            case "Depreciation":
198
                typeT = VoucherType.Depreciation;
2✔
199
                return true;
2✔
200
            case "Devalue":
201
                typeT = VoucherType.Devalue;
2✔
202
                return true;
2✔
203
            case "Amortization":
204
                typeT = VoucherType.Amortization;
2✔
205
                return true;
2✔
206
            case "Uncertain":
207
                typeT = VoucherType.Uncertain;
2✔
208
                return true;
2✔
209
            default:
210
                typeT = VoucherType.General;
16✔
211
                return false;
16✔
212
        }
213
    }
28✔
214

215
    private Regex m_SimpleCurrency = new(@"^@[a-zA-Z]+$");
37✔
216

217
    public VoucherDetail ParseVoucherDetail(ref string expr)
218
    {
109✔
219
        var lst = new List<string>();
109✔
220

221
        Parsing.TrimStartComment(ref expr);
109✔
222
        var user = Parsing.Token(ref expr, false, static t => t.StartsWith("U", StringComparison.Ordinal))
107✔
223
            .ParseUserSpec(Client);
224
        var currency = BaseCurrency.Now;
109✔
225
        if (Parsing.Token(ref expr, false, m_SimpleCurrency.IsMatch) is var t && t != null)
109✔
226
            currency = t.ParseCurrency();
49✔
227
        else if (expr.FirstOrDefault() == '@')
60✔
228
        {
×
229
            expr = expr[1..];
×
230
            currency = Parsing.Quoted(ref expr, '#') + '#';
×
231
        }
×
232
        Parsing.TrimStartComment(ref expr);
109✔
233
        var title = Parsing.Title(ref expr);
109✔
234
        if (title == null)
109✔
235
            if (!AlternativeTitle(ref expr, lst, ref title))
49✔
236
                return null;
37✔
237

238
        double? fund;
239

240
        while (true)
163✔
241
        {
163✔
242
            Parsing.TrimStartComment(ref expr);
163✔
243
            if ((fund = Parsing.Double(ref expr)) != null)
163✔
244
                break;
59✔
245

246
            Parsing.TrimStartComment(ref expr);
104✔
247
            if (Parsing.Optional(ref expr, "null"))
104✔
248
                break;
3✔
249
            if (Parsing.Optional(ref expr, "/"))
101✔
250
                break;
10✔
251

252
            if (lst.Count > 2)
91✔
253
                throw new ArgumentException("语法错误", nameof(expr));
×
254

255
            Parsing.TrimStartComment(ref expr);
91✔
256
            lst.Add(Parsing.Token(ref expr));
91✔
257
        }
91✔
258

259
        var content = lst.Count >= 1 ? lst[0] : null;
72✔
260
        var remark = lst.Count >= 2 ? lst[1] : null;
72✔
261

262
        if (content == "G()")
72✔
263
            content = Guid.NewGuid().ToString().ToUpperInvariant();
×
264

265
        if (remark == "G()")
72✔
266
            remark = Guid.NewGuid().ToString().ToUpperInvariant();
×
267

268
        return new()
72✔
269
            {
270
                User = user,
271
                Currency = currency,
272
                Title = title.Title,
273
                SubTitle = title.SubTitle,
274
                Content = string.IsNullOrEmpty(content) ? null : content,
275
                Fund = fund,
276
                Remark = string.IsNullOrEmpty(remark) ? null : remark,
277
            };
278
    }
109✔
279

280
    protected virtual bool AlternativeTitle(ref string expr, ICollection<string> lst, ref ITitle title) => false;
9✔
281
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc