Merged in feature/2021-08-06 (pull request #162)

Feature/2021 08 06
This commit is contained in:
Patrick Fic
2021-08-06 19:31:18 +00:00
30 changed files with 921 additions and 114 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2"> <babeledit_project version="1.2" be_version="2.7.1">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -799,6 +799,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>reminder</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>scheduledfor</name> <name>scheduledfor</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -7033,6 +7054,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>tt_allow_post_to_invoiced</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>use_fippa</name> <name>use_fippa</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -8102,6 +8144,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>allow_text_message</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>checklist</name> <name>checklist</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -14305,6 +14368,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>sms</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node> <folder_node>
<name>sub_status</name> <name>sub_status</name>
<children> <children>
@@ -16904,6 +16988,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>markasexported</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>markpstexempt</name> <name>markpstexempt</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -17114,6 +17219,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>uninvoice</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>unvoid</name> <name>unvoid</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -21454,6 +21580,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>adminwarning</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>allocations</name> <name>allocations</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -22398,6 +22545,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>deletedelivery</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>deleteintake</name> <name>deleteintake</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -1,26 +1,50 @@
import { Button, Divider, Popover, Space } from "antd";
import { AlertFilled } from "@ant-design/icons"; 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 React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; 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 { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component"; import DataLabel from "../data-label/data-label.component";
import ScheduleAtChange from "./job-at-change.component"; import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventColor from "./schedule-event.color.component";
import queryString from "query-string";
import ScheduleEventNote from "./schedule-event.note.component"; import ScheduleEventNote from "./schedule-event.note.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setScheduleContext: (context) => setScheduleContext: (context) =>
dispatch(setModalContext({ context: context, modal: "schedule" })), dispatch(setModalContext({ context: context, modal: "schedule" })),
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
}); });
export function ScheduleEventComponent({ export function ScheduleEventComponent({
bodyshop,
setMessage,
openChatByPhone,
event, event,
refetch, refetch,
handleCancel, handleCancel,
@@ -77,9 +101,10 @@ export function ScheduleEventComponent({
{(event.job && event.job.ownr_ea) || ""} {(event.job && event.job.ownr_ea) || ""}
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.fields.ownr_ph1")}> <DataLabel label={t("jobs.fields.ownr_ph1")}>
<PhoneFormatter> <ChatOpenButton
{(event.job && event.job.ownr_ph1) || ""} phone={event.job && event.job.ownr_ph1}
</PhoneFormatter> jobid={event.job.id}
/>
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.fields.alt_transport")}> <DataLabel label={t("jobs.fields.alt_transport")}>
{(event.job && event.job.alt_transport) || ""} {(event.job && event.job.alt_transport) || ""}
@@ -109,23 +134,62 @@ export function ScheduleEventComponent({
{t("appointments.actions.preview")} {t("appointments.actions.preview")}
</Button> </Button>
) : null} ) : null}
<Button
onClick={() => { <Dropdown
const Template = TemplateList("job").appointment_reminder; overlay={
GenerateDocument( <Menu>
{ <Menu.Item
name: Template.key, onClick={() => {
variables: { id: event.job.id }, const Template = TemplateList("job").appointment_reminder;
}, GenerateDocument(
{ to: event.job && event.job.ownr_ea, subject: Template.subject }, {
"e", name: Template.key,
event.job && event.job.id variables: { id: event.job.id },
); },
}} {
disabled={event.arrived} 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>
}
> >
{t("appointments.actions.sendreminder")} <Button>{t("appointments.actions.sendreminder")}</Button>
</Button> </Dropdown>
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}> <Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
{t("appointments.actions.cancel")} {t("appointments.actions.cancel")}
</Button> </Button>
@@ -206,4 +270,7 @@ export function ScheduleEventComponent({
</Popover> </Popover>
); );
} }
export default connect(null, mapDispatchToProps)(ScheduleEventComponent); export default connect(
mapStateToProps,
mapDispatchToProps
)(ScheduleEventComponent);

View File

@@ -18,6 +18,7 @@ import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker
import moment from "moment-business-days"; import moment from "moment-business-days";
import { insertAuditTrail } from "../../../../redux/application/application.actions"; import { insertAuditTrail } from "../../../../redux/application/application.actions";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -41,6 +42,8 @@ export function JobChecklistForm({
const [intakeJob] = useMutation(UPDATE_JOB); const [intakeJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED); const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED);
const [updateOwner] = useMutation(UPDATE_OWNER);
const { jobId } = useParams(); const { jobId } = useParams();
const history = useHistory(); const history = useHistory();
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
@@ -114,6 +117,23 @@ export function JobChecklistForm({
}); });
} }
} }
//Updae Owner Allow to Text
const updateOwnerResult = await updateOwner({
variables: {
ownerId: job.owner.id,
owner: { allow_text_message: values.allow_text_message },
},
});
if (!!updateOwnerResult.errors) {
notification["error"]({
message: t("checklist.errors.complete", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false); setLoading(false);
if (!!!result.errors) { if (!!!result.errors) {
@@ -156,6 +176,7 @@ export function JobChecklistForm({
initialValues={{ initialValues={{
...(type === "intake" && { ...(type === "intake" && {
addToProduction: true, addToProduction: true,
allow_text_message: job.owner.allow_text_message,
scheduled_completion: scheduled_completion:
(job && job.scheduled_completion) || (job && job.scheduled_completion) ||
moment().businessAdd( moment().businessAdd(
@@ -191,6 +212,14 @@ export function JobChecklistForm({
> >
<Switch disabled={readOnly} /> <Switch disabled={readOnly} />
</Form.Item> </Form.Item>
<Form.Item
name="allow_text_message"
valuePropName="checked"
label={t("checklist.labels.allow_text_message")}
disabled={readOnly}
>
<Switch disabled={readOnly} />
</Form.Item>
<Form.Item <Form.Item
name="scheduled_completion" name="scheduled_completion"
label={t("jobs.fields.scheduled_completion")} label={t("jobs.fields.scheduled_completion")}

View File

@@ -15,6 +15,7 @@ const JobSearchSelect = (
{ {
disabled, disabled,
convertedOnly = false, convertedOnly = false,
notInvoiced = false,
notExported = true, notExported = true,
clm_no = false, clm_no = false,
...restProps ...restProps
@@ -30,6 +31,7 @@ const JobSearchSelect = (
variables: { variables: {
...(convertedOnly ? { isConverted: true } : {}), ...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}), ...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
}, },
} }
: {}), : {}),

View File

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

View File

@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -21,8 +22,8 @@ export default connect(
export function JobAdminMarkReexport({ bodyshop, job }) { export function JobAdminMarkReexport({ bodyshop, job }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(gql` const [markJobForReexport] = useMutation(gql`
mutation UPDATE_JOB($jobId: uuid!) { mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
update_jobs_by_pk( update_jobs_by_pk(
pk_columns: { id: $jobId } pk_columns: { id: $jobId }
_set: { date_exported: null _set: { date_exported: null
@@ -30,14 +31,84 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
} }
) { ) {
id id
intakechecklist date_exported
status
date_invoiced
} }
} }
`); `);
const handleUpdate = async (values) => { 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 () => {
setLoading(true); setLoading(true);
const result = await updateJob({ 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({
variables: { jobId: job.id }, variables: { jobId: job.id },
}); });
@@ -51,16 +122,31 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
}); });
} }
setLoading(false); setLoading(false);
//Get the owner details, populate it all back into the job.
}; };
return ( return (
<Button <>
loading={loading} <Button
disabled={!job.date_exported} loading={loading}
onClick={handleUpdate} disabled={!job.date_exported}
> onClick={handleMarkForExport}
{t("jobs.labels.markforreexport")} >
</Button> {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>
</>
); );
} }

View File

@@ -75,7 +75,7 @@ export function JobsAvailableContainer({
const client = useApolloClient(); const client = useApolloClient();
const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK); const estDataLazyLoad = useLazyQuery(QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK);
const [loadEstData, estData] = estDataLazyLoad; const [loadEstData, estDataRaw] = estDataLazyLoad;
const importOptionsState = useState({ overrideHeaders: false }); const importOptionsState = useState({ overrideHeaders: false });
const importOptions = importOptionsState[0]; const importOptions = importOptionsState[0];
@@ -88,11 +88,13 @@ export function JobsAvailableContainer({
setOwnerModalVisible(false); setOwnerModalVisible(false);
setInsertLoading(true); setInsertLoading(true);
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
if ( if (
!( !(
estData.data &&
estData.data.available_jobs_by_pk && estData &&
estData.data.available_jobs_by_pk.est_data estData.est_data
) )
) { ) {
//We don't have the right data. Error! //We don't have the right data. Error!
@@ -106,28 +108,28 @@ export function JobsAvailableContainer({
const newTotals = ( const newTotals = (
await Axios.post("/job/totals", { await Axios.post("/job/totals", {
job: { job: {
...estData.data.available_jobs_by_pk.est_data, ...estData.est_data,
joblines: estData.data.available_jobs_by_pk.est_data.joblines.data, joblines: estData.est_data.joblines.data,
}, },
}) })
).data; ).data;
let existingVehicles; let existingVehicles;
if ( if (
estData.data.available_jobs_by_pk.est_data.vehicle && estData.est_data.vehicle &&
estData.data.available_jobs_by_pk.est_data.vin estData.est_data.vin
) { ) {
//There's vehicle data, need to double check the VIN. //There's vehicle data, need to double check the VIN.
existingVehicles = await client.query({ existingVehicles = await client.query({
query: SEARCH_VEHICLE_BY_VIN, query: SEARCH_VEHICLE_BY_VIN,
variables: { variables: {
vin: estData.data.available_jobs_by_pk.est_data.vehicle.data.v_vin, vin: estData.est_data.vehicle.data.v_vin,
}, },
}); });
} }
const newJob = { const newJob = {
...estData.data.available_jobs_by_pk.est_data, ...estData.est_data,
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"), clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"), owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
job_totals: newTotals, job_totals: newTotals,
@@ -172,7 +174,7 @@ export function JobsAvailableContainer({
}); });
deleteJob({ deleteJob({
variables: { id: estData.data.available_jobs_by_pk.id }, variables: { id: estData.id },
}).then((r) => { }).then((r) => {
refetch(); refetch();
setInsertLoading(false); setInsertLoading(false);
@@ -194,12 +196,12 @@ export function JobsAvailableContainer({
setJobModalVisible(false); setJobModalVisible(false);
setInsertLoading(true); setInsertLoading(true);
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
if ( if (
!( !(
estData.data &&
estData.data.available_jobs_by_pk && estData &&
estData.data.available_jobs_by_pk.est_data estData.est_data
) )
) { ) {
//We don't have the right data. Error! //We don't have the right data. Error!
@@ -209,7 +211,7 @@ export function JobsAvailableContainer({
}); });
} else { } else {
//create upsert job //create upsert job
let supp = _.cloneDeep(estData.data.available_jobs_by_pk.est_data); let supp = _.cloneDeep(estData.est_data);
delete supp.owner; delete supp.owner;
delete supp.vehicle; delete supp.vehicle;
@@ -220,7 +222,7 @@ export function JobsAvailableContainer({
let suppDelta = await GetSupplementDelta( let suppDelta = await GetSupplementDelta(
client, client,
selectedJob, selectedJob,
estData.data.available_jobs_by_pk.est_data.joblines.data estData.est_data.joblines.data
); );
delete supp.joblines; delete supp.joblines;
@@ -279,7 +281,7 @@ export function JobsAvailableContainer({
//Job has been inserted. Clean up the available jobs record. //Job has been inserted. Clean up the available jobs record.
deleteJob({ deleteJob({
variables: { id: estData.data.available_jobs_by_pk.id }, variables: { id: estData.id },
}).then((r) => { }).then((r) => {
refetch(); refetch();
setInsertLoading(false); setInsertLoading(false);
@@ -305,13 +307,13 @@ export function JobsAvailableContainer({
}; };
const owner = const owner =
estData.data && estDataRaw.data &&
estData.data.available_jobs_by_pk && estDataRaw.data.available_jobs_by_pk &&
estData.data.available_jobs_by_pk.est_data && estDataRaw.data.available_jobs_by_pk.est_data &&
estData.data.available_jobs_by_pk.est_data.owner && estDataRaw.data.available_jobs_by_pk.est_data.owner &&
estData.data.available_jobs_by_pk.est_data.owner.data && estDataRaw.data.available_jobs_by_pk.est_data.owner.data &&
!estData.data.available_jobs_by_pk.issupplement !estDataRaw.data.available_jobs_by_pk.issupplement
? estData.data.available_jobs_by_pk.est_data.owner.data ? estDataRaw.data.available_jobs_by_pk.est_data.owner.data
: null; : null;
const onOwnerModalCancel = () => { const onOwnerModalCancel = () => {
@@ -349,8 +351,8 @@ export function JobsAvailableContainer({
message={t("jobs.labels.creating_new_job")} message={t("jobs.labels.creating_new_job")}
> >
<OwnerFindModalContainer <OwnerFindModalContainer
loading={estData.loading} loading={estDataRaw.loading}
error={estData.error} error={estDataRaw.error}
owner={owner} owner={owner}
selectedOwner={selectedOwner} selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner} setSelectedOwner={setSelectedOwner}
@@ -359,8 +361,8 @@ export function JobsAvailableContainer({
onCancel={onOwnerModalCancel} onCancel={onOwnerModalCancel}
/> />
<JobsFindModalContainer <JobsFindModalContainer
loading={estData.loading} loading={estDataRaw.loading}
error={estData.error} error={estDataRaw.error}
selectedJob={selectedJob} selectedJob={selectedJob}
setSelectedJob={setSelectedJob} setSelectedJob={setSelectedJob}
importOptionsState={importOptionsState} importOptionsState={importOptionsState}
@@ -390,3 +392,12 @@ export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(JobsAvailableContainer); )(JobsAvailableContainer);
function replaceEmpty(someObj, replaceValue = null) {
const replacer = (key, value) => (value === "" ? replaceValue : value);
//^ because you seem to want to replace (strings) "null" or "undefined" too
console.log(someObj)
const temp = JSON.stringify(someObj, replacer);
console.log(`temp`, temp);
return JSON.parse(temp);
}

View File

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

View File

@@ -25,6 +25,7 @@ export function ProductionListTable({
currentUser, currentUser,
state, state,
setColumns, setColumns,
setState,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW); const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
@@ -43,6 +44,10 @@ export function ProductionListTable({
}; };
}) })
); );
setState(
bodyshop.production_config.filter((pc) => pc.name === value)[0].columns
.tableState
);
const assoc = bodyshop.associations.find( const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email (a) => a.useremail === currentUser.email
@@ -77,6 +82,8 @@ export function ProductionListTable({
}; };
}) })
); );
setState(bodyshop.production_config[0].columns.tableState);
}; };
return ( return (

View File

@@ -41,9 +41,9 @@ export function ProductionListTable({
const [state, setState] = useState( const [state, setState] = useState(
(bodyshop.production_config && (bodyshop.production_config &&
bodyshop.production_config.find((p) => p.name === defaultView) bodyshop.production_config.find((p) => p.name === defaultView)?.columns
?.tableState) || .tableState) ||
bodyshop.production_config[0]?.tableState || { bodyshop.production_config[0]?.columns.tableState || {
sortedInfo: {}, sortedInfo: {},
filteredInfo: { text: "" }, filteredInfo: { text: "" },
} }
@@ -155,7 +155,7 @@ export function ProductionListTable({
// } // }
// }; // };
if (!!!columns) return <div>No columns found.</div>; if (!!!columns || columns.length === 0) return <div>No columns found.</div>;
return ( return (
<div> <div>
@@ -173,6 +173,7 @@ export function ProductionListTable({
<ProductionListTableViewSelect <ProductionListTableViewSelect
state={state} state={state}
setState={setState}
setColumns={setColumns} setColumns={setColumns}
/> />

View File

@@ -444,6 +444,13 @@ export default function ShopInfoGeneral({ form }) {
> >
<Switch /> <Switch />
</Form.Item> </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>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}> <LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}> <Form.List name={["md_messaging_presets"]}>

View File

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

View File

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

View File

@@ -105,7 +105,8 @@ export function TimeTicketList({
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number), sorter: (a, b) =>
alphaSort(a.job && a.job.ro_number, b.job && b.job.ro_number),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => render: (text, record) =>

View File

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

View File

@@ -92,6 +92,7 @@ export const QUERY_BODYSHOP = gql`
cdk_dealerid cdk_dealerid
features features
attach_pdf_to_email attach_pdf_to_email
tt_allow_post_to_invoiced
employees { employees {
id id
active active
@@ -180,6 +181,7 @@ export const UPDATE_SHOP = gql`
md_jobline_presets md_jobline_presets
cdk_dealerid cdk_dealerid
attach_pdf_to_email attach_pdf_to_email
tt_allow_post_to_invoiced
employees { employees {
id id
first_name first_name
@@ -206,6 +208,10 @@ export const QUERY_INTAKE_CHECKLIST = gql`
scheduled_delivery scheduled_delivery
intakechecklist intakechecklist
status status
owner {
allow_text_message
id
}
labhrs: joblines_aggregate( labhrs: joblines_aggregate(
where: { where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }] _and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]

View File

@@ -1025,6 +1025,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
$search: String $search: String
$isConverted: Boolean $isConverted: Boolean
$notExported: Boolean $notExported: Boolean
$notInvoiced: Boolean
) { ) {
search_jobs( search_jobs(
args: { search: $search } args: { search: $search }
@@ -1033,6 +1034,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
_and: { _and: {
converted: { _eq: $isConverted } converted: { _eq: $isConverted }
date_exported: { _is_null: $notExported } date_exported: { _is_null: $notExported }
date_invoiced: { _is_null: $notInvoiced }
} }
} }
) { ) {

View File

@@ -1,5 +1,5 @@
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { Card, Col, Result, Row, Space } from "antd"; import { Card, Col, Result, Row, Space, Typography } from "antd";
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -84,6 +84,9 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
return ( return (
<RbacWrapper action="jobs:admin"> <RbacWrapper action="jobs:admin">
<Typography.Title level={4} style={{ color: "tomato" }}>
{t("jobs.labels.adminwarning")}
</Typography.Title>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col {...colSpan}> <Col {...colSpan}>
<Card style={cardStyle}> <Card style={cardStyle}>

View File

@@ -53,6 +53,7 @@
"nocompletingjobs": "No jobs scheduled for completion.", "nocompletingjobs": "No jobs scheduled for completion.",
"nodateselected": "No date has been selected.", "nodateselected": "No date has been selected.",
"priorappointments": "Previous Appointments", "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: ", "scheduledfor": "Scheduled appointment for: ",
"smartscheduling": "Smart Scheduling", "smartscheduling": "Smart Scheduling",
"suggesteddates": "Suggested Dates" "suggesteddates": "Suggested Dates"
@@ -445,6 +446,7 @@
"production_statuses": "Production Statuses" "production_statuses": "Production Statuses"
}, },
"target_touchtime": "Target Touch Time", "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?", "use_fippa": "Use FIPPA for Names on Generated Documents?",
"website": "Website", "website": "Website",
"zip_post": "Zip/Postal Code" "zip_post": "Zip/Postal Code"
@@ -510,6 +512,7 @@
}, },
"labels": { "labels": {
"addtoproduction": "Add Job to Production?", "addtoproduction": "Add Job to Production?",
"allow_text_message": "Permission to Text?",
"checklist": "Checklist", "checklist": "Checklist",
"printpack": "Job Intake Print Pack", "printpack": "Job Intake Print Pack",
"removefromproduction": "Remove job from production?" "removefromproduction": "Remove job from production?"
@@ -897,6 +900,7 @@
"sendagain": "Send Again", "sendagain": "Send Again",
"sendby": "Send By", "sendby": "Send By",
"signin": "Sign In", "signin": "Sign In",
"sms": "SMS",
"sub_status": { "sub_status": {
"expired": "The subscription for this shop has expired. Please contact technical support to reactivate the subscription. " "expired": "The subscription for this shop has expired. Please contact technical support to reactivate the subscription. "
}, },
@@ -1056,6 +1060,7 @@
"intake": "Intake", "intake": "Intake",
"manualnew": "Create New Job Manually", "manualnew": "Create New Job Manually",
"mark": "Mark", "mark": "Mark",
"markasexported": "Mark as Exported",
"markpstexempt": "Mark Job PST Exempt", "markpstexempt": "Mark Job PST Exempt",
"markpstexemptconfirm": "Are you sure you want to do this? To undo this, you must manually update all PST rates.", "markpstexemptconfirm": "Are you sure you want to do this? To undo this, you must manually update all PST rates.",
"postbills": "Post Bills", "postbills": "Post Bills",
@@ -1066,6 +1071,7 @@
"schedule": "Schedule", "schedule": "Schedule",
"sendcsi": "Send CSI", "sendcsi": "Send CSI",
"sync": "Sync", "sync": "Sync",
"uninvoice": "Uninvoice",
"unvoid": "Unvoid Job", "unvoid": "Unvoid Job",
"viewchecklist": "View Checklists", "viewchecklist": "View Checklists",
"viewdetail": "View Details" "viewdetail": "View Details"
@@ -1285,6 +1291,7 @@
"additionaltotal": "Additional Total", "additionaltotal": "Additional Total",
"adjustmentrate": "Adjustment Rate", "adjustmentrate": "Adjustment Rate",
"adjustments": "Adjustments", "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", "allocations": "Allocations",
"alreadyclosed": "This job has already been closed.", "alreadyclosed": "This job has already been closed.",
"appointmentconfirmation": "Send confirmation to customer?", "appointmentconfirmation": "Send confirmation to customer?",
@@ -1337,7 +1344,8 @@
"waived": "Waived" "waived": "Waived"
}, },
"deleteconfirm": "Are you sure you want to delete this job? This cannot be undone. ", "deleteconfirm": "Are you sure you want to delete this job? This cannot be undone. ",
"deleteintake": "Delete Intake", "deletedelivery": "Delete Delivery Checklist",
"deleteintake": "Delete Intake Checklist",
"deliverchecklist": "Deliver Checklist", "deliverchecklist": "Deliver Checklist",
"difference": "Difference", "difference": "Difference",
"diskscan": "Scan Disk for Estimates", "diskscan": "Scan Disk for Estimates",

View File

@@ -53,6 +53,7 @@
"nocompletingjobs": "", "nocompletingjobs": "",
"nodateselected": "No se ha seleccionado ninguna fecha.", "nodateselected": "No se ha seleccionado ninguna fecha.",
"priorappointments": "Nombramientos previos", "priorappointments": "Nombramientos previos",
"reminder": "",
"scheduledfor": "Cita programada para:", "scheduledfor": "Cita programada para:",
"smartscheduling": "", "smartscheduling": "",
"suggesteddates": "" "suggesteddates": ""
@@ -445,6 +446,7 @@
"production_statuses": "" "production_statuses": ""
}, },
"target_touchtime": "", "target_touchtime": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "", "use_fippa": "",
"website": "", "website": "",
"zip_post": "" "zip_post": ""
@@ -510,6 +512,7 @@
}, },
"labels": { "labels": {
"addtoproduction": "", "addtoproduction": "",
"allow_text_message": "",
"checklist": "", "checklist": "",
"printpack": "", "printpack": "",
"removefromproduction": "" "removefromproduction": ""
@@ -897,6 +900,7 @@
"sendagain": "", "sendagain": "",
"sendby": "", "sendby": "",
"signin": "", "signin": "",
"sms": "",
"sub_status": { "sub_status": {
"expired": "" "expired": ""
}, },
@@ -1056,6 +1060,7 @@
"intake": "", "intake": "",
"manualnew": "", "manualnew": "",
"mark": "", "mark": "",
"markasexported": "",
"markpstexempt": "", "markpstexempt": "",
"markpstexemptconfirm": "", "markpstexemptconfirm": "",
"postbills": "Contabilizar facturas", "postbills": "Contabilizar facturas",
@@ -1066,6 +1071,7 @@
"schedule": "Programar", "schedule": "Programar",
"sendcsi": "", "sendcsi": "",
"sync": "", "sync": "",
"uninvoice": "",
"unvoid": "", "unvoid": "",
"viewchecklist": "", "viewchecklist": "",
"viewdetail": "" "viewdetail": ""
@@ -1285,6 +1291,7 @@
"additionaltotal": "", "additionaltotal": "",
"adjustmentrate": "", "adjustmentrate": "",
"adjustments": "", "adjustments": "",
"adminwarning": "",
"allocations": "", "allocations": "",
"alreadyclosed": "", "alreadyclosed": "",
"appointmentconfirmation": "¿Enviar confirmación al cliente?", "appointmentconfirmation": "¿Enviar confirmación al cliente?",
@@ -1337,6 +1344,7 @@
"waived": "" "waived": ""
}, },
"deleteconfirm": "", "deleteconfirm": "",
"deletedelivery": "",
"deleteintake": "", "deleteintake": "",
"deliverchecklist": "", "deliverchecklist": "",
"difference": "", "difference": "",

View File

@@ -53,6 +53,7 @@
"nocompletingjobs": "", "nocompletingjobs": "",
"nodateselected": "Aucune date n'a été sélectionnée.", "nodateselected": "Aucune date n'a été sélectionnée.",
"priorappointments": "Rendez-vous précédents", "priorappointments": "Rendez-vous précédents",
"reminder": "",
"scheduledfor": "Rendez-vous prévu pour:", "scheduledfor": "Rendez-vous prévu pour:",
"smartscheduling": "", "smartscheduling": "",
"suggesteddates": "" "suggesteddates": ""
@@ -445,6 +446,7 @@
"production_statuses": "" "production_statuses": ""
}, },
"target_touchtime": "", "target_touchtime": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "", "use_fippa": "",
"website": "", "website": "",
"zip_post": "" "zip_post": ""
@@ -510,6 +512,7 @@
}, },
"labels": { "labels": {
"addtoproduction": "", "addtoproduction": "",
"allow_text_message": "",
"checklist": "", "checklist": "",
"printpack": "", "printpack": "",
"removefromproduction": "" "removefromproduction": ""
@@ -897,6 +900,7 @@
"sendagain": "", "sendagain": "",
"sendby": "", "sendby": "",
"signin": "", "signin": "",
"sms": "",
"sub_status": { "sub_status": {
"expired": "" "expired": ""
}, },
@@ -1056,6 +1060,7 @@
"intake": "", "intake": "",
"manualnew": "", "manualnew": "",
"mark": "", "mark": "",
"markasexported": "",
"markpstexempt": "", "markpstexempt": "",
"markpstexemptconfirm": "", "markpstexemptconfirm": "",
"postbills": "Poster des factures", "postbills": "Poster des factures",
@@ -1066,6 +1071,7 @@
"schedule": "Programme", "schedule": "Programme",
"sendcsi": "", "sendcsi": "",
"sync": "", "sync": "",
"uninvoice": "",
"unvoid": "", "unvoid": "",
"viewchecklist": "", "viewchecklist": "",
"viewdetail": "" "viewdetail": ""
@@ -1285,6 +1291,7 @@
"additionaltotal": "", "additionaltotal": "",
"adjustmentrate": "", "adjustmentrate": "",
"adjustments": "", "adjustments": "",
"adminwarning": "",
"allocations": "", "allocations": "",
"alreadyclosed": "", "alreadyclosed": "",
"appointmentconfirmation": "Envoyer une confirmation au client?", "appointmentconfirmation": "Envoyer une confirmation au client?",
@@ -1337,6 +1344,7 @@
"waived": "" "waived": ""
}, },
"deleteconfirm": "", "deleteconfirm": "",
"deletedelivery": "",
"deleteintake": "", "deleteintake": "",
"deliverchecklist": "", "deliverchecklist": "",
"difference": "", "difference": "",

View File

@@ -6,7 +6,6 @@ import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities"; import { getMainDefinition } from "@apollo/client/utilities";
//import { split } from "apollo-link"; //import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger"; import apolloLogger from "apollo-link-logger";
import axios from "axios";
import { auth } from "../firebase/firebase.utils"; import { auth } from "../firebase/firebase.utils";
import errorLink from "../graphql/apollo-error-handling"; import errorLink from "../graphql/apollo-error-handling";
@@ -48,7 +47,7 @@ const roundTripLink = new ApolloLink((operation, forward) => {
}); });
const TrackExecutionTime = async (operationName, time) => { const TrackExecutionTime = async (operationName, time) => {
await axios.post("/ioevent", { operationName, time, dbevent: true }); //await axios.post("/ioevent", { operationName, time, dbevent: true });
}; };
const subscriptionMiddleware = { const subscriptionMiddleware = {

View File

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

View File

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

View File

@@ -0,0 +1,88 @@
- 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

@@ -0,0 +1,89 @@
- 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
- tt_allow_post_to_invoiced
- 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

@@ -0,0 +1,80 @@
- 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

@@ -0,0 +1,81 @@
- 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
- tt_allow_post_to_invoiced
- 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

@@ -850,6 +850,7 @@ tables:
- target_touchtime - target_touchtime
- template_header - template_header
- textid - textid
- tt_allow_post_to_invoiced
- updated_at - updated_at
- use_fippa - use_fippa
- website - website
@@ -916,6 +917,7 @@ tables:
- state - state
- state_tax_id - state_tax_id
- target_touchtime - target_touchtime
- tt_allow_post_to_invoiced
- updated_at - updated_at
- use_fippa - use_fippa
- website - website

View File

@@ -76,7 +76,7 @@ exports.default = async (req, res) => {
allxmlsToUpload.push({ allxmlsToUpload.push({
xml: ret, xml: ret,
filename: `IM_${bodyshop.imexshopid}_${moment().format( filename: `IM_${bodyshop.autohouseid}_${moment().format(
"DDMMYYYY_HHMMSS" "DDMMYYYY_HHMMSS"
)}.xml`, )}.xml`,
}); });