Fixed rounding bug on invoice total calculation BOD-120. Added documents viewing to edit invoice BOD-75 BOD-63.

This commit is contained in:
Patrick Fic
2020-05-07 09:25:48 -07:00
parent 9d4b36aeec
commit 723f8da825
13 changed files with 65 additions and 30 deletions

View File

@@ -16,6 +16,8 @@ import AlertComponent from "../alert/alert.component";
import InvoiceFormContainer from "../invoice-form/invoice-form.container"; import InvoiceFormContainer from "../invoice-form/invoice-form.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { UPDATE_INVOICE_LINE } from "../../graphql/invoice-lines.queries"; import { UPDATE_INVOICE_LINE } from "../../graphql/invoice-lines.queries";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -27,7 +29,7 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
const [updateInvoice] = useMutation(UPDATE_INVOICE); const [updateInvoice] = useMutation(UPDATE_INVOICE);
const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE); const [updateInvoiceLine] = useMutation(UPDATE_INVOICE_LINE);
const { loading, error, data } = useQuery(QUERY_INVOICE_BY_PK, { const { loading, error, data, refetch } = useQuery(QUERY_INVOICE_BY_PK, {
variables: { invoiceid: search.invoiceid }, variables: { invoiceid: search.invoiceid },
skip: !!!search.invoiceid, skip: !!!search.invoiceid,
}); });
@@ -100,7 +102,13 @@ export function InvoiceDetailEditContainer({ bodyshop }) {
<Button htmlType="submit" type="primary"> <Button htmlType="submit" type="primary">
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
<InvoiceFormContainer form={form} hideVendor /> <InvoiceFormContainer form={form} invoiceEdit />
<JobDocumentsGallery
jobId={data ? data.invoices_by_pk.jobid : null}
invoiceId={search.invoiceid}
documentsList={data ? data.invoices_by_pk.documents : []}
invoicesCallback={refetch}
/>
</Form> </Form>
</LoadingSkeleton> </LoadingSkeleton>
); );

View File

@@ -23,7 +23,7 @@ export default function InvoiceFormComponent({
lineData, lineData,
responsibilityCenters, responsibilityCenters,
loadLines, loadLines,
hideVendor, invoiceEdit,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -61,6 +61,7 @@ export default function InvoiceFormComponent({
> >
<JobSearchSelect <JobSearchSelect
options={roAutoCompleteOptions} options={roAutoCompleteOptions}
disabled={invoiceEdit}
onBlur={() => { onBlur={() => {
if (form.getFieldValue("jobid") !== null) { if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } }); loadLines({ variables: { id: form.getFieldValue("jobid") } });
@@ -71,7 +72,7 @@ export default function InvoiceFormComponent({
<Form.Item <Form.Item
label={t("invoices.fields.vendor")} label={t("invoices.fields.vendor")}
name="vendorid" name="vendorid"
style={{ display: hideVendor ? "none" : null }} style={{ display: invoiceEdit ? "none" : null }}
rules={[ rules={[
{ {
required: true, required: true,
@@ -162,6 +163,7 @@ export default function InvoiceFormComponent({
<Form.Item <Form.Item
name="upload" name="upload"
label="Upload" label="Upload"
style={{ display: invoiceEdit ? "none" : null }}
valuePropName="fileList" valuePropName="fileList"
getValueFromEvent={(e) => { getValueFromEvent={(e) => {
console.log("Upload event:", e); console.log("Upload event:", e);
@@ -239,10 +241,6 @@ export default function InvoiceFormComponent({
return null; return null;
}} }}
</Form.Item> </Form.Item>
<Button onClick={() => console.log(form.getFieldsValue())}>
Get Values
</Button>
</div> </div>
); );
} }

View File

@@ -12,7 +12,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function InvoiceFormContainer({ bodyshop, form, hideVendor }) { export function InvoiceFormContainer({ bodyshop, form, invoiceEdit }) {
const { data: RoAutoCompleteData } = useQuery(ACTIVE_JOBS_FOR_AUTOCOMPLETE, { const { data: RoAutoCompleteData } = useQuery(ACTIVE_JOBS_FOR_AUTOCOMPLETE, {
variables: { statuses: bodyshop.md_ro_statuses.open_statuses || ["Open"] }, variables: { statuses: bodyshop.md_ro_statuses.open_statuses || ["Open"] },
}); });
@@ -27,7 +27,7 @@ export function InvoiceFormContainer({ bodyshop, form, hideVendor }) {
<div> <div>
<InvoiceFormComponent <InvoiceFormComponent
form={form} form={form}
hideVendor={hideVendor} invoiceEdit={invoiceEdit}
roAutoCompleteOptions={RoAutoCompleteData && RoAutoCompleteData.jobs} roAutoCompleteOptions={RoAutoCompleteData && RoAutoCompleteData.jobs}
vendorAutoCompleteOptions={ vendorAutoCompleteOptions={
VendorAutoCompleteData && VendorAutoCompleteData.vendors VendorAutoCompleteData && VendorAutoCompleteData.vendors

View File

@@ -33,7 +33,7 @@ export const CalculateInvoiceTotal = (invoice) => {
} }
}); });
const invoiceTotal = Dinero({ amount: (parseFloat(total) || 0) * 100 }); const invoiceTotal = Dinero({ amount: Math.round((total || 0) * 100) });
const enteredTotal = subtotal.add(federalTax).add(stateTax).add(localTax); const enteredTotal = subtotal.add(federalTax).add(stateTax).add(localTax);
const discrepancy = enteredTotal.subtract(invoiceTotal); const discrepancy = enteredTotal.subtract(invoiceTotal);

View File

@@ -4,7 +4,7 @@ const { Option } = Select;
//To be used as a form element only. //To be used as a form element only.
const JobSearchSelect = ({ value, onChange, options, onBlur }) => { const JobSearchSelect = ({ value, onChange, options, onBlur, disabled }) => {
const [option, setOption] = useState(value); const [option, setOption] = useState(value);
useEffect(() => { useEffect(() => {
@@ -15,22 +15,25 @@ const JobSearchSelect = ({ value, onChange, options, onBlur }) => {
return ( return (
<Select <Select
disabled={disabled}
showSearch showSearch
autoFocus autoFocus
value={option} value={option}
style={{ style={{
width: 300 width: 300,
}} }}
onChange={setOption} onChange={setOption}
optionFilterProp="children" optionFilterProp="children"
onBlur={onBlur} onBlur={onBlur}
> >
{options {options
? options.map(o => ( ? options.map((o) => (
<Option key={o.id} value={o.id}> <Option key={o.id} value={o.id}>
{`${o.ro_number ? o.ro_number : o.est_number} | ${o.ownr_ln || {`${o.ro_number ? o.ro_number : o.est_number} | ${
""} ${o.ownr_fn || ""} | ${o.v_model_yr || o.ownr_ln || ""
""} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`} } ${o.ownr_fn || ""} | ${o.v_model_yr || ""} ${
o.v_make_desc || ""
} ${o.v_model_desc || ""}`}
</Option> </Option>
)) ))
: null} : null}

View File

@@ -4,9 +4,15 @@ import DocumentsUploadContainer from "../documents-upload/documents-upload.conta
import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component"; import JobsDocumentsDownloadButton from "./jobs-document-gallery.download.component";
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component"; import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
function JobsDocumentsComponent({ data, jobId, refetch }) { function JobsDocumentsComponent({
data,
jobId,
refetch,
invoiceId,
invoicesCallback,
}) {
const [galleryImages, setgalleryImages] = useState([]); const [galleryImages, setgalleryImages] = useState([]);
console.log("Gallery Data", data);
useEffect(() => { useEffect(() => {
setgalleryImages( setgalleryImages(
data.reduce((acc, value) => { data.reduce((acc, value) => {
@@ -28,17 +34,18 @@ function JobsDocumentsComponent({ data, jobId, refetch }) {
}, [data, setgalleryImages]); }, [data, setgalleryImages]);
return ( return (
<div className='clearfix'> <div className="clearfix">
<DocumentsUploadContainer <DocumentsUploadContainer
jobId={jobId} jobId={jobId}
callbackAfterUpload={refetch} invoiceId={invoiceId}
callbackAfterUpload={invoicesCallback || refetch}
tagsArray={["test"]} tagsArray={["test"]}
/> />
<JobsDocumentsDownloadButton galleryImages={galleryImages} /> <JobsDocumentsDownloadButton galleryImages={galleryImages} />
<JobsDocumentsDeleteButton <JobsDocumentsDeleteButton
galleryImages={galleryImages} galleryImages={galleryImages}
deletionCallback={refetch} deletionCallback={invoicesCallback || refetch}
/> />
<Gallery <Gallery

View File

@@ -5,14 +5,28 @@ import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import JobDocuments from "./jobs-documents-gallery.component"; import JobDocuments from "./jobs-documents-gallery.component";
export default function JobsDocumentsContainer({ jobId }) { export default function JobsDocumentsContainer({
jobId,
invoiceId,
documentsList,
invoicesCallback,
}) {
const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, { const { loading, error, data, refetch } = useQuery(GET_DOCUMENTS_BY_JOB, {
variables: { jobId: jobId }, variables: { jobId: jobId },
fetchPolicy: "network-only" fetchPolicy: "network-only",
skip: !!invoiceId,
}); });
if (loading) return <LoadingSpinner />; if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent type="error" message={error.message} />; if (error) return <AlertComponent type="error" message={error.message} />;
return <JobDocuments data={data.documents} jobId={jobId} refetch={refetch} />; return (
<JobDocuments
data={(data && data.documents) || documentsList || []}
invoiceId={invoiceId}
jobId={jobId}
refetch={refetch}
invoicesCallback={invoicesCallback}
/>
);
} }

View File

@@ -6,7 +6,6 @@ import "firebase/analytics";
import "firebase/messaging"; import "firebase/messaging";
const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG); const config = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
console.log("Firebase config", config);
firebase.initializeApp(config); firebase.initializeApp(config);
export const auth = firebase.auth(); export const auth = firebase.auth();

View File

@@ -107,6 +107,12 @@ export const QUERY_INVOICE_BY_PK = gql`
joblineid joblineid
applicable_taxes applicable_taxes
} }
documents {
id
key
name
type
}
} }
} }
`; `;

View File

@@ -826,7 +826,7 @@
} }
}, },
"titles": { "titles": {
"app": "Bodyshop by ImEX Systems", "app": "ImEX Online",
"bc": { "bc": {
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"contracts": "Contracts", "contracts": "Contracts",

View File

@@ -826,7 +826,7 @@
} }
}, },
"titles": { "titles": {
"app": "Carrocería de ImEX Systems", "app": "ImEX Online",
"bc": { "bc": {
"availablejobs": "", "availablejobs": "",
"contracts": "", "contracts": "",

View File

@@ -826,7 +826,7 @@
} }
}, },
"titles": { "titles": {
"app": "Carrosserie par ImEX Systems", "app": "ImEX Online",
"bc": { "bc": {
"availablejobs": "", "availablejobs": "",
"contracts": "", "contracts": "",

View File

@@ -20,7 +20,7 @@ i18n
//lng: "en", //lng: "en",
detection: {}, detection: {},
fallbackLng: "en-US", fallbackLng: "en-US",
debug: true, debug: process.env.NODE_ENV === "production" ? false : true,
//keySeparator: false, // we do not use keys in form messages.welcome //keySeparator: false, // we do not use keys in form messages.welcome