Compare commits
17 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d094da35 | ||
|
|
0669282432 | ||
|
|
bf4dc7e158 | ||
|
|
5de2036fdb | ||
|
|
1629663e15 | ||
|
|
e25f2db2b1 | ||
|
|
cbf5d268ea | ||
|
|
a92a95a9fa | ||
|
|
0be7bf2c8e | ||
|
|
56b810dd40 | ||
|
|
000ded6649 | ||
|
|
72181e1ff7 | ||
|
|
d73b1d2220 | ||
|
|
8645b434c8 | ||
|
|
38a13bd082 | ||
|
|
3bc5f5d626 | ||
|
|
86a2351316 |
@@ -156,3 +156,11 @@
|
||||
td.ant-table-column-sort {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.rowWithColor > td {
|
||||
background-color: var(--bgColor) !important;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
const span = {
|
||||
sm: { span: 24 },
|
||||
md: { span: 12 },
|
||||
lg: { span: 8 },
|
||||
lg: { span: 24 },
|
||||
xl: { span: 12 },
|
||||
xxl: { span: 8 },
|
||||
};
|
||||
|
||||
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
@@ -137,12 +137,6 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...span}>
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
@@ -163,6 +157,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
@@ -1,16 +1,119 @@
|
||||
import { Table } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
|
||||
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
||||
import CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
export default function JobDetailCardsPartsComponent({ loading, data }) {
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
|
||||
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
jobRO: selectJobReadOnly,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobDetailCardsPartsComponent);
|
||||
|
||||
export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
|
||||
const { t } = useTranslation();
|
||||
const { joblines_status } = data;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("joblines.fields.line_desc"),
|
||||
dataIndex: "line_desc",
|
||||
fixed: "left",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||
},
|
||||
}),
|
||||
width: "30%",
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.part_type"),
|
||||
dataIndex: "part_type",
|
||||
key: "part_type",
|
||||
width: "15%",
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
t(`joblines.fields.part_types.${a.part_type}`),
|
||||
t(`joblines.fields.part_types.${b.part_type}`)
|
||||
),
|
||||
render: (text, record) =>
|
||||
record.part_type
|
||||
? t(`joblines.fields.part_types.${record.part_type}`)
|
||||
: null,
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.part_qty"),
|
||||
dataIndex: "part_qty",
|
||||
key: "part_qty",
|
||||
width: "10%",
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.notes"),
|
||||
dataIndex: "notes",
|
||||
key: "notes",
|
||||
render: (text, record) => (
|
||||
<JobLineNotePopup disabled={jobRO} jobline={record} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.location"),
|
||||
dataIndex: "location",
|
||||
key: "location",
|
||||
sorter: (a, b) => alphaSort(a.location, b.location),
|
||||
render: (text, record) => (
|
||||
<JobLineLocationPopup jobline={record} disabled={jobRO} />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.status"),
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
sorter: (a, b) => alphaSort(a.status, b.status),
|
||||
filters:
|
||||
(data &&
|
||||
data.joblines
|
||||
?.map((l) => l.status)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s || "No Status*",
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.status),
|
||||
render: (text, record) => (
|
||||
<JobLineStatusPopup jobline={record} disabled={jobRO} />
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div>
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||
<PartsStatusPie joblines_status={joblines_status} />
|
||||
<Table
|
||||
key="id"
|
||||
columns={columns}
|
||||
dataSource={data ? data.joblines : []}
|
||||
/>
|
||||
</CardTemplate>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -461,54 +462,56 @@ export function JobsDetailHeaderActions({
|
||||
)}
|
||||
<JobsDetailHeaderActionsAddevent jobid={job.id} />
|
||||
{!jobRO && job.converted && (
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
title={t("jobs.labels.voidjob")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onConfirm={async () => {
|
||||
//delete the job.
|
||||
const result = await voidJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
status: bodyshop.md_ro_statuses.default_void,
|
||||
voided: true,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
inproduction: false,
|
||||
},
|
||||
note: [
|
||||
{
|
||||
jobid: job.id,
|
||||
created_by: currentUser.email,
|
||||
audit: true,
|
||||
text: t("jobs.labels.voidnote"),
|
||||
<RbacWrapper action="jobs:void" noauth>
|
||||
<Menu.Item>
|
||||
<Popconfirm
|
||||
title={t("jobs.labels.voidjob")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onConfirm={async () => {
|
||||
//delete the job.
|
||||
const result = await voidJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
status: bodyshop.md_ro_statuses.default_void,
|
||||
voided: true,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
inproduction: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
note: [
|
||||
{
|
||||
jobid: job.id,
|
||||
created_by: currentUser.email,
|
||||
audit: true,
|
||||
text: t("jobs.labels.voidnote"),
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.voided"),
|
||||
});
|
||||
//go back to jobs list.
|
||||
history.push(`/manage/`);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.voiding", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
>
|
||||
{t("menus.jobsactions.void")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.voided"),
|
||||
});
|
||||
//go back to jobs list.
|
||||
history.push(`/manage/`);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.voiding", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
>
|
||||
{t("menus.jobsactions.void")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
</RbacWrapper>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -112,7 +112,9 @@ export function JobsList({ bodyshop }) {
|
||||
title: t("jobs.fields.ro_number"),
|
||||
dataIndex: "ro_number",
|
||||
key: "ro_number",
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sorter: (a, b) =>
|
||||
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
|
||||
parseInt((b.ro_number || "0").replace(/\D/g, "")),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
|
||||
|
||||
@@ -23,17 +23,34 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
|
||||
const { id: jobId, job } = printCenterModal.context;
|
||||
const tempList = TemplateList("job", {});
|
||||
const { t } = useTranslation();
|
||||
const JobsReportsList = Object.keys(tempList)
|
||||
.map((key) => {
|
||||
return tempList[key];
|
||||
})
|
||||
.filter(
|
||||
(temp) =>
|
||||
!temp.regions ||
|
||||
(temp.regions && temp.regions[bodyshop.region_config]) ||
|
||||
(temp.regions &&
|
||||
bodyshop.region_config.includes(Object.keys(temp.regions)) === true)
|
||||
);
|
||||
|
||||
const JobsReportsList =
|
||||
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
|
||||
? Object.keys(tempList)
|
||||
.map((key) => {
|
||||
return tempList[key];
|
||||
})
|
||||
.filter(
|
||||
(temp) =>
|
||||
(!temp.regions ||
|
||||
(temp.regions && temp.regions[bodyshop.region_config]) ||
|
||||
(temp.regions &&
|
||||
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
|
||||
true)) &&
|
||||
(!temp.dms || temp.dms === false)
|
||||
)
|
||||
: Object.keys(tempList)
|
||||
.map((key) => {
|
||||
return tempList[key];
|
||||
})
|
||||
.filter(
|
||||
(temp) =>
|
||||
!temp.regions ||
|
||||
(temp.regions && temp.regions[bodyshop.region_config]) ||
|
||||
(temp.regions &&
|
||||
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
|
||||
true)
|
||||
);
|
||||
|
||||
const filteredJobsReportsList =
|
||||
search !== ""
|
||||
|
||||
@@ -88,7 +88,7 @@ export function ProductionListDetail({
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"33%"}
|
||||
width={"50%"}
|
||||
onClose={handleClose}
|
||||
visible={selected}
|
||||
>
|
||||
|
||||
@@ -246,11 +246,21 @@ export function ProductionListTable({
|
||||
(x) => x.status === record.status
|
||||
);
|
||||
|
||||
if (!color) return null;
|
||||
if (!color) {
|
||||
if (index % 2 === 0)
|
||||
return {
|
||||
style: {
|
||||
backgroundColor: `rgb(236, 236, 236)`,
|
||||
},
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
className: "rowWithColor",
|
||||
style: {
|
||||
backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
|
||||
"--bgColor": `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -26,6 +26,8 @@ const ret = {
|
||||
"jobs:partsqueue": 4,
|
||||
"jobs:checklist-view": 2,
|
||||
"jobs:list-ready": 1,
|
||||
"jobs:void": 5,
|
||||
|
||||
"bills:enter": 2,
|
||||
"bills:view": 2,
|
||||
"bills:list": 2,
|
||||
|
||||
@@ -12,7 +12,8 @@ import "./schedule-calendar.styles.scss";
|
||||
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
|
||||
import { selectProblemJobs } from "../../redux/application/application.selectors";
|
||||
import { Alert, Collapse } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -66,10 +67,21 @@ export function ScheduleCalendarWrapperComponent({
|
||||
<Alert
|
||||
key={problem.id}
|
||||
type="error"
|
||||
message={t("appointments.labels.dataconsistency", {
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
})}
|
||||
message={
|
||||
<Trans
|
||||
i18nKey="appointments.labels.dataconsistency"
|
||||
components={[
|
||||
<Link
|
||||
to={`/manage/jobs/${problem.id}`}
|
||||
target="_blank"
|
||||
/>,
|
||||
]}
|
||||
values={{
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
@@ -79,10 +91,18 @@ export function ScheduleCalendarWrapperComponent({
|
||||
<Alert
|
||||
key={problem.id}
|
||||
type="error"
|
||||
message={t("appointments.labels.dataconsistency", {
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
})}
|
||||
message={
|
||||
<Trans
|
||||
i18nKey="appointments.labels.dataconsistency"
|
||||
components={[
|
||||
<Link to={`/manage/jobs/${problem.id}`} target="_blank" />,
|
||||
]}
|
||||
values={{
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Form, InputNumber } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -316,6 +316,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.jobs.void")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["md_rbac", "jobs:void"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||
rules={[
|
||||
|
||||
@@ -33,6 +33,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
|
||||
<JobSearchSelect
|
||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -868,10 +868,40 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
||||
count
|
||||
status
|
||||
}
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
|
||||
joblines(
|
||||
where: {
|
||||
removed: { _eq: false }
|
||||
part_type: { _is_null: false, _nin: ["PAE", "PAS", "PASL"] }
|
||||
}
|
||||
order_by: { line_no: asc }
|
||||
) {
|
||||
id
|
||||
alt_partm
|
||||
line_no
|
||||
unq_seq
|
||||
line_ind
|
||||
line_desc
|
||||
line_ref
|
||||
part_type
|
||||
part_qty
|
||||
mod_lbr_ty
|
||||
db_hrs
|
||||
mod_lb_hrs
|
||||
lbr_op
|
||||
lbr_amt
|
||||
op_code_desc
|
||||
status
|
||||
notes
|
||||
location
|
||||
tax_part
|
||||
db_ref
|
||||
manual_line
|
||||
prt_dsmk_p
|
||||
prt_dsmk_m
|
||||
ioucreated
|
||||
convertedtolbr
|
||||
critical
|
||||
}
|
||||
owner {
|
||||
id
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"blocked": "Blocked",
|
||||
"cancelledappointment": "Canceled appointment for: ",
|
||||
"completingjobs": "Completing Jobs",
|
||||
"dataconsistency": "{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.",
|
||||
"dataconsistency": "<0>{{ro_number}}</0> has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.",
|
||||
"expectedjobs": "Expected Jobs in Production: ",
|
||||
"expectedprodhrs": "Expected Production Hours:",
|
||||
"history": "History",
|
||||
@@ -405,7 +405,8 @@
|
||||
"list-active": "Jobs -> List Active",
|
||||
"list-all": "Jobs -> List All",
|
||||
"list-ready": "Jobs -> List Ready",
|
||||
"partsqueue": "Jobs -> Parts Queue"
|
||||
"partsqueue": "Jobs -> Parts Queue",
|
||||
"void": "Jobs -> Void"
|
||||
},
|
||||
"owners": {
|
||||
"detail": "Owners -> Detail",
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"list-active": "",
|
||||
"list-all": "",
|
||||
"list-ready": "",
|
||||
"partsqueue": ""
|
||||
"partsqueue": "",
|
||||
"void": ""
|
||||
},
|
||||
"owners": {
|
||||
"detail": "",
|
||||
|
||||
@@ -405,7 +405,8 @@
|
||||
"list-active": "",
|
||||
"list-all": "",
|
||||
"list-ready": "",
|
||||
"partsqueue": ""
|
||||
"partsqueue": "",
|
||||
"void": ""
|
||||
},
|
||||
"owners": {
|
||||
"detail": "",
|
||||
|
||||
@@ -512,6 +512,7 @@ export const TemplateList = (type, context) => {
|
||||
key: "dms_posting_sheet",
|
||||
disabled: false,
|
||||
group: "financial",
|
||||
dms: true,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
||||
@@ -729,9 +729,15 @@ async function InsertDmsVehicle(socket) {
|
||||
deliveryDate: moment()
|
||||
// .tz(socket.JobData.bodyshop.timezone)
|
||||
.format("YYYYMMDD"),
|
||||
licensePlateNo: String(socket.JobData.plate_no)
|
||||
.replace(/([^\w]|_)/g, "")
|
||||
.toUpperCase(),
|
||||
licensePlateNo:
|
||||
socket.JobData.plate_no === null
|
||||
? null
|
||||
: String(socket.JobData.plate_no).replace(/([^\w]|_)/g, "")
|
||||
.length === 0
|
||||
? null
|
||||
: String(socket.JobData.plate_no)
|
||||
.replace(/([^\w]|_)/g, "")
|
||||
.toUpperCase(),
|
||||
make: socket.txEnvelope.dms_make,
|
||||
modelAbrev: socket.txEnvelope.dms_model,
|
||||
modelYear: socket.JobData.v_model_yr,
|
||||
|
||||
Reference in New Issue
Block a user