-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
363 lines (286 loc) · 14.4 KB
/
Program.cs
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
using System.Collections.Immutable;
using System.Globalization;
using Intech.Invoice;
using Intech.Invoice.DbMigration;
using MailKit.Net.Smtp;
using MailKit.Security;
using Npgsql;
var dbConnectionString = new DbConnString(host: Environment.GetEnvironmentVariable("PG_HOST"),
user: Environment.GetEnvironmentVariable("PG_USER"),
password: Environment.GetEnvironmentVariable("PG_PASSWORD"),
db: Environment.GetEnvironmentVariable("PG_DATABASE"));
using var pgDataSource = NpgsqlDataSource.Create(dbConnectionString.Npgsql());
var envCulture = Environment.GetEnvironmentVariable("CULTURE");
if (envCulture is not null)
{
CultureInfo.CurrentCulture = new CultureInfo(envCulture);
}
var timezone = Timezone.Default();
var systemClock = new SystemClock(timezone);
var supportedCommands = ImmutableHashSet.Create("supplier create", "client create", "invoice create",
"invoice pdf", "invoice details", "invoice list", "supplier modify", "supplier list", "client list", "client modify", "supplier delete", "client delete", "migration init", "migration create", "migration apply",
"invoice paid", "invoice send");
var currentCommand = string.Join(" ", args.Take(2));
var migrations = new InDirectoryMigrations(Path.Combine(Environment.CurrentDirectory, "db", "migrations"), pgDataSource);
var pgDump = new PgDump(dbConnectionString.PgDump());
var pgSchema = new PgSchema(Path.Combine("db", "schema.sql"), pgDataSource, pgDump);
try
{
if (Environment.GetEnvironmentVariable("STANDARD_VAT_RATE") is null)
{
throw new Exception("STANDARD_VAT_RATE env var must be set");
}
var standardVatRate = int.Parse(Environment.GetEnvironmentVariable("STANDARD_VAT_RATE"));
if (args.Length < 2 || !supportedCommands.Contains(currentCommand))
{
Console.Write($"""
Please provide one of the supported commands:
{string.Join("\n", supportedCommands)}.
""");
}
else
{
switch (currentCommand)
{
case "supplier create":
{
Console.WriteLine("Enter supplier name:");
var supplierName = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter supplier address:");
var supplierAddress = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter supplier VAT number:");
var supplierVatNumber = new StrictInputVatNumber(new Nonblank(new ConsoleInput(Console.ReadLine()))).ToString();
Console.WriteLine("Enter supplier IBAN:");
var supplierIban = new StrictInputIban(new Nonblank(new ConsoleInput(Console.ReadLine()))).ToString();
Console.WriteLine("Enter supplier email:");
var supplierEmail = new StrictInputEmail(new Nonblank(new ConsoleInput(Console.ReadLine()))).ToString();
var supplier = new UniqPgSuppliers(new PgSuppliers(pgDataSource), pgDataSource).Add(supplierName, supplierAddress, supplierVatNumber, supplierIban, supplierEmail);
Console.Write($"Supplier {supplier} has been created.");
break;
}
case "client create":
{
Console.WriteLine("Enter client name:");
var clientName = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter client address:");
var clientAddress = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter client VAT number:");
var clientVatNumber = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter client email:");
var email = new StrictInputEmail(new Nonblank(new ConsoleInput(Console.ReadLine()))).ToString();
var uniqPgClients = new UniqPgClients(new PgClients(pgDataSource), pgDataSource);
var addedClient = uniqPgClients.Add(clientName, clientAddress, clientVatNumber, email);
Console.Write($"Client {addedClient} has been created.");
break;
}
case "invoice create":
{
Console.WriteLine($"Enter supplier id{new ListHint<Supplier>(new PgSuppliers(pgDataSource))}:");
int supplierId = int.Parse(Console.ReadLine());
Console.WriteLine($"Enter client id{new ListHint<Client>(new PgClients(pgDataSource))}:");
int clientId = int.Parse(Console.ReadLine());
Console.WriteLine($"Enter VAT rate as positive integer, \"reverse-charged\" string or leave blank to apply the standard rate of {standardVatRate}%:");
var vatRateInput = new VatRateInput(Console.ReadLine());
var vatRate = vatRateInput.VatRate();
Console.WriteLine("Enter line item name:");
string lineItemName = new Nonblank(new ConsoleInput(Console.ReadLine())).ToString();
Console.WriteLine("Enter line item price (integer):");
int lineItemPrice = int.Parse(Console.ReadLine());
Console.WriteLine("Enter line item quantity (integer):");
int lineItemQuantity = int.Parse(Console.ReadLine());
var invoiceDate = systemClock.TodayInAppTimeZone();
new PgTransaction(pgDataSource).Wrap(() =>
{
var invoice = new PgInvoices(pgDataSource).Add(
number: new TimestampedNumber(systemClock).ToString(),
date: invoiceDate,
dueDate: DueDate.Standard(invoiceDate).Date(),
vatRate: vatRate.IntValue(),
supplierId: supplierId,
clientId: clientId);
new PgLineItems(pgDataSource).Add(invoice.Id(), lineItemName, lineItemPrice, lineItemQuantity);
Console.Write($"Invoice {invoice} has been issued.");
});
break;
}
case "invoice pdf":
{
int invoiceId = int.Parse(args[2]);
var invoice = new PgInvoice(invoiceId, pgDataSource);
invoice.SavePdf();
Console.Write($"Invoice PDF has been saved.");
break;
}
case "invoice details":
{
int invoiceId = int.Parse(args[2]);
var pgInvoice = new PgInvoice(invoiceId, pgDataSource);
pgInvoice.WithDetails((int id, string clientName, string number, DateOnly date, DateOnly dueDate,
long subtotal, long vatAmount, long total, bool paid, DateOnly? paidDate) =>
{
Console.WriteLine($"Id: {id}");
Console.WriteLine($"Client: {clientName}");
Console.WriteLine($"Number: {number}");
Console.WriteLine($"Date: {date}");
Console.WriteLine($"Due date: {dueDate}");
Console.WriteLine($"Subtotal: {subtotal}");
Console.WriteLine($"VAT amount: {vatAmount}");
Console.WriteLine($"Total: {total}");
Console.WriteLine($"Paid: {paid}");
Console.WriteLine($"Paid on: {paidDate}");
});
break;
}
case "invoice list":
{
new InvoiceList(new PgInvoices(pgDataSource)).Print();
break;
}
case "supplier modify":
{
int id = int.Parse(args[2]);
Console.WriteLine("Enter new supplier name:");
var newName = Console.ReadLine();
Console.WriteLine("Enter new supplier address:");
var newAddress = Console.ReadLine();
Console.WriteLine("Enter new supplier VAT number:");
var newVatNumber = Console.ReadLine();
Console.WriteLine("Enter new supplier IBAN:");
var newIban = Console.ReadLine();
Console.WriteLine("Enter new supplier email:");
var newEmail = Console.ReadLine();
var supplier = new PgSupplier(id, pgDataSource);
supplier.Modify(newName, newAddress, newVatNumber, newIban, newEmail);
Console.Write($"Supplier {supplier} has been modified.");
break;
}
case "supplier list":
{
new SupplierList(new PgSuppliers(pgDataSource)).Print();
break;
}
case "client list":
{
new ClientList(new PgClients(pgDataSource)).Print();
break;
}
case "client modify":
{
int id = int.Parse(args[2]);
Console.WriteLine("Enter new client name:");
var newName = Console.ReadLine();
Console.WriteLine("Enter new client address:");
var newAddress = Console.ReadLine();
Console.WriteLine("Enter new client VAT number:");
var newVatNumber = Console.ReadLine();
var client = new PgClient(id, pgDataSource);
client.Modify(newName, newAddress, newVatNumber);
Console.Write($"Client {client} has been modified.");
break;
}
case "supplier delete":
{
int id = int.Parse(args[2]);
var supplier = new PgSupplier(id, pgDataSource);
var name = supplier.Name();
supplier.Delete();
Console.Write($"Supplier {name} has been deleted.");
break;
}
case "client delete":
{
int id = int.Parse(args[2]);
var client = new PgClient(id, pgDataSource);
var name = client.Name();
client.Delete();
Console.Write($"Client {name} has been deleted.");
break;
}
case "migration init":
{
new InFileMigration(Path.Combine("assets", "initial_migration.pgsql"), pgDataSource).Apply();
migrations.Init();
pgSchema.Generate();
Console.WriteLine("Migrations have been initialized.");
break;
}
case "migration create":
{
var invalidArgs = args.Length < 3;
if (invalidArgs)
{
throw new Exception("Please provide migration name.");
}
var name = args[2];
migrations.CreateEmpty(new TimestampedId(name, systemClock));
Console.WriteLine("Migration has been created.");
break;
}
case "migration apply":
{
new Pending(migrations).Apply(whenAny: (applied) =>
{
foreach (var migration in applied)
{
Console.WriteLine($"Migration {migration} has been applied.");
}
pgSchema.Regenerate();
},
whenNone: () => Console.WriteLine("There are no pending migrations."));
break;
}
case "invoice paid":
{
var invalidArgs = args.Length < 3;
if (invalidArgs)
{
throw new Exception("Please provide invoice id.");
}
int id;
try
{
id = int.Parse(args[2]);
}
catch (FormatException e)
{
throw new Exception("Invalid invoice id.", e);
}
var pgInvoice = new PgInvoice(id, pgDataSource);
pgInvoice.MarkPaid(systemClock.TodayInAppTimeZone());
Console.WriteLine($"Invoice {pgInvoice} has been marked as paid.");
break;
}
case "invoice send":
{
var id = int.Parse(args[2]);
var host = Environment.GetEnvironmentVariable("SMTP_HOST");
var port = int.Parse(Environment.GetEnvironmentVariable("SMTP_PORT"));
var username = Environment.GetEnvironmentVariable("SMTP_USERNAME");
var password = Environment.GetEnvironmentVariable("SMTP_PASSWORD");
var secure = bool.Parse(Environment.GetEnvironmentVariable("SMTP_SECURE"));
using var smtpClient = new SmtpClient();
smtpClient.Connect(host: host, port: port, secure ? SecureSocketOptions.Auto : SecureSocketOptions.None);
// Needed because Mailcrab with TLS enabled does not work for some reason
if (secure) smtpClient.Authenticate(userName: username, password: password);
var pgInvoice = new PgInvoice(id, pgDataSource);
pgInvoice.Send(smtpClient);
smtpClient.Disconnect(quit: true);
Console.WriteLine($"Invoice has been sent to the client.");
break;
}
}
}
}
catch (Exception e)
{
var showDetailedExceptions = Convert.ToBoolean(Environment.GetEnvironmentVariable("SHOW_DETAILED_EXCEPTIONS")) || false;
if (showDetailedExceptions)
{
Console.WriteLine(e);
}
else
{
Console.WriteLine(e.Message);
}
// Console.Error.WriteLine(e.Message);
}