Merged in release/2022-01-14 (pull request #349)

release/2022-01-14

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2022-01-14 22:41:23 +00:00
14 changed files with 206 additions and 33 deletions

View File

@@ -38007,6 +38007,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_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>production_by_csr</name>
<definition_loaded>false</definition_loaded>

View File

@@ -18,6 +18,7 @@ import ProductionListLastContacted from "./production-list-columns.lastcontacted
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
const r = ({ technician, state, activeStatuses, bodyshop }) => {
@@ -251,6 +252,29 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => <ProductionListColumnStatus record={record} />,
},
{
title: i18n.t("jobs.fields.category"),
dataIndex: "category",
key: "category",
ellipsis: true,
filters:
(bodyshop &&
bodyshop.md_categories.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.category),
sorter: (a, b) => alphaSort(a.category, b.category),
sortOrder:
state.sortedInfo.columnKey === "category" && state.sortedInfo.order,
render: (text, record) => (
<ProductionListColumnCategory record={record} />
),
},
{
title: i18n.t("production.labels.bodyhours"),
dataIndex: "labhrs",

View File

@@ -0,0 +1,63 @@
import { useMutation } from "@apollo/client";
import { Dropdown, Menu, Spin } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionListColumnCategory({ record, bodyshop }) {
const [updateJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const handleSetStatus = async (e) => {
logImEXEvent("production_change_status");
setLoading(true);
const { key } = e;
await updateJob({
variables: {
jobId: record.id,
job: {
category: key,
},
},
});
setLoading(false);
};
return (
<Dropdown
overlay={
<Menu
style={{ maxHeight: "200px", overflowY: "auto" }}
onClick={handleSetStatus}
>
{bodyshop.md_categories.map((item) => (
<Menu.Item key={item}>{item}</Menu.Item>
))}
</Menu>
}
trigger={["click"]}
>
<div style={{ width: "100%", height: "19px", cursor: "pointer" }}>
{record.category}
{loading && <Spin />}
</div>
</Dropdown>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnCategory);

View File

@@ -88,6 +88,12 @@ export function ProductionListTable({
);
const handleTableChange = (pagination, filters, sorter) => {
console.log(
"🚀 ~ file: production-list-table.component.jsx ~ line 91 ~ pagination, filters, sorter",
pagination,
filters,
sorter
);
setState({
...state,
filteredInfo: filters,
@@ -265,6 +271,7 @@ export function ProductionListTable({
columns={columns.map((c, index) => {
return {
...c,
filteredValue: state.filteredInfo[c.key] || null,
sortOrder:
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
title: headerItem(c),

View File

@@ -25,15 +25,20 @@ export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
const { ssbuckets } = bodyshop;
const data = useMemo(() => {
return Object.keys(loadData.expectedLoad).map((key) => {
const metadataBucket = ssbuckets.filter((b) => b.id === key)[0];
return (
(loadData &&
loadData.expectedLoad &&
Object.keys(loadData.expectedLoad).map((key) => {
const metadataBucket = ssbuckets.filter((b) => b.id === key)[0];
return {
bucket: loadData.expectedLoad[key].label,
current: loadData.expectedLoad[key].count,
target: metadataBucket && metadataBucket.target,
};
});
return {
bucket: loadData.expectedLoad[key].label,
current: loadData.expectedLoad[key].count,
target: metadataBucket && metadataBucket.target,
};
})) ||
[]
);
}, [loadData, ssbuckets]);
const popContent = (

View File

@@ -75,9 +75,17 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<div>
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<>
<DataLabel label={t("bodyshop.labels.dms.cdk_dealerid")}>
{form.getFieldValue("cdk_dealerid")}
</DataLabel>
{bodyshop.cdk_dealerid && (
<DataLabel label={t("bodyshop.labels.dms.cdk_dealerid")}>
{form.getFieldValue("cdk_dealerid")}
</DataLabel>
)}
{bodyshop.pbs_serialnumber && (
<DataLabel label={t("bodyshop.labels.dms.pbs_serialnumber")}>
{form.getFieldValue("pbs_serialnumber")}
</DataLabel>
)}
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.dms.default_journal")}
@@ -158,11 +166,11 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
label={t("jobs.fields.dms.payer.control_type")}
key={`${index}control_type`}
name={[field.name, "control_type"]}
rules={[
{
required: true,
},
]}
// rules={[
// {
// required: true,
// },
// ]}
>
<Select showSearch>
<Select.Option value="ro_number">

View File

@@ -123,6 +123,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
ro_number
ownr_fn
ownr_ln
category
ownr_co_nm
v_model_yr
v_model_desc
@@ -193,6 +194,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
status
ro_number
ownr_fn
category
ownr_ln
ownr_co_nm
v_model_yr
@@ -263,6 +265,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
id
updated_at
status
category
ro_number
ownr_fn
ownr_ln

View File

@@ -2260,6 +2260,7 @@
"parts_not_recieved": "Parts Not Received",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date and Type",
"production_by_category": "Production by Category",
"production_by_csr": "Production by CSR",
"production_by_last_name": "Production by Last Name",
"production_by_repair_status": "Production by Status",

View File

@@ -2260,6 +2260,7 @@
"parts_not_recieved": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_category": "",
"production_by_csr": "",
"production_by_last_name": "",
"production_by_repair_status": "",

View File

@@ -2260,6 +2260,7 @@
"parts_not_recieved": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_category": "",
"production_by_csr": "",
"production_by_last_name": "",
"production_by_repair_status": "",

View File

@@ -1598,6 +1598,14 @@ export const TemplateList = (type, context) => {
//idtype: "vendor",
disabled: false,
},
production_by_category: {
title: i18n.t("reportcenter.templates.production_by_category"),
description: "",
subject: i18n.t("reportcenter.templates.production_by_category"),
key: "production_by_category",
//idtype: "vendor",
disabled: false,
},
}
: {}),
...(!type || type === "special"

View File

@@ -68,6 +68,10 @@ app.get("/test", async function (req, res) {
"git rev-parse --short HEAD"
);
logger.log("test-api-status", "DEBUG", "api", { commit });
sendEmail.sendServerEmail({
subject: `API Check - ${process.env.NODE_ENV}`,
text: `Server API check has come in. `,
});
res.status(200).send(`OK - ${commit}`);
});

View File

@@ -15,6 +15,7 @@ require("dotenv").config({
let Client = require("ssh2-sftp-client");
const client = require("../graphql-client/graphql-client").client;
const { sendServerEmail } = require("../email/sendemail");
const AHDineroFormat = "0.00";
const AhDateFormat = "MMDDYYYY";
@@ -112,16 +113,6 @@ exports.default = async (req, res) => {
});
}
}
//if (process.env.NODE_ENV !== "production") {
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
res.json(allxmlsToUpload);
return;
// }
let sftp = new Client();
sftp.on("error", (errors) =>
logger.log("autohouse-sftp-error", "ERROR", "api", null, {
@@ -155,7 +146,17 @@ exports.default = async (req, res) => {
} finally {
sftp.end();
}
sendServerEmail({
subject: `Autohouse Report ${moment().format("MM-DD-YY")}`,
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
Uploaded: ${JSON.stringify(
allxmlsToUpload.map((x) => x.filename),
null,
2
)}
`,
});
res.sendStatus(200);
} catch (error) {
res.status(200).json(error);
@@ -682,13 +683,11 @@ const CreateCosts = (job) => {
ticketTotalsByCostCenter[defaultCosts.LAM] || Dinero(),
StructuralLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAS] || Dinero(),
ElectricalLaborTotalCost:
ElectricalLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAE] || Dinero(),
FrameLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAF] || Dinero(),
GlassLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAG] || Dinero(),
DetailLaborTotalCost:
FrameLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAF] || Dinero(),
GlassLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAG] || Dinero(),
DetailLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAD] || Dinero(),
PMTotalCost: billTotalsByCostCenters[defaultCosts.MAPA] || Dinero(),
BMTotalCost: billTotalsByCostCenters[defaultCosts.MASH] || Dinero(),

View File

@@ -18,6 +18,34 @@ let transporter = nodemailer.createTransport({
SES: { ses, aws },
});
exports.sendServerEmail = async function ({ subject, text }) {
try {
transporter.sendMail(
{
from: `ImEX Online API - ${process.env.NODE_ENV} <noreply@imex.online>`,
to: ["patrick@snapt.ca"],
subject: subject,
text: text,
ses: {
// optional extra arguments for SendRawEmail
Tags: [
{
Name: "tag_name",
Value: "tag_value",
},
],
},
},
(err, info) => {
console.log(err || info);
}
);
} catch (error) {
console.log(error);
logger.log("server-email-failure", "error", null, null, error);
}
};
exports.sendEmail = async (req, res) => {
logger.log("send-email", "DEBUG", req.user.email, null, {
from: `${req.body.from.name} <${req.body.from.address}>`,