IO-992 WIP Job Audits

This commit is contained in:
Patrick Fic
2021-07-28 15:25:01 -07:00
parent 6bf8eacfbd
commit 46ddc440fe
16 changed files with 449 additions and 12 deletions

View File

@@ -1138,6 +1138,111 @@
<folder_node> <folder_node>
<name>messages</name> <name>messages</name>
<children> <children>
<concept_node>
<name>billposted</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>billupdated</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobassignmentchange</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobassignmentremoved</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobchecklist</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>jobconverted</name> <name>jobconverted</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -1159,6 +1264,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>jobfieldchanged</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>jobimported</name> <name>jobimported</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -1180,6 +1306,90 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>jobinproductionchange</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobmodifylbradj</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobspartsorder</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobspartsreturn</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>jobstatuschange</name> <name>jobstatuschange</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -26,6 +26,8 @@ import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-bu
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -33,6 +35,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })), dispatch(setModalContext({ context: context, modal: "partsOrder" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export default connect( export default connect(
@@ -40,7 +44,10 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(BillDetailEditcontainer); )(BillDetailEditcontainer);
export function BillDetailEditcontainer({ setPartsOrderContext }) { export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
}) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -134,6 +141,12 @@ export function BillDetailEditcontainer({ setPartsOrderContext }) {
}); });
await Promise.all(updates); await Promise.all(updates);
insertAuditTrail({
jobid: bill.jobid,
billid: search.billid,
operation: AuditTrailMapping.billupdated(bill.invoice_number),
});
await refetch(); await refetch();
form.setFieldsValue(transformData(data)); form.setFieldsValue(transformData(data));
form.resetFields(); form.resetFields();

View File

@@ -11,12 +11,14 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS, QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB, UPDATE_JOB,
} from "../../graphql/jobs.queries"; } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors"; import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import { handleUpload } from "../documents-upload/documents-upload.utility"; import { handleUpload } from "../documents-upload/documents-upload.utility";
@@ -27,6 +29,8 @@ const mapStateToProps = createStructuredSelector({
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")), toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
function BillEnterModalContainer({ function BillEnterModalContainer({
@@ -34,6 +38,7 @@ function BillEnterModalContainer({
toggleModalVisible, toggleModalVisible,
bodyshop, bodyshop,
currentUser, currentUser,
insertAuditTrail,
}) { }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -83,7 +88,7 @@ function BillEnterModalContainer({
}, },
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"], refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
}); });
console.log("adjustmentsToInsert", adjustmentsToInsert);
const adjKeys = Object.keys(adjustmentsToInsert); const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) { if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them. //Query the adjustments, merge, and update them.
@@ -116,7 +121,12 @@ function BillEnterModalContainer({
message: JSON.stringify(jobUpdate.errors), message: JSON.stringify(jobUpdate.errors),
}), }),
}); });
return;
} }
insertAuditTrail({
jobid: values.jobid,
operation: AuditTrailMapping.jobmodifylbradj(),
});
} }
if (!!r1.errors) { if (!!r1.errors) {
@@ -172,6 +182,12 @@ function BillEnterModalContainer({
}); });
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch(); if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
});
if (enterAgain) { if (enterAgain) {
form.resetFields(); form.resetFields();
form.setFieldsValue({ billlines: [] }); form.setFieldsValue({ billlines: [] });

View File

@@ -16,16 +16,20 @@ import {
import ConfigFormComponents from "../../../config-form-components/config-form-components.component"; import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component"; import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
import moment from "moment-business-days"; import moment from "moment-business-days";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function JobChecklistForm({ export function JobChecklistForm({
insertAuditTrail,
formItems, formItems,
bodyshop, bodyshop,
currentUser, currentUser,
@@ -60,10 +64,11 @@ export function JobChecklistForm({
...job.production_vars, ...job.production_vars,
...values.production_vars, ...values.production_vars,
note: note:
values.production_vars &&
values.production_vars.note && values.production_vars.note &&
values.production_vars.note !== "" values.production_vars.note !== ""
? values.production_vars.note ? job.production_vars && values.production_vars.note
: job.production_vars.note, : job.production_vars && job.production_vars.note,
}, },
}), }),
...(type === "intake" && { ...(type === "intake" && {
@@ -114,6 +119,17 @@ export function JobChecklistForm({
if (!!!result.errors) { if (!!!result.errors) {
notification["success"]({ message: t("checklist.successes.completed") }); notification["success"]({ message: t("checklist.successes.completed") });
history.push(`/manage/jobs/${jobId}`); history.push(`/manage/jobs/${jobId}`);
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.jobchecklist(
type,
(type === "deliver" && values.removeFromProduction && false) ||
(type === "intake" && values.addToProduction),
(type === "intake" && bodyshop.md_ro_statuses.default_arrived) ||
(type === "deliver" && bodyshop.md_ro_statuses.default_delivered)
),
});
} else { } else {
notification["error"]({ notification["error"]({
message: t("checklist.errors.complete", { message: t("checklist.errors.complete", {

View File

@@ -37,8 +37,8 @@ export function JobEmployeeAssignments({
}); });
const [visibility, setVisibility] = useState(false); const [visibility, setVisibility] = useState(false);
const onChange = (e) => { const onChange = (value, option) => {
setAssignment({ ...assignment, employeeid: e }); setAssignment({ ...assignment, employeeid: value, name: option.name });
}; };
const popContent = ( const popContent = (
@@ -56,7 +56,11 @@ export function JobEmployeeAssignments({
} }
> >
{bodyshop.employees.map((emp) => ( {bodyshop.employees.map((emp) => (
<Select.Option value={emp.id} key={emp.id}> <Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`} {`${emp.first_name} ${emp.last_name}`}
</Select.Option> </Select.Option>
))} ))}

View File

@@ -6,14 +6,34 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_ASSIGNMENTS } from "../../graphql/jobs.queries"; import { UPDATE_JOB_ASSIGNMENTS } from "../../graphql/jobs.queries";
import JobEmployeeAssignmentsComponent from "./job-employee-assignments.component"; import JobEmployeeAssignmentsComponent from "./job-employee-assignments.component";
export default function JobEmployeeAssignmentsContainer({ job, refetch }) { import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobEmployeeAssignmentsContainer);
export function JobEmployeeAssignmentsContainer({
job,
refetch,
insertAuditTrail,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB_ASSIGNMENTS); const [updateJob] = useMutation(UPDATE_JOB_ASSIGNMENTS);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleAdd = async (assignment) => { const handleAdd = async (assignment) => {
setLoading(true); setLoading(true);
const { operation, employeeid } = assignment; const { operation, employeeid, name } = assignment;
logImEXEvent("job_assign_employee", { operation }); logImEXEvent("job_assign_employee", { operation });
let empAssignment = determineFieldName(operation); let empAssignment = determineFieldName(operation);
@@ -23,6 +43,11 @@ export default function JobEmployeeAssignmentsContainer({ job, refetch }) {
}); });
if (refetch) refetch(); if (refetch) refetch();
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentchange(operation, name),
});
if (!!result.errors) { if (!!result.errors) {
notification["error"]({ notification["error"]({
message: t("jobs.errors.assigning", { message: t("jobs.errors.assigning", {
@@ -48,6 +73,10 @@ export default function JobEmployeeAssignmentsContainer({ job, refetch }) {
}), }),
}); });
} }
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobassignmentremoved(operation),
});
setLoading(false); setLoading(false);
}; };

View File

@@ -1,7 +1,10 @@
import { notification } from "antd"; import { notification } from "antd";
import i18n from "i18next"; import i18n from "i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { store } from "../../redux/store";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
export default function AddToProduction( export default function AddToProduction(
apolloClient, apolloClient,
@@ -21,6 +24,13 @@ export default function AddToProduction(
notification["success"]({ notification["success"]({
message: i18n.t("jobs.successes.save"), message: i18n.t("jobs.successes.save"),
}); });
store.dispatch(
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.jobinproductionchange(!remove),
})
);
if (completionCallback) completionCallback(); if (completionCallback) completionCallback();
}) })
.catch((error) => { .catch((error) => {

View File

@@ -9,6 +9,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries"; import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries"; import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries"; import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setEmailOptions } from "../../redux/email/email.actions"; import { setEmailOptions } from "../../redux/email/email.actions";
import { import {
setModalContext, setModalContext,
@@ -19,6 +20,7 @@ import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
@@ -36,6 +38,8 @@ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")), toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")),
setBillEnterContext: (context) => setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })), dispatch(setModalContext({ context: context, modal: "billEnter" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function PartsOrderModalContainer({ export function PartsOrderModalContainer({
@@ -45,6 +49,7 @@ export function PartsOrderModalContainer({
bodyshop, bodyshop,
setEmailOptions, setEmailOptions,
setBillEnterContext, setBillEnterContext,
insertAuditTrail,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -109,6 +114,18 @@ export function PartsOrderModalContainer({
: bodyshop.md_order_statuses.default_ordered || "Ordered*", : bodyshop.md_order_statuses.default_ordered || "Ordered*",
}, },
}); });
insertAuditTrail({
jobid: jobId,
operation: isReturn
? AuditTrailMapping.jobspartsreturn(
insertResult.data.insert_parts_orders.returning[0].order_number
)
: AuditTrailMapping.jobspartsorder(
insertResult.data.insert_parts_orders.returning[0].order_number
),
});
if (!!jobLinesResult.errors) { if (!!jobLinesResult.errors) {
notification["error"]({ notification["error"]({
message: t("parts_orders.errors.creating"), message: t("parts_orders.errors.creating"),

View File

@@ -5,6 +5,7 @@ export const INSERT_NEW_PARTS_ORDERS = gql`
insert_parts_orders(objects: $po) { insert_parts_orders(objects: $po) {
returning { returning {
id id
order_number
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import Icon, {
FileImageFilled, FileImageFilled,
PrinterFilled, PrinterFilled,
ToolFilled, ToolFilled,
HistoryOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
Button, Button,
@@ -46,6 +47,8 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component"; import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -54,6 +57,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) => setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })), dispatch(setModalContext({ context: context, modal: "printCenter" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function JobsDetailPage({ export function JobsDetailPage({
setPrintCenterContext, setPrintCenterContext,
@@ -61,6 +66,7 @@ export function JobsDetailPage({
job, job,
mutationUpdateJob, mutationUpdateJob,
handleSubmit, handleSubmit,
insertAuditTrail,
refetch, refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -82,6 +88,7 @@ export function JobsDetailPage({
const handleFinish = async (values) => { const handleFinish = async (values) => {
setLoading(true); setLoading(true);
const result = await mutationUpdateJob({ const result = await mutationUpdateJob({
variables: { variables: {
jobId: job.id, jobId: job.id,
@@ -106,6 +113,60 @@ export function JobsDetailPage({
notification["success"]({ notification["success"]({
message: t("jobs.successes.savetitle"), message: t("jobs.successes.savetitle"),
}); });
const changedAuditFields = form.getFieldsValue(
[
"scheduled_in",
"actual_in",
"scheduled_completion",
"actual_completion",
"scheduled_delivery",
"actual_delivery",
"date_invoiced",
"ins_co_nm",
"ded_amt",
"ded_status",
"date_exported",
"special_coverage_policy",
"ca_gst_registrant",
"ca_bc_pvrt",
"scheduled_in",
"rate_la1",
"rate_la2",
"rate_la3",
"rate_la4",
"rate_laa",
"rate_lab",
"rate_lad",
"rate_lae",
"rate_laf",
"rate_lag",
"rate_lam",
"rate_lar",
"rate_las",
"rate_lau",
"rate_ma2s",
"rate_ma2t",
"rate_ma3s",
"rate_mabl",
"rate_macs",
"rate_mapa",
"rate_mahw",
"rate_mash",
"rate_matd",
],
(meta) => meta && meta.touched
);
Object.keys(changedAuditFields).forEach((key) => {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobfieldchange(
key,
changedAuditFields[key]
),
});
});
await refetch(); await refetch();
form.setFieldsValue(transormJobToForm(job)); form.setFieldsValue(transormJobToForm(job));
form.resetFields(); form.resetFields();
@@ -283,7 +344,7 @@ export function JobsDetailPage({
<Tabs.TabPane <Tabs.TabPane
tab={ tab={
<span> <span>
<Icon component={FaRegStickyNote} /> <HistoryOutlined />
{t("jobs.labels.audit")} {t("jobs.labels.audit")}
</span> </span>
} }

View File

@@ -85,8 +85,18 @@
}, },
"audit_trail": { "audit_trail": {
"messages": { "messages": {
"billposted": "Bill with invoice number {{invoice_number}} posted.",
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
"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}}.",
"jobconverted": "Job converted and assigned number {{ro_number}}.", "jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.", "jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}",
"jobmodifylbradj": "Labor adjustments modified.",
"jobspartsorder": "Parts order {{order_number}} added to job.",
"jobspartsreturn": "Parts return {{order_number}} added to job.",
"jobstatuschange": "Job status changed to {{status}}.", "jobstatuschange": "Job status changed to {{status}}.",
"jobsupplement": "Job supplement imported." "jobsupplement": "Job supplement imported."
} }

View File

@@ -85,8 +85,18 @@
}, },
"audit_trail": { "audit_trail": {
"messages": { "messages": {
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "",
"jobimported": "", "jobimported": "",
"jobinproductionchange": "",
"jobmodifylbradj": "",
"jobspartsorder": "",
"jobspartsreturn": "",
"jobstatuschange": "", "jobstatuschange": "",
"jobsupplement": "" "jobsupplement": ""
} }

View File

@@ -85,8 +85,18 @@
}, },
"audit_trail": { "audit_trail": {
"messages": { "messages": {
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
"jobassignmentremoved": "",
"jobchecklist": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "",
"jobimported": "", "jobimported": "",
"jobinproductionchange": "",
"jobmodifylbradj": "",
"jobspartsorder": "",
"jobspartsreturn": "",
"jobstatuschange": "", "jobstatuschange": "",
"jobsupplement": "" "jobsupplement": ""
} }

View File

@@ -7,6 +7,25 @@ const AuditTrailMapping = {
jobimported: () => i18n.t("audit_trail.messages.jobimported"), jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobconverted: (ro_number) => jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }), i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) =>
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: () => i18n.t("audit_trail.messages.jobmodifylbradj", {}),
billposted: (invoice_number) =>
i18n.t("audit_trail.messages.billposted", { invoice_number }),
billupdated: (invoice_number) =>
i18n.t("audit_trail.messages.billupdated", { invoice_number }),
jobassignmentchange: (operation, name) =>
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 }),
}; };
export default AuditTrailMapping; export default AuditTrailMapping;

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ALTER COLUMN "created" TYPE timestamp
without time zone;
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."audit_trail" ALTER COLUMN "created" TYPE timestamptz;
type: run_sql