diff --git a/src/angular/hq/src/app/clients/client-details.service.ts b/src/angular/hq/src/app/clients/client-details.service.ts index 3213a5c9..1b0814e0 100644 --- a/src/angular/hq/src/app/clients/client-details.service.ts +++ b/src/angular/hq/src/app/clients/client-details.service.ts @@ -10,7 +10,7 @@ export enum ProjectStatus { WaitingForClient = 3, WaitingForStaff = 4, InProduction = 5, - Ongoing = 6, + OnGoing = 6, Completed = 7, Closed = 8, Lost = 9, diff --git a/src/angular/hq/src/app/clients/client-details/client-details-search-filter/client-details-search-filter.component.html b/src/angular/hq/src/app/clients/client-details/client-details-search-filter/client-details-search-filter.component.html index 36e7cbfa..dc86023e 100644 --- a/src/angular/hq/src/app/clients/client-details/client-details-search-filter/client-details-search-filter.component.html +++ b/src/angular/hq/src/app/clients/client-details/client-details-search-filter/client-details-search-filter.component.html @@ -40,10 +40,10 @@ Waiting For Staff - InProduction + In Production - - Ongoing + + On Going Completed diff --git a/src/angular/hq/src/app/models/projects/get-project-v1.ts b/src/angular/hq/src/app/models/projects/get-project-v1.ts index f01f0808..1a4ec6ad 100644 --- a/src/angular/hq/src/app/models/projects/get-project-v1.ts +++ b/src/angular/hq/src/app/models/projects/get-project-v1.ts @@ -21,7 +21,22 @@ export enum SortColumn { EndDate = 4, ChargeCode = 5, ClientName = 6, - Status = 7, + ProjectStatus = 7, + BookingPeriod = 8, + BookingStartDate = 9, + BookingEndDate = 10, + TotalHours = 11, + TotalAvailableHours = 12, + ThisHours = 13, + ThisPendingHours = 14, + LastHours = 15, + BookingHours = 16, + BookingAvailableHours = 17, + TotalPercentComplete = 18, + BookingPercentComplete = 19, + SummaryHoursTotal = 20, + SummaryHoursAvailable = 21, + SummaryPercentComplete = 22, } export interface GetProjectRecordV1 { @@ -40,9 +55,19 @@ export interface GetProjectRecordV1 { bookingPeriod: Period; startDate: Date; endDate: Date; - status: ProjectStatus; + projectStatus: ProjectStatus; billingEmail: string; officialName: string; + summaryHoursTotal: number; + summaryHoursAvailable: number; + summaryPercentComplete: number; + bookingStartDate: Date; + bookingEndDate: Date; + totalStartDate?: Date; + totalEndDate?: Date; + totalPercentComplete?: number; + bookingPercentComplete?: number; + totalHours: number; } export interface GetProjectRecordsV1 { diff --git a/src/angular/hq/src/app/projects/project-list-search-filter/project-list-search-filter.component.html b/src/angular/hq/src/app/projects/project-list-search-filter/project-list-search-filter.component.html index f2e391a6..eb5ca683 100644 --- a/src/angular/hq/src/app/projects/project-list-search-filter/project-list-search-filter.component.html +++ b/src/angular/hq/src/app/projects/project-list-search-filter/project-list-search-filter.component.html @@ -82,7 +82,7 @@ Lost - + On going diff --git a/src/angular/hq/src/app/projects/project-list/project-list.component.html b/src/angular/hq/src/app/projects/project-list/project-list.component.html index b9c87c31..7bb16ea1 100644 --- a/src/angular/hq/src/app/projects/project-list/project-list.component.html +++ b/src/angular/hq/src/app/projects/project-list/project-list.component.html @@ -89,22 +89,29 @@ Projects Status - Complete + Complete % + + No matching records found {{ project.startDate }} {{ project.endDate }} - {{ getProjectSatusString(project.status) }} + {{ getProjectSatusString(project.projectStatus) }} - - - - - + + @if (project.projectStatus === ProjectStatus.OnGoing) { + @if ( + project.bookingPercentComplete && + project.bookingPercentComplete > 1 + ) { + + + + } @else { + + + + } + + {{ project.bookingHours | number: "0.2-2" }} hrs this + {{ getPeriodName(project.bookingPeriod) }} to date + {{ project.bookingPercentComplete | percent }} + + } + @if (project.projectStatus === ProjectStatus.InProduction) { + @if ( + project.totalPercentComplete && project.totalPercentComplete > 1 + ) { + + + + } @else { + + + + } + + {{ project.totalHours | number: "0.2-2" }} hrs total + {{ + project.totalPercentComplete !== null + ? (project.totalPercentComplete | percent) + : "-" + }} + + } diff --git a/src/angular/hq/src/app/projects/project-list/project-list.component.ts b/src/angular/hq/src/app/projects/project-list/project-list.component.ts index d3630250..f42a647b 100644 --- a/src/angular/hq/src/app/projects/project-list/project-list.component.ts +++ b/src/angular/hq/src/app/projects/project-list/project-list.component.ts @@ -26,6 +26,7 @@ import { HQRole } from '../../enums/hqrole'; import { InRolePipe } from '../../pipes/in-role.pipe'; import { ProjectListSearchFilterComponent } from '../project-list-search-filter/project-list-search-filter.component'; import { ProjectSearchFilterService } from '../services/ProjectSearchFilterService'; +import { Period } from '../../models/times/get-time-v1'; @Component({ selector: 'hq-project-list', @@ -55,6 +56,8 @@ export class ProjectListComponent { sortColumn = SortColumn; sortDirection = SortDirection; HQRole = HQRole; + ProjectStatus = ProjectStatus; + Math = Math; constructor( private hqService: HQService, @@ -149,6 +152,19 @@ export class ProjectListComponent { this.projectSearchFilterService.page.setValue(1); } getProjectSatusString(status: ProjectStatus): string { - return ProjectStatus[status]; + return this.camelToFlat(ProjectStatus[status]); + } + camelToFlat = (camel: string) => { + const camelCase = camel.replace(/([a-z])([A-Z])/g, '$1 $2').split(' '); + + let flat = ''; + + camelCase.forEach((word) => { + flat = flat + word.charAt(0).toUpperCase() + word.slice(1) + ' '; + }); + return flat; + }; + getPeriodName(period: Period) { + return Period[period]; } } diff --git a/src/angular/hq/src/app/psr/psrlist/psrlist.component.html b/src/angular/hq/src/app/psr/psrlist/psrlist.component.html index 19b3d431..14d5df31 100644 --- a/src/angular/hq/src/app/psr/psrlist/psrlist.component.html +++ b/src/angular/hq/src/app/psr/psrlist/psrlist.component.html @@ -201,7 +201,7 @@ No matching records found {{ getProjectSatusString(report.status) }} - @if (report.status === ProjectStatus.Ongoing) { + @if (report.status === ProjectStatus.OnGoing) { @if ( report.bookingPercentComplete && report.bookingPercentComplete > 1 ) { diff --git a/src/dotnet/HQ.Abstractions/Projects/GetProjectsV1.cs b/src/dotnet/HQ.Abstractions/Projects/GetProjectsV1.cs index f4f6b601..bf1502af 100644 --- a/src/dotnet/HQ.Abstractions/Projects/GetProjectsV1.cs +++ b/src/dotnet/HQ.Abstractions/Projects/GetProjectsV1.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.Json.Serialization; using System.Threading.Tasks; using HQ.Abstractions.Common; @@ -31,7 +32,22 @@ public enum SortColumn EndDate = 4, ChargeCode = 5, ClientName = 6, - Status = 7 + Status = 7, + BookingPeriod = 8, + BookingStartDate = 9, + BookingEndDate = 10, + TotalHours = 11, + TotalAvailableHours = 12, + ThisHours = 13, + ThisPendingHours = 14, + LastHours = 15, + BookingHours = 16, + BookingAvailableHours = 17, + TotalPercentComplete = 18, + BookingPercentComplete = 19, + SummaryHoursTotal = 20, + SummaryHoursAvailable = 21, + SummaryPercentComplete = 22, } public class Response : PagedResponseV1; @@ -57,5 +73,41 @@ public class Record public string? BillingEmail { get; set; } public string? OfficialName { get; set; } public ProjectStatus? ProjectStatus { get; set; } + + /// + /// Total hours across all time. + /// + public decimal TotalHours { get; set; } + /// + /// Total hours available across all time. + /// + public decimal? TotalAvailableHours { get; set; } + /// + /// Total hours from last week. + /// + public decimal ThisHours { get; set; } + /// + /// Hours not accepted during this period. + /// + public decimal ThisPendingHours { get; set; } + /// + /// Booking hours from project period. + /// + public DateOnly? BookingStartDate { get; set; } + /// + /// Booking available hours from project period. + /// + public decimal BookingAvailableHours { get; set; } + public DateOnly? BookingEndDate { get; set; } + public decimal? TotalPercentComplete { get; set; } + [JsonIgnore] + public decimal? TotalPercentCompleteSort { get; set; } + public decimal BookingPercentComplete { get; set; } + public DateOnly? TotalStartDate { get; set; } + public DateOnly? TotalEndDate { get; set; } + public decimal? SummaryHoursTotal { get; set; } + public decimal? SummaryHoursAvailable { get; set; } + public decimal? SummaryPercentComplete { get; set; } + public decimal? SummaryPercentCompleteSort { get; set; } } } \ No newline at end of file diff --git a/src/dotnet/HQ.Server/Services/ProjectServiceV1.cs b/src/dotnet/HQ.Server/Services/ProjectServiceV1.cs index f57a371a..ca6ce528 100644 --- a/src/dotnet/HQ.Server/Services/ProjectServiceV1.cs +++ b/src/dotnet/HQ.Server/Services/ProjectServiceV1.cs @@ -6,6 +6,7 @@ using FluentResults; +using HQ.Abstractions; using HQ.Abstractions.Enumerations; using HQ.Abstractions.Projects; using HQ.Abstractions.Staff; @@ -148,6 +149,11 @@ public ProjectServiceV1(ChargeCodeServiceV1 chargeCodeServiceV1, HQDbContext con { records = records.Where(t => t.Quote!.Status == request.ProjectStatus); } + var bookingStartDate = DateOnly.FromDateTime(DateTime.Today).GetPeriodStartDate(Period.Month); + var bookingEndDate = DateOnly.FromDateTime(DateTime.Today).GetPeriodEndDate(Period.Month); + + + var mapped = records.Select(t => new GetProjectsV1.Record() { @@ -162,13 +168,68 @@ public ProjectServiceV1(ChargeCodeServiceV1 chargeCodeServiceV1, HQDbContext con QuoteId = t.QuoteId, QuoteNumber = t.Quote != null ? t.Quote.QuoteNumber : null, HourlyRate = t.HourlyRate, - BookingHours = t.BookingHours, BookingPeriod = t.BookingPeriod, StartDate = t.StartDate, EndDate = t.EndDate, BillingEmail = t.Client.BillingEmail, OfficialName = t.Client.OfficialName, - Status = t.Quote != null ? (int)t.Quote.Status : 0 + Status = t.Quote != null ? (int)t.Quote.Status : 0, + ProjectStatus = t.Status, + + + BookingStartDate = t.ChargeCode!.Times.Where(x => x.Date >= bookingStartDate && x.Date <= bookingEndDate).Min(x => x.Date), + BookingEndDate = t.ChargeCode!.Times.Where(x => x.Date >= bookingStartDate && x.Date <= bookingEndDate).Max(x => x.Date), + BookingHours = t.ChargeCode!.Times.Where(x => x.Date >= bookingStartDate && x.Date <= bookingEndDate).Sum(x => x.Hours), + BookingAvailableHours = t.BookingHours - t.ChargeCode!.Times.Where(x => x.Date >= bookingStartDate && x.Date <= bookingEndDate).Sum(x => x.Hours), + BookingPercentComplete = t.BookingHours == 0 ? 0 : t.ChargeCode!.Times.Where(x => x.Date >= bookingStartDate && x.Date <= bookingEndDate).Sum(x => x.Hours) / t.BookingHours, + + TotalHours = t.ChargeCode!.Times.Sum(x => x.Hours), + TotalAvailableHours = t.TotalHours != null ? t.TotalHours.Value - t.ChargeCode!.Times.Sum(x => x.Hours) : null, + TotalPercentComplete = !t.TotalHours.HasValue || t.TotalHours == 0 ? null : t.ChargeCode!.Times.Sum(x => x.Hours) / t.TotalHours.Value, + TotalPercentCompleteSort = !t.TotalHours.HasValue || t.TotalHours == 0 ? -1 : t.ChargeCode!.Times.Sum(x => x.Hours) / t.TotalHours.Value, + TotalStartDate = t.ChargeCode.Times.Min(t => t.Date), + TotalEndDate = t.ChargeCode.Times.Max(t => t.Date), + }) + .Select(t => new GetProjectsV1.Record() + { + Id = t.Id, + ProjectNumber = t.ProjectNumber, + ChargeCode = t.ChargeCode, + ClientId = t.ClientId, + ClientName = t.ClientName, + ProjectManagerId = t.ProjectManagerId, + ProjectManagerName = t.ProjectManagerName, + Name = t.Name, + QuoteId = t.QuoteId, + QuoteNumber = t.QuoteNumber, + HourlyRate = t.HourlyRate, + BookingPeriod = t.BookingPeriod, + StartDate = t.StartDate, + EndDate = t.EndDate, + BillingEmail = t.BillingEmail, + OfficialName = t.OfficialName, + Status = t.Status, + ProjectStatus = t.ProjectStatus, + + + BookingStartDate = t.BookingStartDate, + BookingEndDate = t.BookingEndDate, + BookingHours = t.BookingHours, + BookingAvailableHours = t.BookingAvailableHours, + BookingPercentComplete = t.BookingPercentComplete, + + TotalHours = t.TotalHours, + TotalAvailableHours = t.TotalAvailableHours, + TotalPercentComplete = t.TotalPercentComplete, + TotalPercentCompleteSort = t.TotalPercentCompleteSort, + TotalStartDate = t.TotalStartDate, + TotalEndDate = t.TotalEndDate, + + SummaryHoursTotal = t.ProjectStatus == ProjectStatus.Ongoing ? t.BookingHours : t.TotalHours, + SummaryHoursAvailable = t.ProjectStatus == ProjectStatus.Ongoing ? t.BookingAvailableHours : t.TotalAvailableHours, + SummaryPercentComplete = t.ProjectStatus == ProjectStatus.Ongoing ? t.BookingPercentComplete : t.TotalPercentComplete, + SummaryPercentCompleteSort = t.ProjectStatus == ProjectStatus.Ongoing ? t.BookingPercentComplete : t.TotalPercentCompleteSort + }); var sortMap = new Dictionary() @@ -179,7 +240,22 @@ public ProjectServiceV1(ChargeCodeServiceV1 chargeCodeServiceV1, HQDbContext con { Abstractions.Projects.GetProjectsV1.SortColumn.EndDate, "EndDate" }, { Abstractions.Projects.GetProjectsV1.SortColumn.ClientName, "ClientName" }, { Abstractions.Projects.GetProjectsV1.SortColumn.ChargeCode, "ChargeCode" }, - { Abstractions.Projects.GetProjectsV1.SortColumn.Status, "Status" } + { Abstractions.Projects.GetProjectsV1.SortColumn.Status, "ProjectStatus" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingPeriod, "BookingPeriod" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingStartDate, "BookingStartDate" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingEndDate, "BookingEndDate" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.TotalHours, "TotalHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.TotalAvailableHours, "TotalAvailableHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.ThisHours, "ThisHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.ThisPendingHours, "ThisPendingHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.LastHours, "LastHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingHours, "BookingHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingAvailableHours, "BookingAvailableHours" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.TotalPercentComplete, "TotalPercentCompleteSort" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.BookingPercentComplete, "BookingPercentComplete" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.SummaryHoursTotal, "SummaryHoursTotal" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.SummaryHoursAvailable, "SummaryHoursAvailable" }, + { Abstractions.Projects.GetProjectsV1.SortColumn.SummaryPercentComplete, "SummaryPercentCompleteSort" }, }; var sortProperty = sortMap[request.SortBy];