diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index a6b7d0456..22823dc7c 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -40827,6 +40827,69 @@ + + jobs + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + lastmonth + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + lastweek + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + monthlytarget false @@ -40848,6 +40911,48 @@ + + productivestatistics + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + productivetimeticketsoverdate + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + targets false @@ -40869,6 +40974,69 @@ + + thismonth + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + thisweek + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + timetickets + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + todateactual false @@ -40890,6 +41058,27 @@ + + totaloverperiod + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + weeklyactual false diff --git a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js index 2b9c0b3f7..9f925a1a3 100644 --- a/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js +++ b/client/src/components/scoreboard-targets-table/scoreboard-targets-table.util.js @@ -49,11 +49,6 @@ export const ListOfDaysInCurrentMonth = () => { }; export const ListDaysBetween = ({ start, end }) => { - console.log( - "🚀 ~ file: scoreboard-targets-table.util.js ~ line 52 ~ start, end", - start, - end - ); const days = []; const dateStart = moment(start); const dateEnd = moment(end); diff --git a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.bar.component.jsx b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.bar.component.jsx index 427766d3b..063638a47 100644 --- a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.bar.component.jsx +++ b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.bar.component.jsx @@ -1,14 +1,12 @@ import { Card } from "antd"; -import moment from "moment"; -import React, { useMemo } from "react"; +import React from "react"; +import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { - Area, Bar, CartesianGrid, ComposedChart, Legend, - Line, ResponsiveContainer, Tooltip, XAxis, @@ -16,9 +14,7 @@ import { } from "recharts"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util"; -import _ from "lodash"; - +import TimeTicketsDatesSelector from "../ticket-tickets-dates-selector/time-tickets-dates-selector.component"; const graphProps = { strokeWidth: 3, }; @@ -35,86 +31,16 @@ export default connect( mapDispatchToProps )(ScoreboardTicketsBar); -export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) { - console.log( - "🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 39 ~ start, end,", - start, - end - ); - const listOfBusDays = Utils.ListOfDaysInCurrentMonth(); - - const data = useMemo(() => { - const ticketsGroupedByDate = _.groupBy(timetickets, "date"); - - const listOfDays = Utils.ListDaysBetween({ start, end }); - console.log( - "🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 45 ~ listOfDays", - listOfDays - ); - - console.log( - "🚀 ~ file: scoreboard-timetickets.bar.component.jsx ~ line 43 ~ groupedByDate", - ticketsGroupedByDate - ); - - const ret = []; - listOfDays.forEach((day) => { - const r = { - date: day, - total: 0, - }; - }); - }, [timetickets]); - // const data = listOfBusDays.reduce((acc, val) => { - // //Sum up the current day. - - // const groupedbyDate = _.groupBy(data, "date"); - // let dayhrs; - // if (!!sbEntriesByDate[val]) { - // dayhrs = sbEntriesByDate[val].reduce( - // (dayAcc, dayVal) => { - // return { - // bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs, - // painthrs: dayAcc.painthrs + dayVal.painthrs, - // }; - // }, - // { bodyhrs: 0, painthrs: 0 } - // ); - // } else { - // dayhrs = { - // bodyhrs: 0, - // painthrs: 0, - // }; - // } - - // const theValue = { - // date: moment(val).format("D ddd"), - // paintHrs: _.round(dayhrs.painthrs, 1), - // bodyHrs: _.round(dayhrs.bodyhrs, 1), - // accTargetHrs: _.round( - // Utils.AsOfDateTargetHours( - // bodyshop.scoreboard_target.dailyBodyTarget + - // bodyshop.scoreboard_target.dailyPaintTarget, - // val - // ), - // 1 - // ), - // accHrs: _.round( - // acc.length > 0 - // ? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs - // : dayhrs.painthrs + dayhrs.bodyhrs, - // 1 - // ), - // }; - - // return [...acc, theValue]; - // }, []); - +export function ScoreboardTicketsBar({ data, bodyshop }) { + const { t } = useTranslation(); return ( - + } + > @@ -122,34 +48,32 @@ export function ScoreboardTicketsBar({ start, end, timetickets, bodyshop }) { - - - - */} + {data && + data.employees.map((e, idx) => ( + + ))} + + {/* + /> */} diff --git a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx index fae8f4379..f85982359 100644 --- a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx +++ b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx @@ -1,12 +1,16 @@ -import React from "react"; -import { useLocation } from "react-router-dom"; -import queryString from "query-string"; -import moment from "moment"; import { useQuery } from "@apollo/client"; +import { Col, Row } from "antd"; +import _ from "lodash"; +import moment from "moment"; +import queryString from "query-string"; +import React, { useMemo } from "react"; +import { useLocation } from "react-router-dom"; import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries"; import AlertComponent from "../alert/alert.component"; -import TimeTicketsDatesSelector from "../../components/ticket-tickets-dates-selector/time-tickets-dates-selector.component"; +import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util"; import ScoreboardTicketsBar from "./scoreboard-timetickets.bar.component"; +import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component"; export default function ScoreboardTimeTickets() { const searchParams = queryString.parse(useLocation().search); @@ -15,25 +19,230 @@ export default function ScoreboardTimeTickets() { ? moment(start) : moment().startOf("week").subtract(7, "days"); const endDate = end ? moment(end) : moment().endOf("week"); + + const fixedPeriods = useMemo(() => { + const endOfThisMonth = moment().endOf("month"); + const startofthisMonth = moment().startOf("month"); + + const endOfLastmonth = moment().subtract(1, "month").endOf("month"); + const startOfLastmonth = moment().subtract(1, "month").startOf("month"); + + const endOfThisWeek = moment().endOf("week"); + const startOfThisWeek = moment().startOf("week"); + + const endOfLastWeek = moment().subtract(1, "week").endOf("week"); + const startOfLastWeek = moment().subtract(1, "week").startOf("week"); + + const allDates = [ + endOfThisMonth, + startofthisMonth, + endOfLastmonth, + startOfLastmonth, + endOfThisWeek, + startOfThisWeek, + endOfLastWeek, + startOfLastWeek, + ]; + const start = moment.min(allDates); + const end = moment.max(allDates); + return { + start, + end, + endOfThisMonth, + startofthisMonth, + endOfLastmonth, + startOfLastmonth, + endOfThisWeek, + startOfThisWeek, + endOfLastWeek, + startOfLastWeek, + }; + }, []); + const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE, { variables: { - start: startDate, - end: endDate, + start: startDate.format("YYYY-MM-DD"), + end: endDate.format("YYYY-MM-DD"), + fixedStart: fixedPeriods.start.format("YYYY-MM-DD"), + fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"), }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", + pollInterval: 60000, + skip: !fixedPeriods, }); - if (error) return ; + const calculatedData = useMemo(() => { + if (!data) return []; + const ret = { + totalThisWeek: 0, + totalLastWeek: 0, + totalThisMonth: 0, + totalLastMonth: 0, + totalOverPeriod: 0, + employees: {}, + }; + data.fixedperiod.forEach((ticket) => { + const ticketDate = moment(ticket.date); + + if (!ret.employees[ticket.employee.employee_number]) { + ret.employees[ticket.employee.employee_number] = { + totalThisWeek: 0, + totalLastWeek: 0, + totalThisMonth: 0, + totalLastMonth: 0, + totalOverPeriod: 0, + }; + } + + if ( + ticketDate.isBetween( + fixedPeriods.startOfThisWeek, + fixedPeriods.endOfThisWeek, + undefined, + "[]" + ) + ) { + ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs; + ret.employees[ticket.employee.employee_number].totalThisWeek = + ret.employees[ticket.employee.employee_number].totalThisWeek + + ticket.productivehrs; + } else if ( + ticketDate.isBetween( + fixedPeriods.startOfLastWeek, + fixedPeriods.endOfLastWeek, + undefined, + "[]" + ) + ) { + ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs; + ret.employees[ticket.employee.employee_number].totalLastWeek = + ret.employees[ticket.employee.employee_number].totalLastWeek + + ticket.productivehrs; + } + if ( + ticketDate.isBetween( + fixedPeriods.startofthisMonth, + fixedPeriods.endOfThisMonth, + undefined, + "[]" + ) + ) { + ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs; + ret.employees[ticket.employee.employee_number].totalThisMonth = + ret.employees[ticket.employee.employee_number].totalThisMonth + + ticket.productivehrs; + } else if ( + ticketDate.isBetween( + fixedPeriods.startOfLastmonth, + fixedPeriods.endOfLastmonth, + undefined, + "[]" + ) + ) { + ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs; + ret.employees[ticket.employee.employee_number].totalLastMonth = + ret.employees[ticket.employee.employee_number].totalLastMonth + + ticket.productivehrs; + } + }); + + const ticketsGroupedByDate = _.groupBy(data.timetickets, "date"); + const listOfDays = Utils.ListDaysBetween({ + start: startDate, + end: endDate, + }); + + const employees = []; + const ret2 = []; + let totals = { + totalproductive: 0, + totalactual: 0, + employees: {}, + }; + + listOfDays.forEach((day) => { + const r = { + date: moment(day).format("MM/DD"), + actualtotal: 0, + productivetotal: 0, + employees: {}, + }; + + if (ticketsGroupedByDate[day]) { + ticketsGroupedByDate[day].forEach((ticket) => { + r.actualtotal = r.actualtotal + ticket.actualhrs; + r.productivetotal = r.productivetotal + ticket.productivehrs; + totals.totalactual = totals.totalactual + ticket.actualhrs; + totals.totalproductive = + totals.totalproductive + ticket.productivehrs; + + employees.push(ticket.employee.employee_number); + //Add to table data. + ret.employees[ticket.employee.employee_number].totalOverPeriod = + ret.employees[ticket.employee.employee_number].totalOverPeriod + + ticket.productivehrs; + + if (!totals.employees[ticket.employee.employee_number]) + totals.employees[ticket.employee.employee_number] = { + totalactual: 0, + totalproductive: 0, + }; + + if (!r.employees[ticket.employee.employee_number]) + r.employees[ticket.employee.employee_number] = { + actual: 0, + productive: 0, + }; + + //Add to totals. + totals.employees[ticket.employee.employee_number].totalproductive = + totals.employees[ticket.employee.employee_number].totalproductive + + ticket.productivehrs; + + totals.employees[ticket.employee.employee_number].totalactual = + totals.employees[ticket.employee.employee_number].totalactual + + ticket.actualhrs; + //Add to dailys. + r.employees[ticket.employee.employee_number].productive = + r.employees[ticket.employee.employee_number].productive + + ticket.productivehrs; + + r.employees[ticket.employee.employee_number].actual = + r.employees[ticket.employee.employee_number].actual + + ticket.actualhrs; + }); + } + + ret2.push(r); + }); + + return { + fixed: ret, + timeperiod: { + totals, + chartData: ret2, + employees: _.uniq(employees), + colors: getColorArray(employees.length), + }, + }; + }, [fixedPeriods, data, startDate, endDate]); + + if (error) return ; + if (loading) return ; return ( -
- - -
+ + + + + + + + ); } @@ -43,3 +252,50 @@ export default function ScoreboardTimeTickets() { //Hours produced in last 7 days //Hours produced for time period by day //Hours produced by employee by day for time period. + +function getColorArray(num) { + return [ + "#3366cc", + "#dc3912", + "#ff9900", + "#109618", + "#990099", + "#0099c6", + "#dd4477", + "#66aa00", + "#b82e2e", + "#316395", + "#3366cc", + "#994499", + "#22aa99", + "#aaaa11", + "#6633cc", + "#e67300", + "#8b0707", + "#651067", + "#329262", + "#5574a6", + "#3b3eac", + "#b77322", + "#16d620", + "#b91383", + "#f4359e", + "#9c5935", + "#a9c413", + "#2a778d", + "#668d1c", + "#bea413", + "#0c5922", + "#743411", + ]; + // var result = []; + // for (var i = 0; i < num; i += 1) { + // var letters = "0123456789ABCDEF".split(""); + // var color = "#"; + // for (var j = 0; j < 6; j += 1) { + // color += letters[Math.floor(Math.random() * 16)]; + // } + // result.push(color); + // } + // return result; +} diff --git a/client/src/components/scoreboard-timetickets/scoreboard-timetickets.stats.component.jsx b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.stats.component.jsx new file mode 100644 index 000000000..863f5f14f --- /dev/null +++ b/client/src/components/scoreboard-timetickets/scoreboard-timetickets.stats.component.jsx @@ -0,0 +1,101 @@ +import { Card, Col, Row, Space, Statistic, Table } from "antd"; +import React from "react"; +import { useTranslation } from "react-i18next"; +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 +)(ScoreboardTicketsStats); + +export function ScoreboardTicketsStats({ data, bodyshop }) { + const { t } = useTranslation(); + + const columns = [ + { + title: t("employees.fields.employee_number"), + dataIndex: "employee_number", + key: "employee_number", + sorter: (a, b) => a.employee_number - b.employee_number, + }, + { + title: t("scoreboard.labels.thisweek"), + dataIndex: "totalThisWeek", + key: "totalThisWeek", + sorter: (a, b) => a.totalThisWeek - b.totalThisWeek, + }, + { + title: t("scoreboard.labels.lastweek"), + dataIndex: "totalLastWeek", + key: "totalLastWeek", + sorter: (a, b) => a.totalLastWeek - b.totalLastWeek, + }, + { + title: t("scoreboard.labels.thismonth"), + dataIndex: "totalThisMonth", + key: "totalThisMonth", + sorter: (a, b) => a.totalThisMonth - b.totalThisMonth, + }, + { + title: t("scoreboard.labels.lastmonth"), + dataIndex: "totalLastMonth", + key: "totalLastMonth", + sorter: (a, b) => a.totalLastMonth - b.totalLastMonth, + }, + { + title: t("scoreboard.labels.totaloverperiod"), + dataIndex: "totalOverPeriod", + key: "totalOverPeriod", + sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod, + }, + ]; + + const tableData = data + ? Object.keys(data.employees).map((key) => { + return { employee_number: key, ...data.employees[key] }; + }) + : []; + + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/client/src/components/ticket-tickets-dates-selector/time-tickets-dates-selector.component.jsx b/client/src/components/ticket-tickets-dates-selector/time-tickets-dates-selector.component.jsx index b7a5ac44b..a827e3fd7 100644 --- a/client/src/components/ticket-tickets-dates-selector/time-tickets-dates-selector.component.jsx +++ b/client/src/components/ticket-tickets-dates-selector/time-tickets-dates-selector.component.jsx @@ -17,6 +17,7 @@ export default function TimeTicketsDatesSelector() { if (!!start && !!end) { history.push({ search: queryString.stringify({ + ...searchParams, start: start.format("YYYY-MM-DD"), end: end.format("YYYY-MM-DD"), }), @@ -25,6 +26,7 @@ export default function TimeTicketsDatesSelector() { } else { history.push({ search: queryString.stringify({ + ...searchParams, start: null, end: null, }), diff --git a/client/src/graphql/jobs-lines.queries.js b/client/src/graphql/jobs-lines.queries.js index 3617df239..b2184f072 100644 --- a/client/src/graphql/jobs-lines.queries.js +++ b/client/src/graphql/jobs-lines.queries.js @@ -61,6 +61,7 @@ export const GET_LINE_TICKET_BY_PK = gql` flat_rate clockon clockoff + rate employee { id first_name diff --git a/client/src/graphql/timetickets.queries.js b/client/src/graphql/timetickets.queries.js index 057edc1cb..12d370560 100644 --- a/client/src/graphql/timetickets.queries.js +++ b/client/src/graphql/timetickets.queries.js @@ -26,7 +26,12 @@ export const QUERY_TICKETS_BY_JOBID = gql` `; export const QUERY_TIME_TICKETS_IN_RANGE = gql` - query QUERY_TIME_TICKETS_IN_RANGE($start: date!, $end: date!) { + query QUERY_TIME_TICKETS_IN_RANGE( + $start: date! + $end: date! + $fixedStart: date! + $fixedEnd: date! + ) { timetickets( where: { date: { _gte: $start, _lte: $end } } order_by: { date: desc_nulls_first } @@ -56,6 +61,35 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql` last_name } } + fixedperiod: timetickets( + where: { date: { _gte: $fixedStart, _lte: $fixedEnd } } + order_by: { date: desc_nulls_first } + ) { + actualhrs + ciecacode + clockoff + clockon + cost_center + created_at + date + id + rate + productivehrs + memo + jobid + flat_rate + job { + id + ro_number + } + employeeid + employee { + id + employee_number + first_name + last_name + } + } } `; diff --git a/client/src/pages/scoreboard/scoreboard.page.container.jsx b/client/src/pages/scoreboard/scoreboard.page.container.jsx index 1abffe30e..a15ad12ac 100644 --- a/client/src/pages/scoreboard/scoreboard.page.container.jsx +++ b/client/src/pages/scoreboard/scoreboard.page.container.jsx @@ -1,19 +1,21 @@ -import Icon from "@ant-design/icons"; +import Icon, { BarsOutlined } from "@ant-design/icons"; import { Tabs } from "antd"; import React, { useEffect } from "react"; import { useTranslation } from "react-i18next"; +import { FaShieldAlt } from "react-icons/fa"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component"; +import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component"; import { setBreadcrumbs, setSelectedHeader, } from "../../redux/application/application.actions"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { FaHardHat, FaRegStickyNote, FaShieldAlt } from "react-icons/fa"; -import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component"; +import queryString from "query-string"; +import { useHistory, useLocation } from "react-router-dom"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -26,7 +28,9 @@ const mapDispatchToProps = (dispatch) => ({ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) { const { t } = useTranslation(); - + const searchParams = queryString.parse(useLocation().search); + const { tab } = searchParams; + const history = useHistory(); useEffect(() => { document.title = t("titles.scoreboard"); setSelectedHeader("scoreboard"); @@ -41,12 +45,21 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) { return ( - + { + searchParams.tab = key; + history.push({ + search: queryString.stringify(searchParams), + }); + }} + > - {t("menus.jobsdetail.general")} + {t("scoreboard.labels.jobs")} } destroyInactiveTabPane @@ -57,8 +70,8 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) { - - {t("menus.jobsdetail.general")} + + {t("scoreboard.labels.timetickets")} } destroyInactiveTabPane diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 9f42c9251..ba02ecc71 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2426,9 +2426,18 @@ "asoftodaytarget": "As of Today", "dailyactual": "Actual (D)", "dailytarget": "Daily", + "jobs": "Jobs", + "lastmonth": "Last Month", + "lastweek": "Last Week", "monthlytarget": "Monthly", + "productivestatistics": "Productive Hours Statistics", + "productivetimeticketsoverdate": "Productive Hours over Selected Dates", "targets": "Targets", + "thismonth": "This Month", + "thisweek": "This Week", + "timetickets": "Timetickets", "todateactual": "Actual (MTD)", + "totaloverperiod": "Total Period", "weeklyactual": "Actual (W)", "weeklytarget": "Weekly", "workingdays": "Working Days / Month" diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 2c26b4a3b..ae5d81de2 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2426,9 +2426,18 @@ "asoftodaytarget": "", "dailyactual": "", "dailytarget": "", + "jobs": "", + "lastmonth": "", + "lastweek": "", "monthlytarget": "", + "productivestatistics": "", + "productivetimeticketsoverdate": "", "targets": "", + "thismonth": "", + "thisweek": "", + "timetickets": "", "todateactual": "", + "totaloverperiod": "", "weeklyactual": "", "weeklytarget": "", "workingdays": "" diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 2f2547393..b71d833e8 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2426,9 +2426,18 @@ "asoftodaytarget": "", "dailyactual": "", "dailytarget": "", + "jobs": "", + "lastmonth": "", + "lastweek": "", "monthlytarget": "", + "productivestatistics": "", + "productivetimeticketsoverdate": "", "targets": "", + "thismonth": "", + "thisweek": "", + "timetickets": "", "todateactual": "", + "totaloverperiod": "", "weeklyactual": "", "weeklytarget": "", "workingdays": ""