Merged in release/2022-06-10 (pull request #502)
release/2022-06-10 Approved-by: Patrick Fic
This commit is contained in:
@@ -2181,6 +2181,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>existinginventoryline</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>exporting</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -17266,6 +17287,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>addtoro</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>consumefrominventory</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -15,7 +15,8 @@ export default function BillDeleteButton({ bill }) {
|
||||
setLoading(true);
|
||||
const result = await deleteBill({
|
||||
variables: { billId: bill.id },
|
||||
update(cache) {
|
||||
update(cache, { errors }) {
|
||||
if (errors) return;
|
||||
cache.modify({
|
||||
fields: {
|
||||
bills(existingBills, { readField }) {
|
||||
@@ -36,11 +37,22 @@ export default function BillDeleteButton({ bill }) {
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("bills.successes.deleted") });
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
//Check if it's an fkey violation.
|
||||
const error = JSON.stringify(result.errors);
|
||||
|
||||
if (error.toLowerCase().includes("inventory_billid_fkey")) {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.deleting", {
|
||||
error: t("bills.errors.existinginventoryline"),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bills.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
|
||||
@@ -485,22 +485,33 @@ export function BillEnterModalLinesComponent({
|
||||
|
||||
dataIndex: "actions",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
<Button disabled={disabled} onClick={() => remove(record.name)}>
|
||||
<DeleteFilled />
|
||||
</Button>
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() =>
|
||||
Simple_Inventory.treatment === "on" && (
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => (
|
||||
<Space wrap>
|
||||
<Button
|
||||
disabled={
|
||||
disabled ||
|
||||
getFieldValue("billlines")[record.fieldKey]?.inventories
|
||||
?.length > 0
|
||||
}
|
||||
onClick={() => remove(record.name)}
|
||||
>
|
||||
<DeleteFilled />
|
||||
</Button>
|
||||
{Simple_Inventory.treatment === "on" && (
|
||||
<BilllineAddInventory
|
||||
disabled={!billEdit || form.isFieldsTouched()}
|
||||
disabled={
|
||||
!billEdit ||
|
||||
form.isFieldsTouched() ||
|
||||
form.getFieldValue("is_credit_memo")
|
||||
}
|
||||
billline={getFieldValue("billlines")[record.fieldKey]}
|
||||
jobid={getFieldValue("jobid")}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Form.Item>
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -7,9 +7,11 @@ import "./bill-inventory-table.styles.scss";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
billEnterModal: selectBillEnterModal,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -17,6 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(BillInventoryTable);
|
||||
|
||||
export function BillInventoryTable({
|
||||
billEnterModal,
|
||||
bodyshop,
|
||||
form,
|
||||
billEdit,
|
||||
@@ -28,11 +31,18 @@ export function BillInventoryTable({
|
||||
useEffect(() => {
|
||||
if (inventoryData) {
|
||||
form.setFieldsValue({
|
||||
inventory: inventoryData.inventory,
|
||||
inventory: billEnterModal.context.consumeinventoryid
|
||||
? inventoryData.inventory.map((i) => {
|
||||
if (i.id === billEnterModal.context.consumeinventoryid)
|
||||
i.consumefrominventory = true;
|
||||
return i;
|
||||
})
|
||||
: inventoryData.inventory,
|
||||
});
|
||||
}
|
||||
}, [inventoryData, form]);
|
||||
}, [inventoryData, form, billEnterModal.context.consumeinventoryid]);
|
||||
|
||||
console.log(form.getFieldsValue());
|
||||
return (
|
||||
<Form.Item
|
||||
shouldUpdate={(prev, cur) => prev.vendorid !== cur.vendorid}
|
||||
|
||||
@@ -9,11 +9,14 @@ import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -24,9 +27,15 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(BillMarkExportedButton);
|
||||
|
||||
export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
|
||||
export function BillMarkExportedButton({
|
||||
currentUser,
|
||||
bodyshop,
|
||||
authLevel,
|
||||
bill,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
|
||||
const [updateBill] = useMutation(gql`
|
||||
mutation UPDATE_BILL($billId: uuid!) {
|
||||
@@ -46,6 +55,20 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
|
||||
variables: { billId: bill.id },
|
||||
});
|
||||
|
||||
await insertExportLog({
|
||||
variables: {
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: bill.id,
|
||||
successful: true,
|
||||
message: t("general.labels.markedexported"),
|
||||
useremail: currentUser.email,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({
|
||||
message: t("bills.successes.markexported"),
|
||||
@@ -69,11 +92,7 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
|
||||
|
||||
if (hasAccess)
|
||||
return (
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={bill.exported}
|
||||
onClick={handleUpdate}
|
||||
>
|
||||
<Button loading={loading} disabled={bill.exported} onClick={handleUpdate}>
|
||||
{t("bills.labels.markexported")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -64,9 +64,9 @@ export function BilllineAddInventory({
|
||||
cost_center: billline.cost_center,
|
||||
deductedfromlbr: billline.deductedfromlbr,
|
||||
applicable_taxes: {
|
||||
local: false, //billline.applicable_taxes.local,
|
||||
state: false, //billline.applicable_taxes.state,
|
||||
federal: false, // billline.applicable_taxes.federal,
|
||||
local: billline.applicable_taxes.local,
|
||||
state: billline.applicable_taxes.state,
|
||||
federal: billline.applicable_taxes.federal,
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -76,7 +76,9 @@ export function BilllineAddInventory({
|
||||
|
||||
const insertResult = await insertInventoryLine({
|
||||
variables: {
|
||||
joblineId: billline.joblineid,
|
||||
joblineId:
|
||||
billline.joblineid === "noline" ? billline.id : billline.joblineid, //This will return null as there will be no jobline that has the id of the bill line.
|
||||
//Unfortunately, we can't send null as the GQL syntax validation fails.
|
||||
joblineStatus: bodyshop.md_order_statuses.default_returned,
|
||||
inv: {
|
||||
shopid: bodyshop.id,
|
||||
@@ -99,8 +101,9 @@ export function BilllineAddInventory({
|
||||
act_price: billline.actual_price,
|
||||
cost: billline.actual_cost,
|
||||
quantity: billline.quantity,
|
||||
job_line_id: billline.joblineid,
|
||||
part_type: billline.jobline.part_type,
|
||||
job_line_id:
|
||||
billline.joblineid === "noline" ? null : billline.joblineid,
|
||||
part_type: billline.jobline && billline.jobline.part_type,
|
||||
cm_received: true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Button } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBillEnterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(InventoryBillRo);
|
||||
export function InventoryBillRo({
|
||||
bodyshop,
|
||||
setBillEnterContext,
|
||||
inventoryline,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setBillEnterContext({
|
||||
actions: {
|
||||
//refetch: refetch
|
||||
},
|
||||
context: {
|
||||
disableInvNumber: true,
|
||||
//job: { id: job.id },
|
||||
consumeinventoryid: inventoryline.id,
|
||||
bill: {
|
||||
vendorid: bodyshop.inhousevendorid,
|
||||
invoice_number: "ih",
|
||||
isinhouse: true,
|
||||
date: moment(),
|
||||
total: 0,
|
||||
|
||||
// billlines: selectedLines.map((p) => {
|
||||
// return {
|
||||
// joblineid: p.id,
|
||||
// actual_price: p.act_price,
|
||||
// actual_cost: 0, //p.act_price,
|
||||
// line_desc: p.line_desc,
|
||||
// line_remarks: p.line_remarks,
|
||||
// part_type: p.part_type,
|
||||
// quantity: p.quantity || 1,
|
||||
// applicable_taxes: {
|
||||
// local: false,
|
||||
// state: false,
|
||||
// federal: false,
|
||||
// },
|
||||
// };
|
||||
// }),
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("inventory.actions.addtoro")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -4,10 +4,11 @@ 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 { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -75,7 +76,14 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
key: "consumedbyjob",
|
||||
|
||||
ellipsis: true,
|
||||
render: (text, record) => record.bill?.job?.ro_number,
|
||||
render: (text, record) =>
|
||||
record.bill?.job?.ro_number ? (
|
||||
<Link to={`/manage/jobs/${record.bill?.job?.id}`}>
|
||||
{record.bill?.job?.ro_number}
|
||||
</Link>
|
||||
) : (
|
||||
<InventoryBillRo inventoryline={record} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount);
|
||||
|
||||
export function JobPartsQueueCount({ bodyshop, parts }) {
|
||||
export function JobPartsQueueCount({ bodyshop, parts, style }) {
|
||||
const partsStatus = useMemo(() => {
|
||||
if (!parts) return null;
|
||||
return parts.reduce(
|
||||
@@ -36,7 +36,7 @@ export function JobPartsQueueCount({ bodyshop, parts }) {
|
||||
if (!parts) return null;
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Row style={style}>
|
||||
<Col span={4}>
|
||||
<Tooltip title="Total">
|
||||
<Tag>{partsStatus.total}</Tag>
|
||||
|
||||
@@ -6,12 +6,17 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
@@ -22,9 +27,15 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(JobAdminMarkReexport);
|
||||
|
||||
export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
|
||||
export function JobAdminMarkReexport({
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
job,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
const [markJobForReexport] = useMutation(gql`
|
||||
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
@@ -101,6 +112,20 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
|
||||
variables: { jobId: job.id, date_exported: moment() },
|
||||
});
|
||||
|
||||
await insertExportLog({
|
||||
variables: {
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
jobid: job.id,
|
||||
successful: true,
|
||||
message: t("general.labels.markedexported"),
|
||||
useremail: currentUser.email,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.save") });
|
||||
insertAuditTrail({
|
||||
|
||||
@@ -4,14 +4,15 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
selectAuthLevel,
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
authLevel: selectAuthLevel,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -23,6 +24,8 @@ export default connect(
|
||||
)(BillMarkSelectedExported);
|
||||
|
||||
export function BillMarkSelectedExported({
|
||||
bodyshop,
|
||||
currentUser,
|
||||
billids,
|
||||
disabled,
|
||||
loadingCallback,
|
||||
@@ -31,7 +34,7 @@ export function BillMarkSelectedExported({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
const [updateBill] = useMutation(gql`
|
||||
mutation UPDATE_BILL($billIds: [uuid!]!) {
|
||||
update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) {
|
||||
@@ -49,9 +52,21 @@ export function BillMarkSelectedExported({
|
||||
loadingCallback(true);
|
||||
const result = await updateBill({
|
||||
variables: { billIds: billids },
|
||||
update(cache){
|
||||
|
||||
}
|
||||
update(cache) {},
|
||||
});
|
||||
|
||||
await insertExportLog({
|
||||
variables: {
|
||||
logs: billids.map((id) => {
|
||||
return {
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: id,
|
||||
successful: true,
|
||||
message: t("general.labels.markedexported"),
|
||||
useremail: currentUser.email,
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Icon from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Input, Popover } from "antd";
|
||||
import { Button, Input, Popover, Tooltip } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaRegStickyNote } from "react-icons/fa";
|
||||
@@ -69,10 +69,11 @@ export default function ProductionListColumnComment({ record }) {
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
<Icon component={FaRegStickyNote} style={{ marginRight: ".2rem" }} />
|
||||
{record.comment || " "}
|
||||
<Tooltip title={record.comment}>{record.comment || " "}</Tooltip>
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -5,25 +5,26 @@ import moment from "moment";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { TimeFormatter } from "../../utils/DateFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||
import JobAltTransportChange from "../job-at-change/job-at-change.component";
|
||||
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import ProductionListColumnAlert from "./production-list-columns.alert.component";
|
||||
import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component";
|
||||
import ProductionListColumnComment from "./production-list-columns.comment.component";
|
||||
import ProductionListDate from "./production-list-columns.date.component";
|
||||
import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component";
|
||||
import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component";
|
||||
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
|
||||
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
|
||||
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
import ProductionListColumnComment from "./production-list-columns.comment.component";
|
||||
import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
|
||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
|
||||
const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
return [
|
||||
@@ -105,6 +106,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
<ProductionListDate record={record} field="actual_in" time />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.actual_in") + " (HH:MM)",
|
||||
dataIndex: "actual_in_time",
|
||||
key: "actual_in_time",
|
||||
ellipsis: true,
|
||||
|
||||
render: (text, record) => (
|
||||
<TimeFormatter>{record.actual_in}</TimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.scheduled_completion"),
|
||||
dataIndex: "scheduled_completion",
|
||||
@@ -124,6 +135,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.scheduled_completion") + " (HH:MM)",
|
||||
dataIndex: "scheduled_completion_time",
|
||||
key: "scheduled_completion_time",
|
||||
ellipsis: true,
|
||||
|
||||
render: (text, record) => (
|
||||
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.date_last_contacted"),
|
||||
dataIndex: "date_last_contacted",
|
||||
@@ -176,6 +197,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.scheduled_delivery") + " (HH:MM)",
|
||||
dataIndex: "scheduled_delivery_time",
|
||||
key: "scheduled_delivery_time",
|
||||
ellipsis: true,
|
||||
|
||||
render: (text, record) => (
|
||||
<TimeFormatter>{record.scheduled_delivery}</TimeFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
|
||||
@@ -50,50 +50,45 @@ export default function ProductionListDate({
|
||||
"production-completion-soon"));
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Dropdown
|
||||
//trigger={["click"]}
|
||||
visible={visible}
|
||||
style={{
|
||||
height: "19px",
|
||||
}}
|
||||
overlay={
|
||||
<Card
|
||||
style={{ padding: "1rem" }}
|
||||
<Dropdown
|
||||
//trigger={["click"]}
|
||||
visible={visible}
|
||||
style={{
|
||||
height: "19px",
|
||||
}}
|
||||
overlay={
|
||||
<Card style={{ padding: "1rem" }} onClick={(e) => e.stopPropagation()}>
|
||||
<FormDatePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<FormDatePicker
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
format="MM/DD/YYYY"
|
||||
isDateOnly={!time}
|
||||
/>
|
||||
{time && (
|
||||
<TimePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
format="MM/DD/YYYY"
|
||||
isDateOnly={!time}
|
||||
minuteStep={15}
|
||||
format="hh:mm a"
|
||||
/>
|
||||
{time && (
|
||||
<TimePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
minuteStep={15}
|
||||
format="hh:mm a"
|
||||
/>
|
||||
)}
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.close")}
|
||||
</Button>
|
||||
</Card>
|
||||
}
|
||||
)}
|
||||
<Button onClick={() => setVisible(false)}>
|
||||
{t("general.actions.close")}
|
||||
</Button>
|
||||
</Card>
|
||||
}
|
||||
>
|
||||
<div
|
||||
onClick={() => setVisible(true)}
|
||||
style={{
|
||||
height: "19px",
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
<div
|
||||
onClick={() => setVisible(true)}
|
||||
style={{
|
||||
height: "19px",
|
||||
}}
|
||||
className={className}
|
||||
>
|
||||
<DateFormatter bordered={false}>{record[field]}</DateFormatter>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<DateFormatter bordered={false}>{record[field]}</DateFormatter>
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,10 @@ export function SignInComponent({
|
||||
<Form.Item
|
||||
name="email"
|
||||
rules={[
|
||||
{ required: true, message: t("general.validation.required") },
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required", { label: "Email" }),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
@@ -71,7 +74,10 @@ export function SignInComponent({
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: t("general.validation.required") },
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required", { label: "Password" }),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
|
||||
@@ -43,9 +43,9 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) {
|
||||
xs: "100%",
|
||||
sm: "100%",
|
||||
md: "100%",
|
||||
lg: "70%",
|
||||
xl: "70%",
|
||||
xxl: "70%",
|
||||
lg: "100%",
|
||||
xl: "90%",
|
||||
xxl: "85%",
|
||||
};
|
||||
const drawerPercentage = selectedBreakpoint
|
||||
? bpoints[selectedBreakpoint[0]]
|
||||
|
||||
@@ -42,7 +42,10 @@ export const UPDATE_INVENTORY_LINES = gql`
|
||||
|
||||
export const QUERY_OUTSTANDING_INVENTORY = gql`
|
||||
query QUERY_OUTSTANDING_INVENTORY {
|
||||
inventory(where: { consumedbybillid: { _is_null: true } }) {
|
||||
inventory(
|
||||
where: { consumedbybillid: { _is_null: true } }
|
||||
order_by: { line_desc: asc }
|
||||
) {
|
||||
id
|
||||
actual_cost
|
||||
actual_price
|
||||
|
||||
@@ -85,6 +85,7 @@ export const QUERY_PARTS_QUEUE = gql`
|
||||
vehicleid
|
||||
ownerid
|
||||
queued_for_parts
|
||||
comment
|
||||
joblines_status {
|
||||
count
|
||||
part_type
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
PageHeader,
|
||||
InputNumber,
|
||||
Input,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -42,7 +43,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
const [closeJob] = useMutation(UPDATE_JOB);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
const handleFinish = async ({ removefromproduction, ...values }) => {
|
||||
setLoading(true);
|
||||
const result = await client.mutate({
|
||||
mutation: generateJobLinesUpdatesForInvoicing(values.joblines),
|
||||
@@ -63,6 +64,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
kmin: values.kmin,
|
||||
kmout: values.kmout,
|
||||
dms_allocation: values.dms_allocation,
|
||||
...(removefromproduction ? { inproduction: false } : {}),
|
||||
},
|
||||
},
|
||||
refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"],
|
||||
@@ -248,6 +250,15 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
|
||||
onlyFuture={!!bodyshop.cdk_dealerid}
|
||||
/>
|
||||
</Form.Item>
|
||||
{!jobRO && (
|
||||
<Form.Item
|
||||
label={t("jobs.actions.removefromproduction")}
|
||||
name="removefromproduction"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
|
||||
<Form.Item
|
||||
label={t("jobs.fields.kmin")}
|
||||
|
||||
@@ -11,6 +11,7 @@ import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
|
||||
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
|
||||
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
|
||||
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
|
||||
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
@@ -228,9 +229,18 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
dataIndex: "partsstatus",
|
||||
key: "partsstatus",
|
||||
render: (text, record) => (
|
||||
<JobPartsQueueCount parts={record.joblines_status} />
|
||||
<JobPartsQueueCount
|
||||
style={{ minWidth: "10rem" }}
|
||||
parts={record.joblines_status}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.comment"),
|
||||
dataIndex: "comment",
|
||||
key: "comment",
|
||||
render: (text, record) => <ProductionListColumnComment record={record} />,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.queued_for_parts"),
|
||||
dataIndex: "queued_for_parts",
|
||||
@@ -247,7 +257,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
//onFilter: (value, record) => record.queued_for_parts === value,
|
||||
onFilter: (value, record) => record.queued_for_parts === value,
|
||||
render: (text, record) => (
|
||||
<JobRemoveFromPartsQueue
|
||||
checked={record.queued_for_parts}
|
||||
|
||||
@@ -6,7 +6,6 @@ export const getJobMedia = (jobid) => ({
|
||||
});
|
||||
|
||||
export const getBillMedia = ({ jobid, invoice_number }) => {
|
||||
console.log("in the action");
|
||||
return {
|
||||
type: MediaActionTypes.GET_MEDIA_FOR_BILL,
|
||||
payload: { jobid, invoice_number },
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
"errors": {
|
||||
"creating": "Error adding bill. {{error}}",
|
||||
"deleting": "Error deleting bill. {{error}}",
|
||||
"existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.",
|
||||
"exporting": "Error exporting payable(s). {{error}}",
|
||||
"exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.",
|
||||
"invalidro": "Not a valid RO.",
|
||||
@@ -1075,6 +1076,7 @@
|
||||
"inventory": {
|
||||
"actions": {
|
||||
"addtoinventory": "Add to Inventory",
|
||||
"addtoro": "Add to RO",
|
||||
"consumefrominventory": "Consume from Inventory?"
|
||||
},
|
||||
"errors": {
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"deleting": "",
|
||||
"existinginventoryline": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
@@ -1075,6 +1076,7 @@
|
||||
"inventory": {
|
||||
"actions": {
|
||||
"addtoinventory": "",
|
||||
"addtoro": "",
|
||||
"consumefrominventory": ""
|
||||
},
|
||||
"errors": {
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
"errors": {
|
||||
"creating": "",
|
||||
"deleting": "",
|
||||
"existinginventoryline": "",
|
||||
"exporting": "",
|
||||
"exporting-partner": "",
|
||||
"invalidro": "",
|
||||
@@ -1075,6 +1076,7 @@
|
||||
"inventory": {
|
||||
"actions": {
|
||||
"addtoinventory": "",
|
||||
"addtoro": "",
|
||||
"consumefrominventory": ""
|
||||
},
|
||||
"errors": {
|
||||
|
||||
@@ -17,6 +17,11 @@ export function DateTimeFormatter(props) {
|
||||
)
|
||||
: null;
|
||||
}
|
||||
export function TimeFormatter(props) {
|
||||
return props.children
|
||||
? moment(props.children).format(props.format ? props.format : "hh:mm a")
|
||||
: null;
|
||||
}
|
||||
|
||||
export function TimeAgoFormatter(props) {
|
||||
const m = moment(props.children);
|
||||
|
||||
@@ -781,33 +781,33 @@ function GenerateCostingData(job) {
|
||||
//Push adjustments to bottom line.
|
||||
if (job.adjustment_bottom_line) {
|
||||
//Add to totals.
|
||||
const Adjustment = Dinero({ amount: job.adjustment_bottom_line * -100 }); //Need to invert, since this is being assigned as a cost.
|
||||
summaryData.totalLaborCost = summaryData.totalLaborCost.add(Adjustment);
|
||||
summaryData.totalCost = summaryData.totalCost.add(Adjustment);
|
||||
const Adjustment = Dinero({ amount: job.adjustment_bottom_line * 100 }); //Need to invert, since this is being assigned as a cost.
|
||||
summaryData.totalLaborSales = summaryData.totalLaborSales.add(Adjustment);
|
||||
summaryData.totalSales = summaryData.totalSales.add(Adjustment);
|
||||
//Add to lines.
|
||||
costCenterData.push({
|
||||
id: "Adj",
|
||||
cost_center: "Adjustment",
|
||||
sale_labor: Dinero().toFormat(),
|
||||
sale_labor_dinero: Dinero(),
|
||||
sale_labor: Adjustment.toFormat(),
|
||||
sale_labor_dinero: Adjustment,
|
||||
sale_parts: Dinero().toFormat(),
|
||||
sale_parts_dinero: Dinero(),
|
||||
sale_additional: Dinero(),
|
||||
sale_additional_dinero: Dinero(),
|
||||
sale_sublet: Dinero(),
|
||||
sale_sublet_dinero: Dinero(),
|
||||
sales: Dinero().toFormat(),
|
||||
sales_dinero: Dinero(),
|
||||
sales: Adjustment.toFormat(),
|
||||
sales_dinero: Adjustment,
|
||||
cost_parts: Dinero().toFormat(),
|
||||
cost_parts_dinero: Dinero(),
|
||||
cost_labor: Adjustment.toFormat(),
|
||||
cost_labor_dinero: Adjustment,
|
||||
cost_labor: Dinero().toFormat(), //Adjustment.toFormat(),
|
||||
cost_labor_dinero: Dinero(), // Adjustment,
|
||||
cost_additional: Dinero(),
|
||||
cost_additional_dinero: Dinero(),
|
||||
cost_sublet: Dinero(),
|
||||
cost_sublet_dinero: Dinero(),
|
||||
costs: Adjustment.toFormat(),
|
||||
costs_dinero: Adjustment,
|
||||
costs: Dinero().toFormat(),
|
||||
costs_dinero: Dinero(),
|
||||
gpdollars_dinero: Dinero(),
|
||||
gpdollars: Dinero().toFormat(),
|
||||
gppercent: formatGpPercent(0),
|
||||
|
||||
Reference in New Issue
Block a user