Merged in release/2024-08-23 (pull request #1662)

Release/2024 08 23
This commit is contained in:
Dave Richer
2024-08-24 02:08:03 +00:00
5 changed files with 100 additions and 180 deletions

View File

@@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import dayjs from "../../utils/day"; 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 DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, isDateOnly = false, ...restProps }) => {
const [isManualInput, setIsManualInput] = useState(false); const [isManualInput, setIsManualInput] = useState(false);
@@ -11,79 +11,34 @@ const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, is
const handleChange = useCallback( const handleChange = useCallback(
(newDate) => { (newDate) => {
if (newDate === null && onChange) { if (onChange) {
onChange(null); onChange(newDate || null);
} else if (newDate && onChange) {
onChange(newDate);
} }
setIsManualInput(false); setIsManualInput(false);
}, },
[onChange] [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( const handleBlur = useCallback(
(e) => { (e) => {
// Bail if this is not a manual input
if (!isManualInput) { if (!isManualInput) {
return; return;
} }
// Reset manual input flag
setIsManualInput(false); setIsManualInput(false);
const v = e.target.value; const v = e?.target?.value;
if (!v) return; if (!v) return;
const upperV = normalizeDateTimeString(v); let parsedDate = isDateOnly ? fuzzyMatchDate(v)?.startOf("day") : fuzzyMatchDate(v);
let parsedDate;
for (const format of isDateOnly ? dateFormats : dateTimeFormats) { if (parsedDate && onChange) {
parsedDate = dayjs(upperV, format); onChange(parsedDate);
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);
}
} }
}, },
[isManualInput, isDateOnly, onlyFuture, onChange, value] [isManualInput, isDateOnly, onChange]
); );
const handleKeyDown = useCallback( const handleKeyDown = useCallback(

View File

@@ -1,96 +1,63 @@
export const dateTimeFormats = [ import dayjs from "../../utils/day";
// 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
"MM/D/YYYY h:mm A", // Example: 12/5/2023 9:00 AM const dateFormats = [
"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 = [
"MMDDYYYY", "MMDDYYYY",
"MMDDYY", "MMDDYY",
// Four-digit year "M/D/YYYY",
"M/D/YYYY", // Example: 1/5/2023 "MM/D/YYYY",
"MM/D/YYYY", // Example: 12/5/2023 "M/DD/YYYY",
"M/DD/YYYY", // Example: 1/25/2023 "MM/DD/YYYY",
"MM/DD/YYYY", // Example: 12/25/2023 "M/D/YY",
"MM/D/YY",
// Two-digit year "M/DD/YY",
"M/D/YY", // Example: 1/5/23 "MM/DD/YY"
"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
]; ];
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
};

View File

@@ -1,3 +1,11 @@
- name: Kaizen Data Pump
webhook: '{{HASURA_API_URL}}/data/kaizen'
schedule: 30 6 * * *
include_in_metadata: true
payload: {}
headers:
- name: x-imex-auth
value_from_env: DATAPUMP_AUTH
- name: Task Reminders - name: Task Reminders
webhook: '{{HASURA_API_URL}}/tasks-remind-handler' webhook: '{{HASURA_API_URL}}/tasks-remind-handler'
schedule: '*/15 * * * *' schedule: '*/15 * * * *'

View File

@@ -56,8 +56,8 @@ exports.default = async (req, res) => {
try { try {
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, { const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
bodyshopid: bodyshop.id, bodyshopid: bodyshop.id,
start: start ? moment(start).startOf("hours") : moment().subtract(2, "hours").startOf("hour"), start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).endOf("hours") }) ...(end && { end: moment(end).endOf("day") })
}); });
const kaizenObject = { const kaizenObject = {
@@ -176,24 +176,19 @@ exports.default = async (req, res) => {
} finally { } finally {
sftp.end(); sftp.end();
} }
// sendServerEmail({ sendServerEmail({
// subject: `Kaizen Report ${moment().format("MM-DD-YY")}`, subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
// text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))} text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
// Uploaded: ${JSON.stringify( Uploaded: ${JSON.stringify(
// allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })), allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
// null, null,
// 2 2
// )} )}
// `, `
// }); });
res.sendStatus(200); res.sendStatus(200);
} catch (error) { } catch (error) {
res.status(200).json(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))}`
});
} }
}; };

View File

@@ -965,22 +965,17 @@ function CalculateTaxesTotals(job, otherTotals) {
} }
}); });
if (job.adjustment_bottom_line) { if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const subtotal_before_adjustment = subtotal.add(Dinero({ amount: Math.round(job.adjustment_bottom_line * -100) })); for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
const percent_of_adjustment = if (IsTrueOrYes(pfp["PAN"][`prt_tx_in${tyCounter}`])) {
Math.round( //This amount is taxable for this type.
subtotal_before_adjustment.toUnit() / taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
(job.adjustment_bottom_line > 0 ? job.adjustment_bottom_line : job.adjustment_bottom_line * -1) Dinero({
) / 100; amount: Math.round(job.adjustment_bottom_line * 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);
} }
}); }
} }
const remainingTaxableAmounts = taxableAmountsByTier; const remainingTaxableAmounts = taxableAmountsByTier;