Modification to Credits not Received.

This commit is contained in:
Patrick Fic
2022-04-21 11:45:41 -07:00
parent 51843f364b
commit 49818cc043
20 changed files with 511 additions and 26 deletions

View File

@@ -32515,6 +32515,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>saving</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>
</children>
</folder_node>
<folder_node>
@@ -32583,6 +32604,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>cm_received</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>comments</name>
<definition_loaded>false</definition_loaded>
@@ -33008,6 +33050,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>mark_as_received</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>newpartsorder</name>
<definition_loaded>false</definition_loaded>
@@ -33202,6 +33265,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>line_updated</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>received</name>
<definition_loaded>false</definition_loaded>

View File

@@ -0,0 +1,136 @@
import { Checkbox, Form, Skeleton, Typography } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-cm-returns-table.styles.scss";
export default function BillCmdReturnsTableComponent({
form,
loadOutstandingReturns,
returnLoading,
returnData,
}) {
const { t } = useTranslation();
useEffect(() => {
if (returnData) {
form.setFieldsValue({
outstanding_returns: returnData.parts_order_lines,
});
}
}, [returnData, form]);
return (
<Form.Item
shouldUpdate={(prev, cur) =>
prev.jobid !== cur.jobid ||
prev.is_credit_memo !== cur.is_credit_memo ||
prev.vendorid !== cur.vendorid
}
noStyle
>
{() => {
const isReturn = form.getFieldValue("is_credit_memo");
if (!isReturn) {
return null;
}
if (returnLoading) return <Skeleton />;
return (
<Form.List name="outstanding_returns">
{(fields, { add, remove, move }) => {
return (
<>
<Typography.Title level={4}>
{t("bills.labels.creditsnotreceived")}
</Typography.Title>
<table className="bill-cm-returns-table">
<thead>
<tr>
<th>{t("parts_orders.fields.line_desc")}</th>
<th>{t("parts_orders.fields.part_type")}</th>
<th>{t("parts_orders.fields.quantity")}</th>
<th>{t("parts_orders.fields.act_price")}</th>
<th>{t("parts_orders.fields.cost")}</th>
<th>{t("parts_orders.labels.mark_as_received")}</th>
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.key}>
<td>
<Form.Item
// label={t("joblines.fields.line_desc")}
key={`${index}line_desc`}
name={[field.name, "line_desc"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}quantity`}
name={[field.name, "quantity"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}act_price`}
name={[field.name, "act_price"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cost`}
name={[field.name, "cost"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cm_received`}
name={[field.name, "cm_received"]}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}}
</Form.List>
);
}}
</Form.Item>
);
}

View File

@@ -0,0 +1,19 @@
.bill-cm-returns-table {
table-layout: fixed;
width: 100%;
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
.ant-form-item {
margin-bottom: 0px !important;
}
}
tr:hover {
background-color: #f5f5f5;
}
}

View File

@@ -11,6 +11,7 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
@@ -47,6 +48,7 @@ function BillEnterModalContainer({
const [enterAgain, setEnterAgain] = useState(false);
const [insertBill] = useMutation(INSERT_NEW_BILL);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
const [loading, setLoading] = useState(false);
const client = useApolloClient();
@@ -76,7 +78,8 @@ function BillEnterModalContainer({
}
setLoading(true);
const { upload, location, ...remainingValues } = values;
const { upload, location, outstanding_returns, ...remainingValues } =
values;
let adjustmentsToInsert = {};
@@ -156,6 +159,25 @@ function BillEnterModalContainer({
});
}
const markPolReceived = outstanding_returns.filter(
(o) => o.cm_received === true
);
if (markPolReceived.length > 0) {
const r2 = await updatePartsOrderLines({
variables: { partsLineIds: markPolReceived.map((p) => p.id) },
});
if (!!r2.errors) {
setLoading(false);
setEnterAgain(false);
notification["error"]({
message: t("parts_orders.errors.updating", {
message: JSON.stringify(r2.errors),
}),
});
}
}
if (!!r1.errors) {
setLoading(false);
setEnterAgain(false);

View File

@@ -47,6 +47,7 @@ export function BillFormComponent({
billEdit,
disableInvNumber,
job,
loadOutstandingReturns,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -58,6 +59,14 @@ export function BillFormComponent({
);
const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount);
opt &&
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: opt.value,
},
});
};
useEffect(() => {
@@ -65,8 +74,8 @@ export function BillFormComponent({
}, [job, form]);
useEffect(() => {
if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
const vendorId = form.getFieldValue("vendorid");
if (vendorId && vendorAutoCompleteOptions) {
const matchingVendors = vendorAutoCompleteOptions.filter(
(v) => v.id === vendorId
);
@@ -74,10 +83,25 @@ export function BillFormComponent({
setDiscount(matchingVendors[0].discount);
}
}
if (form.getFieldValue("jobid")) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
const jobId = form.getFieldValue("jobid");
if (jobId) {
loadLines({ variables: { id: jobId } });
if (form.getFieldValue("is_credit_memo") && vendorId) {
loadOutstandingReturns({
variables: {
jobId: jobId,
vendorId: vendorId,
},
});
}
}, [form, setDiscount, vendorAutoCompleteOptions, loadLines]);
}
}, [
form,
loadOutstandingReturns,
setDiscount,
vendorAutoCompleteOptions,
loadLines,
]);
return (
<div>
@@ -107,6 +131,13 @@ export function BillFormComponent({
onBlur={() => {
if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
if (form.getFieldValue("vendorid") !== null)
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
}
}}
/>
@@ -228,8 +259,22 @@ export function BillFormComponent({
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
value === true &&
getFieldValue("jobid") &&
getFieldValue("vendorid")
) {
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
}
if (
!bodyshop.bill_allow_post_to_closed &&
job &&
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) &&

View File

@@ -6,6 +6,8 @@ import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component";
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -27,7 +29,11 @@ export function BillFormContainer({
GET_JOB_LINES_TO_ENTER_BILL
);
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
useLazyQuery(QUERY_UNRECEIVED_LINES);
return (
<>
<BillFormComponent
disabled={disabled}
form={form}
@@ -40,7 +46,17 @@ export function BillFormContainer({
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
loadOutstandingReturns={loadOutstandingReturns}
/>
{!billEdit && (
<BillCmdReturnsTableComponent
form={form}
loadOutstandingReturns={loadOutstandingReturns}
returnLoading={returnLoading}
returnData={returnData}
/>
)}
</>
);
}
export default connect(mapStateToProps, null)(BillFormContainer);

View File

@@ -59,7 +59,6 @@ export function BillsListTableComponent({
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
onClick={() => {
console.log(record);
setPartsOrderContext({
actions: {},
context: {

View File

@@ -0,0 +1,66 @@
import { useMutation } from "@apollo/client";
import { Checkbox, notification, Space, Spin } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { MUTATION_UPDATE_PO_CM_REECEIVED } from "../../graphql/parts-orders.queries";
export default function PartsOrderCmReceived({
checked,
orderLineId,
partsOrderId,
}) {
const [updateLine] = useMutation(MUTATION_UPDATE_PO_CM_REECEIVED);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleChange = async (e) => {
setLoading(true);
const result = await updateLine({
variables: {
partsLineId: orderLineId,
partsOrder: { cm_received: e.target.checked },
},
update(cache) {
cache.modify({
id: cache.identify({
id: partsOrderId,
__typename: "parts_orders",
}),
fields: {
parts_order_lines(ex, { readField }) {
console.log(ex);
return ex.map((lineref) => {
if (orderLineId.id !== readField("id", lineref)) {
lineref.cm_received = e.target.checked;
}
return lineref;
});
},
},
});
},
});
if (!!!result.errors) {
notification["success"]({
message: t("parts_orders.successes.line_updated"),
});
} else {
notification["error"]({
message: t("parts_orders.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
return (
<Space>
<Checkbox checked={checked} onChange={handleChange} />
{loading && <Spin size="small" />}
</Space>
);
}

View File

@@ -29,6 +29,7 @@ import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import DataLabel from "../data-label/data-label.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
@@ -346,6 +347,23 @@ export function PartsOrderListTableComponent({
dataIndex: "status",
key: "status",
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cm_received"),
dataIndex: "cm_received",
key: "cm_received",
render: (text, record) => (
<PartsOrderCmReceived
orderLineId={record.id}
checked={record.cm_received}
partsorderid={selectedPartsOrderRecord.id}
/>
),
},
]
: []),
{
title: t("parts_orders.fields.backordered_on"),
dataIndex: "backordered_on",

View File

@@ -305,6 +305,7 @@ export function PartsOrderModalContainer({
quantity: value.part_qty,
job_line_id: isReturn ? value.joblineid : value.id,
part_type: value.part_type,
...(isReturn && { cm_received: false }),
});
return acc;
}, [])

View File

@@ -86,6 +86,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
job_line_id
part_type
cost
cm_received
jobline {
id
part_type

View File

@@ -292,6 +292,22 @@ export const DELETE_PARTS_ORDER = gql`
}
`;
export const MUTATION_UPDATE_PO_CM_REECEIVED = gql`
mutation MUTATION_UPDATE_PO_CM_REECEIVED(
$partsLineId: uuid!
$partsOrder: parts_order_lines_set_input
) {
update_parts_order_lines(
where: { id: { _eq: $partsLineId } }
_set: $partsOrder
) {
returning {
id
cm_received
}
}
}
`;
export const MUTATION_UPDATE_BO_ETA = gql`
mutation MUTATION_UPDATE_BO_ETA(
$partsLineId: uuid!
@@ -339,3 +355,36 @@ export const MUTATION_BACKORDER_PART_LINE = gql`
}
}
`;
export const QUERY_UNRECEIVED_LINES = gql`
query QUERY_UNRECEIVED_LINES($jobId: uuid!, $vendorId: uuid!) {
parts_order_lines(
where: {
parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId } }
cm_received: { _neq: true }
}
) {
cm_received
id
line_desc
quantity
act_price
cost
oem_partno
}
}
`;
export const MUTATION_MARK_RETURN_RECEIVED = gql`
mutation MUTATION_MARK_RETURN_RECEIVED($partsLineIds: [uuid!]!) {
update_parts_order_lines(
where: { id: { _in: $partsLineIds } }
_set: { cm_received: true }
) {
returning {
id
cm_received
}
}
}
`;

View File

@@ -1927,12 +1927,14 @@
"associatedbills": "This parts order cannot",
"backordering": "Error backordering part {{message}}.",
"creating": "Error encountered when creating parts order. ",
"oec": "Error creating EMS files for OEC. {{error}}"
"oec": "Error creating EMS files for OEC. {{error}}",
"saving": "Error saving parts order. {{error}}."
},
"fields": {
"act_price": "Price",
"backordered_eta": "B.O. ETA",
"backordered_on": "B.O. On",
"cm_received": "CM Received?",
"comments": "Comments",
"cost": "Cost",
"db_price": "List Price",
@@ -1955,6 +1957,7 @@
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
"mark_as_received": "Mark as Received?",
"newpartsorder": "New Parts Order",
"notyetordered": "This part has not yet been ordered.",
"oec": "Order via OEC",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Parts order created successfully. ",
"line_updated": "Parts return line updated.",
"received": "Parts order received.",
"return_created": "Parts return created successfully."
}

View File

@@ -1927,12 +1927,14 @@
"associatedbills": "",
"backordering": "",
"creating": "Se encontró un error al crear el pedido de piezas.",
"oec": ""
"oec": "",
"saving": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1955,6 +1957,7 @@
"confirmdelete": "",
"email": "Enviar por correo electrónico",
"inthisorder": "Partes en este pedido",
"mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Pedido de piezas creado con éxito.",
"line_updated": "",
"received": "",
"return_created": ""
}

View File

@@ -1927,12 +1927,14 @@
"associatedbills": "",
"backordering": "",
"creating": "Erreur rencontrée lors de la création de la commande de pièces.",
"oec": ""
"oec": "",
"saving": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1955,6 +1957,7 @@
"confirmdelete": "",
"email": "Envoyé par email",
"inthisorder": "Pièces dans cette commande",
"mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1966,6 +1969,7 @@
},
"successes": {
"created": "Commande de pièces créée avec succès.",
"line_updated": "",
"received": "",
"return_created": ""
}

View File

@@ -3839,6 +3839,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price
@@ -3859,6 +3860,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price
@@ -3890,6 +3892,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."parts_order_lines" add column "cm_received" boolean
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."parts_order_lines" add column "cm_received" boolean
null;

View File

@@ -0,0 +1,5 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE INDEX idx_pol_cm_received ON parts_order_lines(cm_received);
-- CREATE INDEX idx_pol_orderid ON parts_order_lines(orderid);
-- CREATE INDEX idx_parts_order_jobid ON parts_orders(jobid);

View File

@@ -0,0 +1,3 @@
CREATE INDEX idx_pol_cm_received ON parts_order_lines(cm_received);
CREATE INDEX idx_pol_orderid ON parts_order_lines(orderid);
CREATE INDEX idx_parts_order_jobid ON parts_orders(jobid);