Compare commits
5 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eccc992cfa | ||
|
|
d187ed6f73 | ||
|
|
68c1ac3e70 | ||
|
|
2f267a9f2c | ||
|
|
d6fbf02092 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -67,7 +67,6 @@
|
|||||||
*.rb text eol=lf
|
*.rb text eol=lf
|
||||||
*.java text eol=lf
|
*.java text eol=lf
|
||||||
*.php text eol=lf
|
*.php text eol=lf
|
||||||
*.sql text eol=lf
|
|
||||||
|
|
||||||
# Git configuration files
|
# Git configuration files
|
||||||
.gitattributes text eol=lf
|
.gitattributes text eol=lf
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { EditFilled } from "@ant-design/icons";
|
import { EditFilled } from "@ant-design/icons";
|
||||||
import { Alert, Card, Col, Row, Space, Table, Typography } from "antd";
|
import { Alert, Card, Col, Row, Space, Table, Typography } from "antd";
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -9,11 +8,11 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
||||||
|
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
|
||||||
import "./labor-allocations-table.styles.scss";
|
import "./labor-allocations-table.styles.scss";
|
||||||
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
|
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
|
||||||
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
technician: selectTechnician
|
technician: selectTechnician
|
||||||
@@ -65,6 +64,7 @@ export function LaborAllocationsTable({
|
|||||||
key: "total",
|
key: "total",
|
||||||
sorter: (a, b) => a.total - b.total,
|
sorter: (a, b) => a.total - b.total,
|
||||||
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
render: (text, record) => record.total.toFixed(1)
|
render: (text, record) => record.total.toFixed(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -73,6 +73,7 @@ export function LaborAllocationsTable({
|
|||||||
key: "hrs_claimed",
|
key: "hrs_claimed",
|
||||||
sorter: (a, b) => a.claimed - b.claimed,
|
sorter: (a, b) => a.claimed - b.claimed,
|
||||||
sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
render: (text, record) => record.claimed && record.claimed.toFixed(1)
|
render: (text, record) => record.claimed && record.claimed.toFixed(1)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -81,6 +82,7 @@ export function LaborAllocationsTable({
|
|||||||
key: "adjustments",
|
key: "adjustments",
|
||||||
sorter: (a, b) => a.adjustments - b.adjustments,
|
sorter: (a, b) => a.adjustments - b.adjustments,
|
||||||
sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{record.adjustments.toFixed(1)}
|
{record.adjustments.toFixed(1)}
|
||||||
@@ -100,17 +102,17 @@ export function LaborAllocationsTable({
|
|||||||
{
|
{
|
||||||
title: t("jobs.labels.difference"),
|
title: t("jobs.labels.difference"),
|
||||||
dataIndex: "difference",
|
dataIndex: "difference",
|
||||||
|
|
||||||
key: "difference",
|
key: "difference",
|
||||||
sorter: (a, b) => a.difference - b.difference,
|
sorter: (a, b) => a.difference - b.difference,
|
||||||
sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
|
||||||
|
align: "right",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<strong
|
<strong
|
||||||
style={{
|
style={{
|
||||||
color: record.difference >= 0 ? "green" : "red"
|
color: record.difference.toFixed(1) >= 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{_.round(record.difference, 1)}
|
{(Math.abs(record.difference) < 0.05 ? 0 : record.difference).toFixed(1)}
|
||||||
</strong>
|
</strong>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -129,7 +131,6 @@ export function LaborAllocationsTable({
|
|||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}`
|
render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}`
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("joblines.fields.act_price"),
|
title: t("joblines.fields.act_price"),
|
||||||
dataIndex: "act_price",
|
dataIndex: "act_price",
|
||||||
@@ -187,7 +188,7 @@ export function LaborAllocationsTable({
|
|||||||
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
|
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (summary.difference !== 0 && typeof warningCallback === "function") {
|
if (Math.abs(summary.difference.toFixed(1)) !== 0 && typeof warningCallback === "function") {
|
||||||
warningCallback({ key: "labor", warning: t("jobs.labels.outstandinghours") });
|
warningCallback({ key: "labor", warning: t("jobs.labels.outstandinghours") });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,19 +218,21 @@ export function LaborAllocationsTable({
|
|||||||
summary={() => (
|
summary={() => (
|
||||||
<Table.Summary.Row>
|
<Table.Summary.Row>
|
||||||
<Table.Summary.Cell>
|
<Table.Summary.Cell>
|
||||||
<Typography.Title level={4}>{t("general.labels.totals")}</Typography.Title>
|
<Typography.Title level={4} style={{ margin: 0, lineHeight: 1 }}>
|
||||||
|
{t("general.labels.totals")}
|
||||||
|
</Typography.Title>
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>{summary.hrs_total.toFixed(1)}</Table.Summary.Cell>
|
<Table.Summary.Cell align="right">{summary.hrs_total.toFixed(1)}</Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell>
|
<Table.Summary.Cell align="right">{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>{summary.adjustments.toFixed(1)}</Table.Summary.Cell>
|
<Table.Summary.Cell align="right">{summary.adjustments.toFixed(1)}</Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>
|
<Table.Summary.Cell align="right">
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
style={{
|
style={{
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color: summary.difference >= 0 ? "green" : "red"
|
color: summary.difference.toFixed(1) >= 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{summary.difference.toFixed(1)}
|
{(Math.abs(summary.difference) < 0.05 ? 0 : summary.difference).toFixed(1)}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</Table.Summary.Cell>
|
</Table.Summary.Cell>
|
||||||
</Table.Summary.Row>
|
</Table.Summary.Row>
|
||||||
@@ -261,11 +264,10 @@ export function LaborAllocationsTable({
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
{showWarning && summary.difference !== 0 && (
|
{showWarning && Math.abs(summary.difference.toFixed(1)) !== 0 && (
|
||||||
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstandinghours")} />
|
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstandinghours")} />
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(mapStateToProps, null)(LaborAllocationsTable);
|
export default connect(mapStateToProps, null)(LaborAllocationsTable);
|
||||||
|
|||||||
@@ -207,7 +207,6 @@
|
|||||||
- default_prod_list_view
|
- default_prod_list_view
|
||||||
- id
|
- id
|
||||||
- kanban_settings
|
- kanban_settings
|
||||||
- notification_settings
|
|
||||||
- qbo_realmId
|
- qbo_realmId
|
||||||
- shopid
|
- shopid
|
||||||
- useremail
|
- useremail
|
||||||
@@ -223,7 +222,6 @@
|
|||||||
- authlevel
|
- authlevel
|
||||||
- default_prod_list_view
|
- default_prod_list_view
|
||||||
- kanban_settings
|
- kanban_settings
|
||||||
- notification_settings
|
|
||||||
- qbo_realmId
|
- qbo_realmId
|
||||||
filter:
|
filter:
|
||||||
user:
|
user:
|
||||||
@@ -683,31 +681,6 @@
|
|||||||
- exported:
|
- exported:
|
||||||
_eq: false
|
_eq: false
|
||||||
event_triggers:
|
event_triggers:
|
||||||
- name: notifications_bills
|
|
||||||
definition:
|
|
||||||
enable_manual: false
|
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
retry_conf:
|
|
||||||
interval_sec: 10
|
|
||||||
num_retries: 0
|
|
||||||
timeout_sec: 60
|
|
||||||
webhook_from_env: HASURA_API_URL
|
|
||||||
headers:
|
|
||||||
- name: event-secret
|
|
||||||
value_from_env: EVENT_SECRET
|
|
||||||
request_transform:
|
|
||||||
body:
|
|
||||||
action: transform
|
|
||||||
template: |-
|
|
||||||
{
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
method: POST
|
|
||||||
query_params: {}
|
|
||||||
template_engine: Kriti
|
|
||||||
url: '{{$base_url}}/notifications/events/handleBillsChange'
|
|
||||||
version: 2
|
|
||||||
- name: os_bills
|
- name: os_bills
|
||||||
definition:
|
definition:
|
||||||
delete:
|
delete:
|
||||||
@@ -2832,80 +2805,6 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
- table:
|
|
||||||
name: job_watchers
|
|
||||||
schema: public
|
|
||||||
object_relationships:
|
|
||||||
- name: job
|
|
||||||
using:
|
|
||||||
foreign_key_constraint_on: jobid
|
|
||||||
- name: user
|
|
||||||
using:
|
|
||||||
foreign_key_constraint_on: user_email
|
|
||||||
insert_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
check:
|
|
||||||
user:
|
|
||||||
_and:
|
|
||||||
- associations:
|
|
||||||
active:
|
|
||||||
_eq: true
|
|
||||||
- authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
columns:
|
|
||||||
- user_email
|
|
||||||
- created_at
|
|
||||||
- id
|
|
||||||
- jobid
|
|
||||||
comment: ""
|
|
||||||
select_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
columns:
|
|
||||||
- user_email
|
|
||||||
- created_at
|
|
||||||
- id
|
|
||||||
- jobid
|
|
||||||
filter:
|
|
||||||
user:
|
|
||||||
_and:
|
|
||||||
- associations:
|
|
||||||
active:
|
|
||||||
_eq: true
|
|
||||||
- authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
comment: ""
|
|
||||||
update_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
columns:
|
|
||||||
- user_email
|
|
||||||
- created_at
|
|
||||||
- id
|
|
||||||
- jobid
|
|
||||||
filter:
|
|
||||||
user:
|
|
||||||
_and:
|
|
||||||
- associations:
|
|
||||||
active:
|
|
||||||
_eq: true
|
|
||||||
- authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
check: null
|
|
||||||
comment: ""
|
|
||||||
delete_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
filter:
|
|
||||||
user:
|
|
||||||
_and:
|
|
||||||
- associations:
|
|
||||||
active:
|
|
||||||
_eq: true
|
|
||||||
- authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
comment: ""
|
|
||||||
- table:
|
- table:
|
||||||
name: joblines
|
name: joblines
|
||||||
schema: public
|
schema: public
|
||||||
@@ -4427,58 +4326,6 @@
|
|||||||
template_engine: Kriti
|
template_engine: Kriti
|
||||||
url: '{{$base_url}}/record-handler/arms'
|
url: '{{$base_url}}/record-handler/arms'
|
||||||
version: 2
|
version: 2
|
||||||
- name: notifications_jobs
|
|
||||||
definition:
|
|
||||||
enable_manual: false
|
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
update:
|
|
||||||
columns:
|
|
||||||
- queued_for_parts
|
|
||||||
- employee_prep
|
|
||||||
- clm_total
|
|
||||||
- towin
|
|
||||||
- employee_body
|
|
||||||
- converted
|
|
||||||
- scheduled_in
|
|
||||||
- scheduled_completion
|
|
||||||
- scheduled_delivery
|
|
||||||
- actual_delivery
|
|
||||||
- actual_completion
|
|
||||||
- alt_transport
|
|
||||||
- date_exported
|
|
||||||
- status
|
|
||||||
- employee_csr
|
|
||||||
- actual_in
|
|
||||||
- deliverchecklist
|
|
||||||
- comment
|
|
||||||
- job_totals
|
|
||||||
- employee_refinish
|
|
||||||
- inproduction
|
|
||||||
- production_vars
|
|
||||||
- intakechecklist
|
|
||||||
- cieca_ttl
|
|
||||||
- date_invoiced
|
|
||||||
retry_conf:
|
|
||||||
interval_sec: 10
|
|
||||||
num_retries: 0
|
|
||||||
timeout_sec: 60
|
|
||||||
webhook_from_env: HASURA_API_URL
|
|
||||||
headers:
|
|
||||||
- name: event-secret
|
|
||||||
value_from_env: EVENT_SECRET
|
|
||||||
request_transform:
|
|
||||||
body:
|
|
||||||
action: transform
|
|
||||||
template: |-
|
|
||||||
{
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
method: POST
|
|
||||||
query_params: {}
|
|
||||||
template_engine: Kriti
|
|
||||||
url: '{{$base_url}}/notifications/events/handleJobsChange'
|
|
||||||
version: 2
|
|
||||||
- name: os_jobs
|
- name: os_jobs
|
||||||
definition:
|
definition:
|
||||||
delete:
|
delete:
|
||||||
@@ -4822,57 +4669,6 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
- table:
|
|
||||||
name: notifications
|
|
||||||
schema: public
|
|
||||||
object_relationships:
|
|
||||||
- name: association
|
|
||||||
using:
|
|
||||||
foreign_key_constraint_on: associationid
|
|
||||||
- name: job
|
|
||||||
using:
|
|
||||||
foreign_key_constraint_on: jobid
|
|
||||||
select_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
columns:
|
|
||||||
- associationid
|
|
||||||
- created_at
|
|
||||||
- fcm_data
|
|
||||||
- fcm_message
|
|
||||||
- fcm_title
|
|
||||||
- id
|
|
||||||
- jobid
|
|
||||||
- meta
|
|
||||||
- read
|
|
||||||
- ui_translation_meta
|
|
||||||
- ui_translation_string
|
|
||||||
- updated_at
|
|
||||||
filter:
|
|
||||||
association:
|
|
||||||
_and:
|
|
||||||
- active:
|
|
||||||
_eq: true
|
|
||||||
- user:
|
|
||||||
authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
comment: ""
|
|
||||||
update_permissions:
|
|
||||||
- role: user
|
|
||||||
permission:
|
|
||||||
columns:
|
|
||||||
- meta
|
|
||||||
- read
|
|
||||||
filter:
|
|
||||||
association:
|
|
||||||
_and:
|
|
||||||
- active:
|
|
||||||
_eq: true
|
|
||||||
- user:
|
|
||||||
authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
check: null
|
|
||||||
comment: ""
|
|
||||||
- table:
|
- table:
|
||||||
name: owners
|
name: owners
|
||||||
schema: public
|
schema: public
|
||||||
@@ -5113,32 +4909,6 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
check: null
|
check: null
|
||||||
event_triggers:
|
|
||||||
- name: notifications_parts_dispatch
|
|
||||||
definition:
|
|
||||||
enable_manual: false
|
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
retry_conf:
|
|
||||||
interval_sec: 10
|
|
||||||
num_retries: 0
|
|
||||||
timeout_sec: 60
|
|
||||||
webhook_from_env: HASURA_API_URL
|
|
||||||
headers:
|
|
||||||
- name: event-secret
|
|
||||||
value_from_env: EVENT_SECRET
|
|
||||||
request_transform:
|
|
||||||
body:
|
|
||||||
action: transform
|
|
||||||
template: |-
|
|
||||||
{
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
method: POST
|
|
||||||
query_params: {}
|
|
||||||
template_engine: Kriti
|
|
||||||
url: '{{$base_url}}/notifications/events/handlePartsDispatchChange'
|
|
||||||
version: 2
|
|
||||||
- table:
|
- table:
|
||||||
name: parts_dispatch_lines
|
name: parts_dispatch_lines
|
||||||
schema: public
|
schema: public
|
||||||
@@ -6109,36 +5879,6 @@
|
|||||||
_eq: true
|
_eq: true
|
||||||
check: null
|
check: null
|
||||||
event_triggers:
|
event_triggers:
|
||||||
- name: notifications_tasks
|
|
||||||
definition:
|
|
||||||
enable_manual: false
|
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
update:
|
|
||||||
columns:
|
|
||||||
- assigned_to
|
|
||||||
- completed
|
|
||||||
- description
|
|
||||||
retry_conf:
|
|
||||||
interval_sec: 10
|
|
||||||
num_retries: 0
|
|
||||||
timeout_sec: 60
|
|
||||||
webhook_from_env: HASURA_API_URL
|
|
||||||
headers:
|
|
||||||
- name: event-secret
|
|
||||||
value_from_env: EVENT_SECRET
|
|
||||||
request_transform:
|
|
||||||
body:
|
|
||||||
action: transform
|
|
||||||
template: |-
|
|
||||||
{
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
method: POST
|
|
||||||
query_params: {}
|
|
||||||
template_engine: Kriti
|
|
||||||
url: '{{$base_url}}/notifications/events/handleTasksChange'
|
|
||||||
version: 2
|
|
||||||
- name: tasks_assigned_changed
|
- name: tasks_assigned_changed
|
||||||
definition:
|
definition:
|
||||||
enable_manual: false
|
enable_manual: false
|
||||||
@@ -6295,32 +6035,6 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
event_triggers:
|
|
||||||
- name: notifications_time_tickets
|
|
||||||
definition:
|
|
||||||
enable_manual: false
|
|
||||||
insert:
|
|
||||||
columns: '*'
|
|
||||||
retry_conf:
|
|
||||||
interval_sec: 10
|
|
||||||
num_retries: 0
|
|
||||||
timeout_sec: 60
|
|
||||||
webhook_from_env: HASURA_API_URL
|
|
||||||
headers:
|
|
||||||
- name: event-secret
|
|
||||||
value_from_env: EVENT_SECRET
|
|
||||||
request_transform:
|
|
||||||
body:
|
|
||||||
action: transform
|
|
||||||
template: |-
|
|
||||||
{
|
|
||||||
"success": true
|
|
||||||
}
|
|
||||||
method: POST
|
|
||||||
query_params: {}
|
|
||||||
template_engine: Kriti
|
|
||||||
url: '{{$base_url}}/notifications/events/handleTimeTicketsChange'
|
|
||||||
version: 2
|
|
||||||
- table:
|
- table:
|
||||||
name: transitions
|
name: transitions
|
||||||
schema: public
|
schema: public
|
||||||
|
|||||||
@@ -39,50 +39,50 @@ END;
|
|||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
begin
|
begin
|
||||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||||
UPDATE counters
|
UPDATE counters
|
||||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||||
RETURNING concat(prefix,count) into new.ro_number;
|
RETURNING concat(prefix,count) into new.ro_number;
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
||||||
LANGUAGE plpgsql SECURITY DEFINER
|
LANGUAGE plpgsql SECURITY DEFINER
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
shopid uuid ;
|
shopid uuid ;
|
||||||
email text;
|
email text;
|
||||||
BEGIN
|
BEGIN
|
||||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||||
IF TG_OP = 'INSERT'
|
IF TG_OP = 'INSERT'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
ELSIF TG_OP = 'UPDATE'
|
ELSIF TG_OP = 'UPDATE'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
ELSIF TG_OP = 'DELETE'
|
ELSIF TG_OP = 'DELETE'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||||
RETURN OLD;
|
RETURN OLD;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
||||||
LANGUAGE sql
|
LANGUAGE sql
|
||||||
AS $$
|
AS $$
|
||||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||||
WHERE a.value != b.value OR b.key IS NULL;
|
WHERE a.value != b.value OR b.key IS NULL;
|
||||||
$$;
|
$$;
|
||||||
CREATE TABLE public.bills (
|
CREATE TABLE public.bills (
|
||||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||||
@@ -211,33 +211,33 @@ CREATE TABLE public.exportlog (
|
|||||||
);
|
);
|
||||||
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
||||||
LANGUAGE plpgsql STABLE
|
LANGUAGE plpgsql STABLE
|
||||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
FROM
|
FROM
|
||||||
exportlog e;
|
exportlog e;
|
||||||
ELSE RETURN query
|
ELSE RETURN query
|
||||||
SELECT
|
SELECT
|
||||||
e.*
|
e.*
|
||||||
FROM
|
FROM
|
||||||
exportlog e
|
exportlog e
|
||||||
LEFT JOIN jobs j on j.id = e.jobid
|
LEFT JOIN jobs j on j.id = e.jobid
|
||||||
LEFT JOIN payments p
|
LEFT JOIN payments p
|
||||||
ON p.id = e.paymentid
|
ON p.id = e.paymentid
|
||||||
LEFT JOIN bills b
|
LEFT JOIN bills b
|
||||||
ON e.billid = b.id
|
ON e.billid = b.id
|
||||||
WHERE
|
WHERE
|
||||||
(
|
(
|
||||||
j.ro_number ILIKE '%' || search || '%'
|
j.ro_number ILIKE '%' || search || '%'
|
||||||
OR b.invoice_number ILIKE '%' || search || '%'
|
OR b.invoice_number ILIKE '%' || search || '%'
|
||||||
OR p.paymentnum ILIKE '%' || search || '%'
|
OR p.paymentnum ILIKE '%' || search || '%'
|
||||||
OR e.useremail ILIKE '%' || search || '%'
|
OR e.useremail ILIKE '%' || search || '%'
|
||||||
)
|
)
|
||||||
AND (e.jobid = j.id
|
AND (e.jobid = j.id
|
||||||
or e.paymentid = p.id
|
or e.paymentid = p.id
|
||||||
or e.billid = b.id)
|
or e.billid = b.id)
|
||||||
;
|
;
|
||||||
END IF;
|
END IF;
|
||||||
END $$;
|
END $$;
|
||||||
CREATE TABLE public.jobs (
|
CREATE TABLE public.jobs (
|
||||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
-- Could not auto-generate a down migration.
|
|
||||||
-- Please write an appropriate down migration for the SQL below:
|
|
||||||
-- alter table "public"."associations" add column "notification_settings" jsonb
|
|
||||||
-- null;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."associations" add column "notification_settings" jsonb
|
|
||||||
null;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE "public"."notifications";
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
CREATE TABLE "public"."notifications" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "job_id" uuid NOT NULL, "association_id" uuid NOT NULL, "ui_translation_string" text NOT NULL, "ui_translation_meta" jsonb, "fcm_title" text NOT NULL, "fcm_message" text NOT NULL, "fcm_data" jsonb, "read" timestamptz, "meta" jsonb, "scenario" Integer NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("association_id") REFERENCES "public"."associations"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."notifications" IS E'Real Time Notifications System';
|
|
||||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
comment on column "public"."notifications"."scenario" is E'Real Time Notifications System';
|
|
||||||
alter table "public"."notifications" alter column "scenario" drop not null;
|
|
||||||
alter table "public"."notifications" add column "scenario" int4;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" drop column "scenario" cascade;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
-- Could not auto-generate a down migration.
|
|
||||||
-- Please write an appropriate down migration for the SQL below:
|
|
||||||
-- alter table "public"."notifications" add column "scenario" text
|
|
||||||
-- not null;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" add column "scenario" text
|
|
||||||
not null;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE "public"."job_watchers";
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
CREATE TABLE "public"."job_watchers" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "job_id" uuid NOT NULL, "user_email" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_email") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."job_watchers" IS E'Job Watchers';
|
|
||||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
comment on column "public"."notifications"."scenario" is E'Real Time Notifications System';
|
|
||||||
alter table "public"."notifications" alter column "scenario" drop not null;
|
|
||||||
alter table "public"."notifications" add column "scenario" text;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" drop column "scenario" cascade;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
-- Could not auto-generate a down migration.
|
|
||||||
-- Please write an appropriate down migration for the SQL below:
|
|
||||||
-- update associations set notification_settings = '{}';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
update associations set notification_settings = '{}';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."associations" alter column "notification_settings" drop not null;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."associations" alter column "notification_settings" set not null;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
-- Could not auto-generate a down migration.
|
|
||||||
-- Please write an appropriate down migration for the SQL below:
|
|
||||||
-- update notifications set meta = '{}';
|
|
||||||
-- update notifications set fcm_data = '{}';
|
|
||||||
-- update notifications set ui_translation_meta = '{}';
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
update notifications set meta = '{}';
|
|
||||||
update notifications set fcm_data = '{}';
|
|
||||||
update notifications set ui_translation_meta = '{}';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "meta" drop not null;
|
|
||||||
ALTER TABLE "public"."notifications" ALTER COLUMN "meta" drop default;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "meta" set default jsonb_build_object();
|
|
||||||
alter table "public"."notifications" alter column "meta" set not null;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "fcm_data" drop not null;
|
|
||||||
ALTER TABLE "public"."notifications" ALTER COLUMN "fcm_data" drop default;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "fcm_data" set default jsonb_build_object();
|
|
||||||
alter table "public"."notifications" alter column "fcm_data" set not null;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "ui_translation_meta" drop not null;
|
|
||||||
ALTER TABLE "public"."notifications" ALTER COLUMN "ui_translation_meta" drop default;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
alter table "public"."notifications" alter column "ui_translation_meta" set default jsonb_build_object();
|
|
||||||
alter table "public"."notifications" alter column "ui_translation_meta" set not null;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" rename column "jobid" to "job_id";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" rename column "job_id" to "jobid";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" rename column "associationid" to "association_id";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."notifications" rename column "association_id" to "associationid";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."job_watchers" rename column "jobid" to "job_id";
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
alter table "public"."job_watchers" rename column "job_id" to "jobid";
|
|
||||||
@@ -39,50 +39,50 @@ END;
|
|||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
begin
|
begin
|
||||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||||
UPDATE counters
|
UPDATE counters
|
||||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||||
RETURNING concat(prefix,count) into new.ro_number;
|
RETURNING concat(prefix,count) into new.ro_number;
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
||||||
LANGUAGE plpgsql SECURITY DEFINER
|
LANGUAGE plpgsql SECURITY DEFINER
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
shopid uuid ;
|
shopid uuid ;
|
||||||
email text;
|
email text;
|
||||||
BEGIN
|
BEGIN
|
||||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||||
IF TG_OP = 'INSERT'
|
IF TG_OP = 'INSERT'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
ELSIF TG_OP = 'UPDATE'
|
ELSIF TG_OP = 'UPDATE'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
ELSIF TG_OP = 'DELETE'
|
ELSIF TG_OP = 'DELETE'
|
||||||
THEN
|
THEN
|
||||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||||
RETURN OLD;
|
RETURN OLD;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
||||||
LANGUAGE sql
|
LANGUAGE sql
|
||||||
AS $$
|
AS $$
|
||||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||||
WHERE a.value != b.value OR b.key IS NULL;
|
WHERE a.value != b.value OR b.key IS NULL;
|
||||||
$$;
|
$$;
|
||||||
CREATE TABLE public.bills (
|
CREATE TABLE public.bills (
|
||||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||||
@@ -211,33 +211,33 @@ CREATE TABLE public.exportlog (
|
|||||||
);
|
);
|
||||||
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
||||||
LANGUAGE plpgsql STABLE
|
LANGUAGE plpgsql STABLE
|
||||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
FROM
|
FROM
|
||||||
exportlog e;
|
exportlog e;
|
||||||
ELSE RETURN query
|
ELSE RETURN query
|
||||||
SELECT
|
SELECT
|
||||||
e.*
|
e.*
|
||||||
FROM
|
FROM
|
||||||
exportlog e
|
exportlog e
|
||||||
LEFT JOIN jobs j on j.id = e.jobid
|
LEFT JOIN jobs j on j.id = e.jobid
|
||||||
LEFT JOIN payments p
|
LEFT JOIN payments p
|
||||||
ON p.id = e.paymentid
|
ON p.id = e.paymentid
|
||||||
LEFT JOIN bills b
|
LEFT JOIN bills b
|
||||||
ON e.billid = b.id
|
ON e.billid = b.id
|
||||||
WHERE
|
WHERE
|
||||||
(
|
(
|
||||||
j.ro_number ILIKE '%' || search || '%'
|
j.ro_number ILIKE '%' || search || '%'
|
||||||
OR b.invoice_number ILIKE '%' || search || '%'
|
OR b.invoice_number ILIKE '%' || search || '%'
|
||||||
OR p.paymentnum ILIKE '%' || search || '%'
|
OR p.paymentnum ILIKE '%' || search || '%'
|
||||||
OR e.useremail ILIKE '%' || search || '%'
|
OR e.useremail ILIKE '%' || search || '%'
|
||||||
)
|
)
|
||||||
AND (e.jobid = j.id
|
AND (e.jobid = j.id
|
||||||
or e.paymentid = p.id
|
or e.paymentid = p.id
|
||||||
or e.billid = b.id)
|
or e.billid = b.id)
|
||||||
;
|
;
|
||||||
END IF;
|
END IF;
|
||||||
END $$;
|
END $$;
|
||||||
CREATE TABLE public.jobs (
|
CREATE TABLE public.jobs (
|
||||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||||
|
|||||||
@@ -329,7 +329,8 @@ const main = async () => {
|
|||||||
main().catch((error) => {
|
main().catch((error) => {
|
||||||
logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, {
|
logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
errorjson: JSON.stringify(error)
|
errorjson: JSON.stringify(error),
|
||||||
|
stack: error.stack
|
||||||
});
|
});
|
||||||
// Note: If we want the app to crash on all uncaught async operations, we would
|
// Note: If we want the app to crash on all uncaught async operations, we would
|
||||||
// need to put a `process.exit(1);` here
|
// need to put a `process.exit(1);` here
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ exports.autohouse = require("./autohouse").default;
|
|||||||
exports.chatter = require("./chatter").default;
|
exports.chatter = require("./chatter").default;
|
||||||
exports.claimscorp = require("./claimscorp").default;
|
exports.claimscorp = require("./claimscorp").default;
|
||||||
exports.kaizen = require("./kaizen").default;
|
exports.kaizen = require("./kaizen").default;
|
||||||
|
exports.usageReport = require("./usageReport").default;
|
||||||
90
server/data/usageReport.js
Normal file
90
server/data/usageReport.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
|
});
|
||||||
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
|
const emailer = require("../email/sendemail");
|
||||||
|
const moment = require("moment-timezone");
|
||||||
|
const converter = require("json-2-csv");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const InstanceMgr = require("../utils/instanceMgr").default;
|
||||||
|
|
||||||
|
exports.default = async (req, res) => {
|
||||||
|
try {
|
||||||
|
logger.log("usage-report-email-start", "debug", req?.user?.email, null, {});
|
||||||
|
|
||||||
|
if (InstanceMgr({ rome: false, imex: true })) {
|
||||||
|
//Disable for ImEX at the moment.
|
||||||
|
res.sendStatus(403);
|
||||||
|
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Validate using autohouse token header.
|
||||||
|
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
|
||||||
|
res.sendStatus(401);
|
||||||
|
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Query the usage data.
|
||||||
|
const queryResults = await client.request(queries.STATUS_UPDATE, {
|
||||||
|
today: moment().startOf("day").subtract(3, "days"),
|
||||||
|
period: moment().subtract(90, "days").startOf("day")
|
||||||
|
});
|
||||||
|
|
||||||
|
//Massage the data.
|
||||||
|
const shopList = queryResults.bodyshops.map((shop) => ({
|
||||||
|
"Shop Name": shop.shopname,
|
||||||
|
"Days Since Creation": moment().diff(moment(shop.created_at), "days"),
|
||||||
|
"Jobs Created": shop.jobs_created.aggregate.count,
|
||||||
|
"Jobs Updated": shop.jobs_updated.aggregate.count,
|
||||||
|
"Owners Created": shop.owners_created.aggregate.count,
|
||||||
|
"Owners Updated": shop.owners_updated.aggregate.count,
|
||||||
|
"Vehicles Created": shop.vehicles_created.aggregate.count,
|
||||||
|
"Vehicles Updated": shop.vehicles_updated.aggregate.count,
|
||||||
|
"Tasks Created": shop.tasks_created.aggregate.count,
|
||||||
|
"Tasks Updated": shop.tasks_updated.aggregate.count
|
||||||
|
}));
|
||||||
|
|
||||||
|
const csv = converter.json2csv(shopList, { emptyFieldValue: "" });
|
||||||
|
emailer
|
||||||
|
.sendTaskEmail({
|
||||||
|
to: ["patrick.fic@convenient-brands.com", "bradley.rhoades@convenient-brands.com"],
|
||||||
|
subject: `RO Usage Report - ${moment().format("MM/DD/YYYY")}`,
|
||||||
|
text: `
|
||||||
|
Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included.
|
||||||
|
- Updated values should be higher than created values.
|
||||||
|
- Counts are inclusive of the last 3 days of data.
|
||||||
|
`,
|
||||||
|
attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }]
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
logger.log("usage-report-email-success", "debug", req?.user?.email, null, {
|
||||||
|
csv
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.log("usage-report-email-send-error", "ERROR", req?.user?.email, null, {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("usage-report-email-error", "ERROR", req?.user?.email, null, {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
res.status(500).json({ error: error.message, stack: error.stack });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2617,3 +2617,76 @@ exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: timestamptz!) {
|
||||||
|
bodyshops(where: { created_at: { _gte: $period } }) {
|
||||||
|
shopname
|
||||||
|
id
|
||||||
|
created_at
|
||||||
|
jobs_created: jobs_aggregate(where: { created_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jobs_updated: jobs_aggregate(where: { updated_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
owners_created: owners_aggregate(where: { created_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
owners_updated: owners_aggregate(where: { updated_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vehicles_created: vehicles_aggregate(
|
||||||
|
where: { created_at: { _gte: $today } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vehicles_updated: vehicles_aggregate(
|
||||||
|
where: { updated_at: { _gte: $today } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks_created: tasks_aggregate(where: { created_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks_updated: tasks_aggregate(where: { updated_at: { _gte: $today } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jobs {
|
||||||
|
parts_orders_created: parts_orders_aggregate(
|
||||||
|
where: { created_at: { _gte: $today } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts_orders_updated: parts_orders_aggregate(
|
||||||
|
where: { updated_at: { _gte: $today } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handleJobsChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Jobs change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handleJobsChange;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handleBillsChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Bills change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handleBillsChange;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handlePartsDispatchChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Parts Dispatch change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handlePartsDispatchChange;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handlePartsOrderChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Parts Order change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handlePartsOrderChange;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handleTasksChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Tasks change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handleTasksChange;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const handleTimeTicketsChange = (req, res) => {
|
|
||||||
return res.status(200).json({ message: "Time Tickets change handled." });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = handleTimeTicketsChange;
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { autohouse, claimscorp, chatter, kaizen } = require("../data/data");
|
const { autohouse, claimscorp, chatter, kaizen, usageReport } = require("../data/data");
|
||||||
|
|
||||||
router.post("/ah", autohouse);
|
router.post("/ah", autohouse);
|
||||||
router.post("/cc", claimscorp);
|
router.post("/cc", claimscorp);
|
||||||
router.post("/chatter", chatter);
|
router.post("/chatter", chatter);
|
||||||
router.post("/kaizen", kaizen);
|
router.post("/kaizen", kaizen);
|
||||||
|
router.post("/usagereport", usageReport);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1,28 +1,12 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||||
const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler");
|
const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler");
|
||||||
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
|
|
||||||
|
|
||||||
const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange");
|
|
||||||
const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange");
|
|
||||||
const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange");
|
|
||||||
const handleTimeTicketsChange = require("../notifications/eventHandlers/handleTimeTicketsChange");
|
|
||||||
const handleJobsChange = require("../notifications/eventHandlers/handeJobsChange");
|
|
||||||
const handleBillsChange = require("../notifications/eventHandlers/handleBillsChange");
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// These are FCM handlers
|
router.use(validateFirebaseIdTokenMiddleware);
|
||||||
router.post("/subscribe", validateFirebaseIdTokenMiddleware, subscribe);
|
|
||||||
router.post("/unsubscribe", validateFirebaseIdTokenMiddleware, unsubscribe);
|
|
||||||
router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification);
|
|
||||||
|
|
||||||
// Hasura Entry points for creating notifications
|
router.post("/subscribe", subscribe);
|
||||||
router.post("/events/handleJobsChange", eventAuthorizationMiddleware, handleJobsChange);
|
router.post("/unsubscribe", unsubscribe);
|
||||||
router.post("/events/handleBillsChange", eventAuthorizationMiddleware, handleBillsChange);
|
router.post("/sendtestnotification", sendNotification);
|
||||||
router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange);
|
|
||||||
router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange);
|
|
||||||
router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange);
|
|
||||||
router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTimeTicketsChange);
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ exports.taskHandler = async (req, res) => {
|
|||||||
|
|
||||||
return res.status(200).send(csv);
|
return res.status(200).send(csv);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message, stack: error.stackTrace });
|
res.status(500).json({ error: error.message, stack: error.stack });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user