QR-Scanner: Kamera-Fallbacks und Foto-Upload; weitere Aenderungen
This commit is contained in:
1 parent
7a0406eaa2
commit
e2aef67099
13 files changed
+965
-58
No files matched your search
@@ -0,0 +1,184 @@
|
||||
using Lagerbestand.Models;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace Lagerbestand.Services;
|
||||
|
||||
public interface IStorageRowPdfService
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a printable A4 inventory sheet for one shelf row (Area + Row).
|
||||
/// </summary>
|
||||
byte[] GenerateRowPdf(AreaType area, int row, IReadOnlyList<StorageLocation> locations);
|
||||
}
|
||||
|
||||
public class StorageRowPdfService : IStorageRowPdfService
|
||||
{
|
||||
private static readonly string HeaderBackground = Colors.Grey.Lighten3;
|
||||
private static readonly string BorderColor = Colors.Grey.Medium;
|
||||
|
||||
public byte[] GenerateRowPdf(AreaType area, int row, IReadOnlyList<StorageLocation> locations)
|
||||
{
|
||||
var rowLocations = locations
|
||||
.Where(l => l.Area == area && l.Row == row)
|
||||
.ToList();
|
||||
|
||||
var occupied = rowLocations.Count(l => l.IsOccupied);
|
||||
var total = StorageLocation.PositionsPerSlot * StorageLocation.MaxSlot;
|
||||
var printedAt = DateTime.Now;
|
||||
|
||||
var document = Document.Create(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(1.5f, Unit.Centimetre);
|
||||
page.DefaultTextStyle(x => x.FontSize(9));
|
||||
|
||||
page.Header()
|
||||
.PaddingBottom(12)
|
||||
.AlignCenter()
|
||||
.Text($"{area.GetDisplayName()} – Regal {row}")
|
||||
.Bold()
|
||||
.FontSize(20);
|
||||
|
||||
page.Content().Row(r =>
|
||||
{
|
||||
r.RelativeItem().Column(col =>
|
||||
{
|
||||
col.Spacing(14);
|
||||
col.Item().Element(c => RenderPositionTable(c, area, row, ShelfPosition.LinksOben, rowLocations));
|
||||
col.Item().Element(c => RenderPositionTable(c, area, row, ShelfPosition.LinksUnten, rowLocations));
|
||||
});
|
||||
|
||||
r.ConstantItem(14);
|
||||
|
||||
r.RelativeItem().Column(col =>
|
||||
{
|
||||
col.Spacing(14);
|
||||
col.Item().Element(c => RenderPositionTable(c, area, row, ShelfPosition.RechtsOben, rowLocations));
|
||||
col.Item().Element(c => RenderPositionTable(c, area, row, ShelfPosition.RechtsUnten, rowLocations));
|
||||
});
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.PaddingTop(10)
|
||||
.Row(f =>
|
||||
{
|
||||
f.RelativeItem().Text($"Gedruckt am {printedAt:dd.MM.yyyy HH:mm}").FontSize(8).FontColor(Colors.Grey.Darken1);
|
||||
f.RelativeItem().AlignRight().Text($"{occupied} / {total} belegt").FontSize(8).FontColor(Colors.Grey.Darken1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return document.GeneratePdf();
|
||||
}
|
||||
|
||||
private static void RenderPositionTable(IContainer container, AreaType area, int row, ShelfPosition position, List<StorageLocation> rowLocations)
|
||||
{
|
||||
var bySlot = rowLocations
|
||||
.Where(l => l.Position == position)
|
||||
.GroupBy(l => l.SlotNumber)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
|
||||
container.Column(col =>
|
||||
{
|
||||
col.Spacing(4);
|
||||
col.Item().Text(position.GetDisplayName()).Bold().FontSize(11);
|
||||
|
||||
col.Item().Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(28);
|
||||
columns.ConstantColumn(34);
|
||||
columns.RelativeColumn(3);
|
||||
columns.RelativeColumn(2);
|
||||
columns.ConstantColumn(44);
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Element(HeaderCell).Text("Platz");
|
||||
header.Cell().Element(HeaderCell).Text("Code");
|
||||
header.Cell().Element(HeaderCell).Text("Kunde/Inhalt");
|
||||
header.Cell().Element(HeaderCell).Text("Fahrzeug");
|
||||
header.Cell().Element(HeaderCell).Text("Saison");
|
||||
});
|
||||
|
||||
for (var slot = StorageLocation.MinSlot; slot <= StorageLocation.MaxSlot; slot++)
|
||||
{
|
||||
bySlot.TryGetValue(slot, out var location);
|
||||
var code = location?.Code ?? $"{(int)area}{row}{(int)position}{slot}";
|
||||
|
||||
string content, vehicle, season;
|
||||
var muted = false;
|
||||
|
||||
if (location is { IsOccupied: true, OccupiedTireStack: not null })
|
||||
{
|
||||
var stack = location.OccupiedTireStack;
|
||||
content = stack.Customer?.FullName ?? stack.CatalogItem?.Name ?? "Lager";
|
||||
vehicle = stack.Vehicle?.ShortName ?? "–";
|
||||
season = SeasonText(stack.Season);
|
||||
}
|
||||
else if (location is { IsOccupied: true })
|
||||
{
|
||||
content = "Belegt";
|
||||
vehicle = "–";
|
||||
season = "–";
|
||||
}
|
||||
else if (location is { IsAvailable: false })
|
||||
{
|
||||
content = "Gesperrt";
|
||||
vehicle = "–";
|
||||
season = "–";
|
||||
muted = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
content = "Frei";
|
||||
vehicle = "–";
|
||||
season = "–";
|
||||
muted = true;
|
||||
}
|
||||
|
||||
table.Cell().Element(BodyCell).Text(slot.ToString());
|
||||
table.Cell().Element(BodyCell).Text(code);
|
||||
table.Cell().Element(BodyCell).Text(t => Styled(t.Span(content), muted));
|
||||
table.Cell().Element(BodyCell).Text(t => Styled(t.Span(vehicle), muted));
|
||||
table.Cell().Element(BodyCell).Text(t => Styled(t.Span(season), muted));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void Styled(TextSpanDescriptor span, bool muted)
|
||||
{
|
||||
if (muted)
|
||||
span.FontColor(Colors.Grey.Medium).Italic();
|
||||
}
|
||||
|
||||
private static string SeasonText(TireSeason season) => season switch
|
||||
{
|
||||
TireSeason.Sommer => "Sommer",
|
||||
TireSeason.Winter => "Winter",
|
||||
TireSeason.Allrad => "Ganzjahr",
|
||||
_ => season.ToString()
|
||||
};
|
||||
|
||||
private static IContainer HeaderCell(IContainer container) => container
|
||||
.Background(HeaderBackground)
|
||||
.Border(0.5f)
|
||||
.BorderColor(BorderColor)
|
||||
.PaddingVertical(3)
|
||||
.PaddingHorizontal(4)
|
||||
.DefaultTextStyle(x => x.Bold().FontSize(8));
|
||||
|
||||
private static IContainer BodyCell(IContainer container) => container
|
||||
.Border(0.5f)
|
||||
.BorderColor(BorderColor)
|
||||
.PaddingVertical(3)
|
||||
.PaddingHorizontal(4)
|
||||
.DefaultTextStyle(x => x.FontSize(8));
|
||||
}
|
||||
Reference in new issue
Block a user