Compare commits

..

4 Commits

Author SHA1 Message Date
Patrick Fic
f0d6c5e1b1 IO-233 CDK WIP 2021-07-06 09:31:01 -07:00
Patrick Fic
84b39f3d2b IO-233 WIP CDK 2021-07-02 12:53:18 -07:00
Patrick Fic
4ab0947cc8 IO-233 Add customer insert actions. 2021-06-30 16:04:01 -07:00
Patrick Fic
105ecd4221 IO-233 CDK 2021-06-30 13:41:00 -07:00
156 changed files with 6835 additions and 5236 deletions

View File

@@ -1 +1 @@
client_max_body_size 50M;
client_max_body_size 15M;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -4,39 +4,39 @@
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"@apollo/client": "^3.3.21",
"@craco/craco": "^6.2.0",
"@fingerprintjs/fingerprintjs": "^3.2.0",
"@apollo/client": "^3.3.17",
"@craco/craco": "^5.9.0",
"@fingerprintjs/fingerprintjs": "^3.1.2",
"@lourenci/react-kanban": "^2.1.0",
"@sentry/react": "^6.10.0",
"@sentry/tracing": "^6.10.0",
"@sentry/react": "^6.3.6",
"@sentry/tracing": "^6.3.6",
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.16.0",
"@tanem/react-nprogress": "^3.0.74",
"antd": "^4.16.8",
"@stripe/stripe-js": "^1.14.0",
"@tanem/react-nprogress": "^3.0.65",
"antd": "^4.15.5",
"apollo-link-logger": "^2.0.0",
"axios": "^0.21.1",
"craco-less": "^1.18.0",
"dinero.js": "^1.9.0",
"dotenv": "^10.0.0",
"craco-less": "^1.17.1",
"dinero.js": "^1.8.1",
"dotenv": "^9.0.2",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.2",
"firebase": "^8.7.1",
"graphql": "^15.5.1",
"i18next": "^20.3.4",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.2",
"exifr": "^7.0.0",
"firebase": "^8.6.0",
"graphql": "^15.5.0",
"i18next": "^20.2.2",
"i18next-browser-languagedetector": "^6.1.1",
"jsoneditor": "^9.4.1",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.22",
"libphonenumber-js": "^1.9.17",
"logrocket": "^1.2.0",
"markerjs2": "^2.9.0",
"markerjs2": "^2.8.1",
"moment-business-days": "^1.2.0",
"phone": "^3.1.2",
"phone": "^2.4.21",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.1",
"rc-queue-anim": "^2.0.0",
"query-string": "^7.0.0",
"rc-queue-anim": "^1.8.5",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.33.2",
@@ -45,26 +45,26 @@
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.2.5",
"react-i18next": "^11.11.3",
"react-i18next": "^11.8.15",
"react-icons": "^4.2.0",
"react-number-format": "^4.6.4",
"react-number-format": "^4.5.5",
"react-redux": "^7.2.4",
"react-resizable": "^3.0.4",
"react-resizable": "^3.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.0.10",
"recharts": "^2.0.7",
"redux": "^4.1.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.35.2",
"socket.io-client": "^4.1.3",
"sass": "^1.32.13",
"socket.io-client": "^4.1.2",
"styled-components": "^5.3.0",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^2.1.0",
"web-vitals": "^1.1.2",
"workbox-background-sync": "^6.1.5",
"workbox-broadcast-update": "^6.1.5",
"workbox-cacheable-response": "^6.1.5",

View File

@@ -26,8 +26,6 @@ import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-bu
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -35,8 +33,6 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
@@ -44,10 +40,7 @@ export default connect(
mapDispatchToProps
)(BillDetailEditcontainer);
export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
}) {
export function BillDetailEditcontainer({ setPartsOrderContext }) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
const { t } = useTranslation();
@@ -141,12 +134,6 @@ export function BillDetailEditcontainer({
});
await Promise.all(updates);
insertAuditTrail({
jobid: bill.jobid,
billid: search.billid,
operation: AuditTrailMapping.billupdated(bill.invoice_number),
});
await refetch();
form.setFieldsValue(transformData(data));
form.resetFields();

View File

@@ -11,14 +11,12 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container";
import { handleUpload } from "../documents-upload/documents-upload.utility";
@@ -29,8 +27,6 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
function BillEnterModalContainer({
@@ -38,7 +34,6 @@ function BillEnterModalContainer({
toggleModalVisible,
bodyshop,
currentUser,
insertAuditTrail,
}) {
const [form] = Form.useForm();
const { t } = useTranslation();
@@ -86,9 +81,8 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
console.log("adjustmentsToInsert", adjustmentsToInsert);
const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them.
@@ -121,12 +115,7 @@ function BillEnterModalContainer({
message: JSON.stringify(jobUpdate.errors),
}),
});
return;
}
insertAuditTrail({
jobid: values.jobid,
operation: AuditTrailMapping.jobmodifylbradj(),
});
}
if (!!r1.errors) {
@@ -182,12 +171,6 @@ function BillEnterModalContainer({
});
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
});
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ billlines: [] });

View File

@@ -5,7 +5,6 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
@@ -15,7 +14,6 @@ import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
//jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -28,7 +26,6 @@ const mapDispatchToProps = (dispatch) => ({
});
export function BillsListTableComponent({
bodyshop,
job,
billsQuery,
handleOnRowClick,
@@ -55,9 +52,7 @@ export function BillsListTableComponent({
)}
<BillDeleteButton bill={record} />
<Button
disabled={
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
disabled={record.is_credit_memo}
onClick={() =>
setPartsOrderContext({
actions: {},

View File

@@ -34,9 +34,7 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
let dailySales;
if (!!jobsByDate[val]) {
dailySales = jobsByDate[val].reduce((dayAcc, dayVal) => {
return dayAcc.add(
Dinero((dayVal.job_totals && dayVal.job_totals.totals.subtotal) || 0)
);
return dayAcc.add(Dinero(dayVal.job_totals.totals.subtotal));
}, Dinero());
} else {
dailySales = Dinero();

View File

@@ -13,14 +13,7 @@ export default function DashboardProjectedMonthlySales({ data, ...cardProps }) {
const dollars =
data.projected_monthly_sales &&
data.projected_monthly_sales.reduce(
(acc, val) =>
acc.add(
Dinero(
val.job_totals &&
val.job_totals.totals &&
val.job_totals.totals.subtotal
)
),
(acc, val) => acc.add(Dinero(val.job_totals.totals.subtotal)),
Dinero()
);
return (

View File

@@ -14,8 +14,7 @@ export default function DashboardTotalProductionDollars({
const dollars =
data.production_jobs &&
data.production_jobs.reduce(
(acc, val) =>
acc.add(Dinero(val.job_totals && val.job_totals.totals.subtotal)),
(acc, val) => acc.add(Dinero(val.job_totals.totals.subtotal)),
Dinero()
);

View File

@@ -0,0 +1,77 @@
import { Button, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { socket } from "../../pages/dms/dms.container";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { alphaSort } from "../../utils/sorters";
export default function DmsCustomerSelector() {
const { t } = useTranslation();
const [customerList, setcustomerList] = useState([]);
const [visible, setVisible] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
socket.on("cdk-select-customer", (customerList, callback) => {
setVisible(true);
setcustomerList(customerList);
});
const onOk = () => {
setVisible(false);
socket.emit("cdk-selected-customer", selectedCustomer);
};
const columns = [
{
title: t("dms.fields.name1"),
dataIndex: ["name1", "fullName"],
key: "name1",
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
},
{
title: t("dms.fields.name2"),
dataIndex: ["name2", "fullName"],
key: "name2",
sorter: (a, b) => alphaSort(a.name2?.fullName, b.name2?.fullName),
},
{
title: t("dms.fields.phone"),
dataIndex: ["contactInfo", "mainTelephoneNumber", "value"],
key: "phone",
render: (record, value) => (
<PhoneFormatter>
{record.contactInfo?.mainTelephoneNumber?.value}
</PhoneFormatter>
),
},
{
title: t("dms.fields.address"),
//dataIndex: ["name2", "fullName"],
key: "address",
render: (record, value) =>
`${record.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`,
},
];
if (!visible) return <></>;
return (
<Table
title={() => (
<div>
<Button onClick={onOk}>Select</Button>
</div>
)}
pagination={{ position: "top" }}
columns={columns}
rowKey={(record) => record.id.value}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (props) => {
setSelectedCustomer(props.id.value);
},
type: "radio",
selectedRowKeys: [selectedCustomer],
}}
/>
);
}

View File

@@ -37,8 +37,6 @@ export default function EmailOverlayComponent({ form, selectedMediaState }) {
</Form.Item>
<Divider>{t("emails.labels.preview")}</Divider>
<strong>{t("emails.labels.pdfcopywillbeattached")}</strong>
<Form.Item shouldUpdate>
{() => {
return (

View File

@@ -43,10 +43,6 @@ export function EmailOverlayContainer({
const [loading, setLoading] = useState(false);
const [sending, setSending] = useState(false);
const [rawHtml, setRawHtml] = useState("");
const [pdfCopytoAttach, setPdfCopytoAttach] = useState({
filename: null,
pdf: null,
});
const [selectedMedia, setSelectedMedia] = useState([]);
const defaultEmailFrom = {
@@ -63,17 +59,17 @@ export function EmailOverlayContainer({
const handleFinish = async (values) => {
logImEXEvent("email_send_from_modal");
//const attachments = [];
const attachments = [];
// if (values.fileList)
// await asyncForEach(values.fileList, async (f) => {
// const t = {
// ContentType: f.type,
// Filename: f.name,
// Base64Content: (await toBase64(f.originFileObj)).split(",")[1],
// };
// attachments.push(t);
// });
if (values.fileList)
await asyncForEach(values.fileList, async (f) => {
const t = {
ContentType: f.type,
Filename: f.name,
Base64Content: (await toBase64(f.originFileObj)).split(",")[1],
};
attachments.push(t);
});
setSending(true);
try {
@@ -81,28 +77,11 @@ export function EmailOverlayContainer({
...defaultEmailFrom,
...values,
html: rawHtml,
attachments: [
...(values.fileList
? await Promise.all(
values.fileList.map(async (f) => {
return {
filename: f.name,
path: await toBase64(f.originFileObj),
};
})
)
: []),
...(pdfCopytoAttach.pdf
? [
{
path: pdfCopytoAttach.pdf,
filename:
pdfCopytoAttach.filename &&
`${pdfCopytoAttach.filename}.pdf`,
},
]
: []),
],
attachments:
values.fileList &&
(await Promise.all(
values.fileList.map(async (f) => await toBase64(f.originFileObj))
)),
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.src),
//attachments,
});
@@ -120,22 +99,13 @@ export function EmailOverlayContainer({
const render = async () => {
logImEXEvent("email_render_template", { template: emailConfig.template });
setLoading(true);
let { html, pdf, filename } = await RenderTemplate(
emailConfig.template,
bodyshop,
true
);
let html = await RenderTemplate(emailConfig.template, bodyshop, true);
const response = await axios.post("/render/inlinecss", {
html: html,
url: `${window.location.protocol}://${window.location.host}/`,
});
setRawHtml(response.data);
if (pdf) {
setPdfCopytoAttach({ pdf, filename });
}
form.setFieldsValue({
...emailConfig.messageOptions,
cc:
@@ -196,8 +166,8 @@ const toBase64 = (file) =>
reader.onerror = (error) => reject(error);
});
// const asyncForEach = async (array, callback) => {
// for (let index = 0; index < array.length; index++) {
// await callback(array[index], index, array);
// }
// };
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};

View File

@@ -45,7 +45,7 @@ export default function GlobalSearch() {
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
job.v_model_desc || ""
}`}</span>
<span>{`${job.clm_no || ""}`}</span>
<span>{`${job.clm_no}`}</span>
</Space>
</Link>
),
@@ -91,8 +91,8 @@ export default function GlobalSearch() {
vehicle.v_make_desc || ""
} ${vehicle.v_model_desc || ""}`}
</span>
<span>{vehicle.plate_no || ""}</span>
<span> {vehicle.v_vin || ""}</span>
<span>{vehicle.plate_no}</span>
<span> {vehicle.v_vin}</span>
</Space>
</Link>
),
@@ -108,11 +108,10 @@ export default function GlobalSearch() {
label: (
<Link to={`/manage/jobs/${payment.job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{payment.paymentnum}</span>
<span>{payment.job.ro_number}</span>
<span>{payment.memo || ""}</span>
<span>{payment.amount || ""}</span>
<span>{payment.transactionid || ""}</span>
<span>{payment.job.memo}</span>
<span>{payment.job.amount}</span>
<span>{payment.job.transactionid}</span>
</Space>
</Link>
),

View File

@@ -161,7 +161,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
<Input />
</Form.Item>
<Form.Item
label={t("printcenter.jobs.3rdpartyfields.refnumber")}
label={t("printcenter.jobs.3rdpartyfields.ponumber")}
name="ponumber"
>
<Input />

View File

@@ -1,50 +1,24 @@
import { AlertFilled } from "@ant-design/icons";
import {
Button,
Divider,
Dropdown,
Menu,
notification,
Popover,
Space,
} from "antd";
import parsePhoneNumber from "libphonenumber-js";
import moment from "moment";
import queryString from "query-string";
import { Button, Popover, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import {
openChatByPhone,
setMessage,
} from "../../redux/messaging/messaging.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component";
import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component";
import queryString from "query-string";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setScheduleContext: (context) =>
dispatch(setModalContext({ context: context, modal: "schedule" })),
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
});
export function ScheduleEventComponent({
bodyshop,
setMessage,
openChatByPhone,
event,
refetch,
handleCancel,
@@ -64,7 +38,7 @@ export function ScheduleEventComponent({
);
const popoverContent = (
<div style={{ maxWidth: "40vw" }}>
<div>
{!event.isintake ? (
<strong>{event.title}</strong>
) : (
@@ -101,19 +75,17 @@ export function ScheduleEventComponent({
{(event.job && event.job.ownr_ea) || ""}
</DataLabel>
<DataLabel label={t("jobs.fields.ownr_ph1")}>
<ChatOpenButton
phone={event.job && event.job.ownr_ph1}
jobid={event.job.id}
/>
<PhoneFormatter>
{(event.job && event.job.ownr_ph1) || ""}
</PhoneFormatter>
</DataLabel>
<DataLabel label={t("jobs.fields.alt_transport")}>
{(event.job && event.job.alt_transport) || ""}
<ScheduleAtChange job={event && event.job} />
</DataLabel>
<ScheduleEventNote event={event} />
</div>
) : null}
<Divider />
<Space wrap>
{event.job ? (
<Link to={`/manage/jobs/${event.job && event.job.id}`}>
@@ -134,62 +106,23 @@ export function ScheduleEventComponent({
{t("appointments.actions.preview")}
</Button>
) : null}
<Dropdown
overlay={
<Menu>
<Menu.Item
onClick={() => {
const Template = TemplateList("job").appointment_reminder;
GenerateDocument(
{
name: Template.key,
variables: { id: event.job.id },
},
{
to: event.job && event.job.ownr_ea,
subject: Template.subject,
},
"e",
event.job && event.job.id
);
}}
disabled={event.arrived}
>
{t("general.labels.email")}
</Menu.Item>
<Menu.Item
onClick={() => {
const p = parsePhoneNumber(event.job.ownr_ph1, "CA");
if (p && p.isValid()) {
openChatByPhone({
phone_num: p.formatInternational(),
jobid: event.job.id,
});
setMessage(
t("appointments.labels.reminder", {
shopname: bodyshop.shopname,
date: moment(event.start).format("MM/DD/YYYY"),
time: moment(event.start).format("HH:MM a"),
})
);
setVisible(false);
} else {
notification["error"]({
message: t("messaging.error.invalidphone"),
});
}
}}
disabled={event.arrived || !bodyshop.messagingservicesid}
>
{t("general.labels.sms")}
</Menu.Item>
</Menu>
}
<Button
onClick={() => {
const Template = TemplateList("job").appointment_reminder;
GenerateDocument(
{
name: Template.key,
variables: { id: event.job.id },
},
{ to: event.job && event.job.ownr_ea, subject: Template.subject },
"e",
event.job && event.job.id
);
}}
disabled={event.arrived}
>
<Button>{t("appointments.actions.sendreminder")}</Button>
</Dropdown>
{t("appointments.actions.sendreminder")}
</Button>
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
{t("appointments.actions.cancel")}
</Button>
@@ -228,7 +161,6 @@ export function ScheduleEventComponent({
const RegularEvent = event.isintake ? (
<div style={{ display: "flex", flexWrap: "wrap" }}>
<Space>
{event.note && <AlertFilled className="production-alert" />}
<strong>{`${event.job.ro_number || t("general.labels.na")}`}</strong>
<span>{`${(event.job && event.job.ownr_fn) || ""} ${
(event.job && event.job.ownr_ln) || ""
@@ -270,7 +202,4 @@ export function ScheduleEventComponent({
</Popover>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScheduleEventComponent);
export default connect(null, mapDispatchToProps)(ScheduleEventComponent);

View File

@@ -1,74 +0,0 @@
import { EditFilled, SaveFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Input, notification, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DataLabel from "../data-label/data-label.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function ScheduleEventNote({ event }) {
const [editing, setEditing] = useState(false);
const [note, setNote] = useState(event.note || "");
const [loading, setLoading] = useState(false);
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
const { t } = useTranslation();
const toggleEdit = async () => {
if (editing) {
//Await the update
setLoading(true);
const result = await updateAppointment({
variables: {
appid: event.id,
app: { note },
},
});
if (!!!result.errors) {
// notification["success"]({ message: t("appointments.successes.saved") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setEditing(false);
} else {
setEditing(true);
}
setLoading(false);
};
return (
<DataLabel label={t("appointments.fields.note")}>
<Space flex>
{!editing ? (
event.note || ""
) : (
<Input.TextArea
rows={3}
value={note}
onChange={(e) => setNote(e.target.value)}
style={{ maxWidth: "8vw" }}
/>
)}
<Button onClick={toggleEdit} loading={loading}>
{editing ? <SaveFilled /> : <EditFilled />}
</Button>
</Space>
</DataLabel>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ScheduleEventNote);

View File

@@ -1,46 +0,0 @@
import { useQuery } from "@apollo/client";
import { Card, Table } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
export default function JobAuditTrail({ jobId }) {
const { t } = useTranslation();
const { loading, data } = useQuery(QUERY_AUDIT_TRAIL, {
variables: { jobid: jobId },
skip: !jobId,
});
const columns = [
{
title: t("audit.fields.created"),
dataIndex: "created",
key: "created",
render: (text, record) => (
<DateTimeFormatter>{record.created}</DateTimeFormatter>
),
},
{
title: t("audit.fields.useremail"),
dataIndex: "useremail",
key: "useremail",
},
{
title: t("audit.fields.operation"),
dataIndex: "operation",
key: "operation",
},
];
return (
<Card title={t("jobs.labels.audit")}>
<Table
loading={loading}
columns={columns}
rowKey="id"
dataSource={data ? data.audit_trail : []}
/>
</Card>
);
}

View File

@@ -16,20 +16,16 @@ import {
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
import moment from "moment-business-days";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function JobChecklistForm({
insertAuditTrail,
formItems,
bodyshop,
currentUser,
@@ -63,12 +59,6 @@ export function JobChecklistForm({
production_vars: {
...job.production_vars,
...values.production_vars,
note:
values.production_vars &&
values.production_vars.note &&
values.production_vars.note !== ""
? job.production_vars && values.production_vars.note
: job.production_vars && job.production_vars.note,
},
}),
...(type === "intake" && {
@@ -119,17 +109,6 @@ export function JobChecklistForm({
if (!!!result.errors) {
notification["success"]({ message: t("checklist.successes.completed") });
history.push(`/manage/jobs/${jobId}`);
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.jobchecklist(
type,
(type === "deliver" && values.removeFromProduction && false) ||
(type === "intake" && values.addToProduction),
(type === "intake" && bodyshop.md_ro_statuses.default_arrived) ||
(type === "deliver" && bodyshop.md_ro_statuses.default_delivered)
),
});
} else {
notification["error"]({
message: t("checklist.errors.complete", {

View File

@@ -295,18 +295,18 @@ export function JobLinesComponent({
onClick={async () => {
await deleteJobLine({
variables: { joblineId: record.id },
// update(cache) {
// cache.modify({
// id: cache.identify(job),
// fields: {
// joblines(existingJobLines, { readField }) {
// return existingJobLines.filter(
// (jlRef) => record.id !== readField("id", jlRef)
// );
// },
// },
// });
// },
update(cache) {
cache.modify({
id: cache.identify(job),
fields: {
joblines(existingJobLines, { readField }) {
return existingJobLines.filter(
(jlRef) => record.id !== readField("id", jlRef)
);
},
},
});
},
});
await axios.post("/job/totalsssu", {
id: job.id,

View File

@@ -37,8 +37,8 @@ export function JobEmployeeAssignments({
});
const [visibility, setVisibility] = useState(false);
const onChange = (value, option) => {
setAssignment({ ...assignment, employeeid: value, name: option.name });
const onChange = (e) => {
setAssignment({ ...assignment, employeeid: e });
};
const popContent = (
@@ -56,11 +56,7 @@ export function JobEmployeeAssignments({
}
>
{bodyshop.employees.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
<Select.Option value={emp.id} key={emp.id}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}

View File

@@ -6,34 +6,14 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_ASSIGNMENTS } from "../../graphql/jobs.queries";
import JobEmployeeAssignmentsComponent from "./job-employee-assignments.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobEmployeeAssignmentsContainer);
export function JobEmployeeAssignmentsContainer({
job,
refetch,
insertAuditTrail,
}) {
export default function JobEmployeeAssignmentsContainer({ job, refetch }) {
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB_ASSIGNMENTS);
const [loading, setLoading] = useState(false);
const handleAdd = async (assignment) => {
setLoading(true);
const { operation, employeeid, name } = assignment;
const { operation, employeeid } = assignment;
logImEXEvent("job_assign_employee", { operation });
let empAssignment = determineFieldName(operation);
@@ -43,11 +23,6 @@ export function JobEmployeeAssignmentsContainer({
});
if (refetch) refetch();
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentchange(operation, name),
});
if (!!result.errors) {
notification["error"]({
message: t("jobs.errors.assigning", {
@@ -73,10 +48,6 @@ export function JobEmployeeAssignmentsContainer({
}),
});
}
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentremoved(operation),
});
setLoading(false);
};

View File

@@ -1,5 +1,4 @@
import { Button, Card, Space, Table } from "antd";
import { EditFilled } from "@ant-design/icons";
import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -116,29 +115,16 @@ export function JobPayments({
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<Space wrap>
<Button
disabled={record.exportedat}
onClick={() => {
setPaymentContext({
actions: { refetch: refetch },
context: record,
});
}}
>
<EditFilled />
</Button>
<PrintWrapperComponent
templateObject={{
name: TemplateList("payment").payment_receipt.key,
variables: { id: record.id },
}}
messageObject={{
to: job.ownr_ea,
}}
id={job.id}
/>
</Space>
<PrintWrapperComponent
templateObject={{
name: TemplateList("payment").payment_receipt.key,
variables: { id: record.id },
}}
messageObject={{
to: job.ownr_ea,
}}
id={job.id}
/>
),
},
];

View File

@@ -1,4 +1,4 @@
import { Checkbox, Table, Typography } from "antd";
import { Checkbox, PageHeader, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -21,7 +21,6 @@ export default function JobReconciliationBillsTable({
title: t("billlines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
width: "35%",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
@@ -30,8 +29,6 @@ export default function JobReconciliationBillsTable({
title: t("billlines.labels.from"),
dataIndex: "from",
key: "from",
width: "20%",
ellipsis: true,
render: (text, record) =>
`${record.bill.vendor && record.bill.vendor.name} / ${
record.bill.invoice_number
@@ -60,7 +57,7 @@ export default function JobReconciliationBillsTable({
),
},
{
title: t("joblines.fields.part_qty"),
title: t("billlines.fields.quantity"),
dataIndex: "quantity",
key: "quantity",
sorter: (a, b) => a.quantity - b.quantity,
@@ -89,12 +86,10 @@ export default function JobReconciliationBillsTable({
};
return (
<div>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<PageHeader title={t("bills.labels.bills")}>
<Table
pagination={false}
size="small"
scroll={{ y: "80vh", x: true }}
scroll={{ y: "40vh", x: true }}
columns={columns}
rowKey="id"
dataSource={invoiceLineData}
@@ -104,6 +99,6 @@ export default function JobReconciliationBillsTable({
selectedRowKeys: selectedLines,
}}
/>
</div>
</PageHeader>
);
}

View File

@@ -22,23 +22,21 @@ export default function JobReconciliationModalComponent({ job, bills }) {
);
return (
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
<div style={{ flex: 1 }}>
<Row gutter={8}>
<Col span={12}>
<JobReconciliationPartsTable
jobLineData={jobLineData}
jobLineState={jobLineState}
/>
</Col>
<Col span={12}>
<JobReconciliationBillsTable
invoiceLineData={invoiceLineData}
billLineState={billLineState}
/>
</Col>
</Row>
</div>
<div>
<Row gutter={[16, 16]}>
<Col span={12}>
<JobReconciliationPartsTable
jobLineData={jobLineData}
jobLineState={jobLineState}
/>
</Col>
<Col span={12}>
<JobReconciliationBillsTable
invoiceLineData={invoiceLineData}
billLineState={billLineState}
/>
</Col>
</Row>
<Row>
<JobReconciliationTotals
jobLines={jobLineData}

View File

@@ -1,12 +0,0 @@
.imex-reconciliation-modal {
top: 20px;
.ant-modal-content {
height: 95vh;
display: flex;
flex-direction: column;
.ant-modal-body {
display: flex;
flex: 1;
}
}
}

View File

@@ -10,7 +10,6 @@ import { selectReconciliation } from "../../redux/modals/modals.selectors";
import JobReconciliationModalComponent from "./job-reconciliation-modal.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import "./job-reconciliation-modal.styles.scss";
const mapStateToProps = createStructuredSelector({
reconciliationModal: selectReconciliation,
@@ -39,23 +38,23 @@ function JobReconciliationModalContainer({
return (
<Modal
title={t("jobs.labels.reconciliationheader")}
width={"95%"}
width={"90%"}
visible={visible}
okText={t("general.actions.close")}
onOk={handleCancel}
onCancel={handleCancel}
cancelButtonProps={{ display: "none" }}
destroyOnClose
className="imex-reconciliation-modal"
>
{loading && <LoadingSpinner loading={loading} />}
{error && <AlertComponent message={error.message} type="error" />}
{data && (
<JobReconciliationModalComponent
job={data && data.jobs_by_pk}
bills={data && data.bills}
/>
)}
<LoadingSpinner loading={loading}>
{error && <AlertComponent message={error.message} type="error" />}
{data && (
<JobReconciliationModalComponent
job={data && data.jobs_by_pk}
bills={data && data.bills}
/>
)}
</LoadingSpinner>
</Modal>
);
}

View File

@@ -1,4 +1,4 @@
import { Table, Typography } from "antd";
import { PageHeader, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -102,13 +102,11 @@ export default function JobReconcilitionPartsTable({
};
return (
<div>
<Typography.Title level={4}>{t("jobs.labels.lines")}</Typography.Title>
<PageHeader title={t("jobs.labels.lines")}>
<Table
pagination={false}
columns={columns}
size="small"
scroll={{ y: "80vh", x: true }}
scroll={{ y: "40vh", x: true }}
rowKey="id"
dataSource={jobLineData}
onChange={handleTableChange}
@@ -124,6 +122,6 @@ export default function JobReconcilitionPartsTable({
<div style={{ fontStyle: "italic", margin: "4px" }}>
{t("jobs.labels.reconciliation.removedpartsstrikethrough")}
</div>
</div>
</PageHeader>
);
}

View File

@@ -1,6 +1,6 @@
import { LoadingOutlined } from "@ant-design/icons";
import { useLazyQuery } from "@apollo/client";
import { Empty, Select, Space, Tag } from "antd";
import { Empty, Select } from "antd";
import _ from "lodash";
import React, { forwardRef, useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -15,7 +15,6 @@ const JobSearchSelect = (
{
disabled,
convertedOnly = false,
notInvoiced = false,
notExported = true,
clm_no = false,
...restProps
@@ -31,7 +30,6 @@ const JobSearchSelect = (
variables: {
...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
},
}
: {}),
@@ -82,20 +80,13 @@ const JobSearchSelect = (
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id} status={o.status}>
<Space align="center">
<span>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
o.ro_number || t("general.labels.na")
} | ${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
o.v_model_desc || ""
}`}
</span>
<Tag>
<strong>{o.status}</strong>
</Tag>
</Space>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
o.ro_number || t("general.labels.na")
} | ${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
o.v_model_desc || ""
}`}
</Option>
))
: null}

View File

@@ -1,57 +0,0 @@
import { DownCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Dropdown, Menu, notification } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
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)(JobsAdminStatus);
export function JobsAdminStatus({ bodyshop, job }) {
const { t } = useTranslation();
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
const updateJobStatus = (status) => {
mutationUpdateJobstatus({
variables: { jobId: job.id, status: status },
})
.then((r) => {
notification["success"]({ message: t("jobs.successes.save") });
// refetch();
})
.catch((error) => {
notification["error"]({ message: t("jobs.errors.saving") });
});
};
const statusmenu = (
<Menu
onClick={(e) => {
updateJobStatus(e.key);
}}
>
{bodyshop.md_ro_statuses.statuses.map((item) => (
<Menu.Item key={item}>{item}</Menu.Item>
))}
</Menu>
);
return (
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
<Button shape="round">
<span>{job.status}</span>
<DownCircleFilled />
</Button>
</Dropdown>
);
}

View File

@@ -6,8 +6,8 @@ import { useTranslation } from "react-i18next";
export default function JobAdminDeleteIntake({ job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [deleteIntake] = useMutation(gql`
mutation DELETE_INTAKE($jobId: uuid!) {
const [updateJob] = useMutation(gql`
mutation UPDATE_JOB($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { intakechecklist: null }
@@ -18,39 +18,9 @@ export default function JobAdminDeleteIntake({ job }) {
}
`);
const [DELETE_DELIVERY] = useMutation(gql`
mutation DELETE_DELIVERY($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { deliverychecklist: null }
) {
id
deliverychecklist
}
}
`);
const handleDelete = async (values) => {
setLoading(true);
const result = await deleteIntake({
variables: { jobId: job.id },
});
if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
const handleDeleteDelivery = async (values) => {
setLoading(true);
const result = await DELETE_DELIVERY({
const result = await updateJob({
variables: { jobId: job.id },
});
@@ -64,16 +34,12 @@ export default function JobAdminDeleteIntake({ job }) {
});
}
setLoading(false);
//Get the owner details, populate it all back into the job.
};
return (
<>
<Button loading={loading} onClick={handleDelete}>
{t("jobs.labels.deleteintake")}
</Button>
<Button loading={loading} onClick={handleDeleteDelivery}>
{t("jobs.labels.deletedelivery")}
</Button>
</>
<Button loading={loading} onClick={handleDelete}>
{t("jobs.labels.deleteintake")}
</Button>
);
}

View File

@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
@@ -22,8 +21,8 @@ export default connect(
export function JobAdminMarkReexport({ bodyshop, job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [markJobForReexport] = useMutation(gql`
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
const [updateJob] = useMutation(gql`
mutation UPDATE_JOB($jobId: uuid!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: null
@@ -31,84 +30,14 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
}
) {
id
date_exported
status
date_invoiced
intakechecklist
}
}
`);
const [markJobExported] = useMutation(gql`
mutation MARK_JOB_AS_EXPORTED($jobId: uuid!, $date_exported: timestamptz!) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: $date_exported
status: "${bodyshop.md_ro_statuses.default_exported}"
}
) {
id
date_exported
date_invoiced
status
}
}
`);
const [markJobUninvoiced] = useMutation(gql`
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, ) {
update_jobs_by_pk(
pk_columns: { id: $jobId }
_set: { date_exported: null
date_invoiced: null
status: "${bodyshop.md_ro_statuses.default_delivered}"
}
) {
id
date_exported
date_invoiced
status
}
}
`);
const handleMarkForExport = async () => {
const handleUpdate = async (values) => {
setLoading(true);
const result = await markJobForReexport({
variables: { jobId: job.id },
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
const handleMarkExported = async () => {
setLoading(true);
const result = await markJobExported({
variables: { jobId: job.id, date_exported: moment() },
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
const handleUninvoice = async () => {
setLoading(true);
const result = await markJobUninvoiced({
const result = await updateJob({
variables: { jobId: job.id },
});
@@ -122,31 +51,16 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
});
}
setLoading(false);
//Get the owner details, populate it all back into the job.
};
return (
<>
<Button
loading={loading}
disabled={!job.date_exported}
onClick={handleMarkForExport}
>
{t("jobs.labels.markforreexport")}
</Button>
<Button
loading={loading}
disabled={job.date_exported}
onClick={handleMarkExported}
>
{t("jobs.actions.markasexported")}
</Button>
<Button
loading={loading}
disabled={!job.date_invoiced || job.date_exported}
onClick={handleUninvoice}
>
{t("jobs.actions.uninvoice")}
</Button>
</>
<Button
loading={loading}
disabled={!job.date_exported}
onClick={handleUpdate}
>
{t("jobs.labels.markforreexport")}
</Button>
);
}

View File

@@ -11,7 +11,6 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
query: GET_ALL_JOBLINES_BY_PK,
variables: { id: jobId },
});
const existingLines = _.cloneDeep(existingLinesFromDb);
const linesToInsert = [];
const linesToUpdate = [];
@@ -20,14 +19,11 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
const matchingIndex = existingLines.findIndex(
(eL) => eL.unq_seq === newLine.unq_seq
);
//Should do a check to make sure there is only 1 matching unq sequence number.
if (matchingIndex >= 0) {
//Found a relevant matching line. Add it to lines to update.
linesToUpdate.push({
id: existingLines[matchingIndex].id,
newData: { ...newLine, removed: false },
newData: newLine,
});
//Splice out item we found for performance.

View File

@@ -57,7 +57,7 @@ export function JobsAvailableComponent({
title: t("jobs.fields.cieca_id"),
dataIndex: "cieca_id",
key: "cieca_id",
sorter: (a, b) => alphaSort(a.cieca_id, b.cieca_id),
sorter: (a, b) => alphaSort(a, b),
sortOrder:
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
},
@@ -68,10 +68,9 @@ export function JobsAvailableComponent({
//width: "8%",
// onFilter: (value, record) => record.ro_number.includes(value),
// filteredValue: state.filteredInfo.text || null,
sorter: (a, b) =>
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
sorter: (a, b) => alphaSort(a, b),
sortOrder:
state.sortedInfo.columnKey === "job_id" && state.sortedInfo.order,
state.sortedInfo.columnKey === "cieca_id" && state.sortedInfo.order,
render: (text, record) =>
record.job ? (
<Link to={`/manage/jobs/${record.job.id}`}>
@@ -88,7 +87,7 @@ export function JobsAvailableComponent({
dataIndex: "ownr_name",
key: "ownr_name",
ellipsis: true,
sorter: (a, b) => alphaSort(a.ownr_name, b.ownr_name),
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "ownr_name" && state.sortedInfo.order,
},

View File

@@ -25,12 +25,10 @@ import {
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
@@ -44,15 +42,8 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsAvailableContainer({
bodyshop,
currentUser,
insertAuditTrail,
}) {
export function JobsAvailableContainer({ bodyshop, currentUser }) {
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
fetchPolicy: "network-only",
});
@@ -166,11 +157,6 @@ export function JobsAvailableContainer({
});
//Job has been inserted. Clean up the available jobs record.
insertAuditTrail({
jobid: r.data.insert_jobs.returning[0].id,
operation: AuditTrailMapping.jobimported(),
});
deleteJob({
variables: { id: estData.data.available_jobs_by_pk.id },
}).then((r) => {
@@ -297,10 +283,6 @@ export function JobsAvailableContainer({
],
},
});
insertAuditTrail({
jobid: selectedJob,
operation: AuditTrailMapping.jobsupplement(),
});
}
};
@@ -386,7 +368,4 @@ export function JobsAvailableContainer({
</LoadingSpinner>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsAvailableContainer);
export default connect(mapStateToProps, null)(JobsAvailableContainer);

View File

@@ -6,21 +6,18 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
export function JobsChangeStatus({ job, bodyshop, jobRO }) {
const { t } = useTranslation();
const [availableStatuses, setAvailableStatuses] = useState([]);
@@ -32,10 +29,6 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail }) {
})
.then((r) => {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobstatuschange(status),
});
// refetch();
})
.catch((error) => {

View File

@@ -13,10 +13,8 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { CONVERT_JOB_TO_RO } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -24,17 +22,10 @@ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function JobsConvertButton({
bodyshop,
job,
refetch,
jobRO,
insertAuditTrail,
}) {
export function JobsConvertButton({ bodyshop, job, refetch, jobRO }) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
@@ -52,14 +43,6 @@ export function JobsConvertButton({
notification["success"]({
message: t("jobs.successes.converted"),
});
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobconverted(
res.data.update_jobs.returning[0].ro_number
),
});
setVisible(false);
}
setLoading(false);

View File

@@ -11,8 +11,7 @@ import FormItemPhone, {
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import { JobsDetailRatesParts } from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -188,7 +187,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
header={t("menus.jobsdetail.financials")}
>
<JobsDetailRatesChangeButton form={form} />
<JobsMarkPstExempt form={form} />
<LayoutFormRow>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput />

View File

@@ -1,10 +1,7 @@
import { notification } from "antd";
import i18n from "i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { store } from "../../redux/store";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { logImEXEvent } from "../../firebase/firebase.utils";
export default function AddToProduction(
apolloClient,
@@ -24,13 +21,6 @@ export default function AddToProduction(
notification["success"]({
message: i18n.t("jobs.successes.save"),
});
store.dispatch(
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.jobinproductionchange(!remove),
})
);
if (completionCallback) completionCallback();
})
.catch((error) => {

View File

@@ -142,10 +142,7 @@ export function JobsDetailHeaderActions({
</Menu.Item>
<Menu.Item
key="entertimetickets"
disabled={
!job.converted ||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
}
disabled={!job.converted}
onClick={() => {
logImEXEvent("job_header_enter_time_ticekts");

View File

@@ -1,13 +1,4 @@
import {
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Tooltip,
} from "antd";
import { Form, Input, InputNumber, Select, Space, Switch, Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -16,7 +7,6 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
@@ -113,19 +103,8 @@ export function JobsDetailRates({ jobRO, form, job }) {
<Switch disabled={jobRO} />
</Form.Item>
</FormRow>
<Divider
orientation="left"
type="horizontal"
style={{ marginTop: ".8rem", float: "right" }}
>
{t("jobs.forms.laborrates")}
</Divider>
<Space>
<div></div>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<JobsMarkPstExempt form={form} />
</Space>
<FormRow noDivider>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<FormRow header={t("jobs.forms.laborrates")}>
<Form.Item
label={t("jobs.fields.labor_rate_desc")}
name="labor_rate_desc"
@@ -201,6 +180,7 @@ export function JobsDetailRates({ jobRO, form, job }) {
<CurrencyInput disabled={jobRO} />
</Form.Item>
</FormRow>
<JobsDetailRatesParts form={form} />
</div>
);

View File

@@ -19,11 +19,7 @@ export function JobsDetailRatesParts({
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.parts_tax_rates")}
key="rates"
>
<Collapse.Panel header={t("jobs.labels.parts_tax_rates")} key="rates">
<LayoutFormRow header={t("joblines.fields.part_types.PAA")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}

View File

@@ -1,55 +0,0 @@
import { Popconfirm, Button } from "antd";
import React from "react";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
});
export function JobsMarkPstExempt({ jobRO, form }) {
const { t } = useTranslation();
const handleConfirm = () => {
const newPartRates = _.cloneDeep(form.getFieldValue("parts_tax_rates"));
Object.keys(newPartRates).forEach((key) => {
newPartRates[key] = {
...newPartRates[key],
prt_tax_in: false,
prt_tax_rt: 0,
};
});
form.setFieldsValue({
state_tax_rate: 0,
tax_lbr_rt: 0,
tax_levies_rt: 0,
tax_sub_rt: 0,
tax_shop_mat_rt: 0,
tax_paint_mat_rt: 0,
tax_str_rt: 0,
tax_tow_rt: 0,
parts_tax_rates: newPartRates,
});
};
return (
<Popconfirm
onConfirm={handleConfirm}
disabled={jobRO}
okText={t("general.labels.yes")}
cancelText={t("general.labels.no")}
title={t("jobs.actions.markpstexemptconfirm")}
>
<Button type="link" disabled={jobRO}>
{t("jobs.actions.markpstexempt")}
</Button>
</Popconfirm>
);
}
export default connect(mapStateToProps, null)(JobsMarkPstExempt);

View File

@@ -31,14 +31,11 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
title: t("jobs.fields.vehicle"),
dataIndex: "vehicleid",
key: "vehicleid",
render: (text, record) =>
record.vehicleid ? (
<Link to={`/manage/vehicles/${record.vehicleid}`}>
{`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc}`}
</Link>
) : (
t("jobs.errors.novehicle")
),
render: (text, record) => (
<Link to={`/manage/vehicles/${record.vehicleid}`}>
{`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc}`}
</Link>
),
},
{
title: t("jobs.fields.clm_no"),

View File

@@ -90,11 +90,7 @@ export function PartsOrderListTableComponent({
</Button>
)}
<Button
disabled={
jobRO ||
record.return ||
record.vendor.id === bodyshop.inhousevendorid
}
disabled={jobRO || record.return}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
@@ -143,10 +139,7 @@ export function PartsOrderListTableComponent({
</Button>
</Popconfirm>
<Button
disabled={
(jobRO ? !record.return : jobRO) ||
record.vendor.id === bodyshop.inhousevendorid
}
disabled={jobRO ? !record.return : jobRO}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
@@ -164,7 +157,7 @@ export function PartsOrderListTableComponent({
quantity: pol.quantity,
actual_price: pol.act_price,
cost_center: pol.jobline?.part_type
cost_center: pol.jobline.part_type
? responsibilityCenters.defaults.costs[
pol.jobline.part_type
] || null

View File

@@ -9,7 +9,6 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setEmailOptions } from "../../redux/email/email.actions";
import {
setModalContext,
@@ -20,7 +19,6 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import AlertComponent from "../alert/alert.component";
@@ -38,8 +36,6 @@ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function PartsOrderModalContainer({
@@ -49,7 +45,6 @@ export function PartsOrderModalContainer({
bodyshop,
setEmailOptions,
setBillEnterContext,
insertAuditTrail,
}) {
const { t } = useTranslation();
@@ -106,26 +101,12 @@ export function PartsOrderModalContainer({
const jobLinesResult = await updateJobLines({
variables: {
ids: values.parts_order_lines.data
.filter((item) => item.job_line_id)
.map((item) => item.job_line_id),
ids: values.parts_order_lines.data.map((item) => item.job_line_id),
status: isReturn
? bodyshop.md_order_statuses.default_returned || "Returned*"
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
},
});
insertAuditTrail({
jobid: jobId,
operation: isReturn
? AuditTrailMapping.jobspartsreturn(
insertResult.data.insert_parts_orders.returning[0].order_number
)
: AuditTrailMapping.jobspartsorder(
insertResult.data.insert_parts_orders.returning[0].order_number
),
});
if (!!jobLinesResult.errors) {
notification["error"]({
message: t("parts_orders.errors.creating"),

View File

@@ -14,25 +14,12 @@ import IndefiniteLoading from "../indefinite-loading/indefinite-loading.componen
import { logImEXEvent } from "../../firebase/firebase.utils";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionBoardKanbanComponent({
data,
bodyshop,
technician,
insertAuditTrail,
}) {
export function ProductionBoardKanbanComponent({ data, bodyshop, technician }) {
const [boardLanes, setBoardLanes] = useState({
columns: [{ id: "Loading...", title: "Loading...", cards: [] }],
});
@@ -117,11 +104,6 @@ export function ProductionBoardKanbanComponent({
newChildCardNewParent
),
});
insertAuditTrail({
jobid: card.id,
operation: AuditTrailMapping.jobstatuschange(destination.toColumnId),
});
if (update.errors) {
notification["error"]({
message: t("production.errors.boardupdate", {
@@ -148,7 +130,4 @@ export function ProductionBoardKanbanComponent({
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionBoardKanbanComponent);
export default connect(mapStateToProps, null)(ProductionBoardKanbanComponent);

View File

@@ -16,32 +16,24 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const iconStyle = { marginLeft: ".3rem" };
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function ProductionListEmpAssignment({
insertAuditTrail,
bodyshop,
record,
type,
}) {
export function ProductionListEmpAssignment({ bodyshop, record, type }) {
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const handleAdd = async (assignment) => {
setLoading(true);
const { operation, employeeid, name } = assignment;
const { operation, employeeid } = assignment;
logImEXEvent("job_assign_employee", { operation });
let empAssignment = determineFieldName(operation);
@@ -52,11 +44,6 @@ export function ProductionListEmpAssignment({
awaitRefetchQueries: true,
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobassignmentchange(empAssignment, name),
});
if (!!result.errors) {
notification["error"]({
message: t("jobs.errors.assigning", {
@@ -77,11 +64,6 @@ export function ProductionListEmpAssignment({
awaitRefetchQueries: true,
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobassignmentremoved(empAssignment),
});
if (!!result.errors) {
notification["error"]({
message: t("jobs.errors.assigning", {
@@ -98,8 +80,8 @@ export function ProductionListEmpAssignment({
});
const [visibility, setVisibility] = useState(false);
const onChange = (e, option) => {
setAssignment({ ...assignment, employeeid: e, name: option.name });
const onChange = (e) => {
setAssignment({ ...assignment, employeeid: e });
};
const popContent = (
@@ -117,11 +99,7 @@ export function ProductionListEmpAssignment({
}
>
{bodyshop.employees.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
<Select.Option value={emp.id} key={emp.id}>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}

View File

@@ -6,21 +6,12 @@ import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionListColumnStatus({
record,
bodyshop,
insertAuditTrail,
}) {
export function ProductionListColumnStatus({ record, bodyshop }) {
const [updateJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
@@ -37,11 +28,6 @@ export function ProductionListColumnStatus({
},
},
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.jobstatuschange(key),
});
setLoading(false);
};
@@ -66,7 +52,4 @@ export function ProductionListColumnStatus({
</Dropdown>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnStatus);
export default connect(mapStateToProps, null)(ProductionListColumnStatus);

View File

@@ -1,16 +1,11 @@
import { Button, Form, Input, notification } from "antd";
import { LockOutlined } from "@ant-design/icons";
import { Button, Form, Input } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { updateUserDetails } from "../../redux/user/user.actions";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import {
logImEXEvent,
updateCurrentPassword,
} from "../../firebase/firebase.utils";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { logImEXEvent } from "../../firebase/firebase.utils";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -33,96 +28,33 @@ export default connect(
});
};
const handleChangePassword = async ({ password }) => {
logImEXEvent("password_update");
try {
await updateCurrentPassword(password);
notification.success({
message: t("user.successess.passwordchanged"),
});
} catch (error) {
notification.error({
message: error.message,
});
}
};
return (
<div>
<Form
onFinish={handleFinish}
autoComplete={"no"}
initialValues={currentUser}
layout="vertical"
>
<LayoutFormRow>
<Form.Item
label={t("user.fields.displayname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name="displayName"
>
<Input />
</Form.Item>
<Form.Item label={t("user.fields.photourl")} name="photoURL">
<Input />
</Form.Item>
</LayoutFormRow>
<Form.Item
label={t("user.fields.displayname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name="displayName"
>
<Input />
</Form.Item>
<Form.Item label={t("user.fields.photourl")} name="photoURL">
<Input />
</Form.Item>
<Button type="primary" key="submit" htmlType="submit">
{t("user.actions.updateprofile")}
</Button>
</Form>
<Form
onFinish={handleChangePassword}
autoComplete={"no"}
initialValues={currentUser}
layout="vertical"
>
<LayoutFormRow>
<Form.Item label={t("general.labels.newpassword")} name="password">
<Input
prefix={<LockOutlined />}
type="password"
placeholder={t("general.labels.password")}
/>
</Form.Item>
<Form.Item
label={t("general.labels.confirmpassword")}
name="password-confirm"
dependencies={["password"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
}
return Promise.reject(
t("general.labels.passwordsdonotmatch")
);
},
}),
]}
>
<Input
prefix={<LockOutlined />}
type="password"
placeholder={t("general.labels.password")}
/>
</Form.Item>
</LayoutFormRow>
<Button type="primary" key="submit" htmlType="submit">
{t("user.actions.changepassword")}
</Button>
</Form>
</div>
);
});

View File

@@ -1,5 +1,5 @@
import { useLazyQuery } from "@apollo/client";
import { Button, DatePicker, Form, Radio, Space } from "antd";
import { Button, DatePicker, Form, Radio } from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -13,7 +13,6 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import "./report-center-modal.styles.scss";
const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter,
@@ -61,12 +60,8 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
{
name: values.key,
variables: {
...(start
? { start: moment(start).startOf("day").format("YYYY-MM-DD") }
: {}),
...(end
? { end: moment(end).endOf("day").format("YYYY-MM-DD") }
: {}),
...(start ? { start: moment(start).format("YYYY-MM-DD") } : {}),
...(end ? { end: moment(end).format("YYYY-MM-DD") } : {}),
...(id ? { id: id } : {}),
},
},
@@ -91,7 +86,6 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
<Form.Item
name="key"
label={t("reportcenter.labels.key")}
// className="radio-group-columns"
rules={[
{
required: true,
@@ -99,21 +93,12 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
},
]}
>
<Radio.Group>
<Space
direction="vertical"
wrap
size="small"
style={{
maxHeight: "50vh",
}}
>
{Object.keys(Templates).map((key) => (
<Radio key={key} value={key}>
{Templates[key].title}
</Radio>
))}
</Space>
<Radio.Group style={{ columns: "3 auto" }}>
{Object.keys(Templates).map((key) => (
<Radio key={key} value={key}>
{Templates[key].title}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item dependencies={["key"]}>

View File

@@ -31,7 +31,7 @@ export function ReportCenterModalContainer({
onCancel={() => toggleModalVisible()}
cancelButtonProps={{ style: { display: "none" } }}
destroyOnClose
width="80%"
width="60%"
>
<ReportCenterModalComponent />
</Modal>

View File

@@ -1,11 +0,0 @@
.radio-group-columns {
.ant-radio-group {
// display: block;
}
.ant-radio-wrapper {
display: block;
span {
word-wrap: break-word;
}
}
}

View File

@@ -1,14 +1,4 @@
import {
Button,
Col,
Form,
Input,
Row,
Select,
Space,
Switch,
Typography,
} from "antd";
import { Button, Card, Col, Form, Row, Select, Switch } from "antd";
import axios from "axios";
import moment from "moment";
import React, { useState } from "react";
@@ -101,34 +91,31 @@ export function ScheduleJobModalComponent({
<DateTimePicker onlyFuture />
</Form.Item>
</LayoutFormRow>
<Typography.Title level={4}>
{t("appointments.labels.smartscheduling")}
</Typography.Title>
{
// smartOptions.length > 0 && (
// <div>{t("appointments.labels.suggesteddates")}</div>
// )
}
<Space wrap>
<Card title={t("appointments.labels.smartscheduling")}>
<Button onClick={handleSmartScheduling} loading={loading}>
{t("appointments.actions.calculate")}
</Button>
{smartOptions.map((d, idx) => (
<Button
className="imex-flex-row__margin"
key={idx}
onClick={() => {
form.setFieldsValue({ start: new moment(d).add(8, "hours") });
handleDateBlur();
}}
>
<DateFormatter>{d}</DateFormatter>
</Button>
))}
</Space>
{smartOptions.length > 0 && (
<div>{t("appointments.labels.suggesteddates")}</div>
)}
<div
className="imex-flex-row imex-flex-row__flex-space-around"
style={{ flexWrap: "wrap" }}
>
{smartOptions.map((d, idx) => (
<Button
className="imex-flex-row__margin"
key={idx}
onClick={() => {
form.setFieldsValue({ start: new moment(d).add(8, "hours") });
handleDateBlur();
}}
>
<DateFormatter>{d}</DateFormatter>
</Button>
))}
</div>
</Card>
<LayoutFormRow grow>
<Form.Item
name="notifyCustomer"
@@ -137,9 +124,12 @@ export function ScheduleJobModalComponent({
>
<Switch />
</Form.Item>
<Form.Item name="email" label={t("jobs.fields.ownr_ea")}>
<EmailInput disabled={!form.getFieldValue("notifyCustomer")} />
<Form.Item shouldUpdate>
{() => (
<Form.Item name="email" label={t("jobs.fields.ownr_ea")}>
<EmailInput disabled={!form.getFieldValue("notifyCustomer")} />
</Form.Item>
)}
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow>
@@ -168,9 +158,6 @@ export function ScheduleJobModalComponent({
))}
</Select>
</Form.Item>
<Form.Item name={"note"} label={t("appointments.fields.note")}>
<Input />
</Form.Item>
</LayoutFormRow>
{t("appointments.labels.history")}
<ScheduleExistingAppointmentsList

View File

@@ -105,7 +105,6 @@ export function ScheduleJobModalContainer({
start: moment(values.start),
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
color: values.color,
note:values.note
},
jobId: jobId,
altTransport: values.alt_transport,

View File

@@ -436,21 +436,6 @@ export default function ShopInfoGeneral({ form }) {
>
<CurrencyInput />
</Form.Item>
<Form.Item
name={["attach_pdf_to_email"]}
label={t("bodyshop.fields.attach_pdf_to_email")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["tt_allow_post_to_invoiced"]}
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}>

View File

@@ -89,7 +89,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input onBlur={handleBlur} />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenter_accountnumber"
)}
@@ -103,7 +103,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
]}
>
<Input onBlur={handleBlur} />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenter_accountdesc"
@@ -119,7 +119,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input onBlur={handleBlur} />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenter_accountitem"
)}
@@ -133,7 +133,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
]}
>
<Input onBlur={handleBlur} />
</Form.Item> */}
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
@@ -182,7 +182,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input onBlur={handleBlur} />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenter_accountname"
)}
@@ -211,7 +211,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
]}
>
<Input onBlur={handleBlur} />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenter_accountdesc"
@@ -1081,7 +1081,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
@@ -1097,8 +1097,8 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
@@ -1114,7 +1114,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
]}
>
<Input />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
@@ -1175,7 +1175,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
@@ -1203,7 +1203,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "taxes", "state", "accountname"]}
>
<Input />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
@@ -1254,7 +1254,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
@@ -1282,7 +1282,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "taxes", "local", "accountname"]}
>
<Input />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
@@ -1320,8 +1320,8 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={<div>AR</div>}>
{/* <Form.Item
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.ar")}
rules={[
{
@@ -1344,7 +1344,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "ar", "accountnumber"]}
>
<Input />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
@@ -1357,7 +1357,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input />
</Form.Item>
{/* <Form.Item
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
{
@@ -1380,9 +1380,9 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "ar", "accountitem"]}
>
<Input />
</Form.Item> */}
</Form.Item>
</LayoutFormRow>
{/* <LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.ap")}
rules={[
@@ -1443,9 +1443,9 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
>
<Input />
</Form.Item>
</LayoutFormRow> */}
<LayoutFormRow header={<div>Refund</div>}>
{/* <Form.Item
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.refund")}
rules={[
{
@@ -1456,8 +1456,8 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "refund", "name"]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
@@ -1468,8 +1468,8 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "refund", "accountnumber"]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
@@ -1492,7 +1492,7 @@ export default function ShopInfoResponsibilityCenterComponent({ form }) {
name={["md_responsibility_centers", "refund", "accountdesc"]}
>
<Input />
</Form.Item> */}
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[

View File

@@ -30,10 +30,7 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
},
]}
>
<JobSearchSelect
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
notExported={!bodyshop.tt_allow_post_to_invoiced}
/>
<JobSearchSelect />
</Form.Item>
<Form.Item

View File

@@ -82,10 +82,7 @@ export function TimeTicketModalComponent({
},
]}
>
<JobSearchSelect
convertedOnly={!bodyshop.tt_allow_post_to_invoiced}
notExported={!bodyshop.tt_allow_post_to_invoiced}
/>
<JobSearchSelect convertedOnly notExported={false} />
</Form.Item>
)}
</Form.Item>

View File

@@ -35,24 +35,6 @@ export const updateCurrentUser = (userDetails) => {
});
};
export const updateCurrentPassword = async (password) => {
const currentUser = await getCurrentUser();
return currentUser.updatePassword(password);
// return new Promise((resolve, reject) => {
// const unsubscribe = auth.onAuthStateChanged(
// (userAuth) => {
// userAuth.updatePassword(password).then((r) => {
// unsubscribe();
// resolve(userAuth);
// });
// },
// (error) => reject(error)
// );
// });
};
let messaging;
try {
messaging = firebase.messaging();

View File

@@ -20,7 +20,6 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
isintake
block
color
note
job {
alt_transport
ro_number
@@ -70,7 +69,6 @@ export const INSERT_APPOINTMENT_BLOCK = gql`
title
isintake
block
note
}
}
}
@@ -92,7 +90,6 @@ export const INSERT_APPOINTMENT = gql`
isintake
block
color
note
}
}
update_jobs(
@@ -119,7 +116,6 @@ export const QUERY_APPOINTMENT_BY_DATE = gql`
isintake
block
color
note
job {
alt_transport
ro_number
@@ -172,7 +168,6 @@ export const UPDATE_APPOINTMENT = gql`
isintake
block
color
note
}
}
}
@@ -203,7 +198,6 @@ export const QUERY_APPOINTMENTS_BY_JOBID = gql`
canceled
created_at
block
note
}
}
`;

View File

@@ -1,31 +1,18 @@
import { gql } from "@apollo/client";
export const QUERY_AUDIT_TRAIL = gql`
query QUERY_AUDIT_TRAIL($jobid: uuid!) {
audit_trail(
where: { jobid: { _eq: $jobid } }
order_by: { created: desc }
) {
query QUERY_AUDIT_TRAIL($id: uuid!) {
audit_trail(where: { recordid: { _eq: $id } }) {
useremail
jobid
tabname
schemaname
recordid
operation
old_val
new_val
id
created
bodyshopid
}
}
`;
export const INSERT_AUDIT_TRAIL = gql`
mutation INSERT_AUDIT_TRAIL($auditObj: audit_trail_insert_input!) {
insert_audit_trail_one(object: $auditObj) {
id
jobid
billid
bodyshopid
created
operation
useremail
}
}
`;

View File

@@ -91,8 +91,6 @@ export const QUERY_BODYSHOP = gql`
md_jobline_presets
cdk_dealerid
features
attach_pdf_to_email
tt_allow_post_to_invoiced
employees {
id
active
@@ -180,8 +178,6 @@ export const UPDATE_SHOP = gql`
jc_hourly_rates
md_jobline_presets
cdk_dealerid
attach_pdf_to_email
tt_allow_post_to_invoiced
employees {
id
first_name

View File

@@ -23,6 +23,19 @@ export const GET_ALL_JOBLINES_BY_PK = gql`
notes
location
tax_part
parts_order_lines {
id
parts_order {
id
order_number
order_date
user_email
vendor {
id
name
}
}
}
}
}
`;
@@ -215,11 +228,7 @@ export const generateJobLinesUpdatesForInvoicing = (joblines) => {
export const DELETE_JOB_LINE_BY_PK = gql`
mutation DELETE_JOB_LINE_BY_PK($joblineId: uuid!) {
update_joblines_by_pk(
pk_columns: { id: $joblineId }
_set: { removed: true }
) {
removed
delete_joblines_by_pk(id: $joblineId) {
id
}
}

View File

@@ -559,7 +559,6 @@ export const GET_JOB_BY_PK = gql`
}
payments {
id
jobid
amount
payer
created_at
@@ -567,8 +566,6 @@ export const GET_JOB_BY_PK = gql`
transactionid
memo
date
type
exportedat
}
cccontracts {
id
@@ -689,8 +686,6 @@ export const QUERY_JOB_CARD_DETAILS = gql`
v_make_desc
v_model_desc
v_color
v_vin
plate_st
plate_no
vehicle {
id
@@ -1025,7 +1020,6 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
$search: String
$isConverted: Boolean
$notExported: Boolean
$notInvoiced: Boolean
) {
search_jobs(
args: { search: $search }
@@ -1034,7 +1028,6 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
_and: {
converted: { _eq: $isConverted }
date_exported: { _is_null: $notExported }
date_invoiced: { _is_null: $notInvoiced }
}
}
) {
@@ -1047,7 +1040,6 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
v_make_desc
v_model_desc
v_model_yr
status
}
}
`;

View File

@@ -5,7 +5,6 @@ export const INSERT_NEW_PARTS_ORDERS = gql`
insert_parts_orders(objects: $po) {
returning {
id
order_number
}
}
}

View File

@@ -36,7 +36,6 @@ export const GLOBAL_SEARCH_QUERY = gql`
search_payments(args: { search: $search }) {
id
amount
paymentnum
job {
ro_number
id

View File

@@ -81,52 +81,48 @@ export default class Home extends React.Component {
dataSource={Banner00DataSource}
isMobile={this.state.isMobile}
/>,
...(process.env.NODE_ENV !== "production"
? [
// <Content4
// id="Content4_0"
// key="Content4_0"
// dataSource={Content40DataSource}
// isMobile={this.state.isMobile}
// />,
<Content1
id="Content1_0"
key="Content1_0"
dataSource={Content10DataSource}
isMobile={this.state.isMobile}
/>,
<Content0
id="Content0_0"
key="Content0_0"
dataSource={Content00DataSource}
isMobile={this.state.isMobile}
/>,
<Pricing2
id="Pricing2_0"
key="Pricing2_0"
dataSource={Pricing20DataSource}
isMobile={this.state.isMobile}
/>,
// <Pricing1
// id="Pricing1_1"
// key="Pricing1_1"
// dataSource={Pricing11DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content3
// id="Content3_0"
// key="Content3_0"
// dataSource={Content30DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content12
// id="Content12_0"
// key="Content12_0"
// dataSource={Content120DataSource}
// isMobile={this.state.isMobile}
// />,
]
: []),
// <Content4
// id="Content4_0"
// key="Content4_0"
// dataSource={Content40DataSource}
// isMobile={this.state.isMobile}
// />,
<Content1
id="Content1_0"
key="Content1_0"
dataSource={Content10DataSource}
isMobile={this.state.isMobile}
/>,
<Content0
id="Content0_0"
key="Content0_0"
dataSource={Content00DataSource}
isMobile={this.state.isMobile}
/>,
<Pricing2
id="Pricing2_0"
key="Pricing2_0"
dataSource={Pricing20DataSource}
isMobile={this.state.isMobile}
/>,
// <Pricing1
// id="Pricing1_1"
// key="Pricing1_1"
// dataSource={Pricing11DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content3
// id="Content3_0"
// key="Content3_0"
// dataSource={Content30DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content12
// id="Content12_0"
// key="Content12_0"
// dataSource={Content120DataSource}
// isMobile={this.state.isMobile}
// />,
<Footer1
id="Footer1_0"
key="Footer1_0"

View File

@@ -1,4 +1,4 @@
import { Result, Timeline, Space, Tag, Divider, Button } from "antd";
import { Result, Timeline, Space, Tag, Divider, Button, Select } from "antd";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -11,6 +11,7 @@ import { useTranslation } from "react-i18next";
import SocketIO from "socket.io-client";
import { auth } from "../../firebase/firebase.utils";
import moment from "moment";
import DmsCustomerSelector from "../../components/dms-customer-selector/dms-customer-selector.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -38,6 +39,7 @@ export const socket = SocketIO(
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const [logLevel, setLogLevel] = useState("DEBUG");
const [logs, setLogs] = useState([]);
useEffect(() => {
@@ -55,6 +57,19 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
socket.on("connected", () => {
console.log("Connected again.");
});
socket.on("reconnect", () => {
console.log("Connected again.");
setLogs((logs) => {
return [
...logs,
{
timestamp: new Date(),
level: "WARNING",
message: "Reconnected to CDK Export Service",
},
];
});
});
socket.on("log-event", (payload) => {
setLogs((logs) => {
@@ -63,12 +78,13 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
});
socket.connect();
socket.emit("set-log-level", "TRACE");
socket.emit("set-log-level", logLevel);
return () => {
socket.removeAllListeners();
socket.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!bodyshop.cdk_dealerid) return <Result status="404" />;
@@ -77,27 +93,43 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
return (
<div>
<Button
onClick={() => {
socket.emit(
`${dmsType}-export-job`,
"752a4f5f-22ab-414b-b182-98d4e62227ef"
);
}}
>
Export
</Button>
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
}}
>
reconnect
</Button>
<Space>
<Select
placeholder="Log Level"
value={logLevel}
onChange={(value) => {
setLogLevel(value);
socket.emit("set-log-level", value);
}}
>
<Select.Option>TRACE</Select.Option>
<Select.Option>DEBUG</Select.Option>
<Select.Option>INFO</Select.Option>
<Select.Option>WARNING</Select.Option>
<Select.Option>ERROR</Select.Option>
</Select>
<Button
onClick={() => {
socket.emit(
`${dmsType}-export-job`,
"752a4f5f-22ab-414b-b182-98d4e62227ef"
);
}}
>
Export
</Button>
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
}}
>
Reconnect
</Button>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
</Space>
<DmsCustomerSelector />
<Timeline pending={socket.connected && "Processing..."} reverse={true}>
{logs.map((log, idx) => (
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
@@ -105,7 +137,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:MM:ss")}</span>
<Divider type="vertical" />
<span>{log.message}</span>
<span style={{ whiteSpace: "pre-line" }}>{log.message}</span>
</Space>
</Timeline.Item>
))}
@@ -119,11 +151,11 @@ function LogLevelHierarchy(level) {
case "TRACE":
return "pink";
case "DEBUG":
return "orange";
return "green";
case "INFO":
return "blue";
case "WARNING":
return "yellow";
return "orange";
case "ERROR":
return "red";
default:

View File

@@ -12,6 +12,7 @@ import AlertComponent from "../../components/alert/alert.component";
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -78,10 +79,12 @@ export function ExportLogsPageComponent({ bodyshop }) {
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) =>
record.job && (
<Link to={`/manage/jobs/${record.job.id}`}>
<Link to={"/manage/jobs/" + record.job && record.job.id}>
{(record.job && record.job.ro_number) || t("general.labels.na")}
</Link>
),

View File

@@ -1,5 +1,5 @@
import { useQuery } from "@apollo/client";
import { Card, Col, Result, Row, Space, Typography } from "antd";
import { Card, Col, Result, Row, Space } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -15,8 +15,6 @@ import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassoci
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
import NotFound from "../../components/not-found/not-found.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
@@ -84,9 +82,6 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
return (
<RbacWrapper action="jobs:admin">
<Typography.Title level={4} style={{ color: "tomato" }}>
{t("jobs.labels.adminwarning")}
</Typography.Title>
<Row gutter={[16, 16]}>
<Col {...colSpan}>
<Card style={cardStyle}>
@@ -101,7 +96,6 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
<JobsAdminDeleteIntake job={data ? data.jobs_by_pk : {}} />
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
</Space>
</Card>
</Col>

View File

@@ -42,26 +42,9 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
setLoading(true);
const result = await client.mutate({
mutation: generateJobLinesUpdatesForInvoicing(values.joblines),
});
if (result.errors) {
return; // Abandon the rest of the close.
}
const closeResult = await closeJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(),
actual_in: values.actual_in,
actual_completion: values.actual_completion,
actual_delivery: values.actual_delivery,
},
},
refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"],
awaitRefetchQueries: true,
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
// form.resetFields();
@@ -73,6 +56,18 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
});
return; // Abandon the rest of the close.
}
form.resetFields();
form.resetFields();
const closeResult = await closeJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(),
},
},
});
if (!closeResult.errors) {
setLoading(false);
@@ -89,8 +84,6 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
}),
});
}
form.resetFields();
form.resetFields();
setLoading(false);
};

View File

@@ -5,7 +5,6 @@ import Icon, {
FileImageFilled,
PrinterFilled,
ToolFilled,
HistoryOutlined,
} from "@ant-design/icons";
import {
Button,
@@ -46,9 +45,6 @@ import ScheduleJobModalContainer from "../../components/schedule-job-modal/sched
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -57,8 +53,6 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsDetailPage({
setPrintCenterContext,
@@ -66,7 +60,6 @@ export function JobsDetailPage({
job,
mutationUpdateJob,
handleSubmit,
insertAuditTrail,
refetch,
}) {
const { t } = useTranslation();
@@ -88,7 +81,6 @@ export function JobsDetailPage({
const handleFinish = async (values) => {
setLoading(true);
const result = await mutationUpdateJob({
variables: {
jobId: job.id,
@@ -113,62 +105,6 @@ export function JobsDetailPage({
notification["success"]({
message: t("jobs.successes.savetitle"),
});
const changedAuditFields = form.getFieldsValue(
[
"scheduled_in",
"actual_in",
"scheduled_completion",
"actual_completion",
"scheduled_delivery",
"actual_delivery",
"date_invoiced",
"ins_co_nm",
"ded_amt",
"ded_status",
"date_exported",
"special_coverage_policy",
"ca_gst_registrant",
"ca_bc_pvrt",
"scheduled_in",
"rate_la1",
"rate_la2",
"rate_la3",
"rate_la4",
"rate_laa",
"rate_lab",
"rate_lad",
"rate_lae",
"rate_laf",
"rate_lag",
"rate_lam",
"rate_lar",
"rate_las",
"rate_lau",
"rate_ma2s",
"rate_ma2t",
"rate_ma3s",
"rate_mabl",
"rate_macs",
"rate_mapa",
"rate_mahw",
"rate_mash",
"rate_matd",
],
(meta) => meta && meta.touched
);
Object.keys(changedAuditFields).forEach((key) => {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobfieldchange(
key,
changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
: changedAuditFields[key]
),
});
});
await refetch();
form.setFieldsValue(transormJobToForm(job));
form.resetFields();
@@ -343,17 +279,6 @@ export function JobsDetailPage({
>
<JobNotesContainer jobId={job.id} />
</Tabs.TabPane>
<Tabs.TabPane
tab={
<span>
<HistoryOutlined />
{t("jobs.labels.audit")}
</span>
}
key="audit"
>
<JobAuditTrail jobId={job.id} />
</Tabs.TabPane>
</Tabs>
</Form>
</div>

View File

@@ -53,8 +53,3 @@ export const setOnline = (isOnline) => ({
type: ApplicationActionTypes.SET_ONLINE_STATUS,
payload: isOnline,
});
export const insertAuditTrail = ({ jobid, billid, operation }) => ({
type: ApplicationActionTypes.INSERT_AUDIT_TRAIL,
payload: { jobid, billid, operation },
});

View File

@@ -1,7 +1,6 @@
import moment from "moment";
import { all, call, put, select, takeLatest } from "redux-saga/effects";
import { QUERY_SCHEDULE_LOAD_DATA } from "../../graphql/appointments.queries";
import { INSERT_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import client from "../../utils/GraphQLClient";
import { CalculateLoad, CheckJobBucket } from "../../utils/SSSUtils";
import {
@@ -126,56 +125,6 @@ export function* calculateScheduleLoad({ payload: end }) {
}
}
export function* onInsertAuditTrail() {
yield takeLatest(
ApplicationActionTypes.INSERT_AUDIT_TRAIL,
insertAuditTrailSaga
);
}
export function* insertAuditTrailSaga({
payload: { jobid, billid, operation },
}) {
const state = yield select();
const bodyshop = state.user.bodyshop;
const currentUser = state.user.currentUser;
console.log(
"Inserting audit trail for",
bodyshop.shopname,
currentUser.email,
jobid,
billid,
operation
);
const variables = {
auditObj: {
bodyshopid: bodyshop.id,
jobid,
billid,
operation,
useremail: currentUser.email,
},
};
yield client.mutate({
mutation: INSERT_AUDIT_TRAIL,
variables,
update(cache, { data }) {
cache.modify({
fields: {
audit_trail(existingAuditTrail, { readField }) {
const newAuditTrail = cache.writeQuery({
data: data.insert_audit_trail_one,
query: INSERT_AUDIT_TRAIL,
variables,
});
return [...existingAuditTrail, newAuditTrail];
},
},
});
},
});
}
export function* applicationSagas() {
yield all([call(onCalculateScheduleLoad), call(onInsertAuditTrail)]);
yield all([call(onCalculateScheduleLoad)]);
}

View File

@@ -10,6 +10,5 @@ const ApplicationActionTypes = {
SET_JOB_READONLY: "SET_JOB_READONLY",
SET_PARTNER_VERSION: "SET_PARTNER_VERSION",
SET_ONLINE_STATUS: "SET_ONLINE_STATUS",
INSERT_AUDIT_TRAIL: "INSERT_AUDIT_TRAIL",
};
export default ApplicationActionTypes;

View File

@@ -39,7 +39,7 @@ export function* openChatByPhone({ payload }) {
data: { conversations },
} = yield client.query({
query: CONVERSATION_ID_BY_PHONE,
variables: { phone: phone(phone_num).phoneNumber },
variables: { phone: phone(phone_num)[0] },
fetchPolicy: "network-only",
});
@@ -53,7 +53,7 @@ export function* openChatByPhone({ payload }) {
variables: {
conversation: [
{
phone_num: phone(phone_num).phoneNumber,
phone_num: phone(phone_num)[0],
bodyshopid: bodyshop.id,
job_conversations: jobid ? { data: { jobid: jobid } } : null,
},

View File

@@ -100,12 +100,8 @@ export function* onUpdateUserDetails() {
}
export function* updateUserDetails(userDetails) {
try {
const updatedDetails = yield updateCurrentUser(userDetails.payload);
console.log(
"🚀 ~ file: user.sagas.js ~ line 104 ~ updatedDetails",
updatedDetails
);
yield put(updateUserDetailsSuccess(updatedDetails));
yield updateCurrentUser(userDetails.payload);
yield put(updateUserDetailsSuccess(userDetails.payload));
notification.open({
type: "success",
message: i18next.t("profile.successes.updated"),

View File

@@ -37,7 +37,6 @@
"fields": {
"alt_transport": "Alt. Trans.",
"color": "Appointment Color",
"note": "Appt. Note",
"time": "Appointment Time",
"title": "Title"
},
@@ -53,7 +52,6 @@
"nocompletingjobs": "No jobs scheduled for completion.",
"nodateselected": "No date has been selected.",
"priorappointments": "Previous Appointments",
"reminder": "This is {{shopname}} reminding you about an appointment on {{date}} at {{time}}. Please let us know if you are not able to make the appointment. We look forward to seeing you soon. ",
"scheduledfor": "Scheduled appointment for: ",
"smartscheduling": "Smart Scheduling",
"suggesteddates": "Suggested Dates"
@@ -84,24 +82,6 @@
"values": "Values"
}
},
"audit_trail": {
"messages": {
"billposted": "Bill with invoice number {{invoice_number}} posted.",
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
"jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}",
"jobmodifylbradj": "Labor adjustments modified.",
"jobspartsorder": "Parts order {{order_number}} added to job.",
"jobspartsreturn": "Parts return {{order_number}} added to job.",
"jobstatuschange": "Job status changed to {{status}}.",
"jobsupplement": "Job supplement imported."
}
},
"billlines": {
"actions": {
"newline": "New Line"
@@ -219,7 +199,6 @@
"label": "Label"
},
"appt_length": "Default Appointment Length",
"attach_pdf_to_email": "Attach PDF copy to sent emails?",
"bill_federal_tax_rate": "Bills - Federal Tax Rate %",
"bill_local_tax_rate": "Bill - Provincial/State Tax Rate %",
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
@@ -446,7 +425,6 @@
"production_statuses": "Production Statuses"
},
"target_touchtime": "Target Touch Time",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"use_fippa": "Use FIPPA for Names on Generated Documents?",
"website": "Website",
"zip_post": "Zip/Postal Code"
@@ -764,7 +742,6 @@
"attachments": "Attachments",
"documents": "Documents",
"generatingemail": "Generating email...",
"pdfcopywillbeattached": "A PDF copy of this email will be attached when it is sent.",
"preview": "Email Preview"
},
"successes": {
@@ -878,7 +855,6 @@
"message": "Message",
"monday": "Monday",
"na": "N/A",
"newpassword": "New Password",
"no": "No",
"nointernet": "It looks like you're not connected to the internet.",
"nointernet_sub": "Please check your connection and try again. ",
@@ -899,7 +875,6 @@
"sendagain": "Send Again",
"sendby": "Send By",
"signin": "Sign In",
"sms": "SMS",
"sub_status": {
"expired": "The subscription for this shop has expired. Please contact technical support to reactivate the subscription. "
},
@@ -1059,9 +1034,6 @@
"intake": "Intake",
"manualnew": "Create New Job Manually",
"mark": "Mark",
"markasexported": "Mark as Exported",
"markpstexempt": "Mark Job PST Exempt",
"markpstexemptconfirm": "Are you sure you want to do this? To undo this, you must manually update all PST rates.",
"postbills": "Post Bills",
"printCenter": "Print Center",
"recalculate": "Recalculate",
@@ -1070,7 +1042,6 @@
"schedule": "Schedule",
"sendcsi": "Send CSI",
"sync": "Sync",
"uninvoice": "Uninvoice",
"unvoid": "Unvoid Job",
"viewchecklist": "View Checklists",
"viewdetail": "View Details"
@@ -1252,7 +1223,6 @@
"servicecar": "Service Car",
"servicing_dealer": "Servicing Dealer",
"servicing_dealer_contact": "Servicing Dealer Contact",
"special_coverage_policy": "Special Coverage Policy",
"specialcoveragepolicy": "Special Coverage Policy",
"state_tax_rate": "Provincial/State Tax Rate",
"status": "Job Status",
@@ -1290,7 +1260,6 @@
"additionaltotal": "Additional Total",
"adjustmentrate": "Adjustment Rate",
"adjustments": "Adjustments",
"adminwarning": "Use the functionality on this page at your own risk. You are responsible for any and all changes to your data.",
"allocations": "Allocations",
"alreadyclosed": "This job has already been closed.",
"appointmentconfirmation": "Send confirmation to customer?",
@@ -1343,8 +1312,7 @@
"waived": "Waived"
},
"deleteconfirm": "Are you sure you want to delete this job? This cannot be undone. ",
"deletedelivery": "Delete Delivery Checklist",
"deleteintake": "Delete Intake Checklist",
"deleteintake": "Delete Intake",
"deliverchecklist": "Deliver Checklist",
"difference": "Difference",
"diskscan": "Scan Disk for Estimates",
@@ -1473,11 +1441,11 @@
"name": "ImEX Online",
"status": "System Status"
},
"slogan": "A whole new kind of shop management system."
"slogan": "The future of shop management systems. "
},
"hero": {
"button": "Coming Soon",
"title": "A whole new kind of shop management system."
"button": "Learn More",
"title": "Bringing the future to the collision repair process."
},
"labels": {
"features": "Features",
@@ -1837,7 +1805,6 @@
"depreciation": "Depreciation",
"other": "Other",
"ponumber": "PO Number",
"refnumber": "Reference Number",
"sendtype": "Send by",
"state": "Province/State",
"zip": "Postal Code/Zip"
@@ -1863,8 +1830,6 @@
"invoice_total_payable": "Invoice (Total Payable)",
"job_costing_ro": "Job Costing",
"job_notes": "Job Notes",
"key_tag": "Key Tag",
"paint_grid": "Paint Grid",
"parts_label_single": "Parts Label - Single",
"parts_list": "Parts List",
"parts_order": "Parts Order Confirmation",
@@ -1879,7 +1844,6 @@
"qc_sheet": "Quality Control Sheet",
"ro_totals": "RO Totals",
"ro_with_description": "RO Summary with Descriptions",
"stolen_recovery_checklist": "Stolen Recovery Checklist",
"supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter",
"thirdpartypayer": "Third Party Payer",
@@ -1981,15 +1945,12 @@
"bills": "Bills",
"exportlogs": "Export Logs",
"jobs": "Jobs",
"parts_orders": "Parts Orders",
"payments": "Payments",
"scoreboard": "Scoreboard",
"timetickets": "Timetickets"
},
"vendor": "Vendor"
},
"templates": {
"anticipated_revenue": "Anticipated Revenue",
"attendance_detail": "Attendance (All Employees)",
"attendance_employee": "Employee Attendance",
"attendance_summary": "Attendance Summary (All Employees)",
@@ -1999,39 +1960,30 @@
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
"gsr_by_csr": "Gross Sales by CSR",
"gsr_by_delivery_date": "Gross Sales by Delivery Date",
"gsr_by_estimator": "Gross Sales by Estimator",
"gsr_by_exported_date": "Gross Sales by Export Date",
"gsr_by_ins_co": "Gross Sales by Insurance Company",
"gsr_by_ins_co": "Gross Sales by Insurance Company'",
"gsr_by_make": "Gross Sales by Vehicle Make",
"gsr_by_referral": "Gross Sales by Referral Source",
"gsr_by_ro": "Gross Sales by RO",
"gsr_labor_only": "Gross Sales - Labor Only",
"hours_sold_detail_closed": "Hours Sold Detail - Closed",
"hours_sold_detail_closed_csr": "Hours Sold Detail - Closed by CSR",
"hours_sold_detail_closed_ins_co": "Hours Sold Detail - Closed by Source",
"hours_sold_detail_open": "Hours Sold Detail - Open",
"hours_sold_detail_open_csr": "Hours Sold Detail - Open by CSR",
"hours_sold_detail_open_ins_co": "Hours Sold Detail - Open by Source",
"hours_sold_summary_closed": "Hours Sold Summary - Closed",
"hours_sold_summary_closed_csr": "Hours Sold Summary - Closed by CSR",
"hours_sold_summary_closed_ins_co": "Hours Sold Summary - Closed by Source",
"hours_sold_summary_open": "Hours Sold Summary - Open",
"hours_sold_summary_open_csr": "Hours Sold Summary - Open CSR",
"hours_sold_summary_open_ins_co": "Hours Sold Summary - Open by Source",
"job_costing_ro_csr": "Job Costing by CSR",
"job_costing_ro_date_detail": "Job Costing by RO - Detail",
"job_costing_ro_date_summary": "Job Costing by RO - Summary",
"job_costing_ro_estimator": "Job Costing by Estimator",
"job_costing_ro_ins_co": "Job Costing by RO Source",
"lag_time": "Lag Time",
"open_orders": "Open Orders by Date",
"open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator",
"open_orders_ins_co": "Open Orders by Insurance Company",
"parts_backorder": "Backordered Parts",
"parts_not_recieved": "Parts Not Received",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date and Type",
"production_by_csr": "Production by CSR",
@@ -2048,8 +2000,6 @@
"purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed",
"purchases_grouped_by_vendor_summary": "Purchases Grouped by Vendor - Summary",
"schedule": "Appointment Schedule",
"scoreboard_detail": "Scoreboard Detail",
"scoreboard_summary": "Scoreboard Summary",
"supplement_ratio_ins_co": "Supplement Ratio by Source",
"thank_you_date": "Thank You Letters",
"timetickets": "Time Tickets",
@@ -2256,7 +2206,6 @@
},
"user": {
"actions": {
"changepassword": "Change Password",
"signout": "Sign Out",
"updateprofile": "Update Profile"
},
@@ -2271,9 +2220,6 @@
},
"labels": {
"actions": "Actions"
},
"successess": {
"passwordchanged": "Password changed successfully. "
}
},
"vehicles": {
@@ -2336,7 +2282,7 @@
"city": "City",
"cost_center": "Cost Center",
"country": "Country",
"discount": "Discount % (as decimal)",
"discount": "Discount %",
"display_name": "Display Name",
"due_date": "Payment Due Date",
"email": "Contact Email",

View File

@@ -37,7 +37,6 @@
"fields": {
"alt_transport": "",
"color": "",
"note": "",
"time": "",
"title": "Título"
},
@@ -53,7 +52,6 @@
"nocompletingjobs": "",
"nodateselected": "No se ha seleccionado ninguna fecha.",
"priorappointments": "Nombramientos previos",
"reminder": "",
"scheduledfor": "Cita programada para:",
"smartscheduling": "",
"suggesteddates": ""
@@ -84,24 +82,6 @@
"values": ""
}
},
"audit_trail": {
"messages": {
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobmodifylbradj": "",
"jobspartsorder": "",
"jobspartsreturn": "",
"jobstatuschange": "",
"jobsupplement": ""
}
},
"billlines": {
"actions": {
"newline": ""
@@ -219,7 +199,6 @@
"label": ""
},
"appt_length": "",
"attach_pdf_to_email": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
@@ -446,7 +425,6 @@
"production_statuses": ""
},
"target_touchtime": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"website": "",
"zip_post": ""
@@ -764,7 +742,6 @@
"attachments": "",
"documents": "",
"generatingemail": "",
"pdfcopywillbeattached": "",
"preview": ""
},
"successes": {
@@ -878,7 +855,6 @@
"message": "",
"monday": "",
"na": "N / A",
"newpassword": "",
"no": "",
"nointernet": "",
"nointernet_sub": "",
@@ -899,7 +875,6 @@
"sendagain": "",
"sendby": "",
"signin": "",
"sms": "",
"sub_status": {
"expired": ""
},
@@ -1059,9 +1034,6 @@
"intake": "",
"manualnew": "",
"mark": "",
"markasexported": "",
"markpstexempt": "",
"markpstexemptconfirm": "",
"postbills": "Contabilizar facturas",
"printCenter": "Centro de impresión",
"recalculate": "",
@@ -1070,7 +1042,6 @@
"schedule": "Programar",
"sendcsi": "",
"sync": "",
"uninvoice": "",
"unvoid": "",
"viewchecklist": "",
"viewdetail": ""
@@ -1252,7 +1223,6 @@
"servicecar": "Auto de servicio",
"servicing_dealer": "Distribuidor de servicio",
"servicing_dealer_contact": "Servicio Contacto con el concesionario",
"special_coverage_policy": "Política de cobertura especial",
"specialcoveragepolicy": "Política de cobertura especial",
"state_tax_rate": "",
"status": "Estado del trabajo",
@@ -1290,7 +1260,6 @@
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"adminwarning": "",
"allocations": "",
"alreadyclosed": "",
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
@@ -1343,7 +1312,6 @@
"waived": ""
},
"deleteconfirm": "",
"deletedelivery": "",
"deleteintake": "",
"deliverchecklist": "",
"difference": "",
@@ -1837,7 +1805,6 @@
"depreciation": "",
"other": "",
"ponumber": "",
"refnumber": "",
"sendtype": "",
"state": "",
"zip": ""
@@ -1863,8 +1830,6 @@
"invoice_total_payable": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -1879,7 +1844,6 @@
"qc_sheet": "",
"ro_totals": "",
"ro_with_description": "",
"stolen_recovery_checklist": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -1981,15 +1945,12 @@
"bills": "",
"exportlogs": "",
"jobs": "",
"parts_orders": "",
"payments": "",
"scoreboard": "",
"timetickets": ""
},
"vendor": ""
},
"templates": {
"anticipated_revenue": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
@@ -1999,7 +1960,6 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
"gsr_by_exported_date": "",
@@ -2009,29 +1969,21 @@
"gsr_by_ro": "",
"gsr_labor_only": "",
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_ins_co": "",
"job_costing_ro_csr": "",
"job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "",
"job_costing_ro_estimator": "",
"job_costing_ro_ins_co": "",
"lag_time": "",
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",
"parts_not_recieved": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_csr": "",
@@ -2048,8 +2000,6 @@
"purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "",
"schedule": "",
"scoreboard_detail": "",
"scoreboard_summary": "",
"supplement_ratio_ins_co": "",
"thank_you_date": "",
"timetickets": "",
@@ -2256,7 +2206,6 @@
},
"user": {
"actions": {
"changepassword": "",
"signout": "desconectar",
"updateprofile": "Actualización del perfil"
},
@@ -2271,9 +2220,6 @@
},
"labels": {
"actions": ""
},
"successess": {
"passwordchanged": ""
}
},
"vehicles": {

View File

@@ -37,7 +37,6 @@
"fields": {
"alt_transport": "",
"color": "",
"note": "",
"time": "",
"title": "Titre"
},
@@ -53,7 +52,6 @@
"nocompletingjobs": "",
"nodateselected": "Aucune date n'a été sélectionnée.",
"priorappointments": "Rendez-vous précédents",
"reminder": "",
"scheduledfor": "Rendez-vous prévu pour:",
"smartscheduling": "",
"suggesteddates": ""
@@ -84,24 +82,6 @@
"values": ""
}
},
"audit_trail": {
"messages": {
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobconverted": "",
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobmodifylbradj": "",
"jobspartsorder": "",
"jobspartsreturn": "",
"jobstatuschange": "",
"jobsupplement": ""
}
},
"billlines": {
"actions": {
"newline": ""
@@ -219,7 +199,6 @@
"label": ""
},
"appt_length": "",
"attach_pdf_to_email": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
@@ -446,7 +425,6 @@
"production_statuses": ""
},
"target_touchtime": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"website": "",
"zip_post": ""
@@ -764,7 +742,6 @@
"attachments": "",
"documents": "",
"generatingemail": "",
"pdfcopywillbeattached": "",
"preview": ""
},
"successes": {
@@ -878,7 +855,6 @@
"message": "",
"monday": "",
"na": "N / A",
"newpassword": "",
"no": "",
"nointernet": "",
"nointernet_sub": "",
@@ -899,7 +875,6 @@
"sendagain": "",
"sendby": "",
"signin": "",
"sms": "",
"sub_status": {
"expired": ""
},
@@ -1059,9 +1034,6 @@
"intake": "",
"manualnew": "",
"mark": "",
"markasexported": "",
"markpstexempt": "",
"markpstexemptconfirm": "",
"postbills": "Poster des factures",
"printCenter": "Centre d'impression",
"recalculate": "",
@@ -1070,7 +1042,6 @@
"schedule": "Programme",
"sendcsi": "",
"sync": "",
"uninvoice": "",
"unvoid": "",
"viewchecklist": "",
"viewdetail": ""
@@ -1252,7 +1223,6 @@
"servicecar": "Voiture de service",
"servicing_dealer": "Concessionnaire",
"servicing_dealer_contact": "Contacter le concessionnaire",
"special_coverage_policy": "Politique de couverture spéciale",
"specialcoveragepolicy": "Politique de couverture spéciale",
"state_tax_rate": "",
"status": "Statut de l'emploi",
@@ -1290,7 +1260,6 @@
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"adminwarning": "",
"allocations": "",
"alreadyclosed": "",
"appointmentconfirmation": "Envoyer une confirmation au client?",
@@ -1343,7 +1312,6 @@
"waived": ""
},
"deleteconfirm": "",
"deletedelivery": "",
"deleteintake": "",
"deliverchecklist": "",
"difference": "",
@@ -1837,7 +1805,6 @@
"depreciation": "",
"other": "",
"ponumber": "",
"refnumber": "",
"sendtype": "",
"state": "",
"zip": ""
@@ -1863,8 +1830,6 @@
"invoice_total_payable": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -1879,7 +1844,6 @@
"qc_sheet": "",
"ro_totals": "",
"ro_with_description": "",
"stolen_recovery_checklist": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -1981,15 +1945,12 @@
"bills": "",
"exportlogs": "",
"jobs": "",
"parts_orders": "",
"payments": "",
"scoreboard": "",
"timetickets": ""
},
"vendor": ""
},
"templates": {
"anticipated_revenue": "",
"attendance_detail": "",
"attendance_employee": "",
"attendance_summary": "",
@@ -1999,7 +1960,6 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
"gsr_by_exported_date": "",
@@ -2009,29 +1969,21 @@
"gsr_by_ro": "",
"gsr_labor_only": "",
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_ins_co": "",
"job_costing_ro_csr": "",
"job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "",
"job_costing_ro_estimator": "",
"job_costing_ro_ins_co": "",
"lag_time": "",
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",
"parts_not_recieved": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_csr": "",
@@ -2048,8 +2000,6 @@
"purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "",
"schedule": "",
"scoreboard_detail": "",
"scoreboard_summary": "",
"supplement_ratio_ins_co": "",
"thank_you_date": "",
"timetickets": "",
@@ -2256,7 +2206,6 @@
},
"user": {
"actions": {
"changepassword": "",
"signout": "Déconnexion",
"updateprofile": "Mettre à jour le profil"
},
@@ -2271,9 +2220,6 @@
},
"labels": {
"actions": ""
},
"successess": {
"passwordchanged": ""
}
},
"vehicles": {

View File

@@ -1,31 +0,0 @@
import i18n from "i18next";
const AuditTrailMapping = {
jobstatuschange: (status) =>
i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) =>
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
jobspartsorder: (order_number) =>
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
jobspartsreturn: (order_number) =>
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
jobmodifylbradj: () => i18n.t("audit_trail.messages.jobmodifylbradj", {}),
billposted: (invoice_number) =>
i18n.t("audit_trail.messages.billposted", { invoice_number }),
billupdated: (invoice_number) =>
i18n.t("audit_trail.messages.billupdated", { invoice_number }),
jobassignmentchange: (operation, name) =>
i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
jobassignmentremoved: (operation) =>
i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
jobinproductionchange: (inproduction) =>
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
jobchecklist: (type, inproduction, status) =>
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
};
export default AuditTrailMapping;

View File

@@ -8,7 +8,6 @@ import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store";
import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
import _ from "lodash";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -40,10 +39,8 @@ export default async function RenderTemplate(
offset: moment().utcOffset(),
},
};
try {
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {
render.download(
(Templates[templateObject.name] &&
@@ -51,21 +48,8 @@ export default async function RenderTemplate(
""
);
} else {
let pdf;
if (bodyshop.attach_pdf_to_email) {
const pdfRequest = _.cloneDeep(reportRequest);
pdfRequest.template.recipe = "chrome-pdf";
const pdfRender = await jsreport.renderAsync(pdfRequest);
pdf = pdfRender.toDataURI();
}
return new Promise((resolve, reject) => {
resolve({
pdf,
filename:
Templates[templateObject.name] &&
Templates[templateObject.name].title,
html: render.toString(),
});
resolve(render.toString());
});
}
} catch (error) {

View File

@@ -69,14 +69,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "pre",
},
stolen_recovery_checklist: {
title: i18n.t("printcenter.jobs.stolen_recovery_checklist"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.stolen_recovery_checklist"),
key: "stolen_recovery_checklist",
disabled: false,
group: "pre",
},
vehicle_check_in: {
title: i18n.t("printcenter.jobs.vehicle_check_in"),
description: "All Jobs Notes",
@@ -158,22 +150,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "ro",
},
key_tag: {
title: i18n.t("printcenter.jobs.key_tag"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.key_tag"),
key: "key_tag",
disabled: false,
group: "ro",
},
paint_grid: {
title: i18n.t("printcenter.jobs.paint_grid"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.paint_grid"),
key: "paint_grid",
disabled: false,
group: "ro",
},
worksheet_by_line_number: {
title: i18n.t("printcenter.jobs.worksheet_by_line_number"),
description: "All Jobs Notes",
@@ -752,67 +728,6 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
hours_sold_detail_closed_csr: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_csr"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_csr"
),
key: "hours_sold_detail_closed_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
hours_sold_detail_open_csr: {
title: i18n.t("reportcenter.templates.hours_sold_detail_open_csr"),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_open_csr"
),
key: "hours_sold_detail_open_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
},
hours_sold_summary_closed_csr: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_csr"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_csr"
),
key: "hours_sold_summary_closed_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
hours_sold_summary_open_csr: {
title: i18n.t("reportcenter.templates.hours_sold_summary_open_csr"),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_open_csr"
),
key: "hours_sold_summary_open_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
estimator_detail: {
title: i18n.t("reportcenter.templates.estimator_detail"),
description: "",
@@ -875,18 +790,6 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_invoiced"),
},
},
job_costing_ro_csr: {
title: i18n.t("reportcenter.templates.job_costing_ro_csr"),
description: "",
subject: i18n.t("reportcenter.templates.job_costing_ro_csr"),
key: "job_costing_ro_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
},
job_costing_ro_ins_co: {
title: i18n.t("reportcenter.templates.job_costing_ro_ins_co"),
description: "",
@@ -937,18 +840,6 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
gsr_by_csr: {
title: i18n.t("reportcenter.templates.gsr_by_csr"),
description: "",
subject: i18n.t("reportcenter.templates.gsr_by_csr"),
key: "gsr_by_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
gsr_by_make: {
title: i18n.t("reportcenter.templates.gsr_by_make"),
description: "",
@@ -1058,18 +949,6 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
open_orders_csr: {
title: i18n.t("reportcenter.templates.open_orders_csr"),
description: "",
subject: i18n.t("reportcenter.templates.open_orders_csr"),
key: "open_orders_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
},
open_orders_estimator: {
title: i18n.t("reportcenter.templates.open_orders_estimator"),
description: "",
@@ -1190,66 +1069,6 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
lag_time: {
title: i18n.t("reportcenter.templates.lag_time"),
description: "",
subject: i18n.t("reportcenter.templates.lag_time"),
key: "lag_time",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
parts_not_recieved: {
title: i18n.t("reportcenter.templates.parts_not_recieved"),
description: "",
subject: i18n.t("reportcenter.templates.parts_not_recieved"),
key: "parts_not_recieved",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.parts_orders"),
field: i18n.t("parts_orders.fields.order_date"),
},
},
scoreboard_detail: {
title: i18n.t("reportcenter.templates.scoreboard_detail"),
description: "",
subject: i18n.t("reportcenter.templates.scoreboard_detail"),
key: "scoreboard_detail",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.scoreboard"),
field: i18n.t("scoreboard.fields.date"),
},
},
scoreboard_summary: {
title: i18n.t("reportcenter.templates.scoreboard_summary"),
description: "",
subject: i18n.t("reportcenter.templates.scoreboard_summary"),
key: "scoreboard_summary",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.scoreboard"),
field: i18n.t("scoreboard.fields.date"),
},
},
anticipated_revenue: {
title: i18n.t("reportcenter.templates.anticipated_revenue"),
description: "",
subject: i18n.t("reportcenter.templates.anticipated_revenue"),
key: "anticipated_revenue",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.scheduled_completion"), // Also date invoice.
},
},
}
: {}),
...(!type || type === "courtesycarcontract"

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
- args:
cascade: false
read_only: false
sql: "CREATE OR REPLACE FUNCTION public.search_payments(search text)\n RETURNS
SETOF payments\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search
= '' then\n return query select * from payments ;\n else \n return query
SELECT\n p.*\nFROM\n payments p, jobs j\nWHERE\np.jobid = j.id AND\n(\nsearch
<% p.paymentnum OR\nsearch <% j.ownr_fn OR\nsearch <% j.ownr_ln OR\nsearch <%
j.ownr_co_nm OR\nsearch <% j.ro_number OR\n search <% (p.payer) OR\n search
<% (p.transactionid) OR\n search <% (p.memo));\n end if;\n\n\tEND\n$function$;"
type: run_sql

View File

@@ -1,6 +0,0 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."vehicles" add constraint "vehicles_v_vin_shopid_key"
unique ("v_vin", "shopid");
type: run_sql

View File

@@ -1,5 +0,0 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."vehicles" drop constraint "vehicles_v_vin_shopid_key";
type: run_sql

View File

@@ -1,5 +0,0 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "attach_pdf_to_email";
type: run_sql

View File

@@ -1,6 +0,0 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "attach_pdf_to_email" boolean
NOT NULL DEFAULT False;
type: run_sql

View File

@@ -1,87 +0,0 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- cdk_dealerid
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- features
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -1,88 +0,0 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- attach_pdf_to_email
- bill_tax_rates
- cdk_dealerid
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- features
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -1,79 +0,0 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- use_fippa
- website
- workingdays
- zip_post
filter:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -1,80 +0,0 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- attach_pdf_to_email
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- use_fippa
- website
- workingdays
- zip_post
filter:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -1,29 +0,0 @@
- args:
permission:
allow_aggregations: false
columns:
- id
- new_val
- old_val
- operation
- schemaname
- tabname
- useremail
- created
- bodyshopid
- recordid
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: audit_trail
schema: public
type: create_select_permission

View File

@@ -1,6 +0,0 @@
- args:
role: user
table:
name: audit_trail
schema: public
type: drop_select_permission

View File

@@ -1,10 +0,0 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ADD COLUMN "schemaname" text;
type: run_sql
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ALTER COLUMN "schemaname" DROP NOT NULL;
type: run_sql

View File

@@ -1,5 +0,0 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" DROP COLUMN "schemaname" CASCADE;
type: run_sql

View File

@@ -1,10 +0,0 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ADD COLUMN "tabname" text;
type: run_sql
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ALTER COLUMN "tabname" DROP NOT NULL;
type: run_sql

Some files were not shown because too many files have changed in this diff Show More