QoL
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
using System;
|
||||
using PdfSharp.Fonts;
|
||||
class Program { static void Main() { Console.WriteLine("Hello"); } }
|
||||
Binary file not shown.
Binary file not shown.
@@ -162,8 +162,6 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var factory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TimetrackerDbContext>>();
|
||||
await using var db = await factory.CreateDbContextAsync();
|
||||
var databaseCreatedFromModel = false;
|
||||
|
||||
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
@@ -174,24 +172,6 @@ using (var scope = app.Services.CreateScope())
|
||||
await conn.OpenAsync();
|
||||
}
|
||||
|
||||
using (var cmdCheckTables = conn.CreateCommand())
|
||||
{
|
||||
cmdCheckTables.CommandText = @"
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name <> '__EFMigrationsHistory';";
|
||||
var existingTableCount = Convert.ToInt32(await cmdCheckTables.ExecuteScalarAsync() ?? 0);
|
||||
if (existingTableCount == 0)
|
||||
{
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
databaseCreatedFromModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!databaseCreatedFromModel)
|
||||
{
|
||||
|
||||
// Check if AppSettings table already exists
|
||||
using (var cmdCheck = conn.CreateCommand())
|
||||
{
|
||||
@@ -212,28 +192,51 @@ using (var scope = app.Services.CreateScope())
|
||||
await cmdCreateHistory.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Check if the initial migration is already recorded
|
||||
using (var cmdCheckHistory = conn.CreateCommand())
|
||||
// Seed the history table conditionally for each migration based on actual schema presence
|
||||
using (var cmdSyncHistory = conn.CreateCommand())
|
||||
{
|
||||
cmdCheckHistory.CommandText = @"SELECT COUNT(*) FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520133634_Initial';";
|
||||
var historyCount = Convert.ToInt32(await cmdCheckHistory.ExecuteScalarAsync() ?? 0);
|
||||
cmdSyncHistory.CommandText = @"
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260520133634_Initial', '10.0.9'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520133634_Initial');
|
||||
|
||||
if (historyCount == 0)
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260520200000_AddPublicHolidays', '10.0.9'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520200000_AddPublicHolidays');
|
||||
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260522081459_AddMultiUser', '10.0.9'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260522081459_AddMultiUser');
|
||||
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260607213215_AddFlexTimeAndHolidayState', '10.0.9'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260607213215_AddFlexTimeAndHolidayState');
|
||||
|
||||
-- Seed AddMultiTenancy only if Tenants table exists
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260624204954_AddMultiTenancy', '10.0.9'
|
||||
WHERE EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'Tenants')
|
||||
AND NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260624204954_AddMultiTenancy');
|
||||
|
||||
-- Seed AddTenantBranding only if TenantColor column exists
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260624210436_AddTenantBranding', '10.0.9'
|
||||
WHERE EXISTS (SELECT FROM information_schema.columns WHERE table_name = 'Tenants' AND column_name = 'TenantColor')
|
||||
AND NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260624210436_AddTenantBranding');
|
||||
|
||||
-- Seed PendingChanges only if IsApproved column in VacationDays exists
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260625184756_PendingChanges', '10.0.9'
|
||||
WHERE EXISTS (SELECT FROM information_schema.columns WHERE table_name = 'VacationDays' AND column_name = 'IsApproved')
|
||||
AND NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260625184756_PendingChanges');
|
||||
";
|
||||
await cmdSyncHistory.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Seed the history table with the migrations that were already created by EnsureCreated in older versions
|
||||
using (var cmdInsertHistory = conn.CreateCommand())
|
||||
{
|
||||
cmdInsertHistory.CommandText = @"
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"") VALUES
|
||||
('20260520133634_Initial', '10.0.9'),
|
||||
('20260520200000_AddPublicHolidays', '10.0.9'),
|
||||
('20260522081459_AddMultiUser', '10.0.9'),
|
||||
('20260607213215_AddFlexTimeAndHolidayState', '10.0.9');";
|
||||
await cmdInsertHistory.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// DB is empty, MigrateAsync will create everything. We don't need to patch types later if it was purely MigrateAsync...
|
||||
// Wait, SQLite migrations might still create broken defaults on MigrateAsync. We keep EnsurePostgresIntegerPrimaryKeyDefaultAsync below.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,12 +246,9 @@ using (var scope = app.Services.CreateScope())
|
||||
}
|
||||
}
|
||||
|
||||
if (!databaseCreatedFromModel)
|
||||
{
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
|
||||
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase) && !databaseCreatedFromModel)
|
||||
if (dbProvider.Equals("PostgreSQL", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using PdfSharp.Fonts;
|
||||
|
||||
namespace timetracker.Server.Services
|
||||
{
|
||||
public class PdfFontResolver : IFontResolver
|
||||
{
|
||||
public byte[] GetFont(string faceName)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Map faceName to Embedded Resource stream
|
||||
string resourceName = faceName.ToLower() switch
|
||||
{
|
||||
"opensans-bold" => "timetracker.Server.Fonts.OpenSans-Bold.ttf",
|
||||
_ => "timetracker.Server.Fonts.OpenSans-Regular.ttf"
|
||||
};
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not load font resource '{resourceName}'");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
stream.CopyTo(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
|
||||
{
|
||||
if (isBold)
|
||||
{
|
||||
return new FontResolverInfo("OpenSans-Bold");
|
||||
}
|
||||
return new FontResolverInfo("OpenSans-Regular");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using MigraDoc.DocumentObjectModel;
|
||||
using MigraDoc.DocumentObjectModel.Tables;
|
||||
using MigraDoc.Rendering;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -10,12 +10,6 @@ namespace timetracker.Server.Services
|
||||
{
|
||||
public class PdfGeneratorService
|
||||
{
|
||||
public PdfGeneratorService()
|
||||
{
|
||||
// Configure QuestPDF community license
|
||||
QuestPDF.Settings.License = LicenseType.Community;
|
||||
}
|
||||
|
||||
public byte[] GenerateTimesheetPdf(
|
||||
User user,
|
||||
Tenant? tenant,
|
||||
@@ -26,57 +20,76 @@ namespace timetracker.Server.Services
|
||||
List<VacationDay> vacationDays,
|
||||
List<PublicHoliday> holidays)
|
||||
{
|
||||
var document = Document.Create(container =>
|
||||
if (PdfSharp.Fonts.GlobalFontSettings.FontResolver == null)
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.PageColor(Colors.White);
|
||||
page.DefaultTextStyle(x => x.FontSize(10).FontFamily(Fonts.Arial));
|
||||
|
||||
page.Header().Element(c => ComposeHeader(c, user, tenant, year, month));
|
||||
page.Content().Element(c => ComposeContent(c, user, settings, year, month, workDays, vacationDays, holidays));
|
||||
page.Footer().Element(ComposeFooter);
|
||||
});
|
||||
});
|
||||
|
||||
return document.GeneratePdf();
|
||||
PdfSharp.Fonts.GlobalFontSettings.FontResolver = new PdfFontResolver();
|
||||
}
|
||||
|
||||
private void ComposeHeader(IContainer container, User user, Tenant? tenant, int year, int month)
|
||||
var document = new Document();
|
||||
document.Info.Title = "Stundenzettel";
|
||||
document.Info.Author = tenant?.Name ?? "Timetracker";
|
||||
|
||||
var section = document.AddSection();
|
||||
section.PageSetup.PageFormat = PageFormat.A4;
|
||||
section.PageSetup.TopMargin = "2cm";
|
||||
section.PageSetup.BottomMargin = "2cm";
|
||||
section.PageSetup.LeftMargin = "2cm";
|
||||
section.PageSetup.RightMargin = "2cm";
|
||||
|
||||
var style = document.Styles["Normal"];
|
||||
if (style != null && style.Font != null)
|
||||
{
|
||||
style.Font.Name = "OpenSans";
|
||||
style.Font.Size = 10;
|
||||
}
|
||||
|
||||
ComposeHeader(section, user, tenant, year, month);
|
||||
ComposeContent(section, document, user, settings, year, month, workDays, vacationDays, holidays);
|
||||
ComposeFooter(section);
|
||||
|
||||
var pdfRenderer = new PdfDocumentRenderer()
|
||||
{
|
||||
Document = document
|
||||
};
|
||||
pdfRenderer.RenderDocument();
|
||||
|
||||
using var stream = new System.IO.MemoryStream();
|
||||
pdfRenderer.PdfDocument.Save(stream, false);
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
private void ComposeHeader(Section section, User user, Tenant? tenant, int year, int month)
|
||||
{
|
||||
var monthName = new DateTime(year, month, 1).ToString("MMMM yyyy", new System.Globalization.CultureInfo("de-DE"));
|
||||
var companyName = tenant?.Name ?? "Hauptdomain";
|
||||
|
||||
container.Row(row =>
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().Text($"Stundenzettel - {monthName}").FontSize(20).SemiBold().FontColor(Colors.Blue.Darken2);
|
||||
column.Item().Text($"Mitarbeiter: {user.Username}").FontSize(14).SemiBold();
|
||||
column.Item().PaddingTop(2).Text($"Firma: {companyName}").FontSize(11).FontColor(Colors.Grey.Darken2);
|
||||
});
|
||||
});
|
||||
var paragraph = section.AddParagraph($"Stundenzettel - {monthName}");
|
||||
paragraph.Format.Font.Size = 20;
|
||||
paragraph.Format.Font.Bold = true;
|
||||
paragraph.Format.Font.Color = Colors.DarkBlue;
|
||||
paragraph.Format.SpaceAfter = "0.2cm";
|
||||
|
||||
paragraph = section.AddParagraph($"Mitarbeiter: {user.Username}");
|
||||
paragraph.Format.Font.Size = 14;
|
||||
paragraph.Format.Font.Bold = true;
|
||||
paragraph.Format.SpaceAfter = "0.1cm";
|
||||
|
||||
paragraph = section.AddParagraph($"Firma: {companyName}");
|
||||
paragraph.Format.Font.Size = 11;
|
||||
paragraph.Format.Font.Color = Colors.DarkGray;
|
||||
paragraph.Format.SpaceAfter = "1cm";
|
||||
}
|
||||
|
||||
private void ComposeContent(IContainer container, User user, AppSettings settings, int year, int month, List<WorkDay> workDays, List<VacationDay> vacationDays, List<PublicHoliday> holidays)
|
||||
private void ComposeContent(Section section, Document document, User user, AppSettings settings, int year, int month, List<WorkDay> workDays, List<VacationDay> vacationDays, List<PublicHoliday> holidays)
|
||||
{
|
||||
TimeSpan totalNet = TimeSpan.Zero;
|
||||
TimeSpan totalTarget = TimeSpan.Zero;
|
||||
TimeSpan totalOvertime = TimeSpan.Zero;
|
||||
ComposeTable(section, document, settings, year, month, workDays, vacationDays, holidays, out var totalNet, out var totalTarget, out var totalOvertime);
|
||||
|
||||
container.PaddingVertical(1, Unit.Centimetre).Column(column =>
|
||||
{
|
||||
column.Item().Element(c => ComposeTable(c, settings, year, month, workDays, vacationDays, holidays, out totalNet, out totalTarget, out totalOvertime));
|
||||
ComposeSummary(section, totalNet, totalTarget, totalOvertime);
|
||||
|
||||
column.Item().PaddingTop(25).Element(c => ComposeSummary(c, totalNet, totalTarget, totalOvertime));
|
||||
|
||||
column.Item().PaddingTop(50).Element(ComposeSignatures);
|
||||
});
|
||||
ComposeSignatures(section);
|
||||
}
|
||||
|
||||
private void ComposeTable(IContainer container, AppSettings settings, int year, int month, List<WorkDay> workDays, List<VacationDay> vacationDays, List<PublicHoliday> holidays, out TimeSpan totalNetOut, out TimeSpan totalTargetOut, out TimeSpan totalOvertimeOut)
|
||||
private void ComposeTable(Section section, Document document, AppSettings settings, int year, int month, List<WorkDay> workDays, List<VacationDay> vacationDays, List<PublicHoliday> holidays, out TimeSpan totalNetOut, out TimeSpan totalTargetOut, out TimeSpan totalOvertimeOut)
|
||||
{
|
||||
var holidayMap = holidays.ToDictionary(h => h.Date, h => h.Name);
|
||||
var vacationSet = vacationDays.Select(v => v.Date).ToHashSet();
|
||||
@@ -86,34 +99,34 @@ namespace timetracker.Server.Services
|
||||
var totalTarget = TimeSpan.Zero;
|
||||
var totalOvertime = TimeSpan.Zero;
|
||||
|
||||
container.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(70); // Datum
|
||||
columns.ConstantColumn(50); // Start
|
||||
columns.ConstantColumn(50); // Ende
|
||||
columns.ConstantColumn(60); // Pause
|
||||
columns.ConstantColumn(60); // Netto
|
||||
columns.ConstantColumn(60); // Gleitzeit
|
||||
columns.RelativeColumn(); // Info
|
||||
});
|
||||
var table = section.AddTable();
|
||||
table.Borders.Width = 0.5;
|
||||
table.Borders.Color = Colors.LightGray;
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Element(CellStyle).Text("Datum");
|
||||
header.Cell().Element(CellStyle).AlignRight().Text("Start");
|
||||
header.Cell().Element(CellStyle).AlignRight().Text("Ende");
|
||||
header.Cell().Element(CellStyle).AlignRight().Text("Pause");
|
||||
header.Cell().Element(CellStyle).AlignRight().Text("Netto");
|
||||
header.Cell().Element(CellStyle).AlignRight().Text("Gleitzeit");
|
||||
header.Cell().Element(CellStyle).Text("Info/Status");
|
||||
// Define columns
|
||||
table.AddColumn("2.5cm"); // Datum
|
||||
table.AddColumn("1.5cm"); // Start
|
||||
table.AddColumn("1.5cm"); // Ende
|
||||
table.AddColumn("1.8cm"); // Pause
|
||||
table.AddColumn("1.8cm"); // Netto
|
||||
table.AddColumn("1.8cm"); // Gleitzeit
|
||||
table.AddColumn("6.1cm"); // Info
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
{
|
||||
return container.DefaultTextStyle(x => x.SemiBold()).PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
|
||||
}
|
||||
});
|
||||
// Header Row
|
||||
var row = table.AddRow();
|
||||
row.HeadingFormat = true;
|
||||
row.Format.Font.Bold = true;
|
||||
row.Shading.Color = Colors.White;
|
||||
row.Borders.Bottom.Width = 1;
|
||||
row.Borders.Bottom.Color = Colors.Black;
|
||||
|
||||
row.Cells[0].AddParagraph("Datum");
|
||||
row.Cells[1].AddParagraph("Start").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[2].AddParagraph("Ende").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[3].AddParagraph("Pause").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[4].AddParagraph("Netto").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[5].AddParagraph("Gleitzeit").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[6].AddParagraph("Info/Status");
|
||||
|
||||
for (int day = 1; day <= daysInMonth; day++)
|
||||
{
|
||||
@@ -153,75 +166,91 @@ namespace timetracker.Server.Services
|
||||
else if (!isWorkDay) status = "Wochenende";
|
||||
|
||||
var bgColor = Colors.White;
|
||||
if (!isWorkDay || !string.IsNullOrEmpty(holidayName)) bgColor = Colors.Grey.Lighten4;
|
||||
if (isVacation) bgColor = Colors.Orange.Lighten4;
|
||||
if (!isWorkDay || !string.IsNullOrEmpty(holidayName)) bgColor = new Color(245, 245, 245); // Light Gray
|
||||
if (isVacation) bgColor = new Color(255, 236, 179); // Light Orange
|
||||
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).Text(date.ToString("dd.MM.yyyy"));
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).AlignRight().Text(wd?.StartTime?.ToString(@"HH\:mm") ?? "-");
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).AlignRight().Text(wd?.EndTime?.ToString(@"HH\:mm") ?? "-");
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).AlignRight().Text(net.HasValue ? FormatTs(breakSum) : "-");
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).AlignRight().Text(net.HasValue ? FormatTs(net.Value) : "-");
|
||||
row = table.AddRow();
|
||||
row.Shading.Color = bgColor;
|
||||
|
||||
var overtimeColor = overtime < TimeSpan.Zero ? Colors.Red.Darken2 : Colors.Green.Darken2;
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).AlignRight().Text(net.HasValue ? FormatTs(overtime, true) : "-").FontColor(net.HasValue && overtime != TimeSpan.Zero ? overtimeColor : Colors.Black);
|
||||
row.Cells[0].AddParagraph(date.ToString("dd.MM.yyyy"));
|
||||
row.Cells[1].AddParagraph(wd?.StartTime?.ToString(@"HH\:mm") ?? "-").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[2].AddParagraph(wd?.EndTime?.ToString(@"HH\:mm") ?? "-").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[3].AddParagraph(net.HasValue ? FormatTs(breakSum) : "-").Format.Alignment = ParagraphAlignment.Right;
|
||||
row.Cells[4].AddParagraph(net.HasValue ? FormatTs(net.Value) : "-").Format.Alignment = ParagraphAlignment.Right;
|
||||
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).Text(status).FontColor(Colors.Grey.Darken2);
|
||||
|
||||
static IContainer CellStyle(IContainer container, string bgColor)
|
||||
var overtimePara = row.Cells[5].AddParagraph(net.HasValue ? FormatTs(overtime, true) : "-");
|
||||
overtimePara.Format.Alignment = ParagraphAlignment.Right;
|
||||
if (net.HasValue && overtime != TimeSpan.Zero)
|
||||
{
|
||||
return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Background(bgColor).PaddingVertical(3).PaddingHorizontal(2);
|
||||
overtimePara.Format.Font.Color = overtime < TimeSpan.Zero ? Colors.DarkRed : Colors.DarkGreen;
|
||||
}
|
||||
|
||||
var statusPara = row.Cells[6].AddParagraph(status);
|
||||
statusPara.Format.Font.Color = Colors.DarkGray;
|
||||
}
|
||||
});
|
||||
|
||||
totalNetOut = totalNet;
|
||||
totalTargetOut = totalTarget;
|
||||
totalOvertimeOut = totalOvertime;
|
||||
}
|
||||
|
||||
private void ComposeSummary(IContainer container, TimeSpan totalNet, TimeSpan totalTarget, TimeSpan totalOvertime)
|
||||
private void ComposeSummary(Section section, TimeSpan totalNet, TimeSpan totalTarget, TimeSpan totalOvertime)
|
||||
{
|
||||
container.Background(Colors.Grey.Lighten4).Padding(10).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().Text("Zusammenfassung").SemiBold();
|
||||
column.Item().Text($"Soll-Stunden: {FormatTs(totalTarget)}");
|
||||
column.Item().Text($"Ist-Stunden: {FormatTs(totalNet)}");
|
||||
var paragraph = section.AddParagraph();
|
||||
paragraph.Format.SpaceBefore = "1cm";
|
||||
|
||||
var overtimeColor = totalOvertime < TimeSpan.Zero ? Colors.Red.Darken2 : Colors.Green.Darken2;
|
||||
column.Item().Text($"Saldo: {FormatTs(totalOvertime, true)}").SemiBold().FontColor(totalOvertime != TimeSpan.Zero ? overtimeColor : Colors.Black);
|
||||
});
|
||||
});
|
||||
var table = section.AddTable();
|
||||
table.Shading.Color = new Color(245, 245, 245);
|
||||
table.AddColumn("4cm");
|
||||
table.AddColumn("4cm");
|
||||
|
||||
var row = table.AddRow();
|
||||
row.Cells[0].AddParagraph("Zusammenfassung").Format.Font.Bold = true;
|
||||
row.Cells[0].MergeRight = 1;
|
||||
|
||||
row = table.AddRow();
|
||||
row.Cells[0].AddParagraph("Soll-Stunden:");
|
||||
row.Cells[1].AddParagraph(FormatTs(totalTarget));
|
||||
|
||||
row = table.AddRow();
|
||||
row.Cells[0].AddParagraph("Ist-Stunden:");
|
||||
row.Cells[1].AddParagraph(FormatTs(totalNet));
|
||||
|
||||
row = table.AddRow();
|
||||
row.Cells[0].AddParagraph("Saldo:");
|
||||
var saldoPara = row.Cells[1].AddParagraph(FormatTs(totalOvertime, true));
|
||||
saldoPara.Format.Font.Bold = true;
|
||||
if (totalOvertime != TimeSpan.Zero)
|
||||
{
|
||||
saldoPara.Format.Font.Color = totalOvertime < TimeSpan.Zero ? Colors.DarkRed : Colors.DarkGreen;
|
||||
}
|
||||
}
|
||||
|
||||
private void ComposeSignatures(IContainer container)
|
||||
private void ComposeSignatures(Section section)
|
||||
{
|
||||
container.Row(row =>
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().LineHorizontal(1);
|
||||
column.Item().PaddingTop(5).Text("Datum, Unterschrift Mitarbeiter").FontSize(10);
|
||||
});
|
||||
row.ConstantItem(50); // space
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().LineHorizontal(1);
|
||||
column.Item().PaddingTop(5).Text("Datum, Unterschrift Arbeitgeber").FontSize(10);
|
||||
});
|
||||
});
|
||||
var table = section.AddTable();
|
||||
table.Format.SpaceBefore = "2cm";
|
||||
table.AddColumn("7cm");
|
||||
table.AddColumn("2cm");
|
||||
table.AddColumn("7cm");
|
||||
|
||||
var row = table.AddRow();
|
||||
row.Cells[0].Borders.Bottom.Width = 1;
|
||||
row.Cells[2].Borders.Bottom.Width = 1;
|
||||
|
||||
row = table.AddRow();
|
||||
row.Cells[0].AddParagraph("Datum, Unterschrift Mitarbeiter").Format.Font.Size = 8;
|
||||
row.Cells[2].AddParagraph("Datum, Unterschrift Arbeitgeber").Format.Font.Size = 8;
|
||||
}
|
||||
|
||||
private void ComposeFooter(IContainer container)
|
||||
private void ComposeFooter(Section section)
|
||||
{
|
||||
container.AlignCenter().Text(x =>
|
||||
{
|
||||
x.Span("Seite ");
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" von ");
|
||||
x.TotalPages();
|
||||
});
|
||||
var paragraph = section.Footers.Primary.AddParagraph();
|
||||
paragraph.AddText("Seite ");
|
||||
paragraph.AddPageField();
|
||||
paragraph.AddText(" von ");
|
||||
paragraph.AddNumPagesField();
|
||||
paragraph.Format.Alignment = ParagraphAlignment.Center;
|
||||
}
|
||||
|
||||
private static string FormatTs(TimeSpan ts, bool sign = false)
|
||||
|
||||
@@ -1838,3 +1838,323 @@ SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:14:21.574 +02:00 [INF] File logging active. Writing logs to /Users/marcwieland/Uni/Master/Projects/Timetracker/timetracker/timetracker.Server/logs
|
||||
2026-06-26 23:14:22.140 +02:00 [WRN] Entity 'WorkDay' has a global query filter defined and is the required end of a relationship with the entity 'BreakEntry'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.
|
||||
2026-06-26 23:14:22.205 +02:00 [INF] Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
|
||||
2026-06-26 23:14:22.216 +02:00 [INF] Executed DbCommand (5ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsLock' AND "type" = 'table';
|
||||
2026-06-26 23:14:22.220 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
INSERT OR IGNORE INTO "__EFMigrationsLock"("Id", "Timestamp") VALUES(1, '2026-06-26 21:14:22.219046+00:00');
|
||||
SELECT changes();
|
||||
2026-06-26 23:14:22.273 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
||||
"MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY,
|
||||
"ProductVersion" TEXT NOT NULL
|
||||
);
|
||||
2026-06-26 23:14:22.279 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
|
||||
2026-06-26 23:14:22.281 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "MigrationId", "ProductVersion"
|
||||
FROM "__EFMigrationsHistory"
|
||||
ORDER BY "MigrationId";
|
||||
2026-06-26 23:14:22.284 +02:00 [INF] No migrations were applied. The database is already up to date.
|
||||
2026-06-26 23:14:22.286 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
DELETE FROM "__EFMigrationsLock";
|
||||
2026-06-26 23:14:22.412 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "u"."Id", "u"."IsTenantAdmin", "u"."PasswordHash", "u"."PasswordSalt", "u"."TenantId", "u"."Username"
|
||||
FROM "Users" AS "u"
|
||||
WHERE "u"."TenantId" IS NULL AND "u"."Username" = 'marc'
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:22.920 +02:00 [INF] Executed DbCommand (2ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:22.958 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:22.969 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:14:23.006 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[@userId='?' (DbType = Int32), @monday='?' (DbType = Date), @AddDays='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @userId AND "w"."Date" >= @monday AND "w"."Date" < @AddDays
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:14:23.011 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:14:23.026 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Date"
|
||||
FROM "WorkDays" AS "w"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId
|
||||
ORDER BY "w"."Date"
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:23.052 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @startDate AND "w"."Date" <= @endDate
|
||||
ORDER BY "w"."Id"
|
||||
2026-06-26 23:14:23.072 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE "p"."Date" >= @startDate AND "p"."Date" <= @endDate
|
||||
2026-06-26 23:14:23.083 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Date"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @_8__locals1_userId AND "v"."Date" >= @startDate AND "v"."Date" <= @endDate
|
||||
2026-06-26 23:14:23.879 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:28.518 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:14:28.518 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:14:28.527 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @from='?' (DbType = Date), @to='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @from AND "w"."Date" < @to
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:14:33.174 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "u"."Id", "u"."IsTenantAdmin", "u"."PasswordHash", "u"."PasswordSalt", "u"."TenantId", "u"."Username"
|
||||
FROM "Users" AS "u"
|
||||
WHERE "u"."Id" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:33.175 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @from='?' (DbType = Date), @to='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @from AND "w"."Date" < @to
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:14:33.176 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:14:33.177 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:33.178 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:14:33.200 +02:00 [ERR] An unhandled exception has occurred while executing the request.
|
||||
System.InvalidOperationException: The font 'Courier New' cannot be resolved for predefined error font. Use another font name or fix your font resolver. See https://docs.pdfsharp.net/link/migradoc-font-resolving-6.2.html and https://docs.pdfsharp.net/link/font-resolving.html for further information.
|
||||
---> System.InvalidOperationException: No appropriate font found for family name 'Courier New'. Implement IFontResolver and assign to 'GlobalFontSettings.FontResolver' to use fonts. See https://docs.pdfsharp.net/link/font-resolving.html
|
||||
at PdfSharp.Drawing.XGlyphTypeface.GetOrCreateFrom(String familyName, FontResolvingOptions fontResolvingOptions)
|
||||
at PdfSharp.Drawing.XFont.Initialize()
|
||||
at PdfSharp.Drawing.XFont..ctor(String familyName, Double emSize, XFontStyleEx style, XPdfFontOptions pdfOptions)
|
||||
at PdfSharp.Drawing.XFont..ctor(String familyName, Double emSize, XFontStyleEx style)
|
||||
at MigraDoc.Rendering.DocumentRenderer.PredefinedFontsAndChars.CreateFont(String familyName, Double emSize, XFontStyleEx style, String propertyDescription)
|
||||
--- End of inner exception stack trace ---
|
||||
at MigraDoc.Rendering.DocumentRenderer.PredefinedFontsAndChars.CreateFont(String familyName, Double emSize, XFontStyleEx style, String propertyDescription)
|
||||
at MigraDoc.Rendering.DocumentRenderer.PredefinedFontsAndChars.get_ErrorFont()
|
||||
at MigraDoc.Rendering.DocumentRenderer.PredefinedFontsAndChars.CreateAllFixedFonts()
|
||||
at MigraDoc.Rendering.PdfDocumentRenderer.PrepareDocumentRenderer(Boolean prepareCompletely)
|
||||
at MigraDoc.Rendering.PdfDocumentRenderer.PrepareRenderPages()
|
||||
at MigraDoc.Rendering.PdfDocumentRenderer.RenderDocument()
|
||||
at timetracker.Server.Services.PdfGeneratorService.GenerateTimesheetPdf(User user, Tenant tenant, AppSettings settings, Int32 year, Int32 month, List`1 workDays, List`1 vacationDays, List`1 holidays) in /Users/marcwieland/Uni/Master/Projects/Timetracker/timetracker/timetracker.Server/Services/PdfGeneratorService.cs:line 50
|
||||
at Program.<>c.<<<Main>$>b__0_27>d.MoveNext() in /Users/marcwieland/Uni/Master/Projects/Timetracker/timetracker/timetracker.Server/Program.cs:line 779
|
||||
--- End of stack trace from previous location ---
|
||||
at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteTaskResult[T](Task`1 task, HttpContext httpContext)
|
||||
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
|
||||
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
|
||||
at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
|
||||
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
|
||||
2026-06-26 23:14:33.313 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:57.355 +02:00 [INF] File logging active. Writing logs to /Users/marcwieland/Uni/Master/Projects/Timetracker/timetracker/timetracker.Server/logs
|
||||
2026-06-26 23:14:57.589 +02:00 [WRN] Entity 'WorkDay' has a global query filter defined and is the required end of a relationship with the entity 'BreakEntry'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.
|
||||
2026-06-26 23:14:57.653 +02:00 [INF] Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
|
||||
2026-06-26 23:14:57.665 +02:00 [INF] Executed DbCommand (5ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsLock' AND "type" = 'table';
|
||||
2026-06-26 23:14:57.669 +02:00 [INF] Executed DbCommand (2ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
INSERT OR IGNORE INTO "__EFMigrationsLock"("Id", "Timestamp") VALUES(1, '2026-06-26 21:14:57.667524+00:00');
|
||||
SELECT changes();
|
||||
2026-06-26 23:14:57.721 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
||||
"MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY,
|
||||
"ProductVersion" TEXT NOT NULL
|
||||
);
|
||||
2026-06-26 23:14:57.727 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
|
||||
2026-06-26 23:14:57.729 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "MigrationId", "ProductVersion"
|
||||
FROM "__EFMigrationsHistory"
|
||||
ORDER BY "MigrationId";
|
||||
2026-06-26 23:14:57.732 +02:00 [INF] No migrations were applied. The database is already up to date.
|
||||
2026-06-26 23:14:57.734 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
DELETE FROM "__EFMigrationsLock";
|
||||
2026-06-26 23:14:57.860 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "u"."Id", "u"."IsTenantAdmin", "u"."PasswordHash", "u"."PasswordSalt", "u"."TenantId", "u"."Username"
|
||||
FROM "Users" AS "u"
|
||||
WHERE "u"."TenantId" IS NULL AND "u"."Username" = 'marc'
|
||||
LIMIT 1
|
||||
2026-06-26 23:14:58.019 +02:00 [ERR] Hosting failed to start
|
||||
System.IO.IOException: Failed to bind to address http://127.0.0.1:5065: address already in use.
|
||||
---> Microsoft.AspNetCore.Connections.AddressInUseException: Address already in use
|
||||
---> System.Net.Sockets.SocketException (48): Address already in use
|
||||
at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
|
||||
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
|
||||
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
|
||||
--- End of inner exception stack trace ---
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>c__DisplayClass28_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()
|
||||
--- End of stack trace from previous location ---
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
|
||||
--- End of inner exception stack trace ---
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.LocalhostListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
|
||||
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
|
||||
at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__14_1(IHostedService service, CancellationToken token)
|
||||
at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
|
||||
2026-06-26 23:16:46.101 +02:00 [INF] File logging active. Writing logs to /Users/marcwieland/Uni/Master/Projects/Timetracker/timetracker/timetracker.Server/logs
|
||||
2026-06-26 23:16:46.558 +02:00 [WRN] Entity 'WorkDay' has a global query filter defined and is the required end of a relationship with the entity 'BreakEntry'. This may lead to unexpected results when the required entity is filtered out. Either configure the navigation as optional, or define matching query filters for both entities in the navigation. See https://go.microsoft.com/fwlink/?linkid=2131316 for more information.
|
||||
2026-06-26 23:16:46.626 +02:00 [INF] Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
|
||||
2026-06-26 23:16:46.649 +02:00 [INF] Executed DbCommand (5ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsLock' AND "type" = 'table';
|
||||
2026-06-26 23:16:46.658 +02:00 [INF] Executed DbCommand (3ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
INSERT OR IGNORE INTO "__EFMigrationsLock"("Id", "Timestamp") VALUES(1, '2026-06-26 21:16:46.655104+00:00');
|
||||
SELECT changes();
|
||||
2026-06-26 23:16:46.737 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
|
||||
"MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY,
|
||||
"ProductVersion" TEXT NOT NULL
|
||||
);
|
||||
2026-06-26 23:16:46.743 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT COUNT(*) FROM "sqlite_master" WHERE "name" = '__EFMigrationsHistory' AND "type" = 'table';
|
||||
2026-06-26 23:16:46.745 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "MigrationId", "ProductVersion"
|
||||
FROM "__EFMigrationsHistory"
|
||||
ORDER BY "MigrationId";
|
||||
2026-06-26 23:16:46.748 +02:00 [INF] No migrations were applied. The database is already up to date.
|
||||
2026-06-26 23:16:46.749 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
DELETE FROM "__EFMigrationsLock";
|
||||
2026-06-26 23:16:46.876 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "u"."Id", "u"."IsTenantAdmin", "u"."PasswordHash", "u"."PasswordSalt", "u"."TenantId", "u"."Username"
|
||||
FROM "Users" AS "u"
|
||||
WHERE "u"."TenantId" IS NULL AND "u"."Username" = 'marc'
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:47.560 +02:00 [INF] Executed DbCommand (2ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:47.601 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:47.613 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:16:47.651 +02:00 [INF] Executed DbCommand (1ms) [Parameters=[@userId='?' (DbType = Int32), @monday='?' (DbType = Date), @AddDays='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @userId AND "w"."Date" >= @monday AND "w"."Date" < @AddDays
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:16:47.656 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:16:47.668 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Date"
|
||||
FROM "WorkDays" AS "w"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId
|
||||
ORDER BY "w"."Date"
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:47.677 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @startDate AND "w"."Date" <= @endDate
|
||||
ORDER BY "w"."Id"
|
||||
2026-06-26 23:16:47.688 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE "p"."Date" >= @startDate AND "p"."Date" <= @endDate
|
||||
2026-06-26 23:16:47.694 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @startDate='?' (DbType = Date), @endDate='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Date"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @_8__locals1_userId AND "v"."Date" >= @startDate AND "v"."Date" <= @endDate
|
||||
2026-06-26 23:16:48.500 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:50.033 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:16:50.033 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
2026-06-26 23:16:50.041 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @from='?' (DbType = Date), @to='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @from AND "w"."Date" < @to
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:16:51.302 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "u"."Id", "u"."IsTenantAdmin", "u"."PasswordHash", "u"."PasswordSalt", "u"."TenantId", "u"."Username"
|
||||
FROM "Users" AS "u"
|
||||
WHERE "u"."Id" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:51.303 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@_8__locals1_userId='?' (DbType = Int32), @from='?' (DbType = Date), @to='?' (DbType = Date)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "w"."Id", "w"."Date", "w"."EndTime", "w"."StartTime", "w"."TenantId", "w"."UserId", "b"."Id", "b"."EndTime", "b"."StartTime", "b"."WorkDayId"
|
||||
FROM "WorkDays" AS "w"
|
||||
LEFT JOIN "BreakEntries" AS "b" ON "w"."Id" = "b"."WorkDayId"
|
||||
WHERE "w"."TenantId" IS NULL AND "w"."UserId" = @_8__locals1_userId AND "w"."Date" >= @from AND "w"."Date" < @to
|
||||
ORDER BY "w"."Date", "w"."Id"
|
||||
2026-06-26 23:16:51.303 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32), @year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "v"."Id", "v"."Date", "v"."Note", "v"."TenantId", "v"."UserId"
|
||||
FROM "VacationDays" AS "v"
|
||||
WHERE "v"."TenantId" IS NULL AND "v"."UserId" = @userId AND CAST(strftime('%Y', "v"."Date") AS INTEGER) = @year
|
||||
ORDER BY "v"."Date"
|
||||
2026-06-26 23:16:51.303 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@userId='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "a"."Id", "a"."DailyTargetHours", "a"."FlexTimeStartDate", "a"."FlexTimeStartingBalanceHours", "a"."GermanState", "a"."MinimumBreakMinutes", "a"."TenantId", "a"."UserId", "a"."VacationDaysPerYear", "a"."WorkFriday", "a"."WorkMonday", "a"."WorkSaturday", "a"."WorkSunday", "a"."WorkThursday", "a"."WorkTuesday", "a"."WorkWednesday"
|
||||
FROM "AppSettings" AS "a"
|
||||
WHERE "a"."TenantId" IS NULL AND "a"."UserId" = @userId
|
||||
LIMIT 1
|
||||
2026-06-26 23:16:51.304 +02:00 [INF] Executed DbCommand (0ms) [Parameters=[@year='?' (DbType = Int32)], CommandType='"Text"', CommandTimeout='30']
|
||||
SELECT "p"."Id", "p"."Counties", "p"."Date", "p"."Name"
|
||||
FROM "PublicHolidays" AS "p"
|
||||
WHERE CAST(strftime('%Y', "p"."Date") AS INTEGER) = @year
|
||||
ORDER BY "p"."Date"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.8" />
|
||||
<PackageReference Include="MudBlazor" Version="9.4.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||
<PackageReference Include="QuestPDF" Version="2026.6.1" />
|
||||
<PackageReference Include="PDFsharp-MigraDoc" Version="6.2.4" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" />
|
||||
@@ -25,4 +25,8 @@
|
||||
<ProjectReference Include="..\timetracker.Client\timetracker.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Fonts\*.ttf" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user