@@ -5650,6 +5650,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>md_tasks_presets</name>
|
<name>md_tasks_presets</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>enable_tasks</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>
|
<concept_node>
|
||||||
<name>hourstype</name>
|
<name>hourstype</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -5734,6 +5755,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>use_approvals</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
@@ -45372,6 +45414,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>commit</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>commitone</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>
|
<concept_node>
|
||||||
<name>enter</name>
|
<name>enter</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -45414,6 +45498,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>uncommit</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -46337,6 +46442,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>committed</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>
|
<concept_node>
|
||||||
<name>created</name>
|
<name>created</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -48464,6 +48590,53 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>labels</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>approval_queue_in_use</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>calculate</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
|
|||||||
@@ -156,3 +156,11 @@
|
|||||||
td.ant-table-column-sort {
|
td.ant-table-column-sort {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rowWithColor > td {
|
||||||
|
background-color: var(--bgColor) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
|
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
|
Dropdown,
|
||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
|
Menu,
|
||||||
Select,
|
Select,
|
||||||
|
Space,
|
||||||
Tabs,
|
Tabs,
|
||||||
Upload,
|
Upload,
|
||||||
Space,
|
|
||||||
Menu,
|
|
||||||
Dropdown,
|
|
||||||
Button,
|
|
||||||
} from "antd";
|
} from "antd";
|
||||||
|
import _ from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import EmailDocumentsComponent from "../email-documents/email-documents.component";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectEmailConfig } from "../../redux/email/email.selectors";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
|
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
|
||||||
import { selectEmailConfig } from "../../redux/email/email.selectors";
|
import EmailDocumentsComponent from "../email-documents/email-documents.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -54,6 +54,15 @@ export function EmailOverlayComponent({
|
|||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const handle_CC_Click = ({ item, key, keyPath }) => {
|
||||||
|
const email = item.props.value;
|
||||||
|
form.setFieldsValue({
|
||||||
|
cc: _.uniq([
|
||||||
|
...(form.getFieldValue("cc") || ""),
|
||||||
|
...(typeof email === "string" ? [email] : email),
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<div>
|
<div>
|
||||||
@@ -74,6 +83,25 @@ export function EmailOverlayComponent({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const menuCC = (
|
||||||
|
<div>
|
||||||
|
<Menu onClick={handle_CC_Click}>
|
||||||
|
{bodyshop.employees
|
||||||
|
.filter((e) => e.user_email)
|
||||||
|
.map((e, idx) => (
|
||||||
|
<Menu.Item value={e.user_email} key={idx}>
|
||||||
|
{`${e.first_name} ${e.last_name}`}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
{bodyshop.md_to_emails.map((e, idx) => (
|
||||||
|
<Menu.Item value={e.emails} key={idx + "group"}>
|
||||||
|
{e.label}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -122,7 +150,23 @@ export function EmailOverlayComponent({
|
|||||||
>
|
>
|
||||||
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("emails.fields.cc")} name="cc">
|
<Form.Item
|
||||||
|
label={
|
||||||
|
<Space>
|
||||||
|
{t("emails.fields.cc")}
|
||||||
|
<Dropdown overlay={menuCC}>
|
||||||
|
<a
|
||||||
|
className="ant-dropdown-link"
|
||||||
|
href=" #"
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<UserAddOutlined />
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
name="cc"
|
||||||
|
>
|
||||||
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
<Select mode="tags" tokenSeparators={[",", ";"]} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Select, Space, Tag } from "antd";
|
import { Select, Space, Tag } from "antd";
|
||||||
import React, { forwardRef } from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
|
|
||||||
const EmployeeSearchSelect = ({ options, ...props }, ref) => {
|
const EmployeeSearchSelect = ({ options, ...props }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -39,4 +39,4 @@ const EmployeeSearchSelect = ({ options, ...props }, ref) => {
|
|||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default forwardRef(EmployeeSearchSelect);
|
export default EmployeeSearchSelect;
|
||||||
|
|||||||
@@ -262,11 +262,13 @@ function Header({
|
|||||||
{t("menus.header.timetickets")}
|
{t("menus.header.timetickets")}
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
|
{bodyshop?.md_tasks_presets?.use_approvals && (
|
||||||
<Link to="/manage/ttapprovals">
|
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
|
||||||
{t("menus.header.ttapprovals")}
|
<Link to="/manage/ttapprovals">
|
||||||
</Link>
|
{t("menus.header.ttapprovals")}
|
||||||
</Menu.Item>
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="entertimetickets"
|
key="entertimetickets"
|
||||||
icon={<Icon component={GiPlayerTime} />}
|
icon={<Icon component={GiPlayerTime} />}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
|||||||
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
|
||||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||||
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
import JobsDetailHeaderActionsExportcustdataComponent from "./jobs-detail-header-actions.exportcustdata.component";
|
||||||
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -250,21 +251,24 @@ export function JobsDetailHeaderActions({
|
|||||||
>
|
>
|
||||||
{t("timetickets.actions.enter")}
|
{t("timetickets.actions.enter")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
{bodyshop.md_tasks_presets.enable_tasks && (
|
||||||
key="claimtimetickettasks"
|
<Menu.Item
|
||||||
disabled={
|
key="claimtimetickettasks"
|
||||||
!job.converted ||
|
disabled={
|
||||||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
|
!job.converted ||
|
||||||
}
|
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
|
||||||
onClick={() => {
|
}
|
||||||
setTimeTicketTaskContext({
|
onClick={() => {
|
||||||
actions: {},
|
setTimeTicketTaskContext({
|
||||||
context: { jobid: job.id },
|
actions: {},
|
||||||
});
|
context: { jobid: job.id },
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
{t("timetickets.actions.claimtasks")}
|
>
|
||||||
</Menu.Item>
|
{t("timetickets.actions.claimtasks")}
|
||||||
|
</Menu.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
key="enterpayments"
|
key="enterpayments"
|
||||||
disabled={!job.converted}
|
disabled={!job.converted}
|
||||||
@@ -494,54 +498,56 @@ export function JobsDetailHeaderActions({
|
|||||||
)}
|
)}
|
||||||
<JobsDetailHeaderActionsAddevent jobid={job.id} />
|
<JobsDetailHeaderActionsAddevent jobid={job.id} />
|
||||||
{!jobRO && job.converted && (
|
{!jobRO && job.converted && (
|
||||||
<Menu.Item>
|
<RbacWrapper action="jobs:void" noauth>
|
||||||
<Popconfirm
|
<Menu.Item>
|
||||||
title={t("jobs.labels.voidjob")}
|
<Popconfirm
|
||||||
okText="Yes"
|
title={t("jobs.labels.voidjob")}
|
||||||
cancelText="No"
|
okText="Yes"
|
||||||
onClick={(e) => e.stopPropagation()}
|
cancelText="No"
|
||||||
onConfirm={async () => {
|
onClick={(e) => e.stopPropagation()}
|
||||||
//delete the job.
|
onConfirm={async () => {
|
||||||
const result = await voidJob({
|
//delete the job.
|
||||||
variables: {
|
const result = await voidJob({
|
||||||
jobId: job.id,
|
variables: {
|
||||||
job: {
|
jobId: job.id,
|
||||||
status: bodyshop.md_ro_statuses.default_void,
|
job: {
|
||||||
voided: true,
|
status: bodyshop.md_ro_statuses.default_void,
|
||||||
scheduled_in: null,
|
voided: true,
|
||||||
scheduled_completion: null,
|
scheduled_in: null,
|
||||||
inproduction: false,
|
scheduled_completion: null,
|
||||||
},
|
inproduction: false,
|
||||||
note: [
|
|
||||||
{
|
|
||||||
jobid: job.id,
|
|
||||||
created_by: currentUser.email,
|
|
||||||
audit: true,
|
|
||||||
text: t("jobs.labels.voidnote"),
|
|
||||||
},
|
},
|
||||||
],
|
note: [
|
||||||
},
|
{
|
||||||
});
|
jobid: job.id,
|
||||||
|
created_by: currentUser.email,
|
||||||
|
audit: true,
|
||||||
|
text: t("jobs.labels.voidnote"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!!!result.errors) {
|
if (!!!result.errors) {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.voided"),
|
message: t("jobs.successes.voided"),
|
||||||
});
|
});
|
||||||
//go back to jobs list.
|
//go back to jobs list.
|
||||||
history.push(`/manage/`);
|
history.push(`/manage/`);
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.voiding", {
|
message: t("jobs.errors.voiding", {
|
||||||
error: JSON.stringify(result.errors),
|
error: JSON.stringify(result.errors),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
getPopupContainer={(trigger) => trigger.parentNode}
|
getPopupContainer={(trigger) => trigger.parentNode}
|
||||||
>
|
>
|
||||||
{t("menus.jobsactions.void")}
|
{t("menus.jobsactions.void")}
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
</RbacWrapper>
|
||||||
)}
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,17 +23,34 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
|
|||||||
const { id: jobId, job } = printCenterModal.context;
|
const { id: jobId, job } = printCenterModal.context;
|
||||||
const tempList = TemplateList("job", {});
|
const tempList = TemplateList("job", {});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const JobsReportsList = Object.keys(tempList)
|
|
||||||
.map((key) => {
|
const JobsReportsList =
|
||||||
return tempList[key];
|
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
|
||||||
})
|
? Object.keys(tempList)
|
||||||
.filter(
|
.map((key) => {
|
||||||
(temp) =>
|
return tempList[key];
|
||||||
!temp.regions ||
|
})
|
||||||
(temp.regions && temp.regions[bodyshop.region_config]) ||
|
.filter(
|
||||||
(temp.regions &&
|
(temp) =>
|
||||||
bodyshop.region_config.includes(Object.keys(temp.regions)) === true)
|
(!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 =
|
const filteredJobsReportsList =
|
||||||
search !== ""
|
search !== ""
|
||||||
|
|||||||
@@ -246,11 +246,21 @@ export function ProductionListTable({
|
|||||||
(x) => x.status === record.status
|
(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 {
|
return {
|
||||||
|
className: "rowWithColor",
|
||||||
style: {
|
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:partsqueue": 4,
|
||||||
"jobs:checklist-view": 2,
|
"jobs:checklist-view": 2,
|
||||||
"jobs:list-ready": 1,
|
"jobs:list-ready": 1,
|
||||||
|
"jobs:void": 5,
|
||||||
|
|
||||||
"bills:enter": 2,
|
"bills:enter": 2,
|
||||||
"bills:view": 2,
|
"bills:view": 2,
|
||||||
"bills:list": 2,
|
"bills:list": 2,
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import "./schedule-calendar.styles.scss";
|
|||||||
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
|
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
|
||||||
import { selectProblemJobs } from "../../redux/application/application.selectors";
|
import { selectProblemJobs } from "../../redux/application/application.selectors";
|
||||||
import { Alert, Collapse } from "antd";
|
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({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -66,10 +67,21 @@ export function ScheduleCalendarWrapperComponent({
|
|||||||
<Alert
|
<Alert
|
||||||
key={problem.id}
|
key={problem.id}
|
||||||
type="error"
|
type="error"
|
||||||
message={t("appointments.labels.dataconsistency", {
|
message={
|
||||||
ro_number: problem.ro_number,
|
<Trans
|
||||||
code: problem.code,
|
i18nKey="appointments.labels.dataconsistency"
|
||||||
})}
|
components={[
|
||||||
|
<Link
|
||||||
|
to={`/manage/jobs/${problem.id}`}
|
||||||
|
target="_blank"
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
values={{
|
||||||
|
ro_number: problem.ro_number,
|
||||||
|
code: problem.code,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Collapse.Panel>
|
</Collapse.Panel>
|
||||||
@@ -79,10 +91,18 @@ export function ScheduleCalendarWrapperComponent({
|
|||||||
<Alert
|
<Alert
|
||||||
key={problem.id}
|
key={problem.id}
|
||||||
type="error"
|
type="error"
|
||||||
message={t("appointments.labels.dataconsistency", {
|
message={
|
||||||
ro_number: problem.ro_number,
|
<Trans
|
||||||
code: problem.code,
|
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 { Form, InputNumber } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
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 { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
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({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -316,6 +316,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</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
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
InputNumber,
|
InputNumber,
|
||||||
Row,
|
Row,
|
||||||
Space,
|
Space,
|
||||||
|
Switch,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -18,127 +19,146 @@ export default function ShopInfoTaskPresets({ form }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayoutFormRow header={t("bodyshop.labels.md_tasks_presets")}>
|
<>
|
||||||
<Form.List name={["md_tasks_presets", "presets"]}>
|
<LayoutFormRow noDivider>
|
||||||
{(fields, { add, remove, move }) => {
|
<Form.Item
|
||||||
return (
|
label={t("bodyshop.fields.md_tasks_presets.enable_tasks")}
|
||||||
<div>
|
valuePropName="checked"
|
||||||
{fields.map((field, index) => (
|
name={["md_tasks_presets", "enable_tasks"]}
|
||||||
<Form.Item key={field.key}>
|
>
|
||||||
<LayoutFormRow noDivider>
|
<Switch />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.md_tasks_presets.name")}
|
<Form.Item
|
||||||
key={`${index}name`}
|
label={t("bodyshop.fields.md_tasks_presets.use_approvals")}
|
||||||
name={[field.name, "name"]}
|
valuePropName="checked"
|
||||||
rules={[
|
name={["md_tasks_presets", "use_approvals"]}
|
||||||
{
|
>
|
||||||
required: true,
|
<Switch />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>
|
||||||
},
|
</LayoutFormRow>
|
||||||
]}
|
|
||||||
>
|
<LayoutFormRow header={t("bodyshop.labels.md_tasks_presets")}>
|
||||||
<Input />
|
<Form.List name={["md_tasks_presets", "presets"]}>
|
||||||
</Form.Item>
|
{(fields, { add, remove, move }) => {
|
||||||
<Form.Item
|
return (
|
||||||
label={t("bodyshop.fields.md_tasks_presets.hourstype")}
|
<div>
|
||||||
key={`${index}hourstype`}
|
{fields.map((field, index) => (
|
||||||
name={[field.name, "hourstype"]}
|
<Form.Item key={field.key}>
|
||||||
rules={[
|
<LayoutFormRow noDivider>
|
||||||
{
|
<Form.Item
|
||||||
required: true,
|
label={t("bodyshop.fields.md_tasks_presets.name")}
|
||||||
//message: t("general.validation.required"),
|
key={`${index}name`}
|
||||||
},
|
name={[field.name, "name"]}
|
||||||
]}
|
rules={[
|
||||||
>
|
{
|
||||||
<Checkbox.Group>
|
required: true,
|
||||||
<Row>
|
//message: t("general.validation.required"),
|
||||||
<Col span={8}>
|
},
|
||||||
<Checkbox
|
]}
|
||||||
value="LAB"
|
>
|
||||||
style={{ lineHeight: "32px" }}
|
<Input />
|
||||||
>
|
</Form.Item>
|
||||||
{t("joblines.fields.lbr_types.LAB")}
|
<Form.Item
|
||||||
</Checkbox>
|
label={t("bodyshop.fields.md_tasks_presets.hourstype")}
|
||||||
</Col>
|
key={`${index}hourstype`}
|
||||||
<Col span={8}>
|
name={[field.name, "hourstype"]}
|
||||||
<Checkbox
|
rules={[
|
||||||
value="LAR"
|
{
|
||||||
style={{ lineHeight: "32px" }}
|
required: true,
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
{t("joblines.fields.lbr_types.LAR")}
|
},
|
||||||
</Checkbox>
|
]}
|
||||||
</Col>
|
>
|
||||||
<Col span={8}>
|
<Checkbox.Group>
|
||||||
<Checkbox
|
<Row>
|
||||||
value="LAM"
|
<Col span={8}>
|
||||||
style={{ lineHeight: "32px" }}
|
<Checkbox
|
||||||
>
|
value="LAB"
|
||||||
{t("joblines.fields.lbr_types.LAM")}
|
style={{ lineHeight: "32px" }}
|
||||||
</Checkbox>
|
>
|
||||||
</Col>
|
{t("joblines.fields.lbr_types.LAB")}
|
||||||
<Col span={8}>
|
</Checkbox>
|
||||||
<Checkbox
|
</Col>
|
||||||
value="LAF"
|
<Col span={8}>
|
||||||
style={{ lineHeight: "32px" }}
|
<Checkbox
|
||||||
>
|
value="LAR"
|
||||||
{t("joblines.fields.lbr_types.LAF")}
|
style={{ lineHeight: "32px" }}
|
||||||
</Checkbox>
|
>
|
||||||
</Col>
|
{t("joblines.fields.lbr_types.LAR")}
|
||||||
<Col span={8}>
|
</Checkbox>
|
||||||
<Checkbox
|
</Col>
|
||||||
value="LAG"
|
<Col span={8}>
|
||||||
style={{ lineHeight: "32px" }}
|
<Checkbox
|
||||||
>
|
value="LAM"
|
||||||
{t("joblines.fields.lbr_types.LAG")}
|
style={{ lineHeight: "32px" }}
|
||||||
</Checkbox>
|
>
|
||||||
</Col>
|
{t("joblines.fields.lbr_types.LAM")}
|
||||||
</Row>
|
</Checkbox>
|
||||||
</Checkbox.Group>
|
</Col>
|
||||||
</Form.Item>
|
<Col span={8}>
|
||||||
<Form.Item
|
<Checkbox
|
||||||
label={t("bodyshop.fields.md_tasks_presets.percent")}
|
value="LAF"
|
||||||
key={`${index}percent`}
|
style={{ lineHeight: "32px" }}
|
||||||
name={[field.name, "percent"]}
|
>
|
||||||
>
|
{t("joblines.fields.lbr_types.LAF")}
|
||||||
<InputNumber min={0} max={100} />
|
</Checkbox>
|
||||||
</Form.Item>
|
</Col>
|
||||||
<Form.Item
|
<Col span={8}>
|
||||||
label={t("bodyshop.fields.md_tasks_presets.memo")}
|
<Checkbox
|
||||||
key={`${index}memo`}
|
value="LAG"
|
||||||
name={[field.name, "memo"]}
|
style={{ lineHeight: "32px" }}
|
||||||
>
|
>
|
||||||
<Input />
|
{t("joblines.fields.lbr_types.LAG")}
|
||||||
</Form.Item>
|
</Checkbox>
|
||||||
<Space wrap>
|
</Col>
|
||||||
<DeleteFilled
|
</Row>
|
||||||
onClick={() => {
|
</Checkbox.Group>
|
||||||
remove(field.name);
|
</Form.Item>
|
||||||
}}
|
<Form.Item
|
||||||
/>
|
label={t("bodyshop.fields.md_tasks_presets.percent")}
|
||||||
<FormListMoveArrows
|
key={`${index}percent`}
|
||||||
move={move}
|
name={[field.name, "percent"]}
|
||||||
index={index}
|
>
|
||||||
total={fields.length}
|
<InputNumber min={0} max={100} />
|
||||||
/>
|
</Form.Item>
|
||||||
</Space>
|
<Form.Item
|
||||||
</LayoutFormRow>
|
label={t("bodyshop.fields.md_tasks_presets.memo")}
|
||||||
|
key={`${index}memo`}
|
||||||
|
name={[field.name, "memo"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Space wrap>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("bodyshop.actions.add_task_preset")}
|
||||||
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
</div>
|
||||||
<Form.Item>
|
);
|
||||||
<Button
|
}}
|
||||||
type="dashed"
|
</Form.List>
|
||||||
onClick={() => {
|
</LayoutFormRow>
|
||||||
add();
|
</>
|
||||||
}}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
>
|
|
||||||
{t("bodyshop.actions.add_task_preset")}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
</LayoutFormRow>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
|
|||||||
<JobSearchSelect
|
<JobSearchSelect
|
||||||
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
|
||||||
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
notExported={!bodyshop.tt_allow_post_to_invoiced}
|
||||||
|
notInvoiced={!bodyshop.tt_allow_post_to_invoiced}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
|||||||
@@ -205,16 +205,15 @@ export function TimeTicketList({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "Pay",
|
// title: "Pay",
|
||||||
dataIndex: "pay",
|
// dataIndex: "pay",
|
||||||
key: "pay",
|
// key: "pay",
|
||||||
render: (text, record) =>
|
// render: (text, record) =>
|
||||||
Dinero({ amount: Math.round(record.rate * 100) })
|
// Dinero({ amount: Math.round(record.rate * 100) })
|
||||||
.multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
// .multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
||||||
.toFormat("$0.00"),
|
// .toFormat("$0.00"),
|
||||||
},
|
// },
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
@@ -282,16 +281,18 @@ export function TimeTicketList({
|
|||||||
// context={{ jobId: jobId }}
|
// context={{ jobId: jobId }}
|
||||||
// />
|
// />
|
||||||
}
|
}
|
||||||
<Button
|
{bodyshop.md_tasks_presets.enable_tasks && (
|
||||||
onClick={() => {
|
<Button
|
||||||
setTimeTicketTaskContext({
|
onClick={() => {
|
||||||
actions: {},
|
setTimeTicketTaskContext({
|
||||||
context: { jobid: jobId },
|
actions: {},
|
||||||
});
|
context: { jobid: jobId },
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
{t("timetickets.actions.claimtasks")}
|
>
|
||||||
</Button>
|
{t("timetickets.actions.claimtasks")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{jobId &&
|
{jobId &&
|
||||||
(techConsole ? null : (
|
(techConsole ? null : (
|
||||||
<TimeTicketEnterButton
|
<TimeTicketEnterButton
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export function TimeTicketModalComponent({
|
|||||||
authLevel,
|
authLevel,
|
||||||
employeeAutoCompleteOptions,
|
employeeAutoCompleteOptions,
|
||||||
isEdit,
|
isEdit,
|
||||||
|
disabled,
|
||||||
employeeSelectDisabled,
|
employeeSelectDisabled,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -50,7 +51,7 @@ export function TimeTicketModalComponent({
|
|||||||
<Select
|
<Select
|
||||||
value={value === "timetickets.labels.shift" ? t(value) : value}
|
value={value === "timetickets.labels.shift" ? t(value) : value}
|
||||||
{...props}
|
{...props}
|
||||||
disabled={value === "timetickets.labels.shift"}
|
disabled={value === "timetickets.labels.shift" || disabled}
|
||||||
>
|
>
|
||||||
{emps &&
|
{emps &&
|
||||||
emps.rates.map((item) => (
|
emps.rates.map((item) => (
|
||||||
@@ -73,7 +74,7 @@ export function TimeTicketModalComponent({
|
|||||||
<Input
|
<Input
|
||||||
value={value?.startsWith("timetickets.") ? t(value) : value}
|
value={value?.startsWith("timetickets.") ? t(value) : value}
|
||||||
{...props}
|
{...props}
|
||||||
disabled={value?.startsWith("timetickets.")}
|
disabled={value?.startsWith("timetickets.") || disabled}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -127,7 +128,7 @@ export function TimeTicketModalComponent({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<EmployeeSearchSelect
|
<EmployeeSearchSelect
|
||||||
disabled={employeeSelectDisabled}
|
disabled={employeeSelectDisabled || disabled}
|
||||||
options={employeeAutoCompleteOptions}
|
options={employeeAutoCompleteOptions}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
const emps =
|
const emps =
|
||||||
@@ -279,6 +280,7 @@ export function TimeTicketModalComponent({
|
|||||||
<FormDateTimePicker
|
<FormDateTimePicker
|
||||||
minuteStep={5}
|
minuteStep={5}
|
||||||
disabled={
|
disabled={
|
||||||
|
disabled ||
|
||||||
!HasRbacAccess({
|
!HasRbacAccess({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
authLevel,
|
authLevel,
|
||||||
@@ -317,6 +319,7 @@ export function TimeTicketModalComponent({
|
|||||||
<FormDateTimePicker
|
<FormDateTimePicker
|
||||||
minuteStep={5}
|
minuteStep={5}
|
||||||
disabled={
|
disabled={
|
||||||
|
disabled ||
|
||||||
!HasRbacAccess({
|
!HasRbacAccess({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
authLevel,
|
authLevel,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
|||||||
import { selectTimeTicket } from "../../redux/modals/modals.selectors";
|
import { selectTimeTicket } from "../../redux/modals/modals.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import TimeTicketModalComponent from "./time-ticket-modal.component";
|
import TimeTicketModalComponent from "./time-ticket-modal.component";
|
||||||
|
import TimeTicketsCommitToggleComponent from "../time-tickets-commit-toggle/time-tickets-commit-toggle.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
timeTicketModal: selectTimeTicket,
|
timeTicketModal: selectTimeTicket,
|
||||||
@@ -174,7 +175,11 @@ export function TimeTicketModalContainer({
|
|||||||
footer={
|
footer={
|
||||||
<span>
|
<span>
|
||||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
||||||
<Button loading={loading} onClick={() => form.submit()}>
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
|
onClick={() => form.submit()}
|
||||||
|
>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
{timeTicketModal.context && timeTicketModal.context.id ? null : (
|
{timeTicketModal.context && timeTicketModal.context.id ? null : (
|
||||||
@@ -198,6 +203,7 @@ export function TimeTicketModalContainer({
|
|||||||
autoComplete={"off"}
|
autoComplete={"off"}
|
||||||
form={form}
|
form={form}
|
||||||
onFinishFailed={() => setEnterAgain(false)}
|
onFinishFailed={() => setEnterAgain(false)}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
initialValues={
|
initialValues={
|
||||||
timeTicketModal.context.timeticket
|
timeTicketModal.context.timeticket
|
||||||
? {
|
? {
|
||||||
@@ -218,6 +224,9 @@ export function TimeTicketModalContainer({
|
|||||||
<PageHeader
|
<PageHeader
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
|
<TimeTicketsCommitToggleComponent
|
||||||
|
timeticket={timeTicketModal.context?.timeticket}
|
||||||
|
/>
|
||||||
<Button onClick={handleCancel}>
|
<Button onClick={handleCancel}>
|
||||||
{t("general.actions.cancel")}
|
{t("general.actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -241,14 +250,16 @@ export function TimeTicketModalContainer({
|
|||||||
<TimeTicketModalComponent
|
<TimeTicketModalComponent
|
||||||
isEdit={timeTicketModal.context.id}
|
isEdit={timeTicketModal.context.id}
|
||||||
form={form}
|
form={form}
|
||||||
|
disabled={timeTicketModal.context?.timeticket?.committed_at}
|
||||||
employeeAutoCompleteOptions={
|
employeeAutoCompleteOptions={
|
||||||
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
||||||
}
|
}
|
||||||
employeeSelectDisabled={
|
employeeSelectDisabled={
|
||||||
timeTicketModal.context?.timeticket?.employeeid &&
|
timeTicketModal.context?.timeticket?.committed_at ||
|
||||||
|
(timeTicketModal.context?.timeticket?.employeeid &&
|
||||||
!timeTicketModal.context.id
|
!timeTicketModal.context.id
|
||||||
? true
|
? true
|
||||||
: false
|
: false)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import JobSearchSelectComponent from "../job-search-select/job-search-select.com
|
|||||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||||
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
|
||||||
import TimeTicketsTasksPresets from "../time-ticket-tasks-presets/time-ticket-tasks-presets.component";
|
import TimeTicketsTasksPresets from "../time-ticket-tasks-presets/time-ticket-tasks-presets.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
@@ -54,7 +55,8 @@ export function TimeTicketTaskModalComponent({
|
|||||||
calculateTimeTickets={calculateTimeTickets}
|
calculateTimeTickets={calculateTimeTickets}
|
||||||
/>
|
/>
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col lg={12} md={24}>
|
|
||||||
|
<Col xl={12} lg={24}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="jobid"
|
name="jobid"
|
||||||
label={t("timetickets.fields.ro_number")}
|
label={t("timetickets.fields.ro_number")}
|
||||||
@@ -77,37 +79,37 @@ export function TimeTicketTaskModalComponent({
|
|||||||
>
|
>
|
||||||
<EmployeeTeamSearchSelectComponent />
|
<EmployeeTeamSearchSelectComponent />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Space wrap>
|
|
||||||
<Form.Item
|
|
||||||
name="hourstype"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Checkbox.Group>
|
|
||||||
<Space wrap>
|
|
||||||
<Checkbox value="LAB" style={{ display: "flex" }}>
|
|
||||||
Body
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox value="LAR" style={{ display: "flex" }}>
|
|
||||||
Refinish
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox value="LAM" style={{ display: "flex" }}>
|
|
||||||
Mechanical
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox value="LAF" style={{ display: "flex" }}>
|
|
||||||
Frame
|
|
||||||
</Checkbox>
|
|
||||||
<Checkbox value="LAG" style={{ display: "flex" }}>
|
|
||||||
Glass
|
|
||||||
</Checkbox>
|
|
||||||
</Space>
|
|
||||||
</Checkbox.Group>
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="hourstype"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Checkbox.Group>
|
||||||
|
<Space wrap>
|
||||||
|
<Checkbox value="LAB" style={{ display: "flex" }}>
|
||||||
|
{t("jobs.fields.lab")}
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox value="LAR" style={{ display: "flex" }}>
|
||||||
|
{t("jobs.fields.lar")}
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox value="LAM" style={{ display: "flex" }}>
|
||||||
|
{t("jobs.fields.lam")}
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox value="LAF" style={{ display: "flex" }}>
|
||||||
|
{t("jobs.fields.laf")}
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox value="LAG" style={{ display: "flex" }}>
|
||||||
|
{t("jobs.fields.lag")}
|
||||||
|
</Checkbox>
|
||||||
|
</Space>
|
||||||
|
</Checkbox.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Space wrap align="start">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="percent"
|
name="percent"
|
||||||
rules={[
|
rules={[
|
||||||
@@ -119,10 +121,13 @@ export function TimeTicketTaskModalComponent({
|
|||||||
>
|
>
|
||||||
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
|
<InputNumber min={0} max={100} precision={1} addonAfter="%" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Button onClick={calculateTimeTickets}>
|
||||||
|
{t("tt_approvals.labels.calculate")}
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
<Button onClick={calculateTimeTickets}>Calculate</Button>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col lg={12} md={24}>
|
<Col xl={12} lg={24}>
|
||||||
<Form.Item shouldUpdate>
|
<Form.Item shouldUpdate>
|
||||||
{() => {
|
{() => {
|
||||||
const data = form.getFieldValue("timetickets");
|
const data = form.getFieldValue("timetickets");
|
||||||
@@ -167,11 +172,11 @@ export function TimeTicketTaskModalComponent({
|
|||||||
dataIndex: "rate",
|
dataIndex: "rate",
|
||||||
key: "rate",
|
key: "rate",
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "Pay",
|
// title: "Pay",
|
||||||
dataIndex: "pay",
|
// dataIndex: "pay",
|
||||||
key: "pay",
|
// key: "pay",
|
||||||
},
|
// },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -231,11 +236,9 @@ export function TimeTicketTaskModalComponent({
|
|||||||
<Alert key={idx} message={e} />
|
<Alert key={idx} message={e} />
|
||||||
))}
|
))}
|
||||||
<div
|
<div
|
||||||
style={
|
style={{
|
||||||
{
|
display: "none",
|
||||||
//display: "none"
|
}}
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -364,6 +367,11 @@ export function TimeTicketTaskModalComponent({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{bodyshop?.md_tasks_presets?.use_approvals && (
|
||||||
|
<Col span={24}>
|
||||||
|
<Alert message={t("tt_approvals.labels.approval_queue_in_use")} type="warning" />
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,8 @@ export function TimeTickeTaskModalContainer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function handleFinish(values) {
|
async function handleFinish(values) {
|
||||||
console.log(
|
|
||||||
"🚀 ~ file: time-ticket-task-modal.container.jsx:55 ~ handleFinish ~ values:",
|
|
||||||
values
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
if (true) {
|
if (bodyshop.md_tasks_presets.use_approvals) {
|
||||||
const result = await insertTimeTicketApproval({
|
const result = await insertTimeTicketApproval({
|
||||||
variables: {
|
variables: {
|
||||||
timeTicketInput: values.timetickets.map((ticket) => ({
|
timeTicketInput: values.timetickets.map((ticket) => ({
|
||||||
@@ -88,6 +84,7 @@ export function TimeTickeTaskModalContainer({
|
|||||||
_.omit(ticket, "pay")
|
_.omit(ticket, "pay")
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
refetchQueries: ["GET_LINE_TICKET_BY_PK"]
|
||||||
});
|
});
|
||||||
if (result.errors) {
|
if (result.errors) {
|
||||||
notification.open({
|
notification.open({
|
||||||
@@ -105,6 +102,13 @@ export function TimeTickeTaskModalContainer({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log("🚀 ~ file: time-ticket-task-modal.container.jsx:104 ~ handleFinish ~ error:", error)
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(error),
|
||||||
|
}),
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
UPDATE_TIME_TICKET,
|
||||||
|
UPDATE_TIME_TICKETS,
|
||||||
|
} from "../../graphql/timetickets.queries";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||||
|
});
|
||||||
|
export function TimeTicketsCommit({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
timeticket,
|
||||||
|
disabled,
|
||||||
|
refetch,
|
||||||
|
setTimeTicketContext,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateTimeTicket] = useMutation(UPDATE_TIME_TICKET);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleCommit = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const ticketUpdate = timeticket.committed_at
|
||||||
|
? { commited_by: null, committed_at: null }
|
||||||
|
: {
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await updateTimeTicket({
|
||||||
|
variables: {
|
||||||
|
timeticketId: timeticket.id,
|
||||||
|
timeticket: ticketUpdate,
|
||||||
|
},
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
fields: {
|
||||||
|
timeTickets(existingtickets, { readField }) {
|
||||||
|
return existingtickets.map((ticket) => {
|
||||||
|
if (timeticket.id === readField("id", ticket)) {
|
||||||
|
return {
|
||||||
|
...ticket,
|
||||||
|
...ticketUpdate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ticket;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeTicketContext({
|
||||||
|
context: {
|
||||||
|
id: timeticket.id,
|
||||||
|
timeticket: result.data.update_timetickets.returning[0],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.committed"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!timeticket?.id) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button onClick={handleCommit} loading={loading} disabled={!timeticket?.id}>
|
||||||
|
{timeticket?.committed_at
|
||||||
|
? t("timetickets.actions.uncommit")
|
||||||
|
: t("timetickets.actions.commitone")}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TimeTicketsCommit);
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { UPDATE_TIME_TICKETS } from "../../graphql/timetickets.queries";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TimeTicketsCommit({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
timetickets,
|
||||||
|
disabled,
|
||||||
|
loadingCallback,
|
||||||
|
completedCallback,
|
||||||
|
refetch,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateTimeTickets] = useMutation(UPDATE_TIME_TICKETS);
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleCommit = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await updateTimeTickets({
|
||||||
|
variables: {
|
||||||
|
timeticketIds: timetickets.map((ticket) => ticket.id),
|
||||||
|
timeticket: {
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update(cache) {
|
||||||
|
cache.modify({
|
||||||
|
fields: {
|
||||||
|
timeTickets(existingtickets, { readField }) {
|
||||||
|
const modifiedIds = timetickets.map((ticket) => ticket.id);
|
||||||
|
return existingtickets.map((ticket) => {
|
||||||
|
if (modifiedIds.includes(readField("id", ticket))) {
|
||||||
|
return {
|
||||||
|
...ticket,
|
||||||
|
commited_by: currentUser.email,
|
||||||
|
committed_at: moment(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ticket;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("timetickets.errors.creating", {
|
||||||
|
message: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("timetickets.successes.committed"),
|
||||||
|
});
|
||||||
|
if (!!completedCallback) completedCallback([]);
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={handleCommit}
|
||||||
|
loading={loading}
|
||||||
|
disabled={disabled || timetickets?.length === 0}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.commit", { count: timetickets?.length })}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(TimeTicketsCommit);
|
||||||
@@ -9,13 +9,16 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { INSERT_TIME_TICKET_AND_APPROVE } from "../../graphql/timetickets.queries";
|
import { INSERT_TIME_TICKET_AND_APPROVE } from "../../graphql/timetickets.queries";
|
||||||
import { QUERY_TT_APPROVALS_BY_IDS } from "../../graphql/tt-approvals.queries";
|
import { QUERY_TT_APPROVALS_BY_IDS } from "../../graphql/tt-approvals.queries";
|
||||||
import {
|
import {
|
||||||
|
selectAuthLevel,
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
|
authLevel: selectAuthLevel,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function TtApproveButton({
|
export function TtApproveButton({
|
||||||
@@ -23,6 +26,7 @@ export function TtApproveButton({
|
|||||||
currentUser,
|
currentUser,
|
||||||
selectedTickets,
|
selectedTickets,
|
||||||
disabled,
|
disabled,
|
||||||
|
authLevel,
|
||||||
loadingCallback,
|
loadingCallback,
|
||||||
completedCallback,
|
completedCallback,
|
||||||
refetch,
|
refetch,
|
||||||
@@ -64,7 +68,7 @@ export function TtApproveButton({
|
|||||||
} else {
|
} else {
|
||||||
notification.open({
|
notification.open({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: t("timetickets.successes.createdg"),
|
message: t("timetickets.successes.created"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -83,7 +87,14 @@ export function TtApproveButton({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
<Button
|
||||||
|
onClick={handleQbxml}
|
||||||
|
loading={loading}
|
||||||
|
disabled={
|
||||||
|
disabled ||
|
||||||
|
!HasRbacAccess({ bodyshop, authLevel, action: "ttapprovals:approve" })
|
||||||
|
}
|
||||||
|
>
|
||||||
{t("tt_approvals.actions.approveselected")}
|
{t("tt_approvals.actions.approveselected")}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export const QUERY_TICKETS_BY_JOBID = gql`
|
|||||||
memo
|
memo
|
||||||
jobid
|
jobid
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
employee {
|
employee {
|
||||||
employee_number
|
employee_number
|
||||||
first_name
|
first_name
|
||||||
@@ -44,6 +46,8 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
|||||||
memo
|
memo
|
||||||
jobid
|
jobid
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
@@ -86,6 +90,8 @@ export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
|||||||
productivehrs
|
productivehrs
|
||||||
memo
|
memo
|
||||||
jobid
|
jobid
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
flat_rate
|
flat_rate
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
@@ -119,6 +125,8 @@ export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
|
|||||||
memo
|
memo
|
||||||
jobid
|
jobid
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
@@ -161,6 +169,8 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
|||||||
committed_at
|
committed_at
|
||||||
commited_by
|
commited_by
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
@@ -221,6 +231,8 @@ export const INSERT_NEW_TIME_TICKET = gql`
|
|||||||
date
|
date
|
||||||
memo
|
memo
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,6 +256,8 @@ export const INSERT_TIME_TICKET_AND_APPROVE = gql`
|
|||||||
date
|
date
|
||||||
memo
|
memo
|
||||||
flat_rate
|
flat_rate
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_tt_approval_queue(
|
update_tt_approval_queue(
|
||||||
@@ -254,6 +268,7 @@ export const INSERT_TIME_TICKET_AND_APPROVE = gql`
|
|||||||
id
|
id
|
||||||
approved_at
|
approved_at
|
||||||
approved_at
|
approved_at
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +297,38 @@ export const UPDATE_TIME_TICKET = gql`
|
|||||||
date
|
date
|
||||||
flat_rate
|
flat_rate
|
||||||
memo
|
memo
|
||||||
|
committed_at
|
||||||
|
commited_by
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const UPDATE_TIME_TICKETS = gql`
|
||||||
|
mutation UPDATE_TIME_TICKETS(
|
||||||
|
$timeticketIds: [uuid!]!
|
||||||
|
$timeticket: timetickets_set_input!
|
||||||
|
) {
|
||||||
|
update_timetickets(
|
||||||
|
where: { id: { _in: $timeticketIds } }
|
||||||
|
_set: $timeticket
|
||||||
|
) {
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
clockon
|
||||||
|
clockoff
|
||||||
|
employeeid
|
||||||
|
productivehrs
|
||||||
|
actualhrs
|
||||||
|
ciecacode
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
jobid
|
||||||
|
date
|
||||||
|
flat_rate
|
||||||
|
memo
|
||||||
|
committed_at
|
||||||
|
commited_by
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,6 +353,8 @@ export const QUERY_ACTIVE_TIME_TICKETS = gql`
|
|||||||
cost_center
|
cost_center
|
||||||
flat_rate
|
flat_rate
|
||||||
jobid
|
jobid
|
||||||
|
commited_by
|
||||||
|
committed_at
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ownr_fn
|
ownr_fn
|
||||||
|
|||||||
@@ -54,9 +54,12 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
|||||||
<Tabs.TabPane tab={t("bodyshop.labels.employees")} key="employees">
|
<Tabs.TabPane tab={t("bodyshop.labels.employees")} key="employees">
|
||||||
<ShopEmployeesContainer />
|
<ShopEmployeesContainer />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab={t("bodyshop.labels.employee_teams")} key="teams">
|
{
|
||||||
<ShopTeamsContainer />
|
bodyshop.md_tasks_presets.enable_tasks &&
|
||||||
</Tabs.TabPane>
|
<Tabs.TabPane tab={t("bodyshop.labels.employee_teams")} key="teams">
|
||||||
|
<ShopTeamsContainer />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
}
|
||||||
<Tabs.TabPane tab={t("bodyshop.labels.licensing")} key="licensing">
|
<Tabs.TabPane tab={t("bodyshop.labels.licensing")} key="licensing">
|
||||||
<ShopInfoUsersComponent />
|
<ShopInfoUsersComponent />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
|
import TimeTicketsCommit from "../../components/time-tickets-commit/time-tickets-commit.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({});
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ export function TimeTicketsContainer({
|
|||||||
<Space wrap>
|
<Space wrap>
|
||||||
<TimeTicketsAttendanceTable />
|
<TimeTicketsAttendanceTable />
|
||||||
<TimeTicketsPayrollTable />
|
<TimeTicketsPayrollTable />
|
||||||
|
<TimeTicketsCommit timetickets={data ? data.timetickets : []} />
|
||||||
<TimeTicketsDatesSelector />
|
<TimeTicketsDatesSelector />
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"blocked": "Blocked",
|
"blocked": "Blocked",
|
||||||
"cancelledappointment": "Canceled appointment for: ",
|
"cancelledappointment": "Canceled appointment for: ",
|
||||||
"completingjobs": "Completing Jobs",
|
"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: ",
|
"expectedjobs": "Expected Jobs in Production: ",
|
||||||
"expectedprodhrs": "Expected Production Hours:",
|
"expectedprodhrs": "Expected Production Hours:",
|
||||||
"history": "History",
|
"history": "History",
|
||||||
@@ -343,10 +343,12 @@
|
|||||||
"md_payment_types": "Payment Types",
|
"md_payment_types": "Payment Types",
|
||||||
"md_referral_sources": "Referral Sources",
|
"md_referral_sources": "Referral Sources",
|
||||||
"md_tasks_presets": {
|
"md_tasks_presets": {
|
||||||
|
"enable_tasks": "Enable Task Claiming",
|
||||||
"hourstype": "Hour Types",
|
"hourstype": "Hour Types",
|
||||||
"memo": "Time Ticket Memo",
|
"memo": "Time Ticket Memo",
|
||||||
"name": "Preset Name",
|
"name": "Preset Name",
|
||||||
"percent": "Percent"
|
"percent": "Percent",
|
||||||
|
"use_approvals": "Use Time Ticket Approval Queue"
|
||||||
},
|
},
|
||||||
"messaginglabel": "Messaging Preset Label",
|
"messaginglabel": "Messaging Preset Label",
|
||||||
"messagingtext": "Messaging Preset Text",
|
"messagingtext": "Messaging Preset Text",
|
||||||
@@ -405,7 +407,8 @@
|
|||||||
"list-active": "Jobs -> List Active",
|
"list-active": "Jobs -> List Active",
|
||||||
"list-all": "Jobs -> List All",
|
"list-all": "Jobs -> List All",
|
||||||
"list-ready": "Jobs -> List Ready",
|
"list-ready": "Jobs -> List Ready",
|
||||||
"partsqueue": "Jobs -> Parts Queue"
|
"partsqueue": "Jobs -> Parts Queue",
|
||||||
|
"void": "Jobs -> Void"
|
||||||
},
|
},
|
||||||
"owners": {
|
"owners": {
|
||||||
"detail": "Owners -> Detail",
|
"detail": "Owners -> Detail",
|
||||||
@@ -2696,8 +2699,11 @@
|
|||||||
"claimtasks": "Claim Tasks",
|
"claimtasks": "Claim Tasks",
|
||||||
"clockin": "Clock In",
|
"clockin": "Clock In",
|
||||||
"clockout": "Clock Out",
|
"clockout": "Clock Out",
|
||||||
|
"commit": "Commit Tickets ({{count}})",
|
||||||
|
"commitone": "Commit",
|
||||||
"enter": "Enter New Time Ticket",
|
"enter": "Enter New Time Ticket",
|
||||||
"printemployee": "Print Time Tickets"
|
"printemployee": "Print Time Tickets",
|
||||||
|
"uncommit": "Uncommit"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"clockingin": "Error while clocking in. {{message}}",
|
"clockingin": "Error while clocking in. {{message}}",
|
||||||
@@ -2749,6 +2755,7 @@
|
|||||||
"successes": {
|
"successes": {
|
||||||
"clockedin": "Clocked in successfully.",
|
"clockedin": "Clocked in successfully.",
|
||||||
"clockedout": "Clocked out successfully.",
|
"clockedout": "Clocked out successfully.",
|
||||||
|
"committed": "Time Tickets Committed Successfully",
|
||||||
"created": "Time ticket entered successfully.",
|
"created": "Time ticket entered successfully.",
|
||||||
"deleted": "Time ticket deleted successfully."
|
"deleted": "Time ticket deleted successfully."
|
||||||
},
|
},
|
||||||
@@ -2859,6 +2866,10 @@
|
|||||||
"tt_approvals": {
|
"tt_approvals": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"approveselected": "Approve Selected"
|
"approveselected": "Approve Selected"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"approval_queue_in_use": "Time tickets will be added to the approval queue.",
|
||||||
|
"calculate": "Calculate"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
|||||||
@@ -343,10 +343,12 @@
|
|||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"md_tasks_presets": {
|
"md_tasks_presets": {
|
||||||
|
"enable_tasks": "",
|
||||||
"hourstype": "",
|
"hourstype": "",
|
||||||
"memo": "",
|
"memo": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"percent": ""
|
"percent": "",
|
||||||
|
"use_approvals": ""
|
||||||
},
|
},
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
"messagingtext": "",
|
"messagingtext": "",
|
||||||
@@ -405,7 +407,8 @@
|
|||||||
"list-active": "",
|
"list-active": "",
|
||||||
"list-all": "",
|
"list-all": "",
|
||||||
"list-ready": "",
|
"list-ready": "",
|
||||||
"partsqueue": ""
|
"partsqueue": "",
|
||||||
|
"void": ""
|
||||||
},
|
},
|
||||||
"owners": {
|
"owners": {
|
||||||
"detail": "",
|
"detail": "",
|
||||||
@@ -2692,8 +2695,11 @@
|
|||||||
"claimtasks": "",
|
"claimtasks": "",
|
||||||
"clockin": "",
|
"clockin": "",
|
||||||
"clockout": "",
|
"clockout": "",
|
||||||
|
"commit": "",
|
||||||
|
"commitone": "",
|
||||||
"enter": "",
|
"enter": "",
|
||||||
"printemployee": ""
|
"printemployee": "",
|
||||||
|
"uncommit": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"clockingin": "",
|
"clockingin": "",
|
||||||
@@ -2745,6 +2751,7 @@
|
|||||||
"successes": {
|
"successes": {
|
||||||
"clockedin": "",
|
"clockedin": "",
|
||||||
"clockedout": "",
|
"clockedout": "",
|
||||||
|
"committed": "",
|
||||||
"created": "",
|
"created": "",
|
||||||
"deleted": ""
|
"deleted": ""
|
||||||
},
|
},
|
||||||
@@ -2855,6 +2862,10 @@
|
|||||||
"tt_approvals": {
|
"tt_approvals": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"approveselected": ""
|
"approveselected": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"approval_queue_in_use": "",
|
||||||
|
"calculate": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
|||||||
@@ -343,10 +343,12 @@
|
|||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"md_tasks_presets": {
|
"md_tasks_presets": {
|
||||||
|
"enable_tasks": "",
|
||||||
"hourstype": "",
|
"hourstype": "",
|
||||||
"memo": "",
|
"memo": "",
|
||||||
"name": "",
|
"name": "",
|
||||||
"percent": ""
|
"percent": "",
|
||||||
|
"use_approvals": ""
|
||||||
},
|
},
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
"messagingtext": "",
|
"messagingtext": "",
|
||||||
@@ -405,7 +407,8 @@
|
|||||||
"list-active": "",
|
"list-active": "",
|
||||||
"list-all": "",
|
"list-all": "",
|
||||||
"list-ready": "",
|
"list-ready": "",
|
||||||
"partsqueue": ""
|
"partsqueue": "",
|
||||||
|
"void": ""
|
||||||
},
|
},
|
||||||
"owners": {
|
"owners": {
|
||||||
"detail": "",
|
"detail": "",
|
||||||
@@ -2692,8 +2695,11 @@
|
|||||||
"claimtasks": "",
|
"claimtasks": "",
|
||||||
"clockin": "",
|
"clockin": "",
|
||||||
"clockout": "",
|
"clockout": "",
|
||||||
|
"commit": "",
|
||||||
|
"commitone": "",
|
||||||
"enter": "",
|
"enter": "",
|
||||||
"printemployee": ""
|
"printemployee": "",
|
||||||
|
"uncommit": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"clockingin": "",
|
"clockingin": "",
|
||||||
@@ -2745,6 +2751,7 @@
|
|||||||
"successes": {
|
"successes": {
|
||||||
"clockedin": "",
|
"clockedin": "",
|
||||||
"clockedout": "",
|
"clockedout": "",
|
||||||
|
"committed": "",
|
||||||
"created": "",
|
"created": "",
|
||||||
"deleted": ""
|
"deleted": ""
|
||||||
},
|
},
|
||||||
@@ -2855,6 +2862,10 @@
|
|||||||
"tt_approvals": {
|
"tt_approvals": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"approveselected": ""
|
"approveselected": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"approval_queue_in_use": "",
|
||||||
|
"calculate": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ export const TemplateList = (type, context) => {
|
|||||||
key: "dms_posting_sheet",
|
key: "dms_posting_sheet",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
group: "financial",
|
group: "financial",
|
||||||
|
dms: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
@@ -729,9 +729,15 @@ async function InsertDmsVehicle(socket) {
|
|||||||
deliveryDate: moment()
|
deliveryDate: moment()
|
||||||
// .tz(socket.JobData.bodyshop.timezone)
|
// .tz(socket.JobData.bodyshop.timezone)
|
||||||
.format("YYYYMMDD"),
|
.format("YYYYMMDD"),
|
||||||
licensePlateNo: String(socket.JobData.plate_no)
|
licensePlateNo:
|
||||||
.replace(/([^\w]|_)/g, "")
|
socket.JobData.plate_no === null
|
||||||
.toUpperCase(),
|
? 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,
|
make: socket.txEnvelope.dms_make,
|
||||||
modelAbrev: socket.txEnvelope.dms_model,
|
modelAbrev: socket.txEnvelope.dms_model,
|
||||||
modelYear: socket.JobData.v_model_yr,
|
modelYear: socket.JobData.v_model_yr,
|
||||||
|
|||||||
Reference in New Issue
Block a user