IO-586 IO-789 IO-796 IO-778 IO-791 IO-794 IO-777 IO-795

This commit is contained in:
Patrick Fic
2021-03-23 23:26:13 +00:00
32 changed files with 761 additions and 109 deletions

View File

@@ -1338,7 +1338,7 @@
<name>labels</name> <name>labels</name>
<children> <children>
<concept_node> <concept_node>
<name>deductfromlabor</name> <name>deductedfromlbr</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
<description></description> <description></description>
<comment></comment> <comment></comment>
@@ -2160,6 +2160,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>editadjwarning</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> <concept_node>
<name>entered_total</name> <name>entered_total</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -20482,6 +20503,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>removedpartsstrikethrough</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> </children>
</folder_node> </folder_node>
<concept_node> <concept_node>
@@ -24744,6 +24786,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>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> <concept_node>
<name>return_created</name> <name>return_created</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -27368,6 +27431,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>employee</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> <concept_node>
<name>generateasemail</name> <name>generateasemail</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -31727,6 +31811,27 @@
<folder_node> <folder_node>
<name>fields</name> <name>fields</name>
<children> <children>
<concept_node>
<name>active</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> <concept_node>
<name>am</name> <name>am</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -1,5 +1,5 @@
import { useMutation, useQuery } from "@apollo/client"; import { useMutation, useQuery } from "@apollo/client";
import { Button, Form } from "antd"; import { Button, Form, Popconfirm } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
@@ -19,6 +19,7 @@ export default function BillDetailEditcontainer() {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [updateLoading, setUpdateLoading] = useState(false); const [updateLoading, setUpdateLoading] = useState(false);
const [update_bill] = useMutation(UPDATE_BILL); const [update_bill] = useMutation(UPDATE_BILL);
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES); const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
@@ -29,6 +30,15 @@ export default function BillDetailEditcontainer() {
skip: !!!search.billid, skip: !!!search.billid,
}); });
const handleSave = () => {
//It's got a previously deducted bill line!
if (data.bills_by_pk.billlines.filter((b) => b.deductedfromlbr).length > 0)
setVisible(true);
else {
form.submit();
}
};
const handleFinish = async (values) => { const handleFinish = async (values) => {
setUpdateLoading(true); setUpdateLoading(true);
//let adjustmentsToInsert = {}; //let adjustmentsToInsert = {};
@@ -42,7 +52,7 @@ export default function BillDetailEditcontainer() {
); );
billlines.forEach((billline) => { billlines.forEach((billline) => {
const { deductfromlabor, ...il } = billline; const { deductedfromlbr, ...il } = billline;
delete il.__typename; delete il.__typename;
//Need to compare this line to the previous version of the line to see if there is a change in the adjustments. //Need to compare this line to the previous version of the line to see if there is a change in the adjustments.
@@ -52,7 +62,7 @@ export default function BillDetailEditcontainer() {
if (theOldBillLine) { if (theOldBillLine) {
//It was there! Need to change the diff. //It was there! Need to change the diff.
if (theOldBillLine.deductfromlabor !== deductfromlabor) { if (theOldBillLine.deductedfromlbr !== deductedfromlbr) {
//There's a different //There's a different
} }
} }
@@ -64,7 +74,7 @@ export default function BillDetailEditcontainer() {
billLineId: il.id, billLineId: il.id,
billLine: { billLine: {
...il, ...il,
deductedfromlbr: deductfromlabor, deductedfromlbr: deductedfromlbr,
joblineid: il.joblineid === "noline" ? null : il.joblineid, joblineid: il.joblineid === "noline" ? null : il.joblineid,
}, },
}, },
@@ -78,7 +88,7 @@ export default function BillDetailEditcontainer() {
billLines: [ billLines: [
{ {
...il, ...il,
deductedfromlbr: deductfromlabor, deductedfromlbr: deductedfromlbr,
billid: search.billid, billid: search.billid,
joblineid: il.joblineid === "noline" ? null : il.joblineid, joblineid: il.joblineid === "noline" ? null : il.joblineid,
}, },
@@ -93,6 +103,7 @@ export default function BillDetailEditcontainer() {
await refetch(); await refetch();
form.resetFields(); form.resetFields();
form.resetFields(); form.resetFields();
setVisible(false);
setUpdateLoading(false); setUpdateLoading(false);
}; };
@@ -109,19 +120,28 @@ export default function BillDetailEditcontainer() {
return ( return (
<LoadingSkeleton loading={loading}> <LoadingSkeleton loading={loading}>
<Form <Popconfirm
form={form} visible={visible}
onFinish={handleFinish} onConfirm={() => form.submit()}
initialValues={transformData(data)} onCancel={() => setVisible(false)}
okButtonProps={{ loading: updateLoading }}
title={t("bills.labels.editadjwarning")}
> >
<Button <Button
htmlType="submit" htmlType="submit"
disabled={exported} disabled={exported}
onClick={handleSave}
loading={updateLoading} loading={updateLoading}
type="primary" type="primary"
> >
{t("general.actions.save")} {t("general.actions.save")}
</Button> </Button>
</Popconfirm>
<Form
form={form}
onFinish={handleFinish}
initialValues={transformData(data)}
>
<BillFormContainer form={form} billEdit disabled={exported} /> <BillFormContainer form={form} billEdit disabled={exported} />
<JobDocumentsGallery <JobDocumentsGallery
jobId={data ? data.bills_by_pk.jobid : null} jobId={data ? data.bills_by_pk.jobid : null}

View File

@@ -59,20 +59,20 @@ function BillEnterModalContainer({
remainingValues.billlines && remainingValues.billlines &&
remainingValues.billlines.map((i) => { remainingValues.billlines.map((i) => {
const { const {
deductfromlabor, deductedfromlbr,
lbr_adjustment, lbr_adjustment,
location: lineLocation, location: lineLocation,
...restI ...restI
} = i; } = i;
if (deductfromlabor) { if (deductedfromlbr) {
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] = adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) - (adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
restI.actual_price / lbr_adjustment.rate; restI.actual_price / lbr_adjustment.rate;
} }
return { return {
...restI, ...restI,
deductedfromlbr: deductfromlabor, deductedfromlbr: deductedfromlbr,
joblineid: i.joblineid === "noline" ? null : i.joblineid, joblineid: i.joblineid === "noline" ? null : i.joblineid,
}; };
}), }),

View File

@@ -248,19 +248,19 @@ export function BillEnterModalLinesComponent({
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("billlines.labels.deductfromlabor")} label={t("billlines.labels.deductedfromlbr")}
key={`${index}deductfromlabor`} key={`${index}deductedfromlbr`}
valuePropName="checked" valuePropName="checked"
name={[field.name, "deductfromlabor"]} name={[field.name, "deductedfromlbr"]}
> >
<Switch disabled={disabled} /> <Switch disabled={disabled} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
shouldUpdate={(prev, cur) => shouldUpdate={(prev, cur) =>
prev.billlines[index] && prev.billlines[index] &&
prev.billlines[index].deductfromlabor !== prev.billlines[index].deductedfromlbr !==
cur.billlines[index] && cur.billlines[index] &&
cur.billlines[index].deductfromlabor cur.billlines[index].deductedfromlbr
} }
> >
{() => { {() => {
@@ -268,7 +268,7 @@ export function BillEnterModalLinesComponent({
getFieldValue([ getFieldValue([
"billlines", "billlines",
field.name, field.name,
"deductfromlabor", "deductedfromlbr",
]) ])
) )
return ( return (

View File

@@ -176,7 +176,7 @@ export function JobCostingModalComponent({ bodyshop, job }) {
).toFixed(2); ).toFixed(2);
if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0; if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0;
else if (!isFinite(summaryData.gppercent)) else if (!isFinite(summaryData.gppercent))
summaryData.gppercentFormatted = "-∞"; summaryData.gppercentFormatted = "- ∞";
else { else {
summaryData.gppercentFormatted = summaryData.gppercent; summaryData.gppercentFormatted = summaryData.gppercent;
} }

View File

@@ -1,11 +1,15 @@
import { useQuery } from "@apollo/client";
import { Modal } from "antd"; import { Modal } from "antd";
import React from "react"; import React 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 { GET_JOB_RECONCILIATION_BY_PK } from "../../graphql/jobs.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectReconciliation } from "../../redux/modals/modals.selectors"; import { selectReconciliation } from "../../redux/modals/modals.selectors";
import JobReconciliationModalComponent from "./job-reconciliation-modal.component"; import JobReconciliationModalComponent from "./job-reconciliation-modal.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
reconciliationModal: selectReconciliation, reconciliationModal: selectReconciliation,
@@ -20,7 +24,12 @@ function JobReconciliationModalContainer({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { context, visible } = reconciliationModal; const { context, visible } = reconciliationModal;
const { job, bills } = context; const { job } = context;
const { loading, error, data } = useQuery(GET_JOB_RECONCILIATION_BY_PK, {
variables: { id: job && job.id },
skip: !(job && job.id) || !visible,
});
const handleCancel = () => { const handleCancel = () => {
toggleModalVisible(); toggleModalVisible();
@@ -37,7 +46,15 @@ function JobReconciliationModalContainer({
cancelButtonProps={{ display: "none" }} cancelButtonProps={{ display: "none" }}
destroyOnClose destroyOnClose
> >
<JobReconciliationModalComponent job={job} bills={bills} /> <LoadingSpinner loading={loading}>
{error && <AlertComponent message={error.message} type="error" />}
{data && (
<JobReconciliationModalComponent
job={data && data.jobs_by_pk}
bills={data && data.bills}
/>
)}
</LoadingSpinner>
</Modal> </Modal>
); );
} }

View File

@@ -3,6 +3,7 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import "./job-reconciliation-parts-table.styles.scss";
export default function JobReconcilitionPartsTable({ export default function JobReconcilitionPartsTable({
jobLineState, jobLineState,
@@ -114,7 +115,11 @@ export default function JobReconcilitionPartsTable({
onChange: handleOnRowClick, onChange: handleOnRowClick,
selectedRowKeys: selectedLines, selectedRowKeys: selectedLines,
}} }}
rowClassName={(record) => record.removed && "text-strikethrough"}
/> />
<div style={{ fontStyle: "italic", margin: "4px" }}>
{t("jobs.labels.reconciliation.removedpartsstrikethrough")}
</div>
</div> </div>
); );
} }

View File

@@ -0,0 +1,3 @@
.text-strikethrough {
text-decoration: line-through;
}

View File

@@ -39,7 +39,9 @@ export default function JobReconciliationTotals({
return acc.add( return acc.add(
Dinero({ Dinero({
amount: Math.round((val.actual_price || 0) * 100), amount: Math.round((val.actual_price || 0) * 100),
}).multiply(val.quantity || 1) })
.multiply(val.quantity || 1)
.multiply(val.bill.is_credit_memo ? -1 : 1)
); );
}, Dinero()), }, Dinero()),
}; };
@@ -97,6 +99,7 @@ export default function JobReconciliationTotals({
onClick={() => { onClick={() => {
jobLineState[1]([]); jobLineState[1]([]);
billLineState[1]([]); billLineState[1]([]);
setErrors([]);
}} }}
> >
{t("jobs.labels.reconciliation.clear")} {t("jobs.labels.reconciliation.clear")}

View File

@@ -23,11 +23,6 @@ export const reconcileByAssocLine = (
setErrors((errors) => [ setErrors((errors) => [
...errors, ...errors,
..._.uniqBy(duplicatedJobLinesbyInvoiceId).map((dupedId) => { ..._.uniqBy(duplicatedJobLinesbyInvoiceId).map((dupedId) => {
console.log(
"dupedId",
dupedId,
billLines.find((b) => b.id === dupedId)
);
return i18next.t("jobs.labels.reconciliation.multiplebilllines", { return i18next.t("jobs.labels.reconciliation.multiplebilllines", {
line_desc: jobLines.find((j) => j.id === dupedId)?.line_desc, line_desc: jobLines.find((j) => j.id === dupedId)?.line_desc,
}); });

View File

@@ -14,6 +14,11 @@ import { TemplateList } from "../../utils/TemplateConstants";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import parsePhoneNumber from "libphonenumber-js";
import {
openChatByPhone,
setMessage,
} from "../../redux/messaging/messaging.actions";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser' //currentUser: selectCurrentUser'
@@ -21,12 +26,16 @@ const mapStateToProps = createStructuredSelector({
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)), setEmailOptions: (e) => dispatch(setEmailOptions(e)),
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
}); });
export function JobsDetailHeaderCsi({ export function JobsDetailHeaderCsi({
setEmailOptions, setEmailOptions,
bodyshop, bodyshop,
job, job,
openChatByPhone,
setMessage,
...props ...props
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -36,52 +45,90 @@ export function JobsDetailHeaderCsi({
const handleCreateCsi = async (e) => { const handleCreateCsi = async (e) => {
logImEXEvent("job_create_csi"); logImEXEvent("job_create_csi");
const questionSetResult = await client.query({ //Is tehre already a CSI?
query: GET_CURRENT_QUESTIONSET_ID, if (job.csi_invites.length === 0) {
}); const questionSetResult = await client.query({
query: GET_CURRENT_QUESTIONSET_ID,
});
if (questionSetResult.data.csiquestions.length > 0) { if (questionSetResult.data.csiquestions.length > 0) {
const result = await insertCsi({ const result = await insertCsi({
variables: { variables: {
csiInput: { csiInput: {
jobid: job.id, jobid: job.id,
bodyshopid: bodyshop.id, bodyshopid: bodyshop.id,
questionset: questionSetResult.data.csiquestions[0].id, questionset: questionSetResult.data.csiquestions[0].id,
relateddata: { relateddata: {
job: { job: {
id: job.id, id: job.id,
ownr_fn: job.ownr_fn, ownr_fn: job.ownr_fn,
ro_number: job.ro_number, ro_number: job.ro_number,
v_model_yr: job.v_model_yr, v_model_yr: job.v_model_yr,
v_make_desc: job.v_make_desc, v_make_desc: job.v_make_desc,
v_model_desc: job.v_model_desc, v_model_desc: job.v_model_desc,
}, },
bodyshop: { bodyshop: {
city: bodyshop.city, city: bodyshop.city,
email: bodyshop.email, email: bodyshop.email,
state: bodyshop.state, state: bodyshop.state,
country: bodyshop.country, country: bodyshop.country,
address1: bodyshop.address1, address1: bodyshop.address1,
address2: bodyshop.address2, address2: bodyshop.address2,
shopname: bodyshop.shopname, shopname: bodyshop.shopname,
zip_post: bodyshop.zip_post, zip_post: bodyshop.zip_post,
logo_img_path: bodyshop.logo_img_path, logo_img_path: bodyshop.logo_img_path,
},
}, },
}, },
}, },
}, });
});
if (!!!result.errors) { if (!!!result.errors) {
notification["success"]({ message: t("csi.successes.created") }); notification["success"]({ message: t("csi.successes.created") });
} else {
notification["error"]({
message: t("csi.errors.creating", {
message: JSON.stringify(result.errors),
}),
});
return;
}
if (e.key === "email")
setEmailOptions({
messageOptions: {
to: [job.ownr_ea],
replyTo: bodyshop.email,
},
template: {
name: TemplateList("job").csi_invitation.key,
variables: {
id: result.data.insert_csi.returning[0].id,
},
},
});
if (e.key === "text") {
const p = parsePhoneNumber(job.ownr_ph1, "CA");
if (p && p.isValid()) {
// openChatByPhone({
// phone_num: p.formatInternational(),
// jobid: job.id,
// });
setMessage(
`${window.location.protocol}//${window.location.host}/csi/${result.data.insert_csi.returning[0].id}`
);
} else {
notification["error"]({
message: t("messaging.error.invalidphone"),
});
}
}
} else { } else {
notification["error"]({ notification["error"]({
message: t("csi.errors.creating", { message: t("csi.errors.notconfigured"),
message: JSON.stringify(result.errors),
}),
}); });
return;
} }
} else {
if (e.key === "email") if (e.key === "email")
setEmailOptions({ setEmailOptions({
messageOptions: { messageOptions: {
@@ -91,17 +138,27 @@ export function JobsDetailHeaderCsi({
template: { template: {
name: TemplateList("job").csi_invitation.key, name: TemplateList("job").csi_invitation.key,
variables: { variables: {
id: result.data.insert_csi.returning[0].id, id: job.csi_invites[0].id,
}, },
}, },
}); });
if (e.key === "text") { if (e.key === "text") {
const p = parsePhoneNumber(job.ownr_ph1, "CA");
if (p && p.isValid()) {
// openChatByPhone({
// phone_num: p.formatInternational(),
// jobid: job.id,
// });
setMessage(
`${window.location.protocol}//${window.location.host}/csi/${job.csi_invites[0].id}`
);
} else {
notification["error"]({
message: t("messaging.error.invalidphone"),
});
}
} }
} else {
notification["error"]({
message: t("csi.errors.notconfigured"),
});
} }
}; };
@@ -122,13 +179,26 @@ export function JobsDetailHeaderCsi({
{t("general.labels.text")} {t("general.labels.text")}
</Menu.Item> </Menu.Item>
<Menu.Divider /> <Menu.Divider />
{job.csiinvites.map((item, idx) => ( {job.csiinvites.map((item, idx) => {
<Menu.Item key={idx}> return item.completedon ? (
<Link to={`/manage/shop/csi?responseid=${item.id}`}> <Menu.Item key={idx}>
<DateTimeFormatter>{item.completedon}</DateTimeFormatter> <Link to={`/manage/shop/csi?responseid=${item.id}`}>
</Link> <DateTimeFormatter>{item.completedon}</DateTimeFormatter>
</Menu.Item> </Link>
))} </Menu.Item>
) : (
<Menu.Item
key={idx}
onClick={() => {
navigator.clipboard.writeText(
`${window.location.protocol}//${window.location.host}/csi/${item.id}`
);
}}
>
{t("general.actions.copylink")}
</Menu.Item>
);
})}
</Menu.SubMenu> </Menu.SubMenu>
); );
} }

View File

@@ -136,6 +136,46 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true }) {
<InputNumber min={0} max={1} precision={2} disabled={jobRO} /> <InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAG", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
name={["parts_tax_rates", "PAG", "prt_mktyp"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
name={["parts_tax_rates", "PAG", "prt_tax_in"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "PAG", "prt_tax_rt"]}
rules={[
{
required: required,
message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}> <LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
<Form.Item <Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")} label={t("jobs.fields.parts_tax_rates.prt_discp")}

View File

@@ -68,9 +68,7 @@ export function PartsReceiveModalContainer({
}); });
notification["success"]({ notification["success"]({
message: values.isReturn message: t("parts_orders.successes.received"),
? t("parts_orders.successes.return_created")
: t("parts_orders.successes.created"),
}); });
if (refetch) refetch(); if (refetch) refetch();

View File

@@ -1,7 +1,7 @@
import { useLazyQuery } from "@apollo/client"; import { useLazyQuery } from "@apollo/client";
import { Button, DatePicker, Form, Select, Switch } from "antd"; import { Button, DatePicker, Form, Select, Switch } from "antd";
import moment from "moment"; import moment from "moment";
import React from "react"; import React, { 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";
@@ -11,6 +11,8 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import DatePIckerRanges from "../../utils/DatePickerRanges"; import DatePIckerRanges from "../../utils/DatePickerRanges";
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter, reportCenterModal: selectReportCenter,
@@ -25,11 +27,15 @@ export default connect(
export function ReportCenterModalComponent({ reportCenterModal }) { export function ReportCenterModalComponent({ reportCenterModal }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
const Templates = TemplateList("report_center"); const Templates = TemplateList("report_center");
const { visible } = reportCenterModal; const { visible } = reportCenterModal;
const [callVendorQuery, { data, called }] = useLazyQuery(QUERY_ALL_VENDORS, { const [
callVendorQuery,
{ data: vendorData, called: vendorCalled },
] = useLazyQuery(QUERY_ALL_VENDORS, {
skip: !( skip: !(
visible && visible &&
Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")] &&
@@ -37,12 +43,24 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
), ),
}); });
const handleFinish = (values) => { const [
callEmployeeQuery,
{ data: employeeData, called: employeeCalled },
] = useLazyQuery(QUERY_ACTIVE_EMPLOYEES, {
skip: !(
visible &&
Templates[form.getFieldValue("key")] &&
Templates[form.getFieldValue("key")].idtype
),
});
const handleFinish = async (values) => {
setLoading(true);
const start = values.dates[0]; const start = values.dates[0];
const end = values.dates[1]; const end = values.dates[1];
const { id } = values; const { id } = values;
console.log("values", values); console.log("values", values);
GenerateDocument( await GenerateDocument(
{ {
name: values.key, name: values.key,
variables: { variables: {
@@ -56,6 +74,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
}, },
values.email ? "e" : "p" values.email ? "e" : "p"
); );
setLoading(false);
}; };
return ( return (
@@ -89,7 +108,8 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
//Kind of Id //Kind of Id
const idtype = Templates[key] && Templates[key].idtype; const idtype = Templates[key] && Templates[key].idtype;
if (!idtype) return null; if (!idtype) return null;
if (!called && idtype === "vendor") callVendorQuery(); if (!vendorCalled && idtype === "vendor") callVendorQuery();
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
if (idtype === "vendor") if (idtype === "vendor")
return ( return (
<Form.Item <Form.Item
@@ -102,7 +122,26 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
}, },
]} ]}
> >
<VendorSearchSelect options={data ? data.vendors : []} /> <VendorSearchSelect
options={vendorData ? vendorData.vendors : []}
/>
</Form.Item>
);
if (idtype === "employee")
return (
<Form.Item
name="id"
label={t("reportcenter.labels.employee")}
rules={[
{
required: true,
message: t("general.validation.required"),
},
]}
>
<EmployeeSearchSelect
options={employeeData ? employeeData.employees : []}
/>
</Form.Item> </Form.Item>
); );
else return null; else return null;
@@ -138,7 +177,7 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
marginTop: "1rem", marginTop: "1rem",
}} }}
> >
<Button onClick={() => form.submit()} style={{}}> <Button onClick={() => form.submit()} style={{}} loading={loading}>
{t("reportcenter.actions.generate")} {t("reportcenter.actions.generate")}
</Button> </Button>
</div> </div>

View File

@@ -68,7 +68,7 @@ export function TimeTicketsSummaryEmployees({
const handlePrintEmployeeTicket = async (empId) => { const handlePrintEmployeeTicket = async (empId) => {
GenerateDocument( GenerateDocument(
{ {
name: TemplateList().time_tickets_by_employee.key, name: TemplateList().timetickets_employee.key,
variables: { id: empId, start: startDate, end: endDate }, variables: { id: empId, start: startDate, end: endDate },
}, },
{}, {},

View File

@@ -6,6 +6,7 @@ import {
InputNumber, InputNumber,
Select, Select,
Space, Space,
Switch,
Typography, Typography,
} from "antd"; } from "antd";
import React from "react"; import React from "react";
@@ -49,6 +50,14 @@ export default function VendorsFormComponent({
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("vendors.fields.active")}
name="active"
initialValue={true}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item <Form.Item
label={t("vendors.fields.email")} label={t("vendors.fields.email")}
rules={[ rules={[

View File

@@ -166,6 +166,7 @@ export const QUERY_BILL_BY_PK = gql`
quantity quantity
joblineid joblineid
applicable_taxes applicable_taxes
deductedfromlbr
} }
documents { documents {
id id

View File

@@ -580,7 +580,66 @@ export const GET_JOB_BY_PK = gql`
} }
} }
`; `;
export const GET_JOB_RECONCILIATION_BY_PK = gql`
query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) {
bills(where: { jobid: { _eq: $id } }) {
id
vendorid
vendor {
id
name
}
total
invoice_number
date
federal_tax_rate
state_tax_rate
local_tax_rate
is_credit_memo
isinhouse
exported
billlines {
actual_price
quantity
actual_cost
cost_center
id
joblineid
line_desc
applicable_taxes
deductedfromlbr
}
}
jobs_by_pk(id: $id) {
id
joblines(order_by: { line_no: asc }) {
id
removed
line_no
unq_seq
line_ind
line_desc
part_type
oem_partno
db_price
act_price
part_qty
mod_lbr_ty
db_hrs
mod_lb_hrs
lbr_op
lbr_amt
op_code_desc
status
notes
location
tax_part
db_ref
manual_line
}
}
}
`;
export const QUERY_JOB_CARD_DETAILS = gql` export const QUERY_JOB_CARD_DETAILS = gql`
query QUERY_JOB_CARD_DETAILS($id: uuid!) { query QUERY_JOB_CARD_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) { jobs_by_pk(id: $id) {

View File

@@ -16,6 +16,7 @@ export const QUERY_VENDOR_BY_ID = gql`
cost_center cost_center
city city
street1 street1
active
phone phone
} }
} }
@@ -39,6 +40,7 @@ export const QUERY_ALL_VENDORS = gql`
cost_center cost_center
city city
phone phone
active
} }
} }
`; `;
@@ -65,13 +67,14 @@ export const DELETE_VENDOR = gql`
export const QUERY_ALL_VENDORS_FOR_ORDER = gql` export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
query QUERY_ALL_VENDORS_FOR_ORDER($jobId: uuid) { query QUERY_ALL_VENDORS_FOR_ORDER($jobId: uuid) {
vendors(order_by: { name: asc }) { vendors(order_by: { name: asc }, where: { active: { _eq: true } }) {
name name
cost_center cost_center
id id
favorite favorite
discount discount
email email
active
} }
jobs(where: { id: { _eq: $jobId } }) { jobs(where: { id: { _eq: $jobId } }) {
v_make_desc v_make_desc
@@ -81,18 +84,19 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
export const SEARCH_VENDOR_AUTOCOMPLETE = gql` export const SEARCH_VENDOR_AUTOCOMPLETE = gql`
query SEARCH_VENDOR_AUTOCOMPLETE { query SEARCH_VENDOR_AUTOCOMPLETE {
vendors(order_by: { name: asc }) { vendors(order_by: { name: asc }, where: { active: { _eq: true } }) {
name name
discount discount
id id
cost_center cost_center
active
} }
} }
`; `;
export const SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR = gql` export const SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR = gql`
query SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR { query SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR {
vendors(order_by: { name: asc }) { vendors(order_by: { name: asc }, where: { active: { _eq: true } }) {
name name
discount discount
id id
@@ -104,6 +108,7 @@ export const SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR = gql`
city city
email email
state state
active
} }
} }
`; `;

View File

@@ -98,7 +98,7 @@
"state_tax_applicable": "St. Tax?" "state_tax_applicable": "St. Tax?"
}, },
"labels": { "labels": {
"deductfromlabor": "Deduct from Labor?", "deductedfromlbr": "Deduct from Labor?",
"entered": "Entered", "entered": "Entered",
"from": "From", "from": "From",
"other": "-- Not On Estimate --", "other": "-- Not On Estimate --",
@@ -142,10 +142,11 @@
"billcmtotal": "Retail Total of Credit Memos", "billcmtotal": "Retail Total of Credit Memos",
"bills": "Bills", "bills": "Bills",
"dedfromlbr": "Deducted from Labor", "dedfromlbr": "Deducted from Labor",
"deleteconfirm": "Are you sure you want to delete this bill? It cannot be undone.", "deleteconfirm": "Are you sure you want to delete this bill? It cannot be undone. If this bill has deductions from labors, manual changes may be required.",
"discrepancy": "Discrepancy", "discrepancy": "Discrepancy",
"discrepwithcms": "Discrepancy including Credit Memos", "discrepwithcms": "Discrepancy including Credit Memos",
"discrepwithlbradj": "Discrepancy including Lbr. Adj.", "discrepwithlbradj": "Discrepancy including Lbr. Adj.",
"editadjwarning": "This bill had lines which resulted in labor adjustments. Manual correction to adjustments may be required.",
"entered_total": "Total of Entered Lines", "entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.", "enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax", "federal_tax": "Federal Tax",
@@ -1228,7 +1229,8 @@
"joblinestotal": "Job Lines Total", "joblinestotal": "Job Lines Total",
"multipleactprices": "${{act_price}} is the price for multiple job lines.", "multipleactprices": "${{act_price}} is the price for multiple job lines.",
"multiplebilllines": "{{line_desc}} has 2 or more bill lines associated to it.", "multiplebilllines": "{{line_desc}} has 2 or more bill lines associated to it.",
"multiplebillsforactprice": "Found more than 1 bill matching ${{act_price}} retail price." "multiplebillsforactprice": "Found more than 1 bill matching ${{act_price}} retail price.",
"removedpartsstrikethrough": "Strike through lines represent parts that have been removed from the estimate. They are included for completeness of reconciliation."
}, },
"reconciliationheader": "Parts & Sublet Reconciliation", "reconciliationheader": "Parts & Sublet Reconciliation",
"rosaletotal": "Total RO Sale", "rosaletotal": "Total RO Sale",
@@ -1491,6 +1493,7 @@
}, },
"successes": { "successes": {
"created": "Parts order created successfully. ", "created": "Parts order created successfully. ",
"received": "Parts order received.",
"return_created": "Parts return created successfully." "return_created": "Parts return created successfully."
} }
}, },
@@ -1660,6 +1663,7 @@
}, },
"labels": { "labels": {
"dates": "Dates", "dates": "Dates",
"employee": "Employee",
"generateasemail": "Generate as Email?", "generateasemail": "Generate as Email?",
"key": "Report", "key": "Report",
"vendor": "Vendor" "vendor": "Vendor"
@@ -1931,6 +1935,7 @@
"saving": "Error encountered while saving vendor. " "saving": "Error encountered while saving vendor. "
}, },
"fields": { "fields": {
"active": "Active",
"am": "Aftermarket", "am": "Aftermarket",
"city": "City", "city": "City",
"cost_center": "Cost Center", "cost_center": "Cost Center",

View File

@@ -98,7 +98,7 @@
"state_tax_applicable": "" "state_tax_applicable": ""
}, },
"labels": { "labels": {
"deductfromlabor": "", "deductedfromlbr": "",
"entered": "", "entered": "",
"from": "", "from": "",
"other": "", "other": "",
@@ -146,6 +146,7 @@
"discrepancy": "", "discrepancy": "",
"discrepwithcms": "", "discrepwithcms": "",
"discrepwithlbradj": "", "discrepwithlbradj": "",
"editadjwarning": "",
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
@@ -1228,7 +1229,8 @@
"joblinestotal": "", "joblinestotal": "",
"multipleactprices": "", "multipleactprices": "",
"multiplebilllines": "", "multiplebilllines": "",
"multiplebillsforactprice": "" "multiplebillsforactprice": "",
"removedpartsstrikethrough": ""
}, },
"reconciliationheader": "", "reconciliationheader": "",
"rosaletotal": "", "rosaletotal": "",
@@ -1491,6 +1493,7 @@
}, },
"successes": { "successes": {
"created": "Pedido de piezas creado con éxito.", "created": "Pedido de piezas creado con éxito.",
"received": "",
"return_created": "" "return_created": ""
} }
}, },
@@ -1660,6 +1663,7 @@
}, },
"labels": { "labels": {
"dates": "", "dates": "",
"employee": "",
"generateasemail": "", "generateasemail": "",
"key": "", "key": "",
"vendor": "" "vendor": ""
@@ -1931,6 +1935,7 @@
"saving": "Se encontró un error al guardar el proveedor." "saving": "Se encontró un error al guardar el proveedor."
}, },
"fields": { "fields": {
"active": "",
"am": "", "am": "",
"city": "ciudad", "city": "ciudad",
"cost_center": "Centro de costos", "cost_center": "Centro de costos",

View File

@@ -98,7 +98,7 @@
"state_tax_applicable": "" "state_tax_applicable": ""
}, },
"labels": { "labels": {
"deductfromlabor": "", "deductedfromlbr": "",
"entered": "", "entered": "",
"from": "", "from": "",
"other": "", "other": "",
@@ -146,6 +146,7 @@
"discrepancy": "", "discrepancy": "",
"discrepwithcms": "", "discrepwithcms": "",
"discrepwithlbradj": "", "discrepwithlbradj": "",
"editadjwarning": "",
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
@@ -1228,7 +1229,8 @@
"joblinestotal": "", "joblinestotal": "",
"multipleactprices": "", "multipleactprices": "",
"multiplebilllines": "", "multiplebilllines": "",
"multiplebillsforactprice": "" "multiplebillsforactprice": "",
"removedpartsstrikethrough": ""
}, },
"reconciliationheader": "", "reconciliationheader": "",
"rosaletotal": "", "rosaletotal": "",
@@ -1491,6 +1493,7 @@
}, },
"successes": { "successes": {
"created": "Commande de pièces créée avec succès.", "created": "Commande de pièces créée avec succès.",
"received": "",
"return_created": "" "return_created": ""
} }
}, },
@@ -1660,6 +1663,7 @@
}, },
"labels": { "labels": {
"dates": "", "dates": "",
"employee": "",
"generateasemail": "", "generateasemail": "",
"key": "", "key": "",
"vendor": "" "vendor": ""
@@ -1931,6 +1935,7 @@
"saving": "Erreur rencontrée lors de l'enregistrement du fournisseur." "saving": "Erreur rencontrée lors de l'enregistrement du fournisseur."
}, },
"fields": { "fields": {
"active": "",
"am": "", "am": "",
"city": "Ville", "city": "Ville",
"cost_center": "Centre de coûts", "cost_center": "Centre de coûts",

View File

@@ -326,13 +326,7 @@ export const TemplateList = (type, context) => {
key: "schedule", key: "schedule",
disabled: false, disabled: false,
}, },
timetickets: {
title: i18n.t("reportcenter.templates.timetickets"),
description: "Est Detail",
subject: i18n.t("reportcenter.templates.timetickets"),
key: "timetickets",
disabled: false,
},
purchases_by_vendor_detailed_date_range: { purchases_by_vendor_detailed_date_range: {
title: i18n.t( title: i18n.t(
"reportcenter.templates.purchases_by_vendor_detailed_date_range" "reportcenter.templates.purchases_by_vendor_detailed_date_range"
@@ -357,12 +351,19 @@ export const TemplateList = (type, context) => {
idtype: "vendor", idtype: "vendor",
disabled: false, disabled: false,
}, },
timetickets: {
title: i18n.t("reportcenter.templates.timetickets"),
description: "Est Detail",
subject: i18n.t("reportcenter.templates.timetickets"),
key: "timetickets",
disabled: false,
},
timetickets_employee: { timetickets_employee: {
title: i18n.t("reportcenter.templates.timetickets_employee"), title: i18n.t("reportcenter.templates.timetickets_employee"),
description: "Est Detail", description: "Est Detail",
subject: i18n.t("reportcenter.templates.timetickets_employee"), subject: i18n.t("reportcenter.templates.timetickets_employee"),
key: "timetickets_employee", key: "timetickets_employee",
idtype: "vendor", idtype: "employee",
disabled: false, disabled: false,
}, },
timetickets_summary: { timetickets_summary: {
@@ -370,7 +371,7 @@ export const TemplateList = (type, context) => {
description: "Est Detail", description: "Est Detail",
subject: i18n.t("reportcenter.templates.timetickets_summary"), subject: i18n.t("reportcenter.templates.timetickets_summary"),
key: "timetickets_summary", key: "timetickets_summary",
idtype: "vendor", //idtype: "vendor",
disabled: false, disabled: false,
}, },
} }

View File

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

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."vendors" ADD COLUMN "active" boolean NOT NULL DEFAULT
true;
type: run_sql

View File

@@ -0,0 +1,41 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
set: {}
role: user
table:
name: vendors
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,42 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- active
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
set: {}
role: user
table:
name: vendors
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,42 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: vendors
schema: public
type: create_select_permission

View File

@@ -0,0 +1,43 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- active
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: vendors
schema: public
type: create_select_permission

View File

@@ -0,0 +1,41 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_update_permission
- args:
permission:
columns:
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: vendors
schema: public
type: create_update_permission

View File

@@ -0,0 +1,42 @@
- args:
role: user
table:
name: vendors
schema: public
type: drop_update_permission
- args:
permission:
columns:
- active
- bodyshopid
- city
- cost_center
- country
- created_at
- discount
- due_date
- email
- favorite
- id
- name
- phone
- state
- street1
- street2
- updated_at
- zip
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: vendors
schema: public
type: create_update_permission

View File

@@ -1743,13 +1743,15 @@ tables:
- active: - active:
_eq: true _eq: true
columns: columns:
- jobid
- conversationid - conversationid
- id
- jobid
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- conversationid - conversationid
- id
- jobid - jobid
filter: filter:
conversation: conversation:
@@ -4114,6 +4116,7 @@ tables:
- active: - active:
_eq: true _eq: true
columns: columns:
- active
- bodyshopid - bodyshopid
- city - city
- cost_center - cost_center
@@ -4135,6 +4138,7 @@ tables:
- role: user - role: user
permission: permission:
columns: columns:
- active
- bodyshopid - bodyshopid
- city - city
- cost_center - cost_center
@@ -4165,6 +4169,7 @@ tables:
- role: user - role: user
permission: permission:
columns: columns:
- active
- bodyshopid - bodyshopid
- city - city
- cost_center - cost_center