Compare commits

...

16 Commits

Author SHA1 Message Date
Patrick Fic
c1bf112aac Resolve CI Issue. 2022-03-31 14:43:41 -07:00
Patrick Fic
ecfb3e91cd Package updates. 2022-03-31 14:25:20 -07:00
Patrick Fic
e25606070b Resolve Firefox Rendering issues. 2022-03-31 14:25:16 -07:00
Patrick Fic
28f0d9a4b2 IO-1805 QBO Modifications for No 1 group. 2022-03-31 12:50:06 -07:00
Patrick Fic
c125cd8ca2 Add end to autohouse export. 2022-03-30 14:45:09 -07:00
Patrick Fic
123f94a0f5 IO-1666 Sticky headers on production board. 2022-03-30 10:47:09 -07:00
Patrick Fic
d601617819 Add job total cehck for Autohouse. 2022-03-30 08:34:55 -07:00
Patrick Fic
80bd2dc6d8 IO-1794 Rsolve password reset issue. 2022-03-29 09:24:09 -07:00
Patrick Fic
45bd2d3281 IO-1778 Reconciliation modal UI updates. 2022-03-29 09:17:21 -07:00
Patrick Fic
b4304c743e IO-1793 Add detail drawer to production board view. 2022-03-29 08:31:15 -07:00
Patrick Fic
faf9fbb9d8 IO-1638 Parts Received % on prod list. 2022-03-29 08:14:20 -07:00
Patrick Fic
6d1581a4e1 IO-1790 Add invoice date to receivables export. 2022-03-28 16:17:40 -07:00
Patrick Fic
28f6c72de1 Resolve search on payments. 2022-03-28 13:31:16 -07:00
Patrick Fic
54577ac680 IO-1802 Add Lag Time RO to print center. 2022-03-25 15:32:23 -06:00
Patrick Fic
c912681793 Merged in release/2022-03-25 (pull request #434)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-25 13:22:19 +00:00
Patrick Fic
89b515fff4 Merged in release/2022-03-25 (pull request #433)
Release/2022 03 25
2022-03-25 01:04:39 +00:00
30 changed files with 32161 additions and 872 deletions

View File

@@ -2354,6 +2354,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>is_credit_memo_short</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_rate</name>
<definition_loaded>false</definition_loaded>
@@ -26589,6 +26610,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>parts_received</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>parts_tax_rates</name>
<definition_loaded>false</definition_loaded>
@@ -31189,6 +31231,37 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>owner</name>
<children>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>noownerinfo</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>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>owners</name>
<children>
@@ -34925,6 +34998,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>lag_time_ro</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>mechanical_authorization</name>
<definition_loaded>false</definition_loaded>
@@ -36487,6 +36581,58 @@
</translation>
</translations>
</concept_node>
<folder_node>
<name>bodyshop</name>
<children>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>qbo_departmentid</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>qbo_usa</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>
</children>
</folder_node>
</children>
</folder_node>
<concept_node>
<name>cardsettings</name>
<definition_loaded>false</definition_loaded>
@@ -36844,6 +36990,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>stickyheader</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>sublets</name>
<definition_loaded>false</definition_loaded>

View File

@@ -4,40 +4,40 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.5.6",
"@apollo/client": "^3.5.10",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.3",
"@fingerprintjs/fingerprintjs": "^3.3.1",
"@sentry/react": "^6.16.1",
"@sentry/tracing": "^6.16.1",
"@splitsoftware/splitio-react": "^1.3.0",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@sentry/react": "^6.19.3",
"@sentry/tracing": "^6.19.3",
"@splitsoftware/splitio-react": "^1.3.1-rc.1",
"@stripe/react-stripe-js": "^1.7.0",
"@stripe/stripe-js": "^1.22.0",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.17.4",
"@stripe/stripe-js": "^1.26.0",
"@tanem/react-nprogress": "^4.0.12",
"antd": "^4.19.3",
"apollo-link-logger": "^2.0.0",
"axios": "^0.24.0",
"axios": "^0.26.1",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"dotenv": "^16.0.0",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.6.1",
"graphql": "^16.2.0",
"i18next": "^21.6.3",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.8",
"firebase": "^9.6.10",
"graphql": "^16.3.0",
"i18next": "^21.6.14",
"i18next-browser-languagedetector": "^6.1.4",
"jsoneditor": "^9.7.4",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.44",
"logrocket": "^2.1.2",
"markerjs2": "^2.17.2",
"libphonenumber-js": "^1.9.50",
"logrocket": "^2.2.1",
"markerjs2": "^2.20.0",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34",
"phone": "^3.1.10",
"phone": "^3.1.14",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.1",
"prop-types": "^15.8.1",
"query-string": "^7.1.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.2",
@@ -45,41 +45,42 @@
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-drag-listview": "^0.1.8",
"react-drag-listview": "^0.1.9",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.15.1",
"react-grid-layout": "^1.3.4",
"react-i18next": "^11.16.2",
"react-icons": "^4.3.1",
"react-number-format": "^4.9.0",
"react-redux": "^7.2.6",
"react-number-format": "^4.9.1",
"react-redux": "^7.2.7",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0",
"react-scripts": "^4.0.3",
"react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.8",
"recharts": "^2.1.9",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.1.5",
"sass": "^1.45.0",
"socket.io-client": "^4.4.0",
"styled-components": "^5.3.3",
"sass": "^1.49.10",
"socket.io-client": "^4.4.1",
"styled-components": "^5.3.5",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"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",
"web-vitals": "^2.1.4",
"workbox-background-sync": "^6.5.2",
"workbox-broadcast-update": "^6.5.2",
"workbox-cacheable-response": "^6.5.2",
"workbox-core": "^6.5.2",
"workbox-expiration": "^6.5.2",
"workbox-google-analytics": "^6.5.2",
"workbox-navigation-preload": "^6.5.2",
"workbox-precaching": "^6.5.2",
"workbox-range-requests": "^6.5.2",
"workbox-routing": "^6.5.2",
"workbox-strategies": "^6.5.2",
"workbox-streams": "^6.5.2",
"yauzl": "^2.10.0"
},
"scripts": {
@@ -116,11 +117,11 @@
"react-error-overlay": "6.0.9"
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.18.3",
"@sentry/webpack-plugin": "^1.18.8",
"@testing-library/cypress": "^8.0.2",
"cypress": "^9.1.1",
"cypress": "^9.5.3",
"eslint-plugin-cypress": "^2.12.1",
"react-error-overlay": "6.0.9",
"react-error-overlay": "6.0.10",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"
}

View File

@@ -162,10 +162,19 @@ export function AccountingPayablesTableComponent({
const dataSource = state.search
? payments.filter(
(v) =>
(v.vendor.name || "")
(v.paymentnum || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(v.invoice_number || "")
((v.job && v.job.ro_number) || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
((v.job && v.job.ownr_fn) || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
((v.job && v.job.ownr_ln) || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
((v.job && v.job.ownr_co_nm) || "")
.toLowerCase()
.includes(state.search.toLowerCase())
)

View File

@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { logImEXEvent } from "../../firebase/firebase.utils";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import { alphaSort, dateSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
@@ -12,6 +12,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { DateFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
@@ -62,6 +63,18 @@ export function AccountingReceivablesTableComponent({
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
},
{
title: t("jobs.fields.date_invoiced"),
dataIndex: "date_invoiced",
key: "date_invoiced",
sorter: (a, b) => dateSort(a.date_invoiced, b.date_invoiced),
sortOrder:
state.sortedInfo.columnKey === "date_invoiced" &&
state.sortedInfo.order,
render: (text, record) => (
<DateFormatter>{record.date_invoiced}</DateFormatter>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",

View File

@@ -22,7 +22,7 @@ export default function JobReconciliationBillsTable({
dataIndex: "line_desc",
key: "line_desc",
ellipsis: true,
minWidth: "65rem",
width: "10rem",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
@@ -72,11 +72,11 @@ export default function JobReconciliationBillsTable({
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
},
{
title: t("bills.fields.is_credit_memo"),
title: t("bills.fields.is_credit_memo_short"),
dataIndex: "is_credit_memo",
key: "is_credit_memo",
sorter: (a, b) => a.bill.is_credit_memo - b.bill.is_credit_memo,
width: "8rem",
width: "3rem",
sortOrder:
state.sortedInfo.columnKey === "is_credit_memo" &&
state.sortedInfo.order,

View File

@@ -1,6 +1,5 @@
import {
Col,
Divider,
Form,
Input,
InputNumber,
@@ -23,8 +22,8 @@ import FormItemPhone, {
} from "../form-items-formatted/phone-form-item.component";
import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
import FormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
@@ -220,15 +219,8 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
)}
</Col>
</Row>
<Divider
orientation="left"
type="horizontal"
style={{ marginTop: ".8rem", float: "right" }}
>
{t("jobs.forms.appraiserinfo")}
</Divider>
<FormRow noDivider>
<FormRow header={t("jobs.forms.appraiserinfo")}>
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
<Input disabled={jobRO} />
</Form.Item>

View File

@@ -63,10 +63,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""}
${job.v_model_desc || ""}`.trim();
console.log(
"🚀 ~ file: jobs-detail-header.component.jsx ~ line 64 ~ vehicleTitle",
vehicleTitle.length
);
const ownerTitle = `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
job.ownr_co_nm || ""
}`.trim();
return (
<Row gutter={[16, 16]} style={{ alignItems: "stretch" }}>
<Col {...colSpan}>
@@ -159,9 +160,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
style={{ height: "100%" }}
title={
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}>
{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
job.ownr_co_nm || ""
}`}
{ownerTitle.length > 0
? ownerTitle
: t("owner.labels.noownerinfo")}
</Link>
}
>

View File

@@ -425,8 +425,6 @@ export function PartsOrderListTableComponent({
placement="right"
onClose={() => handleOnRowClick(null)}
visible={selectedpartsorder}
//getContainer={false}
style={{ position: "absolute" }}
closable
width={drawerPercentage}
>

View File

@@ -61,20 +61,22 @@ export default function ProductionBoardCard(
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
<span style={{ fontWeight: "bolder" }}>
{card.ro_number || t("general.labels.na")}
<Link
to={
technician
? `/tech/joblookup?selected=${card.id}`
: `/manage/jobs/${card.id}`
}
>
{card.ro_number || t("general.labels.na")}
</Link>
</span>
</Space>
}
extra={
technician ? (
<Link to={`/tech/joblookup?selected=${card.id}`}>
<EyeFilled />
</Link>
) : (
<Link to={`/manage/jobs/${card.id}`}>
<EyeFilled />
</Link>
)
<Link to={{ search: `?selected=${card.id}` }}>
<EyeFilled />
</Link>
}
>
<Row>

View File

@@ -131,6 +131,13 @@ export default function ProductionBoardKanbanCardSettings({
>
<Switch />
</Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.stickyheader")}
name="stickyheader"
>
<Switch />
</Form.Item>
</Col>
</Row>
</Form>

View File

@@ -1,26 +1,27 @@
import { SyncOutlined } from "@ant-design/icons";
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 { SyncOutlined } from "@ant-design/icons";
import { Grid, notification, Button, PageHeader, Space, Statistic } from "antd";
import { Button, Grid, notification, PageHeader, Space, Statistic } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Sticky, StickyContainer } from "react-sticky";
import { createStructuredSelector } from "reselect";
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ProductionBoardCard from "../production-board-kanban-card/production-board-kanban-card.component";
import { createBoardData } from "./production-board-kanban.utils.js";
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
import { logImEXEvent } from "../../firebase/firebase.utils";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
import styled from "styled-components";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import ProductionBoardCard from "../production-board-kanban-card/production-board-kanban-card.component";
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
//import "@asseinfo/react-kanban/dist/styles.css";
import "./production-board-kanban.styles.scss";
import { createBoardData } from "./production-board-kanban.utils.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
@@ -183,9 +184,52 @@ export function ProductionBoardKanbanComponent({
: standardSizes[selectedBreakpoint[0]]
: "250";
const stickyHeader = {
renderColumnHeader: ({ title }) => (
<Sticky>
{({
style,
// the following are also available but unused in this example
isSticky,
wasSticky,
distanceFromTop,
distanceFromBottom,
calculatedHeight,
}) => (
<div
className="react-kanban-column-header"
style={{ ...style, zIndex: "99", backgroundColor: "#ddd" }}
>
{title}
</div>
)}
</Sticky>
),
};
const cardSettings =
associationSettings &&
associationSettings.kanban_settings &&
Object.keys(associationSettings.kanban_settings).length > 0
? associationSettings.kanban_settings
: {
ats: true,
clm_no: true,
compact: false,
ownr_nm: true,
sublets: true,
ins_co_nm: true,
production_note: true,
employeeassignments: true,
scheduled_completion: true,
stickyheader: false,
};
return (
<Container width={width}>
<IndefiniteLoading loading={isMoving} />
<PageHeader
title={
<Space>
@@ -215,34 +259,19 @@ export function ProductionBoardKanbanComponent({
</Space>
}
/>
<Board
children={boardLanes}
disableCardDrag={isMoving}
renderCard={(card) =>
ProductionBoardCard(
technician,
card,
bodyshop,
associationSettings &&
associationSettings.kanban_settings &&
Object.keys(associationSettings.kanban_settings).length > 0
? associationSettings.kanban_settings
: {
ats: true,
clm_no: true,
compact: false,
ownr_nm: true,
sublets: true,
ins_co_nm: true,
production_note: true,
employeeassignments: true,
scheduled_completion: true,
}
)
}
onCardDragEnd={handleDragEnd}
/>
<ProductionListDetailComponent jobs={data} />
<StickyContainer>
<Board
style={{ height: "100%" }}
children={boardLanes}
disableCardDrag={isMoving}
{...(cardSettings.stickyheader && stickyHeader)}
renderCard={(card) =>
ProductionBoardCard(technician, card, bodyshop, cardSettings)
}
onCardDragEnd={handleDragEnd}
/>
</StickyContainer>
</Container>
);
}

View File

@@ -21,6 +21,7 @@ import ProductionListColumnStatus from "./production-list-columns.status.compone
import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import ProductionListColumnComment from "./production-list-columns.comment.component";
import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component";
const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [
@@ -96,7 +97,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
sortOrder:
state.sortedInfo.columnKey === "actual_in" && state.sortedInfo.order,
render: (text, record) => (
<ProductionListDate record={record} field="actual_in" time/>
<ProductionListDate record={record} field="actual_in" time />
),
},
{
@@ -477,6 +478,14 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
/>
),
},
{
title: i18n.t("jobs.labels.parts_received"),
dataIndex: "parts_received",
key: "parts_received",
render: (text, record) => (
<ProductionListColumnPartsReceived record={record} />
),
},
];
};
export default r;

View File

@@ -0,0 +1,41 @@
import { useMemo } from "react";
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
)(ProductionListColumnPartsReceived);
export function ProductionListColumnPartsReceived({ bodyshop, record }) {
const amount = useMemo(() => {
const amount = record.joblines_status.reduce(
(acc, val) => {
acc.total += val.count;
acc.received =
val.status === bodyshop.md_order_statuses.default_received
? acc.received + val.count
: acc.received;
return acc;
},
{ total: 0, received: 0 }
);
return {
...amount,
percent:
amount.total !== 0
? ((amount.received / amount.total) * 100).toFixed(0) + "%"
: "N/A",
};
}, [record, bodyshop.md_order_statuses]);
return `${amount.percent} (${amount.received}/${amount.total})`;
}

View File

@@ -88,12 +88,6 @@ export function ProductionListTable({
);
const handleTableChange = (pagination, filters, sorter) => {
console.log(
"🚀 ~ file: production-list-table.component.jsx ~ line 91 ~ pagination, filters, sorter",
pagination,
filters,
sorter
);
setState({
...state,
filteredInfo: filters,

View File

@@ -163,6 +163,27 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate noStyle>
{() => (
<Form.Item
label={t("bodyshop.labels.qbo_usa")}
shouldUpdate
valuePropName="checked"
name={["accountingconfig", "qbo_usa"]}
>
<Switch
disabled={!form.getFieldValue(["accountingconfig", "qbo"])}
/>
</Form.Item>
)}
</Form.Item>
<Form.Item
label={t("bodyshop.labels.qbo_departmentid")}
name={["accountingconfig", "qbo_departmentid"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.accountingtiers")}
rules={[

View File

@@ -146,6 +146,11 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
joblines_status{
part_type
status
count
}
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
@@ -219,6 +224,11 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
joblines_status{
part_type
status
count
}
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
@@ -294,6 +304,11 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
employee_prep
employee_csr
suspended
joblines_status{
part_type
status
count
}
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]

View File

@@ -7,6 +7,7 @@ import {
confirmPasswordReset,
signInWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
} from "firebase/auth";
import { doc } from "firebase/firestore";
import i18next from "i18next";
@@ -223,16 +224,16 @@ export function* signInSuccessSaga({ payload }) {
export function* onSendPasswordResetStart() {
yield takeLatest(
UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START,
sendPasswordResetEmail
sendPasswordResetEmailSaga
);
yield takeLatest(
UserActionTypes.SEND_PASSWORD_RESET_EMAIL_START_AGAIN,
sendPasswordResetEmail
sendPasswordResetEmailSaga
);
}
export function* sendPasswordResetEmail({ payload }) {
export function* sendPasswordResetEmailSaga({ payload }) {
try {
yield sendPasswordResetEmail(payload, {
yield sendPasswordResetEmail(auth, payload, {
url: "https://imex.online/passwordreset",
});
@@ -269,7 +270,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
const userEmail = yield select((state) => state.user.currentUser.email);
try {
console.log("Setting shop timezone.");
// moment.tz.setDefault(payload.timezone);
// moment.tz.setDefault(payload.timezone);
} catch (error) {
console.log(error);
}

View File

@@ -157,6 +157,7 @@
"federal_tax_rate": "Federal Tax Rate",
"invoice_number": "Invoice Number",
"is_credit_memo": "Credit Memo?",
"is_credit_memo_short": "CM",
"local_tax_rate": "Local Tax Rate",
"ro_number": "RO Number",
"state_tax_rate": "Provincial/State Tax Rate",
@@ -1563,6 +1564,7 @@
"override_header": "Override estimate header on import?",
"ownerassociation": "Owner Association",
"parts": "Parts",
"parts_received": "Parts Rec.",
"parts_tax_rates": "Parts Tax rates",
"partsfilter": "Parts Only",
"partssubletstotal": "Parts & Sublets Total",
@@ -1842,6 +1844,11 @@
"updated": "Note updated successfully."
}
},
"owner": {
"labels": {
"noownerinfo": "No owner information."
}
},
"owners": {
"actions": {
"update": "Update Selected Records"
@@ -2078,6 +2085,7 @@
"labels": "Labels",
"position": "Starting Position"
},
"lag_time_ro": "Lag Time",
"mechanical_authorization": "Mechanical Authorization",
"mpi_animal_checklist": "MPI - Animal Checklist",
"mpi_eglass_auth": "MPI - eGlass Auth",
@@ -2170,6 +2178,12 @@
"ats": "Alternative Transportation",
"bodyhours": "B",
"bodypriority": "B/P",
"bodyshop": {
"labels": {
"qbo_departmentid": "QBO Department ID",
"qbo_usa": "QBO USA"
}
},
"cardsettings": "Card Settings",
"clm_no": "Claim Number",
"comment": "Comment",
@@ -2187,6 +2201,7 @@
"refinishhours": "R",
"scheduled_completion": "Scheduled Completion",
"selectview": "Select a View",
"stickyheader": "Sticky Header (BETA)",
"sublets": "Sublets",
"totalhours": "Total Hrs ",
"touchtime": "T/T",

View File

@@ -157,6 +157,7 @@
"federal_tax_rate": "",
"invoice_number": "",
"is_credit_memo": "",
"is_credit_memo_short": "",
"local_tax_rate": "",
"ro_number": "",
"state_tax_rate": "",
@@ -1563,6 +1564,7 @@
"override_header": "¿Anular encabezado estimado al importar?",
"ownerassociation": "",
"parts": "Partes",
"parts_received": "",
"parts_tax_rates": "",
"partsfilter": "",
"partssubletstotal": "",
@@ -1842,6 +1844,11 @@
"updated": "Nota actualizada con éxito."
}
},
"owner": {
"labels": {
"noownerinfo": ""
}
},
"owners": {
"actions": {
"update": ""
@@ -2078,6 +2085,7 @@
"labels": "",
"position": ""
},
"lag_time_ro": "",
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
@@ -2170,6 +2178,12 @@
"ats": "",
"bodyhours": "",
"bodypriority": "",
"bodyshop": {
"labels": {
"qbo_departmentid": "",
"qbo_usa": ""
}
},
"cardsettings": "",
"clm_no": "",
"comment": "",
@@ -2187,6 +2201,7 @@
"refinishhours": "",
"scheduled_completion": "",
"selectview": "",
"stickyheader": "",
"sublets": "",
"totalhours": "",
"touchtime": "",

View File

@@ -157,6 +157,7 @@
"federal_tax_rate": "",
"invoice_number": "",
"is_credit_memo": "",
"is_credit_memo_short": "",
"local_tax_rate": "",
"ro_number": "",
"state_tax_rate": "",
@@ -1563,6 +1564,7 @@
"override_header": "Remplacer l'en-tête d'estimation à l'importation?",
"ownerassociation": "",
"parts": "les pièces",
"parts_received": "",
"parts_tax_rates": "",
"partsfilter": "",
"partssubletstotal": "",
@@ -1842,6 +1844,11 @@
"updated": "Remarque mise à jour avec succès."
}
},
"owner": {
"labels": {
"noownerinfo": ""
}
},
"owners": {
"actions": {
"update": ""
@@ -2078,6 +2085,7 @@
"labels": "",
"position": ""
},
"lag_time_ro": "",
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
@@ -2170,6 +2178,12 @@
"ats": "",
"bodyhours": "",
"bodypriority": "",
"bodyshop": {
"labels": {
"qbo_departmentid": "",
"qbo_usa": ""
}
},
"cardsettings": "",
"clm_no": "",
"comment": "",
@@ -2187,6 +2201,7 @@
"refinishhours": "",
"scheduled_completion": "",
"selectview": "",
"stickyheader": "",
"sublets": "",
"totalhours": "",
"touchtime": "",

View File

@@ -464,6 +464,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
lag_time_ro: {
title: i18n.t("printcenter.jobs.lag_time_ro"),
description: "CASL Authorization",
subject: i18n.t("printcenter.jobs.lag_time_ro"),
key: "lag_time_ro",
disabled: false,
group: "ro",
},
}
: {}),
...(!type || type === "job_special"

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -611,6 +611,32 @@ exports.default = function ({
}
}
//QB USA with GST
//This was required for the No. 1 Collision Group.
if (
bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
bodyshop.accountingconfig.qbo_usa &&
bodyshop.region_config.includes("CA_")
) {
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(jobs_by_pk.job_totals.totals.federal_tax).toFormat(
DineroQbFormat
),
SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: {
value:
items[bodyshop.md_responsibility_centers.taxes.federal.accountitem],
},
Qty: 1,
},
});
}
return InvoiceLineAdd;
};
@@ -626,7 +652,7 @@ const findTaxCode = ({ local, state, federal }, taxcode) => {
} else if (t.length > 1) {
return "Multiple Tax Codes Match";
} else {
return "No Tax Code Matches";
return "";
}
};
exports.findTaxCode = findTaxCode;

View File

@@ -174,6 +174,55 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
req
);
const lines = bill.billlines.map((il) =>
generateBillLine(
il,
accounts,
bill.job.class,
bill.job.bodyshop.md_responsibility_centers.sales_tax_codes,
classes,
taxCodes,
bill.job.bodyshop.md_responsibility_centers.costs
)
);
//QB USA with GST
//This was required for the No. 1 Collision Group.
if (
bill.job.bodyshop.accountingconfig &&
bill.job.bodyshop.accountingconfig.qbo &&
bill.job.bodyshop.accountingconfig.qbo_usa &&
bill.job.bodyshop.region_config.includes("CA_")
) {
lines.push({
DetailType: "AccountBasedExpenseLineDetail",
AccountBasedExpenseLineDetail: {
...(bill.job.class
? { ClassRef: { value: classes[bill.job.class] } }
: {}),
AccountRef: {
value:
accounts[
bill.job.bodyshop.md_responsibility_centers.taxes.federal
.accountdesc
],
},
},
Amount: Dinero({
amount: Math.round(
bill.billlines.reduce((acc, val) => {
return acc + val.actual_cost * val.quantity;
}, 0) * 100
),
})
.percentage(bill.federal_tax_rate)
.toFormat(DineroQbFormat),
});
}
const billQbo = {
VendorRef: {
value: vendor.Id,
@@ -192,17 +241,7 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
PrivateNote: `RO ${bill.job.ro_number || ""}`,
Line: bill.billlines.map((il) =>
generateBillLine(
il,
accounts,
bill.job.class,
bill.job.bodyshop.md_responsibility_centers.sales_tax_codes,
classes,
taxCodes,
bill.job.bodyshop.md_responsibility_centers.costs
)
),
Line: lines,
};
logger.log("qbo-payable-objectlog", "DEBUG", req.user.email, bill.id, {
billQbo,
@@ -282,7 +321,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req) {
url: urlBuilder(
qbo_realmId,
"query",
`select * From Account where AccountType = 'Cost of Goods Sold'`
`select * From Account where AccountType in ('Cost of Goods Sold', 'Other Current Liability')`
),
method: "POST",
headers: {

View File

@@ -455,6 +455,25 @@ async function InsertInvoice(
CustomerRef: {
value: parentTierRef.Id,
},
...(bodyshop.accountingconfig.qbo_departmentid &&
bodyshop.accountingconfig.qbo_departmentid.trim() !== "" && {
DepartmentRef: { value: bodyshop.accountingconfig.qbo_departmentid },
}),
...(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
bodyshop.accountingconfig.qbo_usa &&
bodyshop.region_config.includes("CA_") && {
TxnTaxDetail: {
TxnTaxCodeRef: {
value:
taxCodes[
bodyshop.md_responsibility_centers.taxes.state.accountitem
],
},
},
}),
...(bodyshop.accountingconfig.printlater
? { PrintStatus: "NeedToPrint" }
: {}),

View File

@@ -39,7 +39,7 @@ exports.default = async (req, res) => {
const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
const specificShopIds = req.body.bodyshopIds; // ['uuid]
const start = req.body.start; //YYYY-MM-DD
const { start, end } = req.body; //YYYY-MM-DD
const allxmlsToUpload = [];
const allErrors = [];
try {
@@ -58,6 +58,7 @@ exports.default = async (req, res) => {
start: start
? moment(start).startOf("day")
: moment().subtract(3, "days").startOf("day"),
...(end && { end: moment(end).startOf("day") }),
}
);
@@ -184,6 +185,11 @@ exports.default = async (req, res) => {
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
if (!job.job_totals) {
errorCallback({ job, error: { toString: () => "No job totals for RO." } });
return {};
}
const repairCosts = CreateCosts(job);
try {

View File

@@ -111,6 +111,11 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
ro_number
clm_total
clm_no
v_model_yr
v_model_desc
v_make_desc
v_vin
plate_no
ownerid
ownr_ln
ownr_fn
@@ -176,6 +181,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
bodyshops(where: {associations: {active: {_eq: true}}}) {
id
md_responsibility_centers
region_config
accountingconfig
md_ins_cos
timezone
@@ -389,6 +395,8 @@ query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
bodyshop{
md_responsibility_centers
timezone
region_config
accountingconfig
}
}
billlines{
@@ -550,7 +558,7 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee
}
}`;
exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) {
exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!, $end: timestamptz) {
bodyshops_by_pk(id: $bodyshopid){
id
@@ -568,7 +576,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
jc_hourly_rates
timezone
}
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) {
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) {
id
ro_number
status