diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 4c202bbd8..c41c57d59 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -1516,6 +1516,27 @@ + + assignedlinehours + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + billposted false @@ -34038,6 +34059,27 @@ tech + + assignedjobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + claimtask false diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx index e34d6a446..c21886af6 100644 --- a/client/src/components/job-detail-lines/job-lines.component.jsx +++ b/client/src/components/job-detail-lines/job-lines.component.jsx @@ -301,7 +301,11 @@ export function JobLinesComponent({ dataIndex: "assigned_team", key: "assigned_team", render: (text, record) => ( - + ), }, ] @@ -442,9 +446,20 @@ export function JobLinesComponent({ {t("joblines.fields.part_types.PAL")} {t("joblines.fields.part_types.PAS")} + {t("joblines.fields.lbr_types.LAA")} {t("joblines.fields.lbr_types.LAB")} - {t("joblines.fields.lbr_types.LAR")} + {t("joblines.fields.lbr_types.LAD")} + {t("joblines.fields.lbr_types.LAE")} + {t("joblines.fields.lbr_types.LAF")} + {t("joblines.fields.lbr_types.LAG")} {t("joblines.fields.lbr_types.LAM")} + {t("joblines.fields.lbr_types.LAR")} + {t("joblines.fields.lbr_types.LAS")} + {t("joblines.fields.lbr_types.LAU")} + {t("joblines.fields.lbr_types.LA1")} + {t("joblines.fields.lbr_types.LA2")} + {t("joblines.fields.lbr_types.LA3")} + {t("joblines.fields.lbr_types.LA2")} {t("general.labels.clear")} diff --git a/client/src/components/job-line-bulk-assign/job-line-bulk-assign.component.jsx b/client/src/components/job-line-bulk-assign/job-line-bulk-assign.component.jsx index 9b7a3fa8d..5f025abba 100644 --- a/client/src/components/job-line-bulk-assign/job-line-bulk-assign.component.jsx +++ b/client/src/components/job-line-bulk-assign/job-line-bulk-assign.component.jsx @@ -11,20 +11,23 @@ import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; +import { insertAuditTrail } from "../../redux/application/application.actions"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, jobRO: selectJobReadOnly, currentUser: selectCurrentUser, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), }); export default connect(mapStateToProps, mapDispatchToProps)(JoblineBulkAssign); export function JoblineBulkAssign({ setSelectedLines, selectedLines, - + insertAuditTrail, bodyshop, jobRO, job, @@ -55,8 +58,28 @@ export function JoblineBulkAssign({ error: JSON.stringify(result.errors), }), }); + } else { + //Insert the audit trail here. + + const teamName = bodyshop.employee_teams.find( + (et) => et.id === values.assigned_team + )?.name; + + const hours = selectedLines.reduce( + (acc, val) => (acc += val.mod_lb_hrs), + 0 + ); + + insertAuditTrail({ + jobid: job.id, + operation: AuditTrailMapping.assignedlinehours( + teamName, + hours.toFixed(1) + ), + }); + + setVisible(false); } - setVisible(false); } catch (error) { notification.open({ type: "error", diff --git a/client/src/components/job-line-team-assignment/job-line-team-assignmnent.component.jsx b/client/src/components/job-line-team-assignment/job-line-team-assignmnent.component.jsx index 929ec1e5d..a83ec62cb 100644 --- a/client/src/components/job-line-team-assignment/job-line-team-assignmnent.component.jsx +++ b/client/src/components/job-line-team-assignment/job-line-team-assignmnent.component.jsx @@ -7,16 +7,25 @@ import { createStructuredSelector } from "reselect"; import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import { insertAuditTrail } from "../../redux/application/application.actions"; +import AuditTrailMapping from "../../utils/AuditTrailMappings"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser bodyshop: selectBodyshop, }); const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + insertAuditTrail: ({ jobid, operation }) => + dispatch(insertAuditTrail({ jobid, operation })), }); -export function JoblineTeamAssignment({ bodyshop, jobline, disabled }) { +export function JoblineTeamAssignment({ + bodyshop, + jobline, + disabled, + jobId, + insertAuditTrail, +}) { const [editing, setEditing] = useState(false); const [loading, setLoading] = useState(false); const [assignedTeam, setAssignedTeam] = useState(jobline.assigned_team); @@ -40,16 +49,19 @@ export function JoblineTeamAssignment({ bodyshop, jobline, disabled }) { }, }); - if ( - assignedTeam === null || - assignedTeam === undefined || - assignedTeam === "" - ) { - alert("TODO - implement calculation to reduce assigned hours if needed."); - } - if (!!!result.errors) { notification["success"]({ message: t("joblines.successes.saved") }); + //insert the audit trail here. + const teamName = bodyshop.employee_teams.find( + (et) => et.id === assignedTeam + )?.name; + insertAuditTrail({ + jobid: jobId, + operation: AuditTrailMapping.assignedlinehours( + teamName, + jobline.mod_lb_hrs + ), + }); } else { notification["error"]({ message: t("joblines.errors.saving", { diff --git a/client/src/components/tech-sider/tech-sider.component.jsx b/client/src/components/tech-sider/tech-sider.component.jsx index 33eae1e0b..aaac65512 100644 --- a/client/src/components/tech-sider/tech-sider.component.jsx +++ b/client/src/components/tech-sider/tech-sider.component.jsx @@ -107,6 +107,13 @@ export function TechSider({ > {t("menus.tech.productionboard")} + } + > + {t("menus.tech.assignedjobs")} + ({ value: preset.name, label: preset.name, - disabled: completedTasks.includes(preset.name), + disabled: completedTasks + .map((task) => task.name) + .includes(preset.name), }))} /> )} diff --git a/client/src/components/time-ticket-task-modal/time-ticket-task-modal.container.jsx b/client/src/components/time-ticket-task-modal/time-ticket-task-modal.container.jsx index b7fb53d41..b2a921d02 100644 --- a/client/src/components/time-ticket-task-modal/time-ticket-task-modal.container.jsx +++ b/client/src/components/time-ticket-task-modal/time-ticket-task-modal.container.jsx @@ -7,15 +7,21 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { selectTimeTicketTasks } from "../../redux/modals/modals.selectors"; -import { selectBodyshop } from "../../redux/user/user.selectors"; +import { + selectBodyshop, + selectCurrentUser, +} from "../../redux/user/user.selectors"; import TimeTicketTaskModalComponent from "./time-ticket-task-modal.component"; import { useApolloClient } from "@apollo/client"; import { QUERY_COMPLETED_TASKS } from "../../graphql/jobs.queries"; import "./time-ticket-task-modal.styles.scss"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; const mapStateToProps = createStructuredSelector({ timeTicketTasksModal: selectTimeTicketTasks, bodyshop: selectBodyshop, + currentUser: selectCurrentUser, + technician: selectTechnician, }); const mapDispatchToProps = (dispatch) => ({ toggleModalVisible: () => dispatch(toggleModalVisible("timeTicketTask")), @@ -27,6 +33,8 @@ export default connect( export function TimeTickeTaskModalContainer({ bodyshop, + currentUser, + technician, timeTicketTasksModal, toggleModalVisible, }) { @@ -80,6 +88,12 @@ export function TimeTickeTaskModalContainer({ jobid: values.jobid, task: values.task, calculateOnly: !handleFinish, + employee: technician + ? { + name: `${technician.first_name} ${technician.last_name}`.trim(), + employeeid: technician.id, + } + : { name: currentUser.displayName, email: currentUser.email }, }); if (response.status === 200 && handleFinish) { //Close the modal diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 2a0529320..b75ad978a 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -2208,3 +2208,28 @@ export const QUERY_COMPLETED_TASKS = gql` } } `; + +export const QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM = gql` + query QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM($teamIds: [uuid!]!) { + jobs( + where: { + inproduction: { _eq: true } + joblines: { assigned_team: { _in: $teamIds } } + } + ) { + id + v_make_desc + v_model_desc + v_color + v_vin + plate_no + plate_st + clm_no + ownr_fn + ownr_ln + ownr_co_nm + status + ro_number + } + } +`; diff --git a/client/src/pages/tech-assigned-prod-jobs/tech-assigned-prod-jobs.component.jsx b/client/src/pages/tech-assigned-prod-jobs/tech-assigned-prod-jobs.component.jsx new file mode 100644 index 000000000..b971bd368 --- /dev/null +++ b/client/src/pages/tech-assigned-prod-jobs/tech-assigned-prod-jobs.component.jsx @@ -0,0 +1,261 @@ +import { useQuery } from "@apollo/client"; +import React, { useState } from "react"; +import { Table } from "antd"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM } from "../../graphql/jobs.queries"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +import { alphaSort } from "../../utils/sorters"; +import { SyncOutlined } from "@ant-design/icons"; +import { Button, Card, Input, Space } from "antd"; +import queryString from "query-string"; +import { useTranslation } from "react-i18next"; +import { useHistory, useLocation } from "react-router-dom"; +import { onlyUnique } from "../../utils/arrayHelper"; +import AlertComponent from "../../components/alert/alert.component"; +import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component"; +import { setModalContext } from "../../redux/modals/modals.actions"; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + technician: selectTechnician, + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + setTimeTicketTaskContext: (context) => + dispatch(setModalContext({ context: context, modal: "timeTicketTask" })), +}); + +export function TechAssignedProdJobs({ + setTimeTicketTaskContext, + technician, + bodyshop, +}) { + const { loading, error, data, refetch } = useQuery( + QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM, + { + variables: { + teamIds: bodyshop.employee_teams + .filter((et) => + et.employee_team_members.find( + (etm) => etm.employeeid === technician.id + ) + ) + .map((et) => et.id), + }, + } + ); + + const searchParams = queryString.parse(useLocation().search); + const { selected } = searchParams; + + const [state, setState] = useState({ + sortedInfo: {}, + filteredInfo: { text: "" }, + }); + const { t } = useTranslation(); + const history = useHistory(); + const [searchText, setSearchText] = useState(""); + if (error) return ; + + const jobs = data + ? searchText === "" + ? data.jobs + : data.jobs.filter( + (j) => + (j.ro_number || "") + .toString() + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_fn || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_ln || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.ownr_co_nm || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) || + (j.plate_no || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.v_model_desc || "") + .toLowerCase() + .includes(searchText.toLowerCase()) || + (j.v_make_desc || "") + .toLowerCase() + .includes(searchText.toLowerCase()) + ) + : []; + const columns = [ + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, + + render: (text, record) => record.ro_number || t("general.labels.na"), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + ellipsis: true, + render: (text, record) => ( + + + + ), + }, + { + title: t("jobs.fields.status"), + dataIndex: "status", + key: "status", + sorter: (a, b) => alphaSort(a.status, b.status), + sortOrder: + state.sortedInfo.columnKey === "status" && state.sortedInfo.order, + filters: + (jobs && + jobs + .map((j) => j.status) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Status*", + value: [s], + }; + })) || + [], + onFilter: (value, record) => value.includes(record.status), + render: (text, record) => { + return record.status || t("general.labels.na"); + }, + }, + + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + render: (text, record) => ( + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ), + }, + { + title: t("vehicles.fields.plate_no"), + dataIndex: "plate_no", + key: "plate_no", + sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), + sortOrder: + state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order, + render: (text, record) => { + return record.plate_no ? record.plate_no : ""; + }, + }, + { + title: t("jobs.fields.clm_no"), + dataIndex: "clm_no", + key: "clm_no", + ellipsis: true, + sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), + sortOrder: + state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order, + render: (text, record) => { + return record.clm_no ? ( + {record.clm_no} + ) : ( + t("general.labels.unknown") + ); + }, + }, + { + title: t("jobs.labels.actions"), + dataIndex: "actions", + key: "actions", + render: (text, record) => ( + + ), + }, + ]; + const handleTableChange = (pagination, filters, sorter) => { + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); + }; + + const handleOnRowClick = (record) => { + if (record) { + if (record.id) { + history.push({ + search: queryString.stringify({ + ...searchParams, + selected: record.id, + }), + }); + } + } + }; + return ( + + + { + setSearchText(e.target.value); + }} + value={searchText} + enterButton + /> + + } + > + { + handleOnRowClick(record); + }, + selectedRowKeys: [selected], + type: "radio", + }} + onChange={handleTableChange} + // onRow={(record, rowIndex) => { + // return { + // onClick: (event) => { + // handleOnRowClick(record); + // }, + // }; + // }} + /> + + ); +} +export default connect( + mapStateToProps, + mapDispatchToProps +)(TechAssignedProdJobs); diff --git a/client/src/pages/tech-lookup/tech-lookup.container.jsx b/client/src/pages/tech-lookup/tech-lookup.container.jsx index 1297220b9..3368c1325 100644 --- a/client/src/pages/tech-lookup/tech-lookup.container.jsx +++ b/client/src/pages/tech-lookup/tech-lookup.container.jsx @@ -1,6 +1,5 @@ import React from "react"; import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component"; -import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component"; import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component"; export default function TechLookupContainer() { @@ -8,7 +7,6 @@ export default function TechLookupContainer() {
-
); diff --git a/client/src/pages/tech/tech.page.component.jsx b/client/src/pages/tech/tech.page.component.jsx index 25b33dc09..078dfca13 100644 --- a/client/src/pages/tech/tech.page.component.jsx +++ b/client/src/pages/tech/tech.page.component.jsx @@ -12,6 +12,8 @@ import TechSider from "../../components/tech-sider/tech-sider.component"; import { selectTechnician } from "../../redux/tech/tech.selectors"; import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component"; import "./tech.page.styles.scss"; +import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component"; + import UpdateAlert from "../../components/update-alert/update-alert.component"; const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container") @@ -40,6 +42,9 @@ const TimeTicketModalTask = lazy(() => "../../components/time-ticket-task-modal/time-ticket-task-modal.container" ) ); +const TechAssignedProdJobs = lazy(() => + import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component") +); const { Content } = Layout; const mapStateToProps = createStructuredSelector({ @@ -63,7 +68,7 @@ export function TechPage({ technician, match }) { {technician ? null : } - + + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 586f2d9fc..c42cecf0b 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -103,6 +103,7 @@ "admin_jobmarkforreexport": "ADMIN: Job marked for re-export.", "admin_jobuninvoice": "ADMIN: Job has been uninvoiced.", "admin_jobunvoid": "ADMIN: Job has been unvoided.", + "assignedlinehours": "Assigned job lines totaling {{hours}} units to {{team}}.", "billposted": "Bill with invoice number {{invoice_number}} posted.", "billupdated": "Bill with invoice number {{invoice_number}} updated.", "failedpayment": "Failed payment attempt.", @@ -346,7 +347,7 @@ "md_payment_types": "Payment Types", "md_referral_sources": "Referral Sources", "md_tasks_presets": { - "enable_tasks": "Enable Task Claiming", + "enable_tasks": "Enable Hour Flagging", "hourstype": "Hour Types", "memo": "Time Ticket Memo", "name": "Preset Name", @@ -1746,7 +1747,7 @@ "federal_tax_amt": "Federal Taxes", "gpdollars": "$ G.P.", "gppercent": "% G.P.", - "hrs_claimed": "Hours Claimed", + "hrs_claimed": "Hours Flagged", "hrs_total": "Hours Total", "importnote": "The job was initially imported.", "inproduction": "In Production", @@ -1997,7 +1998,8 @@ "shops": "My Shops" }, "tech": { - "claimtask": "Claim Task", + "assignedjobs": "Assigned Jobs", + "claimtask": "Flag Hours", "home": "Home", "jobclockin": "Job Clock In", "jobclockout": "Job Clock Out", @@ -2647,7 +2649,7 @@ "timetickets": "Time Tickets", "timetickets_employee": "Employee Time Tickets", "timetickets_summary": "Time Tickets Summary", - "unclaimed_hrs": "Unclaimed Hours", + "unclaimed_hrs": "Unflagged Hours", "void_ros": "Void ROs", "work_in_progress_labour": "Work in Progress - Labor", "work_in_progress_payables": "Work in Progress - Payables" @@ -2727,7 +2729,7 @@ }, "timetickets": { "actions": { - "claimtasks": "Claim Tasks", + "claimtasks": "Flag Hours", "clockin": "Clock In", "clockout": "Clock Out", "commit": "Commit Tickets ({{count}})", @@ -2768,7 +2770,7 @@ "alreadyclockedon": "You are already clocked in to the following job(s):", "ambreak": "AM Break", "amshift": "AM Shift", - "claimtaskpreview": "Claimed Tasks Preview", + "claimtaskpreview": "Flagged Hours Preview", "clockhours": "Shift Clock Hours Summary", "clockintojob": "Clock In to Job", "deleteconfirm": "Are you sure you want to delete this time ticket? This cannot be undone.", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 2869f12ec..511517aeb 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -103,6 +103,7 @@ "admin_jobmarkforreexport": "", "admin_jobuninvoice": "", "admin_jobunvoid": "", + "assignedlinehours": "", "billposted": "", "billupdated": "", "failedpayment": "", @@ -1997,6 +1998,7 @@ "shops": "Mis tiendas" }, "tech": { + "assignedjobs": "", "claimtask": "", "home": "", "jobclockin": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 1a2c14ac4..d134b4a6f 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -103,6 +103,7 @@ "admin_jobmarkforreexport": "", "admin_jobuninvoice": "", "admin_jobunvoid": "", + "assignedlinehours": "", "billposted": "", "billupdated": "", "failedpayment": "", @@ -1997,6 +1998,7 @@ "shops": "Mes boutiques" }, "tech": { + "assignedjobs": "", "claimtask": "", "home": "", "jobclockin": "", diff --git a/client/src/utils/AuditTrailMappings.js b/client/src/utils/AuditTrailMappings.js index 47fdc4535..e96c3f664 100644 --- a/client/src/utils/AuditTrailMappings.js +++ b/client/src/utils/AuditTrailMappings.js @@ -42,6 +42,8 @@ const AuditTrailMapping = { admin_jobmarkexported: () => i18n.t("audit_trail.messages.admin_jobmarkexported"), failedpayment: () => i18n.t("audit_trail.messages.failedpayment"), + assignedlinehours: (team, hours) => + i18n.t("audit_trail.messages.assignedlinehours", { team, hours }), }; export default AuditTrailMapping; diff --git a/server/payroll/claim-task.js b/server/payroll/claim-task.js index b88c52c34..e412c374d 100644 --- a/server/payroll/claim-task.js +++ b/server/payroll/claim-task.js @@ -6,14 +6,14 @@ const { CalculateExpectedHoursForJob, CalculateTicketsHoursForJob, } = require("./pay-all"); - +const moment = require("moment"); // Dinero.defaultCurrency = "USD"; // Dinero.globalLocale = "en-CA"; Dinero.globalRoundingMode = "HALF_EVEN"; exports.claimtask = async function (req, res) { const BearerToken = req.headers.authorization; - const { jobid, task, calculateOnly } = req.body; + const { jobid, task, calculateOnly, employee } = req.body; logger.log("job-payroll-pay-all", "DEBUG", req.user.email, jobid, null); const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { headers: { @@ -70,7 +70,7 @@ exports.claimtask = async function (req, res) { job.bodyshop.md_responsibility_centers.defaults.costs[ laborTypeKey ], - memo: `*Claimed Task* ${theTaskPreset.memo}`, + memo: `*Flagged Task* ${theTaskPreset.memo}`, }); } ); @@ -86,7 +86,16 @@ exports.claimtask = async function (req, res) { const updateResult = await client.request(queries.UPDATE_JOB, { jobId: job.id, job: { - completed_tasks: [...job.completed_tasks, task], + status: theTaskPreset.nextstatus, + completed_tasks: [ + ...job.completed_tasks, + { + name: task, + completedat: moment(), + completed_by: employee, + useremail: req.user.email, + }, + ], }, }); } diff --git a/server/payroll/pay-all.js b/server/payroll/pay-all.js index 99f9f9b98..81f8ff9d5 100644 --- a/server/payroll/pay-all.js +++ b/server/payroll/pay-all.js @@ -46,7 +46,9 @@ exports.payall = async function (req, res) { //Every iteration is what we would need to insert into the time ticket hash //so that it would match the employee hash exactly. const path = diffParser(diff); + if (diff.op === "add") { + console.log(Object.keys(diff.val)); if (typeof diff.val === "object" && Object.keys(diff.val).length > 1) { //Multiple values to add. Object.keys(diff.val).forEach((key) => { @@ -63,7 +65,7 @@ exports.payall = async function (req, res) { cost_center: job.bodyshop.md_responsibility_centers.defaults.costs[key], flat_rate: true, - memo: `*SYS-PAY* Add unclaimed hours. (${req.user.email})`, + memo: `*SYS-PAY* Add unflagged hours. (${req.user.email})`, }); }); } else { @@ -81,7 +83,7 @@ exports.payall = async function (req, res) { job.bodyshop.md_responsibility_centers.defaults.costs[ path.mod_lbr_ty ], - memo: `*SYS-PAY* Add unclaimed hours. (${req.user.email})`, + memo: `*SYS-PAY* Add unflagged hours. (${req.user.email})`, }); } } else if (diff.op === "update") { @@ -100,7 +102,7 @@ exports.payall = async function (req, res) { job.bodyshop.md_responsibility_centers.defaults.costs[ path.mod_lbr_ty ], - memo: `*SYS-PAY* Adjust claimed hours per assignment. (${req.user.email})`, + memo: `*SYS-PAY* Adjust flagged hours per assignment. (${req.user.email})`, }); } else { //Has to be a delete @@ -122,7 +124,7 @@ exports.payall = async function (req, res) { cost_center: job.bodyshop.md_responsibility_centers.defaults.costs[key], flat_rate: true, - memo: `*SYS-PAY* Remove claimed hours per assignment. (${req.user.email})`, + memo: `*SYS-PAY* Remove flagged hours per assignment. (${req.user.email})`, }); }); } else { @@ -140,7 +142,7 @@ exports.payall = async function (req, res) { path.mod_lbr_ty ], flat_rate: true, - memo: `*SYS-PAY* Remove claimed hours per assignment. (${req.user.email})`, + memo: `*SYS-PAY* Remove flagged hours per assignment. (${req.user.email})`, }); } } @@ -203,7 +205,14 @@ function diffParser(diff) { ) { hours = diff.val; } else if (diff.val !== null && diff.val !== undefined) { - hours = diff.val[Object.keys(diff.val)[0]]; + if (diff.path.length === 1) { + hours = + diff.val[Object.keys(diff.val)[0]][ + Object.keys(diff.val[Object.keys(diff.val)[0]]) + ]; + } else { + hours = diff.val[Object.keys(diff.val)[0]]; + } } else if ( typeof diff.oldVal === "number" && diff.oldVal !== null &&