Merge branch 'release/2024-03-22' into master-AIO

This commit is contained in:
Patrick Fic
2024-03-21 15:16:08 -07:00
13 changed files with 753 additions and 571 deletions

View File

@@ -1,5 +1,5 @@
import {useMutation, useQuery} from "@apollo/client";
import {Button, Form, Popconfirm, Space} from "antd";
import {Button, Divider, Form, Popconfirm, Space} from "antd";
import dayjs from "../../utils/day";
import queryString from "query-string";
import React, {useState} from "react";
@@ -203,7 +203,7 @@ export function BillDetailEditcontainer({setPartsOrderContext, insertAuditTrail,
layout="vertical"
>
<BillFormContainer form={form} billEdit disabled={exported}/>
<Divider orientation="left">{t("general.labels.media")}</Divider>
{bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery
job={{id: data ? data.bills_by_pk.jobid : null}}

View File

@@ -173,7 +173,11 @@ export function BillDetailEditReturn({
</Form>
</Modal>
<Button
disabled={data.bills_by_pk.is_credit_memo || disabled}
disabled={
data.bills_by_pk.is_credit_memo ||
data.bills_by_pk.isinhouse ||
disabled
}
onClick={() => {
setOpen(true);
}}

View File

@@ -1,27 +1,27 @@
import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload, } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdOpenInNew } from "react-icons/md";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import AlertComponent from "../alert/alert.component";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobSearchSelect from "../job-search-select/job-search-select.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import BillFormLines from "./bill-form.lines.component";
import { CalculateBillTotal } from "./bill-form.totals.utility";
import Icon, { UploadOutlined } from '@ant-design/icons';
import { useApolloClient } from '@apollo/client';
import { useSplitTreatments } from '@splitsoftware/splitio-react';
import { Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload } from 'antd';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MdOpenInNew } from 'react-icons/md';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { createStructuredSelector } from 'reselect';
import { CHECK_BILL_INVOICE_NUMBER } from '../../graphql/bills.queries';
import { selectBodyshop } from '../../redux/user/user.selectors';
import dayjs from '../../utils/day';
import InstanceRenderManager from '../../utils/instanceRenderMgr';
import AlertComponent from '../alert/alert.component';
import BillFormLinesExtended from '../bill-form-lines-extended/bill-form-lines-extended.component';
import FormDatePicker from '../form-date-picker/form-date-picker.component';
import FormFieldsChanged from '../form-fields-changed-alert/form-fields-changed-alert.component';
import CurrencyInput from '../form-items-formatted/currency-form-item.component';
import JobSearchSelect from '../job-search-select/job-search-select.component';
import LayoutFormRow from '../layout-form-row/layout-form-row.component';
import VendorSearchSelect from '../vendor-search-select/vendor-search-select.component';
import BillFormLines from './bill-form.lines.component';
import { CalculateBillTotal } from './bill-form.totals.utility';
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -41,20 +41,20 @@ export function BillFormComponent({
job,
loadOutstandingReturns,
loadInventory,
preferredMake
}) {
const {t} = useTranslation();
preferredMake,
}) {
const { t } = useTranslation();
const client = useApolloClient();
const [discount, setDiscount] = useState(0);
const {treatments: {Extended_Bill_Posting, ClosingPeriod}} = useSplitTreatments({
const {
treatments: { Extended_Bill_Posting, ClosingPeriod },
} = useSplitTreatments({
attributes: {},
names: ["Extended_Bill_Posting", "ClosingPeriod"],
names: ['Extended_Bill_Posting', 'ClosingPeriod'],
splitKey: bodyshop.imexshopid,
});
const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount);
@@ -62,7 +62,7 @@ export function BillFormComponent({
!billEdit &&
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
jobId: form.getFieldValue('jobid'),
vendorId: opt.value,
},
});
@@ -71,7 +71,7 @@ export function BillFormComponent({
const handleFederalTaxExemptSwitchToggle = (checked) => {
// Early gate
if (!checked) return;
const values = form.getFieldsValue("billlines");
const values = form.getFieldsValue('billlines');
// Gate bill lines
if (!values?.billlines?.length) return;
@@ -83,23 +83,21 @@ export function BillFormComponent({
};
useEffect(() => {
if (job) form.validateFields(["is_credit_memo"]);
if (job) form.validateFields(['is_credit_memo']);
}, [job, form]);
useEffect(() => {
const vendorId = form.getFieldValue("vendorid");
const vendorId = form.getFieldValue('vendorid');
if (vendorId && vendorAutoCompleteOptions) {
const matchingVendors = vendorAutoCompleteOptions.filter(
(v) => v.id === vendorId
);
const matchingVendors = vendorAutoCompleteOptions.filter((v) => v.id === vendorId);
if (matchingVendors.length === 1) {
setDiscount(matchingVendors[0].discount);
}
}
const jobId = form.getFieldValue("jobid");
const jobId = form.getFieldValue('jobid');
if (jobId) {
loadLines({variables: {id: jobId}});
if (form.getFieldValue("is_credit_memo") && vendorId && !billEdit) {
loadLines({ variables: { id: jobId } });
if (form.getFieldValue('is_credit_memo') && vendorId && !billEdit) {
loadOutstandingReturns({
variables: {
jobId: jobId,
@@ -125,18 +123,14 @@ export function BillFormComponent({
return (
<div>
<FormFieldsChanged form={form}/>
<Form.Item
style={{display: "none"}}
name="isinhouse"
valuePropName="checked"
>
<Switch/>
<FormFieldsChanged form={form} />
<Form.Item style={{ display: 'none' }} name="isinhouse" valuePropName="checked">
<Switch />
</Form.Item>
<LayoutFormRow grow>
<Form.Item
name="jobid"
label={t("bills.fields.ro_number")}
label={t('bills.fields.ro_number')}
rules={[
{
required: true,
@@ -149,13 +143,19 @@ export function BillFormComponent({
convertedOnly
notExported={false}
onBlur={() => {
if (form.getFieldValue("jobid") !== null && form.getFieldValue("jobid") !== undefined) {
loadLines({variables: {id: form.getFieldValue("jobid")}});
if (form.getFieldValue("vendorid") !== null && form.getFieldValue("vendorid") !== undefined) {
if (
form.getFieldValue('jobid') !== null &&
form.getFieldValue('jobid') !== undefined
) {
loadLines({ variables: { id: form.getFieldValue('jobid') } });
if (
form.getFieldValue('vendorid') !== null &&
form.getFieldValue('vendorid') !== undefined
) {
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
jobId: form.getFieldValue('jobid'),
vendorId: form.getFieldValue('vendorid'),
},
});
}
@@ -164,7 +164,7 @@ export function BillFormComponent({
/>
</Form.Item>
<Form.Item
label={t("bills.fields.vendor")}
label={t('bills.fields.vendor')}
name="vendorid"
// style={{ display: billEdit ? "none" : null }}
rules={[
@@ -172,14 +172,10 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({getFieldValue}) => ({
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
!getFieldValue(["isinhouse"]) &&
value === bodyshop.inhousevendorid
) {
return Promise.reject(t("bills.validation.manualinhouse"));
if (value && !getFieldValue(['isinhouse']) && value === bodyshop.inhousevendorid) {
return Promise.reject(t('bills.validation.manualinhouse'));
}
return Promise.resolve();
},
@@ -203,7 +199,7 @@ export function BillFormComponent({
type="warning"
message={
<Space>
{t("bills.labels.iouexists")}
{t('bills.labels.iouexists')}
<Link
target="_blank"
rel="noopener noreferrer"
@@ -211,7 +207,7 @@ export function BillFormComponent({
>
<Space>
{iou.ro_number}
<Icon component={MdOpenInNew}/>
<Icon component={MdOpenInNew} />
</Space>
</Link>
</Space>
@@ -220,7 +216,7 @@ export function BillFormComponent({
))}
<LayoutFormRow>
<Form.Item
label={t("bills.fields.invoice_number")}
label={t('bills.fields.invoice_number')}
name="invoice_number"
validateTrigger="onBlur"
hasFeedback
@@ -229,9 +225,9 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({getFieldValue}) => ({
({ getFieldValue }) => ({
async validator(rule, value) {
const vendorid = getFieldValue("vendorid");
const vendorid = getFieldValue('vendorid');
if (vendorid && value) {
const response = await client.query({
query: CHECK_BILL_INVOICE_NUMBER,
@@ -245,14 +241,11 @@ export function BillFormComponent({
return Promise.resolve();
} else if (
response.data.bills_aggregate.nodes.length === 1 &&
response.data.bills_aggregate.nodes[0].id ===
form.getFieldValue("id")
response.data.bills_aggregate.nodes[0].id === form.getFieldValue('id')
) {
return Promise.resolve();
}
return Promise.reject(
t("bills.validation.unique_invoice_number")
);
return Promise.reject(t('bills.validation.unique_invoice_number'));
} else {
return Promise.resolve();
}
@@ -260,41 +253,34 @@ export function BillFormComponent({
}),
]}
>
<Input disabled={disabled || disableInvNumber}/>
<Input disabled={disabled || disableInvNumber} />
</Form.Item>
<Form.Item
label={t("bills.fields.date")}
label={t('bills.fields.date')}
name="date"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
({getFieldValue}) => ({
({ getFieldValue }) => ({
validator(rule, value) {
if (
ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (ClosingPeriod.treatment === 'on' && bodyshop.accountingconfig.ClosingPeriod) {
if (
dayjs(value)
.startOf("day")
.startOf('day')
.isSameOrAfter(
dayjs(
bodyshop.accountingconfig.ClosingPeriod[0]
).startOf("day")
dayjs(bodyshop.accountingconfig.ClosingPeriod[0]).startOf('day')
) &&
dayjs(value)
.startOf("day")
.startOf('day')
.isSameOrBefore(
dayjs(
bodyshop.accountingconfig.ClosingPeriod[1]
).endOf("day")
dayjs(bodyshop.accountingconfig.ClosingPeriod[1]).endOf('day')
)
) {
return Promise.resolve();
} else {
return Promise.reject(t("bills.validation.closingperiod"));
return Promise.reject(t('bills.validation.closingperiod'));
}
} else {
return Promise.resolve();
@@ -303,20 +289,16 @@ export function BillFormComponent({
}),
]}
>
<FormDatePicker disabled={disabled}/>
<FormDatePicker disabled={disabled} />
</Form.Item>
<Form.Item
label={t("bills.fields.is_credit_memo")}
label={t('bills.fields.is_credit_memo')}
name="is_credit_memo"
valuePropName="checked"
rules={[
({getFieldValue}) => ({
({ getFieldValue }) => ({
validator(rule, value) {
if (
value === true &&
getFieldValue("jobid") &&
getFieldValue("vendorid")
) {
if (value === true && getFieldValue('jobid') && getFieldValue('vendorid')) {
//Removed as this would cause an additional reload when validating the form on submit and clear the values.
// loadOutstandingReturns({
// variables: {
@@ -334,7 +316,7 @@ export function BillFormComponent({
job.status === bodyshop.md_ro_statuses.default_void) &&
(value === false || !value)
) {
return Promise.reject(t("bills.labels.onlycmforinvoiced"));
return Promise.reject(t('bills.labels.onlycmforinvoiced'));
}
return Promise.resolve();
@@ -342,10 +324,10 @@ export function BillFormComponent({
}),
]}
>
<Switch/>
<Switch />
</Form.Item>
<Form.Item
label={t("bills.fields.total")}
label={t('bills.fields.total')}
name="total"
rules={[
{
@@ -354,11 +336,11 @@ export function BillFormComponent({
},
]}
>
<CurrencyInput min={0} disabled={disabled}/>
<CurrencyInput min={0} disabled={disabled} />
</Form.Item>
{!billEdit && (
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
<Select style={{width: "10rem"}} disabled={disabled} allowClear>
<Form.Item label={t('bills.fields.allpartslocation')} name="location">
<Select style={{ width: '10rem' }} disabled={disabled} allowClear>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
{loc}
@@ -369,115 +351,99 @@ export function BillFormComponent({
)}
</LayoutFormRow>
<LayoutFormRow>
{
InstanceRenderManager({imex:
<Form.Item
span={3}
label={t("bills.fields.federal_tax_rate")}
name="federal_tax_rate"
>
{InstanceRenderManager({
imex: (
<Form.Item span={3} label={t('bills.fields.federal_tax_rate')} name="federal_tax_rate">
<CurrencyInput min={0} disabled={disabled} />
</Form.Item> })
}
<Form.Item
span={3}
label={t("bills.fields.state_tax_rate")}
name="state_tax_rate"
>
<CurrencyInput min={0} disabled={disabled}/>
</Form.Item>
{
InstanceRenderManager({imex: <>
<Form.Item
span={3}
label={t("bills.fields.local_tax_rate")}
name="local_tax_rate"
>
),
})}
<Form.Item span={3} label={t('bills.fields.state_tax_rate')} name="state_tax_rate">
<CurrencyInput min={0} disabled={disabled} />
</Form.Item>
{InstanceRenderManager({
imex: (
<>
<Form.Item span={3} label={t('bills.fields.local_tax_rate')} name="local_tax_rate">
<CurrencyInput min={0} />
</Form.Item>
{bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
<Form.Item
span={2}
label={t("bills.labels.federal_tax_exempt")}
label={t('bills.labels.federal_tax_exempt')}
name="federal_tax_exempt"
>
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
</Form.Item>
) : null}
</>})
}
</>
),
})}
<Form.Item shouldUpdate span={13}>
{() => {
const values = form.getFieldsValue([
"billlines",
"total",
"federal_tax_rate",
"state_tax_rate",
"local_tax_rate",
'billlines',
'total',
'federal_tax_rate',
'state_tax_rate',
'local_tax_rate',
]);
let totals;
if (
!!values.total &&
!!values.billlines &&
values.billlines.length > 0
)
if (!!values.total && !!values.billlines && values.billlines.length > 0)
totals = CalculateBillTotal(values);
if (!!totals)
return (
<div align="right">
<Space wrap>
<Statistic
title={t("bills.labels.subtotal")}
title={t('bills.labels.subtotal')}
value={totals.subtotal.toFormat()}
precision={2}
/>
{
InstanceRenderManager({imex: <Statistic
title={t("bills.labels.federal_tax")}
{InstanceRenderManager({
imex: (
<Statistic
title={t('bills.labels.federal_tax')}
value={totals.federalTax.toFormat()}
precision={2}
/> })
}
/>
),
})}
<Statistic
title={t("bills.labels.state_tax")}
title={t('bills.labels.state_tax')}
value={totals.stateTax.toFormat()}
precision={2}
/>
{
InstanceRenderManager({imex: <Statistic
title={t("bills.labels.local_tax")}
{InstanceRenderManager({
imex: (
<Statistic
title={t('bills.labels.local_tax')}
value={totals.localTax.toFormat()}
precision={2}
/>})
}
/>
),
})}
<Statistic
title={t("bills.labels.entered_total")}
title={t('bills.labels.entered_total')}
value={totals.enteredTotal.toFormat()}
precision={2}
/>
<Statistic
title={t("bills.labels.bill_total")}
title={t('bills.labels.bill_total')}
value={totals.invoiceTotal.toFormat()}
precision={2}
/>
<Statistic
title={t("bills.labels.discrepancy")}
title={t('bills.labels.discrepancy')}
valueStyle={{
color:
totals.discrepancy.getAmount() === 0
? "green"
: "red",
color: totals.discrepancy.getAmount() === 0 ? 'green' : 'red',
}}
value={totals.discrepancy.toFormat()}
precision={2}
/>
</Space>
{form.getFieldValue("is_credit_memo") ? (
<AlertComponent
type="warning"
message={t("bills.labels.enteringcreditmemo")}
/>
{form.getFieldValue('is_credit_memo') ? (
<AlertComponent type="warning" message={t('bills.labels.enteringcreditmemo')} />
) : null}
</div>
);
@@ -485,9 +451,9 @@ export function BillFormComponent({
}}
</Form.Item>
</LayoutFormRow>
<Divider orientation="left">{t("bills.labels.bill_lines")}</Divider>
<Divider orientation="left">{t('bills.labels.bill_lines')}</Divider>
{Extended_Bill_Posting.treatment === "on" ? (
{Extended_Bill_Posting.treatment === 'on' ? (
<BillFormLinesExtended
lineData={lineData}
discount={discount}
@@ -505,11 +471,13 @@ export function BillFormComponent({
billEdit={billEdit}
/>
)}
<Divider orientation="left" style={{ display: billEdit ? 'none' : null }}>
{t('documents.labels.upload')}
</Divider>
<Form.Item
name="upload"
label="Upload"
style={{display: billEdit ? "none" : null}}
style={{ display: billEdit ? 'none' : null }}
valuePropName="fileList"
getValueFromEvent={(e) => {
if (Array.isArray(e)) {
@@ -518,19 +486,12 @@ export function BillFormComponent({
return e && e.fileList;
}}
>
<Upload.Dragger
multiple={true}
name="logo"
beforeUpload={() => false}
listType="picture"
>
<Upload.Dragger multiple={true} name="logo" beforeUpload={() => false} listType="picture">
<>
<p className="ant-upload-drag-icon">
<UploadOutlined/>
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
<UploadOutlined />
</p>
<p className="ant-upload-text">Click or drag files to this area to upload.</p>
</>
</Upload.Dragger>
</Form.Item>

View File

@@ -60,6 +60,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
text: t("courtesycars.status.in"),
value: "courtesycars.status.in",
},
{
text: t("courtesycars.status.inservice"),
value: "courtesycars.status.inservice",
},
{
text: t("courtesycars.status.out"),
value: "courtesycars.status.out",
@@ -73,7 +77,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
value: "courtesycars.status.leasereturn",
},
],
onFilter: (value, record) => value.includes(record.status),
onFilter: (value, record) => record.status === value,
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => {
@@ -176,7 +180,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
title: t("courtesycars.fields.fuel"),
dataIndex: "fuel",
key: "fuel",
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
sorter: (a, b) => a.fuel - b.fuel,
sortOrder:
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
render: (text, record) => {
@@ -185,12 +189,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
return t("courtesycars.labels.fuel.full");
case 88:
return t("courtesycars.labels.fuel.78");
case 75:
return t("courtesycars.labels.fuel.34");
case 63:
return t("courtesycars.labels.fuel.58");
case 50:
return t("courtesycars.labels.fuel.12");
case 38:
return t("courtesycars.labels.fuel.34");
return t("courtesycars.labels.fuel.38");
case 25:
return t("courtesycars.labels.fuel.14");
case 13:

View File

@@ -23,7 +23,6 @@ export function JobEmployeeAssignments({
jobRO,
body,
refinish,
prep,
csr,
handleAdd,
@@ -78,7 +77,7 @@ export function JobEmployeeAssignments({
setVisibility(false);
}}
>
Assign
{t("allocations.actions.assign")}
</Button>
<Button onClick={() => setVisibility(false)}>Close</Button>
</Space>

View File

@@ -44,13 +44,13 @@ export function JobEmployeeAssignmentsContainer({
});
if (refetch) refetch();
if (!!!result.errors) {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentchange(operation, name),
type: "jobassignmentchange",
});
if (!!result.errors) {
} else {
notification["error"]({
message: t("jobs.errors.assigning", {
message: JSON.stringify(result.errors),
@@ -68,17 +68,19 @@ export function JobEmployeeAssignmentsContainer({
variables: {jobId: job.id, job: {[empAssignment]: null}},
});
if (!!result.errors) {
if (!!!result.errors) {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentremoved(operation),
type: "jobassignmentremoved",
});
} else {
notification["error"]({
message: t("jobs.errors.assigning", {
message: JSON.stringify(result.errors),
}),
});
}
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentremoved(operation),
type: "jobassignmentremoved",});
setLoading(false);
};

View File

@@ -82,7 +82,7 @@ export default function JobReconciliationBillsTable({
state.sortedInfo.order,
render: (text, record) => (
<Checkbox disabled checked={record.bill.is_credit_memo}/>
<Checkbox checked={record.bill.is_credit_memo}/>
),
},
];

View File

@@ -126,7 +126,7 @@ export function BillsListPage({
state.sortedInfo.columnKey === "is_credit_memo" &&
state.sortedInfo.order,
render: (text, record) => (
<Checkbox disabled checked={record.is_credit_memo}/>
<Checkbox checked={record.is_credit_memo}/>
),
},
{
@@ -136,7 +136,7 @@ export function BillsListPage({
sorter: (a, b) => a.exported - b.exported,
sortOrder:
state.sortedInfo.columnKey === "exported" && state.sortedInfo.order,
render: (text, record) => <Checkbox disabled checked={record.exported}/>,
render: (text, record) => <Checkbox checked={record.exported}/>,
},
{
title: t("general.labels.actions"),

View File

@@ -162,9 +162,7 @@ sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
{ text: "False", value: false },
],
onFilter: (value, record) => record.successful === value,
render: (text, record) => (
<Checkbox disabled checked={record.successful}/>
),
render: (text, record) => <Checkbox checked={record.successful} />,
},
{
title: t("general.labels.message"),

View File

@@ -1165,6 +1165,7 @@
"loadingshop": "Loading shop data...",
"loggingin": "Authorizing...",
"markedexported": "Manually marked as exported.",
"media": "Media",
"message": "Message",
"monday": "Monday",
"na": "N/A",

View File

@@ -569,6 +569,13 @@
table:
name: parts_orders
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: billid
table:
name: tasks
schema: public
insert_permissions:
- role: user
permission:
@@ -818,6 +825,13 @@
table:
name: inventory
schema: public
- name: ioevents
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: ioevents
schema: public
- name: jobs
using:
foreign_key_constraint_on:
@@ -846,6 +860,13 @@
table:
name: phonebook
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: tasks
schema: public
- name: timetickets
using:
foreign_key_constraint_on:
@@ -2675,6 +2696,13 @@
- table:
name: ioevents
schema: public
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
- name: user
using:
foreign_key_constraint_on: useremail
- table:
name: job_ar_schema
schema: public
@@ -2824,6 +2852,13 @@
table:
name: parts_order_lines
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: joblineid
table:
name: tasks
schema: public
insert_permissions:
- role: user
permission:
@@ -3311,6 +3346,13 @@
table:
name: scoreboard
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: jobid
table:
name: tasks
schema: public
- name: timetickets
using:
foreign_key_constraint_on:
@@ -5008,6 +5050,13 @@
table:
name: parts_order_lines
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: partsorderid
table:
name: tasks
schema: public
insert_permissions:
- role: user
permission:
@@ -5623,6 +5672,128 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
name: tasks
schema: public
object_relationships:
- name: bill
using:
foreign_key_constraint_on: billid
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
- name: job
using:
foreign_key_constraint_on: jobid
- name: jobline
using:
foreign_key_constraint_on: joblineid
- name: parts_order
using:
foreign_key_constraint_on: partsorderid
- name: user
using:
foreign_key_constraint_on: assigned_to
- name: userByCreatedBy
using:
foreign_key_constraint_on: created_by
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- completed
- deleted
- priority
- assigned_to
- created_by
- description
- title
- completed_at
- created_at
- deleted_at
- due_date
- remind_at
- updated_at
- billid
- bodyshopid
- id
- jobid
- joblineid
- partsorderid
select_permissions:
- role: user
permission:
columns:
- completed
- deleted
- priority
- assigned_to
- created_by
- description
- title
- completed_at
- created_at
- deleted_at
- due_date
- remind_at
- updated_at
- billid
- bodyshopid
- id
- jobid
- joblineid
- partsorderid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
update_permissions:
- role: user
permission:
columns:
- completed
- deleted
- priority
- assigned_to
- created_by
- description
- title
- completed_at
- created_at
- deleted_at
- due_date
- remind_at
- updated_at
- billid
- bodyshopid
- id
- jobid
- joblineid
- partsorderid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
- table:
name: timetickets
schema: public
@@ -6006,6 +6177,13 @@
table:
name: exportlog
schema: public
- name: ioevents
using:
foreign_key_constraint_on:
column: useremail
table:
name: ioevents
schema: public
- name: messages
using:
foreign_key_constraint_on:
@@ -6034,6 +6212,20 @@
table:
name: parts_orders
schema: public
- name: tasks
using:
foreign_key_constraint_on:
column: assigned_to
table:
name: tasks
schema: public
- name: tasksByCreatedBy
using:
foreign_key_constraint_on:
column: created_by
table:
name: tasks
schema: public
- name: timetickets
using:
foreign_key_constraint_on:

View File

@@ -0,0 +1 @@
DROP TABLE "public"."tasks";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."tasks" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "title" text NOT NULL, "description" Text, "deleted" boolean NOT NULL DEFAULT false, "deleted_at" timestamptz, "due_date" timestamptz, "created_by" text NOT NULL, "assigned_to" Text, "completed" boolean NOT NULL DEFAULT false, "completed_at" timestamptz, "remind_at" timestamptz, "priority" numeric, "bodyshopid" UUID NOT NULL, "jobid" UUID NOT NULL, "joblineid" UUID, "partsorderid" UUID, "billid" UUID, PRIMARY KEY ("id") , FOREIGN KEY ("created_by") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("assigned_to") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("joblineid") REFERENCES "public"."joblines"("id") ON UPDATE set null ON DELETE set null, FOREIGN KEY ("partsorderid") REFERENCES "public"."parts_orders"("id") ON UPDATE set null ON DELETE set null, FOREIGN KEY ("billid") REFERENCES "public"."bills"("id") ON UPDATE set null ON DELETE set null);
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_tasks_updated_at"
BEFORE UPDATE ON "public"."tasks"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_tasks_updated_at" ON "public"."tasks"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;