feat: add statistics in json format
refactor: move methods to their proper classes
This commit is contained in:
parent
7742a8d62a
commit
8f104c786e
@ -14,8 +14,8 @@ public class ReportController : ControllerBase
|
|||||||
_reportService = reportService;
|
_reportService = reportService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("ticket")]
|
[HttpGet("pdf/ticket")]
|
||||||
public async Task<IActionResult> GetTicket(int ticketGroupId)
|
public async Task<IActionResult> GetTicketPdf(int ticketGroupId)
|
||||||
{
|
{
|
||||||
var result = await _reportService.GetTicket(ticketGroupId);
|
var result = await _reportService.GetTicket(ticketGroupId);
|
||||||
|
|
||||||
@ -24,21 +24,45 @@ public class ReportController : ControllerBase
|
|||||||
return BadRequest(result.actionResult);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return File(result.ticketPdf, "application/pdf",
|
return File(result.ticketPdf, "application/pdf", $"ticket.pdf");
|
||||||
$"ticket.pdf");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("report")]
|
[HttpGet("pdf/company")]
|
||||||
public async Task<IActionResult> GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate)
|
public async Task<IActionResult> GetCompanyReportPdf(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
{
|
{
|
||||||
var result = await _reportService.GetCompanyReport(companyId, fromDate, toDate);
|
var result = await _reportService.GetCompanyReportPdf(companyId, fromDate, toDate);
|
||||||
|
|
||||||
if (!result.isSucceed)
|
if (!result.isSucceed)
|
||||||
{
|
{
|
||||||
return BadRequest(result.actionResult);
|
return BadRequest(result.actionResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return File(result.reportPdf, "application/pdf",
|
return File(result.reportPdf, "application/pdf", $"report.pdf");
|
||||||
$"report.pdf");
|
}
|
||||||
|
|
||||||
|
[HttpGet("raw/company")]
|
||||||
|
public async Task<IActionResult> GetCompanyReportRaw(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
|
{
|
||||||
|
var result = await _reportService.GetCompanyReportRaw(companyId, fromDate, toDate);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return BadRequest(result.actionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.statistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("raw/admin")]
|
||||||
|
public async Task<IActionResult> GetAdminReportRaw(DateTime fromDate, DateTime toDate)
|
||||||
|
{
|
||||||
|
var result = await _reportService.GetAdminReportRaw(fromDate, toDate);
|
||||||
|
|
||||||
|
if (!result.isSucceed)
|
||||||
|
{
|
||||||
|
return BadRequest(result.actionResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(result.statistics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,134 @@ public class Company
|
|||||||
|
|
||||||
public virtual IList<Vehicle> Vehicles { get; set; } = null!;
|
public virtual IList<Vehicle> Vehicles { get; set; } = null!;
|
||||||
public virtual IList<CompanyDriver> CompanyDrivers { get; set; } = null!;
|
public virtual IList<CompanyDriver> CompanyDrivers { get; set; } = null!;
|
||||||
|
|
||||||
|
public int GetTotalEnrollmentCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetEnrollmentCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalCanceledEnrollmentCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetCanceledEnrollmentCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalSoldTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetSoldTicketCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalReturnedTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetReturnedTicketCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalIndirectTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetIndirectTicketCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetTotalReturnedIndirectTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetReturnedIndirectTicketCount(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetTotalRevenue()
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += vehicle.GetTotalRevenue(enrollment.RouteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetTotalAverageRating()
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int enrollmentCount = 0;
|
||||||
|
|
||||||
|
foreach (var vehicle in Vehicles)
|
||||||
|
{
|
||||||
|
foreach (var enrollment in vehicle.VehicleEnrollments)
|
||||||
|
{
|
||||||
|
if (enrollment.Reviews.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += vehicle.GetAverageRating(enrollment.RouteId);
|
||||||
|
enrollmentCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= enrollmentCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,4 +11,83 @@ public class Route
|
|||||||
|
|
||||||
public virtual IList<RouteAddress> RouteAddresses { get; set; } = null!;
|
public virtual IList<RouteAddress> RouteAddresses { get; set; } = null!;
|
||||||
public virtual IList<VehicleEnrollment> VehicleEnrollments { get; set; } = null!;
|
public virtual IList<VehicleEnrollment> VehicleEnrollments { get; set; } = null!;
|
||||||
|
|
||||||
|
public int GetEnrollmentCount()
|
||||||
|
{
|
||||||
|
return VehicleEnrollments.Count(ve => !ve.IsCanceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCanceledEnrollmentCount()
|
||||||
|
{
|
||||||
|
return VehicleEnrollments.Count(ve => ve.IsCanceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSoldTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetIndirectTicketCount()
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
int departureAddressId = RouteAddresses.First().AddressId;
|
||||||
|
int arrivalAddressId = RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments)
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
||||||
|
t.FirstRouteAddressId != departureAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalAddressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetTotalRevenue()
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments)
|
||||||
|
{
|
||||||
|
foreach (var ticket in enrollment.Tickets)
|
||||||
|
{
|
||||||
|
result += ticket.GetCost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetAverageRating()
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int reviewCount = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments)
|
||||||
|
{
|
||||||
|
if (enrollment.Reviews.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var review in enrollment.Reviews)
|
||||||
|
{
|
||||||
|
result += review.Rating;
|
||||||
|
reviewCount += enrollment.Reviews.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= reviewCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,88 @@ public class Ticket
|
|||||||
public int LastRouteAddressId { get; set; }
|
public int LastRouteAddressId { get; set; }
|
||||||
public bool IsReturned { get; set; } = false;
|
public bool IsReturned { get; set; } = false;
|
||||||
public bool IsMissed { get; set; } = false;
|
public bool IsMissed { get; set; } = false;
|
||||||
|
|
||||||
|
public double GetCost()
|
||||||
|
{
|
||||||
|
double cost = 0;
|
||||||
|
|
||||||
|
var routeAddresses = VehicleEnrollment.Route.RouteAddresses
|
||||||
|
.OrderBy(ra => ra.Order)
|
||||||
|
.SkipWhile(ra => ra.AddressId != FirstRouteAddressId)
|
||||||
|
.TakeWhile(ra => ra.AddressId != LastRouteAddressId)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
foreach (var routeAddress in routeAddresses)
|
||||||
|
{
|
||||||
|
var details = routeAddress.RouteAddressDetails
|
||||||
|
.First(rad => rad.RouteAddressId == routeAddress.Id);
|
||||||
|
|
||||||
|
cost += details.CostToNextCity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime GetDepartureTime()
|
||||||
|
{
|
||||||
|
var departureDateTimeUtc = VehicleEnrollment.DepartureDateTimeUtc;
|
||||||
|
|
||||||
|
var routeAddresses = 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 == FirstRouteAddressId)
|
||||||
|
{
|
||||||
|
departureDateTimeUtc += details.WaitTimeSpan;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
departureDateTimeUtc += details.TimeSpanToNextCity;
|
||||||
|
departureDateTimeUtc += details.WaitTimeSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return departureDateTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime GetArrivalTime()
|
||||||
|
{
|
||||||
|
var arrivalDateTimeUtc = VehicleEnrollment.DepartureDateTimeUtc;
|
||||||
|
|
||||||
|
var routeAddresses = 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 == LastRouteAddressId)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
arrivalDateTimeUtc += details.TimeSpanToNextCity;
|
||||||
|
arrivalDateTimeUtc += details.WaitTimeSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrivalDateTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address GetDepartureAddress()
|
||||||
|
{
|
||||||
|
return VehicleEnrollment.Route.RouteAddresses
|
||||||
|
.First(ra => ra.AddressId == FirstRouteAddressId)
|
||||||
|
.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address GetArrivalAddress()
|
||||||
|
{
|
||||||
|
return VehicleEnrollment.Route.RouteAddresses
|
||||||
|
.First(ra => ra.AddressId == LastRouteAddressId)
|
||||||
|
.Address;
|
||||||
|
}
|
||||||
}
|
}
|
@ -11,4 +11,16 @@ public class TicketGroup
|
|||||||
public User User { get; set; } = null!;
|
public User User { get; set; } = null!;
|
||||||
|
|
||||||
public virtual IList<Ticket> Tickets { get; set; }
|
public virtual IList<Ticket> Tickets { get; set; }
|
||||||
|
|
||||||
|
public double GetCost()
|
||||||
|
{
|
||||||
|
double cost = 0;
|
||||||
|
|
||||||
|
foreach (var ticket in Tickets)
|
||||||
|
{
|
||||||
|
cost += ticket.GetCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
return cost;
|
||||||
|
}
|
||||||
}
|
}
|
@ -25,4 +25,108 @@ public class Vehicle
|
|||||||
public bool HasBelts { get; set; }
|
public bool HasBelts { get; set; }
|
||||||
|
|
||||||
public IList<VehicleEnrollment> VehicleEnrollments { get; set; } = null!;
|
public IList<VehicleEnrollment> VehicleEnrollments { get; set; } = null!;
|
||||||
|
|
||||||
|
public int GetEnrollmentCount(int routeId)
|
||||||
|
{
|
||||||
|
return VehicleEnrollments.Count(ve => !ve.IsCanceled && ve.RouteId == routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCanceledEnrollmentCount(int routeId)
|
||||||
|
{
|
||||||
|
return VehicleEnrollments.Count(ve => ve.IsCanceled && ve.RouteId == routeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSoldTicketCount(int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetReturnedTicketCount(int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
result += enrollment.Tickets.Count(t => t.IsReturned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetIndirectTicketCount(int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
||||||
|
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
||||||
|
t.FirstRouteAddressId != departureRouteAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalRouteAddressId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetReturnedIndirectTicketCount(int routeId)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
||||||
|
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
||||||
|
|
||||||
|
result += enrollment.Tickets.Count(t => t.IsReturned &&
|
||||||
|
(t.FirstRouteAddressId != departureRouteAddressId ||
|
||||||
|
t.LastRouteAddressId != arrivalRouteAddressId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetTotalRevenue(int routeId)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
foreach (var ticket in enrollment.Tickets)
|
||||||
|
{
|
||||||
|
result += ticket.GetCost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetAverageRating(int routeId)
|
||||||
|
{
|
||||||
|
double result = 0;
|
||||||
|
int reviewCount = 0;
|
||||||
|
|
||||||
|
foreach (var enrollment in VehicleEnrollments.Where(ve => ve.RouteId == routeId))
|
||||||
|
{
|
||||||
|
reviewCount += enrollment.Reviews.Count;
|
||||||
|
|
||||||
|
foreach (var review in enrollment.Reviews)
|
||||||
|
{
|
||||||
|
result += review.Rating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= reviewCount;
|
||||||
|
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
||||||
|
|
||||||
|
return Math.Round(result, 3);
|
||||||
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ public class EmailSenderService : IEmailSenderService
|
|||||||
|
|
||||||
_smtpCredentials = smtpCredentials.Value;
|
_smtpCredentials = smtpCredentials.Value;
|
||||||
_smtpClient = new SmtpClient();
|
_smtpClient = new SmtpClient();
|
||||||
_smtpClient.SslProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
|
_smtpClient.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool succeeded, string message)> SendMail(string toEmail, string subject, string message)
|
public async Task<(bool succeeded, string message)> SendMail(string toEmail, string subject, string message)
|
||||||
@ -34,10 +34,11 @@ public class EmailSenderService : IEmailSenderService
|
|||||||
mailMessage.Subject = $"{applicationName}. {subject}";
|
mailMessage.Subject = $"{applicationName}. {subject}";
|
||||||
mailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message};
|
mailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message};
|
||||||
|
|
||||||
await _smtpClient.ConnectAsync(_smtpCredentials.Host, _smtpCredentials.Port, false);
|
await _smtpClient.ConnectAsync(_smtpCredentials.Host, _smtpCredentials.Port, SecureSocketOptions.StartTls);
|
||||||
await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password);
|
await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password);
|
||||||
await _smtpClient.SendAsync(mailMessage);
|
await _smtpClient.SendAsync(mailMessage);
|
||||||
await _smtpClient.DisconnectAsync(true);
|
await _smtpClient.DisconnectAsync(true);
|
||||||
|
_smtpClient.Dispose();
|
||||||
|
|
||||||
return (true, "Letter has been sent successfully");
|
return (true, "Letter has been sent successfully");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using SharedModels.Responses;
|
||||||
|
|
||||||
namespace Server.Services;
|
namespace Server.Services;
|
||||||
|
|
||||||
@ -8,5 +9,11 @@ public interface IReportService
|
|||||||
GetTicket(int ticketGroupId);
|
GetTicket(int ticketGroupId);
|
||||||
|
|
||||||
Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
||||||
GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate);
|
GetCompanyReportPdf(int companyId, DateTime fromDate, DateTime toDate);
|
||||||
|
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, StatisticsResponse statistics)>
|
||||||
|
GetCompanyReportRaw(int companyId, DateTime fromDate, DateTime toDate);
|
||||||
|
|
||||||
|
Task<(bool isSucceed, IActionResult? actionResult, StatisticsResponse statistics)>
|
||||||
|
GetAdminReportRaw(DateTime fromDate, DateTime toDate);
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ using PdfSharpCore.Drawing;
|
|||||||
using PdfSharpCore.Pdf;
|
using PdfSharpCore.Pdf;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
using Server.Models;
|
using Server.Models;
|
||||||
|
using SharedModels.Responses;
|
||||||
using Route = Server.Models.Route;
|
using Route = Server.Models.Route;
|
||||||
|
|
||||||
namespace Server.Services;
|
namespace Server.Services;
|
||||||
@ -223,15 +224,8 @@ public class ReportService : IReportService
|
|||||||
for (var i = 0; i < ticketGroup.Tickets.Count; i++)
|
for (var i = 0; i < ticketGroup.Tickets.Count; i++)
|
||||||
{
|
{
|
||||||
var ticket = ticketGroup.Tickets[i];
|
var ticket = ticketGroup.Tickets[i];
|
||||||
|
|
||||||
var vehicle = ticket.VehicleEnrollment.Vehicle;
|
var vehicle = ticket.VehicleEnrollment.Vehicle;
|
||||||
|
|
||||||
var departureDateTimeUtc = GetDepartureTime(ticket);
|
|
||||||
var arrivalDateTimeUtc = GetArrivalTime(ticket);
|
|
||||||
|
|
||||||
var departureAddress = GetDepartureAddress(ticket);
|
|
||||||
var arrivalAddress = GetArrivalAddress(ticket);
|
|
||||||
|
|
||||||
row = table.AddRow();
|
row = table.AddRow();
|
||||||
table.AddRow();
|
table.AddRow();
|
||||||
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
||||||
@ -251,12 +245,12 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[2].MergeRight = 1;
|
row.Cells[2].MergeRight = 1;
|
||||||
row.Cells[2].MergeDown = 1;
|
row.Cells[2].MergeDown = 1;
|
||||||
row.Cells[2].AddParagraph($"{departureDateTimeUtc:dd.MM.yyyy HH:mm}");
|
row.Cells[2].AddParagraph($"{ticket.GetDepartureTime():dd.MM.yyyy HH:mm}");
|
||||||
|
|
||||||
row.Cells[4].MergeRight = 3;
|
row.Cells[4].MergeRight = 3;
|
||||||
row.Cells[4].MergeDown = 1;
|
row.Cells[4].MergeDown = 1;
|
||||||
row.Cells[4].Format.Font.Size = 8;
|
row.Cells[4].Format.Font.Size = 8;
|
||||||
row.Cells[4].AddParagraph($"{departureAddress.GetFullName()}");
|
row.Cells[4].AddParagraph($"{ticket.GetDepartureAddress().GetFullName()}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].MergeDown = 1;
|
row.Cells[8].MergeDown = 1;
|
||||||
@ -285,12 +279,12 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[2].MergeRight = 1;
|
row.Cells[2].MergeRight = 1;
|
||||||
row.Cells[2].MergeDown = 1;
|
row.Cells[2].MergeDown = 1;
|
||||||
row.Cells[2].AddParagraph($"{arrivalDateTimeUtc:dd.MM.yyyy HH:mm}");
|
row.Cells[2].AddParagraph($"{ticket.GetArrivalTime():dd.MM.yyyy HH:mm}");
|
||||||
|
|
||||||
row.Cells[4].MergeRight = 3;
|
row.Cells[4].MergeRight = 3;
|
||||||
row.Cells[4].MergeDown = 1;
|
row.Cells[4].MergeDown = 1;
|
||||||
row.Cells[4].Format.Font.Size = 8;
|
row.Cells[4].Format.Font.Size = 8;
|
||||||
row.Cells[4].AddParagraph($"{arrivalAddress.GetFullName()}");
|
row.Cells[4].AddParagraph($"{ticket.GetArrivalAddress().GetFullName()}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].MergeDown = 1;
|
row.Cells[8].MergeDown = 1;
|
||||||
@ -302,8 +296,8 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
if (!ticketGroup.Tickets.Last().Equals(ticket))
|
if (!ticketGroup.Tickets.Last().Equals(ticket))
|
||||||
{
|
{
|
||||||
var nextDepartureTimeUtc = GetDepartureTime(ticketGroup.Tickets[i + 1]);
|
var nextDepartureTime = ticketGroup.Tickets[i + 1].GetDepartureTime();
|
||||||
var freeTime = nextDepartureTimeUtc - arrivalDateTimeUtc;
|
var freeTime = nextDepartureTime - ticket.GetArrivalTime();
|
||||||
|
|
||||||
row = table.AddRow();
|
row = table.AddRow();
|
||||||
table.AddRow();
|
table.AddRow();
|
||||||
@ -316,7 +310,7 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[3].MergeRight = 5;
|
row.Cells[3].MergeRight = 5;
|
||||||
row.Cells[3].MergeDown = 1;
|
row.Cells[3].MergeDown = 1;
|
||||||
row.Cells[3].AddParagraph($"{arrivalDateTimeUtc:dd.MM.yyyy HH:mm} – {nextDepartureTimeUtc:dd.MM.yyyy HH:mm}");
|
row.Cells[3].AddParagraph($"{ticket.GetArrivalTime():dd.MM.yyyy HH:mm} – {nextDepartureTime:dd.MM.yyyy HH:mm}");
|
||||||
|
|
||||||
row.Cells[9].MergeRight = 2;
|
row.Cells[9].MergeRight = 2;
|
||||||
row.Cells[9].MergeDown = 1;
|
row.Cells[9].MergeDown = 1;
|
||||||
@ -336,10 +330,6 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
foreach (var ticket in ticketGroup.Tickets)
|
foreach (var ticket in ticketGroup.Tickets)
|
||||||
{
|
{
|
||||||
var departureAddress = GetDepartureAddress(ticket);
|
|
||||||
var arrivalAddress = GetArrivalAddress(ticket);
|
|
||||||
var cost = GetTicketCost(ticket);
|
|
||||||
|
|
||||||
row = table.AddRow();
|
row = table.AddRow();
|
||||||
table.AddRow();
|
table.AddRow();
|
||||||
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
||||||
@ -351,7 +341,7 @@ public class ReportService : IReportService
|
|||||||
row.Cells[1].MergeRight = 3;
|
row.Cells[1].MergeRight = 3;
|
||||||
row.Cells[1].MergeDown = 1;
|
row.Cells[1].MergeDown = 1;
|
||||||
row.Cells[1].Format.Font.Size = 8;
|
row.Cells[1].Format.Font.Size = 8;
|
||||||
row.Cells[1].AddParagraph($"{departureAddress.GetFullName()}");
|
row.Cells[1].AddParagraph($"{ticket.GetDepartureAddress().GetFullName()}");
|
||||||
|
|
||||||
row.Cells[5].MergeDown = 1;
|
row.Cells[5].MergeDown = 1;
|
||||||
row.Cells[5].AddParagraph("Куди");
|
row.Cells[5].AddParagraph("Куди");
|
||||||
@ -359,17 +349,15 @@ public class ReportService : IReportService
|
|||||||
row.Cells[6].MergeRight = 3;
|
row.Cells[6].MergeRight = 3;
|
||||||
row.Cells[6].MergeDown = 1;
|
row.Cells[6].MergeDown = 1;
|
||||||
row.Cells[6].Format.Font.Size = 8;
|
row.Cells[6].Format.Font.Size = 8;
|
||||||
row.Cells[6].AddParagraph($"{arrivalAddress.GetFullName()}");
|
row.Cells[6].AddParagraph($"{ticket.GetArrivalAddress().GetFullName()}");
|
||||||
|
|
||||||
row.Cells[10].MergeDown = 1;
|
row.Cells[10].MergeDown = 1;
|
||||||
row.Cells[10].AddParagraph("Ціна");
|
row.Cells[10].AddParagraph("Ціна");
|
||||||
|
|
||||||
row.Cells[11].MergeDown = 1;
|
row.Cells[11].MergeDown = 1;
|
||||||
row.Cells[11].AddParagraph($"{cost}");
|
row.Cells[11].AddParagraph($"{ticket.GetCost()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalCost = GetTicketGroupCost(ticketGroup);
|
|
||||||
|
|
||||||
row = table.AddRow();
|
row = table.AddRow();
|
||||||
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
row.Shading.Color = isFilled ? Color.FromRgbColor(25, Colors.Black) : Colors.White;
|
||||||
isFilled = !isFilled;
|
isFilled = !isFilled;
|
||||||
@ -380,113 +368,12 @@ public class ReportService : IReportService
|
|||||||
|
|
||||||
row.Cells[10].Borders.Right.Color = Colors.Transparent;
|
row.Cells[10].Borders.Right.Color = Colors.Transparent;
|
||||||
|
|
||||||
row.Cells[11].AddParagraph($"{totalCost}");
|
row.Cells[11].AddParagraph($"{ticketGroup.GetCost()}");
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
departureDateTimeUtc += details.WaitTimeSpan;
|
|
||||||
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;
|
|
||||||
arrivalDateTimeUtc += details.WaitTimeSpan;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> DoesTicketGroupExist(int id)
|
|
||||||
{
|
|
||||||
return await _dbContext.TicketGroups.AnyAsync(tg => tg.Id == id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
public async Task<(bool isSucceed, IActionResult? actionResult, Stream reportPdf)>
|
||||||
GetCompanyReport(int companyId, DateTime fromDate, DateTime toDate)
|
GetCompanyReportPdf(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
{
|
{
|
||||||
if (!await DoesCompanyExist(companyId))
|
if (!await DoesCompanyExist(companyId))
|
||||||
{
|
{
|
||||||
@ -726,22 +613,22 @@ public class ReportService : IReportService
|
|||||||
row.Shading.Color = Color.FromRgbColor(25, Colors.Black);
|
row.Shading.Color = Color.FromRgbColor(25, Colors.Black);
|
||||||
|
|
||||||
row.Cells[0].MergeRight = 1;
|
row.Cells[0].MergeRight = 1;
|
||||||
row.Cells[0].AddParagraph($"{GetRouteEnrollmentCount(route)}");
|
row.Cells[0].AddParagraph($"{route.GetEnrollmentCount()}");
|
||||||
|
|
||||||
row.Cells[2].MergeRight = 1;
|
row.Cells[2].MergeRight = 1;
|
||||||
row.Cells[2].AddParagraph($"{GetRouteCanceledEnrollmentCount(route)}");
|
row.Cells[2].AddParagraph($"{route.GetCanceledEnrollmentCount()}");
|
||||||
|
|
||||||
row.Cells[4].MergeRight = 1;
|
row.Cells[4].MergeRight = 1;
|
||||||
row.Cells[4].AddParagraph($"{GetRouteSelledTicketCount(route)}");
|
row.Cells[4].AddParagraph($"{route.GetSoldTicketCount()}");
|
||||||
|
|
||||||
row.Cells[6].MergeRight = 1;
|
row.Cells[6].MergeRight = 1;
|
||||||
row.Cells[6].AddParagraph($"{GetRouteIndirectTicketCount(route)}");
|
row.Cells[6].AddParagraph($"{route.GetIndirectTicketCount()}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].AddParagraph($"{GetRouteTotalRevenu(route)}");
|
row.Cells[8].AddParagraph($"{route.GetTotalRevenue()}");
|
||||||
|
|
||||||
row.Cells[10].MergeRight = 1;
|
row.Cells[10].MergeRight = 1;
|
||||||
var routeAverageRating = GetRouteAvarageRating(route);
|
var routeAverageRating = route.GetAverageRating();
|
||||||
row.Cells[10].AddParagraph($"{(routeAverageRating == 0 ? "-" : routeAverageRating)}");
|
row.Cells[10].AddParagraph($"{(routeAverageRating == 0 ? "-" : routeAverageRating)}");
|
||||||
|
|
||||||
row = table.AddRow();
|
row = table.AddRow();
|
||||||
@ -783,21 +670,23 @@ public class ReportService : IReportService
|
|||||||
row.Cells[0].MergeRight = 2;
|
row.Cells[0].MergeRight = 2;
|
||||||
row.Cells[0].AddParagraph($"{vehicle.Id}, {vehicle.Type}, {vehicle.Number}");
|
row.Cells[0].AddParagraph($"{vehicle.Id}, {vehicle.Type}, {vehicle.Number}");
|
||||||
|
|
||||||
var executedEnrollmentCount = GetVehicleEnrollmentCount(vehicle, route.Id);
|
var executedEnrollmentCount = vehicle.GetEnrollmentCount(route.Id);
|
||||||
var canceledEnrollmentCount = GetVehicleCanceledEnrollmentCount(vehicle, route.Id);
|
var canceledEnrollmentCount = vehicle.GetCanceledEnrollmentCount(route.Id);
|
||||||
row.Cells[3].MergeRight = 1;
|
row.Cells[3].MergeRight = 1;
|
||||||
row.Cells[3].AddParagraph($"{executedEnrollmentCount + canceledEnrollmentCount}, " +
|
row.Cells[3].AddParagraph($"{executedEnrollmentCount + canceledEnrollmentCount}, " +
|
||||||
$"{executedEnrollmentCount}, {canceledEnrollmentCount}");
|
$"{executedEnrollmentCount}, {canceledEnrollmentCount}");
|
||||||
|
|
||||||
row.Cells[5].MergeRight = 2;
|
row.Cells[5].MergeRight = 2;
|
||||||
row.Cells[5].AddParagraph($"{GetVehicleSelledTicketCount(vehicle, route.Id)}, {GetVehicleReturnedTicketCount(vehicle, route.Id)}; " +
|
row.Cells[5].AddParagraph($"{vehicle.GetSoldTicketCount(route.Id)}, " +
|
||||||
$"{GetVehicleIndirectTicketCount(vehicle, route.Id)}, {GetVehicleReturnedIndirectTicketCount(vehicle, route.Id)}");
|
$"{vehicle.GetReturnedTicketCount(route.Id)}; " +
|
||||||
|
$"{vehicle.GetIndirectTicketCount(route.Id)}, " +
|
||||||
|
$"{vehicle.GetReturnedIndirectTicketCount(route.Id)}");
|
||||||
|
|
||||||
row.Cells[8].MergeRight = 1;
|
row.Cells[8].MergeRight = 1;
|
||||||
row.Cells[8].AddParagraph($"{GetVehicleTotalRevenue(vehicle, route.Id)}");
|
row.Cells[8].AddParagraph($"{vehicle.GetTotalRevenue(route.Id)}");
|
||||||
|
|
||||||
row.Cells[10].MergeRight = 1;
|
row.Cells[10].MergeRight = 1;
|
||||||
var vehicleAverageRating = GetVehicleAverageRating(vehicle, route.Id);
|
var vehicleAverageRating = vehicle.GetAverageRating(route.Id);
|
||||||
row.Cells[10].AddParagraph($"{(vehicleAverageRating == 0 ? "-" : vehicleAverageRating)}");
|
row.Cells[10].AddParagraph($"{(vehicleAverageRating == 0 ? "-" : vehicleAverageRating)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,372 +701,140 @@ public class ReportService : IReportService
|
|||||||
paragraph.Format.Font.Bold = true;
|
paragraph.Format.Font.Bold = true;
|
||||||
section.AddParagraph();
|
section.AddParagraph();
|
||||||
|
|
||||||
var totalEnrollmentCount = GetTotalEnrollmentCount(dbCompany);
|
|
||||||
var totalCanceledEnrollmentCount = GetTotalCanceledEnrollmentCount(dbCompany);
|
|
||||||
var totalSoldTicketCount = GetTotalSoldTickets(dbCompany);
|
|
||||||
var totalReturnedTicketCount = GetTotalReturnedTicketCount(dbCompany);
|
|
||||||
var totalRevenue = GetTotalRevenu(dbCompany);
|
|
||||||
var totalAverageRating = GetTotalAverageRating(dbCompany);
|
|
||||||
|
|
||||||
paragraph = section.AddParagraph(
|
paragraph = section.AddParagraph(
|
||||||
$"У період з {fromDate:dd.MM.yyyy} по {toDate:dd.MM.yyyy} " +
|
$"У період з {fromDate:dd.MM.yyyy} по {toDate:dd.MM.yyyy} " +
|
||||||
$"({(toDate - fromDate).Days} днів) компанією {dbCompany.Name} " +
|
$"({(toDate - fromDate).Days} днів) компанією {dbCompany.Name} " +
|
||||||
$"було заплановано {(totalEnrollmentCount)} поїздки, " +
|
$"було заплановано {dbCompany.GetTotalEnrollmentCount()} поїздки, " +
|
||||||
$"з яких {totalCanceledEnrollmentCount} було скасовано, " +
|
$"з яких {dbCompany.GetTotalCanceledEnrollmentCount()} було скасовано, " +
|
||||||
$"продано {totalSoldTicketCount} квитків, " +
|
$"продано {dbCompany.GetTotalSoldTicketCount()} квитків, " +
|
||||||
$"з яких {totalReturnedTicketCount} було повернено. " +
|
$"з яких {dbCompany.GetTotalReturnedTicketCount()} було повернено. " +
|
||||||
$"За цей час було зароблено {totalRevenue} гривень. " +
|
$"За цей час було зароблено {dbCompany.GetTotalRevenue()} гривень. " +
|
||||||
$"Середній рейтинг по всім поїздкам: {totalAverageRating}");
|
$"Середній рейтинг по всім поїздкам: {dbCompany.GetTotalAverageRating()}");
|
||||||
paragraph.Format.Alignment = ParagraphAlignment.Justify;
|
paragraph.Format.Alignment = ParagraphAlignment.Justify;
|
||||||
paragraph.Format.Font.Size = 14;
|
paragraph.Format.Font.Size = 14;
|
||||||
|
|
||||||
int GetRouteEnrollmentCount(Route route)
|
|
||||||
{
|
|
||||||
return route.VehicleEnrollments.Count(ve => !ve.IsCanceled);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleEnrollmentCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
return vehicle.VehicleEnrollments.Count(ve =>
|
|
||||||
!ve.IsCanceled && ve.RouteId == routeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalEnrollmentCount(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleEnrollmentCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRouteCanceledEnrollmentCount(Route route)
|
|
||||||
{
|
|
||||||
return route.VehicleEnrollments.Count(ve => ve.IsCanceled);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleCanceledEnrollmentCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
return vehicle.VehicleEnrollments.Count(ve =>
|
|
||||||
ve.IsCanceled && ve.RouteId == routeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalCanceledEnrollmentCount(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleCanceledEnrollmentCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRouteSelledTicketCount(Route route)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in route.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleSelledTicketCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
result += enrollment.Tickets.Count(t => !t.IsReturned);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalSoldTickets(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleSelledTicketCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleReturnedTicketCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
result += enrollment.Tickets.Count(t => t.IsReturned);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalReturnedTicketCount(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleReturnedTicketCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetRouteIndirectTicketCount(Route route)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
int departureAddressId = route.RouteAddresses.First().AddressId;
|
|
||||||
int arrivalAddressId = route.RouteAddresses.Last().AddressId;
|
|
||||||
|
|
||||||
foreach (var enrollment in route.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
|
||||||
t.FirstRouteAddressId != departureAddressId ||
|
|
||||||
t.LastRouteAddressId != arrivalAddressId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleIndirectTicketCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
|
||||||
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
|
||||||
|
|
||||||
result += enrollment.Tickets.Count(t => !t.IsReturned &&
|
|
||||||
t.FirstRouteAddressId != departureRouteAddressId ||
|
|
||||||
t.LastRouteAddressId != arrivalRouteAddressId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalIndirectTicketCount(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleIndirectTicketCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetVehicleReturnedIndirectTicketCount(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
var departureRouteAddressId = enrollment.Route.RouteAddresses.First().AddressId;
|
|
||||||
var arrivalRouteAddressId = enrollment.Route.RouteAddresses.Last().AddressId;
|
|
||||||
|
|
||||||
result += enrollment.Tickets.Count(t => t.IsReturned &&
|
|
||||||
(t.FirstRouteAddressId != departureRouteAddressId ||
|
|
||||||
t.LastRouteAddressId != arrivalRouteAddressId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTotalReturnedIndirectTicketCount(Company company)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleReturnedIndirectTicketCount(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetRouteTotalRevenu(Route route)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in route.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
foreach (var ticket in enrollment.Tickets)
|
|
||||||
{
|
|
||||||
result += GetTicketCost(ticket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetVehicleTotalRevenue(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
foreach (var ticket in enrollment.Tickets)
|
|
||||||
{
|
|
||||||
result += GetTicketCost(ticket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetTotalRevenu(Company company)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
result += GetVehicleTotalRevenue(vehicle, enrollment.RouteId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetRouteAvarageRating(Route route)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
int reviewCount = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in route.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
if (enrollment.Reviews.Count == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var review in enrollment.Reviews)
|
|
||||||
{
|
|
||||||
result += review.Rating;
|
|
||||||
reviewCount += enrollment.Reviews.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result /= reviewCount;
|
|
||||||
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetVehicleAverageRating(Vehicle vehicle, int routeId)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
int reviewCount = 0;
|
|
||||||
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments
|
|
||||||
.Where(ve => ve.RouteId == routeId))
|
|
||||||
{
|
|
||||||
reviewCount += enrollment.Reviews.Count;
|
|
||||||
|
|
||||||
foreach (var review in enrollment.Reviews)
|
|
||||||
{
|
|
||||||
result += review.Rating;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result /= reviewCount;
|
|
||||||
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
|
||||||
|
|
||||||
return Math.Round(result, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
double GetTotalAverageRating(Company company)
|
|
||||||
{
|
|
||||||
double result = 0;
|
|
||||||
int enrollmentCount = 0;
|
|
||||||
|
|
||||||
foreach (var vehicle in company.Vehicles)
|
|
||||||
{
|
|
||||||
foreach (var enrollment in vehicle.VehicleEnrollments)
|
|
||||||
{
|
|
||||||
if (enrollment.Reviews.Count == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += GetVehicleAverageRating(vehicle, enrollment.RouteId);
|
|
||||||
enrollmentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result /= enrollmentCount;
|
|
||||||
result = !Double.IsNaN(result) ? Math.Round(result, 3) : 0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: repeated function, yoinked from ticket generation
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> DoesCompanyExist(int id)
|
|
||||||
{
|
|
||||||
return await _dbContext.Companies.AnyAsync(c => c.Id == id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, StatisticsResponse statistics)>
|
||||||
|
GetCompanyReportRaw(int companyId, DateTime fromDate, DateTime toDate)
|
||||||
|
{
|
||||||
|
if (!await DoesCompanyExist(companyId))
|
||||||
|
{
|
||||||
|
return (false, new NotFoundResult(), null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbCompany = await _dbContext.Companies
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Reviews)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Tickets)
|
||||||
|
.ThenInclude(t => t.TicketGroup)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.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(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.RouteAddressDetails)
|
||||||
|
|
||||||
|
.Select(c => new {
|
||||||
|
Company = c,
|
||||||
|
VehicleEnrollments = c.Vehicles
|
||||||
|
.Select(v => new {
|
||||||
|
Vehicle = v,
|
||||||
|
VehicleEnrollments = v.VehicleEnrollments.Where(ve =>
|
||||||
|
ve.DepartureDateTimeUtc.Date >= fromDate &&
|
||||||
|
ve.DepartureDateTimeUtc <= toDate)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.Select(o => o.Company)
|
||||||
|
.FirstAsync(c => c.Id == companyId);
|
||||||
|
|
||||||
|
var statistics = new StatisticsResponse
|
||||||
|
{
|
||||||
|
EnrollmentsPlanned = dbCompany.GetTotalEnrollmentCount(),
|
||||||
|
EnrollmentsCanceled = dbCompany.GetTotalCanceledEnrollmentCount(),
|
||||||
|
TicketsSold = dbCompany.GetTotalSoldTicketCount(),
|
||||||
|
TicketsReturned = dbCompany.GetTotalReturnedTicketCount(),
|
||||||
|
MoneyEarned = dbCompany.GetTotalRevenue(),
|
||||||
|
AverageRating = dbCompany.GetTotalAverageRating()
|
||||||
|
};
|
||||||
|
|
||||||
|
return (true, null, statistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(bool isSucceed, IActionResult? actionResult, StatisticsResponse statistics)>
|
||||||
|
GetAdminReportRaw(DateTime fromDate, DateTime toDate)
|
||||||
|
{
|
||||||
|
var dbCompanies = await _dbContext.Companies
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Reviews)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.Tickets)
|
||||||
|
.ThenInclude(t => t.TicketGroup)
|
||||||
|
|
||||||
|
.Include(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.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(c => c.Vehicles)
|
||||||
|
.ThenInclude(v => v.VehicleEnrollments)
|
||||||
|
.ThenInclude(ve => ve.RouteAddressDetails)
|
||||||
|
|
||||||
|
.Select(c => new
|
||||||
|
{
|
||||||
|
Company = c,
|
||||||
|
VehicleEnrollments = c.Vehicles
|
||||||
|
.Select(v => new
|
||||||
|
{
|
||||||
|
Vehicle = v,
|
||||||
|
VehicleEnrollments = v.VehicleEnrollments.Where(ve =>
|
||||||
|
ve.DepartureDateTimeUtc.Date >= fromDate &&
|
||||||
|
ve.DepartureDateTimeUtc <= toDate)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.Select(o => o.Company)
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
var statistics = new StatisticsResponse();
|
||||||
|
|
||||||
|
foreach (var company in dbCompanies)
|
||||||
|
{
|
||||||
|
statistics.EnrollmentsPlanned += company.GetTotalEnrollmentCount();
|
||||||
|
statistics.EnrollmentsCanceled += company.GetTotalCanceledEnrollmentCount();
|
||||||
|
statistics.TicketsSold += company.GetTotalSoldTicketCount();
|
||||||
|
statistics.TicketsReturned += company.GetTotalReturnedTicketCount();
|
||||||
|
statistics.MoneyEarned += company.GetTotalRevenue();
|
||||||
|
statistics.AverageRating += company.GetTotalAverageRating();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true, null, statistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<bool> DoesCompanyExist(int id)
|
||||||
|
{
|
||||||
|
return await _dbContext.Companies.AnyAsync(c => c.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<bool> DoesTicketGroupExist(int id)
|
||||||
|
{
|
||||||
|
return await _dbContext.TicketGroups.AnyAsync(tg => tg.Id == id);
|
||||||
|
}
|
||||||
}
|
}
|
11
SharedModels/Responses/StatisticsResponse.cs
Normal file
11
SharedModels/Responses/StatisticsResponse.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace SharedModels.Responses;
|
||||||
|
|
||||||
|
public class StatisticsResponse
|
||||||
|
{
|
||||||
|
public int EnrollmentsPlanned { get; set; }
|
||||||
|
public int EnrollmentsCanceled { get; set; }
|
||||||
|
public int TicketsSold { get; set; }
|
||||||
|
public int TicketsReturned { get; set; }
|
||||||
|
public double MoneyEarned { get; set; }
|
||||||
|
public double AverageRating { get; set; }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user