diff --git a/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx b/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx index f9bec96f0..abae21de0 100644 --- a/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx +++ b/client/src/components/production-board-kanban-card/production-board-kanban-card-color-legend.component.jsx @@ -22,7 +22,7 @@ const CardColorLegend = ({ bodyshop }) => { }); return ( - + {t("production.labels.legend")} - data - .reduce( - (acc, val) => - acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), - 0 - ) - .toFixed(1), - [data] - ); - - const totalLAB = useMemo( - () => data.reduce((acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1), - [data] - ); - - const totalLAR = useMemo( - () => data.reduce((acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1), - [data] - ); - const cardSettings = useMemo( () => associationSettings?.kanban_settings && Object.keys(associationSettings.kanban_settings).length > 0 @@ -215,7 +193,17 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr orientation: false, cardSize: "small", model_info: true, - kiosk: false + kiosk: false, + totalHrs: true, + totalAmountInProduction: false, + totalLAB: true, + totalLAR: true, + jobsInProduction: true, + totalHrsOnBoard: false, + totalLABOnBoard: false, + totalLAROnBoard: false, + jobsOnBoard: false, + totalAmountOnBoard: true }, [associationSettings] ); @@ -234,14 +222,8 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
- - - - - - } + title={cardSettings.cardcolor && } + style={{ paddingInline: 0, paddingBlock: 0 }} extra={
); } diff --git a/client/src/components/production-board-kanban/production-board-kanban.container.jsx b/client/src/components/production-board-kanban/production-board-kanban.container.jsx index e6502b178..d4834cc94 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.container.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.container.jsx @@ -2,10 +2,7 @@ import React, { useEffect, useMemo } from "react"; import { useQuery, useSubscription } from "@apollo/client"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { - QUERY_JOBS_IN_PRODUCTION_WITH_STATUSES, - SUBSCRIPTION_JOBS_IN_PRODUCTION_WITH_STATUSES -} from "../../graphql/jobs.queries"; +import { QUERY_JOBS_IN_PRODUCTION, SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries"; import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import ProductionBoardKanbanComponent from "./production-board-kanban.component"; @@ -24,16 +21,14 @@ function ProductionBoardKanbanContainer({ bodyshop, currentUser }) { [bodyshop.md_ro_statuses.production_statuses, bodyshop.md_ro_statuses.additional_board_statuses] ); - const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION_WITH_STATUSES, { - variables: { statuses: combinedStatuses }, + const { refetch, loading, data } = useQuery(QUERY_JOBS_IN_PRODUCTION, { pollInterval: 3600000, fetchPolicy: "network-only", nextFetchPolicy: "network-only", onError: (error) => console.error(`Error fetching jobs in production: ${error.message}`) }); - const { data: updatedJobs } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION_WITH_STATUSES, { - variables: { statuses: combinedStatuses }, + const { data: updatedJobs } = useSubscription(SUBSCRIPTION_JOBS_IN_PRODUCTION, { onError: (error) => console.error(`Error subscribing to jobs in production: ${error.message}`) }); diff --git a/client/src/components/production-board-kanban/production-board-kanban.settings.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.settings.component.jsx index d84b19882..39d60fa14 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.settings.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.settings.component.jsx @@ -1,3 +1,4 @@ +// ProductionBoardKanbanSettings.jsx import { useMutation } from "@apollo/client"; import { Button, Card, Checkbox, Col, Form, notification, Popover, Radio, Row } from "antd"; import React, { useEffect, useState } from "react"; @@ -121,6 +122,22 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par ].map((item) => renderCheckboxItem(item, `production.labels.${item}`))} + + + {[ + { name: "totalHrs", label: "total_hours_in_production" }, + { name: "totalLAB", label: "total_lab_in_production" }, + { name: "totalLAR", label: "total_lar_in_production" }, + { name: "totalAmountInProduction", label: "total_amount_in_production" }, + { name: "jobsInProduction", label: "jobs_in_production" }, + { name: "totalHrsOnBoard", label: "total_hours_on_board" }, + { name: "totalLABOnBoard", label: "total_lab_on_board" }, + { name: "totalLAROnBoard", label: "total_lar_on_board" }, + { name: "jobsOnBoard", label: "total_jobs_on_board" }, + { name: "totalAmountOnBoard", label: "total_amount_on_board" } + ].map((item) => renderCheckboxItem(item.name, `production.settings.statistics.${item.label}`))} + + ); diff --git a/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx new file mode 100644 index 000000000..7ce4591d2 --- /dev/null +++ b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx @@ -0,0 +1,145 @@ +import React, { useMemo } from "react"; +import { Statistic, Card } from "antd"; +import { useTranslation } from "react-i18next"; + +const ProductionStatistics = ({ data, cardSettings, reducerData }) => { + const { t } = useTranslation(); + + const calculateTotal = (items, key, subKey) => { + return items.reduce((acc, item) => acc + (item[key]?.aggregate?.sum?.[subKey] || 0), 0); + }; + + const calculateTotalAmount = (items, key) => { + return items.reduce((acc, item) => acc + (item[key]?.totals?.subtotal?.amount || 0), 0); + }; + + const calculateReducerTotal = (lanes, key, subKey) => { + return lanes.reduce((acc, lane) => { + return ( + acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata[key]?.aggregate?.sum?.[subKey] || 0), 0) + ); + }, 0); + }; + + const calculateReducerTotalAmount = (lanes, key) => { + return lanes.reduce((acc, lane) => { + return ( + acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata[key]?.totals?.subtotal?.amount || 0), 0) + ); + }, 0); + }; + + const formatValue = (value, type) => { + if (type === "Jobs") { + return value.toFixed(0); + } + if (type === "Hrs") { + return value.toFixed(2); + } + return value; + }; + + const totalHrs = useMemo(() => { + if (!cardSettings.totalHrs) return null; + const total = calculateTotal(data, "labhrs", "mod_lb_hrs") + calculateTotal(data, "larhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [data, cardSettings.totalHrs]); + + const totalLAB = useMemo(() => { + if (!cardSettings.totalLAB) return null; + const total = calculateTotal(data, "labhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [data, cardSettings.totalLAB]); + + const totalLAR = useMemo(() => { + if (!cardSettings.totalLAR) return null; + const total = calculateTotal(data, "larhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [data, cardSettings.totalLAR]); + + const jobsInProduction = useMemo( + () => (cardSettings.jobsInProduction ? data.length : null), + [data, cardSettings.jobsInProduction] + ); + + const totalAmountInProduction = useMemo(() => { + if (!cardSettings.totalAmountInProduction) return null; + const total = calculateTotalAmount(data, "job_totals"); + return parseFloat(total.toFixed(2)); + }, [data, cardSettings.totalAmountInProduction]); + + const totalHrsOnBoard = useMemo(() => { + if (!reducerData || !cardSettings.totalHrsOnBoard) return null; + const total = + calculateReducerTotal(reducerData.lanes, "labhrs", "mod_lb_hrs") + + calculateReducerTotal(reducerData.lanes, "larhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [reducerData, cardSettings.totalHrsOnBoard]); + + const totalLABOnBoard = useMemo(() => { + if (!reducerData || !cardSettings.totalLABOnBoard) return null; + const total = calculateReducerTotal(reducerData.lanes, "labhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [reducerData, cardSettings.totalLABOnBoard]); + + const totalLAROnBoard = useMemo(() => { + if (!reducerData || !cardSettings.totalLAROnBoard) return null; + const total = calculateReducerTotal(reducerData.lanes, "larhrs", "mod_lb_hrs"); + return parseFloat(total.toFixed(2)); + }, [reducerData, cardSettings.totalLAROnBoard]); + + const jobsOnBoard = useMemo( + () => + reducerData && cardSettings.jobsOnBoard + ? reducerData.lanes.reduce((acc, lane) => acc + lane.cards.length, 0) + : null, + [reducerData, cardSettings.jobsOnBoard] + ); + + const totalAmountOnBoard = useMemo(() => { + if (!reducerData || !cardSettings.totalAmountOnBoard) return null; + const total = calculateReducerTotalAmount(reducerData.lanes, "job_totals"); + return parseFloat(total.toFixed(2)); + }, [reducerData, cardSettings.totalAmountOnBoard]); + + const statistics = [ + { value: totalHrs, title: t("total_hours_in_production"), suffix: t("production.statistics.hours") }, + { + value: totalAmountInProduction, + title: t("total_amount_in_production"), + prefix: t("production.statistics.currency_symbol") + }, + { value: totalLAB, title: t("total_lab_in_production"), suffix: t("production.statistics.hours") }, + { value: totalLAR, title: t("total_lar_in_production"), suffix: t("production.statistics.hours") }, + { value: jobsInProduction, title: t("jobs_in_production"), suffix: "Jobs" }, + { value: totalHrsOnBoard, title: t("total_hours_on_board"), suffix: t("production.statistics.hours") }, + { + value: totalAmountOnBoard, + title: t("total_amount_on_board"), + prefix: t("production.statistics.currency_symbol") + }, + { value: totalLABOnBoard, title: t("total_lab_on_board"), suffix: t("production.statistics.hours") }, + { value: totalLAROnBoard, title: t("total_lar_on_board"), suffix: t("production.statistics.hours") }, + { value: jobsOnBoard, title: t("total_jobs_on_board"), suffix: "Jobs" } + ]; + + return ( +
+ {statistics.map( + (stat, index) => + stat.value !== null && ( + + + + ) + )} +
+ ); +}; + +export default ProductionStatistics; diff --git a/client/src/components/production-board-kanban/trello-board/controllers/BoardContainer.jsx b/client/src/components/production-board-kanban/trello-board/controllers/BoardContainer.jsx index e59d92aec..96879b0b6 100644 --- a/client/src/components/production-board-kanban/trello-board/controllers/BoardContainer.jsx +++ b/client/src/components/production-board-kanban/trello-board/controllers/BoardContainer.jsx @@ -7,6 +7,7 @@ import Lane from "./Lane"; import { PopoverWrapper } from "react-popopo"; import * as actions from "../../../../redux/trello/trello.actions.js"; import { BoardWrapper } from "../styles/Base.js"; +import ProductionStatistics from "../../production-board-kanban.statistics.jsx"; const useDragMap = () => { const dragMapRef = useRef(new Map()); @@ -30,7 +31,8 @@ const BoardContainer = ({ orientation = "horizontal", cardSettings = {}, eventBusHandle, - reducerData + reducerData, + queryData }) => { const [isDragging, setIsDragging] = useState(false); const [isProcessing, setIsProcessing] = useState(false); @@ -124,33 +126,36 @@ const BoardContainer = ({ ); return ( - - - - {currentReducerData.lanes.map((lane, index) => ( - - ))} - - - +
+ + + + + {currentReducerData.lanes.map((lane, index) => ( + + ))} + + + +
); }; diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 89dc8a726..51ff6927c 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -292,82 +292,6 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql` } `; -export const QUERY_JOBS_IN_PRODUCTION = gql` - query QUERY_JOBS_IN_PRODUCTION { - jobs(where: { inproduction: { _eq: true } }) { - id - updated_at - comment - status - category - iouparent - ro_number - ownerid - ownr_fn - ownr_ln - ownr_co_nm - v_model_yr - v_model_desc - clm_no - v_make_desc - v_color - vehicleid - plate_no - actual_in - scheduled_completion - scheduled_delivery - date_last_contacted - date_next_contact - ins_co_nm - clm_total - ownr_ph1 - ownr_ph2 - special_coverage_policy - owner_owing - production_vars - kanbanparent - alt_transport - employee_body - employee_refinish - employee_prep - employee_csr - est_ct_fn - est_ct_ln - suspended - date_repairstarted - joblines_status { - part_type - status - count - } - labhrs: joblines_aggregate(where: { _and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }] }) { - aggregate { - sum { - mod_lb_hrs - } - } - } - larhrs: joblines_aggregate(where: { _and: [{ mod_lbr_ty: { _eq: "LAR" } }, { removed: { _eq: false } }] }) { - aggregate { - sum { - mod_lb_hrs - } - } - } - subletLines: joblines( - where: { _and: { part_type: { _in: ["PAS", "PASL"] }, removed: { _eq: false } } } - order_by: { line_no: asc } - ) { - id - line_desc - sublet_ignored - sublet_completed - jobid - } - } - } -`; - export const QUERY_LBR_HRS_BY_PK = gql` query QUERY_LBR_HRS_BY_PK($id: uuid!) { jobs_by_pk(id: $id) { @@ -2529,9 +2453,18 @@ export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql` } `; -export const QUERY_JOBS_IN_PRODUCTION_WITH_STATUSES = gql` - query QUERY_JOBS_IN_PRODUCTION($statuses: [String!]) { - jobs(where: { inproduction: { _eq: true }, status: { _in: $statuses } }) { +export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql` + subscription SUBSCRIPTION_JOBS_IN_PRODUCTION { + jobs(where: { inproduction: { _eq: true } }) { + id + updated_at + } + } +`; + +export const QUERY_JOBS_IN_PRODUCTION = gql` + query QUERY_JOBS_IN_PRODUCTION { + jobs(where: { inproduction: { _eq: true } }) { id updated_at comment @@ -2571,6 +2504,7 @@ export const QUERY_JOBS_IN_PRODUCTION_WITH_STATUSES = gql` est_ct_fn est_ct_ln suspended + job_totals date_repairstarted joblines_status { part_type @@ -2604,21 +2538,3 @@ export const QUERY_JOBS_IN_PRODUCTION_WITH_STATUSES = gql` } } `; - -export const SUBSCRIPTION_JOBS_IN_PRODUCTION_WITH_STATUSES = gql` - subscription SUBSCRIPTION_JOBS_IN_PRODUCTION($statuses: [String!]) { - jobs(where: { inproduction: { _eq: true }, status: { _in: $statuses } }) { - id - updated_at - } - } -`; - -export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql` - subscription SUBSCRIPTION_JOBS_IN_PRODUCTION { - jobs(where: { inproduction: { _eq: true } }) { - id - updated_at - } - } -`; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 4f19ee1e5..c68f3ea15 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2733,11 +2733,19 @@ "settings": { "layout": "Layout", "information": "Information", + "statistics_title": "Statistics", "board_settings": "Board Settings", - "tabs": { - "card": "Card", - "board": "Board", - "lane": "Lane" + "statistics": { + "total_hours_in_production": "Hours in Production", + "total_lab_in_production": "Body Hours in Production", + "total_lar_in_production": "Refinish Hours in Production", + "total_amount_in_production": "Dollars in Production", + "jobs_in_production": "Jobs in Production", + "total_hours_on_board": "Hours on Board", + "total_lab_on_board": "Body Hours on Board", + "total_lar_on_board": "Refinish Hours on Board", + "total_amount_on_board": "Dollars on Board", + "total_jobs_on_board": "Jobs on Board" } }, "actions": { @@ -2811,7 +2819,21 @@ }, "successes": { "removed": "Job removed from production." - } + }, + "statistics": { + "total_hours_in_production": "Hours in Production", + "total_lab_in_production": "Body Hours in Production", + "total_lar_in_production": "Refinish Hours in Production", + "total_amount_in_production": "Dollars in Production", + "jobs_in_production": "Jobs in Production", + "total_hours_on_board": "Hours on Board", + "total_lab_on_board": "Body Hours on Board", + "total_lar_on_board": "Refinish Hours on Board", + "total_amount_on_board": "Dollars on Board", + "total_jobs_on_board": "Jobs on Board", + "hours": "Hours", + "currency_symbol": "$" + } }, "profile": { "errors": { diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index a80e7a054..d82de7862 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2722,27 +2722,52 @@ "purchases_by_vendor_summary": "" } }, - "production": { - "actions": { - "addcolumns": "", - "bodypriority-clear": "", - "bodypriority-set": "", - "detailpriority-clear": "", - "detailpriority-set": "", - "paintpriority-clear": "", - "paintpriority-set": "", - "remove": "", - "removecolumn": "", - "saveconfig": "", - "suspend": "", - "unsuspend": "" - }, - "errors": { - "boardupdate": "", - "removing": "", - "settings": "" - }, - "labels": { + "production": { + "options": { + "small": "", + "medium": "", + "large": "", + "vertical": "", + "horizontal": "" + }, + "settings": { + "layout": "", + "information": "", + "statistics_title": "", + "board_settings": "", + "statistics": { + "total_hours_in_production": "", + "total_lab_in_production": "", + "total_lar_in_production": "", + "total_amount_in_production": "", + "jobs_in_production": "", + "total_hours_on_board": "", + "total_lab_on_board": "", + "total_lar_on_board": "", + "total_amount_on_board": "", + "total_jobs_on_board": "" + } + }, + "actions": { + "addcolumns": "", + "bodypriority-clear": "", + "bodypriority-set": "", + "detailpriority-clear": "", + "detailpriority-set": "", + "paintpriority-clear": "", + "paintpriority-set": "", + "remove": "", + "removecolumn": "", + "saveconfig": "", + "suspend": "", + "unsuspend": "" + }, + "errors": { + "boardupdate": "", + "removing": "", + "settings": "" + }, + "labels": { "kiosk_mode": "", "on": "", "off": "", @@ -2754,48 +2779,62 @@ "card_size": "", "model_info": "", "actual_in": "", - "alert": "", - "alertoff": "", - "alerton": "", - "ats": "", - "bodyhours": "", - "bodypriority": "", - "bodyshop": { - "labels": { - "qbo_departmentid": "", - "qbo_usa": "" - } - }, - "cardcolor": "", - "cardsettings": "", - "clm_no": "", - "comment": "", - "compact": "", - "detailpriority": "", - "employeeassignments": "", - "employeesearch": "", - "ins_co_nm": "", - "jobdetail": "", - "laborhrs": "", - "legend": "", - "note": "", - "ownr_nm": "", - "paintpriority": "", - "partsstatus": "", - "production_note": "", - "refinishhours": "", - "scheduled_completion": "", - "selectview": "", - "stickyheader": "", - "sublets": "", - "totalhours": "", - "touchtime": "", - "viewname": "" - }, - "successes": { - "removed": "" - } - }, + "alert": "", + "alertoff": "", + "alerton": "", + "ats": "", + "bodyhours": "", + "bodypriority": "", + "bodyshop": { + "labels": { + "qbo_departmentid": "", + "qbo_usa": "" + } + }, + "cardcolor": "", + "cardsettings": "", + "clm_no": "", + "comment": "", + "compact": "", + "detailpriority": "", + "employeeassignments": "", + "employeesearch": "", + "ins_co_nm": "", + "jobdetail": "", + "laborhrs": "", + "legend": "", + "note": "", + "ownr_nm": "", + "paintpriority": "", + "partsstatus": "", + "production_note": "", + "refinishhours": "", + "scheduled_completion": "", + "selectview": "", + "stickyheader": "", + "sublets": "", + "totalhours": "", + "touchtime": "", + "viewname": "" + }, + "successes": { + "removed": "" + }, + "statistics": { + "total_hours_in_production": "", + "total_lab_in_production": "", + "total_lar_in_production": "", + "total_amount_in_production": "", + "jobs_in_production": "", + "total_hours_on_board": "", + "total_lab_on_board": "", + "total_lar_on_board": "", + "total_amount_on_board": "", + "total_jobs_on_board": "", + "hours": "", + "currency_symbol": "" + } + }, "profile": { "errors": { "state": "Error al leer el estado de la página. Porfavor refresca." diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 8d8c801ea..bf553724e 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2722,72 +2722,119 @@ "purchases_by_vendor_summary": "" } }, - "production": { - "actions": { - "addcolumns": "", - "bodypriority-clear": "", - "bodypriority-set": "", - "detailpriority-clear": "", - "detailpriority-set": "", - "paintpriority-clear": "", - "paintpriority-set": "", - "remove": "", - "removecolumn": "", - "saveconfig": "", - "suspend": "", - "unsuspend": "" - }, - "errors": { - "boardupdate": "", - "removing": "", - "settings": "" - }, - "labels": { + "production": { + "options": { + "small": "", + "medium": "", + "large": "", + "vertical": "", + "horizontal": "" + }, + "settings": { + "layout": "", + "information": "", + "statistics_title": "", + "board_settings": "", + "statistics": { + "total_hours_in_production": "", + "total_lab_in_production": "", + "total_lar_in_production": "", + "total_amount_in_production": "", + "jobs_in_production": "", + "total_hours_on_board": "", + "total_lab_on_board": "", + "total_lar_on_board": "", + "total_amount_on_board": "", + "total_jobs_on_board": "" + } + }, + "actions": { + "addcolumns": "", + "bodypriority-clear": "", + "bodypriority-set": "", + "detailpriority-clear": "", + "detailpriority-set": "", + "paintpriority-clear": "", + "paintpriority-set": "", + "remove": "", + "removecolumn": "", + "saveconfig": "", + "suspend": "", + "unsuspend": "" + }, + "errors": { + "boardupdate": "", + "removing": "", + "settings": "" + }, + "labels": { "kiosk_mode": "", + "on": "", + "off": "", + "wide": "", + "tall": "", + "vertical": "", + "horizontal": "", + "orientation": "", + "card_size": "", "model_info": "", "actual_in": "", - "alert": "", - "alertoff": "", - "alerton": "", - "ats": "", - "bodyhours": "", - "bodypriority": "", - "bodyshop": { - "labels": { - "qbo_departmentid": "", - "qbo_usa": "" - } - }, - "cardcolor": "", - "cardsettings": "", - "clm_no": "", - "comment": "", - "compact": "", - "detailpriority": "", - "employeeassignments": "", - "employeesearch": "", - "ins_co_nm": "", - "jobdetail": "", - "laborhrs": "", - "legend": "", - "note": "", - "ownr_nm": "", - "paintpriority": "", - "partsstatus": "", - "production_note": "", - "refinishhours": "", - "scheduled_completion": "", - "selectview": "", - "stickyheader": "", - "sublets": "", - "totalhours": "", - "touchtime": "", - "viewname": "" - }, - "successes": { - "removed": "" - } - }, + "alert": "", + "alertoff": "", + "alerton": "", + "ats": "", + "bodyhours": "", + "bodypriority": "", + "bodyshop": { + "labels": { + "qbo_departmentid": "", + "qbo_usa": "" + } + }, + "cardcolor": "", + "cardsettings": "", + "clm_no": "", + "comment": "", + "compact": "", + "detailpriority": "", + "employeeassignments": "", + "employeesearch": "", + "ins_co_nm": "", + "jobdetail": "", + "laborhrs": "", + "legend": "", + "note": "", + "ownr_nm": "", + "paintpriority": "", + "partsstatus": "", + "production_note": "", + "refinishhours": "", + "scheduled_completion": "", + "selectview": "", + "stickyheader": "", + "sublets": "", + "totalhours": "", + "touchtime": "", + "viewname": "" + }, + "successes": { + "removed": "" + }, + "statistics": { + "total_hours_in_production": "", + "total_lab_in_production": "", + "total_lar_in_production": "", + "total_amount_in_production": "", + "jobs_in_production": "", + "total_hours_on_board": "", + "total_lab_on_board": "", + "total_lar_on_board": "", + "total_amount_on_board": "", + "total_jobs_on_board": "", + "hours": "", + "currency_symbol": "" + } + }, "profile": { "errors": { "state": "Erreur lors de la lecture de l'état de la page. Rafraichissez, s'il vous plait."