Refactored how in house invoices are created IO-460
This commit is contained in:
@@ -183,7 +183,10 @@ function BillEnterModalContainer({
|
|||||||
0,
|
0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BillFormContainer form={form} />
|
<BillFormContainer
|
||||||
|
form={form}
|
||||||
|
disableInvNumber={billEnterModal.context.disableInvNumber}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export function BillFormComponent({
|
|||||||
responsibilityCenters,
|
responsibilityCenters,
|
||||||
loadLines,
|
loadLines,
|
||||||
billEdit,
|
billEdit,
|
||||||
|
disableInvNumber,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -142,7 +143,15 @@ export function BillFormComponent({
|
|||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Input disabled={disabled} />
|
<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>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bills.fields.date")}
|
label={t("bills.fields.date")}
|
||||||
@@ -193,7 +202,6 @@ export function BillFormComponent({
|
|||||||
>
|
>
|
||||||
<CurrencyInput min={0} />
|
<CurrencyInput min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
|
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
|
||||||
<Select style={{ width: "10rem" }} disabled={disabled}>
|
<Select style={{ width: "10rem" }} disabled={disabled}>
|
||||||
{bodyshop.md_parts_locations.map((loc, idx) => (
|
{bodyshop.md_parts_locations.map((loc, idx) => (
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function BillFormContainer({ bodyshop, form, billEdit, disabled }) {
|
export function BillFormContainer({
|
||||||
|
bodyshop,
|
||||||
|
form,
|
||||||
|
billEdit,
|
||||||
|
disabled,
|
||||||
|
disableInvNumber,
|
||||||
|
}) {
|
||||||
const { data: VendorAutoCompleteData } = useQuery(SEARCH_VENDOR_AUTOCOMPLETE);
|
const { data: VendorAutoCompleteData } = useQuery(SEARCH_VENDOR_AUTOCOMPLETE);
|
||||||
|
|
||||||
const [loadLines, { data: lineData }] = useLazyQuery(
|
const [loadLines, { data: lineData }] = useLazyQuery(
|
||||||
@@ -29,6 +35,7 @@ export function BillFormContainer({ bodyshop, form, billEdit, disabled }) {
|
|||||||
loadLines={loadLines}
|
loadLines={loadLines}
|
||||||
lineData={lineData ? lineData.joblines : []}
|
lineData={lineData ? lineData.joblines : []}
|
||||||
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
||||||
|
disableInvNumber={disableInvNumber}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export default function PartsOrderModalComponent({
|
|||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("parts_orders.fields.act_price")}
|
label={t("parts_orders.fields.act_price")}
|
||||||
key={`${index}act_price`}
|
key={`${index}act_price`}
|
||||||
|
|||||||
@@ -1,29 +1,31 @@
|
|||||||
import { useMutation, useQuery } from "@apollo/react-hooks";
|
import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||||
import { Form, Modal, notification } from "antd";
|
import { Form, Modal, notification } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
||||||
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
|
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
|
||||||
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
|
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import {
|
||||||
|
setModalContext,
|
||||||
|
toggleModalVisible,
|
||||||
|
} from "../../redux/modals/modals.actions";
|
||||||
import { selectPartsOrder } from "../../redux/modals/modals.selectors";
|
import { selectPartsOrder } from "../../redux/modals/modals.selectors";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from "../alert/alert.component";
|
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
|
||||||
import PartsOrderModalComponent from "./parts-order-modal.component";
|
|
||||||
import RenderTemplate, {
|
import RenderTemplate, {
|
||||||
displayTemplateInWindow,
|
displayTemplateInWindow,
|
||||||
} from "../../utils/RenderTemplate";
|
} from "../../utils/RenderTemplate";
|
||||||
import moment from "moment";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import PartsOrderModalComponent from "./parts-order-modal.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -34,6 +36,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
|
||||||
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsOrderModalContainer({
|
export function PartsOrderModalContainer({
|
||||||
@@ -42,6 +46,7 @@ export function PartsOrderModalContainer({
|
|||||||
currentUser,
|
currentUser,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
setEmailOptions,
|
setEmailOptions,
|
||||||
|
setBillEnterContext,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -68,7 +73,6 @@ export function PartsOrderModalContainer({
|
|||||||
|
|
||||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
|
||||||
const [insertBill] = useMutation(INSERT_NEW_BILL);
|
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
logImEXEvent("parts_order_insert");
|
logImEXEvent("parts_order_insert");
|
||||||
@@ -113,37 +117,37 @@ export function PartsOrderModalContainer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (values.vendorid === bodyshop.inhousevendorid) {
|
if (values.vendorid === bodyshop.inhousevendorid) {
|
||||||
logImEXEvent("parts_order_inhouse_invoice");
|
setBillEnterContext({
|
||||||
|
actions: { refetch: refetch },
|
||||||
let invoiceToPost = {
|
context: {
|
||||||
vendorid: bodyshop.inhousevendorid,
|
disableInvNumber: true,
|
||||||
isinhouse: true,
|
job: { id: jobId },
|
||||||
jobid: jobId,
|
bill: {
|
||||||
total: 0,
|
vendorid: bodyshop.inhousevendorid,
|
||||||
invoice_number: "ih",
|
invoice_number: "ih",
|
||||||
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate || 0,
|
isinhouse: true,
|
||||||
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate || 0,
|
date: new moment(),
|
||||||
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate || 0,
|
billlines: values.parts_order_lines.data.map((p) => {
|
||||||
billlines: {
|
return {
|
||||||
data: values.parts_order_lines.data.map((p) => {
|
joblineid: p.job_line_id,
|
||||||
return {
|
actual_price: p.act_price,
|
||||||
joblineid: p.job_line_id,
|
actual_cost: p.act_price,
|
||||||
actual_price: p.act_price,
|
line_desc: p.line_desc,
|
||||||
actual_cost: p.act_price,
|
line_remarks: p.line_remarks,
|
||||||
line_desc: p.line_desc,
|
part_type: p.part_type,
|
||||||
line_remarks: p.line_remarks,
|
quantity: p.quantity || 1,
|
||||||
quantity: p.quantity || 1,
|
applicable_taxes: {
|
||||||
cost_center:
|
local: false,
|
||||||
bodyshop.md_responsibility_centers.defaults.costs.PAO ||
|
state: p.tax_part,
|
||||||
"Other*",
|
federal: true,
|
||||||
};
|
},
|
||||||
}),
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
await insertBill({
|
|
||||||
variables: { bill: invoiceToPost },
|
|
||||||
});
|
});
|
||||||
|
toggleModalVisible();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refetch) refetch();
|
if (refetch) refetch();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const GET_JOB_LINES_BY_PK = gql`
|
|||||||
status
|
status
|
||||||
notes
|
notes
|
||||||
location
|
location
|
||||||
|
tax_part
|
||||||
parts_order_lines {
|
parts_order_lines {
|
||||||
id
|
id
|
||||||
parts_order {
|
parts_order {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."parts_order_lines" DROP COLUMN "part_type";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."parts_order_lines" ADD COLUMN "part_type" text NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- part_type
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- part_type
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- act_price
|
||||||
|
- backordered_eta
|
||||||
|
- backordered_on
|
||||||
|
- created_at
|
||||||
|
- db_price
|
||||||
|
- id
|
||||||
|
- job_line_id
|
||||||
|
- line_desc
|
||||||
|
- line_remarks
|
||||||
|
- oem_partno
|
||||||
|
- orderid
|
||||||
|
- part_type
|
||||||
|
- quantity
|
||||||
|
- status
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
parts_order:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: parts_order_lines
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -3241,6 +3241,7 @@ tables:
|
|||||||
- line_remarks
|
- line_remarks
|
||||||
- oem_partno
|
- oem_partno
|
||||||
- orderid
|
- orderid
|
||||||
|
- part_type
|
||||||
- quantity
|
- quantity
|
||||||
- status
|
- status
|
||||||
- updated_at
|
- updated_at
|
||||||
@@ -3259,6 +3260,7 @@ tables:
|
|||||||
- line_remarks
|
- line_remarks
|
||||||
- oem_partno
|
- oem_partno
|
||||||
- orderid
|
- orderid
|
||||||
|
- part_type
|
||||||
- quantity
|
- quantity
|
||||||
- status
|
- status
|
||||||
- updated_at
|
- updated_at
|
||||||
@@ -3288,6 +3290,7 @@ tables:
|
|||||||
- line_remarks
|
- line_remarks
|
||||||
- oem_partno
|
- oem_partno
|
||||||
- orderid
|
- orderid
|
||||||
|
- part_type
|
||||||
- quantity
|
- quantity
|
||||||
- status
|
- status
|
||||||
- updated_at
|
- updated_at
|
||||||
|
|||||||
Reference in New Issue
Block a user