diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 1f7b2a5c9..a6258dd82 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -3339,6 +3339,53 @@ + + jc_hourly_rates + + + mapa + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + mash + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + lastnumberworkingdays false diff --git a/client/src/components/shop-info/shop-info.general.component.jsx b/client/src/components/shop-info/shop-info.general.component.jsx index 00c6225a6..74170bf3c 100644 --- a/client/src/components/shop-info/shop-info.general.component.jsx +++ b/client/src/components/shop-info/shop-info.general.component.jsx @@ -16,7 +16,7 @@ import PhoneFormItem, { } from "../form-items-formatted/phone-form-item.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; - +import CurrencyInput from "../form-items-formatted/currency-form-item.component"; export default function ShopInfoGeneral({ form }) { const { t } = useTranslation(); return ( @@ -425,6 +425,18 @@ export default function ShopInfoGeneral({ form }) { > + + + + + + diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index 5f456e88a..5bbea1ef5 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -87,6 +87,7 @@ export const QUERY_BODYSHOP = gql` md_ccc_rates enforce_referral website + jc_hourly_rates employees { id active @@ -171,6 +172,7 @@ export const UPDATE_SHOP = gql` md_ccc_rates enforce_referral website + jc_hourly_rates employees { id first_name diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 9dfef4704..7c5106e75 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -218,6 +218,10 @@ "invoice_federal_tax_rate": "Invoices - Federal Tax Rate", "invoice_local_tax_rate": "Invoices - Local Tax Rate", "invoice_state_tax_rate": "Invoices - Provincial/State Tax Rate", + "jc_hourly_rates": { + "mapa": "Job Costing - Paint Materials Hourly Cost Rate", + "mash": "Job Costing - Shop Materials Hourly Cost Rate" + }, "lastnumberworkingdays": "Scoreboard - Last Number of Working Days", "logo_img_path": "Shop Logo", "logo_img_path_height": "Logo Image Height", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 5777c3745..d4b8d2c1e 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -218,6 +218,10 @@ "invoice_federal_tax_rate": "", "invoice_local_tax_rate": "", "invoice_state_tax_rate": "", + "jc_hourly_rates": { + "mapa": "", + "mash": "" + }, "lastnumberworkingdays": "", "logo_img_path": "", "logo_img_path_height": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 1bf35e380..6f7f00692 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -218,6 +218,10 @@ "invoice_federal_tax_rate": "", "invoice_local_tax_rate": "", "invoice_state_tax_rate": "", + "jc_hourly_rates": { + "mapa": "", + "mash": "" + }, "lastnumberworkingdays": "", "logo_img_path": "", "logo_img_path_height": "", diff --git a/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/down.yaml b/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/down.yaml new file mode 100644 index 000000000..6393d9f29 --- /dev/null +++ b/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/down.yaml @@ -0,0 +1,6 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" ADD CONSTRAINT "bodyshops_autohouseid_key" + UNIQUE ("autohouseid"); + type: run_sql diff --git a/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/up.yaml b/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/up.yaml new file mode 100644 index 000000000..13be53f1d --- /dev/null +++ b/hasura/migrations/1622147830428_alter_table_public_bodyshops_alter_column_autohouseid/up.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" DROP CONSTRAINT "bodyshops_autohouseid_key"; + type: run_sql diff --git a/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/down.yaml b/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/down.yaml new file mode 100644 index 000000000..40520bed4 --- /dev/null +++ b/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/down.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "jc_hourly_rates"; + type: run_sql diff --git a/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/up.yaml b/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/up.yaml new file mode 100644 index 000000000..b943423ad --- /dev/null +++ b/hasura/migrations/1622147892235_alter_table_public_bodyshops_add_column_jc_hourly_rates/up.yaml @@ -0,0 +1,6 @@ +- args: + cascade: false + read_only: false + sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "jc_hourly_rates" jsonb NULL + DEFAULT jsonb_build_object(); + type: run_sql diff --git a/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..f17e43eb2 --- /dev/null +++ b/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,83 @@ +- 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 + - enforce_referral + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jobsizelimit + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - 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 + - sub_status + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - website + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..a1885496e --- /dev/null +++ b/hasura/migrations/1622147902848_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,84 @@ +- 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 + - enforce_referral + - federal_tax_id + - id + - imexshopid + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - jobsizelimit + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - 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 + - sub_status + - target_touchtime + - template_header + - textid + - updated_at + - use_fippa + - website + - workingdays + - zip_post + computed_fields: [] + filter: + associations: + user: + authid: + _eq: X-Hasura-User-Id + role: user + table: + name: bodyshops + schema: public + type: create_select_permission diff --git a/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/down.yaml b/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/down.yaml new file mode 100644 index 000000000..cf88f05a1 --- /dev/null +++ b/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/down.yaml @@ -0,0 +1,77 @@ +- 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 + - enforce_referral + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - 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 + - website + - workingdays + - zip_post + filter: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + set: {} + role: user + table: + name: bodyshops + schema: public + type: create_update_permission diff --git a/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/up.yaml b/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/up.yaml new file mode 100644 index 000000000..e8583e4ca --- /dev/null +++ b/hasura/migrations/1622147914381_update_permission_user_public_table_bodyshops/up.yaml @@ -0,0 +1,78 @@ +- 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 + - enforce_referral + - federal_tax_id + - id + - inhousevendorid + - insurance_vendor_id + - intakechecklist + - jc_hourly_rates + - logo_img_path + - md_categories + - md_ccc_rates + - md_classes + - md_hour_split + - 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 + - website + - workingdays + - zip_post + filter: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + 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 3aef96e54..02291ea5a 100644 --- a/hasura/migrations/metadata.yaml +++ b/hasura/migrations/metadata.yaml @@ -771,6 +771,7 @@ tables: - inhousevendorid - insurance_vendor_id - intakechecklist + - jc_hourly_rates - jobsizelimit - logo_img_path - md_categories @@ -841,6 +842,7 @@ tables: - inhousevendorid - insurance_vendor_id - intakechecklist + - jc_hourly_rates - logo_img_path - md_categories - md_ccc_rates diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index fd3a07228..9b7b3677c 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -680,6 +680,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid! bodyshop{ id md_responsibility_centers + jc_hourly_rates } } }`; @@ -780,6 +781,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT bodyshop { id md_responsibility_centers + jc_hourly_rates } } } diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 7b98acf1e..24d1d6382 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -10,8 +10,7 @@ Dinero.globalRoundingMode = "HALF_EVEN"; async function JobCosting(req, res) { const { jobid } = req.body; - console.log("🚀 ~ file: job-costing.js ~ line 13 ~ jobid", jobid); - console.time("querydata"); + console.time("Query for Data"); const BearerToken = req.headers.authorization; const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { @@ -209,6 +208,8 @@ function GenerateCostingData(job) { job.bodyshop.md_responsibility_centers.costs.map((p) => p.name) ); + const materialsHours = { mapaHrs: 0, mashHrs: 0 }; + //Massage the data. const jobLineTotalsByProfitCenter = job && @@ -234,6 +235,7 @@ function GenerateCostingData(job) { if (!acc.labor[defaultProfits["MAPA"]]) acc.labor[defaultProfits["MAPA"]] = Dinero(); + materialsHours.mapaHrs += val.mod_lb_hrs || 0; acc.labor[defaultProfits["MAPA"]] = acc.labor[ defaultProfits["MAPA"] ].add( @@ -253,6 +255,7 @@ function GenerateCostingData(job) { amount: Math.round((job.rate_mash || 0) * 100), }).multiply(val.mod_lb_hrs || 0) ); + materialsHours.mashHrs += val.mod_lb_hrs || 0; } //If labor line, add to paint and shop materials. } @@ -309,12 +312,7 @@ function GenerateCostingData(job) { .multiply(val.part_qty || 0) .percentage(val.prt_dsmk_p) ); - console.log( - `*** partsAmount`, - val.line_desc, - partsProfitCenter, - partsAmount.toJSON() - ); + if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero(); acc.parts[partsProfitCenter] = @@ -348,6 +346,52 @@ function GenerateCostingData(job) { return bill_acc; }, {}); + //If the hourly rates for job costing are set, add them in. + if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) { + if ( + !billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MAPA + ] + ) + billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MAPA + ] = Dinero(); + billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MAPA + ] = billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MAPA + ].add( + Dinero({ + amount: + (job.bodyshop.jc_hourly_rates && + job.bodyshop.jc_hourly_rates.mapa * 100) || + 0, + }).multiply(materialsHours.mapaHrs) + ); + } + if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) { + if ( + !billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MASH + ] + ) + billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MASH + ] = Dinero(); + billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MASH + ] = billTotalsByCostCenters[ + job.bodyshop.md_responsibility_centers.defaults.costs.MASH + ].add( + Dinero({ + amount: + (job.bodyshop.jc_hourly_rates && + job.bodyshop.jc_hourly_rates.mash * 100) || + 0, + }).multiply(materialsHours.mashHrs) + ); + } + const ticketTotalsByCostCenter = job.timetickets.reduce( (ticket_acc, ticket_val) => { //At the invoice level.