From 8f04c5a12caacf4e0b2a5e281461b1685f7a2ede Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 22 Aug 2023 09:15:10 -0700 Subject: [PATCH 1/4] IO-2368 Successful Export Notification for QBO --- .../jobs-close-export-button.component.jsx | 14 ++++++++++---- .../jobs-export-all-button.component.jsx | 10 ++++++++-- .../payable-export-all-button.component.jsx | 17 +++++++++++------ .../payable-export-button.component.jsx | 16 +++++++++++----- .../payment-export-button.component.jsx | 9 ++++++++- .../payments-export-all-button.component.jsx | 9 ++++++++- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx index 1c95b9975..f18bd25d7 100644 --- a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx +++ b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx @@ -4,16 +4,15 @@ import axios from "axios"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { useHistory } from "react-router-dom"; import { createStructuredSelector } from "reselect"; -import { auth } from "../../firebase/firebase.utils"; +import { auth, logImEXEvent } from "../../firebase/firebase.utils"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; -import { logImEXEvent } from "../../firebase/firebase.utils"; -import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; -import { useHistory } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -173,6 +172,13 @@ export function JobsCloseExportButton({ }); } } + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "jobsuccessexport", + message: t("jobs.successes.exported"), + }); + } if (setSelectedJobs) { setSelectedJobs((selectedJobs) => { return selectedJobs.filter((i) => i !== jobId); diff --git a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx index 750a5188b..2f5878351 100644 --- a/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx +++ b/client/src/components/jobs-export-all-button/jobs-export-all-button.component.jsx @@ -169,14 +169,20 @@ export function JobsExportAllButton({ }); } } + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "jobsuccessexport", + message: t("jobs.successes.exported"), + }); + } } }) ); - if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); if (!!completedCallback) completedCallback([]); if (!!loadingCallback) loadingCallback(false); - + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); setLoading(false); }; diff --git a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx index 7aa13f5e3..7596dea7c 100644 --- a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx +++ b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx @@ -1,20 +1,19 @@ import { useMutation } from "@apollo/client"; import { Button, notification } from "antd"; import axios from "axios"; +import _ from "lodash"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { Link } from "react-router-dom"; import { createStructuredSelector } from "reselect"; -import { auth } from "../../firebase/firebase.utils"; +import { auth, logImEXEvent } from "../../firebase/firebase.utils"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_BILLS } from "../../graphql/bills.queries"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; -import { logImEXEvent } from "../../firebase/firebase.utils"; -import _ from "lodash"; -import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; -import { Link } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -164,6 +163,13 @@ export function PayableExportAll({ }); } } + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "billsuccessexport", + message: t("bills.successes.exported"), + }); + } } })() ); @@ -173,7 +179,6 @@ export function PayableExportAll({ if (!!completedCallback) completedCallback([]); if (!!loadingCallback) loadingCallback(false); if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); - setLoading(false); }; diff --git a/client/src/components/payable-export-button/payable-export-button.component.jsx b/client/src/components/payable-export-button/payable-export-button.component.jsx index de0d3efdf..fc5254073 100644 --- a/client/src/components/payable-export-button/payable-export-button.component.jsx +++ b/client/src/components/payable-export-button/payable-export-button.component.jsx @@ -4,16 +4,15 @@ import axios from "axios"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; +import { Link } from "react-router-dom"; import { createStructuredSelector } from "reselect"; -import { auth } from "../../firebase/firebase.utils"; +import { auth, logImEXEvent } from "../../firebase/firebase.utils"; +import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_BILLS } from "../../graphql/bills.queries"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; -import { logImEXEvent } from "../../firebase/firebase.utils"; -import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; -import { Link } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -167,7 +166,13 @@ export function PayableExportButton({ }); } } - if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "billsuccessexport", + message: t("bills.successes.exported"), + }); + } if (setSelectedBills) { setSelectedBills((selectedBills) => { @@ -177,6 +182,7 @@ export function PayableExportButton({ } if (!!loadingCallback) loadingCallback(false); + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); setLoading(false); }; diff --git a/client/src/components/payment-export-button/payment-export-button.component.jsx b/client/src/components/payment-export-button/payment-export-button.component.jsx index 5e5868579..471e2389e 100644 --- a/client/src/components/payment-export-button/payment-export-button.component.jsx +++ b/client/src/components/payment-export-button/payment-export-button.component.jsx @@ -172,8 +172,15 @@ export function PaymentExportButton({ }); } } - if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "paymentsuccessexport", + message: t("payments.successes.exported"), + }); + } if (!!loadingCallback) loadingCallback(false); + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch(); setLoading(false); }; diff --git a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx index 3f9d7fffd..e4218cc3b 100644 --- a/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx +++ b/client/src/components/payments-export-all-button/payments-export-all-button.component.jsx @@ -25,7 +25,7 @@ export function PaymentsExportAllButton({ disabled, loadingCallback, completedCallback, - refetch + refetch, }) { const { t } = useTranslation(); const [updatePayments] = useMutation(UPDATE_PAYMENTS); @@ -150,6 +150,13 @@ export function PaymentsExportAllButton({ }); } } + if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { + notification.open({ + type: "success", + key: "paymentsuccessexport", + message: t("payments.successes.exported"), + }); + } } })() ); From 9319f492dd53a0f34919af2bdca3d8dd765ab415 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 22 Aug 2023 13:29:28 -0700 Subject: [PATCH 2/4] IO-2385 Enabled Emailing of TimeTicket & Attendance from Tech Console Modify print center so that it will/wont display email option if technician is set and production board detail so that it wont display the remove from production / add to scoreboard if technician is set --- .../job-3rd-party-modal.component.jsx | 8 +++-- .../print-center-item.component.jsx | 34 ++++++++++++------- .../production-list-detail.component.jsx | 31 ++++++++++------- .../tech-job-print-tickets.component.jsx | 25 +++++++++++--- client/src/pages/tech/tech.page.component.jsx | 12 ++++--- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 8 files changed, 77 insertions(+), 36 deletions(-) diff --git a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx index af6c4fc3d..b0657d270 100644 --- a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx +++ b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR } from "../../graphql/vendors.queries"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; @@ -13,13 +14,14 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + technician: selectTechnician, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); export default connect(mapStateToProps, mapDispatchToProps)(Jobd3RdPartyModal); -export function Jobd3RdPartyModal({ bodyshop, jobId, job }) { +export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) { const [isModalVisible, setIsModalVisible] = useState(false); const { t } = useTranslation(); const [form] = Form.useForm(); @@ -212,7 +214,9 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) { ]} > - {t("parts_orders.labels.email")} + {!technician ? ( + {t("parts_orders.labels.email")} + ) : null} {t("parts_orders.labels.print")} diff --git a/client/src/components/print-center-item/print-center-item.component.jsx b/client/src/components/print-center-item/print-center-item.component.jsx index d1c6b1b58..48bc92291 100644 --- a/client/src/components/print-center-item/print-center-item.component.jsx +++ b/client/src/components/print-center-item/print-center-item.component.jsx @@ -5,11 +5,13 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { setEmailOptions } from "../../redux/email/email.actions"; import { selectPrintCenter } from "../../redux/modals/modals.selectors"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { GenerateDocument } from "../../utils/RenderTemplate"; const mapStateToProps = createStructuredSelector({ printCenterModal: selectPrintCenter, bodyshop: selectBodyshop, + technician: selectTechnician, }); const mapDispatchToProps = (dispatch) => ({ setEmailOptions: (e) => dispatch(setEmailOptions(e)), @@ -22,6 +24,7 @@ export function PrintCenterItemComponent({ id, bodyshop, disabled, + technician, }) { const [loading, setLoading] = useState(false); const { context } = printCenterModal; @@ -44,19 +47,24 @@ export function PrintCenterItemComponent({ {item.title} - { - GenerateDocument( - { - name: item.key, - variables: { id: id }, - }, - { to: context.job && context.job.ownr_ea, subject: item.subject }, - "e", - id - ); - }} - /> + {!technician ? ( + { + GenerateDocument( + { + name: item.key, + variables: { id: id }, + }, + { + to: context.job && context.job.ownr_ea, + subject: item.subject, + }, + "e", + id + ); + }} + /> + ) : null} {loading && } diff --git a/client/src/components/production-list-detail/production-list-detail.component.jsx b/client/src/components/production-list-detail/production-list-detail.component.jsx index ac5b4146c..170b31b46 100644 --- a/client/src/components/production-list-detail/production-list-detail.component.jsx +++ b/client/src/components/production-list-detail/production-list-detail.component.jsx @@ -1,31 +1,33 @@ +import { PrinterFilled } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; -import { Descriptions, Drawer, Space, PageHeader, Button } from "antd"; +import { Button, Descriptions, Drawer, PageHeader, Space } from "antd"; import queryString from "query-string"; import React from "react"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; import { useHistory, useLocation } from "react-router-dom"; +import { createStructuredSelector } from "reselect"; import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries"; +import { setModalContext } from "../../redux/modals/modals.actions"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { DateFormatter } from "../../utils/DateFormatter"; import AlertComponent from "../alert/alert.component"; import StartChatButton from "../chat-open-button/chat-open-button.component"; +import JobAtChange from "../job-at-change/job-at-change.component"; import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component"; import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component"; import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component"; import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container"; -import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; -import ProductionRemoveButton from "../production-remove-button/production-remove-button.component"; -import JobAtChange from "../job-at-change/job-at-change.component"; -import { PrinterFilled } from "@ant-design/icons"; -import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { setModalContext } from "../../redux/modals/modals.actions"; import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component"; -import { selectBodyshop } from "../../redux/user/user.selectors"; +import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; +import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; +import ProductionRemoveButton from "../production-remove-button/production-remove-button.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, + technician: selectTechnician, }); const mapDispatchToProps = (dispatch) => ({ setPrintCenterContext: (context) => @@ -40,6 +42,7 @@ export function ProductionListDetail({ bodyshop, jobs, setPrintCenterContext, + technician, }) { const search = queryString.parse(useLocation().search); const history = useHistory(); @@ -66,7 +69,9 @@ export function ProductionListDetail({ title={theJob.ro_number} extra={ - {" "} + {!technician ? ( + + ) : null} - + {!technician ? ( + + ) : null} } /> diff --git a/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx index 1cec8dede..b96a1d062 100644 --- a/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx +++ b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx @@ -1,4 +1,4 @@ -import { Button, Card, DatePicker, Form, Popover, Space } from "antd"; +import { Button, Card, DatePicker, Form, Popover, Radio, Space } from "antd"; import moment from "moment"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -69,7 +69,7 @@ export function TechJobPrintTickets({ technician, event, attendacePrint }) { ? Templates.attendance_employee.subject : Templates.timetickets_employee.subject, }, - "p" + values.sendby // === "email" ? "e" : "p" ); } catch (error) { console.log(error); @@ -99,10 +99,25 @@ export function TechJobPrintTickets({ technician, event, attendacePrint }) { format={"MM/DD/YYYY"} /> - + + {() => { + return ( + + + {t("general.labels.email")} + {t("general.labels.print")} + + + ); + }} + ); diff --git a/client/src/pages/tech/tech.page.component.jsx b/client/src/pages/tech/tech.page.component.jsx index 50219f72d..5808ae84c 100644 --- a/client/src/pages/tech/tech.page.component.jsx +++ b/client/src/pages/tech/tech.page.component.jsx @@ -1,21 +1,24 @@ import { BackTop, Layout } from "antd"; -import React, { lazy, Suspense, useEffect } from "react"; +import React, { Suspense, lazy, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { Redirect, Route, Switch } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import ErrorBoundary from "../../components/error-boundary/error-boundary.component"; +import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import TechHeader from "../../components/tech-header/tech-header.component"; import TechSider from "../../components/tech-sider/tech-sider.component"; -import { selectTechnician } from "../../redux/tech/tech.selectors"; -import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component"; -import "./tech.page.styles.scss"; import UpdateAlert from "../../components/update-alert/update-alert.component"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; +import "./tech.page.styles.scss"; const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container") ); +const EmailOverlayContainer = lazy(() => + import("../../components/email-overlay/email-overlay.container.jsx") +); const PrintCenterModalContainer = lazy(() => import("../../components/print-center-modal/print-center-modal.container") ); @@ -69,6 +72,7 @@ export function TechPage({ technician, match }) { > + Date: Fri, 25 Aug 2023 10:23:22 -0700 Subject: [PATCH 3/4] Resolve manual appointment old data after submission. --- .../schedule-manual-event/schedule-manual-event.component.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx b/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx index c36d3549d..ffba05c31 100644 --- a/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx +++ b/client/src/components/schedule-manual-event/schedule-manual-event.component.jsx @@ -59,11 +59,12 @@ export function ScheduleManualEvent({ bodyshop, event }) { refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"], }); } + form.resetFields(); + setVisibility(false); } catch (error) { console.log(error); } finally { setLoading(false); - setVisibility(false); } }; From 74a0b78a71e6b2d3df7ac9b389fc32dd6398f938 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Fri, 25 Aug 2023 11:05:16 -0700 Subject: [PATCH 4/4] IO-2389 Metadata to ExportLog --- server/accounting/pbs/pbs-job-export.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/accounting/pbs/pbs-job-export.js b/server/accounting/pbs/pbs-job-export.js index cfbe71a19..dd63a975d 100644 --- a/server/accounting/pbs/pbs-job-export.js +++ b/server/accounting/pbs/pbs-job-export.js @@ -643,6 +643,7 @@ async function InsertAccountPostingData(socket) { wips.push(item); }); + socket.transWips = wips; const { data: AccountPostingChange } = await axios.post( PBS_ENDPOINTS.AccountingPostingChange, @@ -697,6 +698,7 @@ async function MarkJobExported(socket, jobid) { jobid: jobid, successful: true, useremail: socket.user.email, + metadata: socket.transWips, }, bill: { exported: true,