Merged in release/2022-06-30 (pull request #534)
release/2022-06-30 Approved-by: Patrick Fic
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -2063,6 +2063,32 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>validation</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>atleastone</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -8138,6 +8164,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>default_quote</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>default_received</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -17779,6 +17826,27 @@
|
||||
<folder_node>
|
||||
<name>actions</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>converttolabor</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>new</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -18959,6 +19027,27 @@
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>adjustmenttobeadded</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>billref</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -18980,6 +19069,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>convertedtolabor</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>
|
||||
@@ -33967,6 +34077,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>is_quote</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>mark_as_received</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"@asseinfo/react-kanban": "^2.2.0",
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.3",
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@sentry/react": "^7.1.1",
|
||||
"@sentry/tracing": "^7.1.1",
|
||||
"@splitsoftware/splitio-react": "^1.4.1",
|
||||
|
||||
@@ -144,6 +144,14 @@ function BillEnterModalContainer({
|
||||
adjKeys.forEach((key) => {
|
||||
newAdjustments[key] =
|
||||
(newAdjustments[key] || 0) + adjustmentsToInsert[key];
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: values.jobid,
|
||||
operation: AuditTrailMapping.jobmodifylbradj({
|
||||
mod_lbr_ty: key,
|
||||
hours: adjustmentsToInsert[key].toFixed(1),
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
const jobUpdate = client.mutate({
|
||||
@@ -161,10 +169,6 @@ function BillEnterModalContainer({
|
||||
});
|
||||
return;
|
||||
}
|
||||
insertAuditTrail({
|
||||
jobid: values.jobid,
|
||||
operation: AuditTrailMapping.jobmodifylbradj(),
|
||||
});
|
||||
}
|
||||
|
||||
const markPolReceived =
|
||||
|
||||
@@ -552,7 +552,20 @@ export function BillEnterModalLinesComponent({
|
||||
});
|
||||
|
||||
return (
|
||||
<Form.List name="billlines">
|
||||
<Form.List
|
||||
name="billlines"
|
||||
rules={[
|
||||
{
|
||||
validator: async (_, billlines) => {
|
||||
if (!billlines || billlines.length < 1) {
|
||||
return Promise.reject(
|
||||
new Error(t("billlines.validation.atleastone"))
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -38,12 +38,6 @@ export function EmailDocumentsComponent({
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
console.log(
|
||||
selectedMedia &&
|
||||
selectedMedia
|
||||
.filter((s) => s.isSelected)
|
||||
.reduce((acc, val) => (acc = acc + val.size), 0)
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{loading && <LoadingSpinner />}
|
||||
|
||||
@@ -44,6 +44,7 @@ import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
||||
import JobLinesExpander from "./job-lines-expander.component";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -175,7 +176,7 @@ export function JobLinesComponent({
|
||||
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<>
|
||||
<JobLineConvertToLabor jobline={record} job={job}>
|
||||
<CurrencyFormatter>
|
||||
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||
? record.prt_dsmk_m
|
||||
@@ -188,7 +189,7 @@ export function JobLinesComponent({
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
</JobLineConvertToLabor>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -295,9 +296,9 @@ export function JobLinesComponent({
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<Space>
|
||||
{(record.manual_line || jobIsPrivate) && (
|
||||
<Space>
|
||||
<>
|
||||
<Button
|
||||
disabled={jobRO}
|
||||
onClick={() => {
|
||||
@@ -334,9 +335,9 @@ export function JobLinesComponent({
|
||||
>
|
||||
<DeleteFilled />
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
import { ClockCircleOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
notification,
|
||||
Popover,
|
||||
Select,
|
||||
Space,
|
||||
Tooltip,
|
||||
} from "antd";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
QUERY_JOB_LBR_ADJUSTMENTS,
|
||||
UPDATE_JOB,
|
||||
} from "../../graphql/jobs.queries";
|
||||
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobLineConvertToLabor);
|
||||
|
||||
export function JobLineConvertToLabor({
|
||||
children,
|
||||
jobline,
|
||||
job,
|
||||
insertAuditTrail,
|
||||
...otherBtnProps
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [visibility, setVisibility] = useState(false);
|
||||
const client = useApolloClient();
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
const { mod_lbr_ty } = values;
|
||||
logImEXEvent("job_convert_dollar_to_labor");
|
||||
|
||||
setLoading(true);
|
||||
|
||||
const existingAdjustments = await client.query({
|
||||
query: QUERY_JOB_LBR_ADJUSTMENTS,
|
||||
variables: {
|
||||
id: job.id,
|
||||
},
|
||||
});
|
||||
|
||||
const newAdjustments = _.cloneDeep(
|
||||
existingAdjustments.data.jobs_by_pk.lbr_adjustments
|
||||
);
|
||||
|
||||
newAdjustments[mod_lbr_ty] =
|
||||
(newAdjustments[mod_lbr_ty] || 0) +
|
||||
calculateAdjustment({ mod_lbr_ty, job, jobline });
|
||||
|
||||
const jobUpdate = client.mutate({
|
||||
mutation: UPDATE_JOB,
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: { lbr_adjustments: newAdjustments },
|
||||
},
|
||||
});
|
||||
|
||||
const lineUpdate = client.mutate({
|
||||
mutation: UPDATE_JOB_LINE,
|
||||
variables: {
|
||||
lineId: jobline.id,
|
||||
line: { convertedtolbr: true },
|
||||
},
|
||||
});
|
||||
|
||||
if (!!jobUpdate.errors) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
message: JSON.stringify(jobUpdate.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!!lineUpdate.errors) {
|
||||
notification["error"]({
|
||||
message: t("joblines.errors.saving", {
|
||||
message: JSON.stringify(lineUpdate.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.jobmodifylbradj({
|
||||
hours: calculateAdjustment({ mod_lbr_ty, job, jobline }).toFixed(1),
|
||||
mod_lbr_ty,
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
setVisibility(false);
|
||||
};
|
||||
|
||||
const overlay = (
|
||||
<Card>
|
||||
<Form form={form} layout="vertical" onFinish={handleFinish}>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.mod_lbr_ty")}
|
||||
name="mod_lbr_ty"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select allowClear optionFilterProp="children" showSearch>
|
||||
<Select.Option value="LAA">
|
||||
{t("joblines.fields.lbr_types.LAA")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAB">
|
||||
{t("joblines.fields.lbr_types.LAB")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAD">
|
||||
{t("joblines.fields.lbr_types.LAD")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAE">
|
||||
{t("joblines.fields.lbr_types.LAE")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAF">
|
||||
{t("joblines.fields.lbr_types.LAF")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAG">
|
||||
{t("joblines.fields.lbr_types.LAG")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAM">
|
||||
{t("joblines.fields.lbr_types.LAM")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAR">
|
||||
{t("joblines.fields.lbr_types.LAR")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAS">
|
||||
{t("joblines.fields.lbr_types.LAS")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LAU">
|
||||
{t("joblines.fields.lbr_types.LAU")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA1">
|
||||
{t("joblines.fields.lbr_types.LA1")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA2">
|
||||
{t("joblines.fields.lbr_types.LA2")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA3">
|
||||
{t("joblines.fields.lbr_types.LA3")}
|
||||
</Select.Option>
|
||||
<Select.Option value="LA4">
|
||||
{t("joblines.fields.lbr_types.LA4")}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item shouldUpdate>
|
||||
{() => {
|
||||
const { mod_lbr_ty } = form.getFieldsValue();
|
||||
return t("joblines.labels.adjustmenttobeadded", {
|
||||
adjustment: calculateAdjustment({
|
||||
mod_lbr_ty,
|
||||
job,
|
||||
jobline,
|
||||
}).toFixed(1),
|
||||
});
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={jobline.convertedtolbr}
|
||||
htmlType="submit"
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setVisibility(false)}>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const handleClick = (e) => {
|
||||
setLoading(true);
|
||||
|
||||
form.setFieldsValue({
|
||||
// date: new moment(),
|
||||
// bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
||||
// painthrs: Math.round(v.painthrs * 10) / 10,
|
||||
});
|
||||
setVisibility(true);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
{jobline.act_price !== 0 && (
|
||||
<Popover
|
||||
disabled={jobline.convertedtolbr}
|
||||
content={overlay}
|
||||
visible={visibility}
|
||||
placement="bottom"
|
||||
>
|
||||
<Tooltip title={t("joblines.actions.converttolabor")}>
|
||||
<Button
|
||||
type="link"
|
||||
disabled={jobline.convertedtolbr}
|
||||
loading={loading}
|
||||
onClick={handleClick}
|
||||
{...otherBtnProps}
|
||||
>
|
||||
<ClockCircleOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function calculateAdjustment({ mod_lbr_ty, job, jobline }) {
|
||||
if (!mod_lbr_ty) return 0;
|
||||
const rate = job[`rate_${mod_lbr_ty.toLowerCase()}`];
|
||||
|
||||
if (rate === 0 || rate === null || rate === undefined) return 0;
|
||||
const adj = jobline.act_price / job[`rate_${mod_lbr_ty.toLowerCase()}`];
|
||||
|
||||
return adj;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Form, Select } from "antd";
|
||||
import { Form, Select, Space, Tooltip } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -8,6 +8,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LaborTypeFormItem from "../form-items-formatted/labor-type-form-item.component";
|
||||
import PartTypeFormItem from "../form-items-formatted/part-type-form-item.component";
|
||||
import ReadOnlyFormItem from "../form-items-formatted/read-only-form-item.component";
|
||||
import { WarningOutlined } from "@ant-design/icons";
|
||||
import "./jobs-close-lines.styles.scss";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -62,14 +63,23 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
</Form.Item>
|
||||
</td>
|
||||
<td>
|
||||
<Form.Item
|
||||
span={2}
|
||||
// label={t("joblines.fields.act_price")}
|
||||
key={`${index}act_price`}
|
||||
name={[field.name, "act_price"]}
|
||||
>
|
||||
<ReadOnlyFormItem type="currency" />
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<Form.Item
|
||||
span={2}
|
||||
// label={t("joblines.fields.act_price")}
|
||||
key={`${index}act_price`}
|
||||
name={[field.name, "act_price"]}
|
||||
>
|
||||
<ReadOnlyFormItem type="currency" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
key={`${index}convertedtolbr`}
|
||||
name={[field.name, "convertedtolbr"]}
|
||||
>
|
||||
<HasBeenConvertedTolabor />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</td>
|
||||
<td>
|
||||
<Form.Item
|
||||
@@ -192,3 +202,14 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseLines);
|
||||
|
||||
const HasBeenConvertedTolabor = ({ value }) => {
|
||||
const { t } = useTranslation();
|
||||
console.log(value);
|
||||
if (!value) return null;
|
||||
return (
|
||||
<Tooltip title={t("joblines.labels.convertedtolabor")}>
|
||||
<WarningOutlined style={{ color: "tomato" }} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -217,21 +217,31 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
<JobsRelatedRos jobid={job.id} job={job} />
|
||||
</DataLabel>
|
||||
{job.vehicle && job.vehicle.notes && (
|
||||
<DataLabel label={t("vehicles.fields.notes")}>
|
||||
<span style={{ whiteSpace: "pre" }}>{job.vehicle.notes}</span>
|
||||
<DataLabel
|
||||
label={t("vehicles.fields.notes")}
|
||||
valueStyle={{ whiteSpace: "pre-wrap" }}
|
||||
>
|
||||
{job.vehicle.notes}
|
||||
</DataLabel>
|
||||
)}
|
||||
{job.vehicle && job.vehicle.v_paint_codes && (
|
||||
<DataLabel
|
||||
label={t("vehicles.fields.v_paint_codes", { number: "" })}
|
||||
>
|
||||
<span style={{ whiteSpace: "pre" }}>
|
||||
{Object.keys(job.vehicle.v_paint_codes)
|
||||
.filter(
|
||||
(key) =>
|
||||
job.vehicle.v_paint_codes[key] !== "" &&
|
||||
job.vehicle.v_paint_codes[key] !== null &&
|
||||
job.vehicle.v_paint_codes[key] !== undefined
|
||||
)
|
||||
.map((key, idx) => (
|
||||
<Tag key={idx}>{job.vehicle.v_paint_codes[key]}</Tag>
|
||||
))}
|
||||
</span>
|
||||
</DataLabel>
|
||||
)}
|
||||
{
|
||||
// job.vehicle && job.vehicle.v_paint_codes && (
|
||||
// <DataLabel label={t("vehicles.fields.v_paint_codes")}>
|
||||
// <span style={{ whiteSpace: "pre" }}>
|
||||
// {Object.keys(job.vehicle.v_paint_codes).map((key, idx) => (
|
||||
// <Tag key={idx}>{job.vehicle.v_paint_codes[key]}</Tag>
|
||||
// ))}
|
||||
// </span>
|
||||
// </DataLabel>
|
||||
// )
|
||||
}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
@@ -12,7 +12,24 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
|
||||
export default function LaborAllocationsAdjustmentEdit({
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(LaborAllocationsAdjustmentEdit);
|
||||
|
||||
export function LaborAllocationsAdjustmentEdit({
|
||||
insertAuditTrail,
|
||||
jobId,
|
||||
mod_lbr_ty,
|
||||
adjustments,
|
||||
@@ -51,6 +68,15 @@ export default function LaborAllocationsAdjustmentEdit({
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.save"),
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: AuditTrailMapping.jobmodifylbradj({
|
||||
mod_lbr_ty: values.mod_lbr_ty,
|
||||
hours:
|
||||
values.hours -
|
||||
((adjustments && adjustments[mod_lbr_ty]) || 0).toFixed(1),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
setVisible(false);
|
||||
|
||||
@@ -124,6 +124,15 @@ export function PartsOrderModalComponent({
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
)}
|
||||
{OEConnection.treatment === "on" && !isReturn && (
|
||||
<Form.Item
|
||||
name="is_quote"
|
||||
label={t("parts_orders.labels.is_quote")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
<Divider orientation="left">
|
||||
{t("parts_orders.labels.inthisorder")}
|
||||
@@ -291,17 +300,32 @@ export function PartsOrderModalComponent({
|
||||
<Input.TextArea rows={3} />
|
||||
</Form.Item>
|
||||
|
||||
<Radio.Group
|
||||
defaultValue={sendType}
|
||||
onChange={(e) => setSendType(e.target.value)}
|
||||
>
|
||||
<Radio value={"none"}>{t("general.labels.none")}</Radio>
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
|
||||
{OEConnection.treatment === "on" && !isReturn && (
|
||||
<Radio value={"oec"}>{t("parts_orders.labels.oec")}</Radio>
|
||||
)}
|
||||
</Radio.Group>
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{() => {
|
||||
const is_quote = form.getFieldValue("is_quote");
|
||||
if (is_quote) setSendType("oec");
|
||||
return (
|
||||
<Radio.Group
|
||||
defaultValue={sendType}
|
||||
value={sendType}
|
||||
onChange={(e) => setSendType(e.target.value)}
|
||||
>
|
||||
<Radio disabled={is_quote} value={"none"}>
|
||||
{t("general.labels.none")}
|
||||
</Radio>
|
||||
<Radio disabled={is_quote} value={"e"}>
|
||||
{t("parts_orders.labels.email")}
|
||||
</Radio>
|
||||
<Radio disabled={is_quote} value={"p"}>
|
||||
{t("parts_orders.labels.print")}
|
||||
</Radio>
|
||||
{OEConnection.treatment === "on" && !isReturn && (
|
||||
<Radio value={"oec"}>{t("parts_orders.labels.oec")}</Radio>
|
||||
)}
|
||||
</Radio.Group>
|
||||
);
|
||||
}}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,31 +93,53 @@ export function PartsOrderModalContainer({
|
||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleFinish = async ({ removefrompartsqueue, ...values }) => {
|
||||
const handleFinish = async ({
|
||||
removefrompartsqueue,
|
||||
is_quote,
|
||||
...values
|
||||
}) => {
|
||||
logImEXEvent("parts_order_insert");
|
||||
setSaving(true);
|
||||
const insertResult = await insertPartOrder({
|
||||
variables: {
|
||||
po: [
|
||||
{
|
||||
...values,
|
||||
order_date: moment().format("YYYY-MM-DD"),
|
||||
orderedby: currentUser.email,
|
||||
jobid: jobId,
|
||||
user_email: currentUser.email,
|
||||
return: isReturn,
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
},
|
||||
],
|
||||
},
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||
});
|
||||
if (!!insertResult.error) {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
description: JSON.stringify(insertResult.error),
|
||||
let insertResult;
|
||||
if (!is_quote) {
|
||||
insertResult = await insertPartOrder({
|
||||
variables: {
|
||||
po: [
|
||||
{
|
||||
...values,
|
||||
order_date: moment().format("YYYY-MM-DD"),
|
||||
orderedby: currentUser.email,
|
||||
jobid: jobId,
|
||||
user_email: currentUser.email,
|
||||
return: isReturn,
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
},
|
||||
],
|
||||
},
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||
});
|
||||
if (!!insertResult.error) {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
description: JSON.stringify(insertResult.error),
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification["success"]({
|
||||
message: values.isReturn
|
||||
? t("parts_orders.successes.return_created")
|
||||
: t("parts_orders.successes.created"),
|
||||
});
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: isReturn
|
||||
? AuditTrailMapping.jobspartsreturn(
|
||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||
)
|
||||
: AuditTrailMapping.jobspartsorder(
|
||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const jobLinesResult = await updateJobLines({
|
||||
@@ -127,6 +149,8 @@ export function PartsOrderModalContainer({
|
||||
.map((item) => item.job_line_id),
|
||||
status: isReturn
|
||||
? bodyshop.md_order_statuses.default_returned || "Returned*"
|
||||
: is_quote
|
||||
? bodyshop.md_order_statuses.default_quote || "Quote"
|
||||
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
},
|
||||
});
|
||||
@@ -142,17 +166,6 @@ export function PartsOrderModalContainer({
|
||||
});
|
||||
}
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: jobId,
|
||||
operation: isReturn
|
||||
? AuditTrailMapping.jobspartsreturn(
|
||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||
)
|
||||
: AuditTrailMapping.jobspartsorder(
|
||||
insertResult.data.insert_parts_orders.returning[0].order_number
|
||||
),
|
||||
});
|
||||
|
||||
if (!!jobLinesResult.errors) {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
@@ -160,12 +173,6 @@ export function PartsOrderModalContainer({
|
||||
});
|
||||
}
|
||||
|
||||
notification["success"]({
|
||||
message: values.isReturn
|
||||
? t("parts_orders.successes.return_created")
|
||||
: t("parts_orders.successes.created"),
|
||||
});
|
||||
|
||||
if (values.vendorid === bodyshop.inhousevendorid) {
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
|
||||
@@ -2,7 +2,6 @@ import { PauseCircleOutlined } from "@ant-design/icons";
|
||||
import { Space } from "antd";
|
||||
import i18n from "i18next";
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { TimeFormatter } from "../../utils/DateFormatter";
|
||||
@@ -530,6 +529,27 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
<JobPartsQueueCount parts={record.joblines_status} record={record} />
|
||||
),
|
||||
},
|
||||
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
|
||||
// {
|
||||
// title: i18n.t("vehicles.fields.v_paint_codes", { number: "" }),
|
||||
// dataIndex: "v_paint_codes",
|
||||
// key: "v_paint_codes",
|
||||
// render: (text, record) =>
|
||||
// record.vehicle?.v_paint_codes ? (
|
||||
// <span style={{ whiteSpace: "pre" }}>
|
||||
// {Object.keys(record.vehicle.v_paint_codes)
|
||||
// .filter(
|
||||
// (key) =>
|
||||
// record.vehicle.v_paint_codes[key] !== "" &&
|
||||
// record.vehicle.v_paint_codes[key] !== null &&
|
||||
// record.vehicle.v_paint_codes[key] !== undefined
|
||||
// )
|
||||
// .map((key, idx) => (
|
||||
// <Tag key={idx}>{record.vehicle.v_paint_codes[key]}</Tag>
|
||||
// ))}
|
||||
// </span>
|
||||
// ) : null,
|
||||
// },
|
||||
];
|
||||
};
|
||||
export default r;
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component";
|
||||
import ScheduleCalendarHeaderGraph from "./schedule-calendar-header-graph.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -66,6 +67,9 @@ export function ScheduleCalendarHeaderComponent({
|
||||
<td>
|
||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||
</td>
|
||||
<td>
|
||||
<OwnerNameDisplay ownerObject={j} />
|
||||
</td>
|
||||
<td>
|
||||
{`(${(
|
||||
j.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
@@ -99,6 +103,9 @@ export function ScheduleCalendarHeaderComponent({
|
||||
<td>
|
||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||
</td>
|
||||
<td>
|
||||
<OwnerNameDisplay ownerObject={j} />
|
||||
</td>
|
||||
<td>
|
||||
{`(${(
|
||||
j.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
|
||||
@@ -2,9 +2,28 @@ import { Form, Input } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
export default function ShopInfoOrderStatusComponent({ form }) {
|
||||
const { t } = useTranslation();
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ShopInfoOrderStatusComponent);
|
||||
|
||||
export function ShopInfoOrderStatusComponent({ bodyshop, form }) {
|
||||
const { t } = useTranslation();
|
||||
const { OEConnection } = useTreatments(
|
||||
["OEConnection"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
return (
|
||||
<LayoutFormRow header={t("bodyshop.labels.orderstatuses")}>
|
||||
<Form.Item
|
||||
@@ -56,6 +75,20 @@ export default function ShopInfoOrderStatusComponent({ form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
{OEConnection.treatment === "on" && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.statuses.default_quote")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_order_statuses", "default_quote"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -324,6 +324,9 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
ro_number
|
||||
scheduled_completion
|
||||
actual_completion
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
@@ -352,6 +355,9 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
id
|
||||
scheduled_in
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
|
||||
@@ -177,6 +177,7 @@ export const UPDATE_JOB_LINE = gql`
|
||||
location
|
||||
status
|
||||
removed
|
||||
convertedtolbr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,6 +702,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
prt_dsmk_p
|
||||
prt_dsmk_m
|
||||
ioucreated
|
||||
convertedtolbr
|
||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||
id
|
||||
quantity
|
||||
@@ -1905,6 +1906,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
||||
profitcenter_labor
|
||||
profitcenter_part
|
||||
prt_dsmk_p
|
||||
convertedtolbr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -31,6 +32,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
statusFilters,
|
||||
} = searchParams;
|
||||
const history = useHistory();
|
||||
const [filter, setFilter] = useLocalStorage("filter_parts_queue", null);
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -92,7 +94,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
// searchParams.page = pagination.current;
|
||||
searchParams.sortcolumn = sorter.columnKey;
|
||||
searchParams.sortorder = sorter.order;
|
||||
|
||||
setFilter(filters);
|
||||
history.push({ search: queryString.stringify(searchParams) });
|
||||
};
|
||||
|
||||
@@ -247,6 +249,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
key: "queued_for_parts",
|
||||
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
|
||||
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
|
||||
filteredValue: filter?.queued_for_parts || null,
|
||||
filters: [
|
||||
{
|
||||
text: "Queued",
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"jobimported": "Job imported.",
|
||||
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
||||
"jobioucreated": "IOU Created.",
|
||||
"jobmodifylbradj": "Labor adjustments modified.",
|
||||
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
|
||||
"jobnoteadded": "Note added to job.",
|
||||
"jobnotedeleted": "Note deleted from job.",
|
||||
"jobnoteupdated": "Note updated on job.",
|
||||
@@ -136,6 +136,9 @@
|
||||
"other": "-- Not On Estimate --",
|
||||
"reconciled": "Reconciled!",
|
||||
"unreconciled": "Unreconciled"
|
||||
},
|
||||
"validation": {
|
||||
"atleastone": "At least one bill line must be entered."
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
@@ -502,6 +505,7 @@
|
||||
"default_imported": "Default Imported Status",
|
||||
"default_invoiced": "Default Invoiced Status",
|
||||
"default_ordered": "Default Ordered Status",
|
||||
"default_quote": "Default Quote Status",
|
||||
"default_received": "Default Received Status",
|
||||
"default_returned": "Default Returned",
|
||||
"default_scheduled": "Default Scheduled Status",
|
||||
@@ -1110,6 +1114,7 @@
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"converttolabor": "Convert amount to Labor.",
|
||||
"new": "New Line"
|
||||
},
|
||||
"errors": {
|
||||
@@ -1175,7 +1180,9 @@
|
||||
"unq_seq": "Seq #"
|
||||
},
|
||||
"labels": {
|
||||
"adjustmenttobeadded": "Adjustment to be added: {{adjustment}}",
|
||||
"billref": "Latest Bill",
|
||||
"convertedtolabor": "This line has been converted to labor. Ensure you adjust the profit center for the amount accordingly.",
|
||||
"edit": "Edit Line",
|
||||
"ioucreated": "IOU",
|
||||
"new": "New Line",
|
||||
@@ -2013,6 +2020,7 @@
|
||||
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
|
||||
"email": "Send by Email",
|
||||
"inthisorder": "Parts in this Order",
|
||||
"is_quote": "Parts Quote?",
|
||||
"mark_as_received": "Mark as Received?",
|
||||
"newpartsorder": "New Parts Order",
|
||||
"notyetordered": "This part has not yet been ordered.",
|
||||
|
||||
@@ -136,6 +136,9 @@
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
},
|
||||
"validation": {
|
||||
"atleastone": ""
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
@@ -502,6 +505,7 @@
|
||||
"default_imported": "",
|
||||
"default_invoiced": "",
|
||||
"default_ordered": "",
|
||||
"default_quote": "",
|
||||
"default_received": "",
|
||||
"default_returned": "",
|
||||
"default_scheduled": "",
|
||||
@@ -1110,6 +1114,7 @@
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"converttolabor": "",
|
||||
"new": ""
|
||||
},
|
||||
"errors": {
|
||||
@@ -1175,7 +1180,9 @@
|
||||
"unq_seq": "Seq #"
|
||||
},
|
||||
"labels": {
|
||||
"adjustmenttobeadded": "",
|
||||
"billref": "",
|
||||
"convertedtolabor": "",
|
||||
"edit": "Línea de edición",
|
||||
"ioucreated": "",
|
||||
"new": "Nueva línea",
|
||||
@@ -2013,6 +2020,7 @@
|
||||
"confirmdelete": "",
|
||||
"email": "Enviar por correo electrónico",
|
||||
"inthisorder": "Partes en este pedido",
|
||||
"is_quote": "",
|
||||
"mark_as_received": "",
|
||||
"newpartsorder": "",
|
||||
"notyetordered": "",
|
||||
|
||||
@@ -136,6 +136,9 @@
|
||||
"other": "",
|
||||
"reconciled": "",
|
||||
"unreconciled": ""
|
||||
},
|
||||
"validation": {
|
||||
"atleastone": ""
|
||||
}
|
||||
},
|
||||
"bills": {
|
||||
@@ -502,6 +505,7 @@
|
||||
"default_imported": "",
|
||||
"default_invoiced": "",
|
||||
"default_ordered": "",
|
||||
"default_quote": "",
|
||||
"default_received": "",
|
||||
"default_returned": "",
|
||||
"default_scheduled": "",
|
||||
@@ -1110,6 +1114,7 @@
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"converttolabor": "",
|
||||
"new": ""
|
||||
},
|
||||
"errors": {
|
||||
@@ -1175,7 +1180,9 @@
|
||||
"unq_seq": "Seq #"
|
||||
},
|
||||
"labels": {
|
||||
"adjustmenttobeadded": "",
|
||||
"billref": "",
|
||||
"convertedtolabor": "",
|
||||
"edit": "Ligne d'édition",
|
||||
"ioucreated": "",
|
||||
"new": "Nouvelle ligne",
|
||||
@@ -2013,6 +2020,7 @@
|
||||
"confirmdelete": "",
|
||||
"email": "Envoyé par email",
|
||||
"inthisorder": "Pièces dans cette commande",
|
||||
"is_quote": "",
|
||||
"mark_as_received": "",
|
||||
"newpartsorder": "",
|
||||
"notyetordered": "",
|
||||
|
||||
@@ -18,7 +18,8 @@ const AuditTrailMapping = {
|
||||
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
||||
jobspartsreturn: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
||||
jobmodifylbradj: () => i18n.t("audit_trail.messages.jobmodifylbradj", {}),
|
||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
|
||||
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||
billposted: (invoice_number) =>
|
||||
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
||||
billupdated: (invoice_number) =>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { gql } from "@apollo/client";
|
||||
import { notification } from "antd";
|
||||
import axios from "axios";
|
||||
import jsreport from "jsreport-browser-client-dist";
|
||||
import jsreport from "@jsreport/browser-client";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
import { auth } from "../firebase/firebase.utils";
|
||||
@@ -9,7 +9,9 @@ import { setEmailOptions } from "../redux/email/email.actions";
|
||||
import { store } from "../redux/store";
|
||||
import client from "../utils/GraphQLClient";
|
||||
import { TemplateList } from "./TemplateConstants";
|
||||
|
||||
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
|
||||
|
||||
jsreport.serverUrl = server;
|
||||
|
||||
const Templates = TemplateList();
|
||||
@@ -21,6 +23,10 @@ export default async function RenderTemplate(
|
||||
renderAsExcel = false,
|
||||
renderAsText = false
|
||||
) {
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
|
||||
//Query assets that match the template name. Must be in format <<templateName>>.query
|
||||
let { contextData, useShopSpecificTemplate } = await fetchContextData(
|
||||
templateObject
|
||||
@@ -69,7 +75,7 @@ export default async function RenderTemplate(
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.renderAsync(reportRequest);
|
||||
const render = await jsreport.render(reportRequest);
|
||||
|
||||
if (!renderAsHtml) {
|
||||
render.download(
|
||||
@@ -104,16 +110,17 @@ export default async function RenderTemplate(
|
||||
},
|
||||
};
|
||||
|
||||
const pdfRender = await jsreport.renderAsync(pdfRequest);
|
||||
pdf = pdfRender.toDataURI();
|
||||
const pdfRender = await jsreport.render(pdfRequest);
|
||||
pdf = await pdfRender.toDataURI();
|
||||
}
|
||||
const html = await render.toString();
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve({
|
||||
pdf,
|
||||
filename:
|
||||
Templates[templateObject.name] &&
|
||||
Templates[templateObject.name].title,
|
||||
html: render.toString(),
|
||||
html,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -152,6 +159,9 @@ export async function RenderTemplates(
|
||||
// (template) => template.name === item.templateObject.name
|
||||
// );
|
||||
// });
|
||||
if (window.jsr3) {
|
||||
jsreport.serverUrl = "https://reports3.test.imex.online/";
|
||||
}
|
||||
|
||||
unsortedTemplatesAndData.sort(function (a, b) {
|
||||
return (
|
||||
@@ -242,13 +252,11 @@ export async function RenderTemplates(
|
||||
};
|
||||
|
||||
try {
|
||||
const render = await jsreport.renderAsync(reportRequest);
|
||||
const render = await jsreport.render(reportRequest);
|
||||
if (!renderAsHtml) {
|
||||
render.download("Speed Print");
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(render.toString());
|
||||
});
|
||||
return render.toString();
|
||||
}
|
||||
} catch (error) {
|
||||
notification["error"]({ message: JSON.stringify(error) });
|
||||
|
||||
@@ -23,9 +23,9 @@ export default async function FcmHandler({ client, payload }) {
|
||||
updated_at(oldupdated0) {
|
||||
return new Date();
|
||||
},
|
||||
messages_aggregate(cached) {
|
||||
return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
},
|
||||
// messages_aggregate(cached) {
|
||||
// return { aggregate: { count: cached.aggregate.count + 1 } };
|
||||
// },
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
40
client/src/utils/useLocalStorage.js
Normal file
40
client/src/utils/useLocalStorage.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export default function useLocalStorage(key, initialValue) {
|
||||
// State to store our value
|
||||
// Pass initial state function to useState so logic is only executed once
|
||||
const [storedValue, setStoredValue] = useState(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return initialValue;
|
||||
}
|
||||
try {
|
||||
// Get from local storage by key
|
||||
const item = window.localStorage.getItem(key);
|
||||
// Parse stored json or if none return initialValue
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
// If error also return initialValue
|
||||
console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
// Return a wrapped version of useState's setter function that ...
|
||||
// ... persists the new value to localStorage.
|
||||
const setValue = (value) => {
|
||||
try {
|
||||
// Allow value to be a function so we have same API as useState
|
||||
const valueToStore =
|
||||
value instanceof Function ? value(storedValue) : value;
|
||||
// Save state
|
||||
setStoredValue(valueToStore);
|
||||
// Save to local storage
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
}
|
||||
} catch (error) {
|
||||
// A more advanced implementation would handle the error case
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
@@ -1918,6 +1918,11 @@
|
||||
"@types/yargs" "^16.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@jsreport/browser-client@^3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jsreport/browser-client/-/browser-client-3.1.0.tgz#a84011087ca8a29a6dc6a852fa05ffaf1983a679"
|
||||
integrity sha512-ZElwn2KRIzkUzAyD5UKGxULZUhokWuPOlMzrmiur4WirqH3yoiHlOJEdnRGkjjE/fhZzCR8gBFZ/TuOW/fsOIw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
|
||||
@@ -2384,6 +2384,7 @@
|
||||
- bett_tax
|
||||
- bett_type
|
||||
- cert_part
|
||||
- convertedtolbr
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
@@ -2447,6 +2448,7 @@
|
||||
- bett_tax
|
||||
- bett_type
|
||||
- cert_part
|
||||
- convertedtolbr
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
@@ -2521,6 +2523,7 @@
|
||||
- bett_tax
|
||||
- bett_type
|
||||
- cert_part
|
||||
- convertedtolbr
|
||||
- created_at
|
||||
- db_hrs
|
||||
- db_price
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."joblines" add column "convertedtolbr" boolean
|
||||
-- not null default 'false';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "convertedtolbr" boolean
|
||||
not null default 'false';
|
||||
@@ -257,12 +257,14 @@ exports.validateFirebaseIdToken = async (req, res, next) => {
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.log("api-unauthorized-call", "WARN", null, null, {
|
||||
req,
|
||||
path: req.path,
|
||||
body: req.body,
|
||||
|
||||
type: "unauthroized",
|
||||
error,
|
||||
...error,
|
||||
});
|
||||
|
||||
res.status(403).send("Unauthorized");
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,6 +32,8 @@ exports.mixdataUpload = async (req, res) => {
|
||||
explicitArray: false,
|
||||
});
|
||||
|
||||
logger.log("job-mixdata-parse", "DEBUG", req.user.email, inboundRequest);
|
||||
|
||||
const ScaleType = DetermineScaleType(inboundRequest);
|
||||
const RoNumbersFromInboundRequest = GetListOfRos(
|
||||
inboundRequest,
|
||||
@@ -76,6 +78,7 @@ exports.mixdataUpload = async (req, res) => {
|
||||
res.status(500).JSON(error);
|
||||
logger.log("job-mixdata-upload-error", "ERROR", null, null, {
|
||||
error: error.message,
|
||||
...error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user