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