Migrations for invoices and invoice lines. Added invoice enter modal.

This commit is contained in:
Patrick Fic
2020-02-24 17:09:17 -08:00
parent 13faf47044
commit f70627b5da
31 changed files with 730 additions and 23 deletions

View File

@@ -4,6 +4,7 @@ import { DELETE_ALLOCATION } from "../../graphql/allocations.queries";
import AllocationsLabelComponent from "./allocations-employee-label.component";
import { notification } from "antd";
import { useTranslation } from "react-i18next";
export default function AllocationsLabelContainer({ allocation, refetch }) {
const [deleteAllocation] = useMutation(DELETE_ALLOCATION);
const { t } = useTranslation();

View File

@@ -0,0 +1,71 @@
import { Modal, Form, Input, InputNumber } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import ResetForm from "../form-items-formatted/reset-form-item.component";
export default function InvoiceEnterModalComponent({
visible,
invoice,
handleCancel,
handleSubmit,
form
}) {
const { t } = useTranslation();
const { getFieldDecorator, isFieldsTouched, resetFields } = form;
return (
<Modal
title={
invoice && invoice.id
? t("invoice.labels.edit")
: t("invoice.labels.new")
}
visible={visible}
okText={t("general.labels.save")}
onOk={handleSubmit}
onCancel={handleCancel}
>
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
<Form onSubmit={handleSubmit} autoComplete={"off"}>
{JSON.stringify(invoice)}
{
// <Form.Item label={t("joblines.fields.line_desc")}>
// {getFieldDecorator("line_desc", {
// initialValue: jobLine.line_desc
// })(<Input name="line_desc" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.oem_partno")}>
// {getFieldDecorator("oem_partno", {
// initialValue: jobLine.oem_partno
// })(<Input name="oem_partno" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.part_type")}>
// {getFieldDecorator("part_type", {
// initialValue: jobLine.part_type
// })(<Input name="part_type" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.mod_lbr_ty")}>
// {getFieldDecorator("mod_lbr_ty", {
// initialValue: jobLine.mod_lbr_ty
// })(<Input name="mod_lbr_ty" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.op_code_desc")}>
// {getFieldDecorator("op_code_desc", {
// initialValue: jobLine.op_code_desc
// })(<Input name="op_code_desc" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.mod_lb_hrs")}>
// {getFieldDecorator("mod_lb_hrs", {
// initialValue: jobLine.mod_lb_hrs
// })(<InputNumber name="mod_lb_hrs" />)}
// </Form.Item>
// <Form.Item label={t("joblines.fields.act_price")}>
// {getFieldDecorator("act_price", {
// initialValue: jobLine.act_price
// })(<InputNumber name="act_price" />)}
// </Form.Item>
}
</Form>
</Modal>
);
}

View File

@@ -0,0 +1,114 @@
import { Form, notification } from "antd";
import React from "react";
import { useMutation } from "react-apollo";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
INSERT_NEW_JOB_LINE,
UPDATE_JOB_LINE
} from "../../graphql/jobs-lines.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectInvoiceEnterModal } from "../../redux/modals/modals.selectors";
import InvoiceEnterModalComponent from "./invoice-enter-modal.component";
const mapStateToProps = createStructuredSelector({
invoiceEnterModal: selectInvoiceEnterModal
});
const mapDispatchToProps = dispatch => ({
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter"))
});
function InvoiceEnterModalContainer({
invoiceEnterModal,
toggleModalVisible,
form
}) {
const { t } = useTranslation();
// const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
// const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("invoices.errors.validation"),
description: err.message
});
}
if (!err) {
alert("Closing this modal.");
toggleModalVisible();
// if (!jobLineEditModal.context.id) {
// insertJobLine({
// variables: {
// lineInput: [{ jobid: jobLineEditModal.context.jobid, ...values }]
// }
// })
// .then(r => {
// if (jobLineEditModal.actions.refetch)
// jobLineEditModal.actions.refetch();
// toggleModalVisible();
// notification["success"]({
// message: t("joblines.successes.created")
// });
// })
// .catch(error => {
// notification["error"]({
// message: t("joblines.errors.creating", {
// message: error.message
// })
// });
// });
// } else {
// updateJobLine({
// variables: {
// lineId: jobLineEditModal.context.id,
// line: values
// }
// })
// .then(r => {
// notification["success"]({
// message: t("joblines.successes.updated")
// });
// })
// .catch(error => {
// notification["success"]({
// message: t("joblines.errors.updating", {
// message: error.message
// })
// });
// });
// if (jobLineEditModal.actions.refetch)
// jobLineEditModal.actions.refetch();
// toggleModalVisible();
// }
}
});
};
const handleCancel = () => {
toggleModalVisible();
};
return (
<InvoiceEnterModalComponent
visible={invoiceEnterModal.visible}
invoice={invoiceEnterModal.context}
handleSubmit={handleSubmit}
handleCancel={handleCancel}
form={form}
/>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(
Form.create({ name: "InvoiceEnterModalContainer" })(
InvoiceEnterModalContainer
)
);

View File

@@ -34,8 +34,8 @@ function JobLinesUpsertModalContainer({
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("jobs.errors.validationtitle"),
description: t("jobs.errors.validation")
message: t("joblines.errors.validation"),
description: err.message
});
}
if (!err) {

View File

@@ -0,0 +1,41 @@
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
toggleModalVisible,
setModalContext
} from "../../redux/modals/modals.actions";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
toggleModalVisible: () => dispatch(toggleModalVisible("invoiceEnter")),
setInvoiceEnterContext: context =>
dispatch(setModalContext({ context: context, modal: "invoiceEnter" }))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function JobsDetailPliComponent({
toggleModalVisible,
setInvoiceEnterContext,
job
}) {
return (
<div>
<div
onClick={() => {
setInvoiceEnterContext({
actions: { refetch: null },
context: {
job
}
});
}}
>
Enter Invoice
</div>
</div>
);
});

View File

@@ -0,0 +1,7 @@
import React from "react";
import JobsDetailPliComponent from "./jobs-detail-pli.component";
export default function JobsDetailPliContainer({ job }) {
console.log("job", job);
return <JobsDetailPliComponent job={job} />;
}

View File

@@ -1,5 +1,5 @@
import { Form, Icon, Tabs } from "antd";
import React, { useContext } from "react";
import React, { lazy, Suspense, useContext } from "react";
import { useTranslation } from "react-i18next";
import {
FaHardHat,
@@ -8,17 +8,60 @@ import {
FaShieldAlt
} from "react-icons/fa";
import ResetForm from "../../components/form-items-formatted/reset-form-item.component";
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
import JobsDetailClaims from "../../components/jobs-detail-claims/jobs-detail-claims.component";
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
import JobsDetailFinancials from "../../components/jobs-detail-financial/jobs-detail-financial.component";
import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
import JobsDetailInsurance from "../../components/jobs-detail-insurance/jobs-detail-insurance.component";
import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documents.container";
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
//import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
//import JobsDetailClaims from "../../components/jobs-detail-claims/jobs-detail-claims.component";
//import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
//import JobsDetailFinancials from "../../components/jobs-detail-financial/jobs-detail-financial.component";
//import JobsDetailHeader from "../../components/jobs-detail-header/jobs-detail-header.component";
//import JobsDetailInsurance from "../../components/jobs-detail-insurance/jobs-detail-insurance.component";
//import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documents.container";
//import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
//import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
//import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
//import EnterInvoiceModalContainer from "../../components/invoice-enter-modal/invoice-enter-modal.container";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import JobDetailFormContext from "./jobs-detail.page.context";
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail-pli.container";
const JobsLinesContainer = lazy(() =>
import("../../components/job-detail-lines/job-lines.container")
);
const JobsDetailClaims = lazy(() =>
import("../../components/jobs-detail-claims/jobs-detail-claims.component")
);
const JobsDetailDatesComponent = lazy(() =>
import("../../components/jobs-detail-dates/jobs-detail-dates.component")
);
const JobsDetailFinancials = lazy(() =>
import(
"../../components/jobs-detail-financial/jobs-detail-financial.component"
)
);
const JobsDetailHeader = lazy(() =>
import("../../components/jobs-detail-header/jobs-detail-header.component")
);
const JobsDetailInsurance = lazy(() =>
import(
"../../components/jobs-detail-insurance/jobs-detail-insurance.component"
)
);
const JobsDocumentsContainer = lazy(() =>
import("../../components/jobs-documents/jobs-documents.container")
);
const JobNotesContainer = lazy(() =>
import("../../components/jobs-notes/jobs-notes.container")
);
const ScheduleJobModalContainer = lazy(() =>
import("../../components/schedule-job-modal/schedule-job-modal.container")
);
const JobLineUpsertModalContainer = lazy(() =>
import(
"../../components/job-lines-upsert-modal/job-lines-upsert-modal.container"
)
);
const EnterInvoiceModalContainer = lazy(() =>
import("../../components/invoice-enter-modal/invoice-enter-modal.container")
);
export default function JobsDetailPage({
job,
@@ -45,7 +88,9 @@ export default function JobsDetailPage({
};
return (
<div>
<Suspense
fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />}
>
<ScheduleJobModalContainer
scheduleModalState={scheduleModalState}
jobId={job.id}
@@ -53,6 +98,7 @@ export default function JobsDetailPage({
/>
<JobLineUpsertModalContainer />
<EnterInvoiceModalContainer />
<Form onSubmit={handleSubmit} {...formItemLayout} autoComplete={"off"}>
<JobsDetailHeader
@@ -124,7 +170,7 @@ export default function JobsDetailPage({
}
key="partssublet"
>
Partssublet
<JobsDetailPliContainer job={job} />
</Tabs.TabPane>
<Tabs.TabPane
@@ -175,6 +221,6 @@ export default function JobsDetailPage({
</Tabs.TabPane>
</Tabs>
</Form>
</div>
</Suspense>
);
}

View File

@@ -1,15 +1,18 @@
import ModalsActionTypes from "./modals.types";
const INITIAL_STATE = {
jobLineEdit: {
visible: false,
context: {},
actions: {
refetch: null
}
const baseModal = {
visible: false,
context: {},
actions: {
refetch: null
}
};
const INITIAL_STATE = {
jobLineEdit: { ...baseModal },
invoiceEnter: { ...baseModal }
};
const modalsReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case ModalsActionTypes.TOGGLE_MODAL_VISIBLE:

View File

@@ -7,3 +7,8 @@ export const selectJobLineEditModal = createSelector(
modals => modals.jobLineEdit
);
export const selectInvoiceEnterModal = createSelector(
[selectModals],
modals => modals.invoiceEnter
);

View File

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

View File

@@ -0,0 +1,28 @@
- args:
cascade: false
read_only: false
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
type: run_sql
- args:
cascade: false
read_only: false
sql: "CREATE TABLE \"public\".\"invoices\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
\"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
NOT NULL DEFAULT now(), \"vendorid\" uuid NOT NULL, \"jobid\" uuid NOT NULL,
\"date\" date NOT NULL DEFAULT now(), \"due_date\" date, \"exported\" boolean
NOT NULL DEFAULT false, \"exported_at\" timestamptz, \"is_credit_memo\" boolean
NOT NULL DEFAULT false, \"total\" numeric NOT NULL DEFAULT 0, \"invoice_number\"
text NOT NULL, PRIMARY KEY (\"id\") , FOREIGN KEY (\"jobid\") REFERENCES \"public\".\"jobs\"(\"id\")
ON UPDATE restrict ON DELETE cascade, FOREIGN KEY (\"vendorid\") REFERENCES
\"public\".\"vendors\"(\"id\") ON UPDATE restrict ON DELETE restrict, UNIQUE
(\"jobid\"));\nCREATE OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
= NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_invoices_updated_at\"\nBEFORE
UPDATE ON \"public\".\"invoices\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
ON TRIGGER \"set_public_invoices_updated_at\" ON \"public\".\"invoices\" \nIS
'trigger to set value of column \"updated_at\" to current timestamp on row update';"
type: run_sql
- args:
name: invoices
schema: public
type: add_existing_table_or_view

View File

@@ -0,0 +1,24 @@
- args:
relationship: job
table:
name: invoices
schema: public
type: drop_relationship
- args:
relationship: vendor
table:
name: invoices
schema: public
type: drop_relationship
- args:
relationship: invoice
table:
name: jobs
schema: public
type: drop_relationship
- args:
relationship: invoices
table:
name: vendors
schema: public
type: drop_relationship

View File

@@ -0,0 +1,41 @@
- args:
name: job
table:
name: invoices
schema: public
using:
foreign_key_constraint_on: jobid
type: create_object_relationship
- args:
name: vendor
table:
name: invoices
schema: public
using:
foreign_key_constraint_on: vendorid
type: create_object_relationship
- args:
name: invoice
table:
name: jobs
schema: public
using:
manual_configuration:
column_mapping:
id: jobid
remote_table:
name: invoices
schema: public
type: create_object_relationship
- args:
name: invoices
table:
name: vendors
schema: public
using:
foreign_key_constraint_on:
column: vendorid
table:
name: invoices
schema: public
type: create_array_relationship

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoices
schema: public
type: drop_insert_permission

View File

@@ -0,0 +1,35 @@
- args:
permission:
allow_upsert: true
check:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- vendorid
- jobid
- date
- due_date
- exported
- exported_at
- is_credit_memo
- total
- invoice_number
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: invoices
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoices
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,33 @@
- args:
permission:
allow_aggregations: false
columns:
- exported
- is_credit_memo
- date
- due_date
- total
- invoice_number
- created_at
- exported_at
- updated_at
- id
- jobid
- vendorid
computed_fields: []
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
limit: null
role: user
table:
name: invoices
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoices
schema: public
type: drop_update_permission

View File

@@ -0,0 +1,34 @@
- args:
permission:
columns:
- exported
- is_credit_memo
- date
- due_date
- total
- invoice_number
- created_at
- exported_at
- updated_at
- id
- jobid
- vendorid
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: invoices
schema: public
type: create_update_permission

View File

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

View File

@@ -0,0 +1,26 @@
- args:
cascade: false
read_only: false
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
type: run_sql
- args:
cascade: false
read_only: false
sql: "CREATE TABLE \"public\".\"invoicelines\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
\"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
NOT NULL DEFAULT now(), \"invoiceid\" uuid NOT NULL, \"line_desc\" text, \"actual_price\"
numeric NOT NULL DEFAULT 0, \"actual_cost\" numeric NOT NULL DEFAULT 0, \"cost_center\"
text NOT NULL, \"estlindid\" uuid, PRIMARY KEY (\"id\") , FOREIGN KEY (\"invoiceid\")
REFERENCES \"public\".\"invoices\"(\"id\") ON UPDATE restrict ON DELETE cascade);\nCREATE
OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
= NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_invoicelines_updated_at\"\nBEFORE
UPDATE ON \"public\".\"invoicelines\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
ON TRIGGER \"set_public_invoicelines_updated_at\" ON \"public\".\"invoicelines\"
\nIS 'trigger to set value of column \"updated_at\" to current timestamp on
row update';"
type: run_sql
- args:
name: invoicelines
schema: public
type: add_existing_table_or_view

View File

@@ -0,0 +1,12 @@
- args:
relationship: invoice
table:
name: invoicelines
schema: public
type: drop_relationship
- args:
relationship: invoicelines
table:
name: invoices
schema: public
type: drop_relationship

View File

@@ -0,0 +1,20 @@
- args:
name: invoice
table:
name: invoicelines
schema: public
using:
foreign_key_constraint_on: invoiceid
type: create_object_relationship
- args:
name: invoicelines
table:
name: invoices
schema: public
using:
foreign_key_constraint_on:
column: invoiceid
table:
name: invoicelines
schema: public
type: create_array_relationship

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoicelines
schema: public
type: drop_insert_permission

View File

@@ -0,0 +1,33 @@
- args:
permission:
allow_upsert: true
check:
invoice:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- invoiceid
- line_desc
- actual_price
- actual_cost
- cost_center
- estlindid
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: invoicelines
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoicelines
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,31 @@
- args:
permission:
allow_aggregations: false
columns:
- actual_cost
- actual_price
- cost_center
- line_desc
- created_at
- updated_at
- estlindid
- id
- invoiceid
computed_fields: []
filter:
invoice:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
limit: null
role: user
table:
name: invoicelines
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoicelines
schema: public
type: drop_update_permission

View File

@@ -0,0 +1,32 @@
- args:
permission:
columns:
- actual_cost
- actual_price
- cost_center
- line_desc
- created_at
- updated_at
- estlindid
- id
- invoiceid
filter:
invoice:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: invoicelines
schema: public
type: create_update_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: invoicelines
schema: public
type: drop_delete_permission

View File

@@ -0,0 +1,18 @@
- args:
permission:
filter:
invoice:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: invoicelines
schema: public
type: create_delete_permission