@@ -2349,6 +2349,27 @@
|
||||
<folder_node>
|
||||
<name>actions</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deductallhours</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>edit</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -31108,6 +31129,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>missingprofileinfo</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>multipayers</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -98,6 +98,7 @@ function BillEnterModalContainer({
|
||||
} = values;
|
||||
|
||||
let adjustmentsToInsert = {};
|
||||
let payrollAdjustmentsToInsert = [];
|
||||
|
||||
const r1 = await insertBill({
|
||||
variables: {
|
||||
@@ -121,6 +122,24 @@ function BillEnterModalContainer({
|
||||
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
|
||||
restI.actual_price / lbr_adjustment.rate;
|
||||
}
|
||||
|
||||
//If deduct from labor has lines,
|
||||
//
|
||||
if (
|
||||
deductedfromlbr &&
|
||||
true //payroll is on
|
||||
) {
|
||||
payrollAdjustmentsToInsert.push({
|
||||
id: i.joblineid,
|
||||
convertedtolbr: true,
|
||||
convertedtolbr_data: {
|
||||
mod_lb_hrs:
|
||||
(restI.actual_price / lbr_adjustment.rate) * -1,
|
||||
mod_lbr_ty: lbr_adjustment.mod_lbr_ty,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...restI,
|
||||
deductedfromlbr: deductedfromlbr,
|
||||
@@ -146,6 +165,20 @@ function BillEnterModalContainer({
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
payrollAdjustmentsToInsert.map((li) => {
|
||||
return updateJobLines({
|
||||
variables: {
|
||||
lineId: li.id,
|
||||
line: {
|
||||
convertedtolbr: li.convertedtolbr,
|
||||
convertedtolbr_data: li.convertedtolbr_data,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const adjKeys = Object.keys(adjustmentsToInsert);
|
||||
if (adjKeys.length > 0) {
|
||||
//Query the adjustments, merge, and update them.
|
||||
|
||||
@@ -40,7 +40,7 @@ export function BillEnterModalLinesComponent({
|
||||
billid,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
||||
const { setFieldsValue, getFieldsValue, getFieldValue, setFieldValue } = form;
|
||||
const { Simple_Inventory } = useTreatments(
|
||||
["Simple_Inventory"],
|
||||
{},
|
||||
@@ -376,9 +376,43 @@ export function BillEnterModalLinesComponent({
|
||||
"rate",
|
||||
]);
|
||||
|
||||
const billline = getFieldValue(["billlines", record.name]);
|
||||
|
||||
const jobline = lineData.find(
|
||||
(line) => line.id === billline?.joblineid
|
||||
);
|
||||
|
||||
const employeeTeamName = bodyshop.employee_teams.find(
|
||||
(team) => team.id === jobline?.assigned_team
|
||||
);
|
||||
|
||||
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
|
||||
return (
|
||||
<div>
|
||||
<Space>
|
||||
{t("joblines.fields.assigned_team", {
|
||||
name: employeeTeamName?.name,
|
||||
})}
|
||||
{`${jobline.mod_lb_hrs} units`}
|
||||
<Button
|
||||
onClick={() => {
|
||||
const applicableRate =
|
||||
billline.actual_price / jobline.mod_lb_hrs;
|
||||
|
||||
setFieldValue(
|
||||
[
|
||||
"billlines",
|
||||
record.name,
|
||||
"lbr_adjustment",
|
||||
"rate",
|
||||
],
|
||||
applicableRate
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("bills.actions.deductallhours")}
|
||||
</Button>
|
||||
</Space>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.mod_lbr_ty")}
|
||||
key={`${index}modlbrty`}
|
||||
@@ -448,9 +482,11 @@ export function BillEnterModalLinesComponent({
|
||||
>
|
||||
<InputNumber precision={2} min={0.01} />
|
||||
</Form.Item>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
<Space>
|
||||
{price &&
|
||||
adjustmentRate &&
|
||||
`${(price / adjustmentRate).toFixed(1)} hrs`}
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
return <></>;
|
||||
|
||||
@@ -63,6 +63,12 @@ const BillLineSearchSelect = (
|
||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
||||
</span>
|
||||
{item.act_price === 0 && item.mod_lb_hrs > 0 && (
|
||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||
{`${item.mod_lb_hrs} units`}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||
{item.act_price
|
||||
? `$${item.act_price && item.act_price.toFixed(2)}`
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Alert } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function JobProfileDataWarning({ job }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let missingProfileInfo =
|
||||
Object.keys(job.cieca_pft).length === 0 ||
|
||||
Object.keys(job.cieca_pfl).length === 0 ||
|
||||
Object.keys(job.materials).length === 0;
|
||||
|
||||
if (missingProfileInfo)
|
||||
return (
|
||||
<Alert type="error" message={t("jobs.labels.missingprofileinfo")}></Alert>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -239,7 +239,7 @@ export function PayrollLaborAllocationsTable({
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
if (response.data.success) {
|
||||
if (response.data.success !== false) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
message: t("timetickets.successes.payall"),
|
||||
|
||||
@@ -274,6 +274,7 @@ export const GET_JOB_LINES_TO_ENTER_BILL = gql`
|
||||
lbr_amt
|
||||
op_code_desc
|
||||
alt_partno
|
||||
assigned_team
|
||||
}
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
|
||||
@@ -54,6 +54,7 @@ import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gal
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||
import _ from "lodash";
|
||||
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -288,6 +289,7 @@ export function JobsDetailPage({
|
||||
/>
|
||||
<JobsDetailHeader job={job} />
|
||||
<Divider type="horizontal" />
|
||||
<JobProfileDataWarning job={job} />
|
||||
<FormFieldsChanged form={form} />
|
||||
<Tabs
|
||||
defaultActiveKey={search.tab}
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"deductallhours": "Deduct all",
|
||||
"edit": "Edit",
|
||||
"receive": "Receive Part",
|
||||
"return": "Return Items"
|
||||
@@ -1242,7 +1243,7 @@
|
||||
"fields": {
|
||||
"act_price": "Retail Price",
|
||||
"ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)",
|
||||
"assigned_team": "Team",
|
||||
"assigned_team": "Team {{name}}",
|
||||
"db_price": "List Price",
|
||||
"lbr_types": {
|
||||
"LA1": "LA1",
|
||||
@@ -1753,7 +1754,7 @@
|
||||
"closejob": "Close Job {{ro_number}}",
|
||||
"closingperiod": "This Invoice Date is outside of the Closing Period.",
|
||||
"contracts": "CC Contracts",
|
||||
"convertedtolabor": "Lines Converted to Labor",
|
||||
"convertedtolabor": "Labor Line Adjustments",
|
||||
"cost": "Cost",
|
||||
"cost_Additional": "Cost - Additional",
|
||||
"cost_labor": "Cost - Labor",
|
||||
@@ -1824,6 +1825,7 @@
|
||||
"materials": {
|
||||
"mapa": ""
|
||||
},
|
||||
"missingprofileinfo": "This job has missing tax profile info. To ensure correct totals calculations, re-import the job.",
|
||||
"multipayers": "Additional Payers",
|
||||
"net_repairs": "Net Repairs",
|
||||
"notes": "Notes",
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"deductallhours": "",
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
@@ -1824,6 +1825,7 @@
|
||||
"materials": {
|
||||
"mapa": ""
|
||||
},
|
||||
"missingprofileinfo": "",
|
||||
"multipayers": "",
|
||||
"net_repairs": "",
|
||||
"notes": "Notas",
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
},
|
||||
"bills": {
|
||||
"actions": {
|
||||
"deductallhours": "",
|
||||
"edit": "",
|
||||
"receive": "",
|
||||
"return": ""
|
||||
@@ -1824,6 +1825,7 @@
|
||||
"materials": {
|
||||
"mapa": ""
|
||||
},
|
||||
"missingprofileinfo": "",
|
||||
"multipayers": "",
|
||||
"net_repairs": "",
|
||||
"notes": "Remarques",
|
||||
|
||||
Reference in New Issue
Block a user