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 { createStructuredSelector } from "reselect"; import { auth, logImEXEvent } from "../../firebase/firebase.utils"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { UPDATE_JOBS } from "../../graphql/jobs.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import client from "../../utils/GraphQLClient"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, currentUser: selectCurrentUser }); const mapDispatchToProps = (dispatch) => ({ insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type })) }); function updateJobCache(items) { client.cache.modify({ id: "ROOT_QUERY", fields: { jobs(existingJobs = []) { return existingJobs.filter((jobRef) => jobRef.__ref.includes(items) === false); } } }); } export function JobsExportAllButton({ bodyshop, currentUser, jobIds, disabled, loadingCallback, completedCallback, refetch, insertAuditTrail }) { const { t } = useTranslation(); const [updateJob] = useMutation(UPDATE_JOBS); const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [loading, setLoading] = useState(false); const handleQbxml = async () => { logImEXEvent("jobs_export_all"); let PartnerResponse; setLoading(true); if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { PartnerResponse = await axios.post(`/qbo/receivables`, { jobIds: jobIds, elgen: true }); } else { let QbXmlResponse; try { QbXmlResponse = await axios.post( "/accounting/qbxml/receivables", { jobIds: jobIds }, { headers: { Authorization: `Bearer ${await auth.currentUser.getIdToken()}` } } ); } catch (error) { console.log("Error getting QBXML from Server.", error); notification["error"]({ message: t("jobs.errors.exporting", { error: "Unable to retrieve QBXML. " + JSON.stringify(error.message) }) }); setLoading(false); return; } try { PartnerResponse = await axios.post( "http://localhost:1337/qb/", // "http://609feaeae986.ngrok.io/qb/", QbXmlResponse.data, { headers: { Authorization: `Bearer ${await auth.currentUser.getIdToken()}` } } ); } catch (error) { console.log("Error connecting to quickbooks or partner.", error); notification["error"]({ message: t("jobs.errors.exporting-partner") }); setLoading(false); return; } } console.log("PartnerResponse", PartnerResponse); const groupedData = _.groupBy(PartnerResponse.data, bodyshop.accountingconfig.qbo ? "jobid" : "id"); await Promise.all( Object.keys(groupedData).map(async (key) => { //Check to see if any of them failed. If they didn't don't execute the update. const failedTransactions = groupedData[key].filter((r) => !r.success); const successfulTransactions = groupedData[key].filter((r) => r.success); if (failedTransactions.length > 0) { //Uh oh. At least one was no good. failedTransactions.forEach((ft) => { notification.open({ // key: "failedexports", type: "error", message: t("jobs.errors.exporting", { error: ft.errorMessage || "" }) }); //Call is not awaited as it is not critical to finish before proceeding. }); if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { //QBO Logs are handled server side. await insertExportLog({ variables: { logs: [ { bodyshopid: bodyshop.id, jobid: key, successful: false, message: JSON.stringify(failedTransactions.map((ft) => ft.errorMessage)), useremail: currentUser.email } ] } }); } } else { if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { //QBO Logs are handled server side. await insertExportLog({ variables: { logs: [ { bodyshopid: bodyshop.id, jobid: key, successful: true, useremail: currentUser.email } ] } }); const jobUpdateResponse = await updateJob({ variables: { jobIds: [key], fields: { status: bodyshop.md_ro_statuses.default_exported || "Exported*", date_exported: new Date() } } }); if (!jobUpdateResponse.errors) { notification.open({ type: "success", key: "jobsuccessexport", message: t("jobs.successes.exported") }); jobUpdateResponse.data.update_jobs.returning.forEach((job) => { insertAuditTrail({ jobid: job.id, operation: AuditTrailMapping.jobexported(), type: "jobexported" }); }); updateJobCache(jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)); } else { notification["error"]({ message: t("jobs.errors.exporting", { error: JSON.stringify(jobUpdateResponse.error) }) }); } } if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) { notification.open({ type: "success", key: "jobsuccessexport", message: t("jobs.successes.exported") }); const successfulTransactionsSet = [ ...new Set( successfulTransactions.map( (st) => st[bodyshop.accountingconfig && bodyshop.accountingconfig.qbo ? "jobid" : "id"] ) ) ]; if (successfulTransactionsSet.length > 0) { insertAuditTrail({ jobid: successfulTransactionsSet[0], operation: AuditTrailMapping.jobexported(), type: "jobexported" }); } updateJobCache(successfulTransactionsSet); } } }) ); if (completedCallback) completedCallback([]); if (loadingCallback) loadingCallback(false); setLoading(false); }; return ( ); } export default connect(mapStateToProps, mapDispatchToProps)(JobsExportAllButton);