Merged in release/2021-12-10 (pull request #292)

Release/2021 12 10
This commit is contained in:
Patrick Fic
2021-12-08 18:29:37 +00:00
42 changed files with 1228 additions and 591 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
@@ -1348,6 +1348,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobioucreated</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>jobmodifylbradj</name>
<definition_loaded>false</definition_loaded>
@@ -2716,6 +2737,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>iouexists</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>local_tax</name>
<definition_loaded>false</definition_loaded>
@@ -17339,6 +17381,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ioucreated</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>new</name>
<definition_loaded>false</definition_loaded>
@@ -17800,6 +17863,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>createiou</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>deliver</name>
<definition_loaded>false</definition_loaded>
@@ -24763,6 +24847,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>createiouwarning</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>creating_new_job</name>
<definition_loaded>false</definition_loaded>
@@ -27098,6 +27203,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ioucreated</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>partsqueue</name>
<definition_loaded>false</definition_loaded>

View File

@@ -4,21 +4,21 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.4.17",
"@apollo/client": "^3.5.5",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.0",
"@craco/craco": "^6.4.2",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@openreplay/tracker": "^3.4.7",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker": "^3.4.10",
"@openreplay/tracker-assist": "^3.4.9",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.14.3",
"@sentry/tracing": "^6.14.3",
"@openreplay/tracker-redux": "^3.4.8",
"@sentry/react": "^6.15.0",
"@sentry/tracing": "^6.15.0",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.21.1",
"@stripe/stripe-js": "^1.21.2",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.16.13",
"antd": "^4.17.2",
"apollo-link-logger": "^2.0.0",
"axios": "^0.24.0",
"craco-less": "^1.20.0",
@@ -27,17 +27,17 @@
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.4.1",
"firebase": "^9.6.0",
"graphql": "^16.0.1",
"i18next": "^21.4.2",
"i18next": "^21.5.4",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.7",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.42",
"logrocket": "^2.1.1",
"markerjs2": "^2.17.0",
"libphonenumber-js": "^1.9.44",
"logrocket": "^2.1.2",
"markerjs2": "^2.17.2",
"moment-business-days": "^1.2.0",
"phone": "^3.1.9",
"phone": "^3.1.10",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.1",
@@ -51,7 +51,7 @@
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.14.2",
"react-i18next": "^11.14.3",
"react-icons": "^4.3.1",
"react-number-format": "^4.8.0",
"react-redux": "^7.2.6",
@@ -65,24 +65,24 @@
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.1.2",
"sass": "^1.43.4",
"socket.io-client": "^4.3.2",
"reselect": "^4.1.5",
"sass": "^1.44.0",
"socket.io-client": "^4.4.0",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
"workbox-core": "^6.3.0",
"workbox-expiration": "^6.3.0",
"workbox-google-analytics": "^6.3.0",
"workbox-navigation-preload": "^6.3.0",
"workbox-precaching": "^6.3.0",
"workbox-range-requests": "^6.3.0",
"workbox-routing": "^6.3.0",
"workbox-strategies": "^6.3.0",
"workbox-streams": "^6.3.0"
"workbox-background-sync": "^6.4.2",
"workbox-broadcast-update": "^6.4.2",
"workbox-cacheable-response": "^6.4.2",
"workbox-core": "^6.4.2",
"workbox-expiration": "^6.4.2",
"workbox-google-analytics": "^6.4.2",
"workbox-navigation-preload": "^6.4.2",
"workbox-precaching": "^6.4.2",
"workbox-range-requests": "^6.4.2",
"workbox-routing": "^6.4.2",
"workbox-strategies": "^6.4.2",
"workbox-streams": "^6.4.2"
},
"scripts": {
"postinstall": "patch-package",

View File

@@ -1,6 +1,8 @@
import { UploadOutlined } from "@ant-design/icons";
import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { MdOpenInNew } from "react-icons/md";
import {
Alert,
Divider,
Form,
Input,
@@ -13,6 +15,7 @@ import {
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -132,6 +135,30 @@ export function BillFormComponent({
/>
</Form.Item>
</LayoutFormRow>
{job &&
job.ious &&
job.ious.length > 0 &&
job.ious.map((iou) => (
<Alert
key={iou.id}
type="warning"
message={
<Space>
{t("bills.labels.iouexists")}
<Link
target="_blank"
rel="noopener noreferrer"
to={`/manage/jobs/${iou.id}?tab=repairdata`}
>
<Space>
{iou.ro_number}
<Icon component={MdOpenInNew} />
</Space>
</Link>
</Space>
}
/>
))}
<LayoutFormRow>
<Form.Item
label={t("bills.fields.invoice_number")}
@@ -337,6 +364,7 @@ export function BillFormComponent({
responsibilityCenters={responsibilityCenters}
disabled={disabled}
/>
<Form.Item
name="upload"
label="Upload"

View File

@@ -97,18 +97,20 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
dataSource={allocationsSummary}
locale={{ emptyText: t("dms.labels.refreshallocations") }}
summary={() => {
const totals = allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
const totals =
allocationsSummary &&
allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
@@ -118,7 +120,7 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals.totalSale.toFormat()}
{totals && totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{

View File

@@ -22,7 +22,9 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsLogEvents);
export function DmsLogEvents({ socket, logs, bodyshop }) {
return (
<Timeline pending reverse={true}>
<Timeline pending
reverse={true}
>
{logs.map((log, idx) => (
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
<Space wrap align="start" style={{}}>

View File

@@ -0,0 +1,95 @@
import { useApolloClient } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory } from "react-router";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_LINES_IOU } from "../../graphql/jobs-lines.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { CreateIouForJob } from "../jobs-detail-header-actions/jobs-detail-header-actions.duplicate.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobCreateIOU);
export function JobCreateIOU({
bodyshop,
currentUser,
jobid,
selectedJobLines,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const client = useApolloClient();
const history = useHistory();
const { IOU_Tracking } = useTreatments(
["IOU_Tracking"],
{},
bodyshop.imexshopid
);
if (IOU_Tracking.treatment !== "on") return null;
const handleCreateIou = async () => {
setLoading(true);
//Query all of the job details to recreate.
const iouId = await CreateIouForJob(
client,
jobid,
{
status: bodyshop.md_ro_statuses.default_open,
bodyshopid: bodyshop.id,
useremail: currentUser.email,
},
selectedJobLines
);
notification.open({
type: "success",
message: t("jobs.successes.ioucreated"),
onClick: () => history.push(`/manage/jobs/${iouId}`),
});
const selectedJobLinesIds = selectedJobLines.map((l) => l.id);
await client.mutate({
mutation: UPDATE_JOB_LINES_IOU,
variables: { ids: selectedJobLinesIds },
update(cache) {
cache.modify({
id: cache.identify(jobid),
fields: {
joblines(existingJobLines, { readField }) {
return existingJobLines.map((a) => {
if (!selectedJobLinesIds.includes(a.id)) return a;
return { ...a, ioucreated: true };
});
},
},
});
},
});
setLoading(false);
};
return (
<Popconfirm
title={t("jobs.labels.createiouwarning")}
onConfirm={handleCreateIou}
>
<Button
loading={loading}
disabled={!selectedJobLines || selectedJobLines.length === 0}
>
{t("jobs.actions.createiou")}
</Button>
</Popconfirm>
);
}

View File

@@ -37,6 +37,7 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -427,6 +428,7 @@ export function JobLinesComponent({
>
{t("joblines.actions.new")}
</Button>
<JobCreateIOU jobid={job.id} selectedJobLines={selectedLines} />
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from "react";
import { Input, notification } from "antd";
import { Input, notification, Space } from "antd";
import { FieldTimeOutlined } from "@ant-design/icons";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { useMutation } from "@apollo/client";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
@@ -59,6 +60,12 @@ export default function JobLineNotePopup({ jobline, disabled }) {
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{jobline.ioucreated && (
<Space>
<FieldTimeOutlined />
{t("joblines.labels.ioucreated")}
</Space>
)}
{jobline.notes}
</div>
);

View File

@@ -2,6 +2,8 @@ import Axios from "axios";
import _ from "lodash";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_JOB, QUERY_JOB_FOR_DUPE } from "../../graphql/jobs.queries";
import moment from "moment";
import i18n from "i18next";
export default async function DuplicateJob(
apolloClient,
@@ -57,3 +59,71 @@ export default async function DuplicateJob(
return;
}
export async function CreateIouForJob(
apolloClient,
jobId,
config,
jobLinesToKeep
) {
logImEXEvent("job_create_iou");
const { status } = config;
//get a list of all fields on the job
const res = await apolloClient.query({
query: QUERY_JOB_FOR_DUPE,
variables: { id: jobId },
});
const { jobs_by_pk } = res.data;
const existingJob = _.cloneDeep(jobs_by_pk);
delete existingJob.__typename;
delete existingJob.id;
delete existingJob.createdat;
delete existingJob.updatedat;
const newJob = {
...existingJob,
converted: true,
status: status,
iouparent: jobId,
date_open: moment(),
audit_trails: {
data: [
{
useremail: config.useremail,
bodyshopid: config.bodyshopid,
operation: i18n.t("audit_trail.messages.jobioucreated"),
},
],
},
};
const selectedJoblinesIds = jobLinesToKeep.map((l) => l.id);
const _tempLines = _.cloneDeep(existingJob.joblines).filter((l) =>
selectedJoblinesIds.includes(l.id)
);
_tempLines.forEach((line) => {
delete line.id;
delete line.__typename;
line.manual_line = true;
});
delete newJob.joblines;
newJob.joblines = { data: _tempLines };
const res2 = await apolloClient.mutate({
mutation: INSERT_NEW_JOB,
variables: { job: [newJob] },
});
Axios.post("/job/totalsssu", {
id: res2.data.insert_jobs.returning[0].id,
});
//insert the new job. call the callback with the returned ID when done.
return res2.data.insert_jobs.returning[0].id;
}

View File

@@ -2,7 +2,8 @@ import { useApolloClient } from "@apollo/client";
import Board, { moveCard } from "@asseinfo/react-kanban";
//import "@asseinfo/react-kanban/dist/styles.css";
import "./production-board-kanban.styles.scss";
import { Grid, notification, PageHeader, Space, Statistic } from "antd";
import { SyncOutlined } from '@ant-design/icons'
import { Grid, notification, Button, PageHeader, Space, Statistic } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -33,6 +34,7 @@ const mapDispatchToProps = (dispatch) => ({
export function ProductionBoardKanbanComponent({
data,
bodyshop,
refetch,
technician,
insertAuditTrail,
associationSettings,
@@ -192,6 +194,9 @@ export function ProductionBoardKanbanComponent({
}
extra={
<Space wrap>
<Button onClick={() => refetch && refetch()}>
<SyncOutlined />
</Button>
<ProductionBoardFilters
filter={filter}
setFilter={setFilter}

View File

@@ -1,8 +1,13 @@
import { useQuery, useSubscription } from "@apollo/client";
import React from "react";
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
import _ from "lodash";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries";
import {
QUERY_EXACT_JOB_IN_PRODUCTION,
QUERY_JOBS_IN_PRODUCTION,
SUBSCRIPTION_JOBS_IN_PRODUCTION,
} from "../../graphql/jobs.queries";
import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries";
import {
selectBodyshop,
@@ -14,13 +19,49 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
const { loading, data } = useSubscription(
SUBSCRIPTION_JOBS_IN_PRODUCTION,
{}
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
pollInterval: 3600000,
});
const client = useApolloClient();
const [joblist, setJoblist] = useState([]);
const { data: updatedJobs } = useSubscription(
SUBSCRIPTION_JOBS_IN_PRODUCTION
);
useEffect(() => {
if (!(data && data.jobs)) return;
setJoblist(
data.jobs.map((j) => {
return { id: j.id, updated_at: j.updated_at };
})
);
}, [data]);
useEffect(() => {
if (!updatedJobs || joblist.length === 0) return;
const jobDiff = _.differenceWith(
updatedJobs.jobs,
joblist,
(a, b) => a.id === b.id && a.updated_at === b.updated_at
);
jobDiff.forEach((job) => {
getUpdatedJobData(job.id);
});
setJoblist(updatedJobs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updatedJobs]);
const getUpdatedJobData = async (jobId) => {
client.query({
query: QUERY_EXACT_JOB_IN_PRODUCTION,
variables: { id: jobId },
});
};
const { loading: associationSettingsLoading, data: associationSettings } =
useQuery(QUERY_KANBAN_SETTINGS, {
variables: { email: currentUser.email },
@@ -30,6 +71,7 @@ export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
<ProductionBoardKanbanComponent
loading={loading || associationSettingsLoading}
data={data ? data.jobs : []}
refetch={refetch}
associationSettings={
associationSettings && associationSettings.associations[0]
? associationSettings.associations[0]

View File

@@ -1,4 +1,7 @@
import { SyncOutlined } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button,
Dropdown,
Input,
Menu,
@@ -24,7 +27,6 @@ import ProductionListSaveConfigButton from "../production-list-save-config-butto
import ProductionListPrint from "./production-list-print.component";
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
import ResizeableTitle from "./production-list-table.resizeable.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -35,6 +37,7 @@ const mapStateToProps = createStructuredSelector({
export function ProductionListTable({
loading,
data,
refetch,
bodyshop,
technician,
currentUser,
@@ -42,6 +45,7 @@ export function ProductionListTable({
const [searchText, setSearchText] = useState("");
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
bodyshop.imexshopid,
]);
const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email
@@ -195,6 +199,9 @@ export function ProductionListTable({
}
extra={
<Space wrap>
<Button onClick={() => refetch && refetch()}>
<SyncOutlined />
</Button>
<ProductionListColumnsAdd
columnState={[columns, setColumns]}
tableState={state}

View File

@@ -1,13 +1,61 @@
import { useSubscription } from "@apollo/client";
import React from "react";
import { SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries";
import { useApolloClient, useQuery, useSubscription } from "@apollo/client";
import React, { useEffect, useState } from "react";
import {
QUERY_EXACT_JOB_IN_PRODUCTION,
QUERY_JOBS_IN_PRODUCTION,
SUBSCRIPTION_JOBS_IN_PRODUCTION,
} from "../../graphql/jobs.queries";
import ProductionListTable from "./production-list-table.component";
import _ from "lodash";
export default function ProductionListTableContainer() {
const { loading, data } = useSubscription(
SUBSCRIPTION_JOBS_IN_PRODUCTION,
{}
const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, {
pollInterval: 3600000,
});
const client = useApolloClient();
const [joblist, setJoblist] = useState([]);
const { data: updatedJobs } = useSubscription(
SUBSCRIPTION_JOBS_IN_PRODUCTION
);
return <ProductionListTable loading={loading} data={data ? data.jobs : []} />;
useEffect(() => {
if (!(data && data.jobs)) return;
setJoblist(
data.jobs.map((j) => {
return { id: j.id, updated_at: j.updated_at };
})
);
}, [data]);
useEffect(() => {
if (!updatedJobs || joblist.length === 0) return;
const jobDiff = _.differenceWith(
updatedJobs.jobs,
joblist,
(a, b) => a.id === b.id && a.updated_at === b.updated_at
);
jobDiff.forEach((job) => {
getUpdatedJobData(job.id);
});
setJoblist(updatedJobs);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updatedJobs]);
const getUpdatedJobData = async (jobId) => {
client.query({
query: QUERY_EXACT_JOB_IN_PRODUCTION,
variables: { id: jobId },
});
};
return (
<ProductionListTable
loading={loading}
data={data ? data.jobs : []}
refetch={refetch}
/>
);
}

View File

@@ -3,10 +3,10 @@ import { Button, List, notification, Popover } from "antd";
import React, { useMemo, useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB_LINE_SUBLET } from "../../graphql/jobs-lines.queries";
export default function ProductionSubletsManageComponent({ subletJobLines }) {
const { t } = useTranslation();
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
const [updateJobLine] = useMutation(UPDATE_JOB_LINE_SUBLET);
const [loading, setLoading] = useState(false);
const subletCount = useMemo(() => {
return {
@@ -22,6 +22,8 @@ export default function ProductionSubletsManageComponent({ subletJobLines }) {
const result = await updateJobLine({
variables: {
jobId: sublet.jobid,
now: new Date(),
lineId: sublet.id,
line: {
sublet_completed:

View File

@@ -7,16 +7,31 @@ import { useTranslation } from "react-i18next";
import styled from "styled-components";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ShopInfoROStatusComponent);
const SelectorDiv = styled.div`
.ant-form-item .ant-select {
width: 200px;
}
`;
export default function ShopInfoROStatusComponent({ form }) {
export function ShopInfoROStatusComponent({ bodyshop, form }) {
const { t } = useTranslation();
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
bodyshop.imexshopid,
]);
const [options, setOptions] = useState(

View File

@@ -136,6 +136,27 @@ export const RECEIVE_PARTS_LINE = gql`
}
`;
export const UPDATE_JOB_LINE_SUBLET = gql`
mutation UPDATE_JOB_LINE_SUBLET(
$lineId: uuid!
$line: joblines_set_input!
$now: timestamptz!
$jobId: uuid!
) {
update_jobs_by_pk(pk_columns: { id: $jobId }, _set: { updated_at: $now }) {
id
updated_at
}
update_joblines(where: { id: { _eq: $lineId } }, _set: $line) {
returning {
id
sublet_completed
sublet_ignored
}
}
}
`;
export const UPDATE_JOB_LINE = gql`
mutation UPDATE_JOB_LINE($lineId: uuid!, $line: joblines_set_input!) {
update_joblines(where: { id: { _eq: $lineId } }, _set: $line) {
@@ -179,6 +200,10 @@ export const GET_JOB_LINES_TO_ENTER_BILL = gql`
jobs_by_pk(id: $id) {
id
status
ious {
id
ro_number
}
}
}
`;
@@ -226,3 +251,14 @@ export const DELETE_JOB_LINE_BY_PK = gql`
}
}
`;
export const UPDATE_JOB_LINES_IOU = gql`
mutation UPDATE_JOB_LINES_IOU($ids: [uuid!]!) {
update_joblines(where: { id: { _in: $ids } }, _set: { ioucreated: true }) {
returning {
ioucreated
id
}
}
}
`;

View File

@@ -105,10 +105,18 @@ export const QUERY_PARTS_QUEUE = gql`
}
}
`;
export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
subscription SUBSCRIPTION_JOBS_IN_PRODUCTION {
jobs(where: { inproduction: { _eq: true } }) {
id
updated_at
}
}
`;
export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
query QUERY_EXACT_JOB_IN_PRODUCTION($id: uuid!) {
jobs(where: { id: { _eq: $id } }) {
id
status
ro_number
@@ -171,6 +179,79 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
line_desc
sublet_ignored
sublet_completed
jobid
}
}
}
`;
export const QUERY_JOBS_IN_PRODUCTION = gql`
query QUERY_JOBS_IN_PRODUCTION {
jobs(where: { inproduction: { _eq: true } }) {
id
updated_at
status
ro_number
ownr_fn
ownr_ln
ownr_co_nm
v_model_yr
v_model_desc
clm_no
v_make_desc
v_color
plate_no
actual_in
scheduled_completion
scheduled_delivery
date_last_contacted
date_next_contact
ins_co_nm
clm_total
ownr_ph1
ownr_ph2
special_coverage_policy
owner_owing
production_vars
kanbanparent
alt_transport
employee_body
employee_refinish
employee_prep
employee_csr
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _eq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
subletLines: joblines(
where: {
_and: { part_type: { _in: ["PAS", "PASL"] }, removed: { _eq: false } }
}
order_by: { line_no: asc }
) {
id
line_desc
sublet_ignored
sublet_completed
jobid
}
}
}
@@ -529,6 +610,7 @@ export const GET_JOB_BY_PK = gql`
manual_line
prt_dsmk_p
prt_dsmk_m
ioucreated
billlines(limit: 1, order_by: { bill: { date: desc } }) {
id
quantity
@@ -874,6 +956,7 @@ export const UPDATE_JOB = gql`
update_jobs(where: { id: { _eq: $jobId } }, _set: $job) {
returning {
id
date_exported
status
alt_transport
@@ -1253,9 +1336,7 @@ export const QUERY_JOB_FOR_DUPE = gql`
servicing_dealer
servicing_dealer_contact
shopid
state_tax_rate
tax_lbr_rt
tax_levies_rt
tax_paint_mat_rt
@@ -1320,9 +1401,19 @@ export const QUERY_JOB_FOR_DUPE = gql`
tax_part
unq_seq
manual_line
notes
line_no
tran_code
}
driveable
towin
adj_g_disc
adj_strdis
adj_towdis
ca_gst_registrant
special_coverage_policy
tax_registration_number
tax_shop_mat_rt
}
}
`;
@@ -1580,46 +1671,6 @@ export const QUERY_ALL_JOB_FIELDS = gql`
}
}
`;
export const QUERY_JOBS_IN_PRODUCTION = gql`
query QUERY_JOBS_IN_PRODUCTION {
jobs(
where: { inproduction: { _eq: true } }
order_by: { scheduled_completion: asc }
) {
id
ro_number
ownr_co_nm
ownr_fn
ownr_ln
v_model_yr
v_make_desc
v_model_desc
scheduled_completion
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _eq: "LAR" } }, { removed: { _eq: false } }]
}
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
}
`;
export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
query QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED(

View File

@@ -158,8 +158,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
<DmsCustomerSelector />
<Col span={24}>
<div ref={logsRef}>
<Col span={24}>
<Card
title={t("jobs.labels.dms.logs")}
extra={
@@ -193,8 +193,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
>
<DmsLogEvents socket={socket} logs={logs} />
</Card>
</div>
</Col>
</div>
</Row>
</div>
);

View File

@@ -95,6 +95,7 @@
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}",
"jobioucreated": "IOU Created.",
"jobmodifylbradj": "Labor adjustments modified.",
"jobnoteadded": "Note added to job.",
"jobnotedeleted": "Note deleted from job.",
@@ -176,6 +177,7 @@
"entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax",
"iouexists": "An IOU exists that is associated to this RO.",
"local_tax": "Local Tax",
"markforreexport": "Mark for Re-export",
"new": "New Bill",
@@ -1082,6 +1084,7 @@
"labels": {
"billref": "Latest Bill",
"edit": "Edit Line",
"ioucreated": "IOU",
"new": "New Line",
"nostatus": "No Status",
"presets": "Jobline Presets"
@@ -1111,6 +1114,7 @@
"changestatus": "Change Status",
"changestimator": "Change Estimator",
"convert": "Convert",
"createiou": "Create IOU",
"deliver": "Deliver",
"dms": {
"addpayer": "Add Payer",
@@ -1469,6 +1473,7 @@
"ownerinfo": "Owner Info",
"vehicleinfo": "Vehicle Info"
},
"createiouwarning": "Are you sure you want to create an IOU for these lines? A new RO will be created based on those lines for this customer.",
"creating_new_job": "Creating new job...",
"deductible": {
"stands": "Stands",
@@ -1589,6 +1594,7 @@
"duplicated": "Job duplicated successfully. ",
"exported": "Job(s) exported successfully. ",
"invoiced": "Job closed and invoiced successfully.",
"ioucreated": "IOU created successfully. Click to see.",
"partsqueue": "Job added to parts queue.",
"save": "Job saved successfully.",
"savetitle": "Record saved successfully.",

View File

@@ -95,6 +95,7 @@
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobioucreated": "",
"jobmodifylbradj": "",
"jobnoteadded": "",
"jobnotedeleted": "",
@@ -176,6 +177,7 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"iouexists": "",
"local_tax": "",
"markforreexport": "",
"new": "",
@@ -1082,6 +1084,7 @@
"labels": {
"billref": "",
"edit": "Línea de edición",
"ioucreated": "",
"new": "Nueva línea",
"nostatus": "",
"presets": ""
@@ -1111,6 +1114,7 @@
"changestatus": "Cambiar Estado",
"changestimator": "",
"convert": "Convertir",
"createiou": "",
"deliver": "",
"dms": {
"addpayer": "",
@@ -1469,6 +1473,7 @@
"ownerinfo": "",
"vehicleinfo": ""
},
"createiouwarning": "",
"creating_new_job": "Creando nuevo trabajo ...",
"deductible": {
"stands": "",
@@ -1589,6 +1594,7 @@
"duplicated": "",
"exported": "",
"invoiced": "",
"ioucreated": "",
"partsqueue": "",
"save": "Trabajo guardado con éxito.",
"savetitle": "Registro guardado con éxito.",

View File

@@ -95,6 +95,7 @@
"jobfieldchanged": "",
"jobimported": "",
"jobinproductionchange": "",
"jobioucreated": "",
"jobmodifylbradj": "",
"jobnoteadded": "",
"jobnotedeleted": "",
@@ -176,6 +177,7 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"iouexists": "",
"local_tax": "",
"markforreexport": "",
"new": "",
@@ -1082,6 +1084,7 @@
"labels": {
"billref": "",
"edit": "Ligne d'édition",
"ioucreated": "",
"new": "Nouvelle ligne",
"nostatus": "",
"presets": ""
@@ -1111,6 +1114,7 @@
"changestatus": "Changer le statut",
"changestimator": "",
"convert": "Convertir",
"createiou": "",
"deliver": "",
"dms": {
"addpayer": "",
@@ -1469,6 +1473,7 @@
"ownerinfo": "",
"vehicleinfo": ""
},
"createiouwarning": "",
"creating_new_job": "Création d'un nouvel emploi ...",
"deductible": {
"stands": "",
@@ -1589,6 +1594,7 @@
"duplicated": "",
"exported": "",
"invoiced": "",
"ioucreated": "",
"partsqueue": "",
"save": "Le travail a été enregistré avec succès.",
"savetitle": "Enregistrement enregistré avec succès.",

View File

@@ -6,9 +6,10 @@ import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
//import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger";
import axios from "axios";
import { auth } from "../firebase/firebase.utils";
import errorLink from "../graphql/apollo-error-handling";
import { store } from "../redux/store";
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
});
@@ -47,7 +48,19 @@ const roundTripLink = new ApolloLink((operation, forward) => {
});
const TrackExecutionTime = async (operationName, time) => {
//await axios.post("/ioevent", { operationName, time, dbevent: true });
const rdxStore = store.getState();
try {
console.log("trying");
axios.post("/ioevent", {
operationName,
time,
dbevent: true,
user: rdxStore.user.currentUser.email,
imexshopid: rdxStore.user.bodyshop.imexshopid,
});
} catch (error) {
console.log("IOEvent Error", error);
}
};
const subscriptionMiddleware = {

File diff suppressed because it is too large Load Diff

View File

@@ -2123,6 +2123,7 @@
- est_seq
- glass_flag
- id
- ioucreated
- jobid
- lbr_amt
- lbr_hrs_j
@@ -2185,6 +2186,7 @@
- est_seq
- glass_flag
- id
- ioucreated
- jobid
- lbr_amt
- lbr_hrs_j
@@ -2258,6 +2260,7 @@
- est_seq
- glass_flag
- id
- ioucreated
- jobid
- lbr_amt
- lbr_hrs_j
@@ -2390,6 +2393,9 @@
- name: employee_refinish_rel
using:
foreign_key_constraint_on: employee_refinish
- name: iouparent_rel
using:
foreign_key_constraint_on: iouparent
- name: owner
using:
foreign_key_constraint_on: ownerid
@@ -2453,6 +2459,13 @@
table:
schema: public
name: exportlog
- name: ious
using:
foreign_key_constraint_on:
column: iouparent
table:
schema: public
name: jobs
- name: job_conversations
using:
foreign_key_constraint_on:
@@ -2678,6 +2691,7 @@
- intakechecklist
- invoice_allocation
- invoice_date
- iouparent
- job_totals
- kanbanparent
- kmin
@@ -2931,6 +2945,7 @@
- intakechecklist
- invoice_allocation
- invoice_date
- iouparent
- job_totals
- kanbanparent
- kmin
@@ -3194,6 +3209,7 @@
- intakechecklist
- invoice_allocation
- invoice_date
- iouparent
- job_totals
- kanbanparent
- kmin

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"."jobs" add column "iouparent" uuid
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "iouparent" uuid
null;

View File

@@ -0,0 +1 @@
alter table "public"."jobs" drop constraint "jobs_iouparent_fkey";

View File

@@ -0,0 +1,5 @@
alter table "public"."jobs"
add constraint "jobs_iouparent_fkey"
foreign key ("iouparent")
references "public"."jobs"
("id") on update set null on delete set null;

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "jobs_idx_iouparent";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "jobs_idx_iouparent" on
"public"."jobs" using btree ("iouparent");

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
alter table "public"."joblines" alter column "iou" set default false;
alter table "public"."joblines" alter column "iou" drop not null;
alter table "public"."joblines" add column "iou" bool;

View File

@@ -0,0 +1 @@
alter table "public"."joblines" drop column "iou" cascade;

View File

@@ -17,13 +17,13 @@
"start": "node server.js"
},
"dependencies": {
"aws-sdk": "^2.1028.0",
"aws-sdk": "^2.1043.0",
"axios": "^0.24.0",
"bluebird": "^3.7.2",
"body-parser": "^1.18.3",
"cloudinary": "^1.27.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"cookie-parser": "^1.4.6",
"cors": "2.8.5",
"csrf": "^3.1.0",
"dinero.js": "^1.9.1",
@@ -31,7 +31,7 @@
"express": "^4.16.4",
"firebase-admin": "^10.0.0",
"graphql": "^16.0.1",
"graphql-request": "^3.6.1",
"graphql-request": "^3.7.0",
"graylog2": "^0.2.1",
"inline-css": "^3.0.0",
"intuit-oauth": "^4.0.0",
@@ -40,13 +40,13 @@
"node-mailjet": "^3.3.4",
"node-quickbooks": "^2.0.39",
"nodemailer": "^6.7.1",
"phone": "^3.1.9",
"phone": "^3.1.10",
"query-string": "^7.0.1",
"soap": "^0.43.0",
"socket.io": "^4.3.2",
"socket.io": "^4.4.0",
"ssh2-sftp-client": "^7.1.0",
"stripe": "^8.188.0",
"twilio": "^3.71.1",
"stripe": "^8.191.0",
"twilio": "^3.71.3",
"uuid": "^8.3.2",
"xmlbuilder2": "^3.0.2"
},

View File

@@ -6,7 +6,7 @@ require("dotenv").config({
),
});
const GraphQLClient = require("graphql-request").GraphQLClient;
const axios = require("axios").default;
const AxiosLib = require("axios").default;
const queries = require("../../graphql-client/queries");
const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
@@ -16,6 +16,37 @@ const CalculateAllocations =
const CdkBase = require("../../web-sockets/web-socket");
const moment = require("moment");
const axios = AxiosLib.create();
axios.interceptors.request.use((x) => {
const socket = x.socket;
const headers = {
...x.headers.common,
...x.headers[x.method],
...x.headers,
};
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
console.log(printable);
CdkBase.createLogEvent(socket, "TRACE", `Raw Request: ${printable}`);
return x;
});
axios.interceptors.response.use((x) => {
const socket = x.config.socket;
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(
x.data
)}`;
console.log(printable);
CdkBase.createLogEvent(socket, "TRACE", `Raw Response: ${printable}`);
return x;
});
exports.default = async function (socket, { txEnvelope, jobid }) {
socket.logEvents = [];
socket.recordid = jobid;
@@ -31,7 +62,7 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
socket.JobData = JobData;
//Query for the Vehicle record to get the associated customer.
// socket.DmsVeh = await QueryVehicleFromDms(socket);
socket.DmsVeh = await QueryVehicleFromDms(socket);
//Todo: Need to validate the lines and methods below.
if (socket.DmsVeh && socket.DmsVeh.CustomerRef) {
//Get the associated customer from the Vehicle Record.
@@ -72,7 +103,9 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(
CdkBase.createLogEvent(
socket,
"DEBUG",
`Upserting contact information to DMS for ${socket.JobData.ownr_fn} ${socket.JobData.ownr_ln} ${socket.JobData.ownr_co_nm}`
`Upserting contact information to DMS for ${
socket.JobData.ownr_fn || ""
} ${socket.JobData.ownr_ln || ""} ${socket.JobData.ownr_co_nm || ""}`
);
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
@@ -83,6 +116,7 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(
);
await UpsertVehicleData(socket, ownerRef.ReferenceId);
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
return;
await InsertAccountPostingData(socket);
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
await MarkJobExported(socket, socket.JobData.id);
@@ -135,7 +169,7 @@ async function QueryJobData(socket, jobid) {
async function QueryVehicleFromDms(socket) {
try {
const { data: VehicleGetResponse } = await axios.post(
const { data: VehicleGetResponse, request } = await axios.post(
PBS_ENDPOINTS.VehicleGet,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
@@ -160,8 +194,9 @@ async function QueryVehicleFromDms(socket) {
// IncludeBuildVehicles: false,
// ShortVIN: "String",
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, VehicleGetResponse);
return VehicleGetResponse;
} catch (error) {
@@ -180,13 +215,13 @@ async function QueryCustomersFromDms(socket) {
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
//ContactId: "00000000000000000000000000000000",
ContactCode: socket.JobData.owner.accountingid,
// ContactCode: socket.JobData.owner.accountingid,
FirstName: socket.JobData.ownr_co_nm
? socket.JobData.ownr_co_nm
: socket.JobData.ownr_fn,
LastName: socket.JobData.ownr_ln,
PhoneNumber: socket.JobData.ownr_ph1,
// EmailAddress: "String",
EmailAddress: socket.JobData.ownr_ea,
// ModifiedSince: "0001-01-01T00:00:00.0000000Z",
// ModifiedUntil: "0001-01-01T00:00:00.0000000Z",
// ContactIdList: ["00000000000000000000000000000000"],
@@ -196,7 +231,7 @@ async function QueryCustomersFromDms(socket) {
// DriverLicense: "String",
// ZipCode: "String",
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
@@ -232,7 +267,7 @@ async function QueryCustomerBycodeFromDms(socket, CustomerRef) {
// DriverLicense: "String",
// ZipCode: "String",
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
@@ -318,7 +353,7 @@ async function UpsertContactData(socket, selectedCustomerId) {
// UserRequest: "String",
// UserRef: "00000000000000000000000000000000",
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, ContactChangeResponse);
return ContactChangeResponse;
@@ -347,16 +382,17 @@ async function UpsertVehicleData(socket, ownerRef) {
//FleetNumber: "String",
//Status: "String",
OwnerRef: ownerRef, // "00000000000000000000000000000000",
//ModelNumber: "String",
ModelNumber:
socket.JobData.vehicle && socket.JobData.vehicle.v_makecode,
Make: socket.JobData.v_make_desc,
Model: socket.JobData.v_model_desc,
//Trim: "String",
Trim: socket.JobData.vehicle && socket.JobData.vehicle.v_trimcode,
//VehicleType: "String",
Year: socket.JobData.v_model_yr,
Odometer: socket.JobData.kmin,
Odometer: socket.JobData.kmout,
ExteriorColor: {
Code: socket.JobData.v_color,
//Description: "String",
Description: socket.JobData.v_color,
},
// InteriorColor: { Code: "String", Description: "String" },
//Engine: "String",
@@ -475,7 +511,7 @@ async function UpsertVehicleData(socket, ownerRef) {
// UserRequest: "String",
// UserRef: "00000000000000000000000000000000",
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, VehicleChangeResponse);
return VehicleChangeResponse;
@@ -564,7 +600,7 @@ async function InsertAccountPostingData(socket) {
Lines: wips,
},
},
{ auth: PBS_CREDENTIALS }
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, AccountPostingChange);

View File

@@ -21,7 +21,7 @@ const entegralEndpoint =
const client = require("../graphql-client/graphql-client").client;
const uuid = require("uuid").v4;
const momentFormat = "yyyy-MM-DDTHH:mm:ss.SSSSSSSZ";
const momentFormat = "yyyy-MM-DDTHH:mm:ss.SSS";
exports.default = async (req, res) => {
//Query for the List of Bodyshop Clients.
@@ -47,7 +47,7 @@ exports.default = async (req, res) => {
RqUID: transId,
DocumentInfo: {
BMSVer: "4.0.0",
DocumentType: "Repair Order",
DocumentType: "RO",
DocumentVerCode: "EM",
DocumentVerNum: GetSupplementNumber(job.joblines), //TODO Get Supplement Number
DocumentStatus: GetDocumentstatus(job, bodyshop),
@@ -63,8 +63,10 @@ exports.default = async (req, res) => {
UploadDateTime: moment().format(momentFormat),
},
RepairEvent: {
ArrivalDateTime:
CreatedDateTime:
job.date_open && moment(job.date_open).format(momentFormat),
ArrivalDateTime:
job.actual_in && moment(job.actual_in).format(momentFormat),
ArrivalOdometerReading: job.kmin,
TargetCompletionDateTime:
job.scheduled_completion &&
@@ -244,7 +246,12 @@ exports.default = async (req, res) => {
},
VehicleDesc: {
//ProductionDate: "2009-10",
ModelYear: job.v_model_yr,
ModelYear:
parseInt(job.v_model_yr) < 1900
? parseInt(job.v_model_yr) < moment().format("YY")
? `20${job.v_model_yr}`
: `19${job.v_model_yr}`
: job.v_model_yr,
MakeDesc: job.v_make_desc,
ModelName: job.v_model_desc,
},
@@ -455,7 +462,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Body Labor",
TotalHours: job.job_totals.rates.lab.hours,
TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat(
"0.0"
"0.00"
),
},
{
@@ -463,7 +470,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Frame Labor",
TotalHours: job.job_totals.rates.laf.hours,
TotalAmt: Dinero(job.job_totals.rates.laf.total).toFormat(
"0.0"
"0.00"
),
},
{
@@ -471,7 +478,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Mechanical Labor",
TotalHours: job.job_totals.rates.lam.hours,
TotalAmt: Dinero(job.job_totals.rates.lam.total).toFormat(
"0.0"
"0.00"
),
},
{
@@ -479,7 +486,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Refinish Labor",
TotalHours: job.job_totals.rates.lar.hours,
TotalAmt: Dinero(job.job_totals.rates.lar.total).toFormat(
"0.0"
"0.00"
),
},
],
@@ -490,7 +497,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.paa &&
job.job_totals.parts.parts.list.paa.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAC",
@@ -498,7 +505,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pac &&
job.job_totals.parts.parts.list.pac.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAG",
@@ -506,7 +513,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pag &&
job.job_totals.parts.parts.list.pag.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAL",
@@ -514,7 +521,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pal &&
job.job_totals.parts.parts.list.pal.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAM",
@@ -522,7 +529,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pam &&
job.job_totals.parts.parts.list.pam.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAN",
@@ -530,7 +537,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pan &&
job.job_totals.parts.parts.list.pan.total
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "PAR",
@@ -538,7 +545,7 @@ exports.default = async (req, res) => {
TotalAmt: Dinero(
job.job_totals.parts.parts.list.par &&
job.job_totals.parts.parts.list.par.total
).toFormat("0.0"),
).toFormat("0.00"),
},
],
OtherChargesTotalsInfo: [
@@ -546,21 +553,21 @@ exports.default = async (req, res) => {
TotalType: "OTSL",
TotalTypeDesc: "Sublet",
TotalAmt: Dinero(job.job_totals.parts.sublets.total).toFormat(
"0.0"
"0.00"
),
},
{
TotalType: "MAPA",
TotalTypeDesc: "Paint Materials",
TotalAmt: Dinero(job.job_totals.rates.mapa.total).toFormat(
"0.0"
"0.00"
),
},
{
TotalType: "MASH",
TotalTypeDesc: "Shop Materials",
TotalAmt: Dinero(job.job_totals.rates.mash.total).toFormat(
"0.0"
"0.00"
),
},
// {
@@ -574,14 +581,14 @@ exports.default = async (req, res) => {
TotalType: "OTST",
TotalTypeDesc: "Storage",
TotalAmt: Dinero(job.job_totals.additional.storage).toFormat(
"0.0"
"0.00"
),
},
{
TotalType: "OTTW",
TotalTypeDesc: "Towing",
TotalAmt: Dinero(job.job_totals.additional.towing).toFormat(
"0.0"
"0.00"
),
},
{
@@ -589,16 +596,24 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Additional Charges",
TotalAmt: Dinero(
job.job_totals.additional.additionalCosts
).toFormat("0.0"),
).toFormat("0.00"),
},
],
SummaryTotalsInfo: [
{
TotalType: "TOT",
TotalSubType: "TT",
TotalTypeDesc: "Gross Total",
TotalAmt: Dinero(
job.job_totals.totals.total_repairs
).toFormat("0.00"),
},
{
TotalType: "TOT",
TotalSubType: "T2",
TotalTypeDesc: "Net Total",
TotalAmt: Dinero(job.job_totals.totals.subtotal).toFormat(
"0.0"
"0.00"
),
},
{
@@ -606,7 +621,7 @@ exports.default = async (req, res) => {
TotalSubType: "F7",
TotalTypeDesc: "Sales Tax",
TotalAmt: Dinero(job.job_totals.totals.state_tax).toFormat(
"0.0"
"0.00"
),
},
{
@@ -614,17 +629,9 @@ exports.default = async (req, res) => {
TotalSubType: "GST",
TotalTypeDesc: "GST Tax",
TotalAmt: Dinero(job.job_totals.totals.federal_tax).toFormat(
"0.0"
"0.00"
),
},
{
TotalType: "TOT",
TotalSubType: "TT",
TotalTypeDesc: "Gross Total",
TotalAmt: Dinero(
job.job_totals.totals.total_repairs
).toFormat("0.0"),
},
// {
// TotalType: "TOT",
// TotalSubType: "SM",
@@ -637,7 +644,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Deductible",
TotalAmt: Dinero({
amount: Math.round((job.ded_amt || 0) * 100),
}).toFormat("0.0"),
}).toFormat("0.00"),
},
{
TotalType: "TOT",
@@ -645,7 +652,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Betterment",
TotalAmt: Dinero(
job.job_totals.totals.custPayable.dep_taxes
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "TOT",
@@ -659,7 +666,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Bottom Line Discount",
TotalAmt: Dinero(
job.job_totals.additional.adjustments
).toFormat("0.0"),
).toFormat("0.00"),
},
{
TotalType: "TOT",
@@ -667,7 +674,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Insurance Pay",
TotalAmt: Dinero(job.job_totals.totals.total_repairs)
.subtract(Dinero(job.job_totals.totals.custPayable.total))
.toFormat("0.0"),
.toFormat("0.00"),
},
// {
// TotalType: "TOT",
@@ -681,7 +688,7 @@ exports.default = async (req, res) => {
TotalTypeDesc: "Customer Pay",
TotalAmt: Dinero(
job.job_totals.totals.custPayable.total
).toFormat("0.0"),
).toFormat("0.00"),
},
],
RepairTotalsType: 1,
@@ -813,7 +820,7 @@ exports.default = async (req, res) => {
const [result, rawResponse, , rawRequest] = entegralResponse;
console.log("🚀 ~ file: arms.js ~ line 806 ~ result", result);
res.json(result);
res.json({ result, obj: abc });
} catch (error) {
console.log(error);
res.json(error);

View File

@@ -255,6 +255,8 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
clm_no
dms_allocation
invoice_allocation
kmin
kmout
ownerid
ownr_ln
ownr_fn
@@ -302,6 +304,10 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
v_make_desc
v_color
ca_customer_gst
vehicle{
v_trimcode
v_makecode
}
bodyshop {
id
md_ro_statuses

View File

@@ -1,6 +1,7 @@
const client = require("../graphql-client/graphql-client").client;
const queries = require("../graphql-client/queries");
const path = require("path");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(
@@ -10,15 +11,22 @@ require("dotenv").config({
});
exports.default = async (req, res) => {
const { operationName, time, dbevent } = req.body;
const { operationName, time, dbevent, user, imexshopid } = req.body;
try {
await client.request(queries.INSERT_IOEVENT, {
event: {
operationname: operationName,
time,
dbevent,
},
// await client.request(queries.INSERT_IOEVENT, {
// event: {
// operationname: operationName,
// time,
// dbevent,
// },
// });
console.log("IOEVENT", operationName, time, dbevent, user, imexshopid);
logger.log("ioevent", "trace", user, null, {
imexshopid,
operationName,
time,
dbevent,
});
res.sendStatus(200);

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.1028.0:
version "2.1028.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1028.0.tgz#ce076076174afa9bd311406b8186ea90163e3331"
integrity sha512-OmR0NcpU8zsDcUOZhM+eZ6CzlUFtuaEuRyjm6mxDO0KI7lJAp7/NzB6tcellRrgWxL+NO7b5TSxi+m28qu5ocQ==
aws-sdk@^2.1043.0:
version "2.1043.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1043.0.tgz#170a5b14b3ea25df760f9a6b437bfe35c9a1578e"
integrity sha512-WysMTSLfi8ZCj6QAAitJkxObxOzGLhcu6FxoKHiEnrefvfQtSvwqUq7BbT/pIfijnF9dE/7e9XwjW8Dz/hqF4Q==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -1004,12 +1004,12 @@ convert-source-map@^1.7.0:
dependencies:
safe-buffer "~5.1.1"
cookie-parser@^1.4.5:
version "1.4.5"
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.5.tgz#3e572d4b7c0c80f9c61daf604e4336831b5d1d49"
integrity sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==
cookie-parser@^1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.6.tgz#3ac3a7d35a7a03bbc7e365073a26074824214594"
integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==
dependencies:
cookie "0.4.0"
cookie "0.4.1"
cookie-signature "1.0.6"
cookie-signature@1.0.6:
@@ -1022,7 +1022,7 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookie@~0.4.1:
cookie@0.4.1, cookie@~0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
@@ -1352,10 +1352,10 @@ engine.io-parser@~5.0.0:
dependencies:
base64-arraybuffer "~1.0.1"
engine.io@~6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.0.0.tgz#2b993fcd73e6b3a6abb52b40b803651cd5747cf0"
integrity sha512-Ui7yl3JajEIaACg8MOUwWvuuwU7jepZqX3BKs1ho7NQRuP4LhN4XIykXhp8bEy+x/DhA0LBZZXYSCkZDqrwMMg==
engine.io@~6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.0.tgz#459eab0c3724899d7b63a20c3a6835cf92857939"
integrity sha512-ErhZOVu2xweCjEfYcTdkCnEYUiZgkAcBBAhW4jbIvNG8SLU3orAqoJCiytZjYF7eTpVmmCrLDjLIEaPlUAs1uw==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
@@ -1976,10 +1976,10 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
graphql-request@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.6.1.tgz#689cce1da990131b40b05651f9f32bff506a1d8e"
integrity sha512-Nm1EasrAQVZllyNTlHDLnLZjlhC6eRWnWP6KH//ytnAL08pjlLkdI2K+s6OV92p45hn5b/kUlLbDwACmRoLwrQ==
graphql-request@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.7.0.tgz#c7406e537084f8b9788541e3e6704340ca13055b"
integrity sha512-dw5PxHCgBneN2DDNqpWu8QkbbJ07oOziy8z+bK/TAXufsOLaETuVO4GkXrbs0WjhdKhBMN3BkpN/RIvUHkmNUQ==
dependencies:
cross-fetch "^3.0.6"
extract-files "^9.0.0"
@@ -2974,10 +2974,10 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phone@^3.1.9:
version "3.1.9"
resolved "https://registry.yarnpkg.com/phone/-/phone-3.1.9.tgz#692d70061c0d7391ebcd67f30bee4364b52264e5"
integrity sha512-u5jPSlbB4lq9W2ptznEb4eBBKnr3y8xAL4Dsn4uFzUl2gWZE6QiQgxMUMelHq3VUUzFq1QM579SkTxJ+k++igg==
phone@^3.1.10:
version "3.1.10"
resolved "https://registry.yarnpkg.com/phone/-/phone-3.1.10.tgz#7e9954b1cf8dcac2cf4f45bfd1e4f55e3833dcaf"
integrity sha512-YhUCaFzvp2nGuAL/KkYZ1qDm8E51PkQswDFSzAmQ8jgNt6TU6iHMxp4KEQ2J/mFk9zco84iq6D2j5z7fm7W9Vw==
pick-util@^1.1.3:
version "1.1.3"
@@ -3525,10 +3525,10 @@ soap@^0.43.0:
uuid "^8.3.2"
xml-crypto "^2.1.3"
socket.io-adapter@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.2.tgz#039cd7c71a52abad984a6d57da2c0b7ecdd3c289"
integrity sha512-PBZpxUPYjmoogY0aoaTmo1643JelsaS1CiAwNjRVdrI0X9Seuc19Y2Wife8k88avW6haG8cznvwbubAZwH4Mtg==
socket.io-adapter@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486"
integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==
socket.io-parser@~4.0.4:
version "4.0.4"
@@ -3539,16 +3539,16 @@ socket.io-parser@~4.0.4:
component-emitter "~1.3.0"
debug "~4.3.1"
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==
socket.io@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.0.tgz#8140a0db2c22235f88a6dceb867e4d5c9bd70507"
integrity sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~6.0.0"
socket.io-adapter "~2.3.2"
engine.io "~6.1.0"
socket.io-adapter "~2.3.3"
socket.io-parser "~4.0.4"
socks-proxy-agent@5, socks-proxy-agent@^5.0.0:
@@ -3749,10 +3749,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.188.0:
version "8.188.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.188.0.tgz#1d892c2f9000847c627db06a374529e7fbd2b83e"
integrity sha512-AW5IOKq4y+ENfHddJPrLL/GSvGj1MnBvUe6QMI1z27x/4pMNrU7v0ZqzRSJCihWqP0tUuCmQibSYSbsV4XJ3zA==
stripe@^8.191.0:
version "8.191.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.191.0.tgz#7bb56080dce2e2e9cc4f52dc05792626ea46bfd6"
integrity sha512-3oyuIbuW3WDsbGnMUtIZPkr+sOUJ3NEtEwghd81ZuWc+Tq09MrgWXGekbVnbxA3TMFwfKOILw6QoiPA7za4JUg==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.6.0"
@@ -3916,10 +3916,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.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==
twilio@^3.71.3:
version "3.71.3"
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.71.3.tgz#a446d2b49f8c1ed60b0dd830c919921358c17203"
integrity sha512-m9eda9fvkHxMMDHRtXj8WKI0ViP4EG4xS5au5ay3ScfModhBZ1ZtyfWZ0AfWI++A7a1T1j3ZVNIZ+AMLwxSffw==
dependencies:
axios "^0.21.4"
dayjs "^1.8.29"