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 7423b202..c93cbddd 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 @@ -19,19 +19,25 @@ } } - @if(clientDetailService.showCurrentOnly$ | async){ -
-
- Show current only -
-
diff --git a/src/angular/hq/src/app/clients/client-details/client-details.component.html b/src/angular/hq/src/app/clients/client-details/client-details.component.html index 0ea845a2..eede5ca2 100644 --- a/src/angular/hq/src/app/clients/client-details/client-details.component.html +++ b/src/angular/hq/src/app/clients/client-details/client-details.component.html @@ -6,8 +6,16 @@
- Projects - Quotes + Projects + Quotes
diff --git a/src/angular/hq/src/app/clients/client-details/client-details.service.ts b/src/angular/hq/src/app/clients/client-details/client-details.service.ts index 3159d908..9118627c 100644 --- a/src/angular/hq/src/app/clients/client-details/client-details.service.ts +++ b/src/angular/hq/src/app/clients/client-details/client-details.service.ts @@ -75,11 +75,10 @@ export class ClientDetailsService { this.clientIdSubject.next(clientId); } } - showCurrentOnly(){ + showCurrentOnly() { this.showCurrentOnlySubject.next(true); } - hideCurrentOnly(){ + hideCurrentOnly() { this.showCurrentOnlySubject.next(false); - } } diff --git a/src/angular/hq/src/app/clients/client-details/client-project-list/client-project-list.service.ts b/src/angular/hq/src/app/clients/client-details/client-project-list/client-project-list.service.ts index d7eef66d..0b1100f4 100644 --- a/src/angular/hq/src/app/clients/client-details/client-project-list/client-project-list.service.ts +++ b/src/angular/hq/src/app/clients/client-details/client-project-list/client-project-list.service.ts @@ -11,7 +11,8 @@ import { shareReplay, combineLatest, debounceTime, - switchMap,startWith + switchMap, + startWith, } from 'rxjs'; import { formControlChanges } from '../../../core/functions/form-control-changes'; import { BaseListService } from '../../../core/services/base-list.service'; @@ -43,20 +44,22 @@ export class ClientProjectListService extends BaseListService< this.clientDetailsService.projectStatus, ).pipe( tap(() => this.goToPage(1)), - tap((status)=> { - if(status != null){ + tap((status) => { + if (status != null) { this.clientDetailsService.currentOnly.setValue(false); - } - }), + } + }), shareReplay({ bufferSize: 1, refCount: false }), ); - const currentOnly$ = formControlChanges(this.clientDetailsService.currentOnly).pipe( + const currentOnly$ = formControlChanges( + this.clientDetailsService.currentOnly, + ).pipe( startWith(this.clientDetailsService.currentOnly.value), - tap(value => { - if (value){ + tap((value) => { + if (value) { this.clientDetailsService.projectStatus.setValue(null); } - }) + }), ); const result$ = combineLatest({ search: search$, @@ -66,7 +69,7 @@ export class ClientProjectListService extends BaseListService< sortBy: this.sortOption$, projectStatus: projectStatus$, sortDirection: this.sortDirection$, - currentOnly: currentOnly$ + currentOnly: currentOnly$, }).pipe( debounceTime(500), tap(() => this.loadingSubject.next(true)), 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 24f885d7..45c0daf8 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 @@ -52,17 +52,23 @@

Projects

}
- @if(listService){} + @if (listService) {}
Show current only
On
diff --git a/src/angular/hq/src/app/projects/project-list/project-list.service.ts b/src/angular/hq/src/app/projects/project-list/project-list.service.ts index 03794f46..fca341d9 100644 --- a/src/angular/hq/src/app/projects/project-list/project-list.service.ts +++ b/src/angular/hq/src/app/projects/project-list/project-list.service.ts @@ -15,7 +15,8 @@ import { Observable, shareReplay, switchMap, - tap, startWith + tap, + startWith, } from 'rxjs'; import { SortDirection } from '../../models/common/sort-direction'; import { HQService } from '../../services/hq.service'; @@ -43,9 +44,9 @@ export class ProjectListService extends BaseListService< public projectStatus = new FormControl(null); public projectStatus$ = formControlChanges(this.projectStatus).pipe( tap(() => this.goToPage(1)), - tap((status)=> { - if(status != null){ - this.currentOnly.setValue(false); + tap((status) => { + if (status != null) { + this.currentOnly.setValue(false); } }), shareReplay({ bufferSize: 1, refCount: false }), @@ -60,13 +61,13 @@ export class ProjectListService extends BaseListService< projectManagers$: Observable; currentOnly = new FormControl(true); public currentOnly$ = formControlChanges(this.currentOnly).pipe( - startWith(this.currentOnly.value), - tap(value => { - if (value){ - this.projectStatus.setValue(null); - } - }) - ); + startWith(this.currentOnly.value), + tap((value) => { + if (value) { + this.projectStatus.setValue(null); + } + }), + ); protected override getResponse(): Observable { return combineLatest({ search: this.search$, diff --git a/src/dotnet/HQ.Server/Services/HolidayServiceV1.cs b/src/dotnet/HQ.Server/Services/HolidayServiceV1.cs index 5fcc374e..4dbe2c01 100644 --- a/src/dotnet/HQ.Server/Services/HolidayServiceV1.cs +++ b/src/dotnet/HQ.Server/Services/HolidayServiceV1.cs @@ -31,33 +31,53 @@ public HolidayServiceV1(HQDbContext context, ILogger> UpsertHolidayV1(UpsertHolidayV1.Request request, CancellationToken ct = default) { - var validationResult = Result.Merge( + using (var transaction = await _context.Database.BeginTransactionAsync(System.Data.IsolationLevel.Serializable, ct)) + { + + try + { + var validationResult = Result.Merge( Result.FailIf(string.IsNullOrEmpty(request.Name), "Name is required."), Result.FailIf(await _context.Holidays.AnyAsync(t => t.Id != request.Id && t.Date == request.Date && t.Jurisdiciton == request.Jurisdiciton, ct), "Another holiday already exists on that date.") ); - if (validationResult.IsFailed) - { - return validationResult; - } + if (validationResult.IsFailed) + { + return validationResult; + } - var holiday = await _context.Holidays.FindAsync(request.Id); - if (holiday == null) - { - holiday = new(); - _context.Holidays.Add(holiday); - } + var holiday = await _context.Holidays.FindAsync(request.Id); + if (holiday == null) + { + holiday = new(); + _context.Holidays.Add(holiday); + } - holiday.Name = request.Name; - holiday.Jurisdiciton = request.Jurisdiciton; - holiday.Date = request.Date; + holiday.Name = request.Name; + holiday.Jurisdiciton = request.Jurisdiciton; + holiday.Date = request.Date; - await _context.SaveChangesAsync(ct); + await _context.SaveChangesAsync(ct); - return new UpsertHolidayV1.Response() - { - Id = holiday.Id - }; + var holidayChargeCode = await _context.ChargeCodes.Where(t => t.Project!.Name.ToLower().Contains("holiday")).FirstOrDefaultAsync(ct); + if (holidayChargeCode == null) + { + return Result.Fail("Holiday charge code not found"); + } + await GenerateTimeEntriesForHoliday(holiday, holidayChargeCode, ct); + await transaction.CommitAsync(ct); + + return new UpsertHolidayV1.Response() + { + Id = holiday.Id + }; + } + catch (Exception ex) + { + await transaction.RollbackAsync(ct); + return Result.Fail(new Error("An error occurred while upserting the Chargecode.").CausedBy(ex)); + } + } } public async Task> DeleteHolidayV1(DeleteHolidayV1.Request request, CancellationToken ct = default) @@ -210,6 +230,44 @@ public HolidayServiceV1(HQDbContext context, ILogger t.EndDate == null && t.Jurisdiciton == jurisdiciton); + var times = _context.Times + .AsNoTracking() + .AsQueryable() + .Include(t => t.Staff); + var staffWithHoliday = await times.Where(t => t.Date == holiday.Date && t.ChargeCode.Id == holidayChargeCode.Id && t.Staff.Jurisdiciton == jurisdiciton && t.Staff.EndDate == null).AsNoTracking().Select(t => t.Staff).ToListAsync(ct); + + var staffWithHolidayIds = staffWithHoliday.Select(s => s.Id).ToList(); + var staffWithoutEnteredHoliday = await staff.Where(t => !staffWithHolidayIds.Contains(t.Id)).ToListAsync(ct); + + _logger.LogInformation($"Creating time entries for Holiday {holiday.Name} for staff count {staffWithoutEnteredHoliday.Count()}"); + foreach (var staffMember in staffWithoutEnteredHoliday) + { + var timeEntry = new Time + { + ChargeCodeId = holidayChargeCode.Id, + Hours = 8, + HoursApproved = 8, + Notes = holiday.Name, + StaffId = staffMember.Id, + Date = holiday.Date, + Status = TimeStatus.Accepted, + HolidayId = holiday.Id + }; + _context.Times.Add(timeEntry); + } + await _context.SaveChangesAsync(ct); + _logger.LogInformation($"Created time entries for Holiday {holiday.Name} for staff count {staffWithoutEnteredHoliday.Count()}"); + } + public async Task BackgroundAutoGenerateHolidayTimeEntryV1(CancellationToken ct) { var today = DateOnly.FromDateTime(DateTime.UtcNow); @@ -219,57 +277,17 @@ public async Task BackgroundAutoGenerateHolidayTimeEntryV1(CancellationToken ct) var holidays = _context.Holidays .AsNoTracking() .AsQueryable(); - var jurisdicitons = Enum.GetValues(typeof(Jurisdiciton)); var holidayChargeCode = await _context.ChargeCodes.Where(t => t.Project!.Name.ToLower().Contains("holiday")).FirstOrDefaultAsync(ct); if (holidayChargeCode == null) { return; } - foreach (Jurisdiciton jurisdiciton in jurisdicitons) - { - var upcomingHolidays = await holidays.Where(t => t.Date >= startDate && t.Date <= endDate && t.Jurisdiciton == jurisdiciton).ToListAsync(ct); - foreach (var upcomingHoliday in upcomingHolidays) - { - var staff = _context.Staff. - AsNoTracking() - .AsQueryable(); + var upcomingHolidays = await holidays.Where(t => t.Date >= startDate && t.Date <= endDate).ToListAsync(ct); + foreach (var upcomingHoliday in upcomingHolidays) + { - staff = staff.Where(t => t.EndDate == null && t.Jurisdiciton == jurisdiciton); - var times = _context.Times - .AsNoTracking() - .AsQueryable() - .Include(t => t.Staff); - var staffWithHoliday = await times.Where(t => t.Date == upcomingHoliday.Date && t.ChargeCode.Id == holidayChargeCode.Id && t.Staff.Jurisdiciton == jurisdiciton && t.Staff.EndDate == null).AsNoTracking().Select(t => t.Staff).ToListAsync(ct); - - var staffWithHolidayIds = staffWithHoliday.Select(s => s.Id).ToList(); - var staffWithoutEnteredHoliday = await staff.Where(t => !staffWithHolidayIds.Contains(t.Id)).ToListAsync(ct); - - _logger.LogInformation($"Creating time entries for Holiday {upcomingHoliday.Name} for staff count {staffWithoutEnteredHoliday.Count()}"); - foreach (var staffMember in staffWithoutEnteredHoliday) - { - - var timeEntry = new Time - { - ChargeCodeId = holidayChargeCode.Id, - Hours = 8, - HoursApproved = 8, - Notes = upcomingHoliday.Name, - StaffId = staffMember.Id, - Date = upcomingHoliday.Date, - Status = TimeStatus.Accepted, - HolidayId = upcomingHoliday.Id - }; - _context.Times.Add(timeEntry); - } - await _context.SaveChangesAsync(ct); - - - _logger.LogInformation($"Created time entries for Holiday {upcomingHoliday.Name} for staff count {staffWithoutEnteredHoliday.Count()}"); - } - + await GenerateTimeEntriesForHoliday(upcomingHoliday, holidayChargeCode, ct); } - } - } \ No newline at end of file