WIP Bill Line deduction from Labor IO-571

This commit is contained in:
Patrick Fic
2021-01-08 17:24:40 -08:00
parent baef1eaaf9
commit 12f14e5425
23 changed files with 638 additions and 93 deletions

View File

@@ -1316,6 +1316,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>deductfromlabor</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>entered</name>
<definition_loaded>false</definition_loaded>
@@ -2699,6 +2720,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>default_adjustment_rate</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>
<folder_node>
<name>deliver</name>
<children>
@@ -16607,6 +16649,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>adjustmentrate</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>adjustments</name>
<definition_loaded>false</definition_loaded>

View File

@@ -5,15 +5,15 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import BillFormContainer from "../bill-form/bill-form.container";
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { handleUpload } from "../documents-upload/documents-upload.utility";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -37,81 +37,98 @@ function BillEnterModalContainer({
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
const [loading, setLoading] = useState(false);
const handleFinish = (values) => {
const handleFinish = async (values) => {
console.log(
"🚀 ~ file: bill-enter-modal.container.jsx ~ line 41 ~ handleFinish ~ values",
values
);
setLoading(true);
const { upload, location, ...remainingValues } = values;
insertBill({
const adjustmentsToInsert = [];
const r1 = await insertBill({
variables: {
bill: [
Object.assign({}, remainingValues, {
{
...remainingValues,
billlines: {
data:
remainingValues.billlines &&
remainingValues.billlines.map((i) => {
const { deductfromlabor, lbr_adjustment, ...restI } = i;
if (deductfromlabor) {
adjustmentsToInsert.push({
...lbr_adjustment,
act_price: i.act_price,
});
}
return {
...i,
...restI,
joblineid: i.joblineid === "noline" ? null : i.joblineid,
};
}),
},
}),
},
],
},
})
.then((r) => {
const billId = r.data.insert_bills.returning[0].id;
});
updateJobLines({
variables: {
ids: remainingValues.billlines
.filter((il) => il.joblineid !== "noline")
.map((li) => li.joblineid),
status: bodyshop.md_order_statuses.default_received || "Received*",
location: location,
},
}).then((joblineresult) => {
/////////////////////////
if (upload && upload.length > 0) {
//insert Each of the documents?
upload.forEach((u) => {
handleUpload(
{ file: u.originFileObj },
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: values.jobid,
billId: billId,
tagsArray: null,
callback: null,
}
);
});
}
///////////////////////////
setLoading(false);
notification["success"]({
message: t("bills.successes.created"),
});
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ billlines: [] });
} else {
toggleModalVisible();
}
setEnterAgain(false);
});
})
.catch((error) => {
setLoading(false);
setEnterAgain(false);
notification["error"]({
message: t("bills.errors.creating", {
message: JSON.stringify(error),
}),
});
if (!!r1.errors) {
setLoading(false);
setEnterAgain(false);
notification["error"]({
message: t("bills.errors.creating", {
message: JSON.stringify(r1.errors),
}),
});
}
const billId = r1.data.insert_bills.returning[0].id;
await updateJobLines({
variables: {
ids: remainingValues.billlines
.filter((il) => il.joblineid !== "noline")
.map((li) => li.joblineid),
status: bodyshop.md_order_statuses.default_received || "Received*",
location: location,
},
});
console.log("adjustmentsToInsert", adjustmentsToInsert);
/////////////////////////
if (upload && upload.length > 0) {
//insert Each of the documents?
upload.forEach((u) => {
handleUpload(
{ file: u.originFileObj },
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: values.jobid,
billId: billId,
tagsArray: null,
callback: null,
}
);
});
}
///////////////////////////
setLoading(false);
notification["success"]({
message: t("bills.successes.created"),
});
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ billlines: [] });
} else {
toggleModalVisible();
}
setEnterAgain(false);
};
const handleCancel = () => {

View File

@@ -66,6 +66,14 @@ export function BillFormComponent({
return (
<div>
<Form.Item
// label={t("bills.fields.isinhouse")}
style={{ display: "none" }}
name="isinhouse"
valuePropName="checked"
>
<Switch />
</Form.Item>
<LayoutFormRow grow>
<Form.Item
name="jobid"
@@ -106,7 +114,6 @@ export function BillFormComponent({
/>
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("bills.fields.invoice_number")}
@@ -145,14 +152,7 @@ export function BillFormComponent({
>
<Input disabled={disabled || disableInvNumber} />
</Form.Item>
<Form.Item
// label={t("bills.fields.isinhouse")}
style={{ display: "none" }}
name="isinhouse"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("bills.fields.date")}
name="date"
@@ -222,7 +222,6 @@ export function BillFormComponent({
responsibilityCenters={responsibilityCenters}
disabled={disabled}
/>
<Form.Item
name="upload"
label="Upload"
@@ -239,7 +238,6 @@ export function BillFormComponent({
<Button>Click to upload</Button>
</Upload>
</Form.Item>
<Form.Item shouldUpdate>
{() => {
const values = form.getFieldsValue([

View File

@@ -10,11 +10,24 @@ import {
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
import CurrencyInput from "../form-items-formatted/currency-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";
export default function BillEnterModalLinesComponent({
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function BillEnterModalLinesComponent({
bodyshop,
disabled,
lineData,
discount,
@@ -22,7 +35,7 @@ export default function BillEnterModalLinesComponent({
responsibilityCenters,
}) {
const { t } = useTranslation();
const { setFieldsValue, getFieldsValue } = form;
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
return (
<Form.List name="billlines">
@@ -212,6 +225,110 @@ export default function BillEnterModalLinesComponent({
>
<Switch disabled={disabled} />
</Form.Item>
<Form.Item
label={t("billlines.labels.deductfromlabor")}
key={`${index}deductfromlabor`}
valuePropName="checked"
name={[field.name, "deductfromlabor"]}
>
<Switch disabled={disabled} />
</Form.Item>
<Form.Item
shouldUpdate={(prev, cur) =>
prev.billlines[index].deductfromlabor !==
cur.billlines[index].deductfromlabor
}
>
{() => {
if (
getFieldValue([
"billlines",
field.name,
"deductfromlabor",
])
)
return (
<div>
<Form.Item
label={t("joblines.fields.mod_lbr_ty")}
key={`${index}modlbrty`}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
name={[
field.name,
"lbr_adjustment",
"mod_lbr_ty",
]}
>
<Select allowClear>
<Select.Option value="LAA">
{t("joblines.fields.lbr_types.LAA")}
</Select.Option>
<Select.Option value="LAB">
{t("joblines.fields.lbr_types.LAB")}
</Select.Option>
<Select.Option value="LAD">
{t("joblines.fields.lbr_types.LAD")}
</Select.Option>
<Select.Option value="LAE">
{t("joblines.fields.lbr_types.LAE")}
</Select.Option>
<Select.Option value="LAF">
{t("joblines.fields.lbr_types.LAF")}
</Select.Option>
<Select.Option value="LAG">
{t("joblines.fields.lbr_types.LAG")}
</Select.Option>
<Select.Option value="LAM">
{t("joblines.fields.lbr_types.LAM")}
</Select.Option>
<Select.Option value="LAR">
{t("joblines.fields.lbr_types.LAR")}
</Select.Option>
<Select.Option value="LAS">
{t("joblines.fields.lbr_types.LAS")}
</Select.Option>
<Select.Option value="LAU">
{t("joblines.fields.lbr_types.LAU")}
</Select.Option>
<Select.Option value="LA1">
{t("joblines.fields.lbr_types.LA1")}
</Select.Option>
<Select.Option value="LA2">
{t("joblines.fields.lbr_types.LA2")}
</Select.Option>
<Select.Option value="LA3">
{t("joblines.fields.lbr_types.LA3")}
</Select.Option>
<Select.Option value="LA4">
{t("joblines.fields.lbr_types.LA4")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("jobs.labels.adjustmentrate")}
name={[field.name, "lbr_adjustment", "rate"]}
initialValue={
bodyshop.default_adjustment_rate
}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
>
<InputNumber precision={2} />
</Form.Item>
</div>
);
return <span />;
}}
</Form.Item>
</LayoutFormRow>
<FormListMoveArrows
move={move}
@@ -246,3 +363,8 @@ export default function BillEnterModalLinesComponent({
</Form.List>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(BillEnterModalLinesComponent);

View File

@@ -20,12 +20,16 @@ export default forwardRef(FormItemPhone);
export const PhoneItemFormatterValidation = (getFieldValue, name) => ({
async validator(rule, value) {
const p = phone(getFieldValue(name), "us");
const p2 = phone(getFieldValue(name), "ca");
if (p.length > 0 || p2.length > 0) {
if (!getFieldValue(name)) {
return Promise.resolve();
} else {
return Promise.reject(i18n.t("general.validation.invalidphone"));
const p = phone(getFieldValue(name), "us");
const p2 = phone(getFieldValue(name), "ca");
if (p.length > 0 || p2.length > 0) {
return Promise.resolve();
} else {
return Promise.reject(i18n.t("general.validation.invalidphone"));
}
}
// PhoneInput({

View File

@@ -15,7 +15,7 @@ import { UPDATE_JOB } from "../../graphql/jobs.queries";
export default function LaborAllocationsAdjustmentEdit({
jobId,
mod_lbr_ty,
adjustments = [],
adjustments,
children,
}) {
const [loading, setLoading] = useState(false);
@@ -30,10 +30,10 @@ export default function LaborAllocationsAdjustmentEdit({
variables: {
jobId: jobId,
job: {
lbr_adjustments: [
{ mod_lbr_ty: values.mod_lbr_ty, hours: values.hours },
...adjustments.filter((a) => a.mod_lbr_ty !== values.mod_lbr_ty),
],
lbr_adjustments: {
...adjustments,
[values.mod_lbr_ty]: values.hours,
},
},
},
});
@@ -61,8 +61,7 @@ export default function LaborAllocationsAdjustmentEdit({
onFinish={handleFinish}
initialValues={{
mod_lbr_ty: mod_lbr_ty,
hours: adjustments.filter((i) => i.mod_lbr_ty === mod_lbr_ty)[0]
?.hours,
hours: adjustments[mod_lbr_ty],
}}
>
<Form.Item

View File

@@ -8,9 +8,7 @@ export const CalculateAllocationsTotals = (
//.filter((value, index, self) => self.indexOf(value) === index && !!value);
const ticketCodes = timetickets.map((item) => item.cieca_code);
//.filter((value, index, self) => self.indexOf(value) === index && !!value);
const adjustmentCodes = adjustments.map((item) => item.mod_lbr_ty);
//.filter((value, index, self) => self.indexOf(value) === index && !!value);
const allCodes = [...jobCodes, ...ticketCodes, ...adjustmentCodes].filter(
const allCodes = [...jobCodes, ...ticketCodes].filter(
(value, index, self) => self.indexOf(value) === index && !!value
);
@@ -21,11 +19,7 @@ export const CalculateAllocationsTotals = (
total: joblines.reduce((acc2, val2) => {
return val2.mod_lbr_ty === value ? acc2 + val2.mod_lb_hrs : acc2;
}, 0),
adjustments: adjustments.reduce((acc3, val3) => {
console.log("acc3", acc3);
console.log("val3", val3);
return val3.mod_lbr_ty === value ? acc3 + val3.hours : acc3;
}, 0),
adjustments: adjustments[value] || 0,
claimed: timetickets.reduce((acc3, val3) => {
return val3.ciecacode === value ? acc3 + val3.productivehrs : acc3;
}, 0),

View File

@@ -285,6 +285,18 @@ export default function ShopInfoComponent({ form, saveLoading }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.default_adjustment_rate")}
name={"default_adjustment_rate"}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} precision={2} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (

View File

@@ -75,6 +75,7 @@ export const QUERY_BODYSHOP = gql`
schedule_start_time
schedule_end_time
imexshopid
default_adjustment_rate
employees {
id
first_name
@@ -148,6 +149,7 @@ export const UPDATE_SHOP = gql`
schedule_start_time
schedule_end_time
imexshopid
default_adjustment_rate
employees {
id
first_name

View File

@@ -97,6 +97,7 @@
"state_tax_applicable": "St. Tax?"
},
"labels": {
"deductfromlabor": "Deduct from Labor?",
"entered": "Entered",
"from": "From",
"other": "--Not On Estimate--",
@@ -184,6 +185,7 @@
"country": "Country",
"dailybodytarget": "Scoreboard - Daily Body Target",
"dailypainttarget": "Scoreboard - Daily Paint Target",
"default_adjustment_rate": "Default Labor Deduction Adjustment Rate",
"deliver": {
"templates": "Delivery Templates"
},
@@ -1026,6 +1028,7 @@
},
"labels": {
"additionaltotal": "Additional Total",
"adjustmentrate": "Adjustment Rate",
"adjustments": "Adjustments",
"allocations": "Allocations",
"appointmentconfirmation": "Send confirmation to customer?",

View File

@@ -97,6 +97,7 @@
"state_tax_applicable": ""
},
"labels": {
"deductfromlabor": "",
"entered": "",
"from": "",
"other": "",
@@ -184,6 +185,7 @@
"country": "",
"dailybodytarget": "",
"dailypainttarget": "",
"default_adjustment_rate": "",
"deliver": {
"templates": ""
},
@@ -1026,6 +1028,7 @@
},
"labels": {
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"allocations": "",
"appointmentconfirmation": "¿Enviar confirmación al cliente?",

View File

@@ -97,6 +97,7 @@
"state_tax_applicable": ""
},
"labels": {
"deductfromlabor": "",
"entered": "",
"from": "",
"other": "",
@@ -184,6 +185,7 @@
"country": "",
"dailybodytarget": "",
"dailypainttarget": "",
"default_adjustment_rate": "",
"deliver": {
"templates": ""
},
@@ -1026,6 +1028,7 @@
},
"labels": {
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"allocations": "",
"appointmentconfirmation": "Envoyer une confirmation au client?",

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "default_adjustment_rate";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "default_adjustment_rate" numeric
NULL;
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."bodyshops" ALTER COLUMN "default_adjustment_rate"
DROP DEFAULT;
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."bodyshops" ALTER COLUMN "default_adjustment_rate"
SET DEFAULT 0;
type: run_sql

View File

@@ -0,0 +1,75 @@
- 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
- 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
- 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

View File

@@ -0,0 +1,76 @@
- 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
- 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

View File

@@ -0,0 +1,68 @@
- 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
- 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
- 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

View File

@@ -0,0 +1,69 @@
- 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
- 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

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."jobs" ALTER COLUMN "lbr_adjustments" SET DEFAULT
jsonb_build_array();
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE ONLY "public"."jobs" ALTER COLUMN "lbr_adjustments" SET DEFAULT
jsonb_build_object();
type: run_sql

View File

@@ -707,6 +707,7 @@ tables:
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
@@ -770,6 +771,7 @@ tables:
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class