Merged in release/2022-03-04 (pull request #413)
release/2022-03-04 Approved-by: Patrick Fic
This commit is contained in:
@@ -9,7 +9,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
}
|
||||
/>
|
||||
|
||||
<FormFieldsChanged form={form} />
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.make")}
|
||||
|
||||
@@ -55,15 +55,17 @@ export function JobEmployeeAssignments({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function JobLinesBillRefernece({ jobline }) {
|
||||
{subletRequired && <WarningFilled />}
|
||||
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
|
||||
billLine.bill.vendor.name
|
||||
})`}
|
||||
} #${billLine.bill.invoice_number})`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export function JobNotesComponent({
|
||||
messageObject={{
|
||||
subject: Templates.individual_job_note.subject,
|
||||
}}
|
||||
id={record.id}
|
||||
id={jobId}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
|
||||
@@ -73,6 +73,7 @@ export function PartsOrderModalComponent({
|
||||
options={vendorList}
|
||||
disabled={isReturn}
|
||||
preferredMake={preferredMake}
|
||||
showPhone
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
|
||||
@@ -196,6 +196,11 @@ export function PartsOrderModalContainer({
|
||||
(item) => item.id === values.vendorid
|
||||
)[0];
|
||||
|
||||
let vendorEmails =
|
||||
matchingVendor &&
|
||||
matchingVendor.email &&
|
||||
matchingVendor.email.split(RegExp("[;,]"));
|
||||
|
||||
GenerateDocument(
|
||||
{
|
||||
name: isReturn
|
||||
@@ -206,7 +211,7 @@ export function PartsOrderModalContainer({
|
||||
},
|
||||
},
|
||||
{
|
||||
to: matchingVendor ? [matchingVendor.email] : null,
|
||||
to: matchingVendor ? vendorEmails : null,
|
||||
replyTo: bodyshop.email,
|
||||
subject: isReturn
|
||||
? Templates.parts_return_slip.subject
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function ProductionBoardCard(
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let employee_body, employee_prep, employee_refinish; //employee_csr;
|
||||
let employee_body, employee_prep, employee_refinish, employee_csr;
|
||||
if (card.employee_body) {
|
||||
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
|
||||
}
|
||||
@@ -34,6 +34,9 @@ export default function ProductionBoardCard(
|
||||
(e) => e.id === card.employee_refinish
|
||||
);
|
||||
}
|
||||
if (card.employee_csr) {
|
||||
employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
}
|
||||
// if (card.employee_csr) {
|
||||
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
// }
|
||||
@@ -131,11 +134,11 @@ export default function ProductionBoardCard(
|
||||
)} ${employee_refinish.last_name.charAt(0)}`
|
||||
: ""
|
||||
} ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
|
||||
{/* <Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
employee_csr
|
||||
? `${employee_csr.first_name} ${employee_csr.last_name}`
|
||||
: ""
|
||||
}`}</Col> */}
|
||||
}`}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
@@ -66,6 +66,7 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
include ||
|
||||
j.employee_body === employeeId ||
|
||||
j.employee_prep === employeeId ||
|
||||
j.employee_csr === employeeId ||
|
||||
j.employee_refinish === employeeId;
|
||||
}
|
||||
|
||||
@@ -76,9 +77,8 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
|
||||
Object.keys(DataGroupedByStatus).map((statusGroupKey) => {
|
||||
try {
|
||||
boardLanes.columns.find(
|
||||
(l) => l.id === statusGroupKey
|
||||
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
boardLanes.columns.find((l) => l.id === statusGroupKey).cards =
|
||||
sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
} catch (error) {
|
||||
console.log("Error while creating board card", error);
|
||||
}
|
||||
|
||||
@@ -116,15 +116,17 @@ export function ProductionListEmpAssignment({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { HeartOutlined } from "@ant-design/icons";
|
||||
import { Select, Tag } from "antd";
|
||||
import { Select, Space, Tag } from "antd";
|
||||
import React, { forwardRef, useEffect, useState } from "react";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
const { Option } = Select;
|
||||
|
||||
//To be used as a form element only.
|
||||
|
||||
const VendorSearchSelect = (
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake },
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
|
||||
ref
|
||||
) => {
|
||||
const [option, setOption] = useState(value);
|
||||
@@ -35,6 +36,7 @@ const VendorSearchSelect = (
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
dropdownMatchSelectWidth={false}
|
||||
onChange={setOption}
|
||||
optionFilterProp="name"
|
||||
onSelect={onSelect}
|
||||
@@ -50,10 +52,15 @@ const VendorSearchSelect = (
|
||||
>
|
||||
<div className="imex-flex-row">
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
<HeartOutlined />
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
<HeartOutlined style={{ color: "red" }} />
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
@@ -64,9 +71,14 @@ const VendorSearchSelect = (
|
||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
|
||||
@@ -109,12 +109,14 @@ export default function VendorsFormComponent({
|
||||
|
||||
<Form.Item
|
||||
label={t("vendors.fields.email")}
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: t("general.validation.invalidemail"),
|
||||
},
|
||||
]}
|
||||
rules={
|
||||
[
|
||||
// {
|
||||
// type: "email",
|
||||
// message: t("general.validation.invalidemail"),
|
||||
// },
|
||||
]
|
||||
}
|
||||
name="email"
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("email")} />
|
||||
|
||||
@@ -59,6 +59,8 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
||||
employeeid
|
||||
memo
|
||||
flat_rate
|
||||
clockon
|
||||
clockoff
|
||||
employee {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -696,6 +696,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
joblineid
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
|
||||
@@ -87,6 +87,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
|
||||
discount
|
||||
email
|
||||
active
|
||||
phone
|
||||
}
|
||||
jobs(where: { id: { _eq: $jobId } }) {
|
||||
v_make_desc
|
||||
|
||||
@@ -70,7 +70,12 @@ export function CourtesyCarCreateContainer({
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
>
|
||||
<CourtesyCarFormComponent form={form} saveLoading={loading} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
|
||||
@@ -149,6 +149,7 @@ app.post(
|
||||
fb.unsubscribe
|
||||
);
|
||||
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
||||
app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser);
|
||||
|
||||
//Stripe Processing
|
||||
var stripe = require("./server/stripe/payment");
|
||||
@@ -180,7 +181,6 @@ app.post("/data/arms", data.arms);
|
||||
var taskHandler = require("./server/tasks/tasks");
|
||||
app.post("/taskHandler", taskHandler.taskHandler);
|
||||
|
||||
|
||||
var ioevent = require("./server/ioevent/ioevent");
|
||||
app.post("/ioevent", ioevent.default);
|
||||
app.post("/newlog", (req, res) => {
|
||||
@@ -188,7 +188,6 @@ app.post("/newlog", (req, res) => {
|
||||
logger.log(message, type, user, record, object);
|
||||
});
|
||||
|
||||
|
||||
var cdkGetMake = require("./server/cdk/cdk-get-makes");
|
||||
app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default);
|
||||
|
||||
|
||||
@@ -25,6 +25,43 @@ const adminEmail = [
|
||||
"patrick@thinkimex.com",
|
||||
];
|
||||
|
||||
exports.createUser = (req, res) => {
|
||||
logger.log("admin-create-user", "WARN", req.user.email, null, {
|
||||
request: req.body,
|
||||
});
|
||||
if (!adminEmail.includes(req.user.email)) {
|
||||
logger.log(
|
||||
"admin-create-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
}
|
||||
const { email, displayName, password } = req.body;
|
||||
admin
|
||||
.auth()
|
||||
.createUser({ email, displayName, password })
|
||||
.then((userRecord) => {
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
|
||||
logger.log("admin-update-user-success", "DEBUG", req.user.email, null, {
|
||||
userRecord,
|
||||
});
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.updateUser = (req, res) => {
|
||||
logger.log("admin-update-user", "WARN", req.user.email, null, {
|
||||
request: req.body,
|
||||
|
||||
@@ -111,10 +111,9 @@ exports.job = async (req, res) => {
|
||||
}
|
||||
|
||||
if (
|
||||
moment(item.actual_completion || item.scheduled_completion).tz(timezone).isBefore(
|
||||
moment().tz(timezone),
|
||||
"day"
|
||||
)
|
||||
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;
|
||||
@@ -128,7 +127,9 @@ exports.job = async (req, res) => {
|
||||
} else {
|
||||
const itemDate = moment(
|
||||
item.actual_completion || item.scheduled_completion
|
||||
).tz(timezone).format("yyyy-MM-DD");
|
||||
)
|
||||
.tz(timezone)
|
||||
.format("yyyy-MM-DD");
|
||||
if (!!load[itemDate]) {
|
||||
load[itemDate].hoursOut =
|
||||
(load[itemDate].hoursOut || 0) +
|
||||
@@ -153,14 +154,20 @@ exports.job = async (req, res) => {
|
||||
const end = moment.max([
|
||||
...filteredArrJobs.map((a) => moment(a.scheduled_in).tz(timezone)),
|
||||
...filteredCompJobs
|
||||
.map((p) => moment(p.actual_completion || p.scheduled_completion).tz(timezone))
|
||||
.map((p) =>
|
||||
moment(p.actual_completion || p.scheduled_completion).tz(timezone)
|
||||
)
|
||||
.filter((p) => p.isValid() && p.isAfter(yesterday)),
|
||||
moment().tz(timezone).add(15, "days"),
|
||||
]);
|
||||
const range = Math.round(moment.duration(end.diff(today)).asDays());
|
||||
for (var day = 0; day < range; day++) {
|
||||
const current = moment(today).tz(timezone).add(day, "days").format("yyyy-MM-DD");
|
||||
const prev = moment(today).tz(timezone)
|
||||
const current = moment(today)
|
||||
.tz(timezone)
|
||||
.add(day, "days")
|
||||
.format("yyyy-MM-DD");
|
||||
const prev = moment(today)
|
||||
.tz(timezone)
|
||||
.add(day - 1, "days")
|
||||
.format("yyyy-MM-DD");
|
||||
if (!!!load[current]) {
|
||||
@@ -194,7 +201,7 @@ exports.job = async (req, res) => {
|
||||
load[startIsoFormat] = { blocked: true };
|
||||
}
|
||||
});
|
||||
// //Propose the first 5 dates where we are below target.
|
||||
// //Propose the first 10 dates where we are below target.
|
||||
|
||||
const possibleDates = [];
|
||||
delete load.productionTotal;
|
||||
@@ -204,7 +211,7 @@ exports.job = async (req, res) => {
|
||||
|
||||
loadKeys.forEach((loadKey) => {
|
||||
const isShopOpen =
|
||||
(workingdays[dayOfWeekMapper(moment(loadKey).tz(timezone).day())] || false) &&
|
||||
(workingdays[dayOfWeekMapper(moment(loadKey).day())] || false) &&
|
||||
!load[loadKey].blocked;
|
||||
|
||||
if (
|
||||
@@ -216,10 +223,10 @@ exports.job = async (req, res) => {
|
||||
possibleDates.push(new Date(loadKey).toISOString().substr(0, 10));
|
||||
});
|
||||
|
||||
if (possibleDates.length < 6) {
|
||||
if (possibleDates.length < 11) {
|
||||
res.json(possibleDates);
|
||||
} else {
|
||||
res.json(possibleDates.slice(0, 5));
|
||||
res.json(possibleDates.slice(0, 10));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log("smart-scheduling-error", "ERROR", req.user.email, jobId, {
|
||||
|
||||
Reference in New Issue
Block a user