Merge remote-tracking branch 'origin/rome/release/2024-01-26' into rome/test-beta
# Conflicts: # .circleci/config.yml # client/package-lock.json # client/src/components/header/header.component.jsx # client/src/components/jobs-admin-change-status/jobs-admin-change.status.component.jsx # client/src/components/jobs-admin-delete-intake/jobs-admin-delete-intake.component.jsx # client/src/components/jobs-admin-mark-reexport/jobs-admin-mark-reexport.component.jsx # client/src/components/jobs-admin-unvoid/jobs-admin-unvoid.component.jsx # client/src/components/production-list-detail/production-list-detail.component.jsx # client/src/components/report-center-modal/report-center-modal.component.jsx # client/src/pages/jobs-admin/jobs-admin.page.jsx # client/src/pages/jobs-detail/jobs-detail.page.component.jsx # client/src/utils/TemplateConstants.js # client/yarn.lock # package-lock.json # server.js
This commit is contained in:
@@ -182,7 +182,6 @@ jobs:
|
||||
to: "s3://rome-online-test/"
|
||||
- jira/notify
|
||||
|
||||
|
||||
app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
@@ -203,6 +202,26 @@ jobs:
|
||||
to: "s3://imex-online-beta/"
|
||||
- jira/notify
|
||||
|
||||
rome-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-production-beta/"
|
||||
- jira/notify
|
||||
|
||||
test-hasura-migrate:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
@@ -274,6 +293,28 @@ jobs:
|
||||
to: "s3://imex-online-test-beta/"
|
||||
- jira/notify
|
||||
|
||||
rome-test-app-beta-build:
|
||||
docker:
|
||||
- image: cimg/node:18.18.2
|
||||
resource_class: large
|
||||
working_directory: ~/repo/client
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/repo
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: npm i
|
||||
|
||||
- run: npm run build:test
|
||||
|
||||
- aws-s3/sync:
|
||||
from: build
|
||||
to: "s3://rome-online-test-beta/"
|
||||
- jira/notify
|
||||
|
||||
|
||||
admin-app-build:
|
||||
docker:
|
||||
- image: cimg/node:16.15.0
|
||||
@@ -319,6 +360,10 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
only: master-beta
|
||||
- rome-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/master-beta
|
||||
- hasura-migrate:
|
||||
secret: ${HASURA_PROD_SECRET}
|
||||
filters:
|
||||
@@ -341,6 +386,10 @@ workflows:
|
||||
filters:
|
||||
branches:
|
||||
only: test
|
||||
- rome-test-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
only: rome/test-beta
|
||||
- test-app-beta-build:
|
||||
filters:
|
||||
branches:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
246
client/src/components/job-lifecycle/job-lifecycle.component.jsx
Normal file
246
client/src/components/job-lifecycle/job-lifecycle.component.jsx
Normal file
@@ -0,0 +1,246 @@
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
import moment from "moment";
|
||||
import axios from 'axios';
|
||||
import {Badge, Card, Space, Table, Tag} from 'antd';
|
||||
import {gql, useQuery} from "@apollo/client";
|
||||
import {DateTimeFormatterFunction} from "../../utils/DateFormatter";
|
||||
import {isEmpty} from "lodash";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
require('./job-lifecycle.styles.scss');
|
||||
|
||||
// show text on bar if text can fit
|
||||
export function JobLifecycleComponent({job, statuses, ...rest}) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [lifecycleData, setLifecycleData] = useState(null);
|
||||
const {t} = useTranslation(); // Used for tracking external state changes.
|
||||
|
||||
const {data} = useQuery(gql`
|
||||
query get_job_test($id: uuid!){
|
||||
jobs_by_pk(id:$id){
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
`, {
|
||||
variables: {
|
||||
id: job.id
|
||||
},
|
||||
fetchPolicy: 'cache-only'
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets the lifecycle data for the job.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const getLifecycleData = useCallback(async () => {
|
||||
if (job && job.id && statuses && statuses.statuses) {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.post("/job/lifecycle", {
|
||||
jobids: job.id,
|
||||
statuses: statuses.statuses
|
||||
});
|
||||
const data = response.data.transition[job.id];
|
||||
setLifecycleData(data);
|
||||
} catch (err) {
|
||||
console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [job, statuses, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
setTimeout(() => {
|
||||
getLifecycleData().catch(err => console.error(`${t('job_lifecycle.errors.fetch')}: ${err.message}`));
|
||||
}, 500);
|
||||
}, [data, getLifecycleData, t]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t('job_lifecycle.columns.value'),
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.start'),
|
||||
dataIndex: 'start',
|
||||
key: 'start',
|
||||
render: (text) => DateTimeFormatterFunction(text),
|
||||
sorter: (a, b) => moment(a.start).unix() - moment(b.start).unix(),
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.relative_start'),
|
||||
dataIndex: 'start_readable',
|
||||
key: 'start_readable',
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.end'),
|
||||
dataIndex: 'end',
|
||||
key: 'end',
|
||||
sorter: (a, b) => {
|
||||
if (isEmpty(a.end) || isEmpty(b.end)) {
|
||||
if (isEmpty(a.end) && isEmpty(b.end)) {
|
||||
return 0;
|
||||
}
|
||||
return isEmpty(a.end) ? 1 : -1;
|
||||
}
|
||||
return moment(a.end).unix() - moment(b.end).unix();
|
||||
},
|
||||
render: (text) => isEmpty(text) ? t('job_lifecycle.content.not_available') : DateTimeFormatterFunction(text)
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.relative_end'),
|
||||
dataIndex: 'end_readable',
|
||||
key: 'end_readable',
|
||||
},
|
||||
{
|
||||
title: t('job_lifecycle.columns.duration'),
|
||||
dataIndex: 'duration_readable',
|
||||
key: 'duration_readable',
|
||||
sorter: (a, b) => a.duration - b.duration,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card loading={loading} title={t('job_lifecycle.content.title')}>
|
||||
{!loading ? (
|
||||
lifecycleData && lifecycleData.lifecycle && lifecycleData.durations ? (
|
||||
<Space direction='vertical' style={{width: '100%'}}>
|
||||
<Card
|
||||
type='inner'
|
||||
title={(
|
||||
<Space direction='horizontal' size='small'>
|
||||
<Badge status='processing' count={lifecycleData.durations.totalStatuses}/>
|
||||
{t('job_lifecycle.content.title_durations')}
|
||||
</Space>
|
||||
|
||||
)}
|
||||
style={{width: '100%'}}
|
||||
>
|
||||
<div id="bar-container" style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100px',
|
||||
textAlign: 'center',
|
||||
borderRadius: '5px',
|
||||
borderWidth: '5px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: '#f0f2f5',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}}>
|
||||
{lifecycleData.durations.summations.map((key, index, array) => {
|
||||
const isFirst = index === 0;
|
||||
const isLast = index === array.length - 1;
|
||||
return (
|
||||
<div key={key.status} style={{
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
|
||||
borderTop: '1px solid #f0f2f5',
|
||||
borderBottom: '1px solid #f0f2f5',
|
||||
borderLeft: isFirst ? '1px solid #f0f2f5' : undefined,
|
||||
borderRight: isLast ? '1px solid #f0f2f5' : undefined,
|
||||
|
||||
backgroundColor: key.color,
|
||||
width: `${key.percentage}%`
|
||||
}}
|
||||
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
>
|
||||
|
||||
{key.percentage > 15 ?
|
||||
<>
|
||||
<div>{key.roundedPercentage}</div>
|
||||
<div style={{
|
||||
backgroundColor: '#f0f2f5',
|
||||
borderRadius: '5px',
|
||||
paddingRight: '2px',
|
||||
paddingLeft: '2px',
|
||||
fontSize: '0.8rem',
|
||||
}}>
|
||||
{key.status}
|
||||
</div>
|
||||
</>
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Card type='inner' title={t('job_lifecycle.content.legend_title')}
|
||||
style={{marginTop: '10px'}}>
|
||||
<div>
|
||||
{lifecycleData.durations.summations.map((key) => (
|
||||
<Tag color={key.color} style={{width: '13vh', padding: '4px', margin: '4px'}}>
|
||||
<div
|
||||
aria-label={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
title={`${key.status} | ${key.roundedPercentage} | ${key.humanReadable}`}
|
||||
style={{
|
||||
backgroundColor: '#f0f2f5',
|
||||
color: '#000',
|
||||
padding: '4px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
{key.status} ({key.roundedPercentage})
|
||||
</div>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
{(lifecycleData?.durations?.humanReadableTotal) ||
|
||||
(lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable) ?
|
||||
<Card style={{marginTop: '10px'}}>
|
||||
<ul>
|
||||
{lifecycleData.durations && lifecycleData.durations.humanReadableTotal &&
|
||||
<li>
|
||||
<span
|
||||
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.previous_status_accumulated_time')}:</span> {lifecycleData.durations.humanReadableTotal}
|
||||
</li>
|
||||
}
|
||||
{lifecycleData.lifecycle[0] && lifecycleData.lifecycle[0].value && lifecycleData?.durations?.totalCurrentStatusDuration?.humanReadable &&
|
||||
<li>
|
||||
<span
|
||||
style={{fontWeight: 'bold'}}>{t('job_lifecycle.content.current_status_accumulated_time')} ({lifecycleData.lifecycle[0].value}):</span> {lifecycleData.durations.totalCurrentStatusDuration.humanReadable}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</Card>
|
||||
: null}
|
||||
</Card>
|
||||
<Card type='inner' title={(
|
||||
<>
|
||||
<Space direction="horizontal" size="small">
|
||||
<Badge status='processing' count={lifecycleData.lifecycle.length}/>
|
||||
{t('job_lifecycle.content.title_transitions')}
|
||||
</Space>
|
||||
</>
|
||||
)}>
|
||||
<Table style={{
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
}} columns={columns} dataSource={lifecycleData.lifecycle}/>
|
||||
</Card>
|
||||
</Space>
|
||||
) : (
|
||||
<Card type='inner' style={{textAlign: 'center'}}>
|
||||
{t('job_lifecycle.content.data_unavailable')}
|
||||
</Card>
|
||||
)
|
||||
) : (
|
||||
<Card type='inner' title={t('job_lifecycle.content.title_loading')}>
|
||||
{t('job_lifecycle.content.loading')}
|
||||
</Card>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default JobLifecycleComponent;
|
||||
@@ -51,12 +51,14 @@ export function JobsAdminStatus({insertAuditTrail, bodyshop, job}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown menu={statusMenu} trigger={["click"]} key="changestatus">
|
||||
<Button shape="round">
|
||||
<span>{job.status}</span>
|
||||
<>
|
||||
<Dropdown menu={statusMenu} trigger={["click"]} key="changestatus">
|
||||
<Button shape="round">
|
||||
<span>{job.status}</span>
|
||||
|
||||
<DownCircleFilled/>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<DownCircleFilled/>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
import {gql, useMutation} from "@apollo/client";
|
||||
import {Button, notification} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Space, notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
DELETE_DELIVERY_CHECKLIST,
|
||||
DELETE_INTAKE_CHECKLIST,
|
||||
} from "../../graphql/jobs.queries";
|
||||
|
||||
export default function JobAdminDeleteIntake({job}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [deleteIntake] = useMutation(gql`
|
||||
mutation DELETE_INTAKE($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { intakechecklist: null }
|
||||
) {
|
||||
id
|
||||
intakechecklist
|
||||
}
|
||||
}
|
||||
`);
|
||||
export default function JobAdminDeleteIntake({ job }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [DELETE_DELIVERY] = useMutation(gql`
|
||||
mutation DELETE_DELIVERY($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { deliverchecklist: null }
|
||||
) {
|
||||
id
|
||||
deliverchecklist
|
||||
}
|
||||
}
|
||||
`);
|
||||
const [deleteIntake] = useMutation(DELETE_INTAKE_CHECKLIST);
|
||||
const [deleteDelivery] = useMutation(DELETE_DELIVERY_CHECKLIST);
|
||||
|
||||
const handleDelete = async (values) => {
|
||||
setLoading(true);
|
||||
@@ -48,11 +32,11 @@ export default function JobAdminDeleteIntake({job}) {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleDeleteDelivery = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await DELETE_DELIVERY({
|
||||
variables: {jobId: job.id},
|
||||
});
|
||||
const handleDeleteDelivery = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await deleteDelivery({
|
||||
variables: { jobId: job.id },
|
||||
});
|
||||
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({message: t("jobs.successes.save")});
|
||||
@@ -66,14 +50,24 @@ export default function JobAdminDeleteIntake({job}) {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button loading={loading} onClick={handleDelete}>
|
||||
{t("jobs.labels.deleteintake")}
|
||||
</Button>
|
||||
<Button loading={loading} onClick={handleDeleteDelivery}>
|
||||
{t("jobs.labels.deletedelivery")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Space wrap>
|
||||
<Button
|
||||
loading={loading}
|
||||
onClick={handleDelete}
|
||||
disabled={!job.intakechecklist}
|
||||
>
|
||||
{t("jobs.labels.deleteintake")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
onClick={handleDeleteDelivery}
|
||||
disabled={!job.deliverychecklist}
|
||||
>
|
||||
{t("jobs.labels.deletedelivery")}
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import {gql, useMutation} from "@apollo/client";
|
||||
import {Button, notification} from "antd";
|
||||
import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Space, notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import dayjs from "../../utils/day";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import {INSERT_EXPORT_LOG} from "../../graphql/accounting.queries";
|
||||
import {insertAuditTrail} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import moment from "moment";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
MARK_JOB_AS_EXPORTED,
|
||||
MARK_JOB_AS_UNINVOICED,
|
||||
MARK_JOB_FOR_REEXPORT,
|
||||
} from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -25,67 +33,27 @@ export default connect(
|
||||
)(JobAdminMarkReexport);
|
||||
|
||||
export function JobAdminMarkReexport({
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
job,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
const [markJobForReexport] = useMutation(gql`
|
||||
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { date_exported: null
|
||||
status: "${bodyshop.md_ro_statuses.default_invoiced}"
|
||||
}
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
status
|
||||
date_invoiced
|
||||
}
|
||||
}
|
||||
`);
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
job,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
|
||||
|
||||
const [markJobExported] = useMutation(gql`
|
||||
mutation MARK_JOB_AS_EXPORTED($jobId: uuid!, $date_exported: timestamptz!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { date_exported: $date_exported
|
||||
status: "${bodyshop.md_ro_statuses.default_exported}"
|
||||
}
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
date_invoiced
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
const [markJobUninvoiced] = useMutation(gql`
|
||||
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, ) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { date_exported: null
|
||||
date_invoiced: null
|
||||
status: "${bodyshop.md_ro_statuses.default_delivered}"
|
||||
}
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
date_invoiced
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
const [markJobForReexport] = useMutation(MARK_JOB_FOR_REEXPORT);
|
||||
const [markJobExported] = useMutation(MARK_JOB_AS_EXPORTED);
|
||||
const [markJobUninvoiced] = useMutation(MARK_JOB_AS_UNINVOICED);
|
||||
|
||||
const handleMarkForExport = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobForReexport({
|
||||
variables: {jobId: job.id},
|
||||
});
|
||||
const handleMarkForExport = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobForReexport({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
default_invoiced: bodyshop.md_ro_statuses.default_invoiced,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({message: t("jobs.successes.save")});
|
||||
@@ -103,11 +71,15 @@ export function JobAdminMarkReexport({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleMarkExported = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobExported({
|
||||
variables: {jobId: job.id, date_exported: dayjs()},
|
||||
});
|
||||
const handleMarkExported = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobExported({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
date_exported: moment(),
|
||||
default_exported: bodyshop.md_ro_statuses.default_exported,
|
||||
},
|
||||
});
|
||||
|
||||
await insertExportLog({
|
||||
variables: {
|
||||
@@ -139,11 +111,14 @@ export function JobAdminMarkReexport({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleUninvoice = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobUninvoiced({
|
||||
variables: {jobId: job.id},
|
||||
});
|
||||
const handleUninvoice = async () => {
|
||||
setLoading(true);
|
||||
const result = await markJobUninvoiced({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
default_delivered: bodyshop.md_ro_statuses.default_delivered,
|
||||
},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({message: t("jobs.successes.save")});
|
||||
@@ -161,29 +136,31 @@ export function JobAdminMarkReexport({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!job.date_exported}
|
||||
onClick={handleMarkForExport}
|
||||
>
|
||||
{t("jobs.labels.markforreexport")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={job.date_exported}
|
||||
onClick={handleMarkExported}
|
||||
>
|
||||
{t("jobs.actions.markasexported")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!job.date_invoiced || job.date_exported}
|
||||
onClick={handleUninvoice}
|
||||
>
|
||||
{t("jobs.actions.uninvoice")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Space wrap>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!job.date_exported}
|
||||
onClick={handleMarkForExport}
|
||||
>
|
||||
{t("jobs.labels.markforreexport")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={job.date_exported}
|
||||
onClick={handleMarkExported}
|
||||
>
|
||||
{t("jobs.actions.markasexported")}
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={!job.date_invoiced || job.date_exported}
|
||||
onClick={handleUninvoice}
|
||||
>
|
||||
{t("jobs.actions.uninvoice")}
|
||||
</Button>
|
||||
</Space>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Switch, notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_REMOVE_FROM_AR } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminRemoveAR);
|
||||
|
||||
export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [switchValue, setSwitchValue] = useState(job.remove_from_ar);
|
||||
|
||||
const [mutationUpdateRemoveFromAR] = useMutation(UPDATE_REMOVE_FROM_AR);
|
||||
|
||||
const handleChange = async (value) => {
|
||||
setLoading(true);
|
||||
const result = await mutationUpdateRemoveFromAR({
|
||||
variables: { jobId: job.id, remove_from_ar: value },
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.save") });
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
|
||||
});
|
||||
setSwitchValue(value);
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<div style={{ marginRight: "10px" }}>
|
||||
{t("jobs.labels.remove_from_ar")}:
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
checked={switchValue}
|
||||
loading={loading}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,111 +4,65 @@ import React, {useState} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {createStructuredSelector} from "reselect";
|
||||
import { UNVOID_JOB } from "../../graphql/jobs.queries";
|
||||
import {insertAuditTrail} from "../../redux/application/application.actions";
|
||||
import {selectBodyshop, selectCurrentUser,} from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({jobid, operation}) =>
|
||||
dispatch(insertAuditTrail({jobid, operation})),
|
||||
insertAuditTrail: ({ jobid, operation }) =>
|
||||
dispatch(insertAuditTrail({ jobid, operation })),
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
|
||||
|
||||
export function JobsAdminUnvoid({
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
job,
|
||||
currentUser,
|
||||
}) {
|
||||
const {t} = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateJob] = useMutation(gql`
|
||||
mutation UNVOID_JOB($jobId: uuid!) {
|
||||
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
|
||||
bodyshop.md_ro_statuses.default_imported
|
||||
}", date_void: null}) {
|
||||
id
|
||||
date_void
|
||||
voided
|
||||
status
|
||||
}
|
||||
insert_notes(objects: {jobid: $jobId, audit: true, created_by: "${
|
||||
currentUser.email
|
||||
}", text: "${t("jobs.labels.unvoidnote")}"}) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
insertAuditTrail,
|
||||
bodyshop,
|
||||
job,
|
||||
currentUser,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [mutationUnvoidJob] = useMutation(UNVOID_JOB);
|
||||
|
||||
`);
|
||||
const handleUpdate = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await mutationUnvoidJob({
|
||||
variables: {
|
||||
jobId: job.id,
|
||||
default_imported: bodyshop.md_ro_statuses.default_imported,
|
||||
currentUserEmail: currentUser.email,
|
||||
text: t("jobs.labels.unvoidnote"),
|
||||
},
|
||||
});
|
||||
|
||||
// const result = await voidJob({
|
||||
// variables: {
|
||||
// jobId: job.id,
|
||||
// job: {
|
||||
// status: bodyshop.md_ro_statuses.default_void,
|
||||
// voided: true,
|
||||
// },
|
||||
// note: [
|
||||
// {
|
||||
// jobid: job.id,
|
||||
// created_by: currentUser.email,
|
||||
// audit: true,
|
||||
// text: t("jobs.labels.voidnote", {
|
||||
// date: dayjs().format("MM/DD/yyy"),
|
||||
// time: dayjs().format("hh:mm a"),
|
||||
// }),
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// });
|
||||
if (!result.errors) {
|
||||
notification["success"]({ message: t("jobs.successes.save") });
|
||||
|
||||
// if (!!!result.errors) {
|
||||
// notification["success"]({
|
||||
// message: t("jobs.successes.voided"),
|
||||
// });
|
||||
// //go back to jobs list.
|
||||
// history.push(`/manage/`);
|
||||
// } else {
|
||||
// notification["error"]({
|
||||
// message: t("jobs.errors.voiding", {
|
||||
// error: JSON.stringify(result.errors),
|
||||
// }),
|
||||
// });
|
||||
// }
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobunvoid(),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
//Get the owner details, populate it all back into the job.
|
||||
};
|
||||
|
||||
const handleUpdate = async (values) => {
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: {jobId: job.id},
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification["success"]({message: t("jobs.successes.save")});
|
||||
|
||||
insertAuditTrail({
|
||||
jobid: job.id,
|
||||
operation: AuditTrailMapping.admin_jobunvoid(),
|
||||
});
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.saving", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
//Get the owner details, populate it all back into the job.
|
||||
};
|
||||
|
||||
return (
|
||||
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
|
||||
{t("jobs.actions.unvoid")}
|
||||
</Button>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Button loading={loading} disabled={!job.voided} onClick={handleUpdate}>
|
||||
{t("jobs.actions.unvoid")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,128 +65,132 @@ export function ProductionListDetail({
|
||||
nextFetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
<PageHeader
|
||||
title={theJob.ro_number}
|
||||
extra={
|
||||
<Space wrap>
|
||||
{!technician ? (
|
||||
<ProductionRemoveButton jobId={theJob.id}/>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: {refetch: refetch},
|
||||
context: {
|
||||
id: theJob.id,
|
||||
job: theJob,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<PrinterFilled/>
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
{!technician ? (
|
||||
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}}/>
|
||||
) : null}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"50%"}
|
||||
onClose={handleClose}
|
||||
open={selected}
|
||||
>
|
||||
{loading && <LoadingSkeleton/>}
|
||||
{error && <AlertComponent error={JSON.stringify(error)}/>}
|
||||
{!loading && data && (
|
||||
<div>
|
||||
<Space direction="vertical">
|
||||
<CardTemplate
|
||||
title={t("jobs.labels.employeeassignments")}
|
||||
loading={loading}
|
||||
>
|
||||
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch}/>
|
||||
</CardTemplate>
|
||||
<Descriptions bordered column={1}>
|
||||
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
||||
{theJob.ro_number || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
||||
<Space>
|
||||
{data.jobs_by_pk.alt_transport || ""}
|
||||
<JobAtChange job={data.jobs_by_pk}/>
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
||||
{theJob.clm_no || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.ins_co_nm")}>
|
||||
{theJob.ins_co_nm || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.owner")}>
|
||||
<Space>
|
||||
<OwnerNameDisplay ownerObject={theJob}/>
|
||||
{!technician ? (
|
||||
<>
|
||||
<StartChatButton
|
||||
phone={data.jobs_by_pk.ownr_ph1}
|
||||
jobid={data.jobs_by_pk.id}
|
||||
/>
|
||||
<StartChatButton
|
||||
phone={data.jobs_by_pk.ownr_ph2}
|
||||
jobid={data.jobs_by_pk.id}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PhoneNumberFormatter>
|
||||
{data.jobs_by_pk.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>
|
||||
{data.jobs_by_pk.ownr_ph2}
|
||||
</PhoneNumberFormatter>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
||||
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
||||
theJob.v_make_desc || ""
|
||||
} ${theJob.v_model_desc || ""}`}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.clm_total")}>
|
||||
<CurrencyFormatter>{theJob.clm_total}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.actual_in")}>
|
||||
<DateFormatter>{theJob.actual_in}</DateFormatter>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
|
||||
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
{!bodyshop.uselocalmediaserver && (
|
||||
<JobDetailCardsDocumentsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
<PageHeader
|
||||
title={theJob.ro_number}
|
||||
extra={
|
||||
<Space wrap>
|
||||
{!technician ? (
|
||||
<ProductionRemoveButton jobId={theJob.id} />
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: theJob.id,
|
||||
job: theJob,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
{!technician ? (
|
||||
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
|
||||
) : null}
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"50%"}
|
||||
onClose={handleClose}
|
||||
open={selected}
|
||||
>
|
||||
{loading && <LoadingSkeleton />}
|
||||
{error && <AlertComponent error={JSON.stringify(error)} />}
|
||||
{!loading && data && (
|
||||
<div>
|
||||
<CardTemplate
|
||||
title={t("jobs.labels.employeeassignments")}
|
||||
loading={loading}
|
||||
>
|
||||
<JobEmployeeAssignments job={data.jobs_by_pk} refetch={refetch} />
|
||||
</CardTemplate>
|
||||
<div style={{ height: "8px" }} />
|
||||
<Descriptions bordered column={1}>
|
||||
<Descriptions.Item label={t("jobs.fields.ro_number")}>
|
||||
{theJob.ro_number || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.alt_transport")}>
|
||||
<Space>
|
||||
{data.jobs_by_pk.alt_transport || ""}
|
||||
<JobAtChange job={data.jobs_by_pk} />
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.clm_no")}>
|
||||
{theJob.clm_no || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.ins_co_nm")}>
|
||||
{theJob.ins_co_nm || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.owner")}>
|
||||
<Space>
|
||||
<OwnerNameDisplay ownerObject={theJob} />
|
||||
{!technician ? (
|
||||
<>
|
||||
<StartChatButton
|
||||
phone={data.jobs_by_pk.ownr_ph1}
|
||||
jobid={data.jobs_by_pk.id}
|
||||
/>
|
||||
<StartChatButton
|
||||
phone={data.jobs_by_pk.ownr_ph2}
|
||||
jobid={data.jobs_by_pk.id}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PhoneNumberFormatter>
|
||||
{data.jobs_by_pk.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
<PhoneNumberFormatter>
|
||||
{data.jobs_by_pk.ownr_ph2}
|
||||
</PhoneNumberFormatter>
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.vehicle")}>
|
||||
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${
|
||||
theJob.v_make_desc || ""
|
||||
} ${theJob.v_model_desc || ""}`}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.clm_total")}>
|
||||
<CurrencyFormatter>{theJob.clm_total}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.actual_in")}>
|
||||
<DateFormatter>{theJob.actual_in}</DateFormatter>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
|
||||
<DateFormatter>{theJob.scheduled_completion}</DateFormatter>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div style={{ height: "8px" }} />
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
<div style={{ height: "8px" }} />
|
||||
<JobDetailCardsNotesComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
{!bodyshop.uselocalmediaserver && (
|
||||
<>
|
||||
<div style={{ height: "8px" }} />
|
||||
<JobDetailCardsDocumentsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
const start = values.dates[0];
|
||||
const end = values.dates[1];
|
||||
const {id} = values;
|
||||
const start = values.dates ? values.dates[0] : null;
|
||||
const end = values.dates ? values.dates[1] : null;
|
||||
const { id } = values;
|
||||
|
||||
await GenerateDocument(
|
||||
{
|
||||
@@ -257,20 +257,30 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||
else return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="dates"
|
||||
label={t("reportcenter.labels.dates")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DatePicker.RangePicker
|
||||
format="MM/DD/YYYY"
|
||||
presets={DatePIckerRanges}
|
||||
/>
|
||||
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
|
||||
{() => {
|
||||
const key = form.getFieldValue("key");
|
||||
const datedisable = Templates[key] && Templates[key].datedisable;
|
||||
if (datedisable !== true) {
|
||||
return (
|
||||
<Form.Item
|
||||
name="dates"
|
||||
label={t("reportcenter.labels.dates")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DatePicker.RangePicker
|
||||
format="MM/DD/YYYY"
|
||||
ranges={DatePIckerRanges}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
} else return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
<Form.Item style={{margin: 0, padding: 0}} dependencies={["key"]}>
|
||||
{() => {
|
||||
@@ -306,18 +316,18 @@ export function ReportCenterModalComponent({reportCenterModal, bodyshop}) {
|
||||
}}
|
||||
</Form.Item>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: "1rem",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => form.submit()} style={{}} loading={loading}>
|
||||
{t("reportcenter.actions.generate")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: "1rem",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => form.submit()} style={{}} loading={loading}>
|
||||
{t("reportcenter.actions.generate")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -545,164 +545,229 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
||||
export const GET_JOB_BY_PK = gql`
|
||||
query GET_JOB_BY_PK($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
updated_at
|
||||
actual_completion
|
||||
actual_delivery
|
||||
actual_in
|
||||
adjustment_bottom_line
|
||||
alt_transport
|
||||
area_of_damage
|
||||
auto_add_ats
|
||||
available_jobs {
|
||||
id
|
||||
}
|
||||
ca_bc_pvrt
|
||||
ca_customer_gst
|
||||
ca_gst_registrant
|
||||
category
|
||||
cccontracts {
|
||||
agreementnumber
|
||||
courtesycar {
|
||||
fleetnumber
|
||||
id
|
||||
make
|
||||
model
|
||||
plate
|
||||
year
|
||||
}
|
||||
id
|
||||
start
|
||||
status
|
||||
scheduledreturn
|
||||
}
|
||||
cieca_pfl
|
||||
cieca_pfo
|
||||
cieca_pft
|
||||
cieca_ttl
|
||||
class
|
||||
clm_no
|
||||
clm_total
|
||||
comment
|
||||
converted
|
||||
csiinvites {
|
||||
completedon
|
||||
id
|
||||
}
|
||||
date_estimated
|
||||
date_exported
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_lost_sale
|
||||
date_next_contact
|
||||
date_open
|
||||
date_rentalresp
|
||||
date_repairstarted
|
||||
date_scheduled
|
||||
date_towin
|
||||
date_void
|
||||
ded_amt
|
||||
ded_note
|
||||
ded_status
|
||||
deliverchecklist
|
||||
depreciation_taxes
|
||||
driveable
|
||||
employee_body
|
||||
employee_body_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_refinish_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_prep_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_csr
|
||||
employee_csr_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_csr
|
||||
employee_prep
|
||||
employee_prep_rel {
|
||||
id
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
employee_refinish
|
||||
employee_body
|
||||
alt_transport
|
||||
intakechecklist
|
||||
invoice_final_note
|
||||
comment
|
||||
loss_desc
|
||||
kmin
|
||||
kmout
|
||||
referral_source
|
||||
referral_source_extra
|
||||
unit_number
|
||||
po_number
|
||||
special_coverage_policy
|
||||
scheduled_delivery
|
||||
converted
|
||||
lbr_adjustments
|
||||
ro_number
|
||||
po_number
|
||||
clm_total
|
||||
inproduction
|
||||
vehicleid
|
||||
plate_no
|
||||
plate_st
|
||||
v_vin
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_color
|
||||
vehicleid
|
||||
driveable
|
||||
towin
|
||||
loss_of_use
|
||||
lost_sale_reason
|
||||
vehicle {
|
||||
employee_refinish_rel {
|
||||
id
|
||||
plate_no
|
||||
plate_st
|
||||
v_vin
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_color
|
||||
notes
|
||||
v_paint_codes
|
||||
jobs {
|
||||
id
|
||||
ro_number
|
||||
status
|
||||
clm_no
|
||||
}
|
||||
first_name
|
||||
last_name
|
||||
}
|
||||
available_jobs {
|
||||
id
|
||||
}
|
||||
ins_co_id
|
||||
policy_no
|
||||
loss_date
|
||||
clm_no
|
||||
area_of_damage
|
||||
ins_co_nm
|
||||
ins_addr1
|
||||
ins_city
|
||||
ins_ct_ln
|
||||
ins_ct_fn
|
||||
ins_ea
|
||||
ins_ph1
|
||||
est_co_nm
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
est_ph1
|
||||
est_ea
|
||||
selling_dealer
|
||||
servicing_dealer
|
||||
selling_dealer_contact
|
||||
servicing_dealer_contact
|
||||
regie_number
|
||||
scheduled_completion
|
||||
id
|
||||
ded_amt
|
||||
ded_status
|
||||
depreciation_taxes
|
||||
other_amount_payable
|
||||
towing_payable
|
||||
storage_payable
|
||||
adjustment_bottom_line
|
||||
est_ph1
|
||||
federal_tax_rate
|
||||
state_tax_rate
|
||||
local_tax_rate
|
||||
tax_tow_rt
|
||||
tax_str_rt
|
||||
tax_paint_mat_rt
|
||||
tax_shop_mat_rt
|
||||
tax_sub_rt
|
||||
tax_lbr_rt
|
||||
tax_levies_rt
|
||||
parts_tax_rates
|
||||
job_totals
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_addr1
|
||||
ownr_addr2
|
||||
ownr_city
|
||||
ownr_st
|
||||
ownr_zip
|
||||
ownr_ctry
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
production_vars
|
||||
ca_gst_registrant
|
||||
ownerid
|
||||
ded_note
|
||||
materials
|
||||
auto_add_ats
|
||||
rate_ats
|
||||
id
|
||||
inproduction
|
||||
ins_addr1
|
||||
ins_city
|
||||
ins_co_id
|
||||
ins_co_nm
|
||||
ins_ct_fn
|
||||
ins_ct_ln
|
||||
ins_ea
|
||||
ins_ph1
|
||||
intakechecklist
|
||||
invoice_final_note
|
||||
iouparent
|
||||
job_totals
|
||||
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
||||
act_price
|
||||
act_price_before_ppc
|
||||
ah_detail_line
|
||||
alt_partm
|
||||
alt_partno
|
||||
assigned_team
|
||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||
actual_cost
|
||||
actual_price
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
id
|
||||
joblineid
|
||||
quantity
|
||||
}
|
||||
convertedtolbr
|
||||
critical
|
||||
db_hrs
|
||||
db_price
|
||||
db_ref
|
||||
id
|
||||
ioucreated
|
||||
lbr_amt
|
||||
lbr_op
|
||||
line_desc
|
||||
line_ind
|
||||
line_no
|
||||
line_ref
|
||||
location
|
||||
manual_line
|
||||
mod_lb_hrs
|
||||
mod_lbr_ty
|
||||
notes
|
||||
oem_partno
|
||||
op_code_desc
|
||||
parts_dispatch_lines(limit: 1, order_by: { accepted_at: desc }) {
|
||||
accepted_at
|
||||
id
|
||||
parts_dispatch {
|
||||
employeeid
|
||||
id
|
||||
}
|
||||
}
|
||||
part_qty
|
||||
part_type
|
||||
prt_dsmk_m
|
||||
prt_dsmk_p
|
||||
status
|
||||
tax_part
|
||||
unq_seq
|
||||
}
|
||||
kmin
|
||||
kmout
|
||||
labor_rate_desc
|
||||
lbr_adjustments
|
||||
local_tax_rate
|
||||
loss_date
|
||||
loss_desc
|
||||
loss_of_use
|
||||
lost_sale_reason
|
||||
materials
|
||||
other_amount_payable
|
||||
owner {
|
||||
id
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
ownr_ea
|
||||
ownr_addr1
|
||||
ownr_addr2
|
||||
ownr_city
|
||||
ownr_st
|
||||
ownr_zip
|
||||
ownr_co_nm
|
||||
ownr_ctry
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
ownr_st
|
||||
ownr_zip
|
||||
tax_number
|
||||
}
|
||||
labor_rate_desc
|
||||
ownerid
|
||||
owner_owing
|
||||
ownr_addr1
|
||||
ownr_addr2
|
||||
ownr_city
|
||||
ownr_co_nm
|
||||
ownr_ctry
|
||||
ownr_ea
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
ownr_ph2
|
||||
ownr_st
|
||||
ownr_zip
|
||||
parts_tax_rates
|
||||
payments {
|
||||
amount
|
||||
created_at
|
||||
date
|
||||
exportedat
|
||||
id
|
||||
jobid
|
||||
memo
|
||||
payer
|
||||
paymentnum
|
||||
transactionid
|
||||
type
|
||||
}
|
||||
plate_no
|
||||
plate_st
|
||||
po_number
|
||||
policy_no
|
||||
production_vars
|
||||
rate_ats
|
||||
rate_la1
|
||||
rate_la2
|
||||
rate_la3
|
||||
@@ -726,135 +791,64 @@ export const GET_JOB_BY_PK = gql`
|
||||
rate_mapa
|
||||
rate_mash
|
||||
rate_matd
|
||||
actual_in
|
||||
federal_tax_rate
|
||||
local_tax_rate
|
||||
state_tax_rate
|
||||
referral_source
|
||||
referral_source_extra
|
||||
regie_number
|
||||
remove_from_ar
|
||||
ro_number
|
||||
scheduled_completion
|
||||
scheduled_in
|
||||
actual_completion
|
||||
scheduled_delivery
|
||||
actual_delivery
|
||||
date_estimated
|
||||
date_open
|
||||
date_scheduled
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_lost_sale
|
||||
date_next_contact
|
||||
date_towin
|
||||
date_rentalresp
|
||||
date_exported
|
||||
date_repairstarted
|
||||
date_void
|
||||
scheduled_in
|
||||
selling_dealer
|
||||
selling_dealer_contact
|
||||
servicing_dealer
|
||||
servicing_dealer_contact
|
||||
special_coverage_policy
|
||||
state_tax_rate
|
||||
status
|
||||
owner_owing
|
||||
tax_registration_number
|
||||
class
|
||||
category
|
||||
deliverchecklist
|
||||
voided
|
||||
ca_bc_pvrt
|
||||
ca_customer_gst
|
||||
storage_payable
|
||||
suspended
|
||||
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
||||
tax_lbr_rt
|
||||
tax_levies_rt
|
||||
tax_paint_mat_rt
|
||||
tax_registration_number
|
||||
tax_shop_mat_rt
|
||||
tax_str_rt
|
||||
tax_sub_rt
|
||||
tax_tow_rt
|
||||
towin
|
||||
towing_payable
|
||||
unit_number
|
||||
updated_at
|
||||
v_color
|
||||
v_make_desc
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_vin
|
||||
vehicle {
|
||||
id
|
||||
alt_partm
|
||||
line_no
|
||||
unq_seq
|
||||
line_ind
|
||||
line_desc
|
||||
line_ref
|
||||
part_type
|
||||
oem_partno
|
||||
alt_partno
|
||||
db_price
|
||||
act_price
|
||||
part_qty
|
||||
mod_lbr_ty
|
||||
db_hrs
|
||||
mod_lb_hrs
|
||||
lbr_op
|
||||
lbr_amt
|
||||
op_code_desc
|
||||
status
|
||||
jobs {
|
||||
clm_no
|
||||
id
|
||||
ro_number
|
||||
status
|
||||
}
|
||||
notes
|
||||
location
|
||||
tax_part
|
||||
db_ref
|
||||
manual_line
|
||||
prt_dsmk_p
|
||||
prt_dsmk_m
|
||||
ioucreated
|
||||
convertedtolbr
|
||||
ah_detail_line
|
||||
act_price_before_ppc
|
||||
critical
|
||||
parts_dispatch_lines(limit: 1, order_by: { accepted_at: desc }) {
|
||||
id
|
||||
accepted_at
|
||||
parts_dispatch {
|
||||
id
|
||||
employeeid
|
||||
}
|
||||
}
|
||||
assigned_team
|
||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||
id
|
||||
quantity
|
||||
actual_cost
|
||||
actual_price
|
||||
joblineid
|
||||
bill {
|
||||
id
|
||||
invoice_number
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
payments {
|
||||
id
|
||||
jobid
|
||||
amount
|
||||
payer
|
||||
paymentnum
|
||||
created_at
|
||||
transactionid
|
||||
memo
|
||||
date
|
||||
type
|
||||
exportedat
|
||||
}
|
||||
cccontracts {
|
||||
id
|
||||
status
|
||||
start
|
||||
scheduledreturn
|
||||
agreementnumber
|
||||
courtesycar {
|
||||
id
|
||||
make
|
||||
model
|
||||
year
|
||||
plate
|
||||
fleetnumber
|
||||
}
|
||||
}
|
||||
cieca_ttl
|
||||
cieca_pfo
|
||||
cieca_pfl
|
||||
cieca_pft
|
||||
materials
|
||||
csiinvites {
|
||||
id
|
||||
completedon
|
||||
plate_no
|
||||
plate_st
|
||||
v_color
|
||||
v_make_desc
|
||||
v_model_yr
|
||||
v_model_desc
|
||||
v_paint_codes
|
||||
v_vin
|
||||
}
|
||||
vehicleid
|
||||
voided
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
||||
query GET_JOB_RECONCILIATION_BY_PK($id: uuid!) {
|
||||
bills(where: { jobid: { _eq: $id } }) {
|
||||
@@ -920,6 +914,7 @@ export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_JOB_CARD_DETAILS = gql`
|
||||
query QUERY_JOB_CARD_DETAILS($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
@@ -2295,6 +2290,123 @@ export const GET_JOB_LINE_ORDERS = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_REMOVE_FROM_AR = gql`
|
||||
mutation UPDATE_REMOVE_FROM_AR($jobId: uuid!, $remove_from_ar: Boolean!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { remove_from_ar: $remove_from_ar }
|
||||
) {
|
||||
id
|
||||
remove_from_ar
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UNVOID_JOB = gql`
|
||||
mutation UNVOID_JOB(
|
||||
$jobId: uuid!
|
||||
$default_imported: String!
|
||||
$currentUserEmail: String!
|
||||
$text: String!
|
||||
) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { voided: false, status: $default_imported, date_void: null }
|
||||
) {
|
||||
id
|
||||
date_void
|
||||
voided
|
||||
status
|
||||
}
|
||||
insert_notes(
|
||||
objects: {
|
||||
jobid: $jobId
|
||||
audit: true
|
||||
created_by: $currentUserEmail
|
||||
text: $text
|
||||
}
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_INTAKE_CHECKLIST = gql`
|
||||
mutation DELETE_INTAKE($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { intakechecklist: null }
|
||||
) {
|
||||
id
|
||||
intakechecklist
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_DELIVERY_CHECKLIST = gql`
|
||||
mutation DELETE_DELIVERY($jobId: uuid!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { deliverchecklist: null }
|
||||
) {
|
||||
id
|
||||
deliverchecklist
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MARK_JOB_FOR_REEXPORT = gql`
|
||||
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!, $default_invoiced: String!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { date_exported: null, status: $default_invoiced }
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
status
|
||||
date_invoiced
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MARK_JOB_AS_EXPORTED = gql`
|
||||
mutation MARK_JOB_AS_EXPORTED(
|
||||
$jobId: uuid!
|
||||
$date_exported: timestamptz!
|
||||
$default_exported: String!
|
||||
) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: { date_exported: $date_exported, status: $default_exported }
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
date_invoiced
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MARK_JOB_AS_UNINVOICED = gql`
|
||||
mutation MARK_JOB_AS_UNINVOICED($jobId: uuid!, $default_delivered: String!) {
|
||||
update_jobs_by_pk(
|
||||
pk_columns: { id: $jobId }
|
||||
_set: {
|
||||
date_exported: null
|
||||
date_invoiced: null
|
||||
status: $default_delivered
|
||||
}
|
||||
) {
|
||||
id
|
||||
date_exported
|
||||
date_invoiced
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_COMPLETED_TASKS = gql`
|
||||
query QUERY_COMPLETED_TASKS($jobid: uuid!) {
|
||||
jobs_by_pk(id: $jobid) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {useParams} from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobCalculateTotals from "../../components/job-calculate-totals/job-calculate-totals.component";
|
||||
import ScoreboardAddButton from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
|
||||
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
|
||||
import JobsAdminClass from "../../components/jobs-admin-class/jobs-admin-class.component";
|
||||
import JobsAdminDatesChange from "../../components/jobs-admin-dates/jobs-admin-dates.component";
|
||||
import JobsAdminDeleteIntake from "../../components/jobs-admin-delete-intake/jobs-admin-delete-intake.component";
|
||||
@@ -16,9 +17,8 @@ import JobAdminOwnerReassociate
|
||||
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
|
||||
import JobAdminVehicleReassociate
|
||||
from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
|
||||
import JobsAdminRemoveAR from "../../components/jobs-admin-remove-ar/jobs-admin-remove-ar.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
|
||||
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {GET_JOB_BY_PK} from "../../graphql/jobs.queries";
|
||||
@@ -103,6 +103,7 @@ export function JobsCloseContainer({setBreadcrumbs, setSelectedHeader}) {
|
||||
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminStatus job={data ? data.jobs_by_pk : {}}/>
|
||||
<JobsAdminRemoveAR job={data ? data.jobs_by_pk : {}} />
|
||||
</Space>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
@@ -50,7 +50,8 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import _ from "lodash";
|
||||
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
||||
import {DateTimeFormat} from "./../../utils/DateFormatter";
|
||||
import {DateTimeFormat} from "../../utils/DateFormatter";
|
||||
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -338,6 +339,12 @@ export function JobsDetailPage({
|
||||
label: t("menus.jobsdetail.labor"),
|
||||
children: <JobsDetailLaborContainer job={job} jobId={job.id}/>,
|
||||
},
|
||||
{
|
||||
key: 'lifecycle',
|
||||
icon: <BarsOutlined />,
|
||||
label: t('menus.jobsdetail.lifecycle'),
|
||||
children: <JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses}/>
|
||||
},
|
||||
{
|
||||
key: "dates",
|
||||
icon: <CalendarFilled/>,
|
||||
|
||||
@@ -100,25 +100,26 @@
|
||||
},
|
||||
"audit_trail": {
|
||||
"messages": {
|
||||
"admin_job_remove_from_ar": "ADMIN: Remove from AR updated to: {{status}}",
|
||||
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
|
||||
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
|
||||
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
|
||||
"admin_jobunvoid": "ADMIN: Job has been unvoided.",
|
||||
"assignedlinehours": "Assigned job lines totaling {{hours}} units to {{team}}.",
|
||||
"alerttoggle": "Alert Toggle set to {{status}}",
|
||||
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
|
||||
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
|
||||
"assignedlinehours": "Assigned job lines totaling {{hours}} units to {{team}}.",
|
||||
"billposted": "Bill with invoice number {{invoice_number}} posted.",
|
||||
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
|
||||
"failedpayment": "Failed payment attempt.",
|
||||
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
||||
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
||||
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
||||
"jobinvoiced": "Job has been invoiced.",
|
||||
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
||||
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
|
||||
"jobimported": "Job imported.",
|
||||
"jobinproductionchange": "Job production status set to {{inproduction}}",
|
||||
"jobinvoiced": "Job has been invoiced.",
|
||||
"jobioucreated": "IOU Created.",
|
||||
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
|
||||
"jobnoteadded": "Note added to Job.",
|
||||
@@ -335,6 +336,9 @@
|
||||
"md_ded_notes": "Deductible Notes",
|
||||
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})",
|
||||
"md_from_emails": "Additional From Emails",
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
|
||||
},
|
||||
"md_hour_split": {
|
||||
"paint": "Paint Hour Split",
|
||||
"prep": "Prep Hour Split"
|
||||
@@ -357,9 +361,6 @@
|
||||
},
|
||||
"md_payment_types": "Payment Types",
|
||||
"md_referral_sources": "Referral Sources",
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
|
||||
},
|
||||
"md_tasks_presets": {
|
||||
"enable_tasks": "Enable Hour Flagging",
|
||||
"hourstype": "Hour Types",
|
||||
@@ -827,6 +828,10 @@
|
||||
"usage": "Usage",
|
||||
"vehicle": "Vehicle Description"
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "Not Ready",
|
||||
"ready": "Ready"
|
||||
},
|
||||
"status": {
|
||||
"in": "Available",
|
||||
"inservice": "In Service",
|
||||
@@ -836,10 +841,6 @@
|
||||
},
|
||||
"successes": {
|
||||
"saved": "Courtesy Car saved successfully."
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "Not Ready",
|
||||
"ready": "Ready"
|
||||
}
|
||||
},
|
||||
"csi": {
|
||||
@@ -1227,6 +1228,31 @@
|
||||
"updated": "Inventory line updated."
|
||||
}
|
||||
},
|
||||
"job_lifecycle": {
|
||||
"columns": {
|
||||
"duration": "Duration",
|
||||
"end": "End",
|
||||
"relative_end": "Relative End",
|
||||
"relative_start": "Relative Start",
|
||||
"start": "Start",
|
||||
"value": "Value"
|
||||
},
|
||||
"content": {
|
||||
"current_status_accumulated_time": "Current Status Accumulated Time",
|
||||
"data_unavailable": " There is currently no Lifecycle data for this Job.",
|
||||
"legend_title": "Legend",
|
||||
"loading": "Loading Job Timelines....",
|
||||
"not_available": "N/A",
|
||||
"previous_status_accumulated_time": "Previous Status Accumulated Time",
|
||||
"title": "Job Lifecycle Component",
|
||||
"title_durations": "Historical Status Durations",
|
||||
"title_loading": "Loading",
|
||||
"title_transitions": "Transitions"
|
||||
},
|
||||
"errors": {
|
||||
"fetch": "Error getting Job Lifecycle Data"
|
||||
}
|
||||
},
|
||||
"job_payments": {
|
||||
"buttons": {
|
||||
"goback": "Go Back",
|
||||
@@ -1267,7 +1293,8 @@
|
||||
"fields": {
|
||||
"act_price": "Retail Price",
|
||||
"ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)",
|
||||
"assigned_team": "Team {{name}}",
|
||||
"assigned_team": "Team",
|
||||
"assigned_team_name": "Team {{name}}",
|
||||
"create_ppc": "Create PPC?",
|
||||
"db_price": "List Price",
|
||||
"lbr_types": {
|
||||
@@ -1898,6 +1925,7 @@
|
||||
},
|
||||
"reconciliationheader": "Parts & Sublet Reconciliation",
|
||||
"relatedros": "Related ROs",
|
||||
"remove_from_ar": "Remove from AR",
|
||||
"returntotals": "Return Totals",
|
||||
"rosaletotal": "RO Parts Total",
|
||||
"sale_additional": "Sales - Additional",
|
||||
@@ -2078,6 +2106,7 @@
|
||||
"general": "General",
|
||||
"insurance": "Insurance Information",
|
||||
"labor": "Labor",
|
||||
"lifecycle": "Lifecycle",
|
||||
"partssublet": "Parts & Bills",
|
||||
"rates": "Rates",
|
||||
"repairdata": "Repair Data",
|
||||
@@ -2657,6 +2686,7 @@
|
||||
},
|
||||
"templates": {
|
||||
"anticipated_revenue": "Anticipated Revenue",
|
||||
"ar_aging": "AR Aging",
|
||||
"attendance_detail": "Attendance (All Employees)",
|
||||
"attendance_employee": "Employee Attendance",
|
||||
"attendance_summary": "Attendance Summary (All Employees)",
|
||||
@@ -3032,8 +3062,8 @@
|
||||
"shop-templates": "Shop Templates | $t(titles.app)",
|
||||
"shop_vendors": "Vendors | $t(titles.app)",
|
||||
"techconsole": "Technician Console | $t(titles.app)",
|
||||
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
|
||||
"techjobclock": "Technician Job Clock | $t(titles.app)",
|
||||
"techjoblookup": "Technician Job Lookup | $t(titles.app)",
|
||||
"techshiftclock": "Technician Shift Clock | $t(titles.app)",
|
||||
"temporarydocs": "Temporary Documents | $t(titles.app)",
|
||||
"timetickets": "Time Tickets | $t(titles.app)",
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
},
|
||||
"audit_trail": {
|
||||
"messages": {
|
||||
"admin_job_remove_from_ar": "",
|
||||
"admin_jobmarkexported": "",
|
||||
"admin_jobmarkforreexport": "",
|
||||
"admin_jobuninvoice": "",
|
||||
@@ -114,11 +115,11 @@
|
||||
"jobassignmentchange": "",
|
||||
"jobassignmentremoved": "",
|
||||
"jobchecklist": "",
|
||||
"jobinvoiced": "",
|
||||
"jobconverted": "",
|
||||
"jobfieldchanged": "",
|
||||
"jobimported": "",
|
||||
"jobinproductionchange": "",
|
||||
"jobinvoiced": "",
|
||||
"jobioucreated": "",
|
||||
"jobmodifylbradj": "",
|
||||
"jobnoteadded": "",
|
||||
@@ -262,9 +263,6 @@
|
||||
"address1": "",
|
||||
"address2": "",
|
||||
"appt_alt_transport": "",
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": ""
|
||||
},
|
||||
"appt_colors": {
|
||||
"color": "",
|
||||
"label": ""
|
||||
@@ -338,6 +336,9 @@
|
||||
"md_ded_notes": "",
|
||||
"md_email_cc": "",
|
||||
"md_from_emails": "",
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": ""
|
||||
},
|
||||
"md_hour_split": {
|
||||
"paint": "",
|
||||
"prep": ""
|
||||
@@ -827,6 +828,10 @@
|
||||
"usage": "",
|
||||
"vehicle": ""
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "",
|
||||
"ready": ""
|
||||
},
|
||||
"status": {
|
||||
"in": "",
|
||||
"inservice": "",
|
||||
@@ -836,10 +841,6 @@
|
||||
},
|
||||
"successes": {
|
||||
"saved": ""
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "",
|
||||
"ready": ""
|
||||
}
|
||||
},
|
||||
"csi": {
|
||||
@@ -1227,6 +1228,31 @@
|
||||
"updated": ""
|
||||
}
|
||||
},
|
||||
"job_lifecycle": {
|
||||
"columns": {
|
||||
"duration": "",
|
||||
"end": "",
|
||||
"relative_end": "",
|
||||
"relative_start": "",
|
||||
"start": "",
|
||||
"value": ""
|
||||
},
|
||||
"content": {
|
||||
"current_status_accumulated_time": "",
|
||||
"data_unavailable": "",
|
||||
"legend_title": "",
|
||||
"loading": "",
|
||||
"not_available": "",
|
||||
"previous_status_accumulated_time": "",
|
||||
"title": "",
|
||||
"title_durations": "",
|
||||
"title_loading": "",
|
||||
"title_transitions": ""
|
||||
},
|
||||
"errors": {
|
||||
"fetch": "Error al obtener los datos del ciclo de vida del trabajo"
|
||||
}
|
||||
},
|
||||
"job_payments": {
|
||||
"buttons": {
|
||||
"goback": "",
|
||||
@@ -1268,6 +1294,7 @@
|
||||
"act_price": "Precio actual",
|
||||
"ah_detail_line": "",
|
||||
"assigned_team": "",
|
||||
"assigned_team_name": "",
|
||||
"create_ppc": "",
|
||||
"db_price": "Precio de base de datos",
|
||||
"lbr_types": {
|
||||
@@ -1898,6 +1925,7 @@
|
||||
},
|
||||
"reconciliationheader": "",
|
||||
"relatedros": "",
|
||||
"remove_from_ar": "",
|
||||
"returntotals": "",
|
||||
"rosaletotal": "",
|
||||
"sale_additional": "",
|
||||
@@ -2078,6 +2106,7 @@
|
||||
"general": "",
|
||||
"insurance": "",
|
||||
"labor": "Labor",
|
||||
"lifecycle": "",
|
||||
"partssublet": "Piezas / Subarrendamiento",
|
||||
"rates": "",
|
||||
"repairdata": "Datos de reparación",
|
||||
@@ -2657,6 +2686,7 @@
|
||||
},
|
||||
"templates": {
|
||||
"anticipated_revenue": "",
|
||||
"ar_aging": "",
|
||||
"attendance_detail": "",
|
||||
"attendance_employee": "",
|
||||
"attendance_summary": "",
|
||||
@@ -3032,8 +3062,8 @@
|
||||
"shop-templates": "",
|
||||
"shop_vendors": "Vendedores | $t(titles.app)",
|
||||
"techconsole": "$t(titles.app)",
|
||||
"techjoblookup": "$t(titles.app)",
|
||||
"techjobclock": "$t(titles.app)",
|
||||
"techjoblookup": "$t(titles.app)",
|
||||
"techshiftclock": "$t(titles.app)",
|
||||
"temporarydocs": "",
|
||||
"timetickets": "",
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
},
|
||||
"audit_trail": {
|
||||
"messages": {
|
||||
"admin_job_remove_from_ar": "",
|
||||
"admin_jobmarkexported": "",
|
||||
"admin_jobmarkforreexport": "",
|
||||
"admin_jobuninvoice": "",
|
||||
@@ -114,11 +115,11 @@
|
||||
"jobassignmentchange": "",
|
||||
"jobassignmentremoved": "",
|
||||
"jobchecklist": "",
|
||||
"jobinvoiced": "",
|
||||
"jobconverted": "",
|
||||
"jobfieldchanged": "",
|
||||
"jobimported": "",
|
||||
"jobinproductionchange": "",
|
||||
"jobinvoiced": "",
|
||||
"jobioucreated": "",
|
||||
"jobmodifylbradj": "",
|
||||
"jobnoteadded": "",
|
||||
@@ -335,13 +336,13 @@
|
||||
"md_ded_notes": "",
|
||||
"md_email_cc": "",
|
||||
"md_from_emails": "",
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": ""
|
||||
},
|
||||
"md_hour_split": {
|
||||
"paint": "",
|
||||
"prep": ""
|
||||
},
|
||||
"md_functionality_toggles": {
|
||||
"parts_queue_toggle": ""
|
||||
},
|
||||
"md_ins_co": {
|
||||
"city": "",
|
||||
"name": "",
|
||||
@@ -827,6 +828,10 @@
|
||||
"usage": "",
|
||||
"vehicle": ""
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "",
|
||||
"ready": ""
|
||||
},
|
||||
"status": {
|
||||
"in": "",
|
||||
"inservice": "",
|
||||
@@ -836,10 +841,6 @@
|
||||
},
|
||||
"successes": {
|
||||
"saved": ""
|
||||
},
|
||||
"readiness": {
|
||||
"notready": "",
|
||||
"ready": ""
|
||||
}
|
||||
},
|
||||
"csi": {
|
||||
@@ -1227,6 +1228,31 @@
|
||||
"updated": ""
|
||||
}
|
||||
},
|
||||
"job_lifecycle": {
|
||||
"columns": {
|
||||
"duration": "",
|
||||
"end": "",
|
||||
"relative_end": "",
|
||||
"relative_start": "",
|
||||
"start": "",
|
||||
"value": ""
|
||||
},
|
||||
"content": {
|
||||
"current_status_accumulated_time": "",
|
||||
"data_unavailable": "",
|
||||
"legend_title": "",
|
||||
"loading": "",
|
||||
"not_available": "",
|
||||
"previous_status_accumulated_time": "",
|
||||
"title": "",
|
||||
"title_durations": "",
|
||||
"title_loading": "",
|
||||
"title_transitions": ""
|
||||
},
|
||||
"errors": {
|
||||
"fetch": "Erreur lors de l'obtention des données du cycle de vie des tâches"
|
||||
}
|
||||
},
|
||||
"job_payments": {
|
||||
"buttons": {
|
||||
"goback": "",
|
||||
@@ -1268,6 +1294,7 @@
|
||||
"act_price": "Prix actuel",
|
||||
"ah_detail_line": "",
|
||||
"assigned_team": "",
|
||||
"assigned_team_name": "",
|
||||
"create_ppc": "",
|
||||
"db_price": "Prix de la base de données",
|
||||
"lbr_types": {
|
||||
@@ -1898,6 +1925,7 @@
|
||||
},
|
||||
"reconciliationheader": "",
|
||||
"relatedros": "",
|
||||
"remove_from_ar": "",
|
||||
"returntotals": "",
|
||||
"rosaletotal": "",
|
||||
"sale_additional": "",
|
||||
@@ -2078,6 +2106,7 @@
|
||||
"general": "",
|
||||
"insurance": "",
|
||||
"labor": "La main d'oeuvre",
|
||||
"lifecycle": "",
|
||||
"partssublet": "Pièces / Sous-location",
|
||||
"rates": "",
|
||||
"repairdata": "Données de réparation",
|
||||
@@ -2657,6 +2686,7 @@
|
||||
},
|
||||
"templates": {
|
||||
"anticipated_revenue": "",
|
||||
"ar_aging": "",
|
||||
"attendance_detail": "",
|
||||
"attendance_employee": "",
|
||||
"attendance_summary": "",
|
||||
@@ -3032,8 +3062,8 @@
|
||||
"shop-templates": "",
|
||||
"shop_vendors": "Vendeurs | $t(titles.app)",
|
||||
"techconsole": "$t(titles.app)",
|
||||
"techjoblookup": "$t(titles.app)",
|
||||
"techjobclock": "$t(titles.app)",
|
||||
"techjoblookup": "$t(titles.app)",
|
||||
"techshiftclock": "$t(titles.app)",
|
||||
"temporarydocs": "",
|
||||
"timetickets": "",
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
import i18n from "i18next";
|
||||
|
||||
const AuditTrailMapping = {
|
||||
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
|
||||
admin_job_remove_from_ar: (status) =>
|
||||
i18n.t("audit_trail.messages.admin_job_remove_from_ar", { status }),
|
||||
admin_jobfieldchange: (field, value) =>
|
||||
"ADMIN: " +
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
admin_jobstatuschange: (status) =>
|
||||
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
alertToggle: (status) =>
|
||||
i18n.t("audit_trail.messages.alerttoggle", { status }),
|
||||
appointmentcancel: (lost_sale_reason) =>
|
||||
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
|
||||
appointmentinsert: (start) =>
|
||||
i18n.t("audit_trail.messages.appointmentinsert", { start }),
|
||||
jobstatuschange: (status) =>
|
||||
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
admin_jobstatuschange: (status) =>
|
||||
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||
jobinvoiced: () =>
|
||||
i18n.t("audit_trail.messages.jobinvoiced"),
|
||||
jobconverted: (ro_number) =>
|
||||
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
||||
jobfieldchange: (field, value) =>
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
admin_jobfieldchange: (field, value) =>
|
||||
"ADMIN: " +
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
jobspartsorder: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
||||
jobspartsreturn: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
|
||||
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||
billposted: (invoice_number) =>
|
||||
i18n.t("audit_trail.messages.billposted", { invoice_number }),
|
||||
billupdated: (invoice_number) =>
|
||||
@@ -35,10 +22,18 @@ const AuditTrailMapping = {
|
||||
i18n.t("audit_trail.messages.jobassignmentchange", { operation, name }),
|
||||
jobassignmentremoved: (operation) =>
|
||||
i18n.t("audit_trail.messages.jobassignmentremoved", { operation }),
|
||||
jobinproductionchange: (inproduction) =>
|
||||
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
||||
jobchecklist: (type, inproduction, status) =>
|
||||
i18n.t("audit_trail.messages.jobchecklist", { type, inproduction, status }),
|
||||
jobconverted: (ro_number) =>
|
||||
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
|
||||
jobfieldchange: (field, value) =>
|
||||
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
|
||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||
jobinproductionchange: (inproduction) =>
|
||||
i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
||||
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) =>
|
||||
i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
|
||||
@@ -51,6 +46,13 @@ const AuditTrailMapping = {
|
||||
failedpayment: () => i18n.t("audit_trail.messages.failedpayment"),
|
||||
assignedlinehours: (team, hours) =>
|
||||
i18n.t("audit_trail.messages.assignedlinehours", { team, hours }),
|
||||
jobspartsorder: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
|
||||
jobspartsreturn: (order_number) =>
|
||||
i18n.t("audit_trail.messages.jobspartsreturn", { order_number }),
|
||||
jobstatuschange: (status) =>
|
||||
i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||
};
|
||||
|
||||
export default AuditTrailMapping;
|
||||
|
||||
@@ -17,6 +17,9 @@ export function DateTimeFormatter(props) {
|
||||
)
|
||||
: null;
|
||||
}
|
||||
export function DateTimeFormatterFunction(date) {
|
||||
return moment(date).format("MM/DD/YYYY hh:mm a");
|
||||
}
|
||||
export function TimeFormatter(props) {
|
||||
return props.children
|
||||
? dayjs(props.children).format(props.format ? props.format : "hh:mm a")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,6 @@
|
||||
- function:
|
||||
name: jobs_ar_summary
|
||||
schema: public
|
||||
- function:
|
||||
name: search_bills
|
||||
schema: public
|
||||
|
||||
@@ -2442,7 +2442,7 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- address
|
||||
- buisness_name
|
||||
- business_name
|
||||
- date_accepted
|
||||
- eulaid
|
||||
- first_name
|
||||
@@ -2454,7 +2454,7 @@
|
||||
permission:
|
||||
columns:
|
||||
- address
|
||||
- buisness_name
|
||||
- business_name
|
||||
- first_name
|
||||
- last_name
|
||||
- phone_number
|
||||
@@ -2673,6 +2673,34 @@
|
||||
- table:
|
||||
name: ioevents
|
||||
schema: public
|
||||
- table:
|
||||
name: job_ar_schema
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: id
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- id
|
||||
- ro_number
|
||||
- clm_total
|
||||
- total_payments
|
||||
- balance
|
||||
- date_invoiced
|
||||
- shopid
|
||||
filter:
|
||||
job:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- table:
|
||||
name: job_conversations
|
||||
schema: public
|
||||
@@ -3812,6 +3840,7 @@
|
||||
- referral_source
|
||||
- referral_source_extra
|
||||
- regie_number
|
||||
- remove_from_ar
|
||||
- ro_number
|
||||
- scheduled_completion
|
||||
- scheduled_delivery
|
||||
@@ -4093,6 +4122,7 @@
|
||||
- referral_source
|
||||
- referral_source_extra
|
||||
- regie_number
|
||||
- remove_from_ar
|
||||
- ro_number
|
||||
- scheduled_completion
|
||||
- scheduled_delivery
|
||||
@@ -4159,6 +4189,11 @@
|
||||
- name: job_status_transition
|
||||
definition:
|
||||
enable_manual: true
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- status
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
|
||||
33
hasura/migrations/1705693552101_run_sql_migration/down.sql
Normal file
33
hasura/migrations/1705693552101_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF jobs
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.shopid,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- p.total_payments,
|
||||
-- j.clm_total - p.total_payments as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- sum(p.amount) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid ;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
31
hasura/migrations/1705693552101_run_sql_migration/up.sql
Normal file
31
hasura/migrations/1705693552101_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF jobs
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.shopid,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
p.total_payments,
|
||||
j.clm_total - p.total_payments as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
sum(p.amount) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid ;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."job_ar_schema";
|
||||
@@ -0,0 +1 @@
|
||||
CREATE TABLE "public"."job_ar_schema" ("id" uuid NOT NULL, "ro_number" text, "clm_total" numeric NOT NULL, "total_payments" numeric NOT NULL DEFAULT 0, "balance" numeric NOT NULL DEFAULT 0, PRIMARY KEY ("id") );
|
||||
34
hasura/migrations/1705693896379_run_sql_migration/down.sql
Normal file
34
hasura/migrations/1705693896379_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- DROP FUNCTION public.jobs_ar_summary;
|
||||
--
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- p.total_payments,
|
||||
-- j.clm_total - p.total_payments as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- sum(p.amount) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid ;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
32
hasura/migrations/1705693896379_run_sql_migration/up.sql
Normal file
32
hasura/migrations/1705693896379_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
DROP FUNCTION public.jobs_ar_summary;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
p.total_payments,
|
||||
j.clm_total - p.total_payments as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
sum(p.amount) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid ;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
32
hasura/migrations/1705694146809_run_sql_migration/down.sql
Normal file
32
hasura/migrations/1705694146809_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- p.total_payments,
|
||||
-- j.clm_total - p.total_payments as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid ;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
30
hasura/migrations/1705694146809_run_sql_migration/up.sql
Normal file
30
hasura/migrations/1705694146809_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
p.total_payments,
|
||||
j.clm_total - p.total_payments as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid ;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
32
hasura/migrations/1705694176838_run_sql_migration/down.sql
Normal file
32
hasura/migrations/1705694176838_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- p.total_payments,
|
||||
-- j.clm_total - p.total_payments as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid ;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
30
hasura/migrations/1705694176838_run_sql_migration/up.sql
Normal file
30
hasura/migrations/1705694176838_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
p.total_payments,
|
||||
j.clm_total - p.total_payments as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid ;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
32
hasura/migrations/1705696451631_run_sql_migration/down.sql
Normal file
32
hasura/migrations/1705696451631_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid ;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
30
hasura/migrations/1705696451631_run_sql_migration/up.sql
Normal file
30
hasura/migrations/1705696451631_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid ;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."jobs" add column "remove_from_ar" boolean
|
||||
-- not null default 'false';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."jobs" add column "remove_from_ar" boolean
|
||||
not null default 'false';
|
||||
33
hasura/migrations/1705698426997_run_sql_migration/down.sql
Normal file
33
hasura/migrations/1705698426997_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
31
hasura/migrations/1705698426997_run_sql_migration/up.sql
Normal file
31
hasura/migrations/1705698426997_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."job_ar_schema" add column "date_invoiced" timestamptz
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."job_ar_schema" add column "date_invoiced" timestamptz
|
||||
null;
|
||||
34
hasura/migrations/1705698534883_run_sql_migration/down.sql
Normal file
34
hasura/migrations/1705698534883_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- j.date_invoiced,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
32
hasura/migrations/1705698534883_run_sql_migration/up.sql
Normal file
32
hasura/migrations/1705698534883_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
j.date_invoiced,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
34
hasura/migrations/1705698593644_run_sql_migration/down.sql
Normal file
34
hasura/migrations/1705698593644_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
32
hasura/migrations/1705698593644_run_sql_migration/up.sql
Normal file
32
hasura/migrations/1705698593644_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
34
hasura/migrations/1705698876975_run_sql_migration/down.sql
Normal file
34
hasura/migrations/1705698876975_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null and balance > 0;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
32
hasura/migrations/1705698876975_run_sql_migration/up.sql
Normal file
32
hasura/migrations/1705698876975_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null and balance > 0;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
34
hasura/migrations/1705700945994_run_sql_migration/down.sql
Normal file
34
hasura/migrations/1705700945994_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
32
hasura/migrations/1705700945994_run_sql_migration/up.sql
Normal file
32
hasura/migrations/1705700945994_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" rename column "business_name" to "buisness_name";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" rename column "buisness_name" to "business_name";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" alter column "phone_number" set not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" alter column "phone_number" drop not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" alter column "address" set not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."eula_acceptances" alter column "address" drop not null;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."job_ar_schema" add column "shopid" uuid
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."job_ar_schema" add column "shopid" uuid
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."job_ar_schema" drop constraint "job_ar_schema_id_fkey";
|
||||
@@ -0,0 +1,5 @@
|
||||
alter table "public"."job_ar_schema"
|
||||
add constraint "job_ar_schema_id_fkey"
|
||||
foreign key ("id")
|
||||
references "public"."jobs"
|
||||
("id") on update restrict on delete restrict;
|
||||
35
hasura/migrations/1705952926623_run_sql_migration/down.sql
Normal file
35
hasura/migrations/1705952926623_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced,
|
||||
-- j.shopid
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
33
hasura/migrations/1705952926623_run_sql_migration/up.sql
Normal file
33
hasura/migrations/1705952926623_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced,
|
||||
j.shopid
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) > 0;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
35
hasura/migrations/1706207204357_run_sql_migration/down.sql
Normal file
35
hasura/migrations/1706207204357_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced,
|
||||
-- j.shopid
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) != 0;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
33
hasura/migrations/1706207204357_run_sql_migration/up.sql
Normal file
33
hasura/migrations/1706207204357_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced,
|
||||
j.shopid
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) != 0;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
35
hasura/migrations/1706207267558_run_sql_migration/down.sql
Normal file
35
hasura/migrations/1706207267558_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
-- RETURNS SETOF job_ar_schema
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
--
|
||||
-- RETURN query
|
||||
-- select
|
||||
-- j.id,
|
||||
-- j.ro_number,
|
||||
-- j.clm_total,
|
||||
-- coalesce (p.total_payments,0) as total_payments,
|
||||
-- j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
-- j.date_invoiced,
|
||||
-- j.shopid
|
||||
-- from
|
||||
-- jobs j
|
||||
-- left join (
|
||||
-- select
|
||||
-- p.jobid,
|
||||
-- coalesce (sum(p.amount),0) as total_payments
|
||||
-- from
|
||||
-- payments p
|
||||
-- group by
|
||||
-- p.jobid
|
||||
-- ) p on
|
||||
-- j.id = p.jobid
|
||||
-- where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) != 0;
|
||||
--
|
||||
--
|
||||
-- END
|
||||
-- $function$;
|
||||
33
hasura/migrations/1706207267558_run_sql_migration/up.sql
Normal file
33
hasura/migrations/1706207267558_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
CREATE OR REPLACE FUNCTION public.jobs_ar_summary ()
|
||||
RETURNS SETOF job_ar_schema
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
|
||||
RETURN query
|
||||
select
|
||||
j.id,
|
||||
j.ro_number,
|
||||
j.clm_total,
|
||||
coalesce (p.total_payments,0) as total_payments,
|
||||
j.clm_total - coalesce (p.total_payments,0) as balance,
|
||||
j.date_invoiced,
|
||||
j.shopid
|
||||
from
|
||||
jobs j
|
||||
left join (
|
||||
select
|
||||
p.jobid,
|
||||
coalesce (sum(p.amount),0) as total_payments
|
||||
from
|
||||
payments p
|
||||
group by
|
||||
p.jobid
|
||||
) p on
|
||||
j.id = p.jobid
|
||||
where j.remove_from_ar = false and j.date_invoiced is not null and j.clm_total - coalesce (p.total_payments,0) != 0;
|
||||
|
||||
|
||||
END
|
||||
$function$;
|
||||
47574
new_bodyshop_translations.babel
Normal file
47574
new_bodyshop_translations.babel
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"setup": "rm -rf node_modules && npm i && cd client && rm -rf node_modules && npm i",
|
||||
"setup:win": "rimraf node_modules && npm i && cd client && rimraf node_modules && npm i",
|
||||
"admin": "cd admin && npm start",
|
||||
"client": "cd client && npm start",
|
||||
"server": "nodemon server.js",
|
||||
@@ -50,6 +51,7 @@
|
||||
"nodemailer": "^6.9.8",
|
||||
"phone": "^3.1.42",
|
||||
"recursive-diff": "^1.0.9",
|
||||
"rimraf": "^5.0.5",
|
||||
"soap": "^1.0.0",
|
||||
"socket.io": "^4.7.4",
|
||||
"ssh2-sftp-client": "^9.1.0",
|
||||
|
||||
361
server.js
361
server.js
@@ -1,314 +1,97 @@
|
||||
// Import core modules
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const bodyParser = require("body-parser");
|
||||
const path = require("path");
|
||||
const compression = require("compression");
|
||||
const twilio = require("twilio");
|
||||
const logger = require("./server/utils/logger");
|
||||
const fb = require("./server/firebase/firebase-handler");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const multer = require("multer");
|
||||
const upload = multer();
|
||||
//var enforce = require("express-sslify");
|
||||
const http = require("http");
|
||||
const {Server} = require("socket.io");
|
||||
|
||||
// Load environment variables
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
// Import custom utilities and handlers
|
||||
const logger = require("./server/utils/logger");
|
||||
|
||||
// Express app and server setup
|
||||
const app = express();
|
||||
const port = process.env.PORT || 5000;
|
||||
//const port = 5000;
|
||||
|
||||
const http = require("http");
|
||||
const server = http.createServer(app);
|
||||
const { Server } = require("socket.io");
|
||||
const io = new Server(server, {
|
||||
path: "/ws",
|
||||
cors: {
|
||||
origin: [
|
||||
"https://test.imex.online",
|
||||
"https://www.test.imex.online",
|
||||
"http://localhost:3000",
|
||||
"https://imex.online",
|
||||
"https://www.imex.online",
|
||||
"https://romeonline.io",
|
||||
"https://www.romeonline.io",
|
||||
"https://beta.test.romeonline.io",
|
||||
"https://www.beta.test.romeonline.io",
|
||||
"https://beta.romeonline.io",
|
||||
"https://www.beta.romeonline.io",
|
||||
],
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true,
|
||||
exposedHeaders: ["set-cookie"],
|
||||
},
|
||||
path: "/ws",
|
||||
cors: {
|
||||
origin: [
|
||||
"https://test.imex.online",
|
||||
"https://www.test.imex.online",
|
||||
"http://localhost:3000",
|
||||
"https://imex.online",
|
||||
"https://www.imex.online",
|
||||
"https://romeonline.io",
|
||||
"https://www.romeonline.io",
|
||||
"https://beta.test.romeonline.io",
|
||||
"https://www.beta.test.romeonline.io",
|
||||
"https://beta.romeonline.io",
|
||||
"https://www.beta.romeonline.io",
|
||||
],
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true,
|
||||
exposedHeaders: ["set-cookie"],
|
||||
},
|
||||
});
|
||||
exports.io = io;
|
||||
|
||||
require("./server/web-sockets/web-socket");
|
||||
// app.set('trust proxy', true)
|
||||
// app.use(fb.validateFirebaseIdToken);
|
||||
|
||||
|
||||
// Middleware
|
||||
app.use(compression());
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.json({ limit: "50mb" }));
|
||||
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
|
||||
// app.use(enforce.HTTPS({ trustProtoHeader: true }));
|
||||
app.use(
|
||||
cors({ credentials: true, exposedHeaders: ["set-cookie"] })
|
||||
// cors({
|
||||
// credentials: true,
|
||||
// origin: [
|
||||
// "https://test.imex.online",
|
||||
// "http://localhost:3000",
|
||||
// "https://imex.online",
|
||||
// ],
|
||||
// })
|
||||
);
|
||||
app.use(bodyParser.json({limit: "50mb"}));
|
||||
app.use(bodyParser.urlencoded({limit: "50mb", extended: true}));
|
||||
app.use(cors({credentials: true, exposedHeaders: ["set-cookie"]}));
|
||||
|
||||
//Email Based Paths.
|
||||
var sendEmail = require("./server/email/sendemail.js");
|
||||
app.post("/sendemail", fb.validateFirebaseIdToken, sendEmail.sendEmail);
|
||||
app.post("/emailbounce", bodyParser.text(), sendEmail.emailBounce);
|
||||
|
||||
//Test route to ensure Express is responding.
|
||||
app.get("/test", async function (req, res) {
|
||||
const commit = require("child_process").execSync(
|
||||
"git rev-parse --short HEAD"
|
||||
);
|
||||
// console.log(app.get('trust proxy'));
|
||||
// console.log("remoteAddress", req.socket.remoteAddress);
|
||||
// console.log("X-Forwarded-For", req.header('x-forwarded-for'));
|
||||
logger.log("test-api-status", "DEBUG", "api", { commit });
|
||||
// sendEmail.sendServerEmail({
|
||||
// subject: `API Check - ${process.env.NODE_ENV}`,
|
||||
// text: `Server API check has come in. Remote IP: ${req.socket.remoteAddress}, X-Forwarded-For: ${req.header('x-forwarded-for')}`,
|
||||
// });
|
||||
sendEmail.sendServerEmail({
|
||||
subject: `API Check - ${process.env.NODE_ENV}`,
|
||||
text: `Server API check has come in.`,
|
||||
});
|
||||
res.status(200).send(`OK - ${commit}`);
|
||||
// Helper middleware
|
||||
app.use((req, res, next) => {
|
||||
req.logger = logger;
|
||||
next();
|
||||
});
|
||||
|
||||
//Accounting Qbxml
|
||||
const accountQbxml = require("./server/accounting/qbxml/qbxml");
|
||||
app.post(
|
||||
"/accounting/qbxml/receivables",
|
||||
fb.validateFirebaseIdToken,
|
||||
accountQbxml.receivables
|
||||
);
|
||||
app.post(
|
||||
"/accounting/qbxml/payables",
|
||||
fb.validateFirebaseIdToken,
|
||||
accountQbxml.payables
|
||||
);
|
||||
app.post(
|
||||
"/accounting/qbxml/payments",
|
||||
fb.validateFirebaseIdToken,
|
||||
accountQbxml.payments
|
||||
);
|
||||
// Route groupings
|
||||
app.use('/', require("./server/routes/miscellaneousRoutes"));
|
||||
app.use("/notifications", require("./server/routes/notificationsRoutes"));
|
||||
app.use("/render", require("./server/routes/renderRoutes"));
|
||||
app.use('/mixdata', require("./server/routes/mixDataRoutes"));
|
||||
app.use('/accounting', require("./server/routes/accountingRoutes"));
|
||||
app.use('/qbo', require("./server/routes/qboRoutes"));
|
||||
app.use('/media', require("./server/routes/mediaRoutes"));
|
||||
app.use('/sms', require("./server/routes/smsRoutes"));
|
||||
app.use('/job', require("./server/routes/jobRoutes"));
|
||||
app.use('/scheduling', require("./server/routes/schedulingRoutes"));
|
||||
app.use('/utils', require("./server/routes/utilRoutes"));
|
||||
app.use('/data', require("./server/routes/dataRoutes"));
|
||||
app.use('/adm', require("./server/routes/adminRoutes"));
|
||||
app.use('/tech', require("./server/routes/techRoutes"));
|
||||
app.use('/intellipay', require("./server/routes/intellipayRoutes"));
|
||||
app.use('/cdk', require("./server/routes/cdkRoutes"));
|
||||
app.use('/payroll', require("./server/routes/payrollRoutes"));
|
||||
|
||||
//Cloudinary Media Paths
|
||||
var media = require("./server/media/media");
|
||||
app.post(
|
||||
"/media/sign",
|
||||
fb.validateFirebaseIdToken,
|
||||
media.createSignedUploadURL
|
||||
);
|
||||
app.post("/media/download", fb.validateFirebaseIdToken, media.downloadFiles);
|
||||
app.post("/media/rename", fb.validateFirebaseIdToken, media.renameKeys);
|
||||
app.post("/media/delete", fb.validateFirebaseIdToken, media.deleteFiles);
|
||||
|
||||
//SMS/Twilio Paths
|
||||
var smsReceive = require("./server/sms/receive");
|
||||
app.post(
|
||||
"/sms/receive",
|
||||
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
||||
smsReceive.receive
|
||||
);
|
||||
var smsSend = require("./server/sms/send");
|
||||
app.post("/sms/send", fb.validateFirebaseIdToken, smsSend.send);
|
||||
var smsStatus = require("./server/sms/status");
|
||||
app.post(
|
||||
"/sms/status",
|
||||
twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" }),
|
||||
smsStatus.status
|
||||
);
|
||||
app.post(
|
||||
"/sms/markConversationRead",
|
||||
fb.validateFirebaseIdToken,
|
||||
smsStatus.markConversationRead
|
||||
);
|
||||
|
||||
var job = require("./server/job/job");
|
||||
app.post("/job/totals", fb.validateFirebaseIdToken, job.totals);
|
||||
app.post(
|
||||
"/job/statustransition",
|
||||
// fb.validateFirebaseIdToken,
|
||||
job.statustransition
|
||||
);
|
||||
app.post("/job/totalsssu", fb.validateFirebaseIdToken, job.totalsSsu);
|
||||
app.post("/job/costing", fb.validateFirebaseIdToken, job.costing);
|
||||
app.post("/job/costingmulti", fb.validateFirebaseIdToken, job.costingmulti);
|
||||
|
||||
var ppc = require("./server/ccc/partspricechange");
|
||||
app.post("/job/ppc", fb.validateFirebaseIdToken, ppc.generatePpc);
|
||||
|
||||
var partsScan = require("./server/parts-scan/parts-scan");
|
||||
app.post("/job/partsscan", fb.validateFirebaseIdToken, partsScan.partsScan);
|
||||
//Scheduling
|
||||
var scheduling = require("./server/scheduling/scheduling-job");
|
||||
app.post("/scheduling/job", fb.validateFirebaseIdToken, scheduling.job);
|
||||
|
||||
//Handlebars Paths for Email/Report Rendering
|
||||
// var renderHandlebars = require("./server/render/renderHandlebars");
|
||||
// app.post("/render", fb.validateFirebaseIdToken, renderHandlebars.render);
|
||||
var inlineCss = require("./server/render/inlinecss");
|
||||
app.post("/render/inlinecss", fb.validateFirebaseIdToken, inlineCss.inlinecss);
|
||||
|
||||
// app.post(
|
||||
// "/notifications/send",
|
||||
|
||||
// fb.sendNotification
|
||||
// );
|
||||
app.post("/notifications/subscribe", fb.validateFirebaseIdToken, fb.subscribe);
|
||||
app.post(
|
||||
"/notifications/unsubscribe",
|
||||
fb.validateFirebaseIdToken,
|
||||
fb.unsubscribe
|
||||
);
|
||||
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
|
||||
app.post("/adm/getuser", fb.validateFirebaseIdToken, fb.getUser);
|
||||
app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser);
|
||||
const adm = require("./server/admin/adminops");
|
||||
app.post(
|
||||
"/adm/createassociation",
|
||||
fb.validateFirebaseIdToken,
|
||||
fb.validateAdmin,
|
||||
adm.createAssociation
|
||||
);
|
||||
app.post(
|
||||
"/adm/createshop",
|
||||
fb.validateFirebaseIdToken,
|
||||
fb.validateAdmin,
|
||||
adm.createShop
|
||||
);
|
||||
app.post(
|
||||
"/adm/updateshop",
|
||||
fb.validateFirebaseIdToken,
|
||||
fb.validateAdmin,
|
||||
adm.updateShop
|
||||
);
|
||||
app.post(
|
||||
"/adm/updatecounter",
|
||||
fb.validateFirebaseIdToken,
|
||||
fb.validateAdmin,
|
||||
adm.updateCounter
|
||||
);
|
||||
|
||||
//Stripe Processing
|
||||
// var stripe = require("./server/stripe/payment");
|
||||
// app.post("/stripe/payment", fb.validateFirebaseIdToken, stripe.payment);
|
||||
// app.post(
|
||||
// "/stripe/mobilepayment",
|
||||
// fb.validateFirebaseIdToken,
|
||||
// stripe.mobile_payment
|
||||
// );
|
||||
|
||||
//Tech Console
|
||||
var tech = require("./server/tech/tech");
|
||||
app.post("/tech/login", fb.validateFirebaseIdToken, tech.techLogin);
|
||||
|
||||
var utils = require("./server/utils/utils");
|
||||
app.post("/utils/time", utils.servertime);
|
||||
app.post("/utils/jsr", fb.validateFirebaseIdToken, utils.jsrAuth);
|
||||
var qbo = require("./server/accounting/qbo/qbo");
|
||||
app.post("/qbo/authorize", fb.validateFirebaseIdToken, qbo.authorize);
|
||||
app.get("/qbo/callback", qbo.callback);
|
||||
app.post("/qbo/receivables", fb.validateFirebaseIdToken, qbo.receivables);
|
||||
app.post("/qbo/payables", fb.validateFirebaseIdToken, qbo.payables);
|
||||
app.post("/qbo/payments", fb.validateFirebaseIdToken, qbo.payments);
|
||||
|
||||
var data = require("./server/data/data");
|
||||
app.post("/data/ah", data.autohouse);
|
||||
app.post("/data/cc", data.claimscorp);
|
||||
app.post("/data/kaizen", data.kaizen);
|
||||
app.post("/record-handler/arms", data.arms);
|
||||
|
||||
var taskHandler = require("./server/tasks/tasks");
|
||||
app.post("/taskHandler", fb.validateFirebaseIdToken, taskHandler.taskHandler);
|
||||
|
||||
var mixdataUpload = require("./server/mixdata/mixdata");
|
||||
|
||||
app.post(
|
||||
"/mixdata/upload",
|
||||
fb.validateFirebaseIdToken,
|
||||
upload.any(),
|
||||
mixdataUpload.mixdataUpload
|
||||
);
|
||||
|
||||
var intellipay = require("./server/intellipay/intellipay");
|
||||
app.post(
|
||||
"/intellipay/lightbox_credentials",
|
||||
fb.validateFirebaseIdToken,
|
||||
intellipay.lightbox_credentials
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/intellipay/payment_refund",
|
||||
fb.validateFirebaseIdToken,
|
||||
intellipay.payment_refund
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/intellipay/generate_payment_url",
|
||||
fb.validateFirebaseIdToken,
|
||||
intellipay.generate_payment_url
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/intellipay/postback",
|
||||
// fb.validateFirebaseIdToken,
|
||||
intellipay.postback
|
||||
);
|
||||
|
||||
const payroll = require("./server/payroll/payroll");
|
||||
app.post(
|
||||
"/payroll/calculatelabor",
|
||||
fb.validateFirebaseIdToken,
|
||||
payroll.calculatelabor
|
||||
);
|
||||
app.post("/payroll/payall", fb.validateFirebaseIdToken, payroll.payall);
|
||||
app.post("/payroll/claimtask", fb.validateFirebaseIdToken, payroll.claimtask);
|
||||
|
||||
var ioevent = require("./server/ioevent/ioevent");
|
||||
app.post("/ioevent", ioevent.default);
|
||||
// app.post("/newlog", (req, res) => {
|
||||
// const { message, type, user, record, object } = req.body;
|
||||
// logger.log(message, type, user, record, object);
|
||||
// });
|
||||
|
||||
var os = require("./server/opensearch/os-handler");
|
||||
app.post(
|
||||
"/opensearch", //fb.validateFirebaseIdToken,
|
||||
os.handler
|
||||
);
|
||||
app.post("/search", fb.validateFirebaseIdToken, os.search);
|
||||
|
||||
var cdkGetMake = require("./server/cdk/cdk-get-makes");
|
||||
app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default);
|
||||
|
||||
app.get("/", async function (req, res) {
|
||||
res.status(200).send("Access Forbidden.");
|
||||
// Default route for forbidden access
|
||||
app.get("/", (req, res) => {
|
||||
res.status(200).send("Access Forbidden.");
|
||||
});
|
||||
|
||||
server.listen(port, (error) => {
|
||||
if (error) throw error;
|
||||
logger.log(
|
||||
`[${process.env.NODE_ENV || "DEVELOPMENT"}] Server running on port ${port}`,
|
||||
"INFO",
|
||||
"api"
|
||||
);
|
||||
});
|
||||
const main = async () => {
|
||||
await server.listen(port);
|
||||
}
|
||||
|
||||
// Start server
|
||||
main()
|
||||
.then(() => {
|
||||
logger.log(`[${process.env.NODE_ENV || "DEVELOPMENT"}] Server started on port ${port}`, "INFO", "api");
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log(`[${process.env.NODE_ENV || "DEVELOPMENT"}] Server failed to start on port ${port}`, "ERROR", "api", error);
|
||||
});
|
||||
@@ -166,7 +166,7 @@ async function CheckForErrors(socket, response) {
|
||||
CdkBase.createLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
`Succesful response from DMS. ${response.Message || ""}`
|
||||
`Successful response from DMS. ${response.Message || ""}`
|
||||
);
|
||||
} else {
|
||||
CdkBase.createLogEvent(
|
||||
|
||||
@@ -18,10 +18,10 @@ const {
|
||||
} = require("./qbo-callback");
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
const moment = require("moment-timezone");
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const findTaxCode = require("../qb-receivables-lines").findTaxCode;
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
|
||||
const oauthClient = new OAuthClient({
|
||||
clientId: process.env.QBO_CLIENT_ID,
|
||||
clientSecret: process.env.QBO_SECRET,
|
||||
@@ -30,29 +30,31 @@ exports.default = async (req, res) => {
|
||||
redirectUri: process.env.QBO_REDIRECT_URI,
|
||||
logging: true,
|
||||
});
|
||||
|
||||
try {
|
||||
//Fetch the API Access Tokens & Set them for the session.
|
||||
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
|
||||
email: req.user.email,
|
||||
});
|
||||
|
||||
const { qbo_realmId } = response.associations[0];
|
||||
|
||||
oauthClient.setToken(response.associations[0].qbo_auth);
|
||||
|
||||
if (!qbo_realmId) {
|
||||
res.status(401).json({ error: "No company associated." });
|
||||
return;
|
||||
}
|
||||
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { bills: billsToQuery, elgen } = req.body;
|
||||
//Query Job Info
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("qbo-payable-create", "DEBUG", req.user.email, billsToQuery);
|
||||
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
|
||||
|
||||
@@ -51,15 +51,13 @@ exports.default = async (req, res) => {
|
||||
}
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { payments: paymentsToQuery, elgen } = req.body;
|
||||
//Query Job Info
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("qbo-payment-create", "DEBUG", req.user.email, paymentsToQuery);
|
||||
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_PAYMENTS_FOR_EXPORT, {
|
||||
|
||||
@@ -45,15 +45,14 @@ exports.default = async (req, res) => {
|
||||
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobIds, elgen } = req.body;
|
||||
//Query Job Info
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("qbo-receivable-create", "DEBUG", req.user.email, jobIds);
|
||||
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, {
|
||||
|
||||
@@ -3,10 +3,11 @@ const path = require("path");
|
||||
const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
var builder = require("xmlbuilder2");
|
||||
const builder = require("xmlbuilder2");
|
||||
const QbXmlUtils = require("./qbxml-utils");
|
||||
const moment = require("moment-timezone");
|
||||
const logger = require("../../utils/logger");
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
@@ -15,14 +16,10 @@ require("dotenv").config({
|
||||
});
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { bills: billsToQuery } = req.body;
|
||||
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
logger.log(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const path = require("path");
|
||||
const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
var builder = require("xmlbuilder2");
|
||||
const builder = require("xmlbuilder2");
|
||||
const moment = require("moment-timezone");
|
||||
const QbXmlUtils = require("./qbxml-utils");
|
||||
const QbxmlReceivables = require("./qbxml-receivables");
|
||||
const logger = require("../../utils/logger");
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -19,14 +18,10 @@ require("dotenv").config({
|
||||
const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils;
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { payments: paymentsToQuery } = req.body;
|
||||
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
logger.log(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const path = require("path");
|
||||
const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
const moment = require("moment-timezone");
|
||||
var builder = require("xmlbuilder2");
|
||||
const builder = require("xmlbuilder2");
|
||||
const QbXmlUtils = require("./qbxml-utils");
|
||||
const logger = require("../../utils/logger");
|
||||
const CreateInvoiceLines = require("../qb-receivables-lines").default;
|
||||
const logger = require('../../utils/logger');
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -20,14 +19,10 @@ Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils;
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobIds } = req.body;
|
||||
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
logger.log(
|
||||
|
||||
@@ -5,7 +5,6 @@ require("dotenv").config({
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const soap = require("soap");
|
||||
const queries = require("../graphql-client/queries");
|
||||
|
||||
@@ -34,16 +33,11 @@ const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
|
||||
exports.default = async function ReloadCdkMakes(req, res) {
|
||||
const { bodyshopid, cdk_dealerid } = req.body;
|
||||
try {
|
||||
const BearerToken = req.headers.authorization;
|
||||
//Query all CDK Models
|
||||
const newList = await GetCdkMakes(req, cdk_dealerid);
|
||||
|
||||
//Clear out the existing records
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
const deleteResult = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const axios = require("axios");
|
||||
let nodemailer = require("nodemailer");
|
||||
let aws = require("@aws-sdk/client-ses");
|
||||
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
|
||||
let {defaultProvider} = require("@aws-sdk/credential-provider-node");
|
||||
|
||||
const logger = require("../utils/logger");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
@@ -18,251 +18,252 @@ const ses = new aws.SES({
|
||||
// The key apiVersion is no longer supported in v3, and can be removed.
|
||||
// @deprecated The client uses the "latest" apiVersion.
|
||||
apiVersion: "latest",
|
||||
|
||||
defaultProvider,
|
||||
region: "us-east-2",
|
||||
});
|
||||
|
||||
let transporter = nodemailer.createTransport({
|
||||
SES: { ses, aws },
|
||||
SES: {ses, aws},
|
||||
});
|
||||
|
||||
exports.sendServerEmail = async function ({ subject, text }) {
|
||||
if (process.env.NODE_ENV === undefined) return;
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `Rome Online API - ${process.env.NODE_ENV} <noreply@romeonline.io>`,
|
||||
to: ["patrick@imexsystems.ca"],
|
||||
subject: subject,
|
||||
text: text,
|
||||
ses: {
|
||||
// optional extra arguments for SendRawEmail
|
||||
Tags: [
|
||||
exports.sendServerEmail = async function ({subject, text}) {
|
||||
if (process.env.NODE_ENV === undefined) return;
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
Name: "tag_name",
|
||||
Value: "tag_value",
|
||||
from: `Rome Online API - ${process.env.NODE_ENV} <noreply@romeonline.io>`,
|
||||
to: ["patrick@imexsystems.ca"],
|
||||
subject: subject,
|
||||
text: text,
|
||||
ses: {
|
||||
// optional extra arguments for SendRawEmail
|
||||
Tags: [
|
||||
{
|
||||
Name: "tag_name",
|
||||
Value: "tag_value",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
};
|
||||
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `Rome Online <noreply@romeonline.io>`,
|
||||
to: to,
|
||||
subject: subject,
|
||||
text: text,
|
||||
attachments: attachments || null,
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
exports.sendTaskEmail = async function ({to, subject, text, attachments}) {
|
||||
try {
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `Rome Online <noreply@romeonline.io>`,
|
||||
to: to,
|
||||
subject: subject,
|
||||
text: text,
|
||||
attachments: attachments || null,
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
}
|
||||
};
|
||||
|
||||
exports.sendEmail = async (req, res) => {
|
||||
logger.log("send-email", "DEBUG", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
});
|
||||
logger.log("send-email", "DEBUG", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
});
|
||||
|
||||
let downloadedMedia = [];
|
||||
if (req.body.media && req.body.media.length > 0) {
|
||||
downloadedMedia = await Promise.all(
|
||||
req.body.media.map((m) => {
|
||||
try {
|
||||
return getImage(m);
|
||||
} catch (error) {
|
||||
logger.log("send-email-error", "ERROR", req.user.email, null, {
|
||||
let downloadedMedia = [];
|
||||
if (req.body.media && req.body.media.length > 0) {
|
||||
downloadedMedia = await Promise.all(
|
||||
req.body.media.map((m) => {
|
||||
try {
|
||||
return getImage(m);
|
||||
} catch (error) {
|
||||
logger.log("send-email-error", "ERROR", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
error,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
error,
|
||||
});
|
||||
attachments:
|
||||
[
|
||||
...((req.body.attachments &&
|
||||
req.body.attachments.map((a) => {
|
||||
return {
|
||||
filename: a.filename,
|
||||
path: a.path,
|
||||
};
|
||||
})) ||
|
||||
[]),
|
||||
...downloadedMedia.map((a) => {
|
||||
return {
|
||||
path: a,
|
||||
};
|
||||
}),
|
||||
] || null,
|
||||
html: req.body.html,
|
||||
ses: {
|
||||
// optional extra arguments for SendRawEmail
|
||||
Tags: [
|
||||
{
|
||||
Name: "tag_name",
|
||||
Value: "tag_value",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
if (info) {
|
||||
logger.log("send-email-success", "DEBUG", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
// info,
|
||||
});
|
||||
logEmail(req, {
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
messageId: info.response,
|
||||
});
|
||||
res.json({
|
||||
success: true, //response: info
|
||||
});
|
||||
} else {
|
||||
logger.log("send-email-failure", "ERROR", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
error: err,
|
||||
});
|
||||
logEmail(req, {
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
bodyshopid: req.body.bodyshopid,
|
||||
});
|
||||
res.status(500).json({success: false, error: err});
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
attachments:
|
||||
[
|
||||
...((req.body.attachments &&
|
||||
req.body.attachments.map((a) => {
|
||||
return {
|
||||
filename: a.filename,
|
||||
path: a.path,
|
||||
};
|
||||
})) ||
|
||||
[]),
|
||||
...downloadedMedia.map((a) => {
|
||||
return {
|
||||
path: a,
|
||||
};
|
||||
}),
|
||||
] || null,
|
||||
html: req.body.html,
|
||||
ses: {
|
||||
// optional extra arguments for SendRawEmail
|
||||
Tags: [
|
||||
{
|
||||
Name: "tag_name",
|
||||
Value: "tag_value",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
(err, info) => {
|
||||
console.log(err || info);
|
||||
if (info) {
|
||||
logger.log("send-email-success", "DEBUG", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
// info,
|
||||
});
|
||||
logEmail(req, {
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
messageId: info.response,
|
||||
});
|
||||
res.json({
|
||||
success: true, //response: info
|
||||
});
|
||||
} else {
|
||||
logger.log("send-email-failure", "ERROR", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
replyTo: req.body.ReplyTo.Email,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
error: err,
|
||||
});
|
||||
logEmail(req, {
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
bodyshopid: req.body.bodyshopid,
|
||||
});
|
||||
res.status(500).json({ success: false, error: err });
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
async function getImage(imageUrl) {
|
||||
let image = await axios.get(imageUrl, { responseType: "arraybuffer" });
|
||||
let raw = Buffer.from(image.data).toString("base64");
|
||||
return "data:" + image.headers["content-type"] + ";base64," + raw;
|
||||
let image = await axios.get(imageUrl, {responseType: "arraybuffer"});
|
||||
let raw = Buffer.from(image.data).toString("base64");
|
||||
return "data:" + image.headers["content-type"] + ";base64," + raw;
|
||||
}
|
||||
|
||||
async function logEmail(req, email) {
|
||||
try {
|
||||
const insertresult = await client.request(queries.INSERT_EMAIL_AUDIT, {
|
||||
email: {
|
||||
to: email.to,
|
||||
cc: email.cc,
|
||||
subject: email.subject,
|
||||
bodyshopid: req.body.bodyshopid,
|
||||
useremail: req.user.email,
|
||||
contents: req.body.html,
|
||||
jobid: req.body.jobid,
|
||||
sesmessageid: email.messageId,
|
||||
status: "Sent",
|
||||
},
|
||||
});
|
||||
console.log(insertresult);
|
||||
} catch (error) {
|
||||
logger.log("email-log-error", "error", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
// info,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const insertresult = await client.request(queries.INSERT_EMAIL_AUDIT, {
|
||||
email: {
|
||||
to: email.to,
|
||||
cc: email.cc,
|
||||
subject: email.subject,
|
||||
bodyshopid: req.body.bodyshopid,
|
||||
useremail: req.user.email,
|
||||
contents: req.body.html,
|
||||
jobid: req.body.jobid,
|
||||
sesmessageid: email.messageId,
|
||||
status: "Sent",
|
||||
},
|
||||
});
|
||||
console.log(insertresult);
|
||||
} catch (error) {
|
||||
logger.log("email-log-error", "error", req.user.email, null, {
|
||||
from: `${req.body.from.name} <${req.body.from.address}>`,
|
||||
to: req.body.to,
|
||||
cc: req.body.cc,
|
||||
subject: req.body.subject,
|
||||
// info,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exports.emailBounce = async function (req, res, next) {
|
||||
try {
|
||||
const body = JSON.parse(req.body);
|
||||
if (body.Type === "SubscriptionConfirmation") {
|
||||
logger.log("SNS-message", "DEBUG", "api", null, {
|
||||
body: req.body,
|
||||
});
|
||||
}
|
||||
const message = JSON.parse(body.Message);
|
||||
if (message.notificationType === "Bounce") {
|
||||
let replyTo, subject, messageId;
|
||||
message.mail.headers.forEach((header) => {
|
||||
if (header.name === "Reply-To") {
|
||||
replyTo = header.value;
|
||||
} else if (header.name === "Subject") {
|
||||
subject = header.value;
|
||||
exports.emailBounce = async function (req, res) {
|
||||
try {
|
||||
const body = JSON.parse(req.body);
|
||||
if (body.Type === "SubscriptionConfirmation") {
|
||||
logger.log("SNS-message", "DEBUG", "api", null, {
|
||||
body: req.body,
|
||||
});
|
||||
}
|
||||
});
|
||||
messageId = message.mail.messageId;
|
||||
if (replyTo === "noreply@romeonline.io") {
|
||||
res.sendStatus(200);
|
||||
return;
|
||||
}
|
||||
//If it's bounced, log it as bounced in audit log. Send an email to the user.
|
||||
const result = await client.request(queries.UPDATE_EMAIL_AUDIT, {
|
||||
sesid: messageId,
|
||||
status: "Bounced",
|
||||
context: message.bounce?.bouncedRecipients,
|
||||
});
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `Rome Online <noreply@romeonline.io>`,
|
||||
to: replyTo,
|
||||
subject: `Rome Online Bounced Email - RE: ${subject}`,
|
||||
text: `Rome Online has tried to deliver an email with the subject: ${subject} to the intended recipients but encountered an error.
|
||||
const message = JSON.parse(body.Message);
|
||||
if (message.notificationType === "Bounce") {
|
||||
let replyTo, subject, messageId;
|
||||
message.mail.headers.forEach((header) => {
|
||||
if (header.name === "Reply-To") {
|
||||
replyTo = header.value;
|
||||
} else if (header.name === "Subject") {
|
||||
subject = header.value;
|
||||
}
|
||||
});
|
||||
messageId = message.mail.messageId;
|
||||
if (replyTo === "noreply@romeonline.io") {
|
||||
res.sendStatus(200);
|
||||
return;
|
||||
}
|
||||
//If it's bounced, log it as bounced in audit log. Send an email to the user.
|
||||
const result = await client.request(queries.UPDATE_EMAIL_AUDIT, {
|
||||
sesid: messageId,
|
||||
status: "Bounced",
|
||||
context: message.bounce?.bouncedRecipients,
|
||||
});
|
||||
transporter.sendMail(
|
||||
{
|
||||
from: `Rome Online <noreply@romeonline.io>`,
|
||||
to: replyTo,
|
||||
//bcc: "patrick@snapt.ca",
|
||||
subject: `Rome Online Bounced Email - RE: ${subject}`,
|
||||
text: `Rome Online has tried to deliver an email with the subject: ${subject} to the intended recipients but encountered an error.
|
||||
|
||||
${body.bounce?.bouncedRecipients.map(
|
||||
(r) =>
|
||||
`Recipient: ${r.emailAddress} | Status: ${r.action} | Code: ${r.diagnosticCode}
|
||||
(r) =>
|
||||
`Recipient: ${r.emailAddress} | Status: ${r.action} | Code: ${r.diagnosticCode}
|
||||
`
|
||||
)}
|
||||
)}
|
||||
`,
|
||||
},
|
||||
(err, info) => {
|
||||
console.log("***", err || info);
|
||||
},
|
||||
(err, info) => {
|
||||
console.log("***", err || info);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
logger.log("sns-error", "ERROR", "api", null, {
|
||||
error: JSON.stringify(error),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.log("sns-error", "ERROR", "api", null, {
|
||||
error: JSON.stringify(error),
|
||||
});
|
||||
}
|
||||
res.sendStatus(200);
|
||||
res.sendStatus(200);
|
||||
};
|
||||
|
||||
@@ -1,287 +1,215 @@
|
||||
var admin = require("firebase-admin");
|
||||
const admin = require("firebase-admin");
|
||||
const logger = require("../utils/logger");
|
||||
const path = require("path");
|
||||
const { auth } = require("firebase-admin");
|
||||
const {auth} = require("firebase-admin");
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
var serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON);
|
||||
|
||||
const serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON);
|
||||
const adminEmail = require("../utils/adminEmail");
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
databaseURL: process.env.FIREBASE_DATABASE_URL,
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
databaseURL: process.env.FIREBASE_DATABASE_URL,
|
||||
});
|
||||
|
||||
exports.admin = admin;
|
||||
|
||||
const adminEmail = [
|
||||
"patrick@imex.dev",
|
||||
//"patrick@imex.test",
|
||||
"patrick@imex.prod",
|
||||
"patrick@imexsystems.ca",
|
||||
"patrick@thinkimex.com",
|
||||
];
|
||||
|
||||
exports.createUser = async (req, res) => {
|
||||
logger.log("admin-create-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
ioadmin: true,
|
||||
});
|
||||
logger.log("admin-create-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
ioadmin: true,
|
||||
});
|
||||
|
||||
const { email, displayName, password, shopid, authlevel } = req.body;
|
||||
try {
|
||||
const userRecord = await admin
|
||||
.auth()
|
||||
.createUser({ email, displayName, password });
|
||||
const {email, displayName, password, shopid, authlevel} = req.body;
|
||||
try {
|
||||
const userRecord = await admin
|
||||
.auth()
|
||||
.createUser({email, displayName, password});
|
||||
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
|
||||
const result = await client.request(
|
||||
`
|
||||
const result = await client.request(
|
||||
`
|
||||
mutation INSERT_USER($user: users_insert_input!) {
|
||||
insert_users_one(object: $user) {
|
||||
email
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
user: {
|
||||
email: email.toLowerCase(),
|
||||
authid: userRecord.uid,
|
||||
associations: {
|
||||
data: [{ shopid, authlevel, active: true }],
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
{
|
||||
user: {
|
||||
email: email.toLowerCase(),
|
||||
authid: userRecord.uid,
|
||||
associations: {
|
||||
data: [{shopid, authlevel, active: true}],
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
res.json({ userRecord, result });
|
||||
} catch (error) {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
}
|
||||
res.json({userRecord, result});
|
||||
} catch (error) {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
}
|
||||
};
|
||||
|
||||
exports.updateUser = (req, res) => {
|
||||
logger.log("admin-update-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
ioadmin: true,
|
||||
});
|
||||
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log(
|
||||
"admin-update-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
logger.log("admin-update-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
|
||||
admin
|
||||
.auth()
|
||||
.updateUser(
|
||||
req.body.uid,
|
||||
req.body.user
|
||||
// {
|
||||
// email: "modifiedUser@example.com",
|
||||
// phoneNumber: "+11234567890",
|
||||
// emailVerified: true,
|
||||
// password: "newPassword",
|
||||
// displayName: "Jane Doe",
|
||||
// photoURL: "http://www.example.com/12345678/photo.png",
|
||||
// disabled: true,
|
||||
// }
|
||||
)
|
||||
.then((userRecord) => {
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
|
||||
logger.log("admin-update-user-success", "ADMIN", req.user.email, null, {
|
||||
userRecord,
|
||||
ioadmin: true,
|
||||
});
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
});
|
||||
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log(
|
||||
"admin-update-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
|
||||
admin
|
||||
.auth()
|
||||
.updateUser(
|
||||
req.body.uid,
|
||||
req.body.user
|
||||
// {
|
||||
// email: "modifiedUser@example.com",
|
||||
// phoneNumber: "+11234567890",
|
||||
// emailVerified: true,
|
||||
// password: "newPassword",
|
||||
// displayName: "Jane Doe",
|
||||
// photoURL: "http://www.example.com/12345678/photo.png",
|
||||
// disabled: true,
|
||||
// }
|
||||
)
|
||||
.then((userRecord) => {
|
||||
// See the UserRecord reference doc for the contents of userRecord.
|
||||
|
||||
logger.log("admin-update-user-success", "ADMIN", req.user.email, null, {
|
||||
userRecord,
|
||||
ioadmin: true,
|
||||
});
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.getUser = (req, res) => {
|
||||
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
ioadmin: true,
|
||||
});
|
||||
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log(
|
||||
"admin-update-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
|
||||
admin
|
||||
.auth()
|
||||
.getUser(req.body.uid)
|
||||
.then((userRecord) => {
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-get-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
ioadmin: true,
|
||||
});
|
||||
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log(
|
||||
"admin-update-user-unauthorized",
|
||||
"ERROR",
|
||||
req.user.email,
|
||||
null,
|
||||
{
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
}
|
||||
);
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
|
||||
admin
|
||||
.auth()
|
||||
.getUser(req.body.uid)
|
||||
.then((userRecord) => {
|
||||
res.json(userRecord);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.log("admin-get-user-error", "ERROR", req.user.email, null, {
|
||||
error,
|
||||
});
|
||||
res.status(500).json(error);
|
||||
});
|
||||
};
|
||||
|
||||
exports.sendNotification = async (req, res) => {
|
||||
setTimeout(() => {
|
||||
// Send a message to the device corresponding to the provided
|
||||
// registration token.
|
||||
admin
|
||||
.messaging()
|
||||
.send({
|
||||
topic: "PRD_PATRICK-messaging",
|
||||
notification: {
|
||||
title: `ImEX Online Message - +16049992002`,
|
||||
body: "Test Noti.",
|
||||
//imageUrl: "https://thinkimex.com/img/io-fcm.png",
|
||||
},
|
||||
data: {
|
||||
type: "messaging-inbound",
|
||||
conversationid: "e0eb17c3-3a78-4e3f-b932-55ef35aa2297",
|
||||
text: "Hello. ",
|
||||
image_path: "",
|
||||
phone_num: "+16049992002",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
// Response is a message ID string.
|
||||
console.log("Successfully sent message:", response);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error sending message:", error);
|
||||
});
|
||||
setTimeout(() => {
|
||||
// Send a message to the device corresponding to the provided
|
||||
// registration token.
|
||||
admin
|
||||
.messaging()
|
||||
.send({
|
||||
topic: "PRD_PATRICK-messaging",
|
||||
notification: {
|
||||
title: `ImEX Online Message - +16049992002`,
|
||||
body: "Test Noti.",
|
||||
//imageUrl: "https://thinkimex.com/img/io-fcm.png",
|
||||
},
|
||||
data: {
|
||||
type: "messaging-inbound",
|
||||
conversationid: "e0eb17c3-3a78-4e3f-b932-55ef35aa2297",
|
||||
text: "Hello. ",
|
||||
image_path: "",
|
||||
phone_num: "+16049992002",
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
// Response is a message ID string.
|
||||
console.log("Successfully sent message:", response);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("Error sending message:", error);
|
||||
});
|
||||
|
||||
res.sendStatus(200);
|
||||
}, 500);
|
||||
res.sendStatus(200);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
exports.subscribe = async (req, res) => {
|
||||
const result = await admin
|
||||
.messaging()
|
||||
.subscribeToTopic(
|
||||
req.body.fcm_tokens,
|
||||
`${req.body.imexshopid}-${req.body.type}`
|
||||
);
|
||||
const result = await admin
|
||||
.messaging()
|
||||
.subscribeToTopic(
|
||||
req.body.fcm_tokens,
|
||||
`${req.body.imexshopid}-${req.body.type}`
|
||||
);
|
||||
|
||||
res.json(result);
|
||||
res.json(result);
|
||||
};
|
||||
|
||||
exports.unsubscribe = async (req, res) => {
|
||||
try {
|
||||
const result = await admin
|
||||
.messaging()
|
||||
.unsubscribeFromTopic(
|
||||
req.body.fcm_tokens,
|
||||
`${req.body.imexshopid}-${req.body.type}`
|
||||
);
|
||||
try {
|
||||
const result = await admin
|
||||
.messaging()
|
||||
.unsubscribeFromTopic(
|
||||
req.body.fcm_tokens,
|
||||
`${req.body.imexshopid}-${req.body.type}`
|
||||
);
|
||||
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.sendStatus(500);
|
||||
}
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.sendStatus(500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.validateFirebaseIdToken = async (req, res, next) => {
|
||||
if (
|
||||
(!req.headers.authorization ||
|
||||
!req.headers.authorization.startsWith("Bearer ")) &&
|
||||
!(req.cookies && req.cookies.__session)
|
||||
) {
|
||||
console.error("Unauthorized attempt. No authorization provided.");
|
||||
res.status(403).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
|
||||
let idToken;
|
||||
if (
|
||||
req.headers.authorization &&
|
||||
req.headers.authorization.startsWith("Bearer ")
|
||||
) {
|
||||
// console.log('Found "Authorization" header');
|
||||
// Read the ID Token from the Authorization header.
|
||||
idToken = req.headers.authorization.split("Bearer ")[1];
|
||||
} else if (req.cookies) {
|
||||
//console.log('Found "__session" cookie');
|
||||
// Read the ID Token from cookie.
|
||||
idToken = req.cookies.__session;
|
||||
} else {
|
||||
// No cookie
|
||||
console.error("Unauthorized attempt. No cookie provided.");
|
||||
logger.log("api-unauthorized-call", "WARN", null, null, {
|
||||
req,
|
||||
type: "no-cookie",
|
||||
});
|
||||
res.status(403).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
||||
//console.log("ID Token correctly decoded", decodedIdToken);
|
||||
req.user = decodedIdToken;
|
||||
next();
|
||||
return;
|
||||
} catch (error) {
|
||||
logger.log("api-unauthorized-call", "WARN", null, null, {
|
||||
path: req.path,
|
||||
body: req.body,
|
||||
|
||||
type: "unauthroized",
|
||||
...error,
|
||||
});
|
||||
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
exports.validateAdmin = async (req, res, next) => {
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log("admin-validation-failed", "ERROR", req.user.email, null, {
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
});
|
||||
res.sendStatus(404);
|
||||
return;
|
||||
} else {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
//Admin claims code.
|
||||
// const uid = "JEqqYlsadwPEXIiyRBR55fflfko1";
|
||||
|
||||
@@ -521,6 +521,21 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.QUERY_TRANSITIONS_BY_JOBID = `query QUERY_TRANSITIONS_BY_JOBID($jobids: [uuid!]!) {
|
||||
transitions(where: {jobid: {_in: $jobids}}, order_by: {end: desc}) {
|
||||
start
|
||||
end
|
||||
value
|
||||
prev_value
|
||||
next_value
|
||||
duration
|
||||
type
|
||||
created_at
|
||||
updated_at
|
||||
jobid
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: timestamptz!, $jobId: uuid!) {
|
||||
jobs_by_pk(id: $jobId) {
|
||||
bodyshop {
|
||||
@@ -1970,12 +1985,20 @@ exports.UPDATE_OLD_TRANSITION = `mutation UPDATE_OLD_TRANSITION($jobid: uuid!, $
|
||||
}
|
||||
}`;
|
||||
|
||||
exports.INSERT_NEW_TRANSITION = `mutation INSERT_NEW_TRANSITION($newTransition: transitions_insert_input!, $oldTransitionId: uuid, $duration: numeric) {
|
||||
exports.INSERT_NEW_TRANSITION = (
|
||||
includeOldTransition
|
||||
) => `mutation INSERT_NEW_TRANSITION($newTransition: transitions_insert_input!, ${
|
||||
includeOldTransition ? `$oldTransitionId: uuid!, $duration: numeric` : ""
|
||||
}) {
|
||||
insert_transitions_one(object: $newTransition) {
|
||||
id
|
||||
}
|
||||
update_transitions(where: {id: {_eq: $oldTransitionId}}, _set: {duration: $duration}) {
|
||||
${
|
||||
includeOldTransition
|
||||
? `update_transitions(where: {id: {_eq: $oldTransitionId}}, _set: {duration: $duration}) {
|
||||
affected_rows
|
||||
}`
|
||||
: ""
|
||||
}
|
||||
}`;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
68
server/job/job-lifecycle.js
Normal file
68
server/job/job-lifecycle.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const _ = require("lodash");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const moment = require("moment");
|
||||
const durationToHumanReadable = require("../utils/durationToHumanReadable");
|
||||
const calculateStatusDuration = require("../utils/calculateStatusDuration");
|
||||
|
||||
const jobLifecycle = async (req, res) => {
|
||||
// Grab the jobids and statuses from the request body
|
||||
const {
|
||||
jobids,
|
||||
statuses
|
||||
} = req.body;
|
||||
|
||||
if (!jobids) {
|
||||
return res.status(400).json({
|
||||
error: "Missing jobids"
|
||||
});
|
||||
}
|
||||
|
||||
const jobIDs = _.isArray(jobids) ? jobids : [jobids];
|
||||
const client = req.userGraphQLClient;
|
||||
const resp = await client.request(queries.QUERY_TRANSITIONS_BY_JOBID, {jobids: jobIDs,});
|
||||
|
||||
const transitions = resp.transitions;
|
||||
|
||||
if (!transitions) {
|
||||
return res.status(200).json({
|
||||
jobIDs,
|
||||
transitions: []
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const transitionsByJobId = _.groupBy(resp.transitions, 'jobid');
|
||||
|
||||
const groupedTransitions = {};
|
||||
|
||||
for (let jobId in transitionsByJobId) {
|
||||
let lifecycle = transitionsByJobId[jobId].map(transition => {
|
||||
transition.start_readable = transition.start ? moment(transition.start).fromNow() : 'N/A';
|
||||
transition.end_readable = transition.end ? moment(transition.end).fromNow() : 'N/A';
|
||||
|
||||
if (transition.duration) {
|
||||
transition.duration_seconds = Math.round(transition.duration / 1000);
|
||||
transition.duration_minutes = Math.round(transition.duration_seconds / 60);
|
||||
let duration = moment.duration(transition.duration);
|
||||
transition.duration_readable = durationToHumanReadable(duration);
|
||||
} else {
|
||||
transition.duration_seconds = 0;
|
||||
transition.duration_minutes = 0;
|
||||
transition.duration_readable = 'N/A';
|
||||
}
|
||||
return transition;
|
||||
});
|
||||
|
||||
groupedTransitions[jobId] = {
|
||||
lifecycle: lifecycle,
|
||||
durations: calculateStatusDuration(lifecycle, statuses),
|
||||
};
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
jobIDs,
|
||||
transition: groupedTransitions,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = jobLifecycle;
|
||||
@@ -9,24 +9,25 @@ const logger = require("../utils/logger");
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
const path = require("path");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
|
||||
async function StatusTransition(req, res) {
|
||||
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
res.sendStatus(200);
|
||||
return;
|
||||
const {
|
||||
id: jobid,
|
||||
status: value,
|
||||
shopid: bodyshopid,
|
||||
} = req.body.event.data.new;
|
||||
|
||||
// Create record OPEN on new item, enter state
|
||||
// If change to SCHEDULE, update the last record and create a new record (update status and end time on old record, create a new record saying we came from previous status going to previous status
|
||||
// (Timeline)
|
||||
// Final status is exported, there is no end date as there is no further transition (has no end date)
|
||||
try {
|
||||
const { update_transitions } = await client.request(
|
||||
queries.UPDATE_OLD_TRANSITION,
|
||||
@@ -47,27 +48,36 @@ async function StatusTransition(req, res) {
|
||||
: new Date(update_transitions.returning[0].end) -
|
||||
new Date(update_transitions.returning[0].start);
|
||||
|
||||
const resp2 = await client.request(queries.INSERT_NEW_TRANSITION, {
|
||||
oldTransitionId:
|
||||
update_transitions.affected_rows === 0
|
||||
? null
|
||||
: update_transitions.returning[0].id,
|
||||
duration,
|
||||
newTransition: {
|
||||
bodyshopid: bodyshopid,
|
||||
jobid: jobid,
|
||||
start:
|
||||
update_transitions.affected_rows === 0
|
||||
? new Date()
|
||||
: update_transitions.returning[0].end,
|
||||
prev_value:
|
||||
update_transitions.affected_rows === 0
|
||||
? null
|
||||
: update_transitions.returning[0].value,
|
||||
value: value,
|
||||
type: "status",
|
||||
},
|
||||
});
|
||||
const resp2 = await client.request(
|
||||
queries.INSERT_NEW_TRANSITION(update_transitions.affected_rows > 0),
|
||||
{
|
||||
...(update_transitions.affected_rows > 0
|
||||
? {
|
||||
oldTransitionId:
|
||||
update_transitions.affected_rows === 0
|
||||
? null
|
||||
: update_transitions.returning[0].id,
|
||||
duration,
|
||||
}
|
||||
: {}),
|
||||
newTransition: {
|
||||
bodyshopid: bodyshopid,
|
||||
jobid: jobid,
|
||||
start:
|
||||
update_transitions.affected_rows === 0
|
||||
? new Date()
|
||||
: update_transitions.returning[0].end,
|
||||
prev_value:
|
||||
update_transitions.affected_rows === 0
|
||||
? null
|
||||
: update_transitions.returning[0].value,
|
||||
value: value,
|
||||
type: "status",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
logger.log("job-transition-update-result", "DEBUG", null, jobid, resp2);
|
||||
|
||||
//Check to see if there is an existing status transition record.
|
||||
//Query using Job ID, start is not null, end is null.
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
const Dinero = require("dinero.js");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const adminClient = require("../graphql-client/graphql-client").client;
|
||||
const _ = require("lodash");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
// Dinero.defaultCurrency = "USD";
|
||||
// Dinero.globalLocale = "en-CA";
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
|
||||
exports.totalsSsu = async function (req, res) {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { id } = req.body;
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("job-totals-ssu", "DEBUG", req.user.email, id, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const job = await client
|
||||
@@ -77,21 +75,19 @@ async function TotalsServerSide(req, res) {
|
||||
}
|
||||
|
||||
async function Totals(req, res) {
|
||||
const { job } = req.body;
|
||||
const { job, id } = req.body;
|
||||
|
||||
const logger = req.logger;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("job-totals", "DEBUG", req.user.email, job.id, {
|
||||
jobid: job.id,
|
||||
});
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { id } = req.body;
|
||||
logger.log("job-totals-ssu", "DEBUG", req.user.email, id, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
await AutoAddAtsIfRequired({ job, client });
|
||||
|
||||
try {
|
||||
let ret = {
|
||||
rates: await CalculateRatesTotals({ job, client }),
|
||||
|
||||
@@ -3,3 +3,4 @@ exports.totalsSsu = require("./job-totals").totalsSsu;
|
||||
exports.costing = require("./job-costing").JobCosting;
|
||||
exports.costingmulti = require("./job-costing").JobCostingMulti;
|
||||
exports.statustransition = require("./job-status-transition").statustransition;
|
||||
exports.lifecycle = require('./job-lifecycle');
|
||||
20
server/middleware/eventAuthorizationMIddleware.js
Normal file
20
server/middleware/eventAuthorizationMIddleware.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const path = require("path");
|
||||
|
||||
/**
|
||||
* Checks if the event secret is correct
|
||||
* It adds the following properties to the request object:
|
||||
* - req.isEventAuthorized - Returns true if the event secret is correct
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
function eventAuthorizationMiddleware(req, res, next) {
|
||||
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
|
||||
return res.status(401).send("Unauthorized");
|
||||
}
|
||||
|
||||
req.isEventAuthorized = true;
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = eventAuthorizationMiddleware;
|
||||
26
server/middleware/validateAdminMiddleware.js
Normal file
26
server/middleware/validateAdminMiddleware.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const logger = require("../utils/logger");
|
||||
const adminEmail = require("../utils/adminEmail");
|
||||
|
||||
/**
|
||||
* Validate admin middleware
|
||||
* It adds the following properties to the request object:
|
||||
* - req.isAdmin - returns true if the user passed an admin check
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @returns {*}
|
||||
*/
|
||||
const validateAdminMiddleware = (req, res, next) => {
|
||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
||||
logger.log("admin-validation-failed", "ERROR", req.user.email, null, {
|
||||
request: req.body,
|
||||
user: req.user,
|
||||
});
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
req.isAdmin = true;
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = validateAdminMiddleware;
|
||||
69
server/middleware/validateFirebaseIdTokenMiddleware.js
Normal file
69
server/middleware/validateFirebaseIdTokenMiddleware.js
Normal file
@@ -0,0 +1,69 @@
|
||||
const logger = require("../utils/logger");
|
||||
const admin = require("firebase-admin");
|
||||
|
||||
/**
|
||||
* Middleware to validate Firebase ID Tokens.
|
||||
* This middleware is used to protect API endpoints from unauthorized access.
|
||||
* It adds the following properties to the request object:
|
||||
* - req.user - the decoded Firebase ID Token
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const validateFirebaseIdTokenMiddleware = async (req, res, next) => {
|
||||
if (
|
||||
(
|
||||
!req.headers.authorization ||
|
||||
!req.headers.authorization.startsWith("Bearer ")) &&
|
||||
!(req.cookies && req.cookies.__session
|
||||
)
|
||||
) {
|
||||
console.error("Unauthorized attempt. No authorization provided.");
|
||||
return res.status(403).send("Unauthorized");
|
||||
}
|
||||
|
||||
let idToken;
|
||||
|
||||
if (
|
||||
req.headers.authorization &&
|
||||
req.headers.authorization.startsWith("Bearer ")
|
||||
) {
|
||||
// console.log('Found "Authorization" header');
|
||||
// Read the ID Token from the Authorization header.
|
||||
idToken = req.headers.authorization.split("Bearer ")[1];
|
||||
} else if (req.cookies) {
|
||||
//console.log('Found "__session" cookie');
|
||||
// Read the ID Token from cookie.
|
||||
idToken = req.cookies.__session;
|
||||
} else {
|
||||
// No cookie
|
||||
console.error("Unauthorized attempt. No cookie provided.");
|
||||
logger.log("api-unauthorized-call", "WARN", null, null, {
|
||||
req,
|
||||
type: "no-cookie",
|
||||
});
|
||||
|
||||
return res.status(403).send("Unauthorized");
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
|
||||
//console.log("ID Token correctly decoded", decodedIdToken);
|
||||
req.user = decodedIdToken;
|
||||
next();
|
||||
|
||||
} catch (error) {
|
||||
logger.log("api-unauthorized-call", "WARN", null, null, {
|
||||
path: req.path,
|
||||
body: req.body,
|
||||
|
||||
type: "unauthroized",
|
||||
...error,
|
||||
});
|
||||
|
||||
return res.status(401).send("Unauthorized");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = validateFirebaseIdTokenMiddleware;
|
||||
24
server/middleware/withUserGraphQLClientMiddleware.js
Normal file
24
server/middleware/withUserGraphQLClientMiddleware.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const {GraphQLClient} = require("graphql-request");
|
||||
|
||||
/**
|
||||
* Middleware to add a GraphQL Client to the request object
|
||||
* Adds the following to the request object:
|
||||
* req.userGraphQLClient - GraphQL Client with user Bearer Token
|
||||
* req.BearerToken - Bearer Token
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
const withUserGraphQLClientMiddleware = (req, res, next) => {
|
||||
const BearerToken = req.headers.authorization;
|
||||
req.userGraphQLClient = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
req.BearerToken = BearerToken;
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = withUserGraphQLClientMiddleware;
|
||||
@@ -1,9 +1,8 @@
|
||||
const path = require("path");
|
||||
const _ = require("lodash");
|
||||
const logger = require("../utils/logger");
|
||||
const xml2js = require("xml2js");
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const queries = require("../graphql-client/queries");
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -15,13 +14,10 @@ require("dotenv").config({
|
||||
exports.mixdataUpload = async (req, res) => {
|
||||
const { bodyshopid } = req.body;
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("job-mixdata-upload", "DEBUG", req.user.email, null, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
for (const element of req.files) {
|
||||
|
||||
@@ -5,7 +5,6 @@ require("dotenv").config({
|
||||
),
|
||||
});
|
||||
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
//const client = require("../graphql-client/graphql-client").client;
|
||||
const logger = require("../utils/logger");
|
||||
const queries = require("../graphql-client/queries");
|
||||
@@ -15,10 +14,6 @@ const {getClient} = require('../../libs/awsUtils');
|
||||
|
||||
|
||||
async function OpenSearchUpdateHandler(req, res) {
|
||||
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
const osClient = await getClient();
|
||||
@@ -186,12 +181,8 @@ async function OpenSearchSearchHandler(req, res) {
|
||||
search,
|
||||
});
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
const assocs = await client
|
||||
.setHeaders({Authorization: BearerToken})
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
const Dinero = require("dinero.js");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const logger = require('../utils/logger');
|
||||
const { job } = require("../scheduling/scheduling-job");
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const logger = require("../utils/logger");
|
||||
const _ = require("lodash");
|
||||
|
||||
// Dinero.defaultCurrency = "USD";
|
||||
// Dinero.globalLocale = "en-CA";
|
||||
|
||||
exports.partsScan = async function (req, res) {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobid } = req.body;
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
//Query all jobline data using the user's authorization.
|
||||
|
||||
@@ -12,14 +12,11 @@ const {
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
|
||||
exports.calculatelabor = async function (req, res) {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobid, calculateOnly } = req.body;
|
||||
logger.log("job-payroll-calculate-labor", "DEBUG", req.user.email, jobid, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
const { jobs_by_pk: job } = await client
|
||||
|
||||
@@ -12,14 +12,11 @@ const moment = require("moment");
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
|
||||
exports.claimtask = async function (req, res) {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobid, task, calculateOnly, employee } = req.body;
|
||||
logger.log("job-payroll-pay-all", "DEBUG", req.user.email, jobid, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
const { jobs_by_pk: job } = await client
|
||||
|
||||
@@ -11,14 +11,11 @@ const { json } = require("body-parser");
|
||||
Dinero.globalRoundingMode = "HALF_EVEN";
|
||||
|
||||
exports.payall = async function (req, res) {
|
||||
const BearerToken = req.headers.authorization;
|
||||
const { jobid, calculateOnly } = req.body;
|
||||
logger.log("job-payroll-pay-all", "DEBUG", req.user.email, jobid, null);
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
try {
|
||||
const { jobs_by_pk: job } = await client
|
||||
|
||||
13
server/routes/accountingRoutes.js
Normal file
13
server/routes/accountingRoutes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const {payments, payables, receivables} = require("../accounting/qbxml/qbxml");
|
||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
|
||||
router.post('/qbxml/receivables', withUserGraphQLClientMiddleware, receivables);
|
||||
router.post('/qbxml/payables', withUserGraphQLClientMiddleware, payables);
|
||||
router.post('/qbxml/payments', withUserGraphQLClientMiddleware, payments);
|
||||
|
||||
module.exports = router;
|
||||
18
server/routes/adminRoutes.js
Normal file
18
server/routes/adminRoutes.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const fb = require('../firebase/firebase-handler');
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const {createAssociation, createShop, updateShop, updateCounter} = require("../admin/adminops");
|
||||
const validateAdminMiddleware = require("../middleware/validateAdminMiddleware");
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
|
||||
router.post('/createassociation', validateAdminMiddleware, createAssociation);
|
||||
router.post('/createshop', validateAdminMiddleware, createShop);
|
||||
router.post('/updateshop', validateAdminMiddleware, updateShop);
|
||||
router.post('/updatecounter', validateAdminMiddleware, updateCounter);
|
||||
router.post('/updateuser', fb.updateUser);
|
||||
router.post('/getuser', fb.getUser);
|
||||
router.post('/createuser', fb.createUser);
|
||||
|
||||
module.exports = router;
|
||||
11
server/routes/cdkRoutes.js
Normal file
11
server/routes/cdkRoutes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const cdkGetMake = require('../cdk/cdk-get-makes');
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
|
||||
router.post('/getvehicles', withUserGraphQLClientMiddleware, cdkGetMake.default);
|
||||
|
||||
module.exports = router;
|
||||
9
server/routes/dataRoutes.js
Normal file
9
server/routes/dataRoutes.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {autohouse, claimscorp, kaizen} = require('../data/data');
|
||||
|
||||
router.post('/ah', autohouse);
|
||||
router.post('/cc', claimscorp);
|
||||
router.post('/kaizen', kaizen);
|
||||
|
||||
module.exports = router;
|
||||
11
server/routes/intellipayRoutes.js
Normal file
11
server/routes/intellipayRoutes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const {lightbox_credentials, payment_refund, generate_payment_url, postback} = require("../intellipay/intellipay");
|
||||
|
||||
router.post('/lightbox_credentials', validateFirebaseIdTokenMiddleware, lightbox_credentials);
|
||||
router.post('/payment_refund', validateFirebaseIdTokenMiddleware, payment_refund);
|
||||
router.post('/generate_payment_url', validateFirebaseIdTokenMiddleware, generate_payment_url);
|
||||
router.post('/postback', postback);
|
||||
|
||||
module.exports = router;
|
||||
18
server/routes/jobRoutes.js
Normal file
18
server/routes/jobRoutes.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const job = require('../job/job');
|
||||
const {partsScan} = require('../parts-scan/parts-scan');
|
||||
const eventAuthorizationMiddleware = require('../middleware/eventAuthorizationMIddleware');
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const {totals, statustransition, totalsSsu, costing, lifecycle, costingmulti} = require("../job/job");
|
||||
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
|
||||
|
||||
router.post('/totals', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, totals);
|
||||
router.post('/statustransition', eventAuthorizationMiddleware, statustransition);
|
||||
router.post('/totalsssu', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware,totalsSsu);
|
||||
router.post('/costing', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware,costing);
|
||||
router.post('/lifecycle', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, lifecycle);
|
||||
router.post('/costingmulti', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, costingmulti);
|
||||
router.post('/partsscan', validateFirebaseIdTokenMiddleware, withUserGraphQLClientMiddleware, partsScan);
|
||||
|
||||
module.exports = router;
|
||||
13
server/routes/mediaRoutes.js
Normal file
13
server/routes/mediaRoutes.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const {createSignedUploadURL, downloadFiles, renameKeys, deleteFiles} = require('../media/media');
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
|
||||
router.post('/sign', createSignedUploadURL);
|
||||
router.post('/download', downloadFiles);
|
||||
router.post('/rename', renameKeys);
|
||||
router.post('/delete', deleteFiles);
|
||||
|
||||
module.exports = router;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user