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,66 +172,71 @@ using (var scope = app.Services.CreateScope())
|
||||
await conn.OpenAsync();
|
||||
}
|
||||
|
||||
using (var cmdCheckTables = conn.CreateCommand())
|
||||
// Check if AppSettings table already exists
|
||||
using (var cmdCheck = 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)
|
||||
cmdCheck.CommandText = "SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'AppSettings');";
|
||||
var appSettingsExists = (bool)(await cmdCheck.ExecuteScalarAsync() ?? false);
|
||||
|
||||
if (appSettingsExists)
|
||||
{
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
databaseCreatedFromModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!databaseCreatedFromModel)
|
||||
{
|
||||
|
||||
// Check if AppSettings table already exists
|
||||
using (var cmdCheck = conn.CreateCommand())
|
||||
{
|
||||
cmdCheck.CommandText = "SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname = 'public' AND tablename = 'AppSettings');";
|
||||
var appSettingsExists = (bool)(await cmdCheck.ExecuteScalarAsync() ?? false);
|
||||
|
||||
if (appSettingsExists)
|
||||
// Ensure __EFMigrationsHistory table exists
|
||||
using (var cmdCreateHistory = conn.CreateCommand())
|
||||
{
|
||||
// Ensure __EFMigrationsHistory table exists
|
||||
using (var cmdCreateHistory = conn.CreateCommand())
|
||||
{
|
||||
cmdCreateHistory.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS ""__EFMigrationsHistory"" (
|
||||
""MigrationId"" character varying(150) NOT NULL,
|
||||
""ProductVersion"" character varying(32) NOT NULL,
|
||||
CONSTRAINT ""PK___EFMigrationsHistory"" PRIMARY KEY (""MigrationId"")
|
||||
);";
|
||||
await cmdCreateHistory.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Check if the initial migration is already recorded
|
||||
using (var cmdCheckHistory = conn.CreateCommand())
|
||||
{
|
||||
cmdCheckHistory.CommandText = @"SELECT COUNT(*) FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520133634_Initial';";
|
||||
var historyCount = Convert.ToInt32(await cmdCheckHistory.ExecuteScalarAsync() ?? 0);
|
||||
|
||||
if (historyCount == 0)
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
cmdCreateHistory.CommandText = @"
|
||||
CREATE TABLE IF NOT EXISTS ""__EFMigrationsHistory"" (
|
||||
""MigrationId"" character varying(150) NOT NULL,
|
||||
""ProductVersion"" character varying(32) NOT NULL,
|
||||
CONSTRAINT ""PK___EFMigrationsHistory"" PRIMARY KEY (""MigrationId"")
|
||||
);";
|
||||
await cmdCreateHistory.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
// Seed the history table conditionally for each migration based on actual schema presence
|
||||
using (var cmdSyncHistory = conn.CreateCommand())
|
||||
{
|
||||
cmdSyncHistory.CommandText = @"
|
||||
INSERT INTO ""__EFMigrationsHistory"" (""MigrationId"", ""ProductVersion"")
|
||||
SELECT '20260520133634_Initial', '10.0.9'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM ""__EFMigrationsHistory"" WHERE ""MigrationId"" = '20260520133634_Initial');
|
||||
|
||||
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
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
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));
|
||||
PdfSharp.Fonts.GlobalFontSettings.FontResolver = new PdfFontResolver();
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
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";
|
||||
|
||||
return document.GeneratePdf();
|
||||
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(IContainer container, User user, Tenant? tenant, int year, int month)
|
||||
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);
|
||||
|
||||
ComposeSummary(section, totalNet, totalTarget, 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));
|
||||
|
||||
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,142 +99,158 @@ namespace timetracker.Server.Services
|
||||
var totalTarget = TimeSpan.Zero;
|
||||
var totalOvertime = TimeSpan.Zero;
|
||||
|
||||
container.Table(table =>
|
||||
var table = section.AddTable();
|
||||
table.Borders.Width = 0.5;
|
||||
table.Borders.Color = Colors.LightGray;
|
||||
|
||||
// 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
|
||||
|
||||
// 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++)
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
var date = new DateOnly(year, month, day);
|
||||
var wd = workDays.FirstOrDefault(w => w.Date == date);
|
||||
var isWorkDay = settings.IsWorkDay(date.DayOfWeek);
|
||||
var isVacation = vacationSet.Contains(date);
|
||||
var holidayName = holidayMap.GetValueOrDefault(date);
|
||||
|
||||
TimeSpan? net = null;
|
||||
TimeSpan breakSum = TimeSpan.Zero;
|
||||
|
||||
if (wd?.StartTime != null && wd.EndTime != null)
|
||||
{
|
||||
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
|
||||
});
|
||||
|
||||
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");
|
||||
|
||||
static IContainer CellStyle(IContainer container)
|
||||
{
|
||||
return container.DefaultTextStyle(x => x.SemiBold()).PaddingVertical(5).BorderBottom(1).BorderColor(Colors.Black);
|
||||
}
|
||||
});
|
||||
|
||||
for (int day = 1; day <= daysInMonth; day++)
|
||||
{
|
||||
var date = new DateOnly(year, month, day);
|
||||
var wd = workDays.FirstOrDefault(w => w.Date == date);
|
||||
var isWorkDay = settings.IsWorkDay(date.DayOfWeek);
|
||||
var isVacation = vacationSet.Contains(date);
|
||||
var holidayName = holidayMap.GetValueOrDefault(date);
|
||||
|
||||
TimeSpan? net = null;
|
||||
TimeSpan breakSum = TimeSpan.Zero;
|
||||
|
||||
if (wd?.StartTime != null && wd.EndTime != null)
|
||||
{
|
||||
var gross = wd.EndTime.Value.ToTimeSpan() - wd.StartTime.Value.ToTimeSpan();
|
||||
breakSum = wd.Breaks
|
||||
.Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime)
|
||||
.Aggregate(TimeSpan.Zero, (s, b) =>
|
||||
s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan()));
|
||||
var minMinutes = gross.TotalHours >= 9.0 ? 45 : 30;
|
||||
var minBreak = TimeSpan.FromMinutes(Math.Max(minMinutes, settings.MinimumBreakMinutes));
|
||||
var finalBreak = breakSum > minBreak ? breakSum : minBreak;
|
||||
net = gross - finalBreak;
|
||||
if (net < TimeSpan.Zero) net = null;
|
||||
}
|
||||
|
||||
var target = isWorkDay ? TimeSpan.FromHours(settings.DailyTargetHours) : TimeSpan.Zero;
|
||||
var overtime = net.HasValue ? net.Value - target : TimeSpan.Zero;
|
||||
|
||||
if (net.HasValue) totalNet += net.Value;
|
||||
if (isWorkDay) totalTarget += target;
|
||||
if (isWorkDay && net.HasValue) totalOvertime += overtime;
|
||||
|
||||
string status = "";
|
||||
if (!string.IsNullOrEmpty(holidayName)) status = holidayName;
|
||||
else if (isVacation) status = "Urlaub";
|
||||
else if (!isWorkDay) status = "Wochenende";
|
||||
|
||||
var bgColor = Colors.White;
|
||||
if (!isWorkDay || !string.IsNullOrEmpty(holidayName)) bgColor = Colors.Grey.Lighten4;
|
||||
if (isVacation) bgColor = Colors.Orange.Lighten4;
|
||||
|
||||
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) : "-");
|
||||
|
||||
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);
|
||||
|
||||
table.Cell().Element(c => CellStyle(c, bgColor)).Text(status).FontColor(Colors.Grey.Darken2);
|
||||
|
||||
static IContainer CellStyle(IContainer container, string bgColor)
|
||||
{
|
||||
return container.BorderBottom(1).BorderColor(Colors.Grey.Lighten3).Background(bgColor).PaddingVertical(3).PaddingHorizontal(2);
|
||||
}
|
||||
var gross = wd.EndTime.Value.ToTimeSpan() - wd.StartTime.Value.ToTimeSpan();
|
||||
breakSum = wd.Breaks
|
||||
.Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime)
|
||||
.Aggregate(TimeSpan.Zero, (s, b) =>
|
||||
s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan()));
|
||||
var minMinutes = gross.TotalHours >= 9.0 ? 45 : 30;
|
||||
var minBreak = TimeSpan.FromMinutes(Math.Max(minMinutes, settings.MinimumBreakMinutes));
|
||||
var finalBreak = breakSum > minBreak ? breakSum : minBreak;
|
||||
net = gross - finalBreak;
|
||||
if (net < TimeSpan.Zero) net = null;
|
||||
}
|
||||
});
|
||||
|
||||
var target = isWorkDay ? TimeSpan.FromHours(settings.DailyTargetHours) : TimeSpan.Zero;
|
||||
var overtime = net.HasValue ? net.Value - target : TimeSpan.Zero;
|
||||
|
||||
if (net.HasValue) totalNet += net.Value;
|
||||
if (isWorkDay) totalTarget += target;
|
||||
if (isWorkDay && net.HasValue) totalOvertime += overtime;
|
||||
|
||||
string status = "";
|
||||
if (!string.IsNullOrEmpty(holidayName)) status = holidayName;
|
||||
else if (isVacation) status = "Urlaub";
|
||||
else if (!isWorkDay) status = "Wochenende";
|
||||
|
||||
var bgColor = Colors.White;
|
||||
if (!isWorkDay || !string.IsNullOrEmpty(holidayName)) bgColor = new Color(245, 245, 245); // Light Gray
|
||||
if (isVacation) bgColor = new Color(255, 236, 179); // Light Orange
|
||||
|
||||
row = table.AddRow();
|
||||
row.Shading.Color = bgColor;
|
||||
|
||||
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;
|
||||
|
||||
var overtimePara = row.Cells[5].AddParagraph(net.HasValue ? FormatTs(overtime, true) : "-");
|
||||
overtimePara.Format.Alignment = ParagraphAlignment.Right;
|
||||
if (net.HasValue && overtime != TimeSpan.Zero)
|
||||
{
|
||||
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 =>
|
||||
var paragraph = section.AddParagraph();
|
||||
paragraph.Format.SpaceBefore = "1cm";
|
||||
|
||||
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)
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().Text("Zusammenfassung").SemiBold();
|
||||
column.Item().Text($"Soll-Stunden: {FormatTs(totalTarget)}");
|
||||
column.Item().Text($"Ist-Stunden: {FormatTs(totalNet)}");
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
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