Compare commits
22 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa4b4998fa | ||
|
|
744593e96a | ||
|
|
411605e121 | ||
|
|
cdcef798df | ||
|
|
718c8291a8 | ||
|
|
f1e84c348b | ||
|
|
5f513a8bef | ||
|
|
4b96d5a707 | ||
|
|
220f3d4410 | ||
|
|
841f62bd84 | ||
|
|
91e2e7931b | ||
|
|
3c6faf8473 | ||
|
|
c994eaaa8e | ||
|
|
517d8f4163 | ||
|
|
9deb2964a5 | ||
|
|
9cf9f8b844 | ||
|
|
ad46ea74c0 | ||
|
|
2a28855e4b | ||
|
|
8d25f60097 | ||
|
|
982a51f16e | ||
|
|
68d02648d7 | ||
|
|
89d5b1cfe4 |
@@ -6,11 +6,11 @@ import {
|
||||
PauseCircleOutlined
|
||||
} from "@ant-design/icons";
|
||||
import { Card, Col, Row, Space, Tooltip } from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import Dinero from "dinero.js";
|
||||
|
||||
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
|
||||
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
||||
@@ -18,8 +18,8 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ
|
||||
|
||||
import dayjs from "../../utils/day";
|
||||
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const cardColor = (ssbuckets, totalHrs) => {
|
||||
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
|
||||
@@ -213,21 +213,13 @@ const EstimatorToolTip = ({ metadata, cardSettings }) => {
|
||||
};
|
||||
|
||||
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
|
||||
const amount = metadata?.job_totals?.totals?.subtotal?.amount;
|
||||
const dineroAmount = amount ? Dinero({ amount: parseInt(amount * 100) }).toFormat("0,0.00") : null;
|
||||
const dineroAmount = Dinero(metadata?.job_totals?.totals?.subtotal ?? Dinero()).toFormat();
|
||||
|
||||
return (
|
||||
cardSettings?.subtotal && (
|
||||
<Col span={cardSettings.compact ? 24 : 12}>
|
||||
<EllipsesToolTip
|
||||
title={!!amount ? `${t("production.statistics.currency_symbol")}${dineroAmount}` : null}
|
||||
kiosk={cardSettings.kiosk}
|
||||
>
|
||||
{!!amount ? (
|
||||
<span>{`${t("production.statistics.currency_symbol")}${dineroAmount}`}</span>
|
||||
) : (
|
||||
<span> </span>
|
||||
)}
|
||||
<EllipsesToolTip title={`${dineroAmount}`} kiosk={cardSettings.kiosk}>
|
||||
{dineroAmount}
|
||||
</EllipsesToolTip>
|
||||
</Col>
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Card, Statistic } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { defaultKanbanSettings, statisticsItems } from "./settings/defaultKanbanSettings.js";
|
||||
import Dinero from "dinero.js";
|
||||
|
||||
export const StatisticType = {
|
||||
HOURS: "hours",
|
||||
@@ -31,8 +32,33 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
||||
return items.reduce((acc, item) => acc + (item[key]?.aggregate?.sum?.[subKey] || 0), 0);
|
||||
};
|
||||
|
||||
const sumDineroAmounts = (items, key, getAmountFn) => {
|
||||
return items.reduce(
|
||||
(acc, item) => {
|
||||
const amount = getAmountFn(item, key);
|
||||
const dineroAmount = Dinero(amount ?? 0);
|
||||
return acc.add(dineroAmount);
|
||||
},
|
||||
Dinero({ amount: 0 })
|
||||
);
|
||||
};
|
||||
|
||||
const calculateTotalAmount = (items, key) => {
|
||||
return items.reduce((acc, item) => acc + (item[key]?.totals?.subtotal?.amount || 0), 0);
|
||||
return items.reduce((acc, item) => acc.add(Dinero(item[key]?.totals?.subtotal ?? Dinero())), Dinero({ amount: 0 }));
|
||||
};
|
||||
|
||||
const calculateReducerTotalAmount = (lanes, key) => {
|
||||
return lanes.reduce(
|
||||
(acc, lane) => {
|
||||
return acc.add(
|
||||
lane.cards.reduce(
|
||||
(laneAcc, card) => laneAcc.add(Dinero(card.metadata[key]?.totals?.subtotal ?? Dinero())),
|
||||
Dinero({ amount: 0 })
|
||||
)
|
||||
);
|
||||
},
|
||||
Dinero({ amount: 0 })
|
||||
);
|
||||
};
|
||||
|
||||
const calculateReducerTotal = (lanes, key, subKey) => {
|
||||
@@ -43,14 +69,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
||||
}, 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 === StatisticType.JOBS) {
|
||||
return value.toFixed(0);
|
||||
@@ -87,9 +105,15 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
||||
const totalAmountInProduction = useMemo(() => {
|
||||
if (!cardSettings.totalAmountInProduction) return null;
|
||||
const total = calculateTotalAmount(data, "job_totals");
|
||||
return parseFloat(total.toFixed(2));
|
||||
return total.toFormat("$0,0.00");
|
||||
}, [data, cardSettings.totalAmountInProduction]);
|
||||
|
||||
const totalAmountOnBoard = useMemo(() => {
|
||||
if (!reducerData || !cardSettings.totalAmountOnBoard) return null;
|
||||
const total = calculateReducerTotalAmount(reducerData.lanes, "job_totals");
|
||||
return total.toFormat("$0,0.00");
|
||||
}, [reducerData, cardSettings.totalAmountOnBoard]);
|
||||
|
||||
const totalHrsOnBoard = useMemo(() => {
|
||||
if (!reducerData || !cardSettings.totalHrsOnBoard) return null;
|
||||
const total =
|
||||
@@ -118,12 +142,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
||||
[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 tasksInProduction = useMemo(() => {
|
||||
if (!data || !cardSettings.tasksInProduction) return null;
|
||||
return data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0);
|
||||
@@ -191,7 +209,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
||||
<Statistic
|
||||
title={t(`production.statistics.${stat.label}`)}
|
||||
value={formatValue(stat.value, stat.type)}
|
||||
prefix={stat.type === StatisticType.AMOUNT ? t("production.statistics.currency_symbol") : undefined}
|
||||
suffix={
|
||||
stat.type === StatisticType.HOURS
|
||||
? t("production.statistics.hours")
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Button, Dropdown, Input, Space, Statistic, Table } from "antd";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { PageHeader } from "@ant-design/pro-layout";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Dropdown, Input, Space, Statistic, Table } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import ReactDragListView from "react-drag-listview";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import Prompt from "../../utils/prompt.js";
|
||||
import AlertComponent from "../alert/alert.component.jsx";
|
||||
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
||||
import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
|
||||
import ProductionListPrint from "./production-list-print.component";
|
||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import Prompt from "../../utils/prompt.js";
|
||||
import _ from "lodash";
|
||||
import AlertComponent from "../alert/alert.component.jsx";
|
||||
import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -43,7 +43,7 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
|
||||
const initialStateRef = useRef(
|
||||
(bodyshop.production_config &&
|
||||
bodyshop.production_config.find((p) => p.name === defaultView)?.columns.tableState) ||
|
||||
bodyshop.production_config[0]?.columns.tableState || {
|
||||
(bodyshop.production_config && bodyshop.production_config[0]?.columns.tableState) || {
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" }
|
||||
}
|
||||
|
||||
@@ -206,76 +206,98 @@ export function TimeTicketList({
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
]),
|
||||
{
|
||||
title: t("timetickets.fields.created_by"),
|
||||
dataIndex: "created_by",
|
||||
key: "created_by",
|
||||
sorter: (a, b) => alphaSort(a.created_by, b.created_by),
|
||||
sortOrder: state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
|
||||
render: (text, record) => record.created_by
|
||||
},
|
||||
// {
|
||||
// title: "Pay",
|
||||
// dataIndex: "pay",
|
||||
// key: "pay",
|
||||
// render: (text, record) =>
|
||||
// Dinero({ amount: Math.round(record.rate * 100) })
|
||||
// .multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
||||
// .toFormat("$0.00"),
|
||||
// },
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
{techConsole && (
|
||||
<TimeTicketEnterButton
|
||||
actions={{ refetch }}
|
||||
context={{ id: record.id, timeticket: record }}
|
||||
disabled={!record.job || disabled}
|
||||
>
|
||||
<EditFilled />
|
||||
</TimeTicketEnterButton>
|
||||
)}
|
||||
{!techConsole && (
|
||||
<RbacWrapper
|
||||
action="timetickets:edit"
|
||||
noauth={() => {
|
||||
return <div />;
|
||||
}}
|
||||
>
|
||||
<TimeTicketEnterButton
|
||||
actions={{ refetch }}
|
||||
context={{
|
||||
id: record.id,
|
||||
timeticket: record
|
||||
}}
|
||||
disabled={
|
||||
HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:editcommitted"
|
||||
}) &&
|
||||
HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:shiftedit"
|
||||
})
|
||||
{
|
||||
title: t("timetickets.fields.created_by"),
|
||||
dataIndex: "created_by",
|
||||
key: "created_by",
|
||||
sorter: (a, b) => alphaSort(a.created_by, b.created_by),
|
||||
sortOrder: state.sortedInfo.columnKey === "created_by" && state.sortedInfo.order,
|
||||
render: (text, record) => record.created_by
|
||||
},
|
||||
// {
|
||||
// title: "Pay",
|
||||
// dataIndex: "pay",
|
||||
// key: "pay",
|
||||
// render: (text, record) =>
|
||||
// Dinero({ amount: Math.round(record.rate * 100) })
|
||||
// .multiply(record.flat_rate ? record.productivehrs : record.actualhrs)
|
||||
// .toFormat("$0.00"),
|
||||
// },
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
{techConsole && (
|
||||
<TimeTicketEnterButton
|
||||
actions={{ refetch }}
|
||||
context={{ id: record.id, timeticket: record }}
|
||||
disabled={!record.job || disabled}
|
||||
>
|
||||
<EditFilled />
|
||||
</TimeTicketEnterButton>
|
||||
)}
|
||||
{!techConsole && (
|
||||
<RbacWrapper
|
||||
action="timetickets:edit"
|
||||
noauth={() => {
|
||||
return <div />;
|
||||
}}
|
||||
>
|
||||
<TimeTicketEnterButton
|
||||
actions={{ refetch }}
|
||||
context={{
|
||||
id: record.id,
|
||||
timeticket: record
|
||||
}}
|
||||
disabled={
|
||||
record.ciecacode
|
||||
? record.committed_at
|
||||
? HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:editcommitted"
|
||||
}) &&
|
||||
HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:edit"
|
||||
})
|
||||
: HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:edit"
|
||||
})
|
||||
: record.committed_at
|
||||
? HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:editcommitted"
|
||||
}) &&
|
||||
HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:shiftedit"
|
||||
})
|
||||
: HasRbacAccess({
|
||||
bodyshop,
|
||||
authLevel: authLevel,
|
||||
action: "timetickets:shiftedit"
|
||||
})
|
||||
? disabled
|
||||
: !record.jobid
|
||||
}
|
||||
>
|
||||
<EditFilled />
|
||||
</TimeTicketEnterButton>
|
||||
</RbacWrapper>
|
||||
)}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
>
|
||||
<EditFilled />
|
||||
</TimeTicketEnterButton>
|
||||
</RbacWrapper>
|
||||
)}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
- name: AutoHouse Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/ah'
|
||||
schedule: 0 6 * * *
|
||||
include_in_metadata: true
|
||||
payload: {}
|
||||
headers:
|
||||
- name: x-imex-auth
|
||||
value_from_env: DATAPUMP_AUTH
|
||||
- name: Claimscorp Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/cc'
|
||||
schedule: 30 6 * * *
|
||||
include_in_metadata: true
|
||||
payload: {}
|
||||
headers:
|
||||
- name: x-imex-auth
|
||||
value_from_env: DATAPUMP_AUTH
|
||||
- name: Kaizen Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/kaizen'
|
||||
schedule: 30 5 * * *
|
||||
|
||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
||||
};
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
//Query for the List of Bodyshop Clients.
|
||||
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
||||
const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
|
||||
|
||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
||||
};
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
//Query for the List of Bodyshop Clients.
|
||||
logger.log("claimscorp-start", "DEBUG", "api", null, null);
|
||||
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS);
|
||||
|
||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
||||
};
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
// Only process if in production environment.
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
//Query for the List of Bodyshop Clients.
|
||||
logger.log("kaizen-start", "DEBUG", "api", null, null);
|
||||
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
||||
|
||||
Reference in New Issue
Block a user