Compare commits
38 Commits
release/20
...
release/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59db305cb8 | ||
|
|
fea69fe3a5 | ||
|
|
43e4ff911e | ||
|
|
ae4cff98e7 | ||
|
|
3650cacb51 | ||
|
|
c2bf6841e1 | ||
|
|
f41b94d16d | ||
|
|
24da0207e5 | ||
|
|
bf34765e6b | ||
|
|
4c98a347f5 | ||
|
|
840e760619 | ||
|
|
739265ee6a | ||
|
|
038aaf249e | ||
|
|
e0eb4657d2 | ||
|
|
02a6ccd481 | ||
|
|
79e75a5e73 | ||
|
|
8d22248f4b | ||
|
|
bb8024ba9c | ||
|
|
a960963e36 | ||
|
|
3be50b5067 | ||
|
|
563c1d2402 | ||
|
|
2108a4e96c | ||
|
|
c04a690dc3 | ||
|
|
20e84668a5 | ||
|
|
2e40583d31 | ||
|
|
07c307e17b | ||
|
|
176774a888 | ||
|
|
7b3dcf295e | ||
|
|
380dbd8b96 | ||
|
|
a34c2a5bc2 | ||
|
|
d8ba40979e | ||
|
|
5cd0527e16 | ||
|
|
25513ae5b5 | ||
|
|
d89acbd49d | ||
|
|
a54862a309 | ||
|
|
423157dfcc | ||
|
|
6607c80aca | ||
|
|
162aeca7c8 |
13
README.MD
13
README.MD
@@ -1,14 +1,3 @@
|
||||
Yarn Dependency Management:
|
||||
To force upgrades for some packages:
|
||||
yarn upgrade-interactive --latest
|
||||
|
||||
To Start Hasura CLI:
|
||||
npx hasura console
|
||||
|
||||
Migrating to Staging:
|
||||
npx hasura migrate apply --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
|
||||
npx hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret 'Test-ImEXOnlineBySnaptSoftware!'
|
||||
|
||||
NGROK TEsting:
|
||||
./ngrok.exe http http://localhost:4000 -host-header="localhost:4000"
|
||||
|
||||
@@ -21,4 +10,4 @@ hasura migrate apply --version "1620771761757" --skip-execution --endpoint https
|
||||
hasura migrate status --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
|
||||
|
||||
Generate the license file:
|
||||
$ generate-license-file --input package.json --output third-party-licenses.txt --overwrite
|
||||
$ generate-license-file --input package.json --output third-party-licenses.txt --overwrite
|
||||
@@ -1,4 +1,4 @@
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -736,6 +736,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>expectedjobs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>expectedprodhrs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -946,6 +967,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>severalerrorsfound</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>smartscheduling</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -40859,6 +40901,69 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>gsr_by_atp</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>gsr_by_ats</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>gsr_by_category</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>gsr_by_csr</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -41489,6 +41594,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>jobs_completed_not_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>
|
||||
<name>jobs_invoiced_not_exported</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>jobs_reconcile</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -41615,6 +41762,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>open_orders_specific_csr</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>open_orders_status</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -42224,6 +42392,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>scheduled_parts_list</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>scoreboard_detail</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useLazyQuery } from "@apollo/client";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import { AutoComplete, Divider, Space } from "antd";
|
||||
import { AutoComplete, Divider, Input, Space } from "antd";
|
||||
import _ from "lodash";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -9,7 +8,7 @@ import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
OwnerNameDisplayFunction
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||
export default function GlobalSearch() {
|
||||
@@ -178,13 +177,18 @@ export default function GlobalSearch() {
|
||||
<AutoComplete
|
||||
options={options}
|
||||
onSearch={handleSearch}
|
||||
suffixIcon={loading && <LoadingOutlined spin />}
|
||||
defaultActiveFirstOption
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
allowClear
|
||||
onSelect={(val, opt) => {
|
||||
history.push(opt.label.props.to);
|
||||
}}
|
||||
></AutoComplete>
|
||||
>
|
||||
<Input.Search
|
||||
size="large"
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
enterButton
|
||||
allowClear
|
||||
loading={loading}
|
||||
/>
|
||||
</AutoComplete>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,20 @@ export function JoblinePresetButton({ bodyshop, form }) {
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu
|
||||
style={{
|
||||
columnCount: Math.max(
|
||||
Math.floor(bodyshop.md_jobline_presets.length / 15),
|
||||
1
|
||||
),
|
||||
}}
|
||||
>
|
||||
{bodyshop.md_jobline_presets.map((i, idx) => (
|
||||
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
|
||||
<Menu.Item
|
||||
onClick={() => handleSelect(i)}
|
||||
key={idx}
|
||||
style={{ breakInside: "avoid" }}
|
||||
>
|
||||
{i.label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
|
||||
@@ -166,16 +166,16 @@ export function JobLinesUpsertModalComponent({
|
||||
name="ah_detail_line"
|
||||
valuePropName="checked"
|
||||
dependencies={["mod_lbr_ty"]}
|
||||
initialValue={false}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
console.log(
|
||||
value === true,
|
||||
["LA1", "LA2", "LA3", "LA4", "LAU"].includes(
|
||||
getFieldValue("mod_lbr_ty")
|
||||
)
|
||||
);
|
||||
if (value === false) return Promise.resolve();
|
||||
if (
|
||||
value === false ||
|
||||
value === undefined ||
|
||||
value === null
|
||||
)
|
||||
return Promise.resolve();
|
||||
if (
|
||||
value === true &&
|
||||
["LA1", "LA2", "LA3", "LA4", "LAU"].includes(
|
||||
@@ -289,7 +289,7 @@ export function JobLinesUpsertModalComponent({
|
||||
name="prt_dsmk_p"
|
||||
initialValue={0}
|
||||
>
|
||||
<InputNumber precision={0} min={0} max={100} />
|
||||
<InputNumber precision={0} min={-100} max={100} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.tax_part")}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { selectJobLineEditModal } from "../../redux/modals/modals.selectors";
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobLineEditModal: selectJobLineEditModal,
|
||||
});
|
||||
@@ -40,7 +41,15 @@ function JobLinesUpsertModalContainer({
|
||||
manual_line: !(
|
||||
jobLineEditModal.context && jobLineEditModal.context.id
|
||||
),
|
||||
...UndefinedToNull(values),
|
||||
...UndefinedToNull({
|
||||
...values,
|
||||
prt_dsmk_m: Dinero({
|
||||
amount: Math.round((values.act_price || 0) * 100),
|
||||
})
|
||||
.percentage(Math.abs(values.prt_dsmk_p || 0))
|
||||
.multiply(values.prt_dsmk_p >= 0 ? 1 : -1)
|
||||
.toFormat(0.0),
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -68,7 +77,15 @@ function JobLinesUpsertModalContainer({
|
||||
const r = await updateJobLine({
|
||||
variables: {
|
||||
lineId: jobLineEditModal.context.id,
|
||||
line: values,
|
||||
line: {
|
||||
...values,
|
||||
prt_dsmk_m: Dinero({
|
||||
amount: Math.round(values.act_price * 100),
|
||||
})
|
||||
.percentage(Math.abs(values.prt_dsmk_p || 0))
|
||||
.multiply(values.prt_dsmk_p >= 0 ? 1 : -1)
|
||||
.toFormat(0.0),
|
||||
},
|
||||
},
|
||||
refetchQueries: ["GET_LINE_TICKET_BY_PK"],
|
||||
});
|
||||
|
||||
@@ -43,14 +43,21 @@ export function JobsConvertButton({
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleConvert = async (values) => {
|
||||
const handleConvert = async ({ employee_csr, ...values }) => {
|
||||
if (parentFormIsFieldsTouched()) {
|
||||
alert(t("jobs.labels.savebeforeconversion"));
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await mutationConvertJob({
|
||||
variables: { jobId: job.id, ...values },
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
converted: true,
|
||||
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
|
||||
...values,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (values.ca_gst_registrant) {
|
||||
|
||||
@@ -256,7 +256,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
</FormRow>
|
||||
<FormRow header={t("jobs.forms.other")}>
|
||||
<Form.Item label={t("jobs.fields.category")} name="category">
|
||||
<Select disabled={jobRO}>
|
||||
<Select disabled={jobRO} allowClear>
|
||||
{bodyshop.md_categories.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
|
||||
@@ -34,7 +34,9 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
||||
render: (text, record) =>
|
||||
record.vehicleid ? (
|
||||
<Link to={`/manage/vehicles/${record.vehicleid}`}>
|
||||
{`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc}`}
|
||||
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
}`.trim()}
|
||||
</Link>
|
||||
) : (
|
||||
t("jobs.errors.novehicle")
|
||||
|
||||
@@ -4,11 +4,13 @@ import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
Legend, PolarAngleAxis,
|
||||
Legend,
|
||||
PolarAngleAxis,
|
||||
PolarGrid,
|
||||
PolarRadiusAxis,
|
||||
Radar, RadarChart,
|
||||
Tooltip
|
||||
Radar,
|
||||
RadarChart,
|
||||
Tooltip,
|
||||
} from "recharts";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
@@ -44,6 +46,8 @@ export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
|
||||
<Space>
|
||||
{t("appointments.labels.expectedprodhrs")}
|
||||
<strong>{loadData?.expectedHours?.toFixed(1)}</strong>
|
||||
{t("appointments.labels.expectedjobs")}
|
||||
<strong>{loadData?.expectedJobCount}</strong>
|
||||
</Space>
|
||||
<RadarChart
|
||||
// cx={300}
|
||||
|
||||
@@ -66,8 +66,8 @@ export function ScheduleCalendarHeaderComponent({
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<table>
|
||||
<tbody>
|
||||
{loadData && loadData.jobsOut ? (
|
||||
loadData.jobsOut.map((j) => (
|
||||
{loadData && loadData.allJobsOut ? (
|
||||
loadData.allJobsOut.map((j) => (
|
||||
<tr key={j.id}>
|
||||
<td>
|
||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||
@@ -102,11 +102,12 @@ export function ScheduleCalendarHeaderComponent({
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<table>
|
||||
<tbody>
|
||||
{loadData && loadData.jobsIn ? (
|
||||
loadData.jobsIn.map((j) => (
|
||||
{loadData && loadData.allJobsIn ? (
|
||||
loadData.allJobsIn.map((j) => (
|
||||
<tr key={j.id}>
|
||||
<td>
|
||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||
{j.status}
|
||||
</td>
|
||||
<td>
|
||||
<OwnerNameDisplay ownerObject={j} />
|
||||
@@ -142,7 +143,7 @@ export function ScheduleCalendarHeaderComponent({
|
||||
title={t("appointments.labels.arrivingjobs")}
|
||||
>
|
||||
<Icon component={MdFileDownload} style={{ color: "green" }} />
|
||||
{(loadData.hoursIn || 0) && loadData.hoursIn.toFixed(2)}
|
||||
{(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(2)}
|
||||
</Popover>
|
||||
<Popover
|
||||
placement={"bottom"}
|
||||
@@ -151,7 +152,7 @@ export function ScheduleCalendarHeaderComponent({
|
||||
title={t("appointments.labels.completingjobs")}
|
||||
>
|
||||
<Icon component={MdFileUpload} style={{ color: "red" }} />
|
||||
{(loadData.hoursOut || 0) && loadData.hoursOut.toFixed(2)}
|
||||
{(loadData.allHoursOut || 0) && loadData.allHoursOut.toFixed(2)}
|
||||
</Popover>
|
||||
<ScheduleCalendarHeaderGraph loadData={loadData} />
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ import HeaderComponent from "./schedule-calendar-header.component";
|
||||
import "./schedule-calendar.styles.scss";
|
||||
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
|
||||
import { selectProblemJobs } from "../../redux/application/application.selectors";
|
||||
import { Alert } from "antd";
|
||||
import { Alert, Collapse } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -53,7 +53,28 @@ export function ScheduleCalendarWrapperComponent({
|
||||
return (
|
||||
<>
|
||||
<JobDetailCards />
|
||||
{problemJobs &&
|
||||
{problemJobs && problemJobs.length > 2 ? (
|
||||
<Collapse>
|
||||
<Collapse.Panel
|
||||
header={
|
||||
<span style={{ color: "tomato" }}>
|
||||
{t("appointments.labels.severalerrorsfound")}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{problemJobs.map((problem) => (
|
||||
<Alert
|
||||
key={problem.id}
|
||||
type="error"
|
||||
message={t("appointments.labels.dataconsistency", {
|
||||
ro_number: problem.ro_number,
|
||||
code: problem.code,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
) : (
|
||||
problemJobs.map((problem) => (
|
||||
<Alert
|
||||
key={problem.id}
|
||||
@@ -63,7 +84,8 @@ export function ScheduleCalendarWrapperComponent({
|
||||
code: problem.code,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
|
||||
<Calendar
|
||||
events={data}
|
||||
|
||||
@@ -15,6 +15,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { calculateScheduleLoad } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
@@ -28,6 +29,7 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
calculateScheduleLoad: (endDate) => dispatch(calculateScheduleLoad(endDate)),
|
||||
});
|
||||
|
||||
export function ScheduleJobModalComponent({
|
||||
@@ -36,6 +38,7 @@ export function ScheduleJobModalComponent({
|
||||
existingAppointments,
|
||||
lbrHrsData,
|
||||
jobId,
|
||||
calculateScheduleLoad,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -57,6 +60,7 @@ export function ScheduleJobModalComponent({
|
||||
|
||||
const handleDateBlur = () => {
|
||||
const values = form.getFieldsValue();
|
||||
|
||||
if (lbrHrsData) {
|
||||
const totalHours =
|
||||
lbrHrsData.jobs_by_pk.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
@@ -130,7 +134,12 @@ export function ScheduleJobModalComponent({
|
||||
className="imex-flex-row__margin"
|
||||
key={idx}
|
||||
onClick={() => {
|
||||
form.setFieldsValue({ start: new moment(d).add(8, "hours") });
|
||||
const ssDate = moment(d);
|
||||
if (ssDate.isBefore(moment())) {
|
||||
form.setFieldsValue({ start: moment() });
|
||||
} else {
|
||||
form.setFieldsValue({ start: moment(d).add(8, "hours") });
|
||||
}
|
||||
handleDateBlur();
|
||||
}}
|
||||
>
|
||||
@@ -191,6 +200,9 @@ export function ScheduleJobModalComponent({
|
||||
<Form.Item shouldUpdate={(prev, cur) => prev.start !== cur.start}>
|
||||
{() => {
|
||||
const values = form.getFieldsValue();
|
||||
if (values.start) {
|
||||
calculateScheduleLoad(moment(values.start).add(3, "days"));
|
||||
}
|
||||
return (
|
||||
<div className="schedule-job-modal">
|
||||
<ScheduleDayViewContainer day={values.start} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.schedule-job-modal {
|
||||
height: 70vh;
|
||||
overflow-y: auto;
|
||||
.rbc-calendar {
|
||||
.rbc-toolbar {
|
||||
.rbc-btn-group {
|
||||
|
||||
@@ -85,7 +85,7 @@ export function TimeTicketList({
|
||||
text: (() => {
|
||||
const emp = bodyshop.employees.find((e) => e.id === s);
|
||||
|
||||
return `${emp.first_name} ${emp.last_name}`;
|
||||
return `${emp?.first_name} ${emp?.last_name}`;
|
||||
})(), //
|
||||
value: [s],
|
||||
};
|
||||
|
||||
@@ -131,6 +131,7 @@ const JobRelatedTicketsTable = ({
|
||||
|
||||
return {
|
||||
id: `${item.jobKey}${costCenter}`,
|
||||
costCenter,
|
||||
item,
|
||||
actHrs: actHrs.toFixed(1),
|
||||
prodHrs: prodHrs.toFixed(1),
|
||||
@@ -151,7 +152,9 @@ const JobRelatedTicketsTable = ({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "empname" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.item.employee.first_name} ${record.item.employee.last_name}`,
|
||||
`${record.item.employee.first_name} ${record.item.employee.last_name} ${
|
||||
record.costCenter ? `(${record.costCenter})` : ""
|
||||
}`.trim(),
|
||||
},
|
||||
{
|
||||
title: t("timetickets.fields.actualhrs"),
|
||||
|
||||
@@ -294,6 +294,12 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
where: { inproduction: { _eq: true }, suspended: { _eq: false } }
|
||||
) {
|
||||
id
|
||||
actual_in
|
||||
scheduled_in
|
||||
actual_completion
|
||||
scheduled_completion
|
||||
inproduction
|
||||
ro_number
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
@@ -327,12 +333,15 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
}
|
||||
) {
|
||||
id
|
||||
status
|
||||
ro_number
|
||||
scheduled_completion
|
||||
actual_completion
|
||||
scheduled_in
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
inproduction
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
@@ -360,11 +369,16 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
) {
|
||||
id
|
||||
scheduled_in
|
||||
actual_in
|
||||
scheduled_completion
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
alt_transport
|
||||
actual_completion
|
||||
inproduction
|
||||
status
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
) {
|
||||
|
||||
@@ -1153,31 +1153,8 @@ export const UPDATE_JOBS = gql`
|
||||
`;
|
||||
|
||||
export const CONVERT_JOB_TO_RO = gql`
|
||||
mutation CONVERT_JOB_TO_RO(
|
||||
$jobId: uuid!
|
||||
$class: String
|
||||
$ins_co_nm: String!
|
||||
$ca_gst_registrant: Boolean
|
||||
$driveable: Boolean
|
||||
$towin: Boolean
|
||||
$referral_source: String
|
||||
$referral_source_extra: String
|
||||
$employee_csr: uuid
|
||||
) {
|
||||
update_jobs(
|
||||
where: { id: { _eq: $jobId } }
|
||||
_set: {
|
||||
converted: true
|
||||
ins_co_nm: $ins_co_nm
|
||||
class: $class
|
||||
ca_gst_registrant: $ca_gst_registrant
|
||||
towin: $towin
|
||||
driveable: $driveable
|
||||
referral_source: $referral_source
|
||||
referral_source_extra: $referral_source_extra
|
||||
employee_csr: $employee_csr
|
||||
}
|
||||
) {
|
||||
mutation CONVERT_JOB_TO_RO($jobId: uuid!, $job: jobs_set_input!) {
|
||||
update_jobs(where: { id: { _eq: $jobId } }, _set: $job) {
|
||||
returning {
|
||||
id
|
||||
ro_number
|
||||
|
||||
@@ -51,6 +51,7 @@ import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.comp
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -96,7 +97,7 @@ export function JobsDetailPage({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
job: {
|
||||
...values,
|
||||
...UndefinedToNull(values, ["alt_transport", "category", "referral_source"]),
|
||||
parts_tax_rates: {
|
||||
...job.parts_tax_rates,
|
||||
...values.parts_tax_rates,
|
||||
|
||||
@@ -39,9 +39,11 @@ export function VehicleDetailContainer({
|
||||
document.title = t("titles.vehicledetail", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr} ${
|
||||
data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc
|
||||
} ${data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc}`
|
||||
? `${(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) || ""
|
||||
}`
|
||||
: "",
|
||||
});
|
||||
setSelectedHeader("vehicles");
|
||||
@@ -53,7 +55,14 @@ export function VehicleDetailContainer({
|
||||
label: t("titles.bc.vehicle-details", {
|
||||
vehicle:
|
||||
data && data.vehicles_by_pk
|
||||
? `${data.vehicles_by_pk.v_model_yr} ${data.vehicles_by_pk.v_make_desc} ${data.vehicles_by_pk.v_model_desc}`
|
||||
? `${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_yr) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_make_desc) || ""
|
||||
} ${
|
||||
(data.vehicles_by_pk && data.vehicles_by_pk.v_model_desc) ||
|
||||
""
|
||||
}`
|
||||
: "",
|
||||
}),
|
||||
},
|
||||
@@ -64,7 +73,11 @@ export function VehicleDetailContainer({
|
||||
CreateRecentItem(
|
||||
vehId,
|
||||
"vehicle",
|
||||
`${data.vehicles_by_pk.v_vin} | ${data.vehicles_by_pk.v_model_yr} ${data.vehicles_by_pk.v_make_desc} ${data.vehicles_by_pk.v_model_desc}`,
|
||||
`${data.vehicles_by_pk.v_vin || "N/A"} | ${
|
||||
data.vehicles_by_pk.v_model_yr || ""
|
||||
} ${data.vehicles_by_pk.v_make_desc || ""} ${
|
||||
data.vehicles_by_pk.v_model_desc || ""
|
||||
}`.trim(),
|
||||
`/manage/vehicles/${vehId}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
productionTotal: {},
|
||||
productionHours: 0,
|
||||
};
|
||||
|
||||
let problemJobs = [];
|
||||
//Set the current load.
|
||||
buckets.forEach((bucket) => {
|
||||
load.productionTotal[bucket.id] = { count: 0, label: bucket.label };
|
||||
@@ -45,6 +45,32 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
|
||||
prodJobs.forEach((item) => {
|
||||
//Add all of the jobs currently in production to the buckets so that we have a starting point.
|
||||
if (
|
||||
!item.actual_completion &&
|
||||
moment(item.scheduled_completion).isBefore(moment().startOf("day"))
|
||||
) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job was scheduled to go, but it has not been completed. Update the scheduled completion date to correct projections",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
item.actual_completion &&
|
||||
moment(item.actual_completion).isBefore(moment().startOf("day"))
|
||||
) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job is already marked as completed, but it is still in production. This job should be removed from production",
|
||||
});
|
||||
}
|
||||
if (!(item.actual_completion || item.scheduled_completion)) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job does not have a scheduled or actual completion date. Update the scheduled or actual completion dates to correct projections",
|
||||
});
|
||||
}
|
||||
|
||||
const bucketId = CheckJobBucket(buckets, item);
|
||||
load.productionHours =
|
||||
load.productionHours +
|
||||
@@ -59,77 +85,120 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
});
|
||||
|
||||
arrJobs.forEach((item) => {
|
||||
if (!item.scheduled_in)
|
||||
if (!item.scheduled_in) {
|
||||
console.log("JOB HAS NO SCHEDULED IN DATE.", item);
|
||||
const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job has no scheduled in date",
|
||||
});
|
||||
}
|
||||
if (!item.actual_completion && item.actual_in && !item.inproduction) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job has an actual in date, but no actual completion date and is not marked as in production",
|
||||
});
|
||||
}
|
||||
if (item.actual_in && moment(item.actual_in).isAfter(moment())) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job has an actual in date set in the future",
|
||||
});
|
||||
}
|
||||
if (
|
||||
item.actual_completion &&
|
||||
moment(item.actual_completion).isAfter(moment())
|
||||
) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job has an actual completion date set in the future",
|
||||
});
|
||||
}
|
||||
if (item.actual_completion && item.inproduction) {
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job has an actual completion date but it is still marked in production",
|
||||
});
|
||||
}
|
||||
|
||||
const itemDate = moment(item.actual_in || item.scheduled_in).format(
|
||||
"yyyy-MM-DD"
|
||||
);
|
||||
|
||||
const AddJobForSchedulingCalc = !item.inproduction;
|
||||
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursIn =
|
||||
(load[itemDate].hoursIn || 0) +
|
||||
load[itemDate].allHoursIn =
|
||||
(load[itemDate].allHoursIn || 0) +
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
load[itemDate].jobsIn.push(item);
|
||||
|
||||
//If the job hasn't already arrived, add it to the jobs in list.
|
||||
// Make sure it also hasn't already been completed, or isn't an in and out job.
|
||||
//This prevents the duplicate counting.
|
||||
load[itemDate].allJobsIn.push(item);
|
||||
if (AddJobForSchedulingCalc) {
|
||||
load[itemDate].jobsIn.push(item);
|
||||
load[itemDate].hoursIn =
|
||||
(load[itemDate].hoursIn || 0) +
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
}
|
||||
} else {
|
||||
load[itemDate] = {
|
||||
jobsIn: [item],
|
||||
allJobsIn: [item],
|
||||
jobsIn: AddJobForSchedulingCalc ? [item] : [], //Same as above, only add it if it isn't already in production.
|
||||
jobsOut: [],
|
||||
hoursIn:
|
||||
allJobsOut: [],
|
||||
allHoursIn:
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
hoursIn: AddJobForSchedulingCalc
|
||||
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let problemJobs = [];
|
||||
compJobs.forEach((item) => {
|
||||
if (!(item.actual_completion || item.scheduled_completion))
|
||||
console.log("JOB HAS NO COMPLETION DATE.", item);
|
||||
console.warn("JOB HAS NO COMPLETION DATE.", item);
|
||||
|
||||
const inProdJobs = prodJobs.find((p) => p.id === item.id);
|
||||
const inArrJobs = arrJobs.find((p) => p.id === item.id);
|
||||
|
||||
if (!(inProdJobs || inArrJobs)) {
|
||||
//Job isn't found in production or coming in.
|
||||
//is it going today or scheduled to go today?
|
||||
if (
|
||||
moment(item.actual_completion || item.scheduled_completion).isSame(
|
||||
moment(),
|
||||
"day"
|
||||
)
|
||||
) {
|
||||
console.log("Job is going today anyways, ignore it.", item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
moment(item.actual_completion || item.scheduled_completion).isBefore(
|
||||
moment(),
|
||||
"day"
|
||||
)
|
||||
) {
|
||||
console.log("Job should have already gone. Ignoring it.", item);
|
||||
return;
|
||||
}
|
||||
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job is scheduled for completion, but it is not marked in production nor is it an arriving job in this period. Check the scheduled in and completion dates",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const AddJobForSchedulingCalc = inProdJobs || inArrJobs;
|
||||
|
||||
const itemDate = moment(
|
||||
item.actual_completion || item.scheduled_completion
|
||||
).format("yyyy-MM-DD");
|
||||
//Skip it, it's already completed.
|
||||
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursOut =
|
||||
(load[itemDate].hoursOut || 0) +
|
||||
load[itemDate].allHoursOut =
|
||||
(load[itemDate].allHoursOut || 0) +
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
load[itemDate].jobsOut.push(item);
|
||||
//Add only the jobs that are still in production to get rid of.
|
||||
//If it's not in production, we'd subtract unnecessarily.
|
||||
load[itemDate].allJobsOut.push(item);
|
||||
|
||||
if (AddJobForSchedulingCalc) {
|
||||
load[itemDate].jobsOut.push(item);
|
||||
load[itemDate].hoursOut =
|
||||
(load[itemDate].hoursOut || 0) +
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
}
|
||||
} else {
|
||||
load[itemDate] = {
|
||||
jobsOut: [item],
|
||||
hoursOut:
|
||||
allJobsOut: [item],
|
||||
jobsOut: AddJobForSchedulingCalc ? [item] : [], //Same as above.
|
||||
hoursOut: AddJobForSchedulingCalc
|
||||
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||
: 0,
|
||||
allHoursOut:
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
};
|
||||
@@ -137,7 +206,8 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
});
|
||||
|
||||
//Propagate the expected load to each day.
|
||||
const range = Math.round(moment.duration(end.diff(today)).asDays());
|
||||
|
||||
const range = Math.round(moment.duration(end.diff(today)).asDays()) + 1;
|
||||
for (var day = 0; day < range; day++) {
|
||||
const current = moment(today).add(day, "days").format("yyyy-MM-DD");
|
||||
const prev = moment(today)
|
||||
@@ -146,6 +216,7 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
if (!!!load[current]) {
|
||||
load[current] = {};
|
||||
}
|
||||
|
||||
if (day === 0) {
|
||||
//Starting on day 1. The load is current.
|
||||
load[current].expectedLoad = CalculateLoad(
|
||||
@@ -154,6 +225,10 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
load[current].jobsIn || [],
|
||||
load[current].jobsOut || []
|
||||
);
|
||||
load[current].expectedJobCount =
|
||||
prodJobs.length +
|
||||
(load[current].jobsIn || []).length -
|
||||
(load[current].jobsOut || []).length;
|
||||
load[current].expectedHours =
|
||||
load.productionHours +
|
||||
(load[current].hoursIn || 0) -
|
||||
@@ -165,6 +240,10 @@ export function* calculateScheduleLoad({ payload: end }) {
|
||||
load[current].jobsIn || [],
|
||||
load[current].jobsOut || []
|
||||
);
|
||||
load[current].expectedJobCount =
|
||||
load[prev].expectedJobCount +
|
||||
(load[current].jobsIn || []).length -
|
||||
(load[current].jobsOut || []).length;
|
||||
load[current].expectedHours =
|
||||
load[prev].expectedHours +
|
||||
(load[current].hoursIn || 0) -
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
"blocked": "Blocked",
|
||||
"cancelledappointment": "Canceled appointment for: ",
|
||||
"completingjobs": "Completing Jobs",
|
||||
"dataconsistency": "{{ro_number}} has a data consistency issue. It has been excluded for scheduling purposes. CODE: {{code}}.",
|
||||
"dataconsistency": "{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.",
|
||||
"expectedjobs": "Expected Jobs in Production: ",
|
||||
"expectedprodhrs": "Expected Production Hours:",
|
||||
"history": "History",
|
||||
"inproduction": "Jobs In Production",
|
||||
@@ -60,6 +61,7 @@
|
||||
"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: ",
|
||||
"severalerrorsfound": "Several jobs have issues which may prevent accurate smart scheduling. Click to expand.",
|
||||
"smartscheduling": "Smart Scheduling",
|
||||
"suggesteddates": "Suggested Dates"
|
||||
},
|
||||
@@ -2424,6 +2426,9 @@
|
||||
"export_payables": "Export Log - Payables",
|
||||
"export_payments": "Export Log - Payments",
|
||||
"export_receivables": "Export Log - Receivables",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "Gross Sales by ATS",
|
||||
"gsr_by_category": "Gross Sales by Category",
|
||||
"gsr_by_csr": "Gross Sales by CSR",
|
||||
"gsr_by_delivery_date": "Gross Sales by Delivery Date",
|
||||
"gsr_by_estimator": "Gross Sales by Estimator",
|
||||
@@ -2454,12 +2459,15 @@
|
||||
"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",
|
||||
"jobs_completed_not_invoiced": "Jobs Completed not Invoiced",
|
||||
"jobs_invoiced_not_exported": "Jobs Invoiced not Exported",
|
||||
"jobs_reconcile": "Parts/Sublet/Labor Reconciliation",
|
||||
"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",
|
||||
"open_orders_specific_csr": "Open Orders filtered by CSR",
|
||||
"open_orders_status": "Open Orders by Status",
|
||||
"parts_backorder": "IOU Parts List",
|
||||
"parts_not_recieved": "Parts Not Received",
|
||||
@@ -2477,7 +2485,10 @@
|
||||
"production_by_target_date": "Production by Target Date",
|
||||
"production_by_technician": "Production by Technician",
|
||||
"production_by_technician_one": "Production filtered by Technician",
|
||||
"production_over_time": "Production Level over Time",
|
||||
"psr_by_make": "Percent of Sales by Vehicle Make",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)",
|
||||
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
|
||||
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
|
||||
"purchases_by_date_range_detail": "Purchases by Date - Detail",
|
||||
@@ -2489,6 +2500,7 @@
|
||||
"returns_grouped_by_vendor_detailed": "Returns Grouped by Vendor - Detailed",
|
||||
"returns_grouped_by_vendor_summary": "Returns Grouped by Vendor - Summary",
|
||||
"schedule": "Appointment Schedule",
|
||||
"scheduled_parts_list": "Parts for Jobs Scheduled In",
|
||||
"scoreboard_detail": "Scoreboard Detail",
|
||||
"scoreboard_summary": "Scoreboard Summary",
|
||||
"supplement_ratio_ins_co": "Supplement Ratio by Source",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"cancelledappointment": "Cita cancelada para:",
|
||||
"completingjobs": "",
|
||||
"dataconsistency": "",
|
||||
"expectedjobs": "",
|
||||
"expectedprodhrs": "",
|
||||
"history": "",
|
||||
"inproduction": "",
|
||||
@@ -60,6 +61,7 @@
|
||||
"priorappointments": "Nombramientos previos",
|
||||
"reminder": "",
|
||||
"scheduledfor": "Cita programada para:",
|
||||
"severalerrorsfound": "",
|
||||
"smartscheduling": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
@@ -2424,6 +2426,9 @@
|
||||
"export_payables": "",
|
||||
"export_payments": "",
|
||||
"export_receivables": "",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "",
|
||||
"gsr_by_category": "",
|
||||
"gsr_by_csr": "",
|
||||
"gsr_by_delivery_date": "",
|
||||
"gsr_by_estimator": "",
|
||||
@@ -2454,12 +2459,15 @@
|
||||
"job_costing_ro_date_summary": "",
|
||||
"job_costing_ro_estimator": "",
|
||||
"job_costing_ro_ins_co": "",
|
||||
"jobs_completed_not_invoiced": "",
|
||||
"jobs_invoiced_not_exported": "",
|
||||
"jobs_reconcile": "",
|
||||
"lag_time": "",
|
||||
"open_orders": "",
|
||||
"open_orders_csr": "",
|
||||
"open_orders_estimator": "",
|
||||
"open_orders_ins_co": "",
|
||||
"open_orders_specific_csr": "",
|
||||
"open_orders_status": "",
|
||||
"parts_backorder": "",
|
||||
"parts_not_recieved": "",
|
||||
@@ -2477,7 +2485,10 @@
|
||||
"production_by_target_date": "",
|
||||
"production_by_technician": "",
|
||||
"production_by_technician_one": "",
|
||||
"production_over_time": "",
|
||||
"psr_by_make": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||
"purchases_by_cost_center_detail": "",
|
||||
"purchases_by_cost_center_summary": "",
|
||||
"purchases_by_date_range_detail": "",
|
||||
@@ -2489,6 +2500,7 @@
|
||||
"returns_grouped_by_vendor_detailed": "",
|
||||
"returns_grouped_by_vendor_summary": "",
|
||||
"schedule": "",
|
||||
"scheduled_parts_list": "",
|
||||
"scoreboard_detail": "",
|
||||
"scoreboard_summary": "",
|
||||
"supplement_ratio_ins_co": "",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"cancelledappointment": "Rendez-vous annulé pour:",
|
||||
"completingjobs": "",
|
||||
"dataconsistency": "",
|
||||
"expectedjobs": "",
|
||||
"expectedprodhrs": "",
|
||||
"history": "",
|
||||
"inproduction": "",
|
||||
@@ -60,6 +61,7 @@
|
||||
"priorappointments": "Rendez-vous précédents",
|
||||
"reminder": "",
|
||||
"scheduledfor": "Rendez-vous prévu pour:",
|
||||
"severalerrorsfound": "",
|
||||
"smartscheduling": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
@@ -2424,6 +2426,9 @@
|
||||
"export_payables": "",
|
||||
"export_payments": "",
|
||||
"export_receivables": "",
|
||||
"gsr_by_atp": "",
|
||||
"gsr_by_ats": "",
|
||||
"gsr_by_category": "",
|
||||
"gsr_by_csr": "",
|
||||
"gsr_by_delivery_date": "",
|
||||
"gsr_by_estimator": "",
|
||||
@@ -2454,12 +2459,15 @@
|
||||
"job_costing_ro_date_summary": "",
|
||||
"job_costing_ro_estimator": "",
|
||||
"job_costing_ro_ins_co": "",
|
||||
"jobs_completed_not_invoiced": "",
|
||||
"jobs_invoiced_not_exported": "",
|
||||
"jobs_reconcile": "",
|
||||
"lag_time": "",
|
||||
"open_orders": "",
|
||||
"open_orders_csr": "",
|
||||
"open_orders_estimator": "",
|
||||
"open_orders_ins_co": "",
|
||||
"open_orders_specific_csr": "",
|
||||
"open_orders_status": "",
|
||||
"parts_backorder": "",
|
||||
"parts_not_recieved": "",
|
||||
@@ -2477,7 +2485,10 @@
|
||||
"production_by_target_date": "",
|
||||
"production_by_technician": "",
|
||||
"production_by_technician_one": "",
|
||||
"production_over_time": "",
|
||||
"psr_by_make": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||
"purchases_by_cost_center_detail": "",
|
||||
"purchases_by_cost_center_summary": "",
|
||||
"purchases_by_date_range_detail": "",
|
||||
@@ -2489,6 +2500,7 @@
|
||||
"returns_grouped_by_vendor_detailed": "",
|
||||
"returns_grouped_by_vendor_summary": "",
|
||||
"schedule": "",
|
||||
"scheduled_parts_list": "",
|
||||
"scoreboard_detail": "",
|
||||
"scoreboard_summary": "",
|
||||
"supplement_ratio_ins_co": "",
|
||||
|
||||
@@ -1343,6 +1343,32 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
gsr_by_category: {
|
||||
title: i18n.t("reportcenter.templates.gsr_by_category"),
|
||||
description: "",
|
||||
subject: i18n.t("reportcenter.templates.gsr_by_category"),
|
||||
key: "gsr_by_category",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
gsr_by_ats: {
|
||||
title: i18n.t("reportcenter.templates.gsr_by_ats"),
|
||||
description: "",
|
||||
subject: i18n.t("reportcenter.templates.gsr_by_ats"),
|
||||
key: "gsr_by_ats",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "sales",
|
||||
},
|
||||
gsr_labor_only: {
|
||||
title: i18n.t("reportcenter.templates.gsr_labor_only"),
|
||||
description: "",
|
||||
@@ -1421,6 +1447,19 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
open_orders_specific_csr: {
|
||||
title: i18n.t("reportcenter.templates.open_orders_specific_csr"),
|
||||
description: "",
|
||||
subject: i18n.t("reportcenter.templates.open_orders_specific_csr"),
|
||||
key: "open_orders_specific_csr",
|
||||
idtype: "employee",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_open"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
export_payables: {
|
||||
title: i18n.t("reportcenter.templates.export_payables"),
|
||||
description: "",
|
||||
@@ -1734,6 +1773,88 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
scheduled_parts_list: {
|
||||
title: i18n.t("reportcenter.templates.scheduled_parts_list"),
|
||||
subject: i18n.t("reportcenter.templates.scheduled_parts_list"),
|
||||
key: "scheduled_parts_list",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.scheduled_in"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
jobs_completed_not_invoiced: {
|
||||
title: i18n.t("reportcenter.templates.jobs_completed_not_invoiced"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.jobs_completed_not_invoiced"
|
||||
),
|
||||
key: "jobs_completed_not_invoiced",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
jobs_invoiced_not_exported: {
|
||||
title: i18n.t("reportcenter.templates.jobs_invoiced_not_exported"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.jobs_invoiced_not_exported"
|
||||
),
|
||||
key: "jobs_invoiced_not_exported",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
purchase_return_ratio_grouped_by_vendor_detail: {
|
||||
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"
|
||||
),
|
||||
key: "purchase_return_ratio_grouped_by_vendor_detail",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.bills"),
|
||||
field: i18n.t("bills.fields.date"),
|
||||
},
|
||||
group: "purchases",
|
||||
},
|
||||
purchase_return_ratio_grouped_by_vendor_summary: {
|
||||
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"
|
||||
),
|
||||
key: "purchase_return_ratio_grouped_by_vendor_summary",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.bills"),
|
||||
field: i18n.t("bills.fields.date"),
|
||||
},
|
||||
group: "purchases",
|
||||
},
|
||||
production_over_time: {
|
||||
title: i18n.t("reportcenter.templates.production_over_time"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.production_over_time"
|
||||
),
|
||||
key: "production_over_time",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.actual_in"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "courtesycarcontract"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export default function UndefinedToNull(obj) {
|
||||
export default function UndefinedToNull(obj, keys) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (keys && keys.indexOf(key) >= 0) {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
} else {
|
||||
if (obj[key] === undefined) obj[key] = null;
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -4502,6 +4502,62 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
name: payment_response
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: bodyshop
|
||||
using:
|
||||
foreign_key_constraint_on: bodyshopid
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: payment
|
||||
using:
|
||||
foreign_key_constraint_on: paymentid
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- amount
|
||||
- bodyshopid
|
||||
- declinereason
|
||||
- ext_paymentid
|
||||
- jobid
|
||||
- paymentid
|
||||
- response
|
||||
- successful
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- successful
|
||||
- response
|
||||
- amount
|
||||
- declinereason
|
||||
- ext_paymentid
|
||||
- bodyshopid
|
||||
- id
|
||||
- jobid
|
||||
- paymentid
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
name: payments
|
||||
schema: public
|
||||
|
||||
10
hasura/migrations/1674581461316_run_sql_migration/down.sql
Normal file
10
hasura/migrations/1674581461316_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
|
||||
-- SELECT j.jobid,
|
||||
-- j.status,
|
||||
-- count(1) AS count,
|
||||
-- j.part_type
|
||||
-- FROM joblines j
|
||||
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.removed IS FALSE))
|
||||
-- GROUP BY j.jobid, j.status, j.part_type;
|
||||
8
hasura/migrations/1674581461316_run_sql_migration/up.sql
Normal file
8
hasura/migrations/1674581461316_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
CREATE OR REPLACE VIEW "public"."joblines_status" AS
|
||||
SELECT j.jobid,
|
||||
j.status,
|
||||
count(1) AS count,
|
||||
j.part_type
|
||||
FROM joblines j
|
||||
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.removed IS FALSE))
|
||||
GROUP BY j.jobid, j.status, j.part_type;
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."exportlog_billid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "exportlog_billid" on
|
||||
"public"."exportlog" using btree ("billid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."exportlog_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "exportlog_jobid" on
|
||||
"public"."exportlog" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."exportlog_payments";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "exportlog_payments" on
|
||||
"public"."exportlog" using btree ("paymentid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."jobs_idx_date_open";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "jobs_idx_date_open" on
|
||||
"public"."jobs" using btree ("date_open");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."jobs_idx_date_invoiced";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "jobs_idx_date_invoiced" on
|
||||
"public"."jobs" using btree ("date_invoiced");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_bills_vendorid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_bills_vendorid" on
|
||||
"public"."bills" using btree ("vendorid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_parts_orders_vendorid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_parts_orders_vendorid" on
|
||||
"public"."parts_orders" using btree ("vendorid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_ccc_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_ccc_jobid" on
|
||||
"public"."cccontracts" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_ccc_courtesycarid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_ccc_courtesycarid" on
|
||||
"public"."cccontracts" using btree ("courtesycarid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_jobs_actual_completion";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_jobs_actual_completion" on
|
||||
"public"."jobs" using btree ("actual_completion");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_jobs_actual_in";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_jobs_actual_in" on
|
||||
"public"."jobs" using btree ("actual_in");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_jobs_employee_csr";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_jobs_employee_csr" on
|
||||
"public"."jobs" using btree ("employee_csr");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_jobs_body_csr";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_jobs_body_csr" on
|
||||
"public"."jobs" using btree ("employee_body");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_jobs_employee_refinish";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_jobs_employee_refinish" on
|
||||
"public"."jobs" using btree ("employee_refinish");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_timetickets_employeeid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_timetickets_employeeid" on
|
||||
"public"."timetickets" using btree ("employeeid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_timetickets_cost_center";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_timetickets_cost_center" on
|
||||
"public"."timetickets" using btree ("cost_center");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_scoreboard_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_scoreboard_jobid" on
|
||||
"public"."scoreboard" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_scoreboard_date";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_scoreboard_date" on
|
||||
"public"."scoreboard" using btree ("date");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_payments_date";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_payments_date" on
|
||||
"public"."payments" using btree ("date");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."exportlog_createdat";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "exportlog_createdat" on
|
||||
"public"."exportlog" using btree ("created_at");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_transitions_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_transitions_jobid" on
|
||||
"public"."transitions" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_transitions_start";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_transitions_start" on
|
||||
"public"."transitions" using btree ("start");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_transitions_end";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_transitions_end" on
|
||||
"public"."transitions" using btree ("end");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_audit_bodyshopid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_audit_bodyshopid" on
|
||||
"public"."audit_trail" using btree ("bodyshopid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_audit_jobid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_audit_jobid" on
|
||||
"public"."audit_trail" using btree ("jobid");
|
||||
@@ -0,0 +1 @@
|
||||
DROP INDEX IF EXISTS "public"."idx_audit_billid";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE INDEX "idx_audit_billid" on
|
||||
"public"."audit_trail" using btree ("billid");
|
||||
11
hasura/migrations/1676337846761_run_sql_migration/down.sql
Normal file
11
hasura/migrations/1676337846761_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE INDEX idx_phonebook_firstname ON public.phonebook USING gin (firstname public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_lastname ON public.phonebook USING gin (lastname public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_company ON public.phonebook USING gin (company public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_address1 ON public.phonebook USING gin (address1 public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_phone1 ON public.phonebook USING gin (phone1 public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_phone2 ON public.phonebook USING gin (phone2 public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_email ON public.phonebook USING gin (email public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_phonebook_category ON public.phonebook USING gin (category public.gin_trgm_ops);
|
||||
-- CREATE INDEX idx_vendor_name ON public.vendors USING gin (name public.gin_trgm_ops);
|
||||
9
hasura/migrations/1676337846761_run_sql_migration/up.sql
Normal file
9
hasura/migrations/1676337846761_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
CREATE INDEX idx_phonebook_firstname ON public.phonebook USING gin (firstname public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_lastname ON public.phonebook USING gin (lastname public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_company ON public.phonebook USING gin (company public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_address1 ON public.phonebook USING gin (address1 public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_phone1 ON public.phonebook USING gin (phone1 public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_phone2 ON public.phonebook USING gin (phone2 public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_email ON public.phonebook USING gin (email public.gin_trgm_ops);
|
||||
CREATE INDEX idx_phonebook_category ON public.phonebook USING gin (category public.gin_trgm_ops);
|
||||
CREATE INDEX idx_vendor_name ON public.vendors USING gin (name public.gin_trgm_ops);
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."payment_response";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE "public"."payment_response" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "bodyshopid" uuid NOT NULL, "jobid" uuid, "paymentid" uuid, "successful" boolean NOT NULL DEFAULT false, "ext_paymentid" text NOT NULL, "amount" numeric NOT NULL, "declinereason" text, "response" jsonb NOT NULL DEFAULT jsonb_build_object(), PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("paymentid") REFERENCES "public"."payments"("id") ON UPDATE cascade ON DELETE cascade);
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -895,6 +895,12 @@ const CreateCosts = (job) => {
|
||||
GlassLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAG] || Dinero(),
|
||||
DetailLaborTotalCost: Dinero(),
|
||||
// ticketTotalsByCostCenter[defaultCosts.LAD] || Dinero(),
|
||||
LaborMiscTotalCost: (ticketTotalsByCostCenter[defaultCosts.LA1] || Dinero())
|
||||
.add(ticketTotalsByCostCenter[defaultCosts.LA2] || Dinero())
|
||||
.add(ticketTotalsByCostCenter[defaultCosts.LA2] || Dinero())
|
||||
.add(ticketTotalsByCostCenter[defaultCosts.LA3] || Dinero())
|
||||
.add(ticketTotalsByCostCenter[defaultCosts.LA4] || Dinero())
|
||||
.add(ticketTotalsByCostCenter[defaultCosts.LAU] || Dinero()),
|
||||
PMTotalCost: billTotalsByCostCenters[defaultCosts.MAPA] || Dinero(),
|
||||
BMTotalCost: billTotalsByCostCenters[defaultCosts.MASH] || Dinero(),
|
||||
MiscTotalCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),
|
||||
|
||||
@@ -23,11 +23,12 @@ let transporter = nodemailer.createTransport({
|
||||
});
|
||||
|
||||
exports.sendServerEmail = async function ({ subject, text }) {
|
||||
if (process.env.NODE_ENV === undefined) return;
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `ImEX Online API - ${process.env.NODE_ENV} <noreply@imex.online>`,
|
||||
to: ["patrick@snapt.ca"],
|
||||
to: ["patrick@imexsystems.ca"],
|
||||
subject: subject,
|
||||
text: text,
|
||||
ses: {
|
||||
|
||||
@@ -536,7 +536,16 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
|
||||
arrJobs: jobs(where: {scheduled_in: {_gte: $now}, suspended: {_eq: false}}) {
|
||||
id
|
||||
scheduled_in
|
||||
actual_in
|
||||
scheduled_completion
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
alt_transport
|
||||
actual_completion
|
||||
inproduction
|
||||
status
|
||||
labhrs: joblines_aggregate(where: {mod_lbr_ty: {_neq: "LAR"}, removed: {_eq: false}}) {
|
||||
aggregate {
|
||||
sum {
|
||||
@@ -554,9 +563,15 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
|
||||
}
|
||||
compJobs: jobs(where: {_and: [{suspended: {_eq: false}}, {_or: [{scheduled_completion: {_gte: $now}}, {actual_completion: {_gte: $now}}]}]}) {
|
||||
id
|
||||
status
|
||||
ro_number
|
||||
scheduled_completion
|
||||
actual_completion
|
||||
scheduled_in
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
inproduction
|
||||
labhrs: joblines_aggregate(where: {mod_lbr_ty: {_neq: "LAR"}, removed: {_eq: false}}) {
|
||||
aggregate {
|
||||
sum {
|
||||
@@ -574,15 +589,20 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
|
||||
}
|
||||
prodJobs: jobs(where: {inproduction: {_eq: true}, suspended: {_eq: false}}) {
|
||||
id
|
||||
actual_in
|
||||
scheduled_in
|
||||
actual_completion
|
||||
scheduled_completion
|
||||
labhrs: joblines_aggregate(where: {_and: [{mod_lbr_ty: {_neq: "LAR"}}, {removed: {_eq: false}}]}) {
|
||||
inproduction
|
||||
ro_number
|
||||
labhrs: joblines_aggregate(where: {mod_lbr_ty: {_neq: "LAR"}, removed: {_eq: false}}) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
}
|
||||
}
|
||||
}
|
||||
larhrs: joblines_aggregate(where: {_and: [{mod_lbr_ty: {_eq: "LAR"}}, {removed: {_eq: false}}]}) {
|
||||
larhrs: joblines_aggregate(where: {mod_lbr_ty: {_eq: "LAR"}, removed: {_eq: false}}) {
|
||||
aggregate {
|
||||
sum {
|
||||
mod_lb_hrs
|
||||
@@ -590,7 +610,8 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
`;
|
||||
|
||||
exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employeeId: String!) {
|
||||
employees(where: {_and: {shopid: {_eq: $shopId}, employee_number: {_eq: $employeeId}}}) {
|
||||
|
||||
@@ -41,9 +41,9 @@ exports.job = async (req, res) => {
|
||||
(bucket) =>
|
||||
bucket.gte <= jobHrs && (!!bucket.lt ? bucket.lt > jobHrs : true)
|
||||
)[0];
|
||||
|
||||
const load = {
|
||||
productionTotal: {},
|
||||
productionHours: 0,
|
||||
};
|
||||
//Set the current load.
|
||||
ssbuckets.forEach((bucket) => {
|
||||
@@ -70,27 +70,6 @@ exports.job = async (req, res) => {
|
||||
// );
|
||||
const filteredArrJobs = [];
|
||||
|
||||
// filteredArrJobs.forEach((item) => {
|
||||
// const itemDate = moment(item.scheduled_in)
|
||||
// .tz(timezone)
|
||||
// .format("yyyy-MM-DD");
|
||||
// if (!!load[itemDate]) {
|
||||
// load[itemDate].hoursIn =
|
||||
// (load[itemDate].hoursIn || 0) +
|
||||
// item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
// item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
// load[itemDate].jobsIn.push(item);
|
||||
// } else {
|
||||
// load[itemDate] = {
|
||||
// jobsIn: [item],
|
||||
// jobsOut: [],
|
||||
// hoursIn:
|
||||
// item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
// item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
// };
|
||||
// }
|
||||
// });
|
||||
|
||||
arrJobs.forEach((item) => {
|
||||
let isSameBucket = false;
|
||||
if (JobBucket.id === CheckJobBucket(ssbuckets, item)) {
|
||||
@@ -102,21 +81,27 @@ exports.job = async (req, res) => {
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
|
||||
const itemDate = moment(item.scheduled_in)
|
||||
const AddJobForSchedulingCalc = !item.inproduction;
|
||||
|
||||
const itemDate = moment(item.actual_in || item.scheduled_in)
|
||||
.tz(timezone)
|
||||
.format("yyyy-MM-DD");
|
||||
if (isSameBucket) {
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursIn = (load[itemDate].hoursIn || 0) + jobHours;
|
||||
load[itemDate].jobsIn.push(item);
|
||||
load[itemDate].hoursIn =
|
||||
(load[itemDate].hoursIn || 0) + AddJobForSchedulingCalc
|
||||
? jobHours
|
||||
: 0;
|
||||
if (AddJobForSchedulingCalc) load[itemDate].jobsIn.push(item);
|
||||
} else {
|
||||
load[itemDate] = {
|
||||
jobsIn: [item],
|
||||
jobsIn: AddJobForSchedulingCalc ? [item] : [],
|
||||
jobsOut: [],
|
||||
hoursIn: jobHours,
|
||||
hoursIn: AddJobForSchedulingCalc ? jobHours : 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!load[itemDate]) {
|
||||
load[itemDate] = {
|
||||
jobsIn: [],
|
||||
@@ -139,52 +124,28 @@ exports.job = async (req, res) => {
|
||||
const inProdJobs = filteredProdJobsList.find((p) => p.id === item.id);
|
||||
const inArrJobs = filteredArrJobs.find((p) => p.id === item.id);
|
||||
|
||||
if (!(inProdJobs || inArrJobs)) {
|
||||
//Job isn't found in production or coming in.
|
||||
//is it going today or scheduled to go today?
|
||||
if (
|
||||
moment(item.actual_completion || item.scheduled_completion)
|
||||
.tz(timezone)
|
||||
.isSame(moment().tz(timezone), "day")
|
||||
) {
|
||||
console.log("Job is going today anyways, ignore it.", item);
|
||||
return;
|
||||
}
|
||||
const AddJobForSchedulingCalc = inProdJobs || inArrJobs;
|
||||
|
||||
if (
|
||||
moment(item.actual_completion || item.scheduled_completion)
|
||||
.tz(timezone)
|
||||
.isBefore(moment().tz(timezone), "day")
|
||||
) {
|
||||
console.log("Job should have already gone. Ignoring it.", item);
|
||||
return;
|
||||
}
|
||||
console.log("PROBLEM JOB", item);
|
||||
problemJobs.push({
|
||||
...item,
|
||||
code: "Job is scheduled for completion, but it is not marked in production nor is it an arriving job in this period. Check the scheduled in and completion dates",
|
||||
});
|
||||
return;
|
||||
const itemDate = moment(
|
||||
item.actual_completion || item.scheduled_completion
|
||||
)
|
||||
.tz(timezone)
|
||||
.format("yyyy-MM-DD");
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursOut =
|
||||
(load[itemDate].hoursOut || 0) + AddJobForSchedulingCalc
|
||||
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||
: 0;
|
||||
if (AddJobForSchedulingCalc) load[itemDate].jobsOut.push(item);
|
||||
} else {
|
||||
const itemDate = moment(
|
||||
item.actual_completion || item.scheduled_completion
|
||||
)
|
||||
.tz(timezone)
|
||||
.format("yyyy-MM-DD");
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursOut =
|
||||
(load[itemDate].hoursOut || 0) +
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||
load[itemDate].jobsOut.push(item);
|
||||
} else {
|
||||
load[itemDate] = {
|
||||
jobsOut: [item],
|
||||
hoursOut:
|
||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
};
|
||||
}
|
||||
load[itemDate] = {
|
||||
jobsOut: AddJobForSchedulingCalc ? [item] : [],
|
||||
hoursOut: AddJobForSchedulingCalc
|
||||
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user