Compare commits

...

27 Commits

Author SHA1 Message Date
Patrick Fic
affbb3f168 IO-2920 Rever test URL to correct value for intellipay. 2024-11-27 13:15:03 -08:00
Patrick Fic
cb4d4e4c2c IO-2920 Remove shop config for intellipay amounts. 2024-11-25 14:09:09 -08:00
Patrick Fic
225b57fd58 IO-2920 Update checkfee method to get CC amount based on new information. 2024-11-25 14:08:38 -08:00
Patrick Fic
268b1ba9c1 IO-2920 Refactor fee discounting to use API to check. 2024-11-25 10:51:48 -08:00
Patrick Fic
9c1774c417 Merge branch 'release/2024-11-15' into feature/IO-2920-cash-discounting 2024-11-15 10:58:22 -08:00
Allan Carr
26b3a43ce5 Merge branch 'feature/IO-3027-Datapumps-Refactor' into release/2024-11-15
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>

# Conflicts:
#	.vscode/settings.json
2024-11-15 10:05:08 -08:00
Allan Carr
78678dd3dc IO-3027 Datapumps Refactor
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-15 10:04:03 -08:00
Allan Carr
9dc4546b2e Merged in feature/IO-3033-Total-Loss-Indicator (pull request #1925)
IO-3033 Total Loss Indicator

Approved-by: Dave Richer
2024-11-15 17:47:56 +00:00
Allan Carr
95aa0e45a6 IO-3033 Total Loss Indicator
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-14 16:47:35 -08:00
Allan Carr
ce9a77efcf IO-3027 Datapumps Refactor
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-14 16:15:17 -08:00
Dave Richer
e9e1e820a7 release/2024-11-15 - Expose S3 client through createS3Client
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-11-14 11:57:40 -08:00
Allan Carr
c7fc75aa5c Merged in feature/IO-3031-Appointment-Schedule-View-Day (pull request #1922)
IO-3031 View Day when Scheduling

Approved-by: Dave Richer
2024-11-14 19:50:34 +00:00
Allan Carr
b027a4e618 IO-3031 Adjust prop
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-14 11:52:47 -08:00
Allan Carr
98d2372daf Merged in feature/IO-3030-QBO-Payment-Private-Note (pull request #1920)
IO-3030 QBO Payment Private Note

Approved-by: Dave Richer
2024-11-14 19:40:20 +00:00
Allan Carr
bf51380167 IO-3031 View Day when Scheduling
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-14 11:19:09 -08:00
Dave Richer
1ec827097f Merged in feature/IO-3029-Enhanced-Logging-File-Based (pull request #1921)
feature/IO-3029-Enhanced-Logging-File-Based: Adjust XML and JSON log to always upload
2024-11-14 18:55:57 +00:00
Allan Carr
ff7dd7d3ea IO-3030 QBO Payment Private Note
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-14 10:37:08 -08:00
Dave Richer
8cc4f88fa7 Merged in feature/IO-3029-Enhanced-Logging-File-Based (pull request #1918)
feature/IO-3029-Enhanced-Logging-File-Based: Final Enhancements
2024-11-14 16:36:13 +00:00
Dave Richer
7e6ab3a5ff Merged in feature/IO-3029-Enhanced-Logging-File-Based (pull request #1916)
feature/IO-3029-Enhanced-Logging-File-Based: Update Stream Key name
2024-11-14 04:14:54 +00:00
Dave Richer
34f876f838 Merged in feature/IO-3029-Enhanced-Logging-File-Based (pull request #1914)
feature/IO-3029-Enhanced-Logging-File-Based: Add File based S3 Logging.
2024-11-14 03:57:24 +00:00
Allan Carr
2f3eccf3d8 Merged in feature/IO-3028-Word-Wrap-Line-Description (pull request #1912)
IO-3028 Extend to Notes
2024-11-13 18:50:32 +00:00
Allan Carr
2b3e64d607 IO-3028 Extend to Notes
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-13 10:49:47 -08:00
Allan Carr
05b20505bb Merged in feature/IO-3028-Word-Wrap-Line-Description (pull request #1910)
IO-3028 Word Wrap Line Description

Approved-by: Dave Richer
2024-11-13 18:17:34 +00:00
Allan Carr
bddeae945c IO-3028 Word Wrap Line Description
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-11-13 10:06:01 -08:00
Patrick Fic
5b267f03b9 Add additional GIN indexes for db. 2024-11-12 20:21:43 -08:00
Patrick Fic
9aab47d8f8 IO-2920 Update config & totals for discount. 2024-11-05 16:37:21 -08:00
Patrick Fic
f2f84e2da8 Merge branch 'master-AIO' into feature/IO-2920-cash-discounting 2024-11-05 16:00:47 -08:00
26 changed files with 1194 additions and 434 deletions

30
.vscode/settings.json vendored
View File

@@ -8,5 +8,35 @@
"pattern": "**/IMEX.xml",
"systemId": "logs/IMEX.xsd"
}
],
"cSpell.words": [
"antd",
"appointmentconfirmation",
"appt",
"autohouse",
"autohouseid",
"billlines",
"bodyshop",
"bodyshopid",
"bodyshops",
"CIECA",
"claimscorp",
"claimscorpid",
"Dinero",
"driveable",
"IMEX",
"imexshopid",
"jobid",
"joblines",
"Kaizen",
"labhrs",
"larhrs",
"mixdata",
"ownr",
"promanager",
"shopname",
"smartscheduling",
"timetickets",
"touchtime"
]
}

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1">
<babeledit_project be_version="2.7.1" version="1.2">
<!--
BabelEdit project file
@@ -11156,6 +11156,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>imexpay</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>insurancecos</name>
<definition_loaded>false</definition_loaded>
@@ -11198,27 +11219,6 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>intellipay</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>intellipay_cash_discount</name>
<definition_loaded>false</definition_loaded>
@@ -11747,6 +11747,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ttl_adjustment</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ttl_tax_adjustment</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
@@ -11775,6 +11817,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>romepay</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>scheduling</name>
<definition_loaded>false</definition_loaded>
@@ -20639,6 +20702,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>time</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total</name>
<definition_loaded>false</definition_loaded>
@@ -21811,6 +21895,48 @@
<folder_node>
<name>columns</name>
<children>
<concept_node>
<name>average_human_readable</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>average_value</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>duration</name>
<definition_loaded>false</definition_loaded>
@@ -31746,6 +31872,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tlos_ind</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>towin</name>
<definition_loaded>false</definition_loaded>
@@ -36253,6 +36400,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_cust_payable_cash_discount</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_repairs</name>
<definition_loaded>false</definition_loaded>
@@ -36274,6 +36442,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_repairs_cash_discount</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_sales</name>
<definition_loaded>false</definition_loaded>
@@ -48360,6 +48549,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tasks_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>tasks_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48402,6 +48612,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_amount_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_amount_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48444,6 +48675,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_hours_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_hours_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48465,6 +48717,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_jobs_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_jobs_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48507,6 +48780,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lab_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lab_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48549,6 +48843,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lar_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lar_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48724,6 +49039,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tasks_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>tasks_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48766,6 +49102,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_amount_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_amount_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48808,6 +49165,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_hours_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_hours_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48829,6 +49207,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_jobs_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_jobs_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48871,6 +49270,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lab_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lab_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -48913,6 +49333,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lar_in_view</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>total_lar_on_board</name>
<definition_loaded>false</definition_loaded>
@@ -51761,6 +52202,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_not_production_status</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_over_time</name>
<definition_loaded>false</definition_loaded>
@@ -54225,6 +54687,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>created_by</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>description</name>
<definition_loaded>false</definition_loaded>
@@ -54487,6 +54970,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>related_items</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>remind_at</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,4 +1,4 @@
import { DatePicker } from "antd";
import { DatePicker, Space, TimePicker } from "antd";
import PropTypes from "prop-types";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -20,6 +20,7 @@ const DateTimePicker = ({
onlyFuture,
onlyToday,
isDateOnly = false,
isSeparatedTime = false,
bodyshop,
...restProps
}) => {
@@ -87,24 +88,56 @@ const DateTimePicker = ({
return (
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
<DatePicker
showTime={
isDateOnly
? false
: {
format: "hh:mm a",
minuteStep: 15,
defaultValue: dayjs(dayjs(), "HH:mm:ss")
}
}
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
value={value ? dayjs(value) : null}
onChange={handleChange}
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
onBlur={onBlur || handleBlur}
disabledDate={handleDisabledDate}
{...restProps}
/>
{isSeparatedTime && (
<Space direction="vertical" style={{ width: "100%" }}>
<DatePicker
showTime={false}
format="MM/DD/YYYY"
value={value ? dayjs(value) : null}
onChange={handleChange}
placeholder={t("general.labels.date")}
onBlur={handleBlur}
disabledDate={handleDisabledDate}
isDateOnly={true}
{...restProps}
/>
{value && (
<TimePicker
format="hh:mm a"
minuteStep={15}
defaultOpenValue={dayjs()
.minute(Math.floor(dayjs().minute() / 15) * 15)
.second(0)}
onChange={(value) => {
handleChange(value);
onBlur();
}}
placeholder={t("general.labels.time")}
{...restProps}
/>
)}
</Space>
)}
{!isSeparatedTime && (
<DatePicker
showTime={
isDateOnly
? false
: {
format: "hh:mm a",
minuteStep: 15,
defaultValue: dayjs(dayjs(), "HH:mm:ss")
}
}
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
value={value ? dayjs(value) : null}
onChange={handleChange}
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
onBlur={onBlur || handleBlur}
disabledDate={handleDisabledDate}
{...restProps}
/>
)}
</div>
);
};
@@ -116,7 +149,8 @@ DateTimePicker.propTypes = {
id: PropTypes.string,
onlyFuture: PropTypes.bool,
onlyToday: PropTypes.bool,
isDateOnly: PropTypes.bool
isDateOnly: PropTypes.bool,
isSeparatedTime: PropTypes.bool
};
export default connect(mapStateToProps, null)(DateTimePicker);

View File

@@ -118,8 +118,7 @@ export function JobLinesComponent({
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {})
}
}),
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
ellipsis: true
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
},
{
title: t("joblines.fields.oem_partno"),

View File

@@ -45,7 +45,8 @@ export default function JobLineNotePopup({ jobline, disabled }) {
if (editing)
return (
<div>
<Input
<Input.TextArea
autoSize
autoFocus
suffix={loading ? <LoadingSpinner /> : null}
value={note}

View File

@@ -1,10 +1,10 @@
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Form, Input, InputNumber, Modal, Select, Switch } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import InputCurrency from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -61,7 +61,7 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
]}
name="line_desc"
>
<Input />
<Input.TextArea autoSize />
</Form.Item>
<JoblinesPreset form={form} />
</LayoutFormRow>

View File

@@ -7,6 +7,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobTotalsCashDiscount from "./jobs-totals.cash-discount-display.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -132,23 +133,41 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
]
}),
{
key: t("jobs.labels.total_repairs"),
total: job.job_totals.totals.total_repairs,
bold: true
},
...(bodyshop.intellipay_config?.enable_cash_discount
? [
{
key: t("jobs.labels.total_repairs_cash_discount"),
total: job.job_totals.totals.total_repairs,
bold: true
},
{
key: t("jobs.labels.total_repairs"),
render: <JobTotalsCashDiscount amountDinero={job.job_totals.totals.total_repairs} />,
bold: true
}
]
: [
{
key: t("jobs.labels.total_repairs"),
total: job.job_totals.totals.total_repairs,
bold: true
}
]),
{
key: t("jobs.fields.ded_amt"),
total: job.job_totals.totals.custPayable.deductible
},
...(InstanceRenderManager({
imex: [{
key: t("jobs.fields.federal_tax_payable"),
total: job.job_totals.totals.custPayable.federal_tax
}],
...InstanceRenderManager({
imex: [
{
key: t("jobs.fields.federal_tax_payable"),
total: job.job_totals.totals.custPayable.federal_tax
}
],
rome: [],
promanager: "USE_ROME"
})),
}),
{
key: t("jobs.fields.other_amount_payable"),
total: job.job_totals.totals.custPayable.other_customer_amount
@@ -158,11 +177,26 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
total: job.job_totals.totals.custPayable.dep_taxes
},
{
key: t("jobs.labels.total_cust_payable"),
total: job.job_totals.totals.custPayable.total,
bold: true
},
...(bodyshop.intellipay_config?.enable_cash_discount
? [
{
key: t("jobs.labels.total_cust_payable_cash_discount"),
total: job.job_totals.totals.custPayable.total,
bold: true
},
{
key: t("jobs.labels.total_cust_payable"),
render: <JobTotalsCashDiscount amountDinero={job.job_totals.totals.custPayable.total} />,
bold: true
}
]
: [
{
key: t("jobs.labels.total_cust_payable"),
total: job.job_totals.totals.custPayable.total,
bold: true
}
]),
{
key: t("jobs.labels.net_repairs"),
total: job.job_totals.totals.net_repairs,
@@ -188,7 +222,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
dataIndex: "total",
key: "total",
align: "right",
render: (text, record) => Dinero(record.total).toFormat(),
render: (text, record) => (record.render ? record.render : Dinero(record.total).toFormat()),
width: "20%",
onCell: (record, rowIndex) => {
return { style: { fontWeight: record.bold && "bold" } };

View File

@@ -0,0 +1,60 @@
import { notification, Spin } from "antd";
import axios from "axios";
import Dinero from "dinero.js";
import React, { useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(JobTotalsCashDiscount);
export function JobTotalsCashDiscount({ bodyshop, amountDinero }) {
const [loading, setLoading] = useState(true);
const [fee, setFee] = useState(0);
const fetchData = useCallback(async () => {
if (amountDinero && bodyshop) {
setLoading(true);
let response;
try {
response = await axios.post("/intellipay/checkfee", {
bodyshop: { id: bodyshop.id, imexshopid: bodyshop.imexshopid, state: bodyshop.state },
amount: Dinero(amountDinero).toFormat("0.00")
});
if (response?.data?.error) {
notification.open({
type: "error",
message:
response.data?.error ||
"Error encountered when contacting IntelliPay service to determine cash discounted price."
});
} else {
setFee(response.data?.fee || 0);
}
} catch (error) {
notification.open({
type: "error",
message:
error.response?.data?.error ||
"Error encountered when contacting IntelliPay service to determine cash discounted price."
});
} finally {
setLoading(false);
}
}
}, [amountDinero, bodyshop]);
useEffect(() => {
fetchData();
}, [fetchData, bodyshop, amountDinero]);
if (loading) return <Spin size="small" />;
return Dinero(amountDinero)
.add(Dinero({ amount: Math.round(fee * 100) }))
.toFormat();
}

View File

@@ -5,6 +5,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
@@ -12,7 +13,6 @@ import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -185,6 +185,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.towin")} name="towin" valuePropName="checked">
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tlos_ind")} name="tlos_ind" valuePropName="checked">
<Switch disabled={jobRO} />
</Form.Item>
</FormRow>
</Col>
<Col {...lossColDamage}>

View File

@@ -1,6 +1,5 @@
import { Button, Col, Form, Input, Row, Select, Space, Switch, Typography } from "antd";
import axios from "axios";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -8,13 +7,14 @@ import { createStructuredSelector } from "reselect";
import { calculateScheduleLoad } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import EmailInput from "../form-items-formatted/email-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
import "./schedule-job-modal.scss";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -84,7 +84,7 @@ export function ScheduleJobModalComponent({
}
]}
>
<DateTimePicker onBlur={handleDateBlur} onlyFuture />
<DateTimePicker onBlur={handleDateBlur} onlyFuture isSeparatedTime />
</Form.Item>
<Form.Item
name="scheduled_completion"

View File

@@ -142,7 +142,7 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
rome: [
{
key: "intellipay",
label: t("bodyshop.labels.intellipay"),
label: InstanceRenderManager({ rome: t("bodyshop.labels.romepay"), imex: t("bodyshop.labels.imexpay") }),
children: <ShopInfoIntellipay form={form} />
}
],

View File

@@ -37,17 +37,6 @@ export function ShopInfoIntellipay({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
valuePropName="checked"
dependencies={[["intellipay_config", "enable_cash_discount"]]}
name={["intellipay_config", "cash_discount_percentage"]}
rules={[
({ getFieldsValue }) => ({ required: form.getFieldValue(["intellipay_config", "enable_cash_discount"]) })
]}
>
<InputNumber min={0} max={100} precision={1} suffix='%'/>
</Form.Item>
</LayoutFormRow>
</>
);

View File

@@ -692,6 +692,7 @@ export const GET_JOB_BY_PK = gql`
tax_str_rt
tax_sub_rt
tax_tow_rt
tlos_ind
towin
towing_payable
unit_number

View File

@@ -665,9 +665,9 @@
"employees": "Employees",
"estimators": "Estimators",
"filehandlers": "Adjusters",
"imexpay": "ImEX Pay",
"insurancecos": "Insurance Companies",
"intakechecklist": "Intake Checklist",
"intellipay": "IntelliPay",
"intellipay_cash_discount": "Please ensure that cash discounting has been enabled on your merchant account. Reach out to IntelliPay Support if you need assistance. ",
"jobstatuses": "Job Statuses",
"laborrates": "Labor Rates",
@@ -693,11 +693,14 @@
"profits": "Profit Centers",
"sales_tax_codes": "Sales Tax Codes",
"tax_accounts": "Tax Accounts",
"title": "Responsibility Centers"
"title": "Responsibility Centers",
"ttl_adjustment": "",
"ttl_tax_adjustment": ""
},
"roguard": {
"title": "RO Guard"
},
"romepay": "Rome Pay",
"scheduling": "SMART Scheduling",
"scoreboardsetup": "Scoreboard Setup",
"shopinfo": "Shop Information",
@@ -1254,6 +1257,7 @@
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
"time": "Select Time",
"total": "Total",
"totals": "Totals",
"tuesday": "Tuesday",
@@ -1873,6 +1877,7 @@
"tax_str_rt": "Storage Tax Rate",
"tax_sub_rt": "Sublet Tax Rate",
"tax_tow_rt": "Towing Tax Rate",
"tlos_ind": "Total Loss Indicator",
"towin": "Tow In",
"towing_payable": "Towing Payable",
"unitnumber": "Unit #",
@@ -2107,7 +2112,9 @@
"threshhold": "Max Threshold: ${{amount}}",
"total_cost": "Total Cost",
"total_cust_payable": "Total Customer Amount Payable",
"total_cust_payable_cash_discount": "$t(jobs.labels.total_cust_payable) (Cash Discounted)",
"total_repairs": "Total Repairs",
"total_repairs_cash_discount": "Total Repairs (Cash Discounted)",
"total_sales": "Total Sales",
"total_sales_tax": "Total Sales Tax",
"totals": "Totals",
@@ -2850,22 +2857,22 @@
"statistics": {
"jobs_in_production": "Jobs in Production",
"tasks_in_production": "Tasks in Production",
"tasks_on_board": "Tasks on Board",
"tasks_in_view": "Tasks in View",
"tasks_on_board": "Tasks on Board",
"total_amount_in_production": "Dollars in Production",
"total_amount_on_board": "Dollars on Board",
"total_amount_in_view": "Dollars in View",
"total_amount_on_board": "Dollars on Board",
"total_hours_in_production": "Hours in Production",
"total_hours_on_board": "Hours on Board",
"total_hours_in_view": "Hours in View",
"total_jobs_on_board": "Jobs on Board",
"total_hours_on_board": "Hours on Board",
"total_jobs_in_view": "Jobs in View",
"total_jobs_on_board": "Jobs on Board",
"total_lab_in_production": "Body Hours in Production",
"total_lab_on_board": "Body Hours on Board",
"total_lab_in_view": "Body Hours in View",
"total_lab_on_board": "Body Hours on Board",
"total_lar_in_production": "Refinish Hours in Production",
"total_lar_on_board": "Refinish Hours on Board",
"total_lar_in_view": "Refinish Hours in View"
"total_lar_in_view": "Refinish Hours in View",
"total_lar_on_board": "Refinish Hours on Board"
},
"statistics_title": "Statistics"
},
@@ -2876,22 +2883,22 @@
"jobs_in_production": "Jobs in Production",
"tasks": "Tasks",
"tasks_in_production": "Tasks in Production",
"tasks_on_board": "Tasks on Board",
"tasks_in_view": "Tasks in View",
"tasks_on_board": "Tasks on Board",
"total_amount_in_production": "Dollars in Production",
"total_amount_on_board": "Dollars on Board",
"total_amount_in_view": "Dollars in View",
"total_amount_on_board": "Dollars on Board",
"total_hours_in_production": "Hours in Production",
"total_hours_on_board": "Hours on Board",
"total_hours_in_view": "Hours in View",
"total_jobs_on_board": "Jobs on Board",
"total_hours_on_board": "Hours on Board",
"total_jobs_in_view": "Jobs in View",
"total_jobs_on_board": "Jobs on Board",
"total_lab_in_production": "Body Hours in Production",
"total_lab_on_board": "Body Hours on Board",
"total_lab_in_view": "Body Hours in View",
"total_lab_on_board": "Body Hours on Board",
"total_lar_in_production": "Refinish Hours in Production",
"total_lar_on_board": "Refinish Hours on Board",
"total_lar_in_view": "Refinish Hours in View"
"total_lar_in_view": "Refinish Hours in View",
"total_lar_on_board": "Refinish Hours on Board"
},
"successes": {
"removed": "Job removed from production."

View File

@@ -665,9 +665,9 @@
"employees": "",
"estimators": "",
"filehandlers": "",
"imexpay": "",
"insurancecos": "",
"intakechecklist": "",
"intellipay": "",
"intellipay_cash_discount": "",
"jobstatuses": "",
"laborrates": "",
@@ -693,11 +693,14 @@
"profits": "",
"sales_tax_codes": "",
"tax_accounts": "",
"title": ""
"title": "",
"ttl_adjustment": "",
"ttl_tax_adjustment": ""
},
"roguard": {
"title": ""
},
"romepay": "",
"scheduling": "",
"scoreboardsetup": "",
"shopinfo": "",
@@ -1254,6 +1257,7 @@
"sunday": "",
"text": "",
"thursday": "",
"time": "",
"total": "",
"totals": "",
"tuesday": "",
@@ -1873,6 +1877,7 @@
"tax_str_rt": "",
"tax_sub_rt": "",
"tax_tow_rt": "",
"tlos_ind": "",
"towin": "",
"towing_payable": "Remolque a pagar",
"unitnumber": "Unidad #",
@@ -2107,7 +2112,9 @@
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_cust_payable_cash_discount": "",
"total_repairs": "",
"total_repairs_cash_discount": "",
"total_sales": "",
"total_sales_tax": "",
"totals": "",
@@ -2850,22 +2857,22 @@
"statistics": {
"jobs_in_production": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"tasks_on_board": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_amount_on_board": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_hours_on_board": "",
"total_jobs_in_view": "",
"total_jobs_on_board": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lab_on_board": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
"total_lar_in_view": "",
"total_lar_on_board": ""
},
"statistics_title": ""
},
@@ -2876,22 +2883,22 @@
"jobs_in_production": "",
"tasks": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"tasks_on_board": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_amount_on_board": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_hours_on_board": "",
"total_jobs_in_view": "",
"total_jobs_on_board": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lab_on_board": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
"total_lar_in_view": "",
"total_lar_on_board": ""
},
"successes": {
"removed": ""

View File

@@ -665,9 +665,9 @@
"employees": "",
"estimators": "",
"filehandlers": "",
"imexpay": "",
"insurancecos": "",
"intakechecklist": "",
"intellipay": "",
"intellipay_cash_discount": "",
"jobstatuses": "",
"laborrates": "",
@@ -693,11 +693,14 @@
"profits": "",
"sales_tax_codes": "",
"tax_accounts": "",
"title": ""
"title": "",
"ttl_adjustment": "",
"ttl_tax_adjustment": ""
},
"roguard": {
"title": ""
},
"romepay": "",
"scheduling": "",
"scoreboardsetup": "",
"shopinfo": "",
@@ -1254,6 +1257,7 @@
"sunday": "",
"text": "",
"thursday": "",
"time": "",
"total": "",
"totals": "",
"tuesday": "",
@@ -1873,6 +1877,7 @@
"tax_str_rt": "",
"tax_sub_rt": "",
"tax_tow_rt": "",
"tlos_ind": "",
"towin": "",
"towing_payable": "Remorquage à payer",
"unitnumber": "Unité #",
@@ -2107,7 +2112,9 @@
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_cust_payable_cash_discount": "",
"total_repairs": "",
"total_repairs_cash_discount": "",
"total_sales": "",
"total_sales_tax": "",
"totals": "",
@@ -2850,22 +2857,22 @@
"statistics": {
"jobs_in_production": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"tasks_on_board": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_amount_on_board": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_hours_on_board": "",
"total_jobs_in_view": "",
"total_jobs_on_board": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lab_on_board": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
"total_lar_in_view": "",
"total_lar_on_board": ""
},
"statistics_title": ""
},
@@ -2876,22 +2883,22 @@
"jobs_in_production": "",
"tasks": "",
"tasks_in_production": "",
"tasks_on_board": "",
"tasks_in_view": "",
"tasks_on_board": "",
"total_amount_in_production": "",
"total_amount_on_board": "",
"total_amount_in_view": "",
"total_amount_on_board": "",
"total_hours_in_production": "",
"total_hours_on_board": "",
"total_hours_in_view": "",
"total_jobs_on_board": "",
"total_hours_on_board": "",
"total_jobs_in_view": "",
"total_jobs_on_board": "",
"total_lab_in_production": "",
"total_lab_on_board": "",
"total_lab_in_view": "",
"total_lab_on_board": "",
"total_lar_in_production": "",
"total_lar_on_board": "",
"total_lar_in_view": ""
"total_lar_in_view": "",
"total_lar_on_board": ""
},
"successes": {
"removed": ""

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
-- CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);

View File

@@ -0,0 +1,8 @@
CREATE INDEX jobs_search_gin_ro_number ON jobs USING GIN ((ro_number) gin_trgm_ops);
CREATE INDEX jobs_search_gin_ownrfn ON jobs USING GIN ((ownr_fn) gin_trgm_ops);
CREATE INDEX jobs_search_gin_clm_no ON jobs USING GIN ((clm_no) gin_trgm_ops);
CREATE INDEX jobs_search_gin_plate_no ON jobs USING GIN ((plate_no) gin_trgm_ops);
CREATE INDEX jobs_search_gin_v_make_desc ON jobs USING GIN (( v_make_desc) gin_trgm_ops);
CREATE INDEX jobs_search_gin_v_model_desc ON jobs USING GIN (( v_model_desc) gin_trgm_ops);
CREATE INDEX jobs_search_gin_ownr_ln ON jobs USING GIN (( ownr_ln) gin_trgm_ops);
CREATE INDEX jobs_search_gin_ownr_co_nm ON jobs USING GIN (( ownr_co_nm) gin_trgm_ops);

View File

@@ -219,6 +219,7 @@ async function InsertPayment(oauthClient, qbo_realmId, req, payment, parentRef,
PaymentMethodRef: {
value: paymentMethods[payment.type]
},
PrivateNote: payment.memo.length > 4000 ? payment.memo.substring(0, 4000).trim() : payment.memo.trim(),
PaymentRefNum: payment.transactionid,
...(invoices && invoices.length === 1 && invoices[0]
? {

View File

@@ -47,7 +47,11 @@ exports.default = async (req, res) => {
}
// Send immediate response and continue processing.
res.status(200).send();
res.status(202).json({
success: true,
message: "Processing request ...",
timestamp: new Date().toISOString()
});
try {
logger.log("autohouse-start", "DEBUG", "api", null, null);
@@ -146,7 +150,7 @@ async function processBatch(batch, start, end) {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
autuhouseid: bodyshop.autuhouseid,
autohouseid: bodyshop.autohouseid,
fatal: true,
errors: [error.toString()]
});
@@ -154,7 +158,7 @@ async function processBatch(batch, start, end) {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
autuhouseid: bodyshop.autuhouseid,
autohouseid: bodyshop.autohouseid,
errors: erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allxmlsToUpload) {
for (const xmlObj of allxmlsToUpload) {
try {
logger.log("autohouse-sftp-upload", "DEBUG", "api", null, { filename: xmlObj.filename });
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
logger.log("autohouse-sftp-upload-result", "DEBUG", "api", null, {
logger.log("autohouse-sftp-upload", "DEBUG", "api", null, {
filename: xmlObj.filename,
result: xmlObj.result
});
@@ -609,10 +612,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, {
error
});
logger.log("autohouse-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};

View File

@@ -39,7 +39,11 @@ exports.default = async (req, res) => {
}
// Send immediate response and continue processing.
res.status(200).send();
res.status(202).json({
success: true,
message: "Processing request ...",
timestamp: new Date().toISOString()
});
try {
logger.log("chatter-start", "DEBUG", "api", null, null);
@@ -176,9 +180,8 @@ async function uploadViaSFTP(allcsvsToUpload) {
for (const csvObj of allcsvsToUpload) {
try {
logger.log("chatter-sftp-upload", "DEBUG", "api", null, { filename: csvObj.filename });
csvObj.result = await sftp.put(Buffer.from(csvObj.csv), `${csvObj.filename}`);
logger.log("chatter-sftp-upload-result", "DEBUG", "api", null, {
logger.log("chatter-sftp-upload", "DEBUG", "api", null, {
filename: csvObj.filename,
result: csvObj.result
});

View File

@@ -26,174 +26,184 @@ const ftpSetup = {
password: process.env.CLAIMSCORP_PASSWORD,
debug: (message, ...data) => logger.log(message, "DEBUG", "api", null, data),
algorithms: {
serverHostKey: ["ssh-rsa", "ssh-dss"]
serverHostKey: ["ssh-rsa", "ssh-dss", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384"]
}
};
const allxmlsToUpload = [];
const allErrors = [];
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);
const specificShopIds = req.body.bodyshopIds; // ['uuid]
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
// Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
const allxmlsToUpload = [];
const allErrors = [];
// Send immediate response and continue processing.
res.status(202).json({
success: true,
message: "Processing request ...",
timestamp: new Date().toISOString()
});
try {
for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
logger.log("claimscorp-start", "DEBUG", "api", null, null);
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS); //Query for the List of Bodyshop Clients.
const specificShopIds = req.body.bodyshopIds; // ['uuid];
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
const batchSize = 10;
const shopsToProcess =
specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
logger.log("claimscorp-shopsToProcess-generated", "DEBUG", "api", null, null);
if (shopsToProcess.length === 0) {
logger.log("claimscorp-shopsToProcess-empty", "DEBUG", "api", null, null);
return;
}
const batchPromises = [];
for (let i = 0; i < shopsToProcess.length; i += batchSize) {
const batch = shopsToProcess.slice(i, i + batchSize);
const batchPromise = (async () => {
await processBatch(batch, start, end);
if (skipUpload) {
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
} else {
await uploadViaSFTP(allxmlsToUpload);
}
})();
batchPromises.push(batchPromise);
}
await Promise.all(batchPromises);
await sendServerEmail({
subject: `ClaimsCorp Report ${moment().format("MM-DD-YY")}`,
text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
null,
2
)}`
});
logger.log("claimscorp-end", "DEBUG", "api", null, null);
} catch (error) {
logger.log("claimscorp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
}
};
async function processBatch(batch, start, end) {
for (const bodyshop of batch) {
const erroredJobs = [];
try {
logger.log("claimscorp-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
const erroredJobs = [];
try {
const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
bodyshopid: bodyshop.id,
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).endOf("day") })
});
const claimsCorpObject = {
DataFeed: {
ShopInfo: {
ShopID: bodyshops_by_pk.claimscorpid,
ShopName: bodyshops_by_pk.shopname,
RO: jobs.map((j) =>
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
})
)
}
}
};
if (erroredJobs.length > 0) {
logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
var ret = builder
.create(
{
// version: "1.0",
// encoding: "UTF-8",
//keepNullNodes: true,
},
claimsCorpObject
)
.end({ allowEmptyTags: true });
allxmlsToUpload.push({
count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
xml: ret,
filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
});
logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
} catch (error) {
//Error at the shop level.
logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, {
...error
});
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
claimscorpid: bodyshop.claimscorpid,
fatal: true,
errors: [error.toString()]
});
} finally {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
claimscorpid: bodyshop.claimscorpid,
errors: erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
error: ej.error
}))
});
}
}
if (skipUpload) {
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
res.json(allxmlsToUpload);
sendServerEmail({
subject: `ClaimsCorp 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
)}
`
const { jobs, bodyshops_by_pk } = await client.request(queries.CLAIMSCORP_QUERY, {
bodyshopid: bodyshop.id,
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).endOf("day") })
});
return;
}
let sftp = new Client();
sftp.on("error", (errors) =>
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
...errors
})
);
try {
//Connect to the FTP and upload all.
const claimsCorpObject = {
DataFeed: {
ShopInfo: {
ShopID: bodyshops_by_pk.claimscorpid,
ShopName: bodyshops_by_pk.shopname,
RO: jobs.map((j) =>
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
})
)
}
}
};
await sftp.connect(ftpSetup);
for (const xmlObj of allxmlsToUpload) {
logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
filename: xmlObj.filename
});
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
logger.log("claimscorp-sftp-upload-result", "DEBUG", "api", null, {
uploadResult
if (erroredJobs.length > 0) {
logger.log("claimscorp-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
const ret = builder.create({}, claimsCorpObject).end({ allowEmptyTags: true });
allxmlsToUpload.push({
count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
xml: ret,
filename: `${bodyshop.claimscorpid}-${moment().format("YYYYMMDDTHHMMss")}.xml`
});
logger.log("claimscorp-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
} catch (error) {
logger.log("claimscorp-sftp-error", "ERROR", "api", null, {
...error
//Error at the shop level.
logger.log("claimscorp-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
claimscorpid: bodyshop.claimscorpid,
fatal: true,
errors: [error.toString()]
});
} finally {
sftp.end();
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
claimscorpid: bodyshop.claimscorpid,
errors: erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
error: ej.error
}))
});
}
sendServerEmail({
subject: `ClaimsCorp 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);
}
};
}
async function uploadViaSFTP(allxmlsToUpload) {
const sftp = new Client();
sftp.on("error", (errors) =>
logger.log("claimscorp-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
);
try {
//Connect to the FTP and upload all.
await sftp.connect(ftpSetup);
for (const xmlObj of allxmlsToUpload) {
try {
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
logger.log("claimscorp-sftp-upload", "DEBUG", "api", null, {
filename: xmlObj.filename,
result: xmlObj.result
});
} catch (error) {
logger.log("claimscorp-sftp-upload-error", "ERROR", "api", null, {
filename: xmlObj.filename,
error: error.message,
stack: error.stack
});
throw error;
}
}
} catch (error) {
logger.log("claimscorp-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
throw error;
} finally {
sftp.end();
}
}
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
@@ -445,10 +455,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, {
error
});
logger.log("claimscorp-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};

View File

@@ -16,8 +16,7 @@ const { sendServerEmail } = require("../email/sendemail");
const DineroFormat = "0,0.00";
const DateFormat = "MM/DD/YYYY";
const repairOpCodes = ["OP4", "OP9", "OP10"];
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
const ftpSetup = {
host: process.env.KAIZEN_HOST,
@@ -30,173 +29,179 @@ const ftpSetup = {
}
};
const allxmlsToUpload = [];
const allErrors = [];
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"];
const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, {
imexshopid: kaizenShopsIDs
});
const specificShopIds = req.body.bodyshopIds; // ['uuid]
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
// Only process if the appropriate token is provided.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
return;
}
const allxmlsToUpload = [];
const allErrors = [];
// Send immediate response and continue processing.
res.status(202).json({
success: true,
message: "Processing request ...",
timestamp: new Date().toISOString()
});
try {
for (const bodyshop of specificShopIds ? bodyshops.filter((b) => specificShopIds.includes(b.id)) : bodyshops) {
logger.log("kaizen-start", "DEBUG", "api", null, null);
const { bodyshops } = await client.request(queries.GET_KAIZEN_SHOPS, { imexshopid: kaizenShopsIDs }); //Query for the List of Bodyshop Clients.
const specificShopIds = req.body.bodyshopIds; // ['uuid];
const { start, end, skipUpload } = req.body; //YYYY-MM-DD
const batchSize = 10;
const shopsToProcess =
specificShopIds?.length > 0 ? bodyshops.filter((shop) => specificShopIds.includes(shop.id)) : bodyshops;
logger.log("kaizen-shopsToProcess-generated", "DEBUG", "api", null, null);
if (shopsToProcess.length === 0) {
logger.log("kaizen-shopsToProcess-empty", "DEBUG", "api", null, null);
return;
}
const batchPromises = [];
for (let i = 0; i < shopsToProcess.length; i += batchSize) {
const batch = shopsToProcess.slice(i, i + batchSize);
const batchPromise = (async () => {
await processBatch(batch, start, end);
if (skipUpload) {
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
} else {
await uploadViaSFTP(allxmlsToUpload);
}
})();
batchPromises.push(batchPromise);
}
await Promise.all(batchPromises);
await sendServerEmail({
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
text: `Errors:\n${JSON.stringify(allErrors, null, 2)}\n\nUploaded:\n${JSON.stringify(
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count, result: x.result })),
null,
2
)}`
});
logger.log("kaizen-end", "DEBUG", "api", null, null);
} catch (error) {
logger.log("kaizen-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
}
};
async function processBatch(batch, start, end) {
for (const bodyshop of batch) {
const erroredJobs = [];
try {
logger.log("kaizen-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
const erroredJobs = [];
try {
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
bodyshopid: bodyshop.id,
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).endOf("day") })
});
const kaizenObject = {
DataFeed: {
ShopInfo: {
ShopName: bodyshops_by_pk.shopname,
Jobs: jobs.map((j) =>
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
})
)
}
}
};
if (erroredJobs.length > 0) {
logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
var ret = builder
.create(
{
// version: "1.0",
// encoding: "UTF-8",
//keepNullNodes: true,
},
kaizenObject
)
.end({ allowEmptyTags: true });
allxmlsToUpload.push({
count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
xml: ret,
filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
});
logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
} catch (error) {
//Error at the shop level.
logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, {
...error
});
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
shopname: bodyshop.shopname,
fatal: true,
errors: [error.toString()]
});
} finally {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
shopname: bodyshop.shopname,
errors: erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
error: ej.error
}))
});
}
}
if (skipUpload) {
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
res.json(allxmlsToUpload);
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
)}
`
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
bodyshopid: bodyshop.id,
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).endOf("day") })
});
return;
}
let sftp = new Client();
sftp.on("error", (errors) =>
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
...errors
})
);
try {
//Connect to the FTP and upload all.
const kaizenObject = {
DataFeed: {
ShopInfo: {
ShopName: bodyshops_by_pk.shopname,
Jobs: jobs.map((j) =>
CreateRepairOrderTag({ ...j, bodyshop: bodyshops_by_pk }, function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
})
)
}
}
};
await sftp.connect(ftpSetup);
for (const xmlObj of allxmlsToUpload) {
logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
filename: xmlObj.filename
});
const uploadResult = await sftp.put(Buffer.from(xmlObj.xml), `/${xmlObj.filename}`);
logger.log("kaizen-sftp-upload-result", "DEBUG", "api", null, {
uploadResult
if (erroredJobs.length > 0) {
logger.log("kaizen-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
}
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
const ret = builder.create({}, kaizenObject).end({ allowEmptyTags: true });
allxmlsToUpload.push({
count: kaizenObject.DataFeed.ShopInfo.Jobs.length,
xml: ret,
filename: `${bodyshop.shopname}-${moment().format("YYYYMMDDTHHMMss")}.xml`
});
logger.log("kaizen-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
} catch (error) {
logger.log("kaizen-sftp-error", "ERROR", "api", null, {
...error
//Error at the shop level.
logger.log("kaizen-error-shop", "ERROR", "api", bodyshop.id, { error: error.message, stack: error.stack });
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
shopname: bodyshop.shopname,
fatal: true,
errors: [error.toString()]
});
} finally {
sftp.end();
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
shopname: bodyshop.shopname,
errors: erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
error: ej.error
}))
});
}
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);
}
};
}
async function uploadViaSFTP(allxmlsToUpload) {
const sftp = new Client();
sftp.on("error", (errors) =>
logger.log("kaizen-sftp-connection-error", "ERROR", "api", null, { error: errors.message, stack: errors.stack })
);
try {
//Connect to the FTP and upload all.
await sftp.connect(ftpSetup);
for (const xmlObj of allxmlsToUpload) {
try {
xmlObj.result = await sftp.put(Buffer.from(xmlObj.xml), `${xmlObj.filename}`);
logger.log("kaizen-sftp-upload", "DEBUG", "api", null, {
filename: xmlObj.filename,
result: xmlObj.result
});
} catch (error) {
logger.log("kaizen-sftp-upload-error", "ERROR", "api", null, {
filename: xmlObj.filename,
error: error.message,
stack: error.stack
});
throw error;
}
}
} catch (error) {
logger.log("kaizen-sftp-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
throw error;
} finally {
sftp.end();
}
}
const CreateRepairOrderTag = (job, errorCallback) => {
//Level 2
@@ -420,10 +425,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
};
return ret;
} catch (error) {
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, {
error
});
logger.log("kaizen-job-calculate-error", "ERROR", "api", null, { error: error.message, stack: error.stack });
errorCallback({ jobid: job.id, ro_number: job.ro_number, error });
}
};

View File

@@ -149,6 +149,58 @@ exports.generate_payment_url = async (req, res) => {
}
};
//Reference: https://intellipay.com/dist/webapi26.html#operation/fee
exports.checkfee = async (req, res) => {
// Requires amount, bodyshop.imexshopid, and state? to get data.
logger.log("intellipay-fee-check", "DEBUG", req.user?.email, null, null);
//If there's no amount, there can't be a fee. Skip the call.
if (!req.body.amount || req.body.amount <= 0) {
res.json({ fee: 0 });
return;
}
const shopCredentials = await getShopCredentials(req.body.bodyshop);
try {
const options = {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
//TODO: Move these to environment variables/database.
data: qs.stringify(
{
method: "fee",
...shopCredentials,
amount: req.body.amount,
paymenttype: `CC`,
cardnum: "4111111111111111", //Not needed per documentation, but incorrect values come back without it.
state:
req.body.bodyshop?.state && req.body.bodyshop.state?.length === 2
? req.body.bodyshop.state.toUpperCase()
: "ZZ" //Same as above
},
{ sort: false } //ColdFusion Query Strings depend on order. This preserves it.
),
url: `https://${domain}.cpteller.com/api/26/webapi.cfc`
};
const response = await axios(options);
if (response.data?.error) {
res.status(400).json({ error: response.data.error });
} else if (response.data < 0) {
res.json({ error: "Fee amount negative. Check API credentials & account configuration." });
} else {
res.json({ fee: response.data });
}
} catch (error) {
//console.log(error);
logger.log("intellipay-fee-check-error", "ERROR", req.user?.email, null, {
error: error.message
});
res.status(400).json({ error });
}
};
exports.postback = async (req, res) => {
try {
logger.log("intellipay-postback", "DEBUG", req.user?.email, null, req.body);

View File

@@ -1,11 +1,12 @@
const express = require("express");
const router = express.Router();
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const { lightbox_credentials, payment_refund, generate_payment_url, postback } = require("../intellipay/intellipay");
const { lightbox_credentials, payment_refund, generate_payment_url, postback, checkfee } = require("../intellipay/intellipay");
router.post("/lightbox_credentials", validateFirebaseIdTokenMiddleware, lightbox_credentials);
router.post("/payment_refund", validateFirebaseIdTokenMiddleware, payment_refund);
router.post("/generate_payment_url", validateFirebaseIdTokenMiddleware, generate_payment_url);
router.post("/checkfee", validateFirebaseIdTokenMiddleware, checkfee);
router.post("/postback", postback);
module.exports = router;

View File

@@ -95,14 +95,14 @@ const createS3Client = () => {
throw error;
}
};
return {
uploadFileToS3,
downloadFileFromS3,
listFilesInS3Bucket,
deleteFileFromS3,
copyFileInS3,
fileExistsInS3
fileExistsInS3,
...s3Client
};
};