fix(pay-period): change payload to send regular hours and other hours, rather than each individual shift type as a property
This commit is contained in:
parent
7c7edea768
commit
5a1017f82b
|
|
@ -1134,6 +1134,45 @@
|
|||
"SchedulePresets"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/schedule-presets/apply-presets/{email}": {
|
||||
"post": {
|
||||
"operationId": "SchedulePresetsController_applyPresets",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "email",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "preset",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "start",
|
||||
"required": true,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"SchedulePresets"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"info": {
|
||||
|
|
@ -1551,20 +1590,10 @@
|
|||
"example": 40,
|
||||
"description": "pay-period`s regular hours"
|
||||
},
|
||||
"evening_hours": {
|
||||
"type": "number",
|
||||
"other_hours": {
|
||||
"type": "object",
|
||||
"example": 0,
|
||||
"description": "pay-period`s evening hours"
|
||||
},
|
||||
"emergency_hours": {
|
||||
"type": "number",
|
||||
"example": 0,
|
||||
"description": "pay-period`s emergency hours"
|
||||
},
|
||||
"overtime_hours": {
|
||||
"type": "number",
|
||||
"example": 2,
|
||||
"description": "pay-period`s overtime hours"
|
||||
"description": "pay-period`s other hours"
|
||||
},
|
||||
"expenses": {
|
||||
"type": "number",
|
||||
|
|
@ -1585,9 +1614,7 @@
|
|||
"required": [
|
||||
"employee_name",
|
||||
"regular_hours",
|
||||
"evening_hours",
|
||||
"emergency_hours",
|
||||
"overtime_hours",
|
||||
"other_hours",
|
||||
"expenses",
|
||||
"mileage",
|
||||
"is_approved"
|
||||
|
|
|
|||
|
|
@ -62,17 +62,21 @@ export class LeaveRequestsUtils {
|
|||
await this.shiftsCommand.upsertShiftsByDate(email, iso_date, {
|
||||
old_shift: existing
|
||||
? {
|
||||
date: existing.date,
|
||||
start_time: existing.start_time.toISOString().slice(11, 16),
|
||||
end_time: existing.end_time.toISOString().slice(11, 16),
|
||||
type: existing.bank_code?.type ?? type,
|
||||
is_remote: existing.is_remote,
|
||||
is_approved:existing.is_approved,
|
||||
comment: existing.comment ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
new_shift: {
|
||||
date: existing?.date ?? '',
|
||||
start_time: toHHmm(start_minutes),
|
||||
end_time: toHHmm(end_minutes),
|
||||
is_remote: existing?.is_remote ?? false,
|
||||
is_approved:existing?.is_approved ?? false,
|
||||
comment: comment ?? existing?.comment ?? "",
|
||||
type: type,
|
||||
},
|
||||
|
|
@ -97,10 +101,12 @@ export class LeaveRequestsUtils {
|
|||
|
||||
await this.shiftsCommand.upsertShiftsByDate(email, iso_date, {
|
||||
old_shift: {
|
||||
date: existing.date,
|
||||
start_time: existing.start_time.toISOString().slice(11, 16),
|
||||
end_time: existing.end_time.toISOString().slice(11, 16),
|
||||
type: existing.bank_code?.type ?? type,
|
||||
is_remote: existing.is_remote,
|
||||
is_approved:existing.is_approved,
|
||||
comment: existing.comment ?? undefined,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export class EmployeePeriodOverviewDto {
|
|||
// employee_id: number;
|
||||
|
||||
|
||||
email:string;
|
||||
email: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'Alex Dupont',
|
||||
|
|
@ -21,15 +21,21 @@ export class EmployeePeriodOverviewDto {
|
|||
@ApiProperty({ example: 40, description: 'pay-period`s regular hours' })
|
||||
regular_hours: number;
|
||||
|
||||
@ApiProperty({ example: 0, description: 'pay-period`s evening hours' })
|
||||
@ApiProperty({ example: 0, description: 'pay-period`s other hours' })
|
||||
other_hours: {
|
||||
evening_hours: number;
|
||||
|
||||
@ApiProperty({ example: 0, description: 'pay-period`s emergency hours' })
|
||||
emergency_hours: number;
|
||||
|
||||
@ApiProperty({ example: 2, description: 'pay-period`s overtime hours' })
|
||||
overtime_hours: number;
|
||||
|
||||
sick_hours: number;
|
||||
|
||||
holiday_hours: number;
|
||||
|
||||
vacation_hours: number;
|
||||
};
|
||||
|
||||
total_hours: number;
|
||||
|
||||
@ApiProperty({ example: 420.69, description: 'pay-period`s total expenses ($)' })
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper";
|
|||
|
||||
@Injectable()
|
||||
export class PayPeriodsQueryService {
|
||||
constructor( private readonly prisma: PrismaService) {}
|
||||
constructor(private readonly prisma: PrismaService) { }
|
||||
|
||||
async getOverview(pay_period_no: number): Promise<PayPeriodOverviewDto> {
|
||||
const period = await this.prisma.payPeriods.findFirst({
|
||||
|
|
@ -20,11 +20,11 @@ export class PayPeriodsQueryService {
|
|||
|
||||
return this.buildOverview({
|
||||
period_start: period.period_start,
|
||||
period_end : period.period_end,
|
||||
payday : period.payday,
|
||||
period_no : period.pay_period_no,
|
||||
pay_year : period.pay_year,
|
||||
label : period.label,
|
||||
period_end: period.period_end,
|
||||
payday: period.payday,
|
||||
period_no: period.pay_period_no,
|
||||
pay_year: period.pay_year,
|
||||
label: period.label,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -32,11 +32,11 @@ export class PayPeriodsQueryService {
|
|||
const period = computePeriod(pay_year, period_no);
|
||||
return this.buildOverview({
|
||||
period_start: period.period_start,
|
||||
period_end : period.period_end,
|
||||
period_no : period.period_no,
|
||||
pay_year : period.pay_year,
|
||||
payday : period.payday,
|
||||
label :period.label,
|
||||
period_end: period.period_end,
|
||||
period_no: period.period_no,
|
||||
pay_year: period.pay_year,
|
||||
payday: period.payday,
|
||||
label: period.label,
|
||||
} as any);
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ export class PayPeriodsQueryService {
|
|||
|
||||
// 2) fetch supervisor
|
||||
const supervisor = await this.prisma.employees.findFirst({
|
||||
where: { user: { email: email }},
|
||||
where: { user: { email: email } },
|
||||
select: {
|
||||
id: true,
|
||||
is_supervisor: true,
|
||||
|
|
@ -101,28 +101,32 @@ export class PayPeriodsQueryService {
|
|||
const seed_names = new Map<number, { name: string; email: string }>(
|
||||
crew.map(crew => [
|
||||
crew.id,
|
||||
{ name:`${crew.first_name} ${crew.last_name}`.trim(),
|
||||
email: crew.email }
|
||||
{
|
||||
name: `${crew.first_name} ${crew.last_name}`.trim(),
|
||||
email: crew.email
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
// 4) overview build
|
||||
return this.buildOverview({
|
||||
period_no : period.pay_period_no,
|
||||
period_no: period.pay_period_no,
|
||||
period_start: period.period_start,
|
||||
period_end : period.period_end,
|
||||
payday : period.payday,
|
||||
pay_year : period.pay_year,
|
||||
label : period.label,
|
||||
period_end: period.period_end,
|
||||
payday: period.payday,
|
||||
pay_year: period.pay_year,
|
||||
label: period.label,
|
||||
//add is_approved
|
||||
}, { filtered_employee_ids: crew_ids, seed_names });
|
||||
}
|
||||
|
||||
private async buildOverview(
|
||||
period: { period_start: string | Date; period_end: string | Date; payday: string | Date;
|
||||
period_no: number; pay_year: number; label: string; }, //add is_approved
|
||||
options?: { filtered_employee_ids?: number[]; seed_names?: Map<number, {name: string, email: string}>}
|
||||
period: {
|
||||
period_start: string | Date; period_end: string | Date; payday: string | Date;
|
||||
period_no: number; pay_year: number; label: string;
|
||||
}, //add is_approved
|
||||
options?: { filtered_employee_ids?: number[]; seed_names?: Map<number, { name: string, email: string }> }
|
||||
): Promise<PayPeriodOverviewDto> {
|
||||
const toDateString = (d: Date) => d.toISOString().slice(0, 10);
|
||||
const toMoney = (v: any) => (typeof v === "object" && "toNumber" in v ? v.toNumber() : (v as number));
|
||||
|
|
@ -137,10 +141,10 @@ export class PayPeriodsQueryService {
|
|||
|
||||
const payd = period.payday instanceof Date
|
||||
? period.payday
|
||||
: new Date (`${period.payday}T00:00:00.000Z`);
|
||||
: new Date(`${period.payday}T00:00:00.000Z`);
|
||||
|
||||
//restrictEmployeeIds = filter for shifts and expenses by employees
|
||||
const where_employee = options?.filtered_employee_ids?.length ? { employee_id: { in: options.filtered_employee_ids } }: {};
|
||||
const where_employee = options?.filtered_employee_ids?.length ? { employee_id: { in: options.filtered_employee_ids } } : {};
|
||||
|
||||
// SHIFTS (filtered by crew)
|
||||
const shifts = await this.prisma.shifts.findMany({
|
||||
|
|
@ -152,16 +156,21 @@ export class PayPeriodsQueryService {
|
|||
start_time: true,
|
||||
end_time: true,
|
||||
is_remote: true,
|
||||
timesheet: { select: {
|
||||
timesheet: {
|
||||
select: {
|
||||
is_approved: true,
|
||||
employee: { select: {
|
||||
employee: {
|
||||
select: {
|
||||
id: true,
|
||||
user: { select: {
|
||||
user: {
|
||||
select: {
|
||||
first_name: true,
|
||||
last_name: true,
|
||||
email: true,
|
||||
} },
|
||||
} },
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
bank_code: { select: { categorie: true, type: true } },
|
||||
|
|
@ -176,17 +185,23 @@ export class PayPeriodsQueryService {
|
|||
},
|
||||
select: {
|
||||
amount: true,
|
||||
timesheet: { select: {
|
||||
timesheet: {
|
||||
select: {
|
||||
is_approved: true,
|
||||
employee: { select: {
|
||||
employee: {
|
||||
select: {
|
||||
id: true,
|
||||
user: { select: {
|
||||
user: {
|
||||
select: {
|
||||
first_name: true,
|
||||
last_name: true,
|
||||
email: true,
|
||||
} },
|
||||
} },
|
||||
} },
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
bank_code: { select: { categorie: true, modifier: true, type: true } },
|
||||
},
|
||||
});
|
||||
|
|
@ -195,14 +210,19 @@ export class PayPeriodsQueryService {
|
|||
|
||||
// seed for employee without data
|
||||
if (options?.seed_names) {
|
||||
for (const [id, {name, email}] of options.seed_names.entries()) {
|
||||
for (const [id, { name, email }] of options.seed_names.entries()) {
|
||||
by_employee.set(id, {
|
||||
email,
|
||||
employee_name: name,
|
||||
regular_hours: 0,
|
||||
other_hours: {
|
||||
evening_hours: 0,
|
||||
emergency_hours: 0,
|
||||
overtime_hours: 0,
|
||||
sick_hours: 0,
|
||||
holiday_hours: 0,
|
||||
vacation_hours: 0,
|
||||
},
|
||||
total_hours: 0,
|
||||
expenses: 0,
|
||||
mileage: 0,
|
||||
|
|
@ -218,9 +238,14 @@ export class PayPeriodsQueryService {
|
|||
email,
|
||||
employee_name: name,
|
||||
regular_hours: 0,
|
||||
other_hours: {
|
||||
evening_hours: 0,
|
||||
emergency_hours: 0,
|
||||
overtime_hours: 0,
|
||||
sick_hours: 0,
|
||||
holiday_hours: 0,
|
||||
vacation_hours: 0,
|
||||
},
|
||||
total_hours: 0,
|
||||
expenses: 0,
|
||||
mileage: 0,
|
||||
|
|
@ -239,16 +264,25 @@ export class PayPeriodsQueryService {
|
|||
const hours = computeHours(shift.start_time, shift.end_time);
|
||||
const type = (shift.bank_code?.type ?? '').toUpperCase();
|
||||
switch (type) {
|
||||
case "EVENING": record.evening_hours += hours;
|
||||
case "EVENING": record.other_hours.evening_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "EMERGENCY": record.emergency_hours += hours;
|
||||
case "EMERGENCY": record.other_hours.emergency_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "OVERTIME": record.overtime_hours += hours;
|
||||
case "OVERTIME": record.other_hours.overtime_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "REGULAR" : record.regular_hours += hours;
|
||||
case "SICK": record.other_hours.sick_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "HOLIDAY": record.other_hours.holiday_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "VACATION": record.other_hours.vacation_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
case "REGULAR": record.regular_hours += hours;
|
||||
record.total_hours += hours;
|
||||
break;
|
||||
}
|
||||
|
|
@ -288,7 +322,7 @@ export class PayPeriodsQueryService {
|
|||
};
|
||||
}
|
||||
|
||||
async getSupervisor(email:string) {
|
||||
async getSupervisor(email: string) {
|
||||
return this.prisma.employees.findFirst({
|
||||
where: { user: { email } },
|
||||
select: { id: true, is_supervisor: true },
|
||||
|
|
@ -297,7 +331,7 @@ export class PayPeriodsQueryService {
|
|||
|
||||
async findAll(): Promise<PayPeriodDto[]> {
|
||||
const currentPayYear = payYearOfDate(new Date());
|
||||
return listPayYear(currentPayYear).map(period =>({
|
||||
return listPayYear(currentPayYear).map(period => ({
|
||||
pay_period_no: period.period_no,
|
||||
pay_year: period.pay_year,
|
||||
payday: period.payday,
|
||||
|
|
@ -318,7 +352,7 @@ export class PayPeriodsQueryService {
|
|||
}
|
||||
|
||||
async findCurrent(date?: string): Promise<PayPeriodDto> {
|
||||
const iso_day = date ?? new Date().toISOString().slice(0,10);
|
||||
const iso_day = date ?? new Date().toISOString().slice(0, 10);
|
||||
return this.findByDate(iso_day);
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +360,7 @@ export class PayPeriodsQueryService {
|
|||
const row = await this.prisma.payPeriods.findFirst({
|
||||
where: { pay_year, pay_period_no: period_no },
|
||||
});
|
||||
if(row) return mapPayPeriodToDto(row);
|
||||
if (row) return mapPayPeriodToDto(row);
|
||||
|
||||
// fallback for outside of view periods
|
||||
const period = computePeriod(pay_year, period_no);
|
||||
|
|
@ -346,27 +380,27 @@ export class PayPeriodsQueryService {
|
|||
const row = await this.prisma.payPeriods.findFirst({
|
||||
where: { period_start: { lte: dt }, period_end: { gte: dt } },
|
||||
});
|
||||
if(row) return mapPayPeriodToDto(row);
|
||||
if (row) return mapPayPeriodToDto(row);
|
||||
|
||||
//fallback for outwside view periods
|
||||
const pay_year = payYearOfDate(date);
|
||||
const periods = listPayYear(pay_year);
|
||||
const hit = periods.find(period => date >= period.period_start && date <= period.period_end);
|
||||
if(!hit) throw new NotFoundException(`No period found for ${date}`);
|
||||
if (!hit) throw new NotFoundException(`No period found for ${date}`);
|
||||
|
||||
return {
|
||||
pay_period_no: hit.period_no,
|
||||
pay_year : hit.pay_year,
|
||||
period_start : hit.period_start,
|
||||
period_end : hit.period_end,
|
||||
payday : hit.payday,
|
||||
label : hit.label
|
||||
pay_year: hit.pay_year,
|
||||
period_start: hit.period_start,
|
||||
period_end: hit.period_end,
|
||||
payday: hit.payday,
|
||||
label: hit.label
|
||||
}
|
||||
}
|
||||
|
||||
async getPeriodWindow(pay_year: number, period_no: number) {
|
||||
return this.prisma.payPeriods.findFirst({
|
||||
where: {pay_year, pay_period_no: period_no },
|
||||
where: { pay_year, pay_period_no: period_no },
|
||||
select: { period_start: true, period_end: true },
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user