Merge branch 'feature/IO-2278-parts-dispatching' into feature/america

This commit is contained in:
Patrick Fic
2023-08-04 13:57:25 -07:00
25 changed files with 677 additions and 40 deletions

View File

@@ -3613,6 +3613,27 @@
<folder_node>
<name>validation</name>
<children>
<concept_node>
<name>closingperiod</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>inventoryquantity</name>
<definition_loaded>false</definition_loaded>
@@ -4247,6 +4268,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>closingperiod</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>country</name>
<definition_loaded>false</definition_loaded>
@@ -28658,6 +28700,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>closingperiod</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>contracts</name>
<definition_loaded>false</definition_loaded>
@@ -34101,6 +34164,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>dispatchedparts</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>home</name>
<definition_loaded>false</definition_loaded>
@@ -36192,9 +36276,56 @@
<folder_node>
<name>parts_dispatch</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>accept</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>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>accepting</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>creating</name>
<definition_loaded>false</definition_loaded>
@@ -44923,6 +45054,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>work_in_progress_jobs</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>work_in_progress_labour</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,6 +1,6 @@
import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { MdOpenInNew } from "react-icons/md";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Alert,
Divider,
@@ -12,14 +12,17 @@ import {
Switch,
Upload,
} from "antd";
import moment from "moment";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdOpenInNew } from "react-icons/md";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
@@ -28,8 +31,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import BillFormLines from "./bill-form.lines.component";
import { CalculateBillTotal } from "./bill-form.totals.utility";
import { useTreatments } from "@splitsoftware/splitio-react";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -58,6 +59,11 @@ export function BillFormComponent({
{},
bodyshop.imexshopid
);
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop.imexshopid
);
const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount);
@@ -259,6 +265,37 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (
moment(value)
.startOf("day")
.isSameOrAfter(
moment(
bodyshop.accountingconfig.ClosingPeriod[0]
).startOf("day")
) &&
moment(value)
.startOf("day")
.isSameOrBefore(
moment(
bodyshop.accountingconfig.ClosingPeriod[1]
).endOf("day")
)
) {
return Promise.resolve();
} else {
return Promise.reject(t("bills.validation.closingperiod"));
}
} else {
return Promise.resolve();
}
},
}),
]}
>
<FormDatePicker disabled={disabled} />

View File

@@ -8,7 +8,18 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
export default function JobLinesExpander({ jobline, jobid }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
fetchPolicy: "network-only",
@@ -23,7 +34,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
return (
<Row>
<Col md={24} lg={12}>
<Col md={24} lg={8}>
<Typography.Title level={4}>
{t("parts_orders.labels.parts_orders")}
</Typography.Title>
@@ -49,7 +60,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
)}
</Timeline>
</Col>
<Col md={24} lg={12}>
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Timeline>
{data.billlines.length > 0 ? (
@@ -71,7 +82,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
</Col>
<Col span={4}>
<span>
{`${t("billlines.fields.actual_cost")}: `}
{`${t("billlines.fields.actual_cost")}: `}
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
</span>
</Col>
@@ -89,6 +100,37 @@ export default function JobLinesExpander({ jobline, jobid }) {
)}
</Timeline>
</Col>
<Col md={24} lg={8}>
<Typography.Title level={4}>
{t("parts_dispatch.labels.parts_dispatch")}
</Typography.Title>
<Timeline>
{data.parts_dispatch_lines.length > 0 ? (
data.parts_dispatch_lines.map((line) => (
<Timeline.Item key={line.id}>
<Space split={<Divider type="vertical" />} wrap>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>
{line.parts_dispatch.number}
</Link>
{
bodyshop.employees.find(
(e) => e.id === line.parts_dispatch.employeeid
)?.first_name
}
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
</Space>
</Timeline.Item>
))
) : (
<Timeline.Item>
{t("parts_orders.labels.notyetordered")}
</Timeline.Item>
)}
</Timeline>
</Col>
</Row>
);
}

View File

@@ -70,7 +70,7 @@ export function JobLineDispatchButton({
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: JSON.stringify(result.errors),
error: result.errors,
}),
});
} else {
@@ -79,7 +79,7 @@ export function JobLineDispatchButton({
{
name: Templates.parts_dispatch.key,
variables: {
id: result.data.insert_part_dispatch_one.id,
id: result.data.insert_parts_dispatch_one.id,
},
},
{},
@@ -91,7 +91,7 @@ export function JobLineDispatchButton({
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: JSON.stringify(error),
error: error,
}),
});
} finally {
@@ -137,7 +137,12 @@ export function JobLineDispatchButton({
</Form.Item>
<Space wrap>
<Button type="danger" onClick={() => form.submit()} loading={loading}>
<Button
type="danger"
onClick={() => form.submit()}
loading={loading}
disabled={selectedLines.length === 0}
>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>

View File

@@ -1,4 +1,4 @@
import { notification, Select } from "antd";
import { notification, Select, Space } from "antd";
import React, { useEffect, useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
@@ -75,7 +75,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{jobline.location}
<Space wrap>
{jobline.location}
{jobline.parts_dispatch_lines.length > 0 && "-Disp"}
</Space>
</div>
);
}

View File

@@ -1,10 +1,35 @@
import { Card, Col, Row, Table } from "antd";
import { useMutation } from "@apollo/client";
import { Button, Card, Col, Row, Table, notification } from "antd";
import moment from "moment-business-days";
import React from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
export default function PartsDispatchExpander({ dispatch, job }) {
const { t } = useTranslation();
const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE);
const handleAccept = async ({ partsDispatchLineId }) => {
const accepted_at = moment();
const result = await updateDispatchLine({
variables: { id: partsDispatchLineId, line: { accepted_at } },
optimisticResponse: {
update_parts_dispatch_lines_by_pk: {
accepted_at,
id: partsDispatchLineId,
},
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.accepting", {
error: JSON.stringify(result.errors),
}),
});
}
};
const columns = [
{
@@ -28,9 +53,16 @@ export default function PartsDispatchExpander({ dispatch, job }) {
width: "20%",
//sorter: (a, b) => alphaSort(a.number, b.number),
render: (text, record) => (
<DateTimeFormatter>{record.accepted_at}</DateTimeFormatter>
),
render: (text, record) =>
record.accepted_at ? (
<DateTimeFormatter>{record.accepted_at}</DateTimeFormatter>
) : (
<Button
onClick={() => handleAccept({ partsDispatchLineId: record.id })}
>
{t("parts_dispatch.actions.accept")}
</Button>
),
},
];
return (
@@ -38,7 +70,7 @@ export default function PartsDispatchExpander({ dispatch, job }) {
<Row gutter={[16, 16]}>
<Col span={24}>
<Table
pagination={false}
rowKey={"id"}
dataSource={dispatch.parts_dispatch_lines}
columns={columns}
/>

View File

@@ -1,14 +1,14 @@
import React, { useEffect, useState } from "react";
import ShopInfoComponent from "./shop-info.component";
import { useMutation, useQuery } from "@apollo/client";
import { Form, notification } from "antd";
import { useQuery, useMutation } from "@apollo/client";
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import moment from "moment";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../graphql/bodyshop.queries";
import AlertComponent from "../alert/alert.component";
import FormsFieldChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import moment from "moment";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ShopInfoComponent from "./shop-info.component";
export default function ShopInfoContainer() {
const [form] = Form.useForm();
const { t } = useTranslation();
@@ -52,13 +52,28 @@ export default function ShopInfoContainer() {
onFinish={handleFinish}
initialValues={
data
? {
...data.bodyshops[0],
schedule_start_time: moment(
data.bodyshops[0].schedule_start_time
),
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
}
? data.bodyshops[0].accountingconfig.ClosingPeriod
? {
...data.bodyshops[0],
accountingconfig: {
...data.bodyshops[0].accountingconfig,
ClosingPeriod: [
moment(data.bodyshops[0].accountingconfig.ClosingPeriod[0]),
moment(data.bodyshops[0].accountingconfig.ClosingPeriod[1]),
],
},
schedule_start_time: moment(
data.bodyshops[0].schedule_start_time
),
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
}
: {
...data.bodyshops[0],
schedule_start_time: moment(
data.bodyshops[0].schedule_start_time
),
schedule_end_time: moment(data.bodyshops[0].schedule_end_time),
}
: null
}
>

View File

@@ -1,6 +1,8 @@
import { DeleteFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button,
DatePicker,
Form,
Input,
InputNumber,
@@ -9,8 +11,13 @@ import {
Space,
Switch,
} from "antd";
import momentTZ from "moment-timezone";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DatePickerRanges from "../../utils/DatePickerRanges";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import PhoneFormItem, {
@@ -18,12 +25,22 @@ import PhoneFormItem, {
} from "../form-items-formatted/phone-form-item.component";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import momentTZ from "moment-timezone";
const timeZonesList = momentTZ.tz.names();
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
export default function ShopInfoGeneral({ form }) {
export function ShopInfoGeneral({ form, bodyshop }) {
const { t } = useTranslation();
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop && bodyshop.imexshopid
);
return (
<div>
@@ -392,6 +409,20 @@ export default function ShopInfoGeneral({ form }) {
>
<Select mode="tags" />
</Form.Item>
{ClosingPeriod.treatment === "on" && (
<>
<Form.Item
allowClear
name={["accountingconfig", "ClosingPeriod"]}
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
ranges={DatePickerRanges}
/>
</Form.Item>
</>
)}
</LayoutFormRow>
<LayoutFormRow
header={t("bodyshop.labels.scoreboardsetup")}
@@ -604,7 +635,9 @@ export default function ShopInfoGeneral({ form }) {
</Form.Item>
<Form.Item
name={["md_email_cc", "parts_return_slip"]}
label={t("bodyshop.fields.md_email_cc", { template: "parts_return_slip" })}
label={t("bodyshop.fields.md_email_cc", {
template: "parts_return_slip",
})}
rules={[
{
//message: t("general.validation.required"),

View File

@@ -1,4 +1,8 @@
import Icon, { SearchOutlined, ScheduleOutlined } from "@ant-design/icons";
import Icon, {
SearchOutlined,
ScheduleOutlined,
CarOutlined,
} from "@ant-design/icons";
import { Layout, Menu } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -100,6 +104,15 @@ export function TechSider({
<Menu.Item key="5" disabled={!!!technician} icon={<ScheduleOutlined />}>
<Link to={`/tech/list`}>{t("menus.tech.productionlist")}</Link>
</Menu.Item>
<Menu.Item
key="dispatchedparts"
disabled={!!!technician}
icon={<CarOutlined />}
>
<Link to={`/tech/dispatchedparts`}>
{t("menus.tech.dispatchedparts")}
</Link>
</Menu.Item>
<Menu.Item
key="6"
disabled={!!!technician}

View File

@@ -2181,6 +2181,18 @@ export const GET_JOB_LINE_ORDERS = gql`
}
}
}
parts_dispatch_lines(where: { joblineid: { _eq: $joblineid } }) {
id
accepted_at
quantity
parts_dispatch {
id
employeeid
dispatched_at
dispatched_by
number
}
}
parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) {
id
act_price

View File

@@ -15,3 +15,65 @@ export const INSERT_PARTS_DISPATCH = gql`
}
}
`;
export const GET_UNACCEPTED_PARTS_DISPATCH = gql`
query GET_UNACCEPTED_PARTS_DISPATCH(
$techId: uuid!
$offset: Int
$limit: Int
) {
parts_dispatch_aggregate(
where: {
employeeid: { _eq: $techId }
parts_dispatch_lines: { accepted_at: { _is_null: true } }
}
) {
aggregate {
count(distinct: true)
}
}
parts_dispatch(
offset: $offset
limit: $limit
where: {
employeeid: { _eq: $techId }
parts_dispatch_lines: { accepted_at: { _is_null: true } }
}
) {
id
job {
id
ro_number
status
v_make_desc
v_model_desc
v_model_yr
v_color
}
dispatched_at
dispatched_by
parts_dispatch_lines {
id
accepted_at
jobline {
line_desc
id
}
quantity
joblineid
}
}
}
`;
export const UPDATE_PARTS_DISPATCH_LINE = gql`
mutation UPDATE_PARTS_DISPATCH_LINE(
$id: uuid!
$line: parts_dispatch_lines_set_input!
) {
update_parts_dispatch_lines_by_pk(pk_columns: { id: $id }, _set: $line) {
accepted_at
id
}
}
`;

View File

@@ -8,7 +8,6 @@ import {
Form,
Input,
InputNumber,
notification,
PageHeader,
Popconfirm,
Row,
@@ -17,12 +16,14 @@ import {
Statistic,
Switch,
Typography,
notification,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
//import { useHistory } from "react-router-dom";
import { useTreatments } from "@splitsoftware/splitio-react";
import Dinero from "dinero.js";
import moment from "moment";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
@@ -37,7 +38,6 @@ import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.qu
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
@@ -55,6 +55,11 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
{},
bodyshop && bodyshop.imexshopid
);
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop && bodyshop.imexshopid
);
const handleFinish = async ({ removefromproduction, ...values }) => {
setLoading(true);
@@ -254,12 +259,40 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
if (!value || moment(value).isSameOrAfter(moment(), "day")) {
return Promise.resolve();
}
return Promise.reject(
new Error(t("jobs.labels.dms.invoicedatefuture"))
);
},
}),
({ getFieldValue }) => ({
validator(_, value) {
if (
ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (
moment(value).isSameOrAfter(
moment(
bodyshop.accountingconfig.ClosingPeriod[0]
).startOf("day")
) &&
moment(value).isSameOrBefore(
moment(
bodyshop.accountingconfig.ClosingPeriod[1]
).endOf("day")
)
) {
return Promise.resolve();
} else {
return Promise.reject(
new Error(t("jobs.labels.closingperiod"))
);
}
} else {
return Promise.resolve();
}
},
}),
]}
>
<DateTimePicker

View File

@@ -0,0 +1,139 @@
import {
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Space, Table } from "antd";
import queryString from "query-string";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import PartsDispatchExpander from "../../components/parts-dispatch-expander/parts-dispatch-expander.component";
import { GET_UNACCEPTED_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
technician: selectTechnician,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export function TechDispatchedParts({ technician, bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { page } = searchParams;
const { loading, error, data, refetch } = useQuery(
GET_UNACCEPTED_PARTS_DISPATCH,
{
variables: {
techId: technician.id,
offset: page ? (page - 1) * 25 : 0,
limit: 25,
},
}
);
const { t } = useTranslation();
const history = useHistory();
if (error) return <AlertComponent message={error.message} type="error" />;
const parts_dispatch = data?.parts_dispatch;
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "job.ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
render: (text, record) => record.job.ro_number || t("general.labels.na"),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
render: (text, record) => {
return record.job.status || t("general.labels.na");
},
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => (
<span>{`${record.job.v_model_yr || ""} ${
record.job.v_make_desc || ""
} ${record.job.v_model_desc || ""}`}</span>
),
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<Button onClick={() => {}}>
{t("timetickets.actions.claimtasks")}
</Button>
),
},
];
const handleTableChange = (pagination, filters, sorter) => {
searchParams.page = pagination.current;
history.push({ search: queryString.stringify(searchParams) });
};
return (
<Card
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
</Space>
}
>
<Table
loading={loading}
pagination={{
pageSize: 25,
current: parseInt(page || 1),
total: data ? data.parts_dispatch_aggregate.aggregate.count : 0,
showSizeChanger: false,
}}
columns={columns}
rowKey="id"
dataSource={parts_dispatch}
scroll={{ x: true }}
onChange={handleTableChange}
expandable={{
expandedRowRender: (record) => (
<PartsDispatchExpander dispatch={record} />
),
rowExpandable: (record) => true,
//expandRowByClick: true,
expandIcon: ({ expanded, onExpand, record }) =>
expanded ? (
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)} />
) : (
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)} />
),
}}
/>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TechDispatchedParts);

View File

@@ -44,7 +44,10 @@ const TimeTicketModalTask = lazy(() =>
);
const TechAssignedProdJobs = lazy(() =>
import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component")
);const TechDispatchedParts = lazy(() =>
import("../tech-dispatched-parts/tech-dispatched-parts.page")
);
const { Content } = Layout;
const mapStateToProps = createStructuredSelector({
@@ -116,6 +119,11 @@ export function TechPage({ technician, match }) {
path={`${match.path}/assigned`}
component={TechAssignedProdJobs}
/>
<Route
exact
path={`${match.path}/dispatchedparts`}
component={TechDispatchedParts}
/>
</Switch>
</FeatureWrapper>
</Suspense>

View File

@@ -224,6 +224,7 @@
"reexport": "Bill marked for re-export."
},
"validation": {
"closingperiod": "This Bill Date is outside of the Closing Period.",
"inventoryquantity": "Quantity must be greater than or equal to what has been added to inventory ({{number}}).",
"manualinhouse": "Manual posting to the in house vendor is restricted. ",
"unique_invoice_number": "This invoice number has already been entered for this vendor."
@@ -263,6 +264,7 @@
"bill_local_tax_rate": "Bill - Local Tax Rate %",
"bill_state_tax_rate": "Bill - State Tax Rate %",
"city": "City",
"closingperiod": "Closing Period",
"country": "Country",
"dailybodytarget": "Scoreboard - Daily Body Target",
"dailypainttarget": "Scoreboard - Daily Paint Target",
@@ -1695,6 +1697,7 @@
"checklists": "Checklists",
"closeconfirm": "Are you sure you want to close this job? This cannot be easily undone.",
"closejob": "Close Job {{ro_number}}",
"closingperiod": "This Invoice Date is outside of the Closing Period.",
"contracts": "CC Contracts",
"convertedtolabor": "Lines Converted to Labor",
"cost": "Cost",
@@ -2000,6 +2003,7 @@
"tech": {
"assignedjobs": "Assigned Jobs",
"claimtask": "Flag Hours",
"dispatchedparts": "Dispatched Parts",
"home": "Home",
"jobclockin": "Job Clock In",
"jobclockout": "Job Clock Out",
@@ -2141,7 +2145,11 @@
}
},
"parts_dispatch": {
"actions": {
"accept": "Accept"
},
"errors": {
"accepting": "Error accepting parts dispatch. {{error}}",
"creating": "Error dispatching parts. {{error}}"
},
"fields": {
@@ -2651,6 +2659,7 @@
"timetickets_summary": "Time Tickets Summary",
"unclaimed_hrs": "Unflagged Hours",
"void_ros": "Void ROs",
"work_in_progress_jobs": "Work in Progress - Jobs",
"work_in_progress_labour": "Work in Progress - Labor",
"work_in_progress_payables": "Work in Progress - Payables"
}

View File

@@ -224,6 +224,7 @@
"reexport": ""
},
"validation": {
"closingperiod": "",
"inventoryquantity": "",
"manualinhouse": "",
"unique_invoice_number": ""
@@ -263,6 +264,7 @@
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
"city": "",
"closingperiod": "",
"country": "",
"dailybodytarget": "",
"dailypainttarget": "",
@@ -1695,6 +1697,7 @@
"checklists": "",
"closeconfirm": "",
"closejob": "",
"closingperiod": "",
"contracts": "",
"convertedtolabor": "",
"cost": "",
@@ -2000,6 +2003,7 @@
"tech": {
"assignedjobs": "",
"claimtask": "",
"dispatchedparts": "",
"home": "",
"jobclockin": "",
"jobclockout": "",
@@ -2141,7 +2145,11 @@
}
},
"parts_dispatch": {
"actions": {
"accept": ""
},
"errors": {
"accepting": "",
"creating": ""
},
"fields": {
@@ -2651,6 +2659,7 @@
"timetickets_summary": "",
"unclaimed_hrs": "",
"void_ros": "",
"work_in_progress_jobs": "",
"work_in_progress_labour": "",
"work_in_progress_payables": ""
}

View File

@@ -224,6 +224,7 @@
"reexport": ""
},
"validation": {
"closingperiod": "",
"inventoryquantity": "",
"manualinhouse": "",
"unique_invoice_number": ""
@@ -263,6 +264,7 @@
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
"city": "",
"closingperiod": "",
"country": "",
"dailybodytarget": "",
"dailypainttarget": "",
@@ -1695,6 +1697,7 @@
"checklists": "",
"closeconfirm": "",
"closejob": "",
"closingperiod": "",
"contracts": "",
"convertedtolabor": "",
"cost": "",
@@ -2000,6 +2003,7 @@
"tech": {
"assignedjobs": "",
"claimtask": "",
"dispatchedparts": "",
"home": "",
"jobclockin": "",
"jobclockout": "",
@@ -2141,7 +2145,11 @@
}
},
"parts_dispatch": {
"actions": {
"accept": ""
},
"errors": {
"accepting": "",
"creating": ""
},
"fields": {
@@ -2651,6 +2659,7 @@
"timetickets_summary": "",
"unclaimed_hrs": "",
"void_ros": "",
"work_in_progress_jobs": "",
"work_in_progress_labour": "",
"work_in_progress_payables": ""
}

View File

@@ -1556,6 +1556,19 @@ export const TemplateList = (type, context) => {
},
group: "payroll",
},
work_in_progress_jobs_excel: {
title: i18n.t("reportcenter.templates.work_in_progress_jobs"),
subject: i18n.t("reportcenter.templates.work_in_progress_jobs"),
key: "work_in_progress_jobs_excel",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "jobs",
},
work_in_progress_labour: {
title: i18n.t("reportcenter.templates.work_in_progress_labour"),
description: "",

View File

@@ -4649,6 +4649,7 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
allow_aggregations: true
update_permissions:
- role: user
permission:
@@ -4726,6 +4727,7 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
allow_aggregations: true
update_permissions:
- role: user
permission:

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "public"."parts_dispatch_employeeid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_dispatch_employeeid" on
"public"."parts_dispatch" using btree ("employeeid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "public"."parts_dispatch_dispatchid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_dispatch_dispatchid" on
"public"."parts_dispatch_lines" using btree ("partsdispatchid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "public"."parts_dispatch_line_accepted_at";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_dispatch_line_accepted_at" on
"public"."parts_dispatch_lines" using btree ("accepted_at");