137 lines
4.6 KiB
JavaScript
137 lines
4.6 KiB
JavaScript
const queries = require("../graphql-client/queries");
|
|
const logger = require("../utils/logger");
|
|
const { CalculateExpectedHoursForJob, RoundPayrollHours } = require("./pay-all");
|
|
const moment = require("moment");
|
|
|
|
const normalizePercent = (value) => Math.round((Number(value || 0) + Number.EPSILON) * 10000) / 10000;
|
|
|
|
const getTaskPresetAllocationError = (taskPresets = []) => {
|
|
const totalsByLaborType = {};
|
|
|
|
taskPresets.forEach((taskPreset) => {
|
|
const percent = normalizePercent(taskPreset?.percent);
|
|
|
|
if (!percent) {
|
|
return;
|
|
}
|
|
|
|
const laborTypes = Array.isArray(taskPreset?.hourstype) ? taskPreset.hourstype : [];
|
|
|
|
laborTypes.forEach((laborType) => {
|
|
if (!laborType) {
|
|
return;
|
|
}
|
|
|
|
totalsByLaborType[laborType] = normalizePercent((totalsByLaborType[laborType] || 0) + percent);
|
|
});
|
|
});
|
|
|
|
const overAllocatedType = Object.entries(totalsByLaborType).find(([, total]) => total > 100);
|
|
|
|
if (!overAllocatedType) {
|
|
return null;
|
|
}
|
|
|
|
const [laborType, total] = overAllocatedType;
|
|
return `Task preset percentages for labor type ${laborType} total ${total}% and cannot exceed 100%.`;
|
|
};
|
|
|
|
exports.GetTaskPresetAllocationError = getTaskPresetAllocationError;
|
|
|
|
exports.claimtask = async function (req, res) {
|
|
const { jobid, task, calculateOnly, employee } = req.body;
|
|
logger.log("job-payroll-pay-all", "DEBUG", req.user.email, jobid, null);
|
|
|
|
const BearerToken = req.BearerToken;
|
|
const client = req.userGraphQLClient;
|
|
|
|
try {
|
|
const { jobs_by_pk: job } = await client
|
|
.setHeaders({ Authorization: BearerToken })
|
|
.request(queries.QUERY_JOB_PAYROLL_DATA, {
|
|
id: jobid
|
|
});
|
|
|
|
const taskPresets = job.bodyshop?.md_tasks_presets?.presets || [];
|
|
const taskPresetAllocationError = getTaskPresetAllocationError(taskPresets);
|
|
if (taskPresetAllocationError) {
|
|
res.status(400).json({ success: false, error: taskPresetAllocationError });
|
|
return;
|
|
}
|
|
|
|
const theTaskPreset = taskPresets.find((tp) => tp.name === task);
|
|
if (!theTaskPreset) {
|
|
res.status(400).json({ success: false, error: "Provided task preset not found." });
|
|
return;
|
|
}
|
|
|
|
const taskAlreadyCompleted = (job.completed_tasks || []).some((completedTask) => completedTask?.name === task);
|
|
if (taskAlreadyCompleted) {
|
|
res.status(400).json({ success: false, error: "Provided task preset has already been completed for this job." });
|
|
return;
|
|
}
|
|
|
|
//Get all of the assignments that are filtered.
|
|
const { assignmentHash, employeeHash } = CalculateExpectedHoursForJob(job, theTaskPreset.hourstype);
|
|
const ticketsToInsert = [];
|
|
//Then add them in based on a percentage to each employee.
|
|
|
|
Object.keys(employeeHash).forEach((employeeIdKey) => {
|
|
//At the employee level.
|
|
Object.keys(employeeHash[employeeIdKey]).forEach((laborTypeKey) => {
|
|
const expected = employeeHash[employeeIdKey][laborTypeKey];
|
|
const expectedHours = RoundPayrollHours(expected.hours * (theTaskPreset.percent / 100));
|
|
|
|
ticketsToInsert.push({
|
|
task_name: task,
|
|
jobid: job.id,
|
|
bodyshopid: job.bodyshop.id,
|
|
employeeid: employeeIdKey,
|
|
productivehrs: expectedHours,
|
|
rate: expected.rate,
|
|
ciecacode: laborTypeKey,
|
|
flat_rate: true,
|
|
created_by: employee?.name || req.user.email,
|
|
payout_context: {
|
|
...(expected.payoutContext || {}),
|
|
generated_by: req.user.email,
|
|
generated_at: new Date().toISOString(),
|
|
generated_from: "claimtask",
|
|
task_name: task
|
|
},
|
|
cost_center: job.bodyshop.md_responsibility_centers.defaults.costs[laborTypeKey],
|
|
memo: `*Flagged Task* ${theTaskPreset.memo}`
|
|
});
|
|
});
|
|
});
|
|
if (!calculateOnly) {
|
|
//Insert the time ticekts if we're not just calculating them.
|
|
await client.request(queries.INSERT_TIME_TICKETS, {
|
|
timetickets: ticketsToInsert.filter((ticket) => ticket.productivehrs !== 0)
|
|
});
|
|
await client.request(queries.UPDATE_JOB, {
|
|
jobId: job.id,
|
|
job: {
|
|
status: theTaskPreset.nextstatus,
|
|
completed_tasks: [
|
|
...job.completed_tasks,
|
|
{
|
|
name: task,
|
|
completedat: moment(),
|
|
completed_by: employee,
|
|
useremail: req.user.email
|
|
}
|
|
]
|
|
}
|
|
});
|
|
}
|
|
res.json({ unassignedHours: assignmentHash.unassigned, ticketsToInsert });
|
|
} catch (error) {
|
|
logger.log("job-payroll-claim-task-error", "ERROR", req.user.email, jobid, {
|
|
jobid: jobid,
|
|
error
|
|
});
|
|
res.status(400).json({ success: false, error: error.message });
|
|
}
|
|
};
|