diff --git a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx index e8f52ee31..e34ae14f7 100644 --- a/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx +++ b/client/src/components/form-date-time-picker/form-date-time-picker.component.jsx @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; import React, { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import dayjs from "../../utils/day"; -import { dateFormats, dateTimeFormats } from "./formats.js"; +import { fuzzyMatchDate } from "./formats.js"; const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, isDateOnly = false, ...restProps }) => { const [isManualInput, setIsManualInput] = useState(false); @@ -11,79 +11,34 @@ const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, is const handleChange = useCallback( (newDate) => { - if (newDate === null && onChange) { - onChange(null); - } else if (newDate && onChange) { - onChange(newDate); + if (onChange) { + onChange(newDate || null); } setIsManualInput(false); }, [onChange] ); - const normalizeDateTimeString = (input) => { - const upperV = input.toUpperCase().replaceAll(".", "/").replaceAll("-", "/"); - - const [datePart, ...timeParts] = upperV.split(" "); - - if (timeParts.length === 0) { - return datePart; // If there's no time part, just return the date part. - } - - const timePart = timeParts.join(" "); // In case there are multiple spaces, join them back - - // Normalize the time part by ensuring there's a space before AM/PM if not already present - const normalizedTime = timePart.replace(/(\d{1,2})(:\d{2})?\s?(AM|PM)/, "$1$2 $3"); - - // Combine the date part with the normalized time part - return `${datePart} ${normalizedTime}`.trim(); - }; - const handleBlur = useCallback( (e) => { + // Bail if this is not a manual input if (!isManualInput) { return; } - + // Reset manual input flag setIsManualInput(false); - const v = e.target.value; + const v = e?.target?.value; + if (!v) return; - const upperV = normalizeDateTimeString(v); - let parsedDate; + let parsedDate = isDateOnly ? fuzzyMatchDate(v)?.startOf("day") : fuzzyMatchDate(v); - for (const format of isDateOnly ? dateFormats : dateTimeFormats) { - parsedDate = dayjs(upperV, format); - if (parsedDate.isValid()) break; - } - - if (parsedDate && parsedDate.isValid()) { - if (isDateOnly) { - parsedDate = parsedDate.startOf("day"); - } - - if (value && value.isValid && value.isValid()) { - parsedDate = parsedDate.set({ - hours: value.hours(), - minutes: value.minutes(), - seconds: value.seconds(), - milliseconds: value.milliseconds() - }); - } - - if (onlyFuture) { - if (dayjs().subtract(1, "day").isBefore(parsedDate)) { - onChange(parsedDate); - } else { - onChange(dayjs().startOf("day")); - } - } else { - onChange(parsedDate); - } + if (parsedDate && onChange) { + onChange(parsedDate); } }, - [isManualInput, isDateOnly, onlyFuture, onChange, value] + [isManualInput, isDateOnly, onChange] ); const handleKeyDown = useCallback( diff --git a/client/src/components/form-date-time-picker/formats.js b/client/src/components/form-date-time-picker/formats.js index b68704a72..ffe9f68c2 100644 --- a/client/src/components/form-date-time-picker/formats.js +++ b/client/src/components/form-date-time-picker/formats.js @@ -1,96 +1,63 @@ -export const dateTimeFormats = [ - // Four-digit year with time - "M/D/YYYY h:mm A", // Example: 1/5/2023 9:00 AM - "M/D/YYYY h:mmA", // Example: 1/5/2023 9:00AM - "M/D/YYYY h A", // Example: 1/5/2023 9 AM - "M/D/YYYY hA", // Example: 1/5/2023 9AM - "M/D/YYYY hh:mm A", // Example: 1/5/2023 02:25 PM - "M/D/YYYY hh:mm:ss A", // Example: 1/5/2023 02:25:45 PM +import dayjs from "../../utils/day"; - "MM/D/YYYY h:mm A", // Example: 12/5/2023 9:00 AM - "MM/D/YYYY h:mmA", // Example: 12/5/2023 9:00AM - "MM/D/YYYY h A", // Example: 12/5/2023 9 AM - "MM/D/YYYY hA", // Example: 12/5/2023 9AM - "MM/D/YYYY hh:mm A", // Example: 12/5/2023 02:25 PM - "MM/D/YYYY hh:mm:ss A", // Example: 12/5/2023 02:25:45 PM - - "M/DD/YYYY h:mm A", // Example: 1/25/2023 9:00 AM - "M/DD/YYYY h:mmA", // Example: 1/25/2023 9:00AM - "M/DD/YYYY h A", // Example: 1/25/2023 9 AM - "M/DD/YYYY hA", // Example: 1/25/2023 9AM - "M/DD/YYYY hh:mm A", // Example: 1/25/2023 02:25 PM - "M/DD/YYYY hh:mm:ss A", // Example: 1/25/2023 02:25:45 PM - - "MM/DD/YYYY h:mm A", // Example: 12/25/2023 9:00 AM - "MM/DD/YYYY h:mmA", // Example: 12/25/2023 9:00AM - "MM/DD/YYYY h A", // Example: 12/25/2023 9 AM - "MM/DD/YYYY hA", // Example: 12/25/2023 9AM - "MM/DD/YYYY hh:mm A", // Example: 12/25/2023 02:25 PM - "MM/DD/YYYY hh:mm:ss A", // Example: 12/25/2023 02:25:45 PM - - // Two-digit year with time - "M/D/YY h:mm A", // Example: 1/5/23 9:00 AM - "M/D/YY h:mmA", // Example: 1/5/23 9:00AM - "M/D/YY h A", // Example: 1/5/23 9 AM - "M/D/YY hA", // Example: 1/5/23 9AM - "M/D/YY hh:mm A", // Example: 1/5/23 02:25 PM - "M/D/YY hh:mm:ss A", // Example: 1/5/23 02:25:45 PM - - "MM/D/YY h:mm A", // Example: 12/5/23 9:00 AM - "MM/D/YY h:mmA", // Example: 12/5/23 9:00AM - "MM/D/YY h A", // Example: 12/5/23 9 AM - "MM/D/YY hA", // Example: 12/5/23 9AM - "MM/D/YY hh:mm A", // Example: 12/5/23 02:25 PM - "MM/D/YY hh:mm:ss A", // Example: 12/5/23 02:25:45 PM - - "M/DD/YY h:mm A", // Example: 1/25/23 9:00 AM - "M/DD/YY h:mmA", // Example: 1/25/23 9:00AM - "M/DD/YY h A", // Example: 1/25/23 9 AM - "M/DD/YY hA", // Example: 1/25/23 9AM - "M/DD/YY hh:mm A", // Example: 1/25/23 02:25 PM - "M/DD/YY hh:mm:ss A", // Example: 1/25/23 02:25:45 PM - - "MM/DD/YY h:mm A", // Example: 12/25/23 9:00 AM - "MM/DD/YY h:mmA", // Example: 12/25/23 9:00AM - "MM/DD/YY h A", // Example: 12/25/23 9 AM - "MM/DD/YY hA", // Example: 12/25/23 9AM - "MM/DD/YY hh:mm A", // Example: 12/25/23 02:25 PM - "MM/DD/YY hh:mm:ss A", // Example: 12/25/23 02:25:45 PM - - // Four-digit year without time - "M/D/YYYY", // Example: 1/5/2023 - "MM/D/YYYY", // Example: 12/5/2023 - "M/DD/YYYY", // Example: 1/25/2023 - "MM/DD/YYYY", // Example: 12/25/2023 - - // Two-digit year without time - "M/D/YY", // Example: 1/5/23 - "MM/D/YY", // Example: 12/5/23 - "M/DD/YY", // Example: 1/25/23 - "MM/DD/YY" // Example: 12/25/23 -]; - -// CONFIRMED -export const dateFormats = [ +const dateFormats = [ "MMDDYYYY", "MMDDYY", - // Four-digit year - "M/D/YYYY", // Example: 1/5/2023 - "MM/D/YYYY", // Example: 12/5/2023 - "M/DD/YYYY", // Example: 1/25/2023 - "MM/DD/YYYY", // Example: 12/25/2023 - - // Two-digit year - "M/D/YY", // Example: 1/5/23 - "MM/D/YY", // Example: 12/5/23 - "M/DD/YY", // Example: 1/25/23 - "MM/DD/YY", // Example: 12/25/23 - - // Explicitly handle single-digit month and day - "M/D/YY", // Example: 1/5/23 - "M/D/YYYY", // Example: 1/5/2023 - "M/DD/YY", // Example: 1/25/23 - "M/DD/YYYY", // Example: 1/25/2023 - "MM/D/YY", // Example: 12/5/23 - "MM/D/YYYY" // Example: 12/5/2023 + "M/D/YYYY", + "MM/D/YYYY", + "M/DD/YYYY", + "MM/DD/YYYY", + "M/D/YY", + "MM/D/YY", + "M/DD/YY", + "MM/DD/YY" ]; + +const timeFormats = ["h:mm A", "h:mmA", "h A", "hA", "hh:mm A", "hh:mm:ss A"]; + +const dateTimeFormats = [ + ...["M/D/YYYY", "MM/D/YYYY", "M/DD/YYYY", "MM/DD/YYYY", "M/D/YY", "MM/D/YY", "M/DD/YY", "MM/DD/YY"].flatMap( + (dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`) + ), + + ...["MMDDYYYY", "MMDDYY"].flatMap((dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`)), + + "M/D/YYYY", + "MM/D/YYYY", + "M/DD/YYYY", + "MM/DD/YYYY", + "M/D/YY", + "MM/D/YY", + "M/DD/YY", + "MM/DD/YY", + "MMDDYYYY", + "MMDDYY" +]; + +const sanitizeInput = (input) => + input + .trim() + .toUpperCase() + .replace(/\s*(am|pm)\s*/i, " $1") + .replaceAll(".", "/") + .replaceAll("-", "/"); + +export const fuzzyMatchDate = (dateString) => { + const sanitizedInput = sanitizeInput(dateString); + + for (const format of dateFormats) { + const parsedDate = dayjs(sanitizedInput, format, true); + if (parsedDate.isValid()) { + return parsedDate; + } + } + + for (const format of dateTimeFormats) { + const parsedDateTime = dayjs(sanitizedInput, format, true); + if (parsedDateTime.isValid()) { + return parsedDateTime; // Return the dayjs object + } + } + + return null; // If no matching format is found +}; diff --git a/hasura/metadata/cron_triggers.yaml b/hasura/metadata/cron_triggers.yaml index c9aa560ec..39202c4a4 100644 --- a/hasura/metadata/cron_triggers.yaml +++ b/hasura/metadata/cron_triggers.yaml @@ -1,6 +1,6 @@ - name: AutoHouse Data Pump webhook: '{{HASURA_API_URL}}/data/ah' - schedule: 0 7 * * * + schedule: 0 6 * * * include_in_metadata: true payload: {} headers: @@ -8,7 +8,7 @@ value_from_env: DATAPUMP_AUTH - name: Claimscorp Data Pump webhook: '{{HASURA_API_URL}}/data/cc' - schedule: 30 7 * * * + schedule: 30 6 * * * include_in_metadata: true payload: {} headers: @@ -16,7 +16,7 @@ value: DATAPUMP_AUTH - name: Kaizen Data Pump webhook: '{{HASURA_API_URL}}/data/kaizen' - schedule: 30 6 * * * + schedule: 30 5 * * * include_in_metadata: true payload: {} headers: diff --git a/server/data/kaizen.js b/server/data/kaizen.js index 1dccef61c..5a09bec24 100644 --- a/server/data/kaizen.js +++ b/server/data/kaizen.js @@ -56,8 +56,8 @@ exports.default = async (req, res) => { try { const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, { bodyshopid: bodyshop.id, - start: start ? moment(start).startOf("hours") : moment().subtract(2, "hours").startOf("hour"), - ...(end && { end: moment(end).endOf("hours") }) + start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"), + ...(end && { end: moment(end).endOf("day") }) }); const kaizenObject = { @@ -176,24 +176,19 @@ exports.default = async (req, res) => { } finally { sftp.end(); } - // sendServerEmail({ - // subject: `Kaizen Report ${moment().format("MM-DD-YY")}`, - // text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))} - // Uploaded: ${JSON.stringify( - // allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })), - // null, - // 2 - // )} - // `, - // }); + sendServerEmail({ + subject: `Kaizen Report ${moment().format("MM-DD-YY")}`, + text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))} + Uploaded: ${JSON.stringify( + allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })), + null, + 2 + )} + ` + }); res.sendStatus(200); } catch (error) { res.status(200).json(error); - sendServerEmail({ - subject: `Kaizen Report ${moment().format("MM-DD-YY @ HH:mm:ss")}`, - text: `Errors: JSON.stringify(error)} - All Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}` - }); } }; diff --git a/server/job/job-totals-USA.js b/server/job/job-totals-USA.js index 3dabed316..ca9a74bca 100644 --- a/server/job/job-totals-USA.js +++ b/server/job/job-totals-USA.js @@ -965,22 +965,17 @@ function CalculateTaxesTotals(job, otherTotals) { } }); - if (job.adjustment_bottom_line) { - const subtotal_before_adjustment = subtotal.add(Dinero({ amount: Math.round(job.adjustment_bottom_line * -100) })); - const percent_of_adjustment = - Math.round( - subtotal_before_adjustment.toUnit() / - (job.adjustment_bottom_line > 0 ? job.adjustment_bottom_line : job.adjustment_bottom_line * -1) - ) / 100; - - Object.keys(taxableAmountsByTier).forEach((taxTierKey) => { - taxable_adjustment = taxableAmountsByTier[taxTierKey].multiply(percent_of_adjustment); - if (job.adjustment_bottom_line > 0) { - taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].add(taxable_adjustment); - } else { - taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].subtract(taxable_adjustment); + if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) { + for (let tyCounter = 1; tyCounter <= 5; tyCounter++) { + if (IsTrueOrYes(pfp["PAN"][`prt_tx_in${tyCounter}`])) { + //This amount is taxable for this type. + taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add( + Dinero({ + amount: Math.round(job.adjustment_bottom_line * 100) + }) + ); } - }); + } } const remainingTaxableAmounts = taxableAmountsByTier;