Merged in release/2021-11-12 (pull request #260)

release/2021-11-12

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2021-11-10 01:06:57 +00:00
33 changed files with 2082 additions and 1169 deletions

View File

@@ -8103,6 +8103,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>filehandlers</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>insurancecos</name>
<definition_loaded>false</definition_loaded>
@@ -17491,6 +17512,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>changefilehandler</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>changelaborrate</name>
<definition_loaded>false</definition_loaded>
@@ -30627,6 +30669,27 @@
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>associatedbills</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>backordering</name>
<definition_loaded>false</definition_loaded>
@@ -32874,6 +32937,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ab_proof_of_loss</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>appointment_confirmation</name>
<definition_loaded>false</definition_loaded>
@@ -36465,6 +36549,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>open_orders_status</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>parts_backorder</name>
<definition_loaded>false</definition_loaded>

View File

@@ -2,40 +2,40 @@
"name": "bodyshop",
"version": "0.1.1",
"private": true,
"proxy": "http://localhost:5000",
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.4.16",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@lourenci/react-kanban": "^2.1.0",
"@openreplay/tracker": "^3.4.4",
"@openreplay/tracker": "^3.4.6",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@sentry/react": "^6.14.1",
"@sentry/tracing": "^6.14.1",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.20.2",
"@tanem/react-nprogress": "^3.0.81",
"@stripe/stripe-js": "^1.21.1",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.16.13",
"apollo-link-logger": "^2.0.0",
"axios": "^0.23.0",
"axios": "^0.24.0",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.1.3",
"graphql": "^15.6.1",
"i18next": "^21.3.3",
"firebase": "^9.3.0",
"graphql": "^16.0.1",
"i18next": "^21.4.1",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.6",
"jsoneditor": "^9.5.7",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.38",
"libphonenumber-js": "^1.9.42",
"logrocket": "^2.1.1",
"markerjs2": "^2.15.0",
"markerjs2": "^2.16.2",
"moment-business-days": "^1.2.0",
"phone": "^3.1.8",
"preval.macro": "^5.0.0",
@@ -51,25 +51,25 @@
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.12.0",
"react-i18next": "^11.13.0",
"react-icons": "^4.3.1",
"react-number-format": "^4.7.3",
"react-redux": "^7.2.5",
"react-redux": "^7.2.6",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0",
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.5",
"redux": "^4.1.1",
"recharts": "^2.1.6",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.43.3",
"reselect": "^4.1.2",
"sass": "^1.43.4",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.9.18",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",

View File

@@ -119,33 +119,35 @@ export function DmsPostForm({ bodyshop, socket, job }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
{bodyshop.cdk_dealerid && (
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</LayoutFormRow>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</LayoutFormRow>
)}
<Form.Item
name="story"
label={t("jobs.fields.dms.story")}
@@ -157,6 +159,7 @@ export function DmsPostForm({ bodyshop, socket, job }) {
>
<Input.TextArea maxLength={240} />
</Form.Item>
<Divider />
<Form.List name={["payers"]}>
{(fields, { add, remove }) => {

View File

@@ -46,12 +46,16 @@ import {
} from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { signOutStart } from "../../redux/user/user.actions";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
recentItems: selectRecentItems,
selectedHeader: selectSelectedHeader,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -69,6 +73,7 @@ const mapDispatchToProps = (dispatch) => ({
function Header({
handleMenuClick,
currentUser,
bodyshop,
selectedHeader,
signOutStart,
setBillEnterContext,
@@ -77,6 +82,7 @@ function Header({
setReportCenterContext,
recentItems,
}) {
console.log("🚀 ~ file: header.component.jsx ~ line 85 ~ bodyshop", bodyshop);
const { t } = useTranslation();
return (
@@ -237,16 +243,26 @@ function Header({
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
)}
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
)}
<Menu.Item key="export-logs">
<Link to="/manage/accounting/exportlogs">
{t("menus.header.export-logs")}

View File

@@ -73,7 +73,7 @@ export const reconcileByPrice = (
jobLines.forEach((jl) => {
const matchingBillLineIds = billLines
.filter((bl) => bl.actual_price === jl.act_price && !jl.removed)
.filter((bl) => bl.actual_price === jl.act_price && bl.quantity === jl.part_qty && !jl.removed)
.map((bl) => bl.id);
if (matchingBillLineIds.length > 1) {

View File

@@ -61,7 +61,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
);
};
const overlay = bodyshop.cdk_dealerid && (
const overlay = (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Menu onClick={handleMenuClick}>
{bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => (
<Menu.Item key={mapping.name}>{mapping.name}</Menu.Item>
@@ -69,7 +69,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
</Menu>
);
return bodyshop.cdk_dealerid ? (
return bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
<Dropdown overlay={overlay}>
<Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button>
</Dropdown>

View File

@@ -1,7 +1,6 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -11,8 +10,6 @@ const mapStateToProps = createStructuredSelector({
});
export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const est = item.props.value;
form.setFieldsValue(est);
@@ -37,7 +34,7 @@ export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
href=" #"
onClick={(e) => e.preventDefault()}
>
{t("jobs.actions.changestimator")} <DownOutlined />
<DownOutlined />
</a>
</Dropdown>
);

View File

@@ -0,0 +1,43 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, Menu } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsDetailChangeFilehandler({ disabled, form, bodyshop }) {
const handleClick = ({ item, key, keyPath }) => {
const est = item.props.value;
form.setFieldsValue(est);
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.md_filehandlers.map((est, idx) => (
<Menu.Item value={est} key={idx}>
{`${est.ins_ct_fn} ${est.ins_ct_ln}`}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<Dropdown overlay={menu} disabled={disabled}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
<DownOutlined />
</a>
</Dropdown>
);
}
export default connect(mapStateToProps, null)(JobsDetailChangeFilehandler);

View File

@@ -6,6 +6,7 @@ import {
InputNumber,
Row,
Select,
Space,
Switch,
} from "antd";
import React from "react";
@@ -23,7 +24,7 @@ import FormItemPhone, {
import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
@@ -85,7 +86,15 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
<Form.Item
label={
<Space>
{t("jobs.fields.ins_ct_ln")}
<JobsDetailChangeFileHandler form={form} disabled={jobRO} />
</Space>
}
name="ins_ct_ln"
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
@@ -135,7 +144,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
label={t("jobs.fields.referral_source_extra")}
name="referral_source_extra"
>
<Input disabled={jobRO}/>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.alt_transport")} name="alt_transport">
<Select disabled={jobRO} allowClear>
@@ -219,12 +228,19 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
{t("jobs.forms.appraiserinfo")}
</Divider>
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
<FormRow noDivider>
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.est_ct_fn")} name="est_ct_fn">
<Form.Item
label={
<Space>
{t("jobs.fields.est_ct_fn")}
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
</Space>
}
name="est_ct_fn"
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">

View File

@@ -119,7 +119,7 @@ export function PartsOrderListTableComponent({
</Button>
<Popconfirm
title={t("parts_orders.labels.confirmdelete")}
disabled={jobRO || !record.return}
disabled={jobRO}
onConfirm={async () => {
//Delete the parts return.!
@@ -139,7 +139,7 @@ export function PartsOrderListTableComponent({
});
}}
>
<Button disabled={jobRO || !record.return}>
<Button disabled={jobRO}>
<DeleteFilled />
</Button>
</Popconfirm>

View File

@@ -1,4 +1,4 @@
import { Input, PageHeader, Space, Spin } from "antd";
import { Input, Space, Spin } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -26,8 +26,7 @@ export function ProductionBoardFilters({
const { t } = useTranslation();
return (
<PageHeader
extra={
<Space wrap>
{loading && <Spin />}
<Input.Search
@@ -46,7 +45,6 @@ export function ProductionBoardFilters({
allowClear
/>
</Space>
}
></PageHeader>
);
}

View File

@@ -1,7 +1,7 @@
import { useApolloClient } from "@apollo/client";
import Board, { moveCard } from "@lourenci/react-kanban";
import "@lourenci/react-kanban/dist/styles.css";
import { notification } from "antd";
import { notification, PageHeader, Space, Statistic } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -130,15 +130,40 @@ export function ProductionBoardKanbanComponent({
});
}
};
const totalHrs = data
.reduce(
(acc, val) =>
acc +
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
return (
<div>
<IndefiniteLoading loading={isMoving} />
<ProductionBoardFilters
filter={filter}
setFilter={setFilter}
loading={isMoving}
<PageHeader
title={
<Space>
<Statistic
title={t("dashboard.titles.productionhours")}
value={totalHrs}
/>
<Statistic
title={t("appointments.labels.inproduction")}
value={data && data.length}
/>
</Space>
}
extra={
<ProductionBoardFilters
filter={filter}
setFilter={setFilter}
loading={isMoving}
/>
}
/>
<Board
children={boardLanes}
disableCardDrag={isMoving}

View File

@@ -182,10 +182,16 @@ export function ProductionListTable({
<div>
<PageHeader
title={
<Statistic
title={t("dashboard.titles.productionhours")}
value={totalHrs}
/>
<Space>
<Statistic
title={t("dashboard.titles.productionhours")}
value={totalHrs}
/>
<Statistic
title={t("appointments.labels.inproduction")}
value={dataSource && dataSource.length}
/>
</Space>
}
extra={
<Space wrap>

View File

@@ -848,6 +848,88 @@ export default function ShopInfoGeneral({ form }) {
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.filehandlers")}>
<Form.List name={["md_filehandlers"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("jobs.fields.ins_ct_fn")}
key={`${index}ins_ct_fn`}
name={[field.name, "ins_ct_fn"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.ins_ct_ln")}
key={`${index}ins_ct_ln`}
name={[field.name, "ins_ct_ln"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.ins_ph1")}
key={`${index}ins_ph1`}
name={[field.name, "ins_ph1"]}
rules={[
({ getFieldValue }) =>
PhoneItemFormatterValidation(getFieldValue, [
field.name,
"ins_ph",
]),
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.ins_ea")}
key={`${index}ins_ea`}
name={[field.name, "ins_ea"]}
rules={[
{
type: "email",
message: "This is not a valid email address.",
},
]}
>
<FormItemEmail
email={form.getFieldValue([field.name, "ins_ea"])}
/>
</Form.Item>
<Space>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")}>
<Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => {

View File

@@ -98,6 +98,7 @@ export const QUERY_BODYSHOP = gql`
md_ded_notes
pbs_configuration
pbs_serialnumber
md_filehandlers
employees {
id
active
@@ -192,6 +193,7 @@ export const UPDATE_SHOP = gql`
md_ded_notes
pbs_configuration
pbs_serialnumber
md_filehandlers
employees {
id
first_name

View File

@@ -2,7 +2,7 @@ import { gql } from "@apollo/client";
export const QUERY_EMPLOYEES = gql`
query QUERY_EMPLOYEES {
employees {
employees(order_by: { employee_number: asc }) {
last_name
id
first_name

View File

@@ -120,7 +120,11 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (!jobId || !bodyshop.cdk_dealerid || !(data && data.jobs_by_pk))
if (
!jobId ||
!(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) ||
!(data && data.jobs_by_pk)
)
return <Result status="404" />;
return (

View File

@@ -208,7 +208,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
{bodyshop.cdk_dealerid && (
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("jobs.fields.kmin")}
name="kmin"
@@ -221,7 +221,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
<InputNumber precision={0} disabled={jobRO} />
</Form.Item>
)}
{bodyshop.cdk_dealerid && (
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("jobs.fields.kmout")}
name="kmout"

View File

@@ -503,6 +503,7 @@
"emaillater": "Email Later",
"employees": "Employees",
"estimators": "Estimators",
"filehandlers": "File Handlers",
"insurancecos": "Insurance Companies",
"intakechecklist": "Intake Checklist",
"jobstatuses": "Job Statuses",
@@ -1091,6 +1092,7 @@
"addtoscoreboard": "Add to Scoreboard",
"allocate": "Allocate",
"autoallocate": "Auto Allocate",
"changefilehandler": "Change File Handler",
"changelaborrate": "Change Labor Rate",
"changestatus": "Change Status",
"changestimator": "Change Estimator",
@@ -1824,6 +1826,7 @@
"receivebill": "Receive Bill"
},
"errors": {
"associatedbills": "This parts order cannot",
"backordering": "Error backordering part {{message}}.",
"creating": "Error encountered when creating parts order. "
},
@@ -1849,7 +1852,7 @@
},
"labels": {
"allpartsto": "All Parts Location",
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. ",
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
"newpartsorder": "New Parts Order",
@@ -1968,6 +1971,7 @@
"zip": "Postal Code/Zip"
},
"3rdpartypayer": "Invoice to Third Party Payer",
"ab_proof_of_loss": "AB - Proof of Loss",
"appointment_confirmation": "Appointment Confirmation",
"appointment_reminder": "Appointment Reminder",
"casl_authorization": "CASL Authorization",
@@ -2176,6 +2180,7 @@
"open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator",
"open_orders_ins_co": "Open Orders by Insurance Company",
"open_orders_status": "Open Orders by Status",
"parts_backorder": "Backordered Parts",
"parts_not_recieved": "Parts Not Received",
"payments_by_date": "Payments by Date",

View File

@@ -503,6 +503,7 @@
"emaillater": "",
"employees": "",
"estimators": "",
"filehandlers": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1091,6 +1092,7 @@
"addtoscoreboard": "",
"allocate": "",
"autoallocate": "",
"changefilehandler": "",
"changelaborrate": "",
"changestatus": "Cambiar Estado",
"changestimator": "",
@@ -1824,6 +1826,7 @@
"receivebill": ""
},
"errors": {
"associatedbills": "",
"backordering": "",
"creating": "Se encontró un error al crear el pedido de piezas."
},
@@ -1968,6 +1971,7 @@
"zip": ""
},
"3rdpartypayer": "",
"ab_proof_of_loss": "",
"appointment_confirmation": "",
"appointment_reminder": "",
"casl_authorization": "",
@@ -2176,6 +2180,7 @@
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"open_orders_status": "",
"parts_backorder": "",
"parts_not_recieved": "",
"payments_by_date": "",

View File

@@ -503,6 +503,7 @@
"emaillater": "",
"employees": "",
"estimators": "",
"filehandlers": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1091,6 +1092,7 @@
"addtoscoreboard": "",
"allocate": "",
"autoallocate": "",
"changefilehandler": "",
"changelaborrate": "",
"changestatus": "Changer le statut",
"changestimator": "",
@@ -1824,6 +1826,7 @@
"receivebill": ""
},
"errors": {
"associatedbills": "",
"backordering": "",
"creating": "Erreur rencontrée lors de la création de la commande de pièces."
},
@@ -1968,6 +1971,7 @@
"zip": ""
},
"3rdpartypayer": "",
"ab_proof_of_loss": "",
"appointment_confirmation": "",
"appointment_reminder": "",
"casl_authorization": "",
@@ -2176,6 +2180,7 @@
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"open_orders_status": "",
"parts_backorder": "",
"parts_not_recieved": "",
"payments_by_date": "",

View File

@@ -407,6 +407,17 @@ export const TemplateList = (type, context) => {
CA_MB: true,
},
},
ab_proof_of_loss: {
title: i18n.t("printcenter.jobs.ab_proof_of_loss"),
description: "Thank You Letter by RO",
key: "ab_proof_of_loss",
subject: i18n.t("printcenter.jobs.ab_proof_of_loss"),
disabled: false,
group: "pre",
regions: {
CA_AB: true,
},
},
// parts_label_multi: {
// title: i18n.t("printcenter.jobs.parts_label_multi"),
// description: "Thank You Letter by RO",
@@ -1181,6 +1192,19 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
open_orders_status: {
title: i18n.t("reportcenter.templates.open_orders_status"),
description: "",
subject: i18n.t("reportcenter.templates.open_orders_status"),
key: "open_orders_status",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "jobs",
},
open_orders_csr: {
title: i18n.t("reportcenter.templates.open_orders_csr"),
description: "",

File diff suppressed because it is too large Load Diff

View File

@@ -828,6 +828,7 @@
- md_classes
- md_ded_notes
- md_estimators
- md_filehandlers
- md_hour_split
- md_ins_cos
- md_jobline_presets
@@ -906,6 +907,7 @@
- md_classes
- md_ded_notes
- md_estimators
- md_filehandlers
- md_hour_split
- md_ins_cos
- md_jobline_presets

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 "md_filehandlers" jsonb
-- null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_filehandlers" jsonb
null default jsonb_build_array();

View File

@@ -17,7 +17,8 @@
"start": "node server.js"
},
"dependencies": {
"aws-sdk": "^2.1013.0",
"aws-sdk": "^2.1023.0",
"axios": "^0.24.0",
"bluebird": "^3.7.2",
"body-parser": "^1.18.3",
"cloudinary": "^1.27.1",
@@ -43,10 +44,10 @@
"phone": "^3.1.8",
"query-string": "^7.0.1",
"soap": "^0.42.0",
"socket.io": "^4.3.1",
"socket.io": "^4.3.2",
"ssh2-sftp-client": "^7.1.0",
"stripe": "^8.184.0",
"twilio": "^3.70.0",
"stripe": "^8.186.1",
"twilio": "^3.71.1",
"uuid": "^8.3.2",
"xmlbuilder2": "^3.0.2"
},

View File

@@ -5,7 +5,6 @@ require("dotenv").config({
`.env.${process.env.NODE_ENV || "development"}`
),
});
const CdkBase = require("../web-sockets/web-socket");
const IMEX_PBS_USER = process.env.IMEX_PBS_USER,
IMEX_PBS_PASSWORD = process.env.IMEX_PBS_PASSWORD;
@@ -19,3 +18,13 @@ exports.PBS_CREDENTIALS = PBS_CREDENTIALS;
// process.env.NODE_ENV === "production"
// ? "https://3pa.dmotorworks.com"
// : "https://uat-3pa.dmotorworks.com";
const pbsDomain = `https://partnerhub.pbsdealers.com/json/reply`;
exports.PBS_ENDPOINTS = {
AccountGet: `${pbsDomain}/AccountGet`,
ContactGet: `${pbsDomain}/ContactGet`,
VehicleGet: `${pbsDomain}/VehicleGet`,
AccountingPostingChange: `${pbsDomain}/AccountingPostingChange`,
ContactChange: `${pbsDomain}/ContactChange`,
VehicleChange: `${pbsDomain}/VehicleChange`,
};

View File

@@ -6,25 +6,49 @@ require("dotenv").config({
),
});
const GraphQLClient = require("graphql-request").GraphQLClient;
const soap = require("soap");
const axios = require("axios").default;
const queries = require("../../graphql-client/queries");
const CdkBase = require("../../web-sockets/web-socket");
const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
//const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
//const CalcualteAllocations = require("./cdk-calculate-allocations").default;
const CalculateAllocations =
require("../../cdk/cdk-calculate-allocations").default;
const CdkBase = require("../../web-sockets/web-socket");
const moment = require("moment");
exports.default = async function (socket, jobid) {
exports.default = async function (socket, { txEnvelope, jobid }) {
socket.logEvents = [];
socket.recordid = jobid;
socket.txEnvelope = txEnvelope;
try {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Received Job export request for id ${jobid}`
);
const JobData = await QueryJobData(socket, jobid);
socket.JobData = JobData;
//Upsert the contact information as per Wafaa's Email.
CdkBase.createLogEvent(
socket,
"DEBUG",
`Upserting contact information to DMS for ${socket.JobData.ownr_fn} ${socket.JobData.ownr_ln} ${socket.JobData.ownr_co_nm}`
);
const ownerRef = await UpsertContactData(socket);
CdkBase.createLogEvent(
socket,
"DEBUG",
`Upserting vehicle information to DMS for ${socket.JobData.v_vin}`
);
// await UpsertVehicleData(socket, ownerRef.ReferenceId);
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
await InsertAccountPostingData(socket);
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
// await MarkJobExported(socket, socket.JobData.id);
socket.emit("export-success", socket.JobData.id);
} catch (error) {
CdkBase.createLogEvent(
socket,
@@ -33,3 +57,409 @@ exports.default = async function (socket, jobid) {
);
}
};
async function CheckForErrors(socket, response) {
if (response.WasSuccessful) {
return;
} else {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error received from DMS:. ${response.Message}`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`Error received from DMS:. ${JSON.stringify(response)}`
);
}
}
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.QUERY_JOBS_FOR_PBS_EXPORT, { id: jobid });
CdkBase.createLogEvent(
socket,
"TRACE",
`Job data query result ${JSON.stringify(result, null, 2)}`
);
return result.jobs_by_pk;
}
async function UpsertContactData(socket) {
try {
const { data: ContactChangeResponse } = await axios.post(
PBS_ENDPOINTS.ContactChange,
{
ContactInfo: {
// Id: socket.JobData.owner.id,
ContactId: socket.JobData.owner.id,
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
Code: socket.JobData.owner.accountingid,
...(socket.JobData.ownr_co_nm
? {
//LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_co_nm,
IsBusiness: true,
}
: {
LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_fn,
IsBusiness: false,
}),
//Salutation: "String",
//MiddleName: "String",
//ContactName: "String",
IsInactive: false,
//ApartmentNumber: "String",
Address: socket.JobData.ownr_addr1,
City: socket.JobData.ownr_city,
//County: socket.JobData.ownr_addr1,
State: socket.JobData.ownr_st,
ZipCode: socket.JobData.ownr_zip,
//BusinessPhone: "String",
//BusinessPhoneExt: "String",
HomePhone: socket.JobData.ownr_ph2,
CellPhone: socket.JobData.ownr_ph1,
//BusinessPhoneRawReverse: "String",
//HomePhoneRawReverse: "String",
//CellPhoneRawReverse: "String",
//FaxNumber: "String",
EmailAddress: socket.JobData.ownr_ea,
//Notes: "String",
//CriticalMemo: "String",
//BirthDate: "0001-01-01T00:00:00.0000000Z",
// Gender: "String",
// DriverLicense: "String",
//PreferredContactMethods: ["String"],
// LastUpdate: "0001-01-01T00:00:00.0000000Z",
// CustomFields: [{ Key: "String", Value: "String", Type: "String" }],
// FleetType: "String",
// CommunicationPreferences: {
// Email: "String",
// Phone: "String",
// TextMessage: "String",
// Letter: "String",
// Preferred: "String",
// },
// SalesRepRef: "00000000000000000000000000000000",
// Language: "String",
// PayableAccount: "String",
// ReceivableAccount: "String",
// IsStatic: false,
// PrimaryImageRef: "00000000000000000000000000000000",
// PayableAccounts: [{ SerialNumber: "String", Account: "String" }],
// ReceivableAccounts: [{ SerialNumber: "String", Account: "String" }],
// ManufacturerLoyaltyNumber: "String",
},
IsAsynchronous: false,
// UserRequest: "String",
// UserRef: "00000000000000000000000000000000",
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, ContactChangeResponse);
return ContactChangeResponse;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in UpsertContactData - ${error}`
);
throw new Error(error);
}
}
async function UpsertVehicleData(socket, ownerRef) {
try {
const { data: VehicleChangeResponse } = await axios.post(
PBS_ENDPOINTS.VehicleChange,
{
VehicleInfo: {
//Id: "string/00000000-0000-0000-0000-000000000000",
//VehicleId: "00000000000000000000000000000000",
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
//StockNumber: "String",
VIN: socket.JobData.v_vin,
LicenseNumber: socket.JobData.plate_no,
//FleetNumber: "String",
//Status: "String",
OwnerRef: ownerRef, // "00000000000000000000000000000000",
//ModelNumber: "String",
Make: socket.JobData.v_make_desc,
Model: socket.JobData.v_model_desc,
//Trim: "String",
//VehicleType: "String",
Year: socket.JobData.v_model_yr,
Odometer: socket.JobData.kmin,
ExteriorColor: {
Code: socket.JobData.v_color,
//Description: "String",
},
// InteriorColor: { Code: "String", Description: "String" },
//Engine: "String",
// Cylinders: "String",
// Transmission: "String",
// DriveWheel: "String",
// Fuel: "String",
// Weight: 0,
// InServiceDate: "0001-01-01T00:00:00.0000000Z",
// LastServiceDate: "0001-01-01T00:00:00.0000000Z",
// LastServiceMileage: 0,
// Lot: "String",
// LotDescription: "String",
// Category: "String",
// Options: [
// {
// Group: "String",
// Code: "String",
// Description: "String",
// AdditionalInfo: "String",
// Price: 0,
// Cost: 0,
// Residual: 0,
// },
// ],
// Refurbishments: [
// {
// ReferenceNumber: "String",
// Description: "String",
// Price: 0,
// Cost: 0,
// Date: "0001-01-01T00:00:00.0000000Z",
// ApplicationModel: "String",
// },
// ],
// Order: {
// InvoiceNumber: "String",
// Price: 0,
// Status: "String",
// Eta: "String",
// EstimatedCost: 0,
// OrderDate: "String",
// StatusDate: "String",
// IgnitionKeyCode: "String",
// DoorKeyCode: "String",
// Description: "String",
// LocationStatus: "String",
// LocationStatusDate: "0001-01-01T00:00:00.0000000Z",
// },
// MSR: 0,
// BaseMSR: 0,
// Retail: 0,
// DateReceived: "0001-01-01T00:00:00.0000000Z",
// InternetPrice: 0,
// Lotpack: 0,
// Holdback: 0,
// InternetNotes: "String",
// Notes: "String",
// CriticalMemo: "String",
// IsCertified: false,
// LastSaleDate: "0001-01-01T00:00:00.0000000Z",
// LastUpdate: "0001-01-01T00:00:00.0000000Z",
// AppraisedValue: 0,
// Warranties: [
// {
// Type: "String",
// CompanyName: "String",
// CoveragePlan: "String",
// Description: "String",
// Price: 0,
// Cost: 0,
// Term: "String",
// Deductible: 0,
// PolicyNumber: "String",
// StartDate: "String",
// StartMileage: 0,
// ExpirationDate: "String",
// ExpirationMileage: 0,
// },
// ],
// Freight: 0,
// Air: 0,
// Inventory: 0,
// IsInactive: false,
// CustomFields: [{ Key: "String", Value: "String", Type: "String" }],
// FloorPlanCode: "String",
// FloorPlanAmount: 0,
// Insurance: {
// Company: "String",
// Policy: "String",
// ExpiryDate: "0001-01-01T00:00:00.0000000Z",
// AgentName: "String",
// AgentPhoneNumber: "String",
// },
// Body: "String",
// ShortVIN: "String",
// AdditionalDrivers: ["00000000000000000000000000000000"],
// OrderDetails: { Distributor: "String" },
// PrimaryImageRef: "00000000000000000000000000000000",
// Hold: {
// VehicleRef: "00000000000000000000000000000000",
// HoldFrom: "0001-01-01T00:00:00.0000000Z",
// HoldUntil: "0001-01-01T00:00:00.0000000Z",
// UserRef: "00000000000000000000000000000000",
// ContactRef: "00000000000000000000000000000000",
// Comments: "String",
// },
// SeatingCapacity: "String",
// DeliveryDate: "0001-01-01T00:00:00.0000000Z",
// WarrantyExpiry: "0001-01-01T00:00:00.0000000Z",
// IsConditionallySold: false,
// SalesDivision: 0,
// StyleRef: "String",
},
IsAsynchronous: false,
// UserRequest: "String",
// UserRef: "00000000000000000000000000000000",
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, VehicleChangeResponse);
return VehicleChangeResponse;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in UpsertVehicleData - ${error}`
);
throw new Error(error);
}
}
async function InsertAccountPostingData(socket) {
try {
const allocations = await CalculateAllocations(socket, socket.JobData.id);
const wips = [];
allocations.forEach((alloc) => {
//Add the sale item from each allocation.
if (alloc.sale.getAmount() > 0 && !alloc.tax) {
const item = {
Account: alloc.profitCenter.dms_acctnumber,
ControlNumber: socket.JobData.ro_number,
Amount: alloc.sale.multiply(-1).toFormat("0.0"),
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
};
wips.push(item);
}
//Add the cost Item.
if (alloc.cost.getAmount() > 0 && !alloc.tax) {
const item = {
Account: alloc.costCenter.dms_acctnumber,
ControlNumber: socket.JobData.ro_number,
Amount: alloc.cost.toFormat("0.0"),
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
};
wips.push(item);
const itemWip = {
Account: alloc.costCenter.dms_wip_acctnumber,
ControlNumber: socket.JobData.ro_number,
Amount: alloc.cost.multiply(-1).toFormat("0.0"),
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
};
wips.push(itemWip);
//Add to the WIP account.
}
if (alloc.tax) {
if (alloc.sale.getAmount() > 0) {
const item2 = {
Account: alloc.profitCenter.dms_acctnumber,
ControlNumber: socket.JobData.ro_number,
Amount: alloc.sale.multiply(-1).toFormat("0.0"),
//Comment: "String",
//AdditionalInfo: "String",
InvoiceNumber: socket.JobData.ro_number,
InvoiceDate: moment(socket.JobData.date_invoiced).toISOString(),
};
wips.push(item2);
}
}
});
console.log(
JSON.stringify({
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
TransactionDate: moment(socket.JobData.date_invoiced).toISOString(), //"0001-01-01T00:00:00.0000000Z",
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: "ImEX Online",
Lines: wips,
},
})
);
const { data: AccountPostingChange } = await axios.post(
PBS_ENDPOINTS.AccountingPostingChange,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
TransactionDate: moment(socket.JobData.date_invoiced).toISOString(), //"0001-01-01T00:00:00.0000000Z",
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: "ImEX Online",
Lines: wips,
},
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, AccountPostingChange);
return AccountPostingChange;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in InsertAccountPostingData - ${error}`
);
throw new Error(error);
}
}
async function MarkJobExported(socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Marking job as exported for id ${jobid}`
);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_JOB_EXPORTED, {
jobId: jobid,
job: {
status:
socket.JobData.bodyshop.md_ro_statuses.default_exported ||
"Exported*",
date_exported: new Date(),
},
log: {
bodyshopid: socket.JobData.bodyshop.id,
jobid: jobid,
successful: true,
useremail: socket.user.email,
},
});
return result;
}

View File

@@ -76,7 +76,12 @@ exports.default = function ({
{
local: false,
federal: true,
state: (jobline.db_ref === "900511" || jobline.db_ref === "900510") ? true: jobline.tax_part,
state:
jobs_by_pk.state_tax_rate === 0
? false
: jobline.db_ref === "900511" || jobline.db_ref === "900510"
? true
: jobline.tax_part,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -142,7 +147,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -204,7 +209,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -267,7 +272,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -346,7 +351,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -394,7 +399,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -445,7 +450,7 @@ exports.default = function ({
{
local: false,
federal: true,
state: true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true,
},
bodyshop.md_responsibility_centers.sales_tax_codes
);

View File

@@ -126,6 +126,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
towing_payable
storage_payable
adjustment_bottom_line
state_tax_rate
owner {
accountingid
}
@@ -242,6 +243,94 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
}
`;
exports.QUERY_JOBS_FOR_PBS_EXPORT = `
query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
jobs_by_pk(id: $id) {
id
job_totals
date_invoiced
ro_number
clm_total
clm_no
invoice_allocation
ownerid
ownr_ln
ownr_fn
ownr_addr1
ownr_addr2
ownr_ph1
ownr_ph2
ownr_zip
ownr_city
ownr_ctry
ownr_st
ownr_ea
ins_co_nm
job_totals
rate_la1
rate_la2
rate_la3
rate_la4
rate_laa
rate_lab
rate_lad
rate_lae
rate_laf
rate_lag
rate_lam
rate_lar
rate_las
rate_lau
rate_ma2s
rate_ma2t
rate_ma3s
rate_mabl
rate_macs
rate_mahw
rate_mapa
rate_mash
rate_matd
class
ca_bc_pvrt
plate_no
plate_st
v_vin
v_model_yr
v_model_desc
v_make_desc
v_color
ca_customer_gst
bodyshop {
id
md_ro_statuses
md_responsibility_centers
accountingconfig
pbs_serialnumber
pbs_configuration
}
owner {
id
accountingid
}
joblines(where:{removed: {_eq:false}}) {
id
line_desc
part_type
act_price
mod_lb_hrs
mod_lbr_ty
part_qty
op_code_desc
profitcenter_labor
profitcenter_part
db_ref
prt_dsmk_p
}
}
}
`;
exports.QUERY_BILLS_FOR_PAYABLES_EXPORT = `
query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
bills(where: {id: {_in: $bills}}) {

View File

@@ -17,7 +17,7 @@ const CdkCalculateAllocations =
require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
const PbsExportJob = require("../accounting/pbs/pbs-job-export");
const PbsExportJob = require("../accounting/pbs/pbs-job-export").default;
io.use(function (socket, next) {
try {
@@ -99,6 +99,17 @@ io.on("connection", (socket) => {
//END CDK
//PBS
socket.on("pbs-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(
socket,
"TRACE",
`Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
);
callback(allocations);
});
socket.on("pbs-export-job", (jobid) => {
PbsExportJob(socket, jobid);
});

View File

@@ -600,10 +600,10 @@ atob@2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
aws-sdk@^2.1013.0:
version "2.1013.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1013.0.tgz#85babc473b0bc90cc1160eb48baf616ddb86e346"
integrity sha512-TXxkp/meAdofpC15goFpNuur7fvh/mcMRfHJoP1jYzTtD0wcoB4FK16GLcny0uDYgkQgZuiO9QYv3Rq5bhGCqQ==
aws-sdk@^2.1023.0:
version "2.1023.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1023.0.tgz#0de16e4e8878ccec4fcd0146322dcf94fdbe09ba"
integrity sha512-RAI8sUfK+00yL9i3xz5kbM3+t/0mjjnKhKyauXAlJN4seDYtIX5+BqMghpkZwvLBdi6idXIuz+FHWETHZccyuA==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -646,6 +646,13 @@ axios@^0.21.4:
dependencies:
follow-redirects "^1.14.0"
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
dependencies:
follow-redirects "^1.14.4"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -1743,6 +1750,11 @@ follow-redirects@^1.14.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
follow-redirects@^1.14.4:
version "1.14.5"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381"
integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -3541,10 +3553,10 @@ socket.io-parser@~4.0.4:
component-emitter "~1.3.0"
debug "~4.3.1"
socket.io@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.3.1.tgz#c0aa14f3f916a8ab713e83a5bd20c16600245763"
integrity sha512-HC5w5Olv2XZ0XJ4gOLGzzHEuOCfj3G0SmoW3jLHYYh34EVsIr3EkW9h6kgfW+K3TFEcmYy8JcPWe//KUkBp5jA==
socket.io@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.3.2.tgz#85ae0cf5cf18acbce648ac9f48aba66df8cea6bf"
integrity sha512-6S5tV4jcY6dbZ/lLzD6EkvNWI3s81JO6ABP/EpvOlK1NPOcIj3AS4khi6xXw6JlZCASq82HQV4SapfmVMMl2dg==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
@@ -3751,10 +3763,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stripe@^8.184.0:
version "8.184.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.184.0.tgz#ea68470ca6045bb47516c4fea3b775fd6ce15a36"
integrity sha512-ZUdvyX+sizTxXLEbUjgTShrulSWSkMIt7hIKdAkhnajYrHdzVtdmhBJl8sQbR9chMVox3Ig5ohilyeIrvcCE2g==
stripe@^8.186.1:
version "8.186.1"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.186.1.tgz#fb717cfcb8c2e73c91e3873dc286f3e8442bfb65"
integrity sha512-BBByVyqKEBY2GNQDLmfwL1RtfsPmjForD+up7tu+P7KYxxvwEzF1gM2Iv4npfOQevGaBmR70sxIWRwn7utS5pg==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.6.0"
@@ -3918,10 +3930,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
twilio@^3.70.0:
version "3.70.0"
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.70.0.tgz#9e6a96f0c46e8256d74bce918baeb7e678c4c1aa"
integrity sha512-GhohvQfP3aHEwiCb6MWqDJ/KeSyFmFwCQtoSuHEwevE7GCxCq6spK36HlCNg3UyTTZNvfdIhN9Sf1wDWeDIbOg==
twilio@^3.71.1:
version "3.71.1"
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.71.1.tgz#15bbb4b51c75d91cc07a8149378c4af330e543cc"
integrity sha512-P/KFvm33UW15EnpHJKgdTxUa1u6MlR/u+sCVnL4ie2TDRv6t7kX+ieIGQMpH7bP/z7FXkTjEt0lz4M+XJ/XWOg==
dependencies:
axios "^0.21.4"
dayjs "^1.8.29"