auto.bus_api/Server/Services/ReportService.cs

474 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using MigraDocCore.DocumentObjectModel;
using MigraDocCore.DocumentObjectModel.Tables;
using MigraDocCore.Rendering;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using Server.Data;
using Server.Models;
namespace Server.Services;
public class ReportService : IReportService
{
private readonly ApplicationDbContext _dbContext;
public ReportService(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<(bool IsSucceed, string? message, Stream ticketPdf)> GetTicket(int ticketGroupId)
{
var dbTicketGroup = await _dbContext.TicketGroups
.Include(tg => tg.User)
.Include(tg => tg.Tickets)
.ThenInclude(t => t.VehicleEnrollment)
.ThenInclude(ve => ve.Vehicle)
.ThenInclude(v => v.Company)
.Include(tg => tg.User)
.Include(tg => tg.Tickets)
.ThenInclude(t => t.VehicleEnrollment)
.ThenInclude(ve => ve.Route)
.ThenInclude(r => r.RouteAddresses)
.ThenInclude(ra => ra.Address)
.ThenInclude(a => a.City)
.ThenInclude(c => c.State)
.ThenInclude(s => s.Country)
.Include(tg => tg.User)
.Include(tg => tg.Tickets)
.ThenInclude(t => t.VehicleEnrollment)
.ThenInclude(ve => ve.RouteAddressDetails)
.FirstOrDefaultAsync(tg => tg.Id == ticketGroupId);
// Define document
var document = new PdfDocument();
document.Info.Title = "ticket";
document.Info.Author = "auto.bus";
// Craft document
var pdfPage = document.AddPage();
pdfPage.Width = XUnit.FromCentimeter(21.0);
pdfPage.Height = XUnit.FromCentimeter(29.7);
var gfx = XGraphics.FromPdfPage(pdfPage);
// HACK²
gfx.MUH = PdfFontEncoding.Unicode;
var doc = new Document();
doc.DefaultPageSetup.LeftMargin = Unit.FromCentimeter(1);
doc.DefaultPageSetup.RightMargin = Unit.FromCentimeter(1);
doc.DefaultPageSetup.MirrorMargins = true;
DefineStyles(doc);
CreatePage(doc);
FillPage(doc, dbTicketGroup);
var docRender = new DocumentRenderer(doc);
docRender.PrepareDocument();
docRender.RenderPage(gfx, 1);
// Save document
var memoryStream = new MemoryStream();
document.Save(memoryStream);
return (true, null, memoryStream);
void DefineStyles(Document doc)
{
var styles = doc.Styles["Normal"];
styles.Font.Name = "Courier New Cyr";
styles = doc.Styles.AddStyle("Table", "Normal");
styles.Font.Size = 10;
styles.ParagraphFormat.SpaceBefore = 2.5;
styles.ParagraphFormat.SpaceAfter = 2.5;
}
void CreatePage(Document doc)
{
var section = doc.AddSection();
// Create header
var paragraph = section.Headers.Primary.AddParagraph();
paragraph.AddText("auto.bus");
paragraph.Style = StyleNames.Header;
paragraph.Format.Font.Size = 20;
paragraph.Format.Font.Bold = true;
paragraph = section.Headers.Primary.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Size = 0;
paragraph.Format.Borders.Top = new Border { Width = 1, Color = Colors.Black };
paragraph.Format.Borders.Bottom = new Border { Color = Colors.Transparent };
// Add title
paragraph = section.AddParagraph();
paragraph.AddText("Посадочний документ");
paragraph.Format.Font.Size = 20;
paragraph.Format.Font.Bold = true;
// Add break line before table
paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Size = 0;
paragraph.Format.Borders.Top = new Border { Width = 1, Color = Colors.Black };
paragraph.Format.Borders.Bottom = new Border { Color = Colors.Transparent };
paragraph.Format.Borders.Style = BorderStyle.DashLargeGap;
paragraph.Format.SpaceBefore = 5;
paragraph.Format.SpaceAfter = 5;
// Add table and define columns
var table = section.AddTable();
table.Style = "Table";
table.Borders.Color = Colors.Black;
table.Borders.Width = 0.25;
table.Borders.Left.Width = 0.5;
table.Borders.Right.Width = 0.5;
table.Rows.LeftIndent = 0;
table.Rows.Height = Unit.FromPoint(15);
table.Rows.VerticalAlignment = VerticalAlignment.Center;
for (int i = 0; i < 12; i++)
{
var column = table.AddColumn(Unit.FromCentimeter(1.583));
column.Format.Alignment = ParagraphAlignment.Center;
}
// Add break line after table
paragraph = section.AddParagraph();
paragraph.Format.Alignment = ParagraphAlignment.Center;
paragraph.Format.Font.Size = 0;
paragraph.Format.Borders.Top = new Border { Width = 1, Color = Colors.Black };
paragraph.Format.Borders.Bottom = new Border { Color = Colors.Transparent };
paragraph.Format.Borders.Style = BorderStyle.DashLargeGap;
paragraph.Format.SpaceBefore = 5;
paragraph.Format.SpaceAfter = 5;
}
void FillPage(Document doc, TicketGroup ticketGroup)
{
var table = doc.LastSection.LastTable;
var row = table.AddRow();
table.AddRow();
row.Cells[0].MergeRight = 2;
row.Cells[0].MergeDown = 1;
row.Cells[0].AddParagraph("aut.bus м. Харків, просп. Науки, 14");
row.Cells[0].Format.Alignment = ParagraphAlignment.Left;
row.Cells[3].MergeRight = 2;
row.Cells[3].MergeDown = 1;
row.Cells[3].AddParagraph("ПОСАДОЧНИЙ ДОКУМЕНТ");
row.Cells[3].Format.Font.Bold = true;
string ticketNums = "";
foreach (var ticket in ticketGroup.Tickets)
{
ticketNums += $"{ticket.Id}";
if (!ticketGroup.Tickets.Last().Equals(ticket))
{
ticketNums += "; ";
continue;
}
ticketNums += ".";
}
row.Cells[6].MergeRight = 2;
row.Cells[6].MergeDown = 1;
row.Cells[6].AddParagraph($"Група: {ticketGroup.Id}.\nКвитки: {ticketNums}");
row.Cells[6].Format.Alignment = ParagraphAlignment.Left;
row.Cells[9].MergeRight = 2;
row.Cells[9].MergeDown = 1;
row.Cells[9].AddParagraph($"{ticketGroup.Tickets.First().PurchaseDateTimeUtc:dd.MM.yyyy HH:mm:ss}");
row = table.AddRow();
row.Cells[0].MergeRight = 2;
row.Cells[0].AddParagraph("Прізвище, Ім'я");
row.Cells[0].Format.Alignment = ParagraphAlignment.Left;
row.Cells[3].MergeRight = 8;
row.Cells[3].AddParagraph($"{ticketGroup.User.LastName} {ticketGroup.User.FirstName}");
row.Cells[3].Format.Alignment = ParagraphAlignment.Left;
// Fill stations
row = table.AddRow();
row.Cells[0].MergeRight = 11;
row.Cells[0].AddParagraph("СТАНЦІЇ");
row.Cells[0].Format.Font.Bold = true;
var isFilled = false;
for (var i = 0; i < ticketGroup.Tickets.Count; i++)
{
var ticket = ticketGroup.Tickets[i];
var vehicle = ticket.VehicleEnrollment.Vehicle;
var departureDateTimeUtc = GetDepartureTime(ticket);
var arrivalDateTimeUtc = GetArrivalTime(ticket);
var departureAddress = GetDepartureAddress(ticket);
var arrivalAddress = GetArrivalAddress(ticket);
row = table.AddRow();
table.AddRow();
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
isFilled = !isFilled;
row.Cells[0].MergeRight = 1;
row.Cells[0].MergeDown = 1;
if (ticketGroup.Tickets.First().Equals(ticket))
{
row.Cells[0].AddParagraph("Відправлення");
}
else
{
row.Cells[0].AddParagraph("Пересадка на");
}
row.Cells[2].MergeRight = 1;
row.Cells[2].MergeDown = 1;
row.Cells[2].AddParagraph($"{departureDateTimeUtc:dd.MM.yyyy HH:mm}");
row.Cells[4].MergeRight = 3;
row.Cells[4].MergeDown = 1;
row.Cells[4].AddParagraph($"{departureAddress}");
row.Cells[8].MergeRight = 1;
row.Cells[8].MergeDown = 1;
row.Cells[8].AddParagraph("Тип, номер автобуса");
row.Cells[10].MergeRight = 1;
row.Cells[10].MergeDown = 1;
row.Cells[10].AddParagraph($"{vehicle.Type}, {vehicle.Number}");
row = table.AddRow();
table.AddRow();
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
isFilled = !isFilled;
row.Cells[0].MergeRight = 1;
row.Cells[0].MergeDown = 1;
if (!ticketGroup.Tickets.Last().Equals(ticket))
{
row.Cells[0].AddParagraph("Пересадка з");
}
else
{
row.Cells[0].AddParagraph("Прибуття");
}
row.Cells[2].MergeRight = 1;
row.Cells[2].MergeDown = 1;
row.Cells[2].AddParagraph($"{arrivalDateTimeUtc:dd.MM.yyyy HH:mm}");
row.Cells[4].MergeRight = 3;
row.Cells[4].MergeDown = 1;
row.Cells[4].AddParagraph($"{arrivalAddress}");
row.Cells[8].MergeRight = 1;
row.Cells[8].MergeDown = 1;
row.Cells[8].AddParagraph("Тип, номер автобуса");
row.Cells[10].MergeRight = 1;
row.Cells[10].MergeDown = 1;
row.Cells[10].AddParagraph($"{vehicle.Type}, {vehicle.Number}");
if (!ticketGroup.Tickets.Last().Equals(ticket))
{
var nextDepartureTimeUtc = GetDepartureTime(ticketGroup.Tickets[i + 1]);
var freeTime = nextDepartureTimeUtc - arrivalDateTimeUtc;
row = table.AddRow();
table.AddRow();
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
isFilled = !isFilled;
row.Cells[0].MergeRight = 2;
row.Cells[0].MergeDown = 1;
row.Cells[0].AddParagraph("Вільний час");
row.Cells[3].MergeRight = 5;
row.Cells[3].MergeDown = 1;
row.Cells[3].AddParagraph($"{arrivalDateTimeUtc:dd.MM.yyyy HH:mm} {nextDepartureTimeUtc:dd.MM.yyyy HH:mm}");
row.Cells[9].MergeRight = 2;
row.Cells[9].MergeDown = 1;
row.Cells[9].AddParagraph($"{freeTime.ToString(@"dd\.hh\:mm\:ss")}");
}
}
// Fill value
row = table.AddRow();
row.Cells[0].MergeRight = 11;
row.Cells[0].AddParagraph("ВАРТІСТЬ");
row.Cells[0].Format.Font.Bold = true;
isFilled = false;
foreach (var ticket in ticketGroup.Tickets)
{
var departureAddress = GetDepartureAddress(ticket);
var arrivalAddress = GetArrivalAddress(ticket);
var cost = GetTicketCost(ticket);
row = table.AddRow();
table.AddRow();
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
isFilled = !isFilled;
row.Cells[0].MergeDown = 1;
row.Cells[0].AddParagraph("Звідки");
row.Cells[1].MergeRight = 3;
row.Cells[1].MergeDown = 1;
row.Cells[1].AddParagraph($"{departureAddress}");
row.Cells[5].MergeDown = 1;
row.Cells[5].AddParagraph("Куди");
row.Cells[6].MergeRight = 3;
row.Cells[6].MergeDown = 1;
row.Cells[6].AddParagraph($"{arrivalAddress}");
row.Cells[10].MergeDown = 1;
row.Cells[10].AddParagraph("Ціна");
row.Cells[11].MergeDown = 1;
row.Cells[11].AddParagraph($"{cost}");
}
var totalCost = GetTicketGroupCost(ticketGroup);
row = table.AddRow();
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
isFilled = !isFilled;
row.Cells[0].MergeRight = 9;
row.Cells[0].AddParagraph("Загальна");
row.Cells[0].Format.Alignment = ParagraphAlignment.Left;
row.Cells[10].Borders.Right.Color = Colors.Transparent;
row.Cells[11].AddParagraph($"{totalCost}");
}
DateTime GetDepartureTime(Ticket ticket)
{
var departureDateTimeUtc = ticket.VehicleEnrollment.DepartureDateTimeUtc;
var routeAddresses = ticket.VehicleEnrollment.Route.RouteAddresses
.OrderBy(ra => ra.Order).ToArray();
foreach (var routeAddress in routeAddresses)
{
var details = routeAddress.RouteAddressDetails
.First(rad => rad.RouteAddressId == routeAddress.Id);
if (routeAddress.AddressId == ticket.FirstRouteAddressId)
{
break;
}
departureDateTimeUtc += details.TimeSpanToNextCity;
departureDateTimeUtc += details.WaitTimeSpan;
}
return departureDateTimeUtc;
}
DateTime GetArrivalTime(Ticket ticket)
{
var arrivalDateTimeUtc = ticket.VehicleEnrollment.DepartureDateTimeUtc;
var routeAddresses = ticket.VehicleEnrollment.Route.RouteAddresses
.OrderBy(ra => ra.Order).ToArray();
foreach (var routeAddress in routeAddresses)
{
var details = routeAddress.RouteAddressDetails
.First(rad => rad.RouteAddressId == routeAddress.Id);
if (routeAddress.AddressId == ticket.LastRouteAddressId)
{
break;
}
arrivalDateTimeUtc += details.TimeSpanToNextCity;
}
return arrivalDateTimeUtc;
}
Address GetDepartureAddress(Ticket ticket)
{
return ticket.VehicleEnrollment.Route.RouteAddresses
.First(ra => ra.AddressId == ticket.FirstRouteAddressId)
.Address;
}
Address GetArrivalAddress(Ticket ticket)
{
return ticket.VehicleEnrollment.Route.RouteAddresses
.First(ra => ra.AddressId == ticket.LastRouteAddressId)
.Address;
}
double GetTicketCost(Ticket ticket)
{
double cost = 0;
var routeAddresses = ticket.VehicleEnrollment.Route.RouteAddresses
.OrderBy(ra => ra.Order)
.SkipWhile(ra => ra.AddressId != ticket.FirstRouteAddressId)
.TakeWhile(ra => ra.AddressId != ticket.LastRouteAddressId)
.ToArray();
foreach (var routeAddress in routeAddresses)
{
var details = routeAddress.RouteAddressDetails
.First(rad => rad.RouteAddressId == routeAddress.Id);
cost += details.CostToNextCity;
}
return cost;
}
double GetTicketGroupCost(TicketGroup ticketGroup)
{
double cost = 0;
foreach (var ticket in ticketGroup.Tickets)
{
cost += GetTicketCost(ticket);
}
return cost;
}
}
public async Task<(bool isSucceed, string? message, Stream reportPdf)> GetCompanyReport()
{
throw new NotImplementedException();
}
}