Compare commits

...

50 Commits

Author SHA1 Message Date
Patrick Fic
132cf98a37 IO-2086 Resolve local media server pulling full size image for thumbnail. 2023-04-21 12:21:28 -07:00
Patrick Fic
f4b3a990d7 Additional schema changes for payroll. 2023-04-21 08:42:47 -07:00
swtmply
20371ea00d Merge branch 'release/2023-04-21' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-21 2023-04-21 22:28:00 +08:00
swtmply
3086a654a1 IO-1461 made the sider sticky 2023-04-21 22:27:48 +08:00
Patrick Fic
60768c8847 Timeticket approval queue aggregation permissions. 2023-04-20 15:28:14 -07:00
Patrick Fic
a68f8d6880 Payroll schema changes. 2023-04-20 15:05:23 -07:00
Allan Carr
522665256e IO-2335 Exported GSR to Excel
Change idtype to reporttype to prevent issues later on down the road if we extend more reports to excel format
2023-04-20 12:43:06 -07:00
swtmply
d3fe2c9d06 IO-2011 removed column background color 2023-04-21 03:37:36 +08:00
swtmply
f080e84985 IO-2242 added email on vendor query 2023-04-21 02:56:11 +08:00
Allan Carr
2a0ad46eea IO-2235
Changes in Prettier
2023-04-20 09:46:21 -07:00
Allan Carr
7725080a11 IO-2235 Exported GSR to Excel
For Morrey Body Shop
2023-04-20 09:33:22 -07:00
Allan Carr
1932795f55 Merge branch 'release/2023-04-21' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-21 2023-04-19 15:44:41 -07:00
Allan Carr
cc7bd1c792 IO-2243 DMS Posting Sheet Report
Requested by Morrey Auto Body
2023-04-19 15:44:09 -07:00
Patrick Fic
a7cf36d5f8 Disable job status transition tracking. 2023-04-19 14:26:12 -07:00
Patrick Fic
f575870685 Added missed limit on global search. 2023-04-19 14:10:33 -07:00
Patrick Fic
171b61b92f Included a limit on global search query to limit performance impact. 2023-04-19 14:08:57 -07:00
Patrick Fic
3c0a883326 Resolve status transition error message. 2023-04-19 13:08:18 -07:00
Patrick Fic
d787821345 IO-2086 Resolve cloudinary based thumbnail retrieval. 2023-04-19 11:43:24 -07:00
Patrick Fic
ed6eab4c38 Merged in release/2023-04-14 (pull request #708)
Remove erroneous console log.
2023-04-14 20:05:43 +00:00
Patrick Fic
b12c9407d9 Remove erroneous console log. 2023-04-14 13:05:09 -07:00
Patrick Fic
300aee5b02 Merged in release/2023-04-14 (pull request #707)
Release/2023 04 14
2023-04-14 20:04:15 +00:00
Patrick Fic
0a93551db4 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14
# Conflicts:
#	client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx
2023-04-14 12:30:22 -07:00
Patrick Fic
8602ccbb8a IO-2240 Resolve efficiency calculation issue. 2023-04-14 12:29:11 -07:00
swtmply
d7b0e3046b IO-2240 Adjusted calculation for total efficiency 2023-04-15 03:26:56 +08:00
swtmply
0e06b449cb IO-2240 Adjusted the location of the calculation 2023-04-15 01:18:29 +08:00
swtmply
8e8208dd9a IO-2240 Moved the calculation within the function 2023-04-15 01:11:44 +08:00
swtmply
79e2fecb24 IO-2239 Removed total title 2023-04-14 23:33:14 +08:00
swtmply
86e909e4e9 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-14 02:53:14 +08:00
swtmply
c7ff893397 IO-1412 Fixed column resize drag speed 2023-04-14 02:53:04 +08:00
Patrick Fic
3981b8684c IO-2244 Round difference calculation to 1 decimal point for productive hours claiming enforcement. 2023-04-13 10:11:15 -07:00
swtmply
1fb856f95f Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-14 00:41:00 +08:00
swtmply
c62c3fa938 IO-2240 total for productive hours in summary 2023-04-14 00:40:50 +08:00
Patrick Fic
be4feca990 Clean up unneeded import. 2023-04-13 09:26:24 -07:00
Patrick Fic
ec45454b3d IO-2244 Restrict claimable productive hours based on remaining hours 2023-04-13 09:25:52 -07:00
swtmply
0e78cb47f9 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-13 22:04:01 +08:00
Patrick Fic
e5b8d003ec IO-2137 Adjust PBS company name logic for posting. 2023-04-13 22:03:35 +08:00
Patrick Fic
2eb81dde37 Add ppc field to job lines. 2023-04-13 22:03:35 +08:00
Patrick Fic
614549a545 Include employee team changes from Payroll Based Changes. 2023-04-13 22:03:35 +08:00
Patrick Fic
5717727d2a IO-2243 Capture successful CDK posting details. 2023-04-13 22:03:35 +08:00
swtmply
f3714cea1e IO-2239 Added totals on cards in job scoreboards 2023-04-13 22:03:35 +08:00
swtmply
a3375e6152 IO-2238 Changed vehicle column to be a hyperlink 2023-04-13 22:03:24 +08:00
Patrick Fic
f48fb7130e IO-2137 Adjust PBS company name logic for posting. 2023-04-12 13:16:18 -07:00
Patrick Fic
1460fa6fd7 Add ppc field to job lines. 2023-04-12 13:13:51 -07:00
Patrick Fic
3d9a07bd39 Include employee team changes from Payroll Based Changes. 2023-04-12 13:08:39 -07:00
Patrick Fic
bd4aa4027a IO-2243 Capture successful CDK posting details. 2023-04-12 13:02:45 -07:00
swtmply
9fa995f002 IO-2239 Added totals on cards in job scoreboards 2023-04-12 03:13:19 +08:00
swtmply
3ed48b26f1 IO-2338 Changed vehicle column to be a hyperlink 2023-04-12 01:44:45 +08:00
swtmply
d585cacdfc IO-1412 Fixed column resize 2023-04-12 00:57:13 +08:00
Patrick Fic
55ddaca328 Merged in release/2023-04-07 (pull request #703)
Release/2023 04 07
2023-04-06 00:22:52 +00:00
Patrick Fic
9c40a03a06 Merged in release/2023-04-07 (pull request #701)
Release/2023 04 07
2023-04-05 22:09:28 +00:00
50 changed files with 1025 additions and 115 deletions

View File

@@ -8864,6 +8864,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tt_enforce_hours_for_tech_console</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>use_fippa</name>
<definition_loaded>false</definition_loaded>
@@ -17310,6 +17331,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total</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>totals</name>
<definition_loaded>false</definition_loaded>
@@ -33806,6 +33848,27 @@
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>deleting</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>noaccess</name>
<definition_loaded>false</definition_loaded>
@@ -34367,6 +34430,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>deleteconfirm</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>existing_owners</name>
<definition_loaded>false</definition_loaded>
@@ -34477,6 +34561,27 @@
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>delete</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>save</name>
<definition_loaded>false</definition_loaded>
@@ -44980,6 +45085,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hoursenteredmorethanavailable</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children>
@@ -47292,6 +47418,27 @@
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>deleting</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>noaccess</name>
<definition_loaded>false</definition_loaded>
@@ -47916,6 +48063,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>deleteconfirm</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>fromvehicle</name>
<definition_loaded>false</definition_loaded>
@@ -48005,6 +48173,27 @@
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>delete</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>save</name>
<definition_loaded>false</definition_loaded>

View File

@@ -143,13 +143,16 @@
}
}
//Update row highlighting on production board.
//Update row highlighting on production board.
.ant-table-tbody > tr.ant-table-row:hover > td {
background: #eaeaea !important;
}
.job-line-manual{
.job-line-manual {
color: tomato;
font-style: italic;
}
td.ant-table-column-sort {
background-color: transparent;
}

View File

@@ -34,8 +34,8 @@ function JobsDocumentsComponent({
const fileType = DetermineFileType(value.type);
if (value.type.startsWith("image")) {
acc.images.push({
src: GenerateSrcUrl(value),
thumbnail: GenerateThumbUrl(value),
// src: GenerateSrcUrl(value),
src: GenerateThumbUrl(value),
// src: GenerateSrcUrl(value),
// thumbnail: GenerateThumbUrl(value),
fullsize: GenerateSrcUrl(value),

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
import { GenerateThumbUrl } from "./job-documents.utility";
function JobsDocumentGalleryExternal({
data,
@@ -15,8 +15,8 @@ function JobsDocumentGalleryExternal({
let documents = data.reduce((acc, value) => {
if (value.type.startsWith("image")) {
acc.push({
src: GenerateSrcUrl(value),
thumbnail: GenerateThumbUrl(value),
//src: GenerateSrcUrl(value),
src: GenerateThumbUrl(value),
thumbnailHeight: 225,
thumbnailWidth: 225,
isSelected: false,

View File

@@ -52,11 +52,15 @@ function JobDocumentsLocalGalleryExternal({
val.type.mime &&
val.type.mime.startsWith("image")
) {
acc.push(val);
acc.push({ ...val, src: val.thumbnail });
}
return acc;
}, [])
: [];
console.log(
"🚀 ~ file: jobs-documents-local-gallery.external.component.jsx:48 ~ useEffect ~ documents:",
documents
);
setgalleryImages(documents);
}, [allMedia, jobId, setgalleryImages, t]);

View File

@@ -6,10 +6,6 @@ export const CalculateAllocationsTotals = (
timetickets,
adjustments = []
) => {
console.log(
"🚀 ~ file: labor-allocations-table.utility.js ~ line 9 ~ adjustments",
adjustments
);
const responsibilitycenters = bodyshop.md_responsibility_centers;
const jobCodes = joblines.map((item) => item.mod_lbr_ty);
//.filter((value, index, self) => self.indexOf(value) === index && !!value);

View File

@@ -201,6 +201,7 @@ export function PartsOrderListTableComponent({
subject: record.return
? Templates.parts_return_slip.subject
: Templates.parts_order.subject,
to: record.vendor.email,
}}
id={job.id}
/>
@@ -296,7 +297,6 @@ export function PartsOrderListTableComponent({
sortOrder:
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
},
{
title: t("parts_orders.fields.act_price"),
dataIndex: "act_price",

View File

@@ -91,11 +91,13 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
b.v_make_desc + b.v_model_desc
),
sortOrder:
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
render: (text, record) => (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
} ${record.v_color || ""} ${record.plate_no || ""}`}</span>
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
record.v_model_yr || ""
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
record.v_color || ""
} ${record.plate_no || ""}`}</Link>
),
},
{

View File

@@ -81,7 +81,7 @@ export function ProductionListTable({
state,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
width: k.width ?? 100,
};
})) ||
[]
@@ -267,6 +267,8 @@ export function ProductionListTable({
sortOrder:
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
title: headerItem(c),
ellipsis: true,
width: c.width ?? 100,
onHeaderCell: (column) => ({
width: column.width,
onResize: handleResize(index),
@@ -276,11 +278,12 @@ export function ProductionListTable({
rowKey="id"
loading={loading}
dataSource={dataSource}
// scroll={{ x: true }}
scroll={{ x: 1000 }}
onChange={handleTableChange}
/>
</ReactDragListView.DragColumn>
</div>
);
}
export default connect(mapStateToProps, null)(ProductionListTable);

View File

@@ -3,8 +3,26 @@ import { Resizable } from "react-resizable";
export default function ResizableComponent(props) {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable width={width || 200} height={0} onResize={onResize}>
<Resizable
width={width || 200}
height={0}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
>
<th {...restProps} />
</Resizable>
);

View File

@@ -92,7 +92,11 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
to: values.to,
subject: Templates[values.key]?.subject,
},
values.sendby === "email" ? "e" : "p",
values.sendbyexcel === "excel"
? "x"
: values.sendby === "email"
? "e"
: "p",
id
);
setLoading(false);
@@ -250,15 +254,38 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
ranges={DatePIckerRanges}
/>
</Form.Item>
<Form.Item
label={t("general.labels.sendby")}
name="sendby"
initialValue="print"
>
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
//Kind of Id
const reporttype = Templates[key] && Templates[key].reporttype;
if (reporttype === "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendbyexcel"
initialValue="excel"
>
<Radio.Group>
<Radio value="excel">{t("general.labels.excel")}</Radio>
</Radio.Group>
</Form.Item>
);
if (reporttype !== "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendby"
initialValue="print"
>
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
</Form.Item>
);
}}
</Form.Item>
<div

View File

@@ -1,4 +1,4 @@
import { Card, Statistic } from "antd";
import { Card, Divider, Statistic } from "antd";
import moment from "moment";
import React from "react";
import { connect } from "react-redux";
@@ -41,6 +41,9 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
label="P"
value={paintHrs.toFixed(1)}
/>
<Divider style={{ margin: 0 }} />
<Statistic value={(bodyHrs + paintHrs).toFixed(1)} />
</Card>
);
}

View File

@@ -1,5 +1,5 @@
import { CalendarOutlined } from "@ant-design/icons";
import { Card, Col, Row, Statistic } from "antd";
import { Card, Col, Divider, Row, Statistic } from "antd";
import _ from "lodash";
import moment from "moment";
import React, { useMemo } from "react";
@@ -177,6 +177,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
<Statistic value={values.toDatePaint.toFixed(1)} />
</Col>
</Row>
<Row>
<Divider style={{ margin: 5 }} />
</Row>
<Row>
<Col {...statSpans}></Col>
<Col {...statSpans}>
@@ -184,14 +187,53 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
value={(values.todayPaint + values.todayBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(values.weeklyPaint + values.weeklyBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(values.toDatePaint + values.toDateBody).toFixed(1)}

View File

@@ -81,6 +81,7 @@ export default function ScoreboardTimeTickets() {
totalLastMonth: 0,
totalOverPeriod: 0,
actualTotalOverPeriod: 0,
totalEffieciencyOverPeriod: 0,
employees: {},
};
data.fixedperiod.forEach((ticket) => {
@@ -94,6 +95,7 @@ export default function ScoreboardTimeTickets() {
totalLastMonth: 0,
totalOverPeriod: 0,
actualTotalOverPeriod: 0,
totalEffieciencyOverPeriod: 0,
};
}
@@ -221,6 +223,28 @@ export default function ScoreboardTimeTickets() {
ret2.push(r);
});
// Add total efficiency of employees
const totalActualAndProductive = Object.keys(ret.employees)
.map((key) => {
return { employee_number: key, ...ret.employees[key] };
})
.reduce(
(acc, e) => {
return {
totalOverPeriod: acc.totalOverPeriod + e.totalOverPeriod,
actualTotalOverPeriod:
acc.actualTotalOverPeriod + e.actualTotalOverPeriod,
};
},
{ totalOverPeriod: 0, actualTotalOverPeriod: 0 }
);
ret.totalEffieciencyOverPeriod =
(totalActualAndProductive.totalOverPeriod /
totalActualAndProductive.actualTotalOverPeriod) *
100;
roundObject(ret);
roundObject(totals);
roundObject(ret2);

View File

@@ -62,7 +62,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
key: "efficiencyoverperiod",
render: (text, record) =>
`${(
(record.totalOverPeriod / (record.actualTotalOverPeriod || .1)) *
(record.totalOverPeriod / (record.actualTotalOverPeriod || 0.1)) *
100
).toFixed(1)} %`,
},
@@ -113,6 +113,12 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
value={data.totalOverPeriod}
/>
</Col>
<Col span={12}>
<Statistic
title={t("scoreboard.labels.efficiencyoverperiod")}
value={`${data.totalEffieciencyOverPeriod}%`}
/>
</Col>
</Row>
<Typography.Text type="secondary">
{t("scoreboard.labels.calendarperiod")}
@@ -121,7 +127,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
<Col md={24} lg={20}>
<Table
columns={columns}
rowKey='employee_number'
rowKey="employee_number"
dataSource={tableData}
id="employee_number"
scroll={{ y: "300px" }}

View File

@@ -589,6 +589,13 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["tt_enforce_hours_for_tech_console"]}
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["bill_allow_post_to_closed"]}
label={t("bodyshop.fields.bill_allow_post_to_closed")}

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client";
import {
Button,
Card,
@@ -21,6 +21,8 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -39,7 +41,17 @@ export function TechClockOffButton({
const [loading, setLoading] = useState(false);
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
const [form] = Form.useForm();
const { queryLoading, data: lineTicketData } = useQuery(
GET_LINE_TICKET_BY_PK,
{
variables: {
id: jobId,
},
skip: !jobId,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
}
);
const { t } = useTranslation();
const emps = bodyshop.employees.filter(
(e) => e.id === (technician && technician.id)
@@ -129,6 +141,54 @@ export function TechClockOffButton({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
console.log(
bodyshop.tt_enforce_hours_for_tech_console
);
if (!bodyshop.tt_enforce_hours_for_tech_console) {
return Promise.resolve();
}
if (
!value ||
getFieldValue("cost_center") === null ||
!lineTicketData
)
return Promise.resolve();
//Check the cost center,
const totals = CalculateAllocationsTotals(
bodyshop,
lineTicketData.joblines,
lineTicketData.timetickets,
lineTicketData.jobs_by_pk.lbr_adjustments
);
const fieldTypeToCheck =
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
);
else {
return Promise.resolve();
}
},
}),
]}
>
<InputNumber min={0} precision={1} />
@@ -178,7 +238,11 @@ export function TechClockOffButton({
</Col>
{!isShiftTicket && (
<Col span={16}>
<LaborAllocationContainer jobid={jobId} />
<LaborAllocationContainer
jobid={jobId || null}
loading={queryLoading}
lineTicketData={lineTicketData}
/>
</Col>
)}
</Row>

View File

@@ -29,7 +29,17 @@ export function TechSider({ technician, techLogout }) {
};
return (
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse}>
<Sider
style={{
height: "100vh",
position: "sticky",
top: 0,
left: 0,
}}
collapsible
collapsed={collapsed}
onCollapse={onCollapse}
>
<Menu theme="dark" defaultSelectedKeys={["1"]} mode="inline">
<Menu.Item
key="1"

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client";
import { Form, Input, InputNumber, Select, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -14,6 +14,7 @@ import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import JobSearchSelect from "../job-search-select/job-search-select.component";
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
@@ -38,7 +39,11 @@ export function TimeTicketModalComponent({
employeeSelectDisabled,
}) {
const { t } = useTranslation();
const [loadLineTicketData, { called, loading, data: lineTicketData }] =
useLazyQuery(GET_LINE_TICKET_BY_PK, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const CostCenterSelect = ({ emps, value, ...props }) => {
return (
<Select
@@ -176,6 +181,51 @@ export function TimeTicketModalComponent({
label={t("timetickets.fields.productivehrs")}
name="productivehrs"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!bodyshop.tt_enforce_hours_for_tech_console) {
return Promise.resolve();
}
if (
!value ||
getFieldValue("cost_center") === null ||
!lineTicketData
)
return Promise.resolve();
//Check the cost center,
const totals = CalculateAllocationsTotals(
bodyshop,
lineTicketData.joblines,
lineTicketData.timetickets,
lineTicketData.jobs_by_pk.lbr_adjustments
);
const fieldTypeToCheck =
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
);
else {
return Promise.resolve();
}
},
}),
{
required:
form.getFieldValue("cost_center") !==
@@ -291,23 +341,28 @@ export function TimeTicketModalComponent({
</Form.Item>
</LayoutFormRow>
<Form.Item dependencies={["jobid"]}>
{() => (
<LaborAllocationContainer
jobid={form.getFieldValue("jobid") || null}
/>
)}
{() => {
const jobid = form.getFieldValue("jobid");
if (
(!called && jobid) ||
(jobid && lineTicketData?.jobs_by_pk?.id !== jobid && !loading)
) {
loadLineTicketData({ variables: { id: jobid } });
}
return (
<LaborAllocationContainer
jobid={jobid || null}
loading={loading}
lineTicketData={lineTicketData}
/>
);
}}
</Form.Item>
</div>
);
}
export function LaborAllocationContainer({ jobid }) {
const { loading, data: lineTicketData } = useQuery(GET_LINE_TICKET_BY_PK, {
variables: { id: jobid },
skip: !jobid,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
if (loading) return <LoadingSkeleton />;
if (!lineTicketData) return null;
return (

View File

@@ -69,6 +69,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
vendor {
id
name
email
}
order_date
deliver_by
@@ -104,6 +105,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
vendor {
id
name
email
}
total
invoice_number

View File

@@ -116,6 +116,7 @@ export const QUERY_BODYSHOP = gql`
md_lost_sale_reasons
md_parts_scan
enforce_conversion_category
tt_enforce_hours_for_tech_console
employees {
user_email
id
@@ -230,6 +231,7 @@ export const UPDATE_SHOP = gql`
md_lost_sale_reasons
md_parts_scan
enforce_conversion_category
tt_enforce_hours_for_tech_console
employees {
id
first_name

View File

@@ -284,6 +284,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
clm_no
v_make_desc
v_color
vehicleid
plate_no
actual_in
scheduled_completion
@@ -1277,7 +1278,7 @@ export const SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE = gql`
export const SEARCH_FOR_JOBS = gql`
query SEARCH_FOR_JOBS($search: String!) {
search_jobs(args: { search: $search }) {
search_jobs(args: { search: $search }, limit: 25) {
id
ro_number
ownr_fn

View File

@@ -2,22 +2,20 @@ import { gql } from "@apollo/client";
export const GLOBAL_SEARCH_QUERY = gql`
query GLOBAL_SEARCH_QUERY($search: String) {
search_jobs(args: { search: $search }) {
search_jobs(args: { search: $search }, limit: 25) {
id
ro_number
status
clm_no
v_model_yr
v_model_desc
v_make_desc
v_color
ownr_fn
ownr_ln
ownr_co_nm
}
search_owners(args: { search: $search }) {
search_owners(args: { search: $search }, limit: 25) {
id
ownr_fn
ownr_ln
@@ -25,7 +23,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
ownr_ph1
ownr_ph2
}
search_vehicles(args: { search: $search }) {
search_vehicles(args: { search: $search }, limit: 25) {
id
v_model_yr
v_model_desc
@@ -34,7 +32,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
v_vin
plate_no
}
search_payments(args: { search: $search }) {
search_payments(args: { search: $search }, limit: 25) {
id
amount
paymentnum
@@ -45,7 +43,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
memo
transactionid
}
search_bills(args: { search: $search }) {
search_bills(args: { search: $search }, limit: 25) {
id
date
invoice_number
@@ -54,7 +52,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
name
}
}
search_phonebook(args: { search: $search }) {
search_phonebook(args: { search: $search }, limit: 25) {
id
firstname
lastname

View File

@@ -1,9 +1,10 @@
.tech-content-container {
overflow-y: auto;
overflow-y: visible;
padding: 1rem;
background: #fff;
}
.tech-layout-container {
height: 100vh;
position: relative;
min-height: 100vh;
}

View File

@@ -541,6 +541,7 @@
"target_touchtime": "Target Touch Time",
"timezone": "Timezone",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"tt_enforce_hours_for_tech_console": "Restrict Claimable hours from Tech Console",
"use_fippa": "Use FIPPA for Names on Generated Documents?",
"uselocalmediaserver": "Use Local Media Server?",
"website": "Website",
@@ -1014,6 +1015,7 @@
"created_at": "Created At",
"email": "Email",
"errors": "Errors",
"excel": "Excel",
"exceptiontitle": "An error has occurred.",
"friday": "Friday",
"globalsearch": "Global Search",
@@ -1063,6 +1065,7 @@
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
"total": "Total",
"totals": "Totals",
"tuesday": "Tuesday",
"unknown": "Unknown",
@@ -2224,6 +2227,7 @@
"csi_invitation": "CSI Invitation",
"csi_invitation_action": "CSI Invite",
"diagnostic_authorization": "Diagnostic Authorization",
"dms_posting_sheet": "DMS Posting Sheet",
"envelope_return_address": "#10 Envelope Return Address Label",
"estimate": "Estimate Only",
"estimate_detail": "Estimate Details",
@@ -2442,6 +2446,8 @@
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
"exported_gsr_by_ro": "Exported Gross Sales - Excel",
"exported_gsr_by_ro_labor": "Exported Gross Sales (Labor) - Excel",
"gsr_by_atp": "",
"gsr_by_ats": "Gross Sales by ATS",
"gsr_by_category": "Gross Sales by Category",
@@ -2661,7 +2667,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time."
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time.",
"hoursenteredmorethanavailable": "The number of hours entered is more than what is available for this cost center."
}
},
"titles": {

View File

@@ -541,6 +541,7 @@
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"tt_enforce_hours_for_tech_console": "",
"use_fippa": "",
"uselocalmediaserver": "",
"website": "",
@@ -1014,6 +1015,7 @@
"created_at": "",
"email": "",
"errors": "",
"excel": "",
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
@@ -1063,6 +1065,7 @@
"sunday": "",
"text": "",
"thursday": "",
"total": "",
"totals": "",
"tuesday": "",
"unknown": "Desconocido",
@@ -2224,6 +2227,7 @@
"csi_invitation": "",
"csi_invitation_action": "",
"diagnostic_authorization": "",
"dms_posting_sheet": "",
"envelope_return_address": "",
"estimate": "",
"estimate_detail": "",
@@ -2442,6 +2446,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"exported_gsr_by_ro": "",
"exported_gsr_by_ro_labor": "",
"gsr_by_atp": "",
"gsr_by_ats": "",
"gsr_by_category": "",
@@ -2661,7 +2667,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
"clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": ""
}
},
"titles": {

View File

@@ -541,6 +541,7 @@
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"tt_enforce_hours_for_tech_console": "",
"use_fippa": "",
"uselocalmediaserver": "",
"website": "",
@@ -1014,6 +1015,7 @@
"created_at": "",
"email": "",
"errors": "",
"excel": "",
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
@@ -1063,6 +1065,7 @@
"sunday": "",
"text": "",
"thursday": "",
"total": "",
"totals": "",
"tuesday": "",
"unknown": "Inconnu",
@@ -2224,6 +2227,7 @@
"csi_invitation": "",
"csi_invitation_action": "",
"diagnostic_authorization": "",
"dms_posting_sheet": "",
"envelope_return_address": "",
"estimate": "",
"estimate_detail": "",
@@ -2442,6 +2446,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"exported_gsr_by_ro": "",
"exported_gsr_by_ro_labor": "",
"gsr_by_atp": "",
"gsr_by_ats": "",
"gsr_by_category": "",
@@ -2661,7 +2667,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
"clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": ""
}
},
"titles": {

View File

@@ -20,7 +20,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "authorization",
},
fippa_authorization: {
title: i18n.t("printcenter.jobs.fippa_authorization"),
description: "CASL Authorization",
@@ -101,7 +100,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "ro",
},
job_notes: {
title: i18n.t("printcenter.jobs.job_notes"),
description: "All Jobs Notes",
@@ -330,7 +328,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
vehicle_delivery_check: {
title: i18n.t("printcenter.jobs.vehicle_delivery_check"),
description: "All Jobs Notes",
@@ -339,7 +336,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
guarantee: {
title: i18n.t("printcenter.jobs.guarantee"),
description: "All Jobs Notes",
@@ -496,6 +492,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "financial",
},
dms_posting_sheet: {
title: i18n.t("printcenter.jobs.dms_posting_sheet"),
description: "DMS Posting Sheet",
subject: i18n.t("printcenter.jobs.dms_posting_sheet"),
key: "dms_posting_sheet",
disabled: false,
group: "financial",
},
}
: {}),
...(!type || type === "job_special"
@@ -622,7 +626,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_closed_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_ins_co"
@@ -640,7 +643,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_closed: {
title: i18n.t("reportcenter.templates.hours_sold_summary_closed"),
description: "",
@@ -654,7 +656,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_closed_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_ins_co"
@@ -672,7 +673,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_open: {
title: i18n.t("reportcenter.templates.hours_sold_detail_open"),
description: "",
@@ -686,7 +686,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_open_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_open_ins_co"
@@ -704,7 +703,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_open: {
title: i18n.t("reportcenter.templates.hours_sold_summary_open"),
description: "",
@@ -718,7 +716,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_open_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_open_ins_co"
@@ -736,7 +733,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_closed_csr: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_csr"
@@ -1090,7 +1086,6 @@ export const TemplateList = (type, context) => {
},
group: "customers",
},
schedule: {
title: i18n.t("reportcenter.templates.schedule"),
subject: i18n.t("reportcenter.templates.schedule"),
@@ -1102,7 +1097,6 @@ export const TemplateList = (type, context) => {
},
group: "customers",
},
timetickets: {
title: i18n.t("reportcenter.templates.timetickets"),
subject: i18n.t("reportcenter.templates.timetickets"),
@@ -1126,7 +1120,6 @@ export const TemplateList = (type, context) => {
title: i18n.t("reportcenter.templates.attendance_detail"),
subject: i18n.t("reportcenter.templates.attendance_detail"),
key: "attendance_detail",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.timetickets"),
@@ -1138,7 +1131,6 @@ export const TemplateList = (type, context) => {
title: i18n.t("reportcenter.templates.attendance_summary"),
subject: i18n.t("reportcenter.templates.attendance_summary"),
key: "attendance_summary",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.timetickets"),
@@ -1158,7 +1150,6 @@ export const TemplateList = (type, context) => {
},
group: "payroll",
},
timetickets_summary: {
title: i18n.t("reportcenter.templates.timetickets_summary"),
subject: i18n.t("reportcenter.templates.timetickets_summary"),
@@ -1171,7 +1162,6 @@ export const TemplateList = (type, context) => {
},
group: "payroll",
},
estimator_detail: {
title: i18n.t("reportcenter.templates.estimator_detail"),
description: "",
@@ -1224,7 +1214,6 @@ export const TemplateList = (type, context) => {
},
group: "purchases",
},
void_ros: {
title: i18n.t("reportcenter.templates.void_ros"),
description: "",
@@ -1329,7 +1318,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
gsr_by_estimator: {
title: i18n.t("reportcenter.templates.gsr_by_estimator"),
description: "",
@@ -1814,7 +1802,9 @@ export const TemplateList = (type, context) => {
group: "jobs",
},
purchase_return_ratio_grouped_by_vendor_detail: {
title: i18n.t("reportcenter.templates.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"
),
@@ -1828,7 +1818,9 @@ export const TemplateList = (type, context) => {
group: "purchases",
},
purchase_return_ratio_grouped_by_vendor_summary: {
title: i18n.t("reportcenter.templates.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"
),
@@ -1843,9 +1835,7 @@ export const TemplateList = (type, context) => {
},
production_over_time: {
title: i18n.t("reportcenter.templates.production_over_time"),
subject: i18n.t(
"reportcenter.templates.production_over_time"
),
subject: i18n.t("reportcenter.templates.production_over_time"),
key: "production_over_time",
//idtype: "vendor",
disabled: false,
@@ -1857,9 +1847,7 @@ export const TemplateList = (type, context) => {
},
customer_list: {
title: i18n.t("reportcenter.templates.customer_list"),
subject: i18n.t(
"reportcenter.templates.customer_list"
),
subject: i18n.t("reportcenter.templates.customer_list"),
key: "customer_list",
//idtype: "vendor",
disabled: false,
@@ -1869,6 +1857,32 @@ export const TemplateList = (type, context) => {
},
group: "customers",
},
exported_gsr_by_ro: {
title: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
key: "exported_gsr_by_ro",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_exported"),
},
group: "sales",
},
exported_gsr_by_ro_labor: {
title: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
key: "exported_gsr_by_ro_labor",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_exported"),
},
group: "sales",
},
}
: {}),
...(!type || type === "courtesycarcontract"
@@ -1963,7 +1977,6 @@ export const TemplateList = (type, context) => {
},
}
: {}),
...(!type || type === "production"
? {
production_by_last_name: {

View File

@@ -761,6 +761,13 @@
table:
name: email_audit_trail
schema: public
- name: employee_teams
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: employee_teams
schema: public
- name: employees
using:
foreign_key_constraint_on:
@@ -824,6 +831,13 @@
table:
name: transitions
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: tt_approval_queue
schema: public
- name: vehicles
using:
foreign_key_constraint_on:
@@ -928,6 +942,7 @@
- textid
- timezone
- tt_allow_post_to_invoiced
- tt_enforce_hours_for_tech_console
- updated_at
- use_fippa
- uselocalmediaserver
@@ -1016,6 +1031,7 @@
- target_touchtime
- timezone
- tt_allow_post_to_invoiced
- tt_enforce_hours_for_tech_console
- updated_at
- use_fippa
- uselocalmediaserver
@@ -1945,6 +1961,165 @@
- active:
_eq: true
check: null
- table:
name: employee_team_members
schema: public
object_relationships:
- name: employee
using:
foreign_key_constraint_on: employeeid
- name: employee_team
using:
foreign_key_constraint_on: teamid
insert_permissions:
- role: user
permission:
check:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
select_permissions:
- role: user
permission:
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
update_permissions:
- role: user
permission:
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
delete_permissions:
- role: user
permission:
backend_only: false
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
name: employee_teams
schema: public
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
array_relationships:
- name: employee_team_members
using:
foreign_key_constraint_on:
column: teamid
table:
name: employee_team_members
schema: public
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- active
- name
- created_at
- updated_at
- bodyshopid
- id
select_permissions:
- role: user
permission:
columns:
- active
- name
- created_at
- updated_at
- bodyshopid
- id
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
update_permissions:
- role: user
permission:
columns:
- active
- bodyshopid
- name
- updated_at
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
- table:
name: employee_vacation
schema: public
@@ -2045,6 +2220,13 @@
table:
name: allocations
schema: public
- name: employee_team_members
using:
foreign_key_constraint_on:
column: employeeid
table:
name: employee_team_members
schema: public
- name: employee_vacations
using:
foreign_key_constraint_on:
@@ -2087,6 +2269,13 @@
table:
name: timetickets
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: employeeid
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -2218,30 +2407,32 @@
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- jobid
- billid
- bodyshopid
- created_at
- id
- jobid
- message
- metadata
- paymentid
- successful
- message
- bodyshopid
- updated_at
- useremail
select_permissions:
- role: user
permission:
columns:
- successful
- message
- useremail
- created_at
- updated_at
- billid
- bodyshopid
- created_at
- id
- jobid
- message
- metadata
- paymentid
- successful
- updated_at
- useremail
filter:
bodyshop:
associations:
@@ -2493,6 +2684,7 @@
_eq: true
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2559,6 +2751,7 @@
permission:
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2637,6 +2830,7 @@
permission:
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2965,6 +3159,13 @@
table:
name: transitions
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: jobid
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -5030,6 +5231,14 @@
- name: job
using:
foreign_key_constraint_on: jobid
array_relationships:
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: timeticketid
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -5048,6 +5257,7 @@
- ciecacode
- clockoff
- clockon
- committed_at
- cost_center
- created_at
- date
@@ -5068,6 +5278,7 @@
- ciecacode
- clockoff
- clockon
- committed_at
- cost_center
- created_at
- date
@@ -5097,6 +5308,7 @@
- ciecacode
- clockoff
- clockon
- committed_at
- cost_center
- created_at
- date
@@ -5217,6 +5429,117 @@
authid:
_eq: X-Hasura-User-Id
check: {}
- table:
name: tt_approval_queue
schema: public
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
- name: employee
using:
foreign_key_constraint_on: employeeid
- name: job
using:
foreign_key_constraint_on: jobid
- name: timeticket
using:
foreign_key_constraint_on: timeticketid
- name: user
using:
foreign_key_constraint_on: approved_by
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
columns:
- id
- created_at
- updated_at
- bodyshopid
- jobid
- employeeid
- timeticketid
- approved_by
- approved_at
- actualhrs
- productivehrs
- rate
- flat_rate
- ciecacode
- cost_center
- date
- memo
select_permissions:
- role: user
permission:
columns:
- flat_rate
- date
- actualhrs
- productivehrs
- rate
- approved_by
- ciecacode
- cost_center
- memo
- approved_at
- created_at
- updated_at
- bodyshopid
- employeeid
- id
- jobid
- timeticketid
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
allow_aggregations: true
update_permissions:
- role: user
permission:
columns:
- flat_rate
- date
- actualhrs
- productivehrs
- rate
- approved_by
- ciecacode
- cost_center
- memo
- approved_at
- created_at
- updated_at
- bodyshopid
- employeeid
- id
- jobid
- timeticketid
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
check: null
- table:
name: users
schema: public
@@ -5287,6 +5610,13 @@
table:
name: parts_orders
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: approved_by
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."joblines" add column "act_price_before_ppc" numeric
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."joblines" add column "act_price_before_ppc" numeric
null;

View File

@@ -0,0 +1 @@
DROP TABLE "public"."employee_teams";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."employee_teams" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "name" text NOT NULL, "active" boolean NOT NULL DEFAULT true, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade);
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_employee_teams_updated_at"
BEFORE UPDATE ON "public"."employee_teams"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_employee_teams_updated_at" ON "public"."employee_teams"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1 @@
DROP TABLE "public"."employee_team_members";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."employee_team_members" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "teamid" uuid NOT NULL, "employeeid" uuid NOT NULL, "labor_rates" jsonb NOT NULL DEFAULT jsonb_build_object(), "percentage" numeric NOT NULL DEFAULT 0, PRIMARY KEY ("id") , FOREIGN KEY ("teamid") REFERENCES "public"."employee_teams"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade);
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_employee_team_members_updated_at"
BEFORE UPDATE ON "public"."employee_team_members"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_employee_team_members_updated_at" ON "public"."employee_team_members"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."exportlog" add column "metadata" jsonb
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."exportlog" add column "metadata" jsonb
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
not null default 'false';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."timetickets" add column "committed_at" timestamptz
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."timetickets" add column "committed_at" timestamptz
null;

View File

@@ -0,0 +1 @@
DROP TABLE "public"."tt_approval_queue";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."tt_approval_queue" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "jobid" uuid NOT NULL, "employeeid" uuid NOT NULL, "timeticketid" uuid, "approved_by" text, "approved_at" timestamptz NOT NULL, "actualhrs" numeric NOT NULL DEFAULT 0, "productivehrs" numeric NOT NULL DEFAULT 0, "rate" numeric NOT NULL DEFAULT 0, "flat_rate" boolean NOT NULL DEFAULT true, "ciecacode" text, "cost_center" text NOT NULL, "date" date NOT NULL DEFAULT now(), "memo" text NOT NULL, 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 ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("timeticketid") REFERENCES "public"."timetickets"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("approved_by") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict);
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_tt_approval_queue_updated_at"
BEFORE UPDATE ON "public"."tt_approval_queue"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_tt_approval_queue_updated_at" ON "public"."tt_approval_queue"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1 @@
alter table "public"."tt_approval_queue" alter column "approved_at" set not null;

View File

@@ -0,0 +1 @@
alter table "public"."tt_approval_queue" alter column "approved_at" drop not null;

View File

@@ -0,0 +1 @@
alter table "public"."tt_approval_queue" alter column "memo" set not null;

View File

@@ -0,0 +1 @@
alter table "public"."tt_approval_queue" alter column "memo" drop not null;

View File

@@ -249,10 +249,10 @@ async function QueryCustomersFromDms(socket) {
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
//ContactId: "00000000000000000000000000000000",
// ContactCode: socket.JobData.owner.accountingid,
FirstName: socket.JobData.ownr_co_nm
FirstName: socket.JobData.ownr_fn,
LastName: socket.JobData.ownr_co_nm
? socket.JobData.ownr_co_nm
: socket.JobData.ownr_fn,
LastName: socket.JobData.ownr_ln,
: socket.JobData.ownr_ln,
PhoneNumber: socket.JobData.ownr_ph1,
EmailAddress: socket.JobData.ownr_ea,
// ModifiedSince: "0001-01-01T00:00:00.0000000Z",

View File

@@ -224,6 +224,7 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
} finally {
//Ensure we always insert logEvents
//GQL to insert logevents.
CdkBase.createLogEvent(
socket,
"DEBUG",
@@ -1213,6 +1214,7 @@ async function GenerateTransWips(socket) {
wips.push(item);
});
socket.transWips = wips;
return wips;
}
@@ -1388,6 +1390,7 @@ async function MarkJobExported(socket, jobid) {
jobid: jobid,
successful: true,
useremail: socket.user.email,
metadata: socket.transWips,
},
bill: {
exported: true,

View File

@@ -20,7 +20,8 @@ async function StatusTransition(req, res) {
res.status(403).send("Unauthorized");
return;
}
res.sendStatus(200);
return;
const {
id: jobid,
status: value,
@@ -78,7 +79,7 @@ async function StatusTransition(req, res) {
res.sendStatus(200); //.json(ret);
} catch (error) {
logger.log("job-costing-error", "ERROR", req.user.email, jobid, {
logger.log("job-status-transition-error", "ERROR", req.user?.email, jobid, {
message: error.message,
stack: error.stack,
});