diff --git a/client/src/components/job-totals-table/job-totals-table.component.jsx b/client/src/components/job-totals-table/job-totals-table.component.jsx new file mode 100644 index 000000000..71906093e --- /dev/null +++ b/client/src/components/job-totals-table/job-totals-table.component.jsx @@ -0,0 +1,9 @@ +import React from "react"; + +export default function JobsTotalsTableComponent({ totals }) { + return ( +
+
{JSON.stringify(totals, null, 2)}
+
+ ); +} diff --git a/client/src/components/job-totals-table/job-totals-table.container.jsx b/client/src/components/job-totals-table/job-totals-table.container.jsx new file mode 100644 index 000000000..1b52f624c --- /dev/null +++ b/client/src/components/job-totals-table/job-totals-table.container.jsx @@ -0,0 +1,56 @@ +import { useQuery } from "@apollo/react-hooks"; +import React, { useEffect, useState } from "react"; +import { QUERY_JOB_FINANCIALS } from "../../graphql/jobs.queries"; +import AlertComponent from "../alert/alert.component"; +import LoadingSpinner from "../loading-spinner/loading-spinner.component"; +import JobTotalsTableComponent from "./job-totals-table.component"; +import { + CalculateRatesTotals, + CalculatePartsTotals, + CalculateCustPayable +} from "./job-totals.utility"; + +export default function JobTotalsTableContainer({ jobId }) { + const { loading, error, data } = useQuery(QUERY_JOB_FINANCIALS, { + variables: { jobId: jobId } + }); + + const [totals, setTotals] = useState({}); + + useEffect(() => { + if (!!data) { + setTotals({ + parts: CalculatePartsTotals(data.jobs_by_pk.joblines), + rates: CalculateRatesTotals(data.jobs_by_pk), + custPayable: CalculateCustPayable(data.jobs_by_pk) + }); + } + }, [data]); + + console.log("totals", totals); + + if (loading) return ; + if (error) return ; + + return ( +
+ +
+
STL Data
+
+ {data + ? JSON.stringify(data.jobs_by_pk.cieca_stl.data, null, 2) + : null} +
+
+
+
ttl Data
+
+ {data + ? JSON.stringify(data.jobs_by_pk.cieca_ttl.data, null, 2) + : null} +
+
+
+ ); +} diff --git a/client/src/components/job-totals-table/job-totals.utility.js b/client/src/components/job-totals-table/job-totals.utility.js new file mode 100644 index 000000000..adc692a5d --- /dev/null +++ b/client/src/components/job-totals-table/job-totals.utility.js @@ -0,0 +1,179 @@ +export function CalculateRatesTotals(ratesList) { + const jobLines = ratesList.joblines; + + let ret = { + rate_la1: { + hours: jobLines + .filter(item => item.mod_lbr_ty === "LA1") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2), + rate: ratesList.rate_la1 + }, + rate_la2: { + hours: jobLines + .filter(item => item.mod_lbr_ty === "LA2") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2), + rate: ratesList.rate_la2 + }, + rate_la3: { + rate: ratesList.rate_la3, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LA3") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_la4: { + rate: ratesList.rate_la4, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LA4") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_laa: { + rate: ratesList.rate_laa, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAA") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lab: { + rate: ratesList.rate_lab, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAB") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lad: { + rate: ratesList.rate_lad, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAD") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lae: { + rate: ratesList.rate_lae, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAE") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_laf: { + rate: ratesList.rate_laf, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAF") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lag: { + rate: ratesList.rate_lag, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAG") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lam: { + rate: ratesList.rate_lam, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAM") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lar: { + rate: ratesList.rate_lar, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAR") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_las: { + rate: ratesList.rate_las, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAS") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + }, + rate_lau: { + rate: ratesList.rate_lau, + hours: jobLines + .filter(item => item.mod_lbr_ty === "LAU") + .reduce((acc, value) => acc + value.mod_lb_hrs, 0) + .toFixed(2) + } + }; + + let subtotal = 0; + for (const property in ret) { + ret[property].total = (ret[property].hours * ret[property].rate).toFixed(2); + subtotal = subtotal + ret[property].hours * ret[property].rate; + } + ret.subtotal = subtotal.toFixed(2); + return ret; +} + +export function CalculatePartsTotals(jobLines) { + const ret = jobLines.reduce( + (acc, value) => { + switch (value.part_type) { + case "PAA": + case "PAC": + case "PAG": + case "PAL": + case "PAM": + case "PAN": + case "PAND": + case "PANF": + case "PAO": + case "PAP": + case "PAR": + return { + ...acc, + parts: { + ...acc.parts, + subtotal: acc.parts.subtotal + value.act_price + //TODO Add Adjustments in + } + }; + case "PAS": + case "PASL": + return { + ...acc, + sublets: { + ...acc.sublets, + subtotal: acc.sublets.subtotal + value.act_price + //TODO Add Adjustments in + } + }; + default: + return acc; + } + }, + { + parts: { subtotal: 0, adjustments: 0, total: 0 }, + sublets: { subtotal: 0, adjustments: 0, total: 0 } + } + ); + + return { + parts: { ...ret.parts, total: ret.parts.subtotal + ret.parts.adjustments }, + sublets: { + ...ret.sublets, + total: ret.sublets.subtotal + ret.sublets.adjustments + } + }; +} + +export function CalculateCustPayable(job) { + return { + deductible: job.ded_amt || 0, + federal_tax: job.federal_tax_payable || 0, + other_customer_amount: job.other_amount_payable || 0, + dep_taxes: job.depreciation_taxes || 0, + total: + job.ded_amt || + 0 + job.federal_tax_payable || + 0 + job.other_amount_payable || + 0 + job.depreciation_taxes || + 0 + }; +} diff --git a/client/src/components/jobs-detail-financial/jobs-detail-financial.component.jsx b/client/src/components/jobs-detail-financial/jobs-detail-financial.component.jsx index bbba78b5e..b35d1b3f1 100644 --- a/client/src/components/jobs-detail-financial/jobs-detail-financial.component.jsx +++ b/client/src/components/jobs-detail-financial/jobs-detail-financial.component.jsx @@ -1,131 +1,133 @@ -import { Divider, Form, Input, InputNumber } from "antd"; +import { Divider, Form, Input, InputNumber, Row, Col } from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; +import JobTotalsTableContainer from "../job-totals-table/job-totals-table.container"; export default function JobsDetailFinancials({ job }) { const { t } = useTranslation(); return ( -
- - - - - - - - - - TODO This is equivalent of GST payable. - - - - TODO equivalent of other customer amount - - - - - - - - - - - - - - Totals Table - - - - - - - - - - - - - - - - - - - - - - - - - - - - Note //TODO Remove ATP rate? - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ + + + + + + + + + + + TODO This is equivalent of GST payable. + + + + TODO equivalent of other customer amount + + + + + + + + + + + + + + Totals Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + Note //TODO Remove ATP rate? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 3dde172f1..45f315b3b 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -385,3 +385,47 @@ export const ACTIVE_JOBS_FOR_AUTOCOMPLETE = gql` } } `; + +export const QUERY_JOB_FINANCIALS = gql` + query QUERY_JOB_FINANCIALS($jobId: uuid!) { + jobs_by_pk(id: $jobId) { + id + cieca_ttl + cieca_stl + ded_amt + depreciation_taxes + federal_tax_payable + other_amount_payable + rate_atp + rate_la1 + rate_la2 + rate_la3 + rate_la4 + rate_laa + rate_lab + rate_lad + rate_lae + rate_laf + rate_lag + rate_lam + rate_lar + rate_las + rate_lau + rate_ma2s + rate_ma2t + rate_ma3s + rate_mabl + rate_macs + rate_mahw + rate_mapa + rate_mash + rate_matd + joblines { + mod_lbr_ty + act_price + mod_lb_hrs + part_type + } + } + } +`; diff --git a/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/down.yaml b/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/down.yaml new file mode 100644 index 000000000..c639e07f8 --- /dev/null +++ b/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/down.yaml @@ -0,0 +1,74 @@ +- args: + role: user + table: + name: joblines + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: false + columns: + - alt_overrd + - alt_part_i + - bett_tax + - cert_part + - glass_flag + - lbr_hrs_j + - lbr_inc + - lbr_op_j + - lbr_tax + - lbr_typ_j + - misc_sublt + - misc_tax + - price_inc + - price_j + - tax_part + - est_seq + - paint_stg + - paint_tone + - part_qty + - unq_seq + - act_price + - bett_amt + - bett_pctg + - db_hrs + - db_price + - lbr_amt + - line_ref + - misc_amt + - mod_lb_hrs + - prt_dsmk_m + - prt_dsmk_p + - alt_co_id + - alt_partm + - alt_partno + - bett_type + - db_ref + - lbr_op + - line_desc + - line_ind + - mod_lbr_ty + - oem_partno + - op_code_desc + - part_type + - status + - created_at + - updated_at + - id + - jobid + computed_fields: [] + filter: + job: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + role: user + table: + name: joblines + schema: public + type: create_select_permission diff --git a/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/up.yaml b/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/up.yaml new file mode 100644 index 000000000..317c3b0d8 --- /dev/null +++ b/hasura/migrations/1586216036874_update_permission_user_public_table_joblines/up.yaml @@ -0,0 +1,74 @@ +- args: + role: user + table: + name: joblines + schema: public + type: drop_select_permission +- args: + permission: + allow_aggregations: true + columns: + - alt_overrd + - alt_part_i + - bett_tax + - cert_part + - glass_flag + - lbr_hrs_j + - lbr_inc + - lbr_op_j + - lbr_tax + - lbr_typ_j + - misc_sublt + - misc_tax + - price_inc + - price_j + - tax_part + - est_seq + - paint_stg + - paint_tone + - part_qty + - unq_seq + - act_price + - bett_amt + - bett_pctg + - db_hrs + - db_price + - lbr_amt + - line_ref + - misc_amt + - mod_lb_hrs + - prt_dsmk_m + - prt_dsmk_p + - alt_co_id + - alt_partm + - alt_partno + - bett_type + - db_ref + - lbr_op + - line_desc + - line_ind + - mod_lbr_ty + - oem_partno + - op_code_desc + - part_type + - status + - created_at + - updated_at + - id + - jobid + computed_fields: [] + filter: + job: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + role: user + table: + name: joblines + schema: public + type: create_select_permission