Compare commits
5 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62185196a8 | ||
|
|
88b313b9d8 | ||
|
|
08e1cf80c2 | ||
|
|
2f3056b49b | ||
|
|
a08125df54 |
@@ -1,9 +1,10 @@
|
||||
import dayjs from "../../utils/day";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import Board from "./trello-board/index";
|
||||
import { Button, notification, Skeleton, Space } from "antd";
|
||||
import { PageHeader } from "@ant-design/pro-layout";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -23,6 +24,7 @@ import cloneDeep from "lodash/cloneDeep";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { defaultFilters, mergeWithDefaults } from "./settings/defaultKanbanSettings.js";
|
||||
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -45,10 +47,136 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isMoving, setIsMoving] = useState(false);
|
||||
const [orientation, setOrientation] = useState("vertical");
|
||||
const { socket } = useContext(SocketContext); // Access socket from the context
|
||||
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
|
||||
const handleJobUpdated = useCallback(
|
||||
(updatedJob) => {
|
||||
setBoardLanes((prevBoardLanes) => {
|
||||
const updatedLanes = cloneDeep(prevBoardLanes.lanes);
|
||||
|
||||
// Find the lane containing the card with the updated job ID
|
||||
let sourceLane = updatedLanes.find((lane) => lane.cards.some((card) => card.id === updatedJob.id));
|
||||
|
||||
if (!sourceLane) {
|
||||
console.log("Card not found in any lane. Checking for valid status to add it.");
|
||||
|
||||
// Find the target lane based on the new status if the card does not exist
|
||||
const targetLane = updatedLanes.find((lane) => lane.id === updatedJob.status);
|
||||
|
||||
if (targetLane && updatedJob.isInProduction) {
|
||||
// Check if job is in production and status is valid
|
||||
console.log(`Adding card to lane ${targetLane.title}`);
|
||||
|
||||
// Add the new card to the target lane
|
||||
const newCard = {
|
||||
id: updatedJob.id,
|
||||
metadata: { ...updatedJob }
|
||||
};
|
||||
targetLane.cards.push(newCard);
|
||||
|
||||
// Update the lane title with the new card count
|
||||
targetLane.title = `${targetLane.title.split(" ")[0]} (${targetLane.cards.length})`;
|
||||
|
||||
return { lanes: updatedLanes }; // Return early since the card is added
|
||||
} else {
|
||||
console.error("No valid lane or status to add the job to.");
|
||||
return prevBoardLanes; // Return the previous state if no valid status or lane
|
||||
}
|
||||
}
|
||||
|
||||
// If the card exists, find it in the source lane
|
||||
const cardIndex = sourceLane.cards.findIndex((card) => card.id === updatedJob.id);
|
||||
const currentCard = sourceLane.cards[cardIndex];
|
||||
|
||||
// If we somehow can't find the card, return
|
||||
if (!currentCard) {
|
||||
console.error("Card not found for the updated job.");
|
||||
return prevBoardLanes; // Return the previous state if the card is not found
|
||||
}
|
||||
|
||||
// Iterate through the properties of updatedJob and update the corresponding values in currentCard.metadata
|
||||
Object.keys(updatedJob).forEach((key) => {
|
||||
// Normalize date fields by comparing their ISO strings or timestamps
|
||||
if (key === "updated_at") {
|
||||
const currentCardDate = dayjs(currentCard.metadata[key]).toISOString();
|
||||
const updatedJobDate = dayjs(updatedJob[key]).toISOString();
|
||||
|
||||
if (currentCardDate !== updatedJobDate) {
|
||||
console.log(`Updating ${key} from ${currentCardDate} to ${updatedJobDate}`);
|
||||
currentCard.metadata[key] = updatedJob[key]; // Assign the new value if different
|
||||
}
|
||||
} else if (key in currentCard.metadata && currentCard.metadata[key] !== updatedJob[key]) {
|
||||
console.log(`Updating ${key} from ${currentCard.metadata[key]} to ${updatedJob[key]}`);
|
||||
currentCard.metadata[key] = updatedJob[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Mark that data has been changed if any field was updated
|
||||
const isDataChanged = !isEqual(currentCard.metadata, updatedJob);
|
||||
|
||||
// Check if the lane (status) has changed
|
||||
const isLaneChanged = updatedJob.status !== sourceLane.id;
|
||||
|
||||
// Case 1: Both data and lane have changed
|
||||
if (isDataChanged && isLaneChanged) {
|
||||
console.log("Case 1: Data and Lane Changed");
|
||||
|
||||
// Remove the card from the source lane
|
||||
const [cardToMove] = sourceLane.cards.splice(cardIndex, 1);
|
||||
|
||||
// Find the target lane based on the new status
|
||||
const targetLane = updatedLanes.find((lane) => lane.id === updatedJob.status);
|
||||
if (targetLane) {
|
||||
targetLane.cards.push({ ...cardToMove, metadata: { ...currentCard.metadata } });
|
||||
sourceLane.title = `${sourceLane.title.split(" ")[0]} (${sourceLane.cards.length})`;
|
||||
targetLane.title = `${targetLane.title.split(" ")[0]} (${targetLane.cards.length})`;
|
||||
} else {
|
||||
console.error("Target lane not found for the updated job.");
|
||||
}
|
||||
}
|
||||
// Case 2: Only data has changed
|
||||
else if (isDataChanged && !isLaneChanged) {
|
||||
console.log("Case 2: Only Data Changed");
|
||||
sourceLane.cards[cardIndex] = { ...currentCard, metadata: { ...currentCard.metadata } };
|
||||
}
|
||||
// Case 3: Only the lane has changed
|
||||
else if (!isDataChanged && isLaneChanged) {
|
||||
console.log("Case 3: Only Lane Changed");
|
||||
|
||||
// Remove the card from the source lane
|
||||
const [cardToMove] = sourceLane.cards.splice(cardIndex, 1);
|
||||
|
||||
// Find the target lane based on the new status
|
||||
const targetLane = updatedLanes.find((lane) => lane.id === updatedJob.status);
|
||||
if (targetLane) {
|
||||
targetLane.cards.push(cardToMove);
|
||||
sourceLane.title = `${sourceLane.title.split(" ")[0]} (${sourceLane.cards.length})`;
|
||||
targetLane.title = `${targetLane.title.split(" ")[0]} (${targetLane.cards.length})`;
|
||||
} else {
|
||||
console.error("Target lane not found for the updated job.");
|
||||
}
|
||||
}
|
||||
|
||||
return { lanes: updatedLanes };
|
||||
});
|
||||
},
|
||||
[setBoardLanes]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for the job-updated event from the socket
|
||||
if (socket) {
|
||||
socket.on("job-updated", handleJobUpdated);
|
||||
|
||||
return () => {
|
||||
socket.off("job-updated", handleJobUpdated);
|
||||
};
|
||||
}
|
||||
}, [socket, handleJobUpdated]);
|
||||
|
||||
useEffect(() => {
|
||||
if (associationSettings) {
|
||||
setLoading(true);
|
||||
|
||||
@@ -28,9 +28,9 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
|
||||
onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`)
|
||||
});
|
||||
|
||||
const { data: updatedJobs } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, {
|
||||
onError: (error) => console.error(`Error subscribing to jobs in production: ${error.message}`)
|
||||
});
|
||||
// const { data: updatedJobs } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, {
|
||||
// onError: (error) => console.error(`Error subscribing to jobs in production: ${error.message}`)
|
||||
// });
|
||||
|
||||
const { loading: associationSettingsLoading, data: associationSettings } = useQuery(QUERY_KANBAN_SETTINGS, {
|
||||
variables: { email: currentUser.email },
|
||||
@@ -40,11 +40,11 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
|
||||
// This provides us the current version of the Lanes from the Redux store
|
||||
// const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
|
||||
|
||||
useEffect(() => {
|
||||
if (updatedJobs && data) {
|
||||
refetch().catch((err) => console.error(`Error re-fetching jobs in production: ${err.message}`));
|
||||
}
|
||||
}, [updatedJobs, data, refetch]);
|
||||
// useEffect(() => {
|
||||
// if (updatedJobs && data) {
|
||||
// refetch().catch((err) => console.error(`Error re-fetching jobs in production: ${err.message}`));
|
||||
// }
|
||||
// }, [updatedJobs, data, refetch]);
|
||||
|
||||
const filteredAssociationSettings = useMemo(() => {
|
||||
return associationSettings?.associations[0] || null;
|
||||
|
||||
@@ -7,6 +7,10 @@ const useSocket = (bodyshop) => {
|
||||
const [clientId, setClientId] = useState(null); // State to store unique identifier
|
||||
|
||||
useEffect(() => {
|
||||
const handleBodyshopMessage = (message) => {
|
||||
console.log(`Received message for bodyshop ${bodyshop.id}:`, message);
|
||||
};
|
||||
|
||||
if (bodyshop && bodyshop.id) {
|
||||
const endpoint = import.meta.env.PROD ? import.meta.env.VITE_APP_AXIOS_BASE_API_URL : "https://localhost:3000";
|
||||
|
||||
@@ -24,15 +28,26 @@ const useSocket = (bodyshop) => {
|
||||
|
||||
setSocket(socketInstance);
|
||||
|
||||
socketInstance.on("connect", () => {
|
||||
// When the socket connects or reconnects, join the bodyshop room
|
||||
const joinRoomOnConnect = () => {
|
||||
console.log("Socket connected:", socketInstance.id);
|
||||
setClientId(socketInstance.id);
|
||||
});
|
||||
|
||||
if (bodyshop.id) {
|
||||
socketInstance.emit("join-bodyshop-room", bodyshop.id);
|
||||
console.log(`Joined bodyshop room: ${bodyshop.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Set up the necessary socket event handlers
|
||||
socketInstance.on("connect", joinRoomOnConnect);
|
||||
|
||||
socketInstance.on("reconnect", (attempt) => {
|
||||
console.log(`Socket reconnected after ${attempt} attempts`);
|
||||
});
|
||||
|
||||
socketInstance.on("bodyshop-message", handleBodyshopMessage);
|
||||
|
||||
socketInstance.on("connect_error", (err) => {
|
||||
console.error("Socket connection error:", err);
|
||||
});
|
||||
@@ -41,7 +56,13 @@ const useSocket = (bodyshop) => {
|
||||
console.log("Socket disconnected");
|
||||
});
|
||||
|
||||
// Clean up on component unmount or when bodyshop changes
|
||||
return () => {
|
||||
if (bodyshop?.id) {
|
||||
socketInstance.emit("leave-bodyshop-room", bodyshop.id);
|
||||
}
|
||||
socketInstance.off("connect", joinRoomOnConnect);
|
||||
socketInstance.off("bodyshop-message", handleBodyshopMessage);
|
||||
socketInstance.disconnect();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -120,27 +120,6 @@ export function Manage({ conflict, bodyshop }) {
|
||||
});
|
||||
}, [t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket && bodyshop && bodyshop.id) {
|
||||
const handleConnect = () => {
|
||||
socket.emit("join-bodyshop-room", bodyshop.id);
|
||||
};
|
||||
|
||||
const handleBodyshopMessage = (message) => {
|
||||
console.log(`Received message for bodyshop ${bodyshop.id}:`, message);
|
||||
};
|
||||
|
||||
socket.on("connect", handleConnect);
|
||||
socket.on("bodyshop-message", handleBodyshopMessage);
|
||||
|
||||
return () => {
|
||||
socket.emit("leave-bodyshop-room", bodyshop.id);
|
||||
socket.off("connect", handleConnect);
|
||||
socket.off("bodyshop-message", handleBodyshopMessage);
|
||||
};
|
||||
}
|
||||
}, [socket, bodyshop]);
|
||||
|
||||
const AppRouteTable = (
|
||||
<Suspense
|
||||
fallback={
|
||||
|
||||
@@ -4240,6 +4240,63 @@
|
||||
- active:
|
||||
_eq: true
|
||||
event_triggers:
|
||||
- name: job_modified
|
||||
definition:
|
||||
enable_manual: false
|
||||
update:
|
||||
columns:
|
||||
- clm_no
|
||||
- v_make_desc
|
||||
- date_next_contact
|
||||
- status
|
||||
- employee_csr
|
||||
- employee_prep
|
||||
- clm_total
|
||||
- suspended
|
||||
- employee_body
|
||||
- ro_number
|
||||
- actual_in
|
||||
- ownr_co_nm
|
||||
- v_model_yr
|
||||
- comment
|
||||
- job_totals
|
||||
- v_vin
|
||||
- ownr_fn
|
||||
- scheduled_completion
|
||||
- special_coverage_policy
|
||||
- v_color
|
||||
- ca_gst_registrant
|
||||
- scheduled_delivery
|
||||
- actual_delivery
|
||||
- actual_completion
|
||||
- kanbanparent
|
||||
- est_ct_fn
|
||||
- employee_refinish
|
||||
- ownr_ph1
|
||||
- date_last_contacted
|
||||
- alt_transport
|
||||
- inproduction
|
||||
- est_ct_ln
|
||||
- production_vars
|
||||
- category
|
||||
- v_model_desc
|
||||
- date_invoiced
|
||||
- est_co_nm
|
||||
- ownr_ln
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/job/job-updated'
|
||||
version: 2
|
||||
- name: job_status_transition
|
||||
definition:
|
||||
enable_manual: true
|
||||
|
||||
@@ -210,7 +210,8 @@ const applyRedisHelpers = (pubClient, app) => {
|
||||
getMultipleSessionData,
|
||||
setMultipleFromArraySessionData,
|
||||
addItemToEndOfList,
|
||||
addItemToBeginningOfList
|
||||
addItemToBeginningOfList,
|
||||
pubClient
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
@@ -222,7 +223,8 @@ const applyRedisHelpers = (pubClient, app) => {
|
||||
getMultipleSessionData,
|
||||
setMultipleFromArraySessionData,
|
||||
addItemToEndOfList,
|
||||
addItemToBeginningOfList
|
||||
addItemToBeginningOfList,
|
||||
pubClient
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
461
server/job/job-updated.js
Normal file
461
server/job/job-updated.js
Normal file
@@ -0,0 +1,461 @@
|
||||
const { isObject } = require("lodash");
|
||||
|
||||
const jobUpdated = async (req, res) => {
|
||||
const { io, logger } = req;
|
||||
|
||||
console.dir(req.body, { depth: null });
|
||||
|
||||
if (!req?.body?.event?.data?.new || !isObject(req?.body?.event?.data?.new)) {
|
||||
logger.log("job-update-error", "ERROR", req.user?.email, null, {
|
||||
message: `Malformed Job Update request sent from Hasura`,
|
||||
body: req?.body
|
||||
});
|
||||
|
||||
return res.json({
|
||||
status: "error",
|
||||
message: `Malformed Job Update request sent from Hasura`
|
||||
});
|
||||
}
|
||||
|
||||
logger.log("job-update", "INFO", req.user?.email, null, {
|
||||
message: `Job updated event received from Hasura`,
|
||||
jobid: req?.body?.event?.data?.new?.id
|
||||
});
|
||||
|
||||
const updatedJob = req.body.event.data.new;
|
||||
const bodyshopID = updatedJob.shopid;
|
||||
|
||||
// Emit the job-updated event only to the room corresponding to the bodyshop
|
||||
io.to(bodyshopID).emit("job-updated", updatedJob);
|
||||
|
||||
return res.json({ message: "Job updated and event emitted" });
|
||||
};
|
||||
|
||||
module.exports = jobUpdated;
|
||||
|
||||
// {
|
||||
// actual_completion: null,
|
||||
// actual_delivery: null,
|
||||
// actual_in: '2024-09-05T23:04:31.439+00:00',
|
||||
// adj_g_disc: 0,
|
||||
// adj_strdis: 0,
|
||||
// adj_towdis: 0,
|
||||
// adjustment_bottom_line: null,
|
||||
// agt_addr1: null,
|
||||
// agt_addr2: null,
|
||||
// agt_city: null,
|
||||
// agt_co_id: null,
|
||||
// agt_co_nm: null,
|
||||
// agt_ct_fn: null,
|
||||
// agt_ct_ln: null,
|
||||
// agt_ct_ph: null,
|
||||
// agt_ct_phx: null,
|
||||
// agt_ctry: null,
|
||||
// agt_ea: null,
|
||||
// agt_fax: null,
|
||||
// agt_faxx: null,
|
||||
// agt_lic_no: null,
|
||||
// agt_ph1: null,
|
||||
// agt_ph1x: null,
|
||||
// agt_ph2: null,
|
||||
// agt_ph2x: null,
|
||||
// agt_st: null,
|
||||
// agt_zip: null,
|
||||
// alt_transport: 'No car',
|
||||
// area_of_damage: { impact1: '19', impact2: '0102' },
|
||||
// asgn_date: '2024-08-06',
|
||||
// asgn_no: '72361166-99',
|
||||
// asgn_type: null,
|
||||
// auto_add_ats: false,
|
||||
// ca_bc_pvrt: null,
|
||||
// ca_customer_gst: 500,
|
||||
// ca_gst_registrant: true,
|
||||
// cat_no: null,
|
||||
// category: null,
|
||||
// cieca_pfl: {},
|
||||
// cieca_pfo: {},
|
||||
// cieca_pft: {},
|
||||
// cieca_stl: {
|
||||
// data: [
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object], [Object], [Object],
|
||||
// [Object]
|
||||
// ]
|
||||
// },
|
||||
// cieca_ttl: {
|
||||
// data: {
|
||||
// g_aa_amt: 0,
|
||||
// g_bett_amt: 0,
|
||||
// g_cust_amt: 0,
|
||||
// g_ded_amt: 0,
|
||||
// g_rpd_amt: 0,
|
||||
// g_tax: 854.37,
|
||||
// g_ttl_amt: 13721.35,
|
||||
// g_ttl_disc: 0,
|
||||
// g_upd_amt: 0,
|
||||
// gst_amt: 612.72,
|
||||
// n_ttl_amt: 13721.35,
|
||||
// prev_net: 0,
|
||||
// supp_amt: 958.53
|
||||
// }
|
||||
// },
|
||||
// ciecaid: '2273515',
|
||||
// class: null,
|
||||
// clm_addr1: null,
|
||||
// clm_addr2: null,
|
||||
// clm_city: null,
|
||||
// clm_ct_fn: null,
|
||||
// clm_ct_ln: null,
|
||||
// clm_ct_ph: null,
|
||||
// clm_ct_phx: null,
|
||||
// clm_ctry: null,
|
||||
// clm_ea: null,
|
||||
// clm_fax: null,
|
||||
// clm_faxx: null,
|
||||
// clm_no: '72361166-99',
|
||||
// clm_ofc_id: null,
|
||||
// clm_ofc_nm: null,
|
||||
// clm_ph1: null,
|
||||
// clm_ph1x: null,
|
||||
// clm_ph2: null,
|
||||
// clm_ph2x: null,
|
||||
// clm_st: null,
|
||||
// clm_title: null,
|
||||
// clm_total: 13721.3,
|
||||
// clm_zip: null,
|
||||
// comment: null,
|
||||
// completed_tasks: [],
|
||||
// converted: true,
|
||||
// created_at: '2024-09-05T22:50:11.37571+00:00',
|
||||
// cust_pr: 'C',
|
||||
// date_estimated: null,
|
||||
// date_exported: null,
|
||||
// date_invoiced: null,
|
||||
// date_last_contacted: null,
|
||||
// date_lost_sale: null,
|
||||
// date_next_contact: '2024-09-07T23:04:31.439+00:00',
|
||||
// date_open: '2024-09-05T22:50:12.083+00:00',
|
||||
// date_rentalresp: null,
|
||||
// date_repairstarted: null,
|
||||
// date_scheduled: '2024-09-05T23:04:12.182+00:00',
|
||||
// date_towin: null,
|
||||
// date_void: null,
|
||||
// ded_amt: 0,
|
||||
// ded_note: null,
|
||||
// ded_status: 'Y',
|
||||
// deliverchecklist: null,
|
||||
// depreciation_taxes: 0,
|
||||
// dms_allocation: null,
|
||||
// driveable: true,
|
||||
// employee_body: null,
|
||||
// employee_csr: null,
|
||||
// employee_prep: null,
|
||||
// employee_refinish: null,
|
||||
// est_addr1: null,
|
||||
// est_addr2: null,
|
||||
// est_city: null,
|
||||
// est_co_nm: null,
|
||||
// est_ct_fn: 'Monique',
|
||||
// est_ct_ln: 'Bruneau',
|
||||
// est_ctry: null,
|
||||
// est_ea: 'MONIQUE@STCAUTO.COM',
|
||||
// est_ph1: null,
|
||||
// est_st: null,
|
||||
// est_zip: null,
|
||||
// federal_tax_rate: 0.05,
|
||||
// g_bett_amt: 0,
|
||||
// id: '344fe1e0-4e0c-4f7b-9728-659c850d8192',
|
||||
// inproduction: true,
|
||||
// ins_addr1: null,
|
||||
// ins_addr2: null,
|
||||
// ins_city: null,
|
||||
// ins_co_id: null,
|
||||
// ins_co_nm: 'ICBC',
|
||||
// ins_ct_fn: null,
|
||||
// ins_ct_ln: null,
|
||||
// ins_ct_ph: null,
|
||||
// ins_ct_phx: null,
|
||||
// ins_ctry: null,
|
||||
// ins_ea: null,
|
||||
// ins_fax: null,
|
||||
// ins_faxx: null,
|
||||
// ins_memo: null,
|
||||
// ins_ph1: null,
|
||||
// ins_ph1x: null,
|
||||
// ins_ph2: null,
|
||||
// ins_ph2x: null,
|
||||
// ins_st: null,
|
||||
// ins_title: null,
|
||||
// ins_zip: null,
|
||||
// insd_addr1: null,
|
||||
// insd_addr2: null,
|
||||
// insd_city: null,
|
||||
// insd_co_nm: null,
|
||||
// insd_ctry: null,
|
||||
// insd_ea: null,
|
||||
// insd_fax: null,
|
||||
// insd_faxx: null,
|
||||
// insd_fn: null,
|
||||
// insd_ln: null,
|
||||
// insd_ph1: null,
|
||||
// insd_ph1x: null,
|
||||
// insd_ph2: null,
|
||||
// insd_ph2x: null,
|
||||
// insd_st: null,
|
||||
// insd_title: null,
|
||||
// insd_zip: null,
|
||||
// intakechecklist: {
|
||||
// addToProduction: true,
|
||||
// allow_text_message: false,
|
||||
// completed_at: '2024-09-05T23:04:31.439Z',
|
||||
// completed_by: 'allan@imex.dev',
|
||||
// form: [ [Object], [Object], [Object], [Object], [Object], [Object] ],
|
||||
// production_vars: {},
|
||||
// scheduled_completion: '2024-09-25T23:03:55.285Z',
|
||||
// scheduled_delivery: null
|
||||
// },
|
||||
// invoice_allocation: null,
|
||||
// invoice_date: null,
|
||||
// invoice_final_note: null,
|
||||
// iouparent: null,
|
||||
// job_totals: {
|
||||
// additional: {
|
||||
// additionalCostItems: [Array],
|
||||
// additionalCosts: [Object],
|
||||
// adjustments: [Object],
|
||||
// pvrt: [Object],
|
||||
// shipping: [Object],
|
||||
// storage: [Object],
|
||||
// total: [Object],
|
||||
// towing: [Object]
|
||||
// },
|
||||
// parts: { parts: [Object], sublets: [Object] },
|
||||
// rates: {
|
||||
// la1: [Object],
|
||||
// la2: [Object],
|
||||
// la3: [Object],
|
||||
// la4: [Object],
|
||||
// laa: [Object],
|
||||
// lab: [Object],
|
||||
// lad: [Object],
|
||||
// lae: [Object],
|
||||
// laf: [Object],
|
||||
// lag: [Object],
|
||||
// lam: [Object],
|
||||
// lar: [Object],
|
||||
// las: [Object],
|
||||
// lau: [Object],
|
||||
// mapa: [Object],
|
||||
// mash: [Object],
|
||||
// rates_subtotal: [Object],
|
||||
// subtotal: [Object]
|
||||
// },
|
||||
// totals: {
|
||||
// custPayable: [Object],
|
||||
// federal_tax: [Object],
|
||||
// local_tax: [Object],
|
||||
// net_repairs: [Object],
|
||||
// statePartsTax: [Object],
|
||||
// state_tax: [Object],
|
||||
// subtotal: [Object],
|
||||
// total_repairs: [Object]
|
||||
// }
|
||||
// },
|
||||
// kanbanparent: '-1',
|
||||
// kmin: null,
|
||||
// kmout: null,
|
||||
// labor_rate_desc: 'EST',
|
||||
// labor_rate_id: null,
|
||||
// lbr_adjustments: {},
|
||||
// local_tax_rate: null,
|
||||
// loss_cat: 'U',
|
||||
// loss_date: '2024-06-19',
|
||||
// loss_desc: 'Animal',
|
||||
// loss_of_use: null,
|
||||
// loss_type: 'A',
|
||||
// lost_sale_reason: null,
|
||||
// materials: {
|
||||
// mapa: { cal_maxdlr: 9999.99, cal_opcode: 'OP13' },
|
||||
// mash: { cal_maxdlr: 9999.99, cal_opcode: 'OP13' }
|
||||
// },
|
||||
// other_amount_payable: null,
|
||||
// owner_owing: 500,
|
||||
// ownerid: 'f392b24f-e828-47fa-bd17-2e5af8493147',
|
||||
// ownr_addr1: null,
|
||||
// ownr_addr2: null,
|
||||
// ownr_city: null,
|
||||
// ownr_co_nm: null,
|
||||
// ownr_ctry: null,
|
||||
// ownr_ea: null,
|
||||
// ownr_fax: null,
|
||||
// ownr_faxx: null,
|
||||
// ownr_fn: 'Neil',
|
||||
// ownr_ln: 'Leslie',
|
||||
// ownr_ph1: null,
|
||||
// ownr_ph1x: null,
|
||||
// ownr_ph2: null,
|
||||
// ownr_ph2x: null,
|
||||
// ownr_st: null,
|
||||
// ownr_title: null,
|
||||
// ownr_zip: null,
|
||||
// parts_tax_rates: {
|
||||
// CCC: {},
|
||||
// CCD: {},
|
||||
// CCDR: {},
|
||||
// CCF: {},
|
||||
// CCM: {},
|
||||
// PAA: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAA'
|
||||
// },
|
||||
// PAC: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAC'
|
||||
// },
|
||||
// PAG: {},
|
||||
// PAL: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAL'
|
||||
// },
|
||||
// PAM: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAM'
|
||||
// },
|
||||
// PAN: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAN'
|
||||
// },
|
||||
// PAO: {},
|
||||
// PAP: {},
|
||||
// PAR: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAR'
|
||||
// },
|
||||
// PAS: {
|
||||
// prt_discp: 0,
|
||||
// prt_mktyp: false,
|
||||
// prt_mkupp: 0,
|
||||
// prt_tax_in: true,
|
||||
// prt_tax_rt: 0.07,
|
||||
// prt_type: 'PAS'
|
||||
// },
|
||||
// PASL: {}
|
||||
// },
|
||||
// pay_amt: 0,
|
||||
// pay_chknm: '0',
|
||||
// pay_date: null,
|
||||
// pay_type: null,
|
||||
// payee_nms: null,
|
||||
// plate_no: 'B15717',
|
||||
// plate_st: 'MB',
|
||||
// po_number: null,
|
||||
// policy_no: '18292147',
|
||||
// production_vars: { note: 'testtsdsadsasdasdsadsdsadsd' },
|
||||
// qb_multiple_payers: null,
|
||||
// queued_for_parts: false,
|
||||
// rate_ats: null,
|
||||
// rate_la1: 0,
|
||||
// rate_la2: 0,
|
||||
// rate_la3: null,
|
||||
// rate_la4: 0,
|
||||
// rate_laa: 0,
|
||||
// rate_lab: 88.43,
|
||||
// rate_lad: null,
|
||||
// rate_lae: null,
|
||||
// rate_laf: 97.06,
|
||||
// rate_lag: 88.43,
|
||||
// rate_lam: 103.5,
|
||||
// rate_lar: 88.43,
|
||||
// rate_las: 88.43,
|
||||
// rate_lau: 0,
|
||||
// rate_ma2s: 0,
|
||||
// rate_ma2t: 0,
|
||||
// rate_ma3s: 0,
|
||||
// rate_mabl: null,
|
||||
// rate_macs: 0,
|
||||
// rate_mahw: 49.7,
|
||||
// rate_mapa: 56.67,
|
||||
// rate_mash: 7.23,
|
||||
// rate_matd: null,
|
||||
// referral_source: null,
|
||||
// referral_source_extra: null,
|
||||
// regie_number: null,
|
||||
// remove_from_ar: false,
|
||||
// ro_number: '10002',
|
||||
// scheduled_completion: '2024-09-25T23:03:55.285+00:00',
|
||||
// scheduled_delivery: null,
|
||||
// scheduled_in: '2024-09-05T22:00:55.2+00:00',
|
||||
// selling_dealer: null,
|
||||
// selling_dealer_contact: null,
|
||||
// servicing_dealer: null,
|
||||
// servicing_dealer_contact: null,
|
||||
// shopid: 'bfec8c8c-b7f1-49e0-be4c-524455f4e582',
|
||||
// special_coverage_policy: false,
|
||||
// state_tax_rate: null,
|
||||
// status: 'Repair Plan',
|
||||
// storage_payable: null,
|
||||
// suspended: false,
|
||||
// tax_lbr_rt: 0.07,
|
||||
// tax_levies_rt: 0.07,
|
||||
// tax_paint_mat_rt: 0.07,
|
||||
// tax_predis: 0,
|
||||
// tax_prethr: 0.07,
|
||||
// tax_pstthr: 0,
|
||||
// tax_registration_number: null,
|
||||
// tax_shop_mat_rt: 0.07,
|
||||
// tax_str_rt: 0.07,
|
||||
// tax_sub_rt: 0.07,
|
||||
// tax_thramt: 0,
|
||||
// tax_tow_rt: 0,
|
||||
// theft_ind: false,
|
||||
// tlos_ind: false,
|
||||
// towin: false,
|
||||
// towing_payable: null,
|
||||
// unit_number: null,
|
||||
// updated_at: '2024-09-12T19:25:48.492299+00:00',
|
||||
// v_color: 'Polished Metal Metal',
|
||||
// v_make_desc: 'Honda',
|
||||
// v_model_desc: 'Civic',
|
||||
// v_model_yr: '17',
|
||||
// v_vin: 'SHHFK7H20HU306419',
|
||||
// vehicleid: '811ba420-e9db-430b-b341-22e197c8dd0e',
|
||||
// voided: false
|
||||
// }
|
||||
// I have been hit, wewet
|
||||
// job-transition-update-result {
|
||||
// type: 'DEBUG',
|
||||
// env: 'development',
|
||||
// user: null,
|
||||
// record: '344fe1e0-4e0c-4f7b-9728-659c850d8192',
|
||||
// insert_transitions_one: { id: '88e73841-247d-4e31-b31c-f1b2ca8364d9' },
|
||||
// update_transitions: { affected_rows: 1 }
|
||||
// }
|
||||
@@ -14,3 +14,4 @@ exports.costing = require("./job-costing").JobCosting;
|
||||
exports.costingmulti = require("./job-costing").JobCostingMulti;
|
||||
exports.statustransition = require("./job-status-transition").statustransition;
|
||||
exports.lifecycle = require("./job-lifecycle");
|
||||
exports.jobUpdated = require("./job-updated");
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const job = require("../job/job");
|
||||
const ppc = require("../ccc/partspricechange");
|
||||
const { partsScan } = require("../parts-scan/parts-scan");
|
||||
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const { totals, statustransition, totalsSsu, costing, lifecycle, costingmulti } = require("../job/job");
|
||||
const { totals, statustransition, totalsSsu, costing, lifecycle, costingmulti, jobUpdated } = require("../job/job");
|
||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||
|
||||
router.post("/totals", validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, totals);
|
||||
@@ -16,5 +15,5 @@ router.post("/lifecycle", validateFirebaseIdTokenMiddleware, withUserGraphQLClie
|
||||
router.post("/costingmulti", validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, costingmulti);
|
||||
router.post("/partsscan", validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, partsScan);
|
||||
router.post("/ppc", validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, ppc.generatePpc);
|
||||
|
||||
router.post("/job-updated", eventAuthorizationMiddleware, jobUpdated);
|
||||
module.exports = router;
|
||||
|
||||
@@ -3,7 +3,14 @@ require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
const { io, setSessionData, clearSessionData, getMultipleSessionData, addItemToEndOfList } = require("../../server");
|
||||
const {
|
||||
io,
|
||||
setSessionData,
|
||||
clearSessionData,
|
||||
getMultipleSessionData,
|
||||
addItemToEndOfList,
|
||||
pubClient
|
||||
} = require("../../server");
|
||||
|
||||
const { admin } = require("../firebase/firebase-handler");
|
||||
const logger = require("../utils/logger");
|
||||
@@ -51,6 +58,9 @@ async function registerSocketEvents(socket) {
|
||||
// Register event to clear DMS session
|
||||
registerDmsClearSessionEvent(socket);
|
||||
|
||||
// Register Production Board events
|
||||
registerProductionBoardEvents(socket);
|
||||
|
||||
// Handle socket disconnection
|
||||
socket.on("disconnect", async () => {
|
||||
await createLogEvent(socket, "DEBUG", `User disconnected.`);
|
||||
@@ -106,6 +116,8 @@ function registerPbsArEvents(socket) {
|
||||
});
|
||||
}
|
||||
|
||||
function registerProductionBoardEvents(socket) {}
|
||||
|
||||
// PBS AP-specific socket events
|
||||
function registerPbsApEvents(socket) {
|
||||
socket.on("pbs-calculate-allocations-ap", async (billids, callback) => {
|
||||
@@ -125,15 +137,35 @@ function registerPbsApEvents(socket) {
|
||||
});
|
||||
}
|
||||
|
||||
const getRedisKeyForSocket = (socketId) => `socket:${socketId}:rooms`;
|
||||
|
||||
// Room management and broadcasting events
|
||||
function registerRoomAndBroadcastEvents(socket) {
|
||||
// Rejoin rooms on reconnect
|
||||
pubClient.lRange(getRedisKeyForSocket(socket.id), 0, -1, (err, rooms) => {
|
||||
if (rooms && rooms.length > 0) {
|
||||
rooms.forEach((room) => {
|
||||
socket.join(room);
|
||||
createLogEvent(socket, "DEBUG", `Client rejoined bodyshop room: ${room}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("join-bodyshop-room", async (bodyshopUUID) => {
|
||||
socket.join(bodyshopUUID);
|
||||
|
||||
// Store room in Redis
|
||||
pubClient.rPush(getRedisKeyForSocket(socket.id), bodyshopUUID);
|
||||
|
||||
await createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${bodyshopUUID}`);
|
||||
});
|
||||
|
||||
socket.on("leave-bodyshop-room", async (bodyshopUUID) => {
|
||||
socket.leave(bodyshopUUID);
|
||||
|
||||
// Remove room from Redis
|
||||
pubClient.lRem(getRedisKeyForSocket(socket.id), 0, bodyshopUUID);
|
||||
|
||||
await createLogEvent(socket, "DEBUG", `Client left bodyshop room: ${bodyshopUUID}`);
|
||||
});
|
||||
|
||||
@@ -141,8 +173,12 @@ function registerRoomAndBroadcastEvents(socket) {
|
||||
io.to(bodyshopUUID).emit("bodyshop-message", message);
|
||||
await createLogEvent(socket, "INFO", `Broadcast message to bodyshop ${bodyshopUUID}`);
|
||||
});
|
||||
}
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
// Optional: Cleanup Redis entry on disconnect if needed
|
||||
createLogEvent(socket, "DEBUG", `Client disconnected: ${socket.id}`);
|
||||
});
|
||||
}
|
||||
// DMS session clearing event
|
||||
function registerDmsClearSessionEvent(socket) {
|
||||
socket.on("clear-dms-session", async () => {
|
||||
|
||||
Reference in New Issue
Block a user