Merged in development (pull request #32)

SIT 1 Bug Fixes
Package updates.
IO-723
IO-723
IO-744
IO-746
IO-743
IO-745
IO-747
This commit is contained in:
Patrick Fic
2021-03-09 01:08:54 +00:00
37 changed files with 31628 additions and 844 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2">
<babeledit_project version="1.2" be_version="2.7.1">
<!--
BabelEdit project file
@@ -20710,6 +20710,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>voidnote</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>
<folder_node>

23348
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
"@stripe/stripe-js": "^1.12.1",
"@tanem/react-nprogress": "^3.0.57",
"@tinymce/tinymce-react": "^3.10.3",
"antd": "^4.12.3",
"antd": "^4.13.1",
"apollo-link-logger": "^2.0.0",
"axios": "^0.21.1",
"craco-less": "^1.17.1",
@@ -26,7 +26,7 @@
"i18next-browser-languagedetector": "^6.0.1",
"jsoneditor": "^9.1.10",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.11",
"libphonenumber-js": "^1.9.12",
"logrocket": "^1.0.13",
"moment-business-days": "^1.2.0",
"phone": "^2.4.21",
@@ -39,11 +39,11 @@
"react-dom": "^17.0.1",
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-i18next": "^11.8.7",
"react-i18next": "^11.8.9",
"react-icons": "^4.2.0",
"react-number-format": "^4.4.4",
"react-redux": "^7.2.2",
"react-resizable": "^1.11.0",
"react-resizable": "^1.11.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-virtualized": "^9.22.3",

View File

@@ -117,6 +117,13 @@ export default function ContractsCarsComponent({
type: "radio",
selectedRowKeys: [selectedCar],
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleSelect(record);
},
};
}}
/>
);
}

View File

@@ -1,9 +1,9 @@
import { useQuery } from "@apollo/client";
import moment from "moment";
import React from "react";
import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component";
import ContractCarsComponent from "./contract-cars.component";
import moment from "moment";
export default function ContractCarsContainer({ selectedCarState, form }) {
const { loading, error, data } = useQuery(QUERY_AVAILABLE_CC, {
@@ -14,6 +14,11 @@ export default function ContractCarsContainer({ selectedCarState, form }) {
const handleSelect = (record) => {
setSelectedCar(record.id);
console.log(
"🚀 ~ file: contract-cars.container.jsx ~ line 19 ~ record",
record
);
form.setFieldsValue({
kmstart: record.mileage,
dailyrate: record.dailycost,

View File

@@ -1,25 +1,26 @@
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import {
Button,
Form,
InputNumber,
notification,
Popover,
Radio,
Form,
InputNumber,
Select,
Space,
} from "antd";
import { useTranslation } from "react-i18next";
import { useMutation } from "@apollo/client";
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
import axios from "axios";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { useHistory } from "react-router-dom";
import axios from "axios";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -49,8 +50,9 @@ export function ContractConvertToRo({
moment(contract.start),
"days"
);
const billingLines = [
{
const billingLines = [];
if (contractLength > 0)
billingLines.push({
unq_seq: 1,
line_no: 1,
line_ref: 1,
@@ -63,9 +65,7 @@ export function ContractConvertToRo({
mod_lb_hrs: 0,
db_ref: "io-ccdr",
// mod_lbr_ty: "PAL",
},
];
});
const mileageDiff =
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
if (mileageDiff > 0) {
@@ -139,7 +139,8 @@ export function ContractConvertToRo({
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
ins_co_nm: "CC",
ins_co_nm: values.ins_co_nm,
class: values.class,
converted: true,
clm_no: contract.job.clm_no ? `${contract.job.clm_no}-CC` : null,
ownr_fn: contract.job.owner.ownr_fn,
@@ -158,6 +159,7 @@ export function ContractConvertToRo({
text: t("contracts.labels.noteconvertedfrom", {
agreementnumber: contract.agreementnumber,
}),
audit: true,
created_by: currentUser.email,
},
],
@@ -306,6 +308,42 @@ export function ContractConvertToRo({
const popContent = (
<div>
<Form onFinish={handleFinish}>
<Form.Item
name={["ins_co_nm"]}
label={t("jobs.fields.ins_co_nm")}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
>
<Select>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={"class"}
label={t("jobs.fields.class")}
rules={[
{
required: bodyshop.enforce_class,
message: t("general.validation.required"),
},
]}
>
<Select>
{bodyshop.md_classes.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("contracts.labels.convertform.applycleanupcharge")}
rules={[

View File

@@ -1,6 +1,7 @@
import { Form, Input, InputNumber } from "antd";
import { Form, Input, InputNumber, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
@@ -19,9 +20,7 @@ export default function ContractFormComponent({
const { t } = useTranslation();
return (
<div>
<div className="imex-flex-row__grow imex-flex-row__margin-large">
<FormFieldsChanged form={form} />
</div>
<FormFieldsChanged form={form} />
<LayoutFormRow>
{create ? null : (
<Form.Item
@@ -65,7 +64,6 @@ export default function ContractFormComponent({
</Form.Item>
)}
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("contracts.fields.kmstart")}
@@ -104,14 +102,17 @@ export default function ContractFormComponent({
</Form.Item>
)}
</LayoutFormRow>
{selectedJobState && (
<div>
<ContractFormJobPrefill
jobId={selectedJobState && selectedJobState[0]}
form={form}
/>
</div>
)}
<Space wrap>
{selectedJobState && (
<div>
<ContractFormJobPrefill
jobId={selectedJobState && selectedJobState[0]}
form={form}
/>
</div>
)}
<ContractLicenseDecodeButton form={form} />
</Space>
<LayoutFormRow>
<Form.Item
label={t("contracts.fields.driver_dlnumber")}
@@ -221,7 +222,6 @@ export default function ContractFormComponent({
<FormDatePicker />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item label={t("contracts.fields.dailyrate")} name="dailyrate">
<InputNumber precision={2} />

View File

@@ -1,7 +1,8 @@
import { Table, Input } from "antd";
import React, { useState } from "react";
import { Input, Table } from "antd";
import React, { useState, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
export default function ContractsJobsComponent({
loading,
@@ -158,6 +159,15 @@ export default function ContractsJobsComponent({
.includes(state.search.toLowerCase())
);
const defaultCurrent = useMemo(() => {
return (
Math.round(
((filteredData.findIndex((v) => v.id === selectedJob) || 0) + 1) / 10
) + 1
);
}, [filteredData, selectedJob]);
if (loading) return <LoadingSkeleton />;
return (
<Table
loading={loading}
@@ -169,8 +179,11 @@ export default function ContractsJobsComponent({
/>
)}
size="small"
pagination={{ position: "top" }}
columns={columns.map((item) => ({ ...item }))}
pagination={{
position: "top",
defaultCurrent: defaultCurrent,
}}
columns={columns}
rowKey="id"
dataSource={filteredData}
onChange={handleTableChange}
@@ -179,6 +192,13 @@ export default function ContractsJobsComponent({
type: "radio",
selectedRowKeys: [selectedJob],
}}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleSelect(record);
},
};
}}
/>
);
}

View File

@@ -1,23 +1,16 @@
import { Slider } from "antd";
import React, { useEffect, useState, forwardRef } from "react";
import React, { forwardRef } from "react";
import { useTranslation } from "react-i18next";
const CourtesyCarFuelComponent = ({ value = 100, onChange }, ref) => {
const [option, setOption] = useState(value);
const CourtesyCarFuelComponent = (props, ref) => {
const { t } = useTranslation();
useEffect(() => {
if (value !== option && onChange) {
onChange(option);
}
}, [value, option, onChange]);
const marks = {
0: {
style: {
color: "#f50",
},
label: t("courtesycars.labels.fuel.empty"),
label: <strong>{t("courtesycars.labels.fuel.empty")}</strong>,
},
13: t("courtesycars.labels.fuel.18"),
25: t("courtesycars.labels.fuel.14"),
@@ -34,14 +27,6 @@ const CourtesyCarFuelComponent = ({ value = 100, onChange }, ref) => {
},
};
return (
<Slider
ref={ref}
marks={marks}
defaultValue={value}
onChange={setOption}
step={null}
/>
);
return <Slider ref={ref} marks={marks} step={null} {...props} />;
};
export default forwardRef(CourtesyCarFuelComponent);

View File

@@ -13,7 +13,10 @@ export default function FormsFieldChanged({ form }) {
const loc = useLocation();
return (
<Form.Item shouldUpdate style={{ margin: 0, padding: 0 }}>
<Form.Item
shouldUpdate
//style={{ margin: 0, padding: 0 }}
>
{() => {
if (form.isFieldsTouched())
return (

View File

@@ -1,5 +1,9 @@
import { List } from "antd";
import { WarningFilled, EyeInvisibleFilled } from "@ant-design/icons";
import {
WarningFilled,
EyeInvisibleFilled,
AuditOutlined,
} from "@ant-design/icons";
import React from "react";
import { useTranslation } from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
@@ -31,6 +35,7 @@ export default function JobDetailCardsNotesComponent({ loading, data }) {
<EyeInvisibleFilled style={{ margin: 4, color: "red" }} />
) : null}
{item.private ? <WarningFilled style={{ margin: 4 }} /> : null}
{item.audit ? <AuditOutlined style={{ margin: 4 }} /> : null}
{item.text}
</List.Item>
)}

View File

@@ -122,6 +122,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
notes: {
data: {
created_by: currentUser.email,
audit: true,
text: t("jobs.labels.importnote", {
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),
@@ -279,6 +280,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser }) {
{
jobid: selectedJob,
created_by: currentUser.email,
audit: true,
text: t("jobs.labels.supplementnote", {
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),

View File

@@ -1,22 +1,28 @@
import { DownCircleFilled } from "@ant-design/icons";
import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
import moment from "moment";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { DELETE_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
@@ -35,6 +41,7 @@ const mapDispatchToProps = (dispatch) => ({
export function JobsDetailHeaderActions({
job,
bodyshop,
currentUser,
refetch,
setScheduleContext,
setBillEnterContext,
@@ -48,6 +55,7 @@ export function JobsDetailHeaderActions({
const history = useHistory();
const [deleteJob] = useMutation(DELETE_JOB);
const [updateJob] = useMutation(UPDATE_JOB);
const [voidJob] = useMutation(VOID_JOB);
const jobInProduction = useMemo(() => {
return bodyshop.md_ro_statuses.production_statuses.includes(job.status);
}, [job, bodyshop.md_ro_statuses.production_statuses]);
@@ -338,13 +346,24 @@ export function JobsDetailHeaderActions({
onClick={(e) => e.stopPropagation()}
onConfirm={async () => {
//delete the job.
const result = await updateJob({
const result = await voidJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_void,
voided: true,
},
note: [
{
jobid: job.id,
created_by: currentUser.email,
audit: true,
text: t("jobs.labels.voidnote", {
date: moment().format("MM/DD/yyy"),
time: moment().format("hh:mm a"),
}),
},
],
},
});

View File

@@ -1,4 +1,5 @@
import {
AuditOutlined,
DeleteFilled,
EditFilled,
EyeInvisibleFilled,
@@ -8,16 +9,23 @@ import { Button, Table } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import NoteUpsertModal from "../note-upsert-modal/note-upsert-modal.container";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
setNoteUpsertContext: (context) =>
dispatch(setModalContext({ context: context, modal: "noteUpsert" })),
});
export function JobNotesComponent({
jobRO,
loading,
data,
refetch,
@@ -40,6 +48,7 @@ export function JobNotesComponent({
<WarningFilled style={{ margin: 4, color: "red" }} />
) : null}
{record.private ? <EyeInvisibleFilled style={{ margin: 4 }} /> : null}
{record.audit ? <AuditOutlined style={{ margin: 4 }} /> : null}
</span>
),
},
@@ -76,11 +85,13 @@ export function JobNotesComponent({
<span>
<Button
loading={deleteLoading}
disabled={record.audit || jobRO}
onClick={() => handleNoteDelete(record.id)}
>
<DeleteFilled />
</Button>
<Button
disabled={record.audit || jobRO}
onClick={() => {
setNoteUpsertContext({
actions: { refetch: refetch },
@@ -123,4 +134,4 @@ export function JobNotesComponent({
</div>
);
}
export default connect(null, mapDispatchToProps)(JobNotesComponent);
export default connect(mapStateToProps, mapDispatchToProps)(JobNotesComponent);

View File

@@ -9,6 +9,7 @@ export default function ScheduleDayViewComponent({ data, day }) {
<ScheduleCalendarWrapperComponent
events={data}
defaultView="day"
view={"day"}
views={["day"]}
date={day}
/>

View File

@@ -15,7 +15,7 @@ export default function ScheduleDayViewContainer({ day }) {
fetchPolicy: "network-only",
});
const { t } = useTranslation();
if (!!!day) return <div>{t("appointments.labels.nodateselected")}</div>;
if (!day) return <div>{t("appointments.labels.nodateselected")}</div>;
if (loading) return <LoadingSkeleton paragraph={{ rows: 4 }} />;
if (error) return <div>{error.message}</div>;
let normalizedData;

View File

@@ -756,6 +756,27 @@ export const UPDATE_JOB = gql`
}
`;
export const VOID_JOB = gql`
mutation VOID_JOB(
$jobId: uuid!
$job: jobs_set_input!
$note: [notes_insert_input!]!
) {
update_jobs_by_pk(_set: $job, pk_columns: { id: $jobId }) {
id
date_exported
status
alt_transport
ro_number
production_vars
lbr_adjustments
}
insert_notes(objects: $note) {
affected_rows
}
}
`;
export const UPDATE_JOBS = gql`
mutation UPDATE_JOBS($jobIds: [uuid!]!, $fields: jobs_set_input!) {
update_jobs(where: { id: { _in: $jobIds } }, _set: $fields) {

View File

@@ -23,6 +23,7 @@ export const QUERY_NOTES_BY_JOB_PK = gql`
private
text
updated_at
audit
}
}
}

View File

@@ -20,8 +20,8 @@ import "./utils/CleanAxios";
import "antd/dist/antd.less";
require("dotenv").config();
Dinero.defaultCurrency = "CAD";
Dinero.globalLocale = "en-CA";
// Dinero.defaultCurrency = "CAD";
// Dinero.globalLocale = "en-CA";
Dinero.globalRoundingMode = "HALF_UP";
if (process.env.NODE_ENV !== "development") {

View File

@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import ContractCarsContainer from "../../components/contract-cars/contract-cars.container";
import ContractFormComponent from "../../components/contract-form/contract-form.component";
import ContractJobsContainer from "../../components/contract-jobs/contract-jobs.container";
import ContractLicenseDecodeButton from "../../components/contract-license-decode-button/contract-license-decode-button.component";
export default function ContractCreatePageComponent({
form,
selectedJobState,
@@ -14,7 +13,12 @@ export default function ContractCreatePageComponent({
const { t } = useTranslation();
const CreateButton = (
<Button type="primary" onClick={() => form.submit()} loading={loading}>
<Button
disabled={!selectedJobState[0] || !selectedCarState[0]}
type="primary"
onClick={() => form.submit()}
loading={loading}
>
{t("general.actions.create")}
</Button>
);
@@ -24,12 +28,18 @@ export default function ContractCreatePageComponent({
{CreateButton}
<ContractJobsContainer selectedJobState={selectedJobState} />
<ContractCarsContainer selectedCarState={selectedCarState} form={form} />
<ContractLicenseDecodeButton form={form} />
<ContractFormComponent
create
form={form}
selectedJobState={selectedJobState}
/>
<div
style={{
display: selectedJobState[0] && selectedCarState[0] ? "" : "none",
}}
>
<ContractFormComponent
create
form={form}
selectedJobState={selectedJobState}
/>
</div>
{CreateButton}
</div>
);

View File

@@ -100,7 +100,6 @@ export function ContractCreatePageContainer({
layout="vertical"
autoComplete="no"
onFinish={handleFinish}
initialValues={{ fuelout: 100 }}
>
<ContractCreatePageComponent
loading={loading}

View File

@@ -1238,7 +1238,8 @@
"vehicle_info": "Vehicle",
"vehicleassociation": "Vehicle Association",
"viewallocations": "View Allocations",
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. "
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. ",
"voidnote": "This repair order was voided on {{date}} at {{time}}."
},
"successes": {
"addedtoproduction": "Job added to production board.",

View File

@@ -1238,7 +1238,8 @@
"vehicle_info": "Vehículo",
"vehicleassociation": "",
"viewallocations": "",
"voidjob": ""
"voidjob": "",
"voidnote": ""
},
"successes": {
"addedtoproduction": "",

View File

@@ -1238,7 +1238,8 @@
"vehicle_info": "Véhicule",
"vehicleassociation": "",
"viewallocations": "",
"voidjob": ""
"voidjob": "",
"voidnote": ""
},
"successes": {
"addedtoproduction": "",

View File

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

View File

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

View File

@@ -0,0 +1,33 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_insert_permission
- args:
permission:
check:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- jobid
- text
- created_by
- critical
- private
set: {}
role: user
table:
name: notes
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,34 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_insert_permission
- args:
permission:
check:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- audit
- created_at
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
set: {}
role: user
table:
name: notes
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,34 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- critical
- private
- created_by
- text
- created_at
- updated_at
- id
- jobid
computed_fields: []
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: notes
schema: public
type: create_select_permission

View File

@@ -0,0 +1,35 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- audit
- created_at
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
computed_fields: []
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: notes
schema: public
type: create_select_permission

View File

@@ -0,0 +1,33 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_update_permission
- args:
permission:
columns:
- critical
- private
- created_by
- text
- created_at
- updated_at
- id
- jobid
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: notes
schema: public
type: create_update_permission

View File

@@ -0,0 +1,34 @@
- args:
role: user
table:
name: notes
schema: public
type: drop_update_permission
- args:
permission:
columns:
- audit
- created_at
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: notes
schema: public
type: create_update_permission

View File

@@ -3129,26 +3129,28 @@ tables:
- active:
_eq: true
columns:
- id
- audit
- created_at
- updated_at
- jobid
- text
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
select_permissions:
- role: user
permission:
columns:
- critical
- private
- created_by
- text
- audit
- created_at
- updated_at
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
filter:
job:
bodyshop:
@@ -3163,14 +3165,15 @@ tables:
- role: user
permission:
columns:
- critical
- private
- created_by
- text
- audit
- created_at
- updated_at
- created_by
- critical
- id
- jobid
- private
- text
- updated_at
filter:
job:
bodyshop:

8190
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,6 @@
"firebase-admin": "^9.5.0",
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"handlebars": "^4.7.7",
"inline-css": "^3.0.0",
"intuit-oauth": "^3.0.2",
"lodash": "^4.17.21",

View File

@@ -2,8 +2,8 @@ const Dinero = require("dinero.js");
const queries = require("../graphql-client/queries");
const GraphQLClient = require("graphql-request").GraphQLClient;
Dinero.defaultCurrency = "USD";
Dinero.globalLocale = "en-CA";
// Dinero.defaultCurrency = "USD";
// Dinero.globalLocale = "en-CA";
Dinero.globalRoundingMode = "HALF_UP";
exports.totalsSsu = async function (req, res) {
@@ -173,9 +173,9 @@ function CalculateRatesTotals(ratesList, shoprates) {
let subtotal = Dinero({ amount: 0 });
let rates_subtotal = Dinero({ amount: 0 });
for (const property in ret) {
ret[property].total = Dinero({ amount: ret[property].rate * 100 }).multiply(
ret[property].hours
);
ret[property].total = Dinero({
amount: Math.round((ret[property].rate || 0) * 100),
}).multiply(ret[property].hours);
subtotal = subtotal.add(ret[property].total);
if (property !== "mapa" && property !== "mash")
rates_subtotal = rates_subtotal.add(ret[property].total);

View File

@@ -1,213 +1,212 @@
const path = require("path");
const moment = require("moment");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
var _ = require("lodash");
const Handlebars = require("handlebars");
const phone = require("phone");
var Dinero = require("dinero.js");
Dinero.defaultCurrency = "CAD";
Dinero.globalLocale = "en-CA";
// const path = require("path");
// const moment = require("moment");
// require("dotenv").config({
// path: path.resolve(
// process.cwd(),
// `.env.${process.env.NODE_ENV || "development"}`
// ),
// });
// var _ = require("lodash");
// const Handlebars = require("handlebars");
// const phone = require("phone");
// var Dinero = require("dinero.js");
// Dinero.defaultCurrency = "CAD";
// Dinero.globalLocale = "en-CA";
//Usage: {{moment appointments_by_pk.start format="dddd, DD MMMM YYYY"}}
// //Usage: {{moment appointments_by_pk.start format="dddd, DD MMMM YYYY"}}
Handlebars.registerHelper("round", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = undefined;
}
try {
return context.toFixed(2);
} catch {
return context;
}
});
// Handlebars.registerHelper("round", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = undefined;
// }
// try {
// return context.toFixed(2);
// } catch {
// return context;
// }
// });
Handlebars.registerHelper("dinerof", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = undefined;
}
var amount = Dinero(context);
if (context) {
return amount.toFormat();
}
return "";
});
// Handlebars.registerHelper("dinerof", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = undefined;
// }
// var amount = Dinero(context);
// if (context) {
// return amount.toFormat();
// }
// return "";
// });
Handlebars.registerHelper("phonef", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = undefined;
}
var ph = phone(context)[0];
if (context) {
return ph;
}
return "";
});
// Handlebars.registerHelper("phonef", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = undefined;
// }
// var ph = phone(context)[0];
// if (context) {
// return ph;
// }
// return "";
// });
Handlebars.registerHelper("partType", function (context, block) {
if (!context) return "";
// Handlebars.registerHelper("partType", function (context, block) {
// if (!context) return "";
switch (context.toUpperCase()) {
case "PAA":
return "Aftermarket";
case "PAE":
return "Existing";
case "PAN":
return "OEM";
case "PAO":
return "Other";
case "PAS":
return "Sublet";
case "PASL":
return "Sublet";
case "PAL":
return "LKQ";
case "PAM":
return "Remanufactured";
case "PAC":
return "Chrome";
case "PAP":
return "OEM Partial";
case "PAR":
return "Record";
default:
return context;
}
});
// switch (context.toUpperCase()) {
// case "PAA":
// return "Aftermarket";
// case "PAE":
// return "Existing";
// case "PAN":
// return "OEM";
// case "PAO":
// return "Other";
// case "PAS":
// return "Sublet";
// case "PASL":
// return "Sublet";
// case "PAL":
// return "LKQ";
// case "PAM":
// return "Remanufactured";
// case "PAC":
// return "Chrome";
// case "PAP":
// return "OEM Partial";
// case "PAR":
// return "Record";
// default:
// return context;
// }
// });
Handlebars.registerHelper("lbrType", function (context, block) {
if (!context) return "";
// Handlebars.registerHelper("lbrType", function (context, block) {
// if (!context) return "";
switch (context.toUpperCase()) {
case "LAA":
return "Aluminum";
case "LAB":
return "Body";
case "LAD":
return "Diagnostic";
case "LAF":
return "Frame";
case "LAG":
return "Glass";
case "LAM":
return "Mechanical";
case "LAR":
return "Refinish";
case "LAS":
return "Structural";
case "LAU":
return "Detail";
// switch (context.toUpperCase()) {
// case "LAA":
// return "Aluminum";
// case "LAB":
// return "Body";
// case "LAD":
// return "Diagnostic";
// case "LAF":
// return "Frame";
// case "LAG":
// return "Glass";
// case "LAM":
// return "Mechanical";
// case "LAR":
// return "Refinish";
// case "LAS":
// return "Structural";
// case "LAU":
// return "Detail";
default:
return context;
}
});
// default:
// return context;
// }
// });
Handlebars.registerHelper("objectKeys", function (obj, block) {
var accum = "";
// Handlebars.registerHelper("objectKeys", function (obj, block) {
// var accum = "";
obj &&
Object.keys(obj).map((key) => {
accum += block.fn({ key, value: obj[key] });
});
return accum;
});
// obj &&
// Object.keys(obj).map((key) => {
// accum += block.fn({ key, value: obj[key] });
// });
// return accum;
// });
Handlebars.registerHelper("dinero", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = undefined;
}
var amount = Dinero({
amount: Math.round((context || 0) * 100),
currency: "CAD",
});
return amount.toFormat();
});
// Handlebars.registerHelper("dinero", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = undefined;
// }
// var amount = Dinero({
// amount: Math.round((context || 0) * 100),
// currency: "CAD",
// });
// return amount.toFormat();
// });
Handlebars.registerHelper("moment", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = undefined;
}
// Handlebars.registerHelper("moment", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = undefined;
// }
if (!!!context) return "";
// if (!!!context) return "";
var date = moment(context);
// var date = moment(context);
if (block.hash.timezone) {
date.tz(block.hash.timezone);
}
// if (block.hash.timezone) {
// date.tz(block.hash.timezone);
// }
var hasFormat = false;
// var hasFormat = false;
// Reset the language back to default before doing anything else
date.locale("en");
// // Reset the language back to default before doing anything else
// date.locale("en");
for (var i in block.hash) {
if (i === "format") {
hasFormat = true;
} else if (date[i]) {
date = date[i](block.hash[i]);
} else {
console.log('moment.js does not support "' + i + '"');
}
}
// for (var i in block.hash) {
// if (i === "format") {
// hasFormat = true;
// } else if (date[i]) {
// date = date[i](block.hash[i]);
// } else {
// console.log('moment.js does not support "' + i + '"');
// }
// }
if (hasFormat) {
date = date.format(block.hash.format);
}
return date;
});
// if (hasFormat) {
// date = date.format(block.hash.format);
// }
// return date;
// });
Handlebars.registerHelper("duration", function (context, block) {
if (context && context.hash) {
block = _.cloneDeep(context);
context = 0;
}
var duration = moment.duration(context);
var hasFormat = false;
// Handlebars.registerHelper("duration", function (context, block) {
// if (context && context.hash) {
// block = _.cloneDeep(context);
// context = 0;
// }
// var duration = moment.duration(context);
// var hasFormat = false;
// Reset the language back to default before doing anything else
duration = duration.lang("en");
// // Reset the language back to default before doing anything else
// duration = duration.lang("en");
for (var i in block.hash) {
if (i === "format") {
hasFormat = true;
} else if (duration[i]) {
duration = duration[i](block.hash[i]);
} else {
console.log('moment.js duration does not support "' + i + '"');
}
}
// for (var i in block.hash) {
// if (i === "format") {
// hasFormat = true;
// } else if (duration[i]) {
// duration = duration[i](block.hash[i]);
// } else {
// console.log('moment.js duration does not support "' + i + '"');
// }
// }
if (hasFormat) {
duration = duration.format(block.hash.format);
}
return duration;
});
// if (hasFormat) {
// duration = duration.format(block.hash.format);
// }
// return duration;
// });
exports.render = (req, res) => {
//Perform request validation
let view;
console.log("[HJS Render] New Render Request.");
//console.log("[HJS Render] Context", req.body.context);
if (req.body.context.bodyshop.template_header) {
console.log("[HJS Render] Including Header");
//view = req.body.view;
view = `${req.body.context.bodyshop.template_header}${req.body.view}`;
} else {
console.log("[HJS Render] No header to include.");
view = req.body.view;
}
var template = Handlebars.compile(view);
res.send(template(req.body.context));
// //Perform request validation
// let view;
// console.log("[HJS Render] New Render Request.");
// //console.log("[HJS Render] Context", req.body.context);
// if (req.body.context.bodyshop.template_header) {
// console.log("[HJS Render] Including Header");
// //view = req.body.view;
// view = `${req.body.context.bodyshop.template_header}${req.body.view}`;
// } else {
// console.log("[HJS Render] No header to include.");
// view = req.body.view;
// }
// var template = Handlebars.compile(view);
// res.send(template(req.body.context));
};