diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 58d320119..fbc0f76cb 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3255,6 +3255,27 @@ + + md_payment_types + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + md_referral_sources false diff --git a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx index 635695550..ad697fe9e 100644 --- a/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx +++ b/client/src/components/payable-export-all-button/payable-export-all-button.component.jsx @@ -9,6 +9,7 @@ import { auth } from "../../firebase/firebase.utils"; import { UPDATE_BILLS } from "../../graphql/bills.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { logImEXEvent } from "../../firebase/firebase.utils"; +import _ from "lodash"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -72,43 +73,49 @@ export function PayableExportAll({ } console.log("handleQbxml -> PartnerResponse", PartnerResponse); - const failedTransactions = PartnerResponse.data.filter((r) => !r.success); - const successfulTransactions = PartnerResponse.data.filter( - (r) => r.success - ); - if (failedTransactions.length > 0) { - //Uh oh. At least one was no good. - failedTransactions.map((ft) => - notification["error"]({ - message: t("bills.errors.exporting", { - error: ft.errorMessage || "", - }), - }) - ); - } - if (successfulTransactions.length > 0) { - const billUpdateResponse = await updateBill({ - variables: { - billIdList: successfulTransactions.map((st) => st.id), - bill: { - exported: true, - exported_at: new Date(), - }, - }, - }); - if (!!!billUpdateResponse.errors) { - notification["success"]({ - message: t("bills.successes.exported"), - }); - } else { - notification["error"]({ - message: t("bills.errors.exporting", { - error: JSON.stringify(billUpdateResponse.error), - }), - }); - } - } + const groupedData = _.groupBy(PartnerResponse.data, "id"); + const proms = []; + Object.keys(groupedData).forEach((key) => { + proms.push( + (async () => { + const failedTransactions = groupedData[key].filter((r) => !r.success); + if (failedTransactions.length > 0) { + //Uh oh. At least one was no good. + failedTransactions.map((ft) => + notification["error"]({ + message: t("bills.errors.exporting", { + error: ft.errorMessage || "", + }), + }) + ); + } else { + const billUpdateResponse = await updateBill({ + variables: { + billIdList: [key], + bill: { + exported: true, + exported_at: new Date(), + }, + }, + }); + if (!!!billUpdateResponse.errors) { + notification["success"]({ + message: t("bills.successes.exported"), + }); + } else { + notification["error"]({ + message: t("bills.errors.exporting", { + error: JSON.stringify(billUpdateResponse.error), + }), + }); + } + } + })() + ); + }); + + await Promise.all(proms); if (!!completedCallback) completedCallback([]); if (!!loadingCallback) loadingCallback(false); setLoading(false); diff --git a/client/src/components/payment-form/payment-form.component.jsx b/client/src/components/payment-form/payment-form.component.jsx index 239a5601c..32d5fbf67 100644 --- a/client/src/components/payment-form/payment-form.component.jsx +++ b/client/src/components/payment-form/payment-form.component.jsx @@ -107,16 +107,11 @@ export function PaymentFormComponent({ ]} > diff --git a/client/src/components/shop-info/shop-info.component.jsx b/client/src/components/shop-info/shop-info.component.jsx index b265a42e7..894727a92 100644 --- a/client/src/components/shop-info/shop-info.component.jsx +++ b/client/src/components/shop-info/shop-info.component.jsx @@ -570,6 +570,19 @@ export default function ShopInfoComponent({ form, saveLoading }) { }} + + + + + + + + + + + + + + + + diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index 6b6c3ec12..9379f40af 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -78,6 +78,7 @@ export const QUERY_BODYSHOP = gql` default_adjustment_rate workingdays use_fippa + md_payment_types employees { id first_name @@ -154,6 +155,7 @@ export const UPDATE_SHOP = gql` default_adjustment_rate workingdays use_fippa + md_payment_types employees { id first_name diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index fec907fab..00f8f8708 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -214,6 +214,7 @@ "md_categories": "Categories", "md_classes": "Classes", "md_ins_cos": "Insurance Companies", + "md_payment_types": "Payment Types", "md_referral_sources": "Referral Sources", "messaginglabel": "Messaging Preset Label", "messagingtext": "Messaging Preset Text", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 92859301a..eb4a2650e 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -214,6 +214,7 @@ "md_categories": "", "md_classes": "", "md_ins_cos": "", + "md_payment_types": "", "md_referral_sources": "", "messaginglabel": "", "messagingtext": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 445626743..f0ba5a32e 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -214,6 +214,7 @@ "md_categories": "", "md_classes": "", "md_ins_cos": "", + "md_payment_types": "", "md_referral_sources": "", "messaginglabel": "", "messagingtext": "", diff --git a/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/down.yaml b/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/down.yaml new file mode 100644 index 000000000..1c80eb003 --- /dev/null +++ b/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/down.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "md_payment_types"; + type: run_sql diff --git a/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/up.yaml b/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/up.yaml new file mode 100644 index 000000000..08be5ef20 --- /dev/null +++ b/hasura/migrations/1613528228078_alter_table_public_bodyshops_add_column_md_payment_types/up.yaml @@ -0,0 +1,6 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "md_payment_types" jsonb NOT + NULL DEFAULT jsonb_build_array(); + type: run_sql diff --git a/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..8ec0d692f --- /dev/null +++ b/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,78 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: false + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - logo_img_path + - md_categories + - md_classes + - md_ins_cos + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - messagingservicesid + - phone + - prodtargethrs + - production_config + - region_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - stripe_acct_id + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + bodyshop: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..59496b04f --- /dev/null +++ b/hasura/migrations/1613528238258_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,79 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: false + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - logo_img_path + - md_categories + - md_classes + - md_ins_cos + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - messagingservicesid + - phone + - prodtargethrs + - production_config + - region_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - stripe_acct_id + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + bodyshop: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..69cb0c83d --- /dev/null +++ b/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,71 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_update_permission +- args: + permission: + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - logo_img_path + - md_categories + - md_classes + - md_ins_cos + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - phone + - prodtargethrs + - production_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - target_touchtime + - updated_at + - use_fippa + - workingdays + - zip_post + filter: + associations: + bodyshop: + associations: + user: + authid: + _eq: X-Hasura-User-Id + set: {} + role: user + table: + name: bodyshops + schema: public + type: create_update_permission diff --git a/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..dfd06b55c --- /dev/null +++ b/hasura/migrations/1613528247057_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,72 @@ +- args: + role: user + table: + name: bodyshops + schema: public + type: drop_update_permission +- args: + permission: + columns: + - accountingconfig + - address1 + - address2 + - appt_alt_transport + - appt_colors + - appt_length + - bill_tax_rates + - city + - country + - created_at + - default_adjustment_rate + - deliverchecklist + - email + - enforce_class + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - logo_img_path + - md_categories + - md_classes + - md_ins_cos + - md_labor_rates + - md_messaging_presets + - md_notes_presets + - md_order_statuses + - md_parts_locations + - md_payment_types + - md_rbac + - md_referral_sources + - md_responsibility_centers + - md_ro_statuses + - phone + - prodtargethrs + - production_config + - schedule_end_time + - schedule_start_time + - scoreboard_target + - shopname + - shoprates + - speedprint + - ssbuckets + - state + - state_tax_id + - target_touchtime + - updated_at + - use_fippa + - workingdays + - zip_post + filter: + associations: + bodyshop: + associations: + user: + authid: + _eq: X-Hasura-User-Id + set: {} + role: user + table: + name: bodyshops + schema: public + type: create_update_permission diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml index 9868bb661..83dd2a869 100644 --- a/hasura/migrations/metadata.yaml +++ b/hasura/migrations/metadata.yaml @@ -755,6 +755,7 @@ tables: - md_notes_presets - md_order_statuses - md_parts_locations + - md_payment_types - md_rbac - md_referral_sources - md_responsibility_centers @@ -820,6 +821,7 @@ tables: - md_notes_presets - md_order_statuses - md_parts_locations + - md_payment_types - md_rbac - md_referral_sources - md_responsibility_centers diff --git a/server/accounting/qbxml/qbxml-payments.js b/server/accounting/qbxml/qbxml-payments.js index e6a4453ef..2eb4086b1 100644 --- a/server/accounting/qbxml/qbxml-payments.js +++ b/server/accounting/qbxml/qbxml-payments.js @@ -37,10 +37,8 @@ exports.default = async (req, res) => { const isThreeTier = bodyshop.accountingconfig.tiers === 3; const twoTierPref = bodyshop.accountingconfig.twotierpref; - const QbXmlToExecute = []; payments.map((i) => { - if (isThreeTier) { QbXmlToExecute.push({ id: i.id, @@ -48,7 +46,7 @@ exports.default = async (req, res) => { qbxml: QbxmlReceivables.generateSourceCustomerQbxml(i.job, bodyshop), // Create the source customer. }); } - + QbXmlToExecute.push({ id: i.id, okStatusCodes: ["0", "3100"], @@ -60,7 +58,7 @@ exports.default = async (req, res) => { twoTierPref ), }); - + QbXmlToExecute.push({ id: i.id, okStatusCodes: ["0", "3100"], @@ -73,8 +71,6 @@ exports.default = async (req, res) => { ), }); - - QbXmlToExecute.push({ id: i.id, okStatusCodes: ["0"], @@ -91,49 +87,96 @@ exports.default = async (req, res) => { const generatePayment = (payment) => { console.log("generatePayment -> payment", payment); - const paymentQbxmlObj = { - QBXML: { - QBXMLMsgsRq: { - "@onError": "continueOnError", - ReceivePaymentAddRq: { - ReceivePaymentAdd: { - CustomerRef: { - FullName: - payment.job.bodyshop.accountingconfig.tiers === 3 - ? `${generateSourceTier(payment.job)}:${generateOwnerTier( - payment.job - )}:${generateJobTier(payment.job)}` - : `${generateOwnerTier(payment.job)}:${generateJobTier( - payment.job - )}`, + let paymentQbxmlObj; + if (payment.amount > 0) { + paymentQbxmlObj = { + QBXML: { + QBXMLMsgsRq: { + "@onError": "continueOnError", + ReceivePaymentAddRq: { + ReceivePaymentAdd: { + CustomerRef: { + FullName: + payment.job.bodyshop.accountingconfig.tiers === 3 + ? `${generateSourceTier(payment.job)}:${generateOwnerTier( + payment.job + )}:${generateJobTier(payment.job)}` + : `${generateOwnerTier(payment.job)}:${generateJobTier( + payment.job + )}`, + }, + ARAccountRef: { + FullName: + payment.job.bodyshop.md_responsibility_centers.ar.accountname, + }, + TxnDate: moment(payment.date).format("YYYY-MM-DD"), //Trim String + RefNumber: + payment.paymentnum || payment.stripeid || payment.transactionid, + TotalAmount: Dinero({ + amount: Math.round(payment.amount * 100), + }).toFormat(DineroQbFormat), + PaymentMethodRef: { + FullName: payment.type, + }, + Memo: `RO ${payment.job.ro_number || ""} OWNER ${ + payment.job.ownr_fn || "" + } ${payment.job.ownr_ln || ""} ${payment.job.ownr_co_nm || ""} ${ + payment.stripeid || "" + } ${payment.payer ? ` PAID BY ${payment.payer}` : ""}`, + IsAutoApply: true, + // AppliedToTxnAdd:{ + // T + // } }, - ARAccountRef: { - FullName: - payment.job.bodyshop.md_responsibility_centers.ar.accountname, - }, - TxnDate: moment(payment.date).format("YYYY-MM-DD"), //Trim String - RefNumber: - payment.paymentnum || payment.stripeid || payment.transactionid, - TotalAmount: Dinero({ - amount: Math.round(payment.amount * 100), - }).toFormat(DineroQbFormat), - PaymentMethodRef: { - FullName: payment.type, - }, - Memo: `RO ${payment.job.ro_number || ""} OWNER ${ - payment.job.ownr_fn || "" - } ${payment.job.ownr_ln || ""} ${payment.job.ownr_co_nm || ""} ${ - payment.stripeid || "" - } ${payment.payer ? ` PAID BY ${payment.payer}` : ""}`, - IsAutoApply: true, - // AppliedToTxnAdd:{ - // T - // } }, }, }, - }, - }; + }; + } else { + paymentQbxmlObj = { + QBXML: { + QBXMLMsgsRq: { + "@onError": "continueOnError", + CreditMemoAddRq: { + CreditMemoAdd: { + CustomerRef: { + FullName: + payment.job.bodyshop.accountingconfig.tiers === 3 + ? `${generateSourceTier(payment.job)}:${generateOwnerTier( + payment.job + )}:${generateJobTier(payment.job)}` + : `${generateOwnerTier(payment.job)}:${generateJobTier( + payment.job + )}`, + }, + ARAccountRef: { + FullName: + payment.job.bodyshop.md_responsibility_centers.ar.accountname, + }, + TxnDate: moment(payment.date).format("YYYY-MM-DD"), //Trim String + RefNumber: + payment.paymentnum || payment.stripeid || payment.transactionid, + + CreditMemoLineAdd: [ + { + ItemRef: { + FullName: + payment.job.bodyshop.md_responsibility_centers.refund + .accountitem, + }, + Desc: payment.memo, + Amount: Dinero({ + amount: Math.round(payment.amount * 100 * -1), + }).toFormat(DineroQbFormat), + SalesTaxCodeRef: { FullName: "E" }, + }, + ], + }, + }, + }, + }, + }; + } var paymentQbxmlPartial = builder .create(paymentQbxmlObj, {