Merged in release/2025-12-19 (pull request #2741)
feature/IO-3487-Auto-Add-Profile-Watchers - Fix Auto Add on a profile level
This commit is contained in:
@@ -253,6 +253,10 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract, disabled
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (currentUser?.email) {
|
||||||
|
newJob.created_user_email = currentUser.email;
|
||||||
|
}
|
||||||
|
|
||||||
//Calcualte the new job totals.
|
//Calcualte the new job totals.
|
||||||
|
|
||||||
const newTotals = (
|
const newTotals = (
|
||||||
|
|||||||
@@ -43,16 +43,18 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines, tec
|
|||||||
const handleCreateIou = async () => {
|
const handleCreateIou = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
//Query all of the job details to recreate.
|
//Query all of the job details to recreate.
|
||||||
const iouId = await CreateIouForJob(
|
const iouId = await CreateIouForJob({
|
||||||
client,
|
apolloClient: client,
|
||||||
job.id,
|
jobLinesToKeep: selectedJobLines,
|
||||||
{
|
jobId: job.id,
|
||||||
|
config: {
|
||||||
status: bodyshop.md_ro_statuses.default_open,
|
status: bodyshop.md_ro_statuses.default_open,
|
||||||
bodyshopid: bodyshop.id,
|
bodyshopid: bodyshop.id,
|
||||||
useremail: currentUser.email
|
useremail: currentUser.email
|
||||||
},
|
},
|
||||||
selectedJobLines
|
currentUser
|
||||||
);
|
});
|
||||||
|
|
||||||
notification.open({
|
notification.open({
|
||||||
type: "success",
|
type: "success",
|
||||||
message: t("jobs.successes.ioucreated"),
|
message: t("jobs.successes.ioucreated"),
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
|||||||
: {})
|
: {})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (currentUser?.email) {
|
||||||
|
newJob.created_user_email = currentUser.email;
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedOwner) {
|
if (selectedOwner) {
|
||||||
newJob.ownerid = selectedOwner;
|
newJob.ownerid = selectedOwner;
|
||||||
delete newJob.owner;
|
delete newJob.owner;
|
||||||
|
|||||||
@@ -175,25 +175,33 @@ export function JobsDetailHeaderActions({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDuplicate = () =>
|
const handleDuplicate = () =>
|
||||||
DuplicateJob(
|
DuplicateJob({
|
||||||
client,
|
apolloClient: client,
|
||||||
job.id,
|
jobId: job.id,
|
||||||
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
||||||
(newJobId) => {
|
completionCallback: (newJobId) => {
|
||||||
history(`/manage/jobs/${newJobId}`);
|
history(`/manage/jobs/${newJobId}`);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: t("jobs.successes.duplicated")
|
message: t("jobs.successes.duplicated")
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
true
|
keepJobLines: true,
|
||||||
);
|
currentUser
|
||||||
|
});
|
||||||
|
|
||||||
const handleDuplicateConfirm = () =>
|
const handleDuplicateConfirm = () =>
|
||||||
DuplicateJob(client, job.id, { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported }, (newJobId) => {
|
DuplicateJob({
|
||||||
history(`/manage/jobs/${newJobId}`);
|
apolloClient: client,
|
||||||
notification.success({
|
jobId: job.id,
|
||||||
message: t("jobs.successes.duplicated")
|
config: { defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
|
||||||
});
|
completionCallback: (newJobId) => {
|
||||||
|
history(`/manage/jobs/${newJobId}`);
|
||||||
|
notification.success({
|
||||||
|
message: t("jobs.successes.duplicated")
|
||||||
|
});
|
||||||
|
},
|
||||||
|
keepJobLines: false,
|
||||||
|
currentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
|
|||||||
@@ -5,7 +5,14 @@ import { INSERT_NEW_JOB, QUERY_JOB_FOR_DUPE } from "../../graphql/jobs.queries";
|
|||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
|
|
||||||
export default async function DuplicateJob(apolloClient, jobId, config, completionCallback, keepJobLines = false) {
|
export default async function DuplicateJob({
|
||||||
|
apolloClient,
|
||||||
|
jobId,
|
||||||
|
config,
|
||||||
|
completionCallback,
|
||||||
|
keepJobLines = false,
|
||||||
|
currentUser
|
||||||
|
}) {
|
||||||
logImEXEvent("job_duplicate");
|
logImEXEvent("job_duplicate");
|
||||||
|
|
||||||
const { defaultOpenStatus } = config;
|
const { defaultOpenStatus } = config;
|
||||||
@@ -19,6 +26,7 @@ export default async function DuplicateJob(apolloClient, jobId, config, completi
|
|||||||
const existingJob = _.cloneDeep(jobs_by_pk);
|
const existingJob = _.cloneDeep(jobs_by_pk);
|
||||||
delete existingJob.__typename;
|
delete existingJob.__typename;
|
||||||
delete existingJob.id;
|
delete existingJob.id;
|
||||||
|
delete existingJob.created_user_email;
|
||||||
delete existingJob.createdat;
|
delete existingJob.createdat;
|
||||||
delete existingJob.updatedat;
|
delete existingJob.updatedat;
|
||||||
delete existingJob.cieca_stl;
|
delete existingJob.cieca_stl;
|
||||||
@@ -29,6 +37,10 @@ export default async function DuplicateJob(apolloClient, jobId, config, completi
|
|||||||
status: defaultOpenStatus
|
status: defaultOpenStatus
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (currentUser?.email) {
|
||||||
|
newJob.created_user_email = currentUser.email;
|
||||||
|
}
|
||||||
|
|
||||||
const _tempLines = _.cloneDeep(existingJob.joblines);
|
const _tempLines = _.cloneDeep(existingJob.joblines);
|
||||||
_tempLines.forEach((line) => {
|
_tempLines.forEach((line) => {
|
||||||
delete line.id;
|
delete line.id;
|
||||||
@@ -55,7 +67,7 @@ export default async function DuplicateJob(apolloClient, jobId, config, completi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function CreateIouForJob(apolloClient, jobId, config, jobLinesToKeep) {
|
export async function CreateIouForJob({ apolloClient, jobId, config, jobLinesToKeep, currentUser }) {
|
||||||
logImEXEvent("job_create_iou");
|
logImEXEvent("job_create_iou");
|
||||||
|
|
||||||
const { status } = config;
|
const { status } = config;
|
||||||
@@ -109,6 +121,9 @@ export async function CreateIouForJob(apolloClient, jobId, config, jobLinesToKee
|
|||||||
delete newJob.joblines;
|
delete newJob.joblines;
|
||||||
newJob.joblines = { data: _tempLines };
|
newJob.joblines = { data: _tempLines };
|
||||||
|
|
||||||
|
if (currentUser?.email) {
|
||||||
|
newJob.created_user_email = currentUser.email;
|
||||||
|
}
|
||||||
const res2 = await apolloClient.mutate({
|
const res2 = await apolloClient.mutate({
|
||||||
mutation: INSERT_NEW_JOB,
|
mutation: INSERT_NEW_JOB,
|
||||||
variables: { job: [newJob] }
|
variables: { job: [newJob] }
|
||||||
|
|||||||
@@ -9,21 +9,23 @@ import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
|||||||
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
|
import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
|
||||||
import { QUERY_OWNER_FOR_JOB_CREATION } from "../../graphql/owners.queries";
|
import { QUERY_OWNER_FOR_JOB_CREATION } from "../../graphql/owners.queries";
|
||||||
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import JobsCreateComponent from "./jobs-create.component";
|
import JobsCreateComponent from "./jobs-create.component";
|
||||||
import JobCreateContext from "./jobs-create.context";
|
import JobCreateContext from "./jobs-create.context";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
setSelectedHeader: (key) => dispatch(setSelectedHeader(key))
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key))
|
||||||
});
|
});
|
||||||
|
|
||||||
function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, currentUser }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
const runInsertJob = (job) => {
|
const runInsertJob = (job) => {
|
||||||
insertJob({ variables: { job: job } })
|
insertJob({ variables: { job } })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
@@ -150,6 +152,11 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
if (job.owner === null) delete job.owner;
|
if (job.owner === null) delete job.owner;
|
||||||
if (job.vehicle === null) delete job.vehicle;
|
if (job.vehicle === null) delete job.vehicle;
|
||||||
|
|
||||||
|
// Associate to the current user if one exists
|
||||||
|
if (currentUser?.email) {
|
||||||
|
job.created_user_email = currentUser.email;
|
||||||
|
}
|
||||||
|
|
||||||
runInsertJob(job);
|
runInsertJob(job);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3684,6 +3684,7 @@
|
|||||||
- completed_tasks
|
- completed_tasks
|
||||||
- converted
|
- converted
|
||||||
- created_at
|
- created_at
|
||||||
|
- created_user_email
|
||||||
- cust_pr
|
- cust_pr
|
||||||
- date_estimated
|
- date_estimated
|
||||||
- date_exported
|
- date_exported
|
||||||
@@ -3961,6 +3962,7 @@
|
|||||||
- completed_tasks
|
- completed_tasks
|
||||||
- converted
|
- converted
|
||||||
- created_at
|
- created_at
|
||||||
|
- created_user_email
|
||||||
- cust_pr
|
- cust_pr
|
||||||
- date_estimated
|
- date_estimated
|
||||||
- date_exported
|
- date_exported
|
||||||
@@ -4251,6 +4253,7 @@
|
|||||||
- completed_tasks
|
- completed_tasks
|
||||||
- converted
|
- converted
|
||||||
- created_at
|
- created_at
|
||||||
|
- created_user_email
|
||||||
- cust_pr
|
- cust_pr
|
||||||
- date_estimated
|
- date_estimated
|
||||||
- date_exported
|
- date_exported
|
||||||
@@ -4641,7 +4644,7 @@
|
|||||||
request_transform:
|
request_transform:
|
||||||
body:
|
body:
|
||||||
action: transform
|
action: transform
|
||||||
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body?.event?.session_variables?.x-hasura-user-id ?? \"Internal\"}},\r\n \"x-hasura-role\": {{$body?.event?.session_variables?.x-hasura-role ?? \"Internal\"}}\r\n }, \r\n \"op\": {{$body.event.op}},\r\n \"data\": {\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"shopid\": {{$body.event.data.new?.shopid}},\r\n \"ro_number\": {{$body.event.data.new?.ro_number}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_jobs_autoadd\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"jobs\"\r\n }\r\n}\r\n"
|
template: "{\r\n \"event\": {\r\n \"session_variables\": {\r\n \"x-hasura-user-id\": {{$body?.event?.session_variables?.x-hasura-user-id ?? \"Internal\"}},\r\n \"x-hasura-role\": {{$body?.event?.session_variables?.x-hasura-role ?? \"Internal\"}}\r\n }, \r\n \"op\": {{$body.event.op}},\r\n \"data\": {\r\n \"new\": {\r\n \"id\": {{$body.event.data.new.id}},\r\n \"shopid\": {{$body.event.data.new?.shopid}},\r\n \"ro_number\": {{$body.event.data.new?.ro_number}},\r\n \"created_user_email\": {{$body.event.data.new?.created_user_email}}\r\n }\r\n }\r\n },\r\n \"trigger\": {\r\n \"name\": \"notifications_jobs_autoadd\"\r\n },\r\n \"table\": {\r\n \"schema\": \"public\",\r\n \"name\": \"jobs\"\r\n }\r\n}\r\n"
|
||||||
method: POST
|
method: POST
|
||||||
query_params: {}
|
query_params: {}
|
||||||
template_engine: Kriti
|
template_engine: Kriti
|
||||||
|
|||||||
@@ -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 "created_user_email" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."jobs" add column "created_user_email" text
|
||||||
|
null;
|
||||||
@@ -3082,17 +3082,19 @@ exports.INSERT_JOB_WATCHERS = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports.GET_NOTIFICATION_WATCHERS = `
|
exports.GET_NOTIFICATION_WATCHERS = `
|
||||||
query GET_NOTIFICATION_WATCHERS($shopId: uuid!, $employeeIds: [uuid!]!) {
|
query GET_NOTIFICATION_WATCHERS($shopId: uuid!, $employeeIds: [uuid!]!, $createdUserEmail: String!) {
|
||||||
associations(where: {
|
associations(where: {
|
||||||
_and: [
|
_and: [
|
||||||
{ shopid: { _eq: $shopId } },
|
{ shopid: { _eq: $shopId } },
|
||||||
{ active: { _eq: true } },
|
{ active: { _eq: true } },
|
||||||
{ notifications_autoadd: { _eq: true } }
|
{ notifications_autoadd: { _eq: true } },
|
||||||
|
{ useremail: { _eq: $createdUserEmail } }
|
||||||
]
|
]
|
||||||
}) {
|
}) {
|
||||||
id
|
id
|
||||||
useremail
|
useremail
|
||||||
}
|
}
|
||||||
|
|
||||||
employees(where: { id: { _in: $employeeIds }, shopid: { _eq: $shopId }, active: { _eq: true } }) {
|
employees(where: { id: { _in: $employeeIds }, shopid: { _eq: $shopId }, active: { _eq: true } }) {
|
||||||
user_email
|
user_email
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const autoAddWatchers = async (req) => {
|
|||||||
const jobId = event?.data?.new?.id;
|
const jobId = event?.data?.new?.id;
|
||||||
const shopId = event?.data?.new?.shopid;
|
const shopId = event?.data?.new?.shopid;
|
||||||
const roNumber = event?.data?.new?.ro_number || "unknown";
|
const roNumber = event?.data?.new?.ro_number || "unknown";
|
||||||
|
const createdUserEmail = event?.data?.new?.created_user_email || "Unknown";
|
||||||
|
|
||||||
if (!jobId || !shopId) {
|
if (!jobId || !shopId) {
|
||||||
throw new Error(`Missing jobId (${jobId}) or shopId (${shopId}) for auto-add watchers`);
|
throw new Error(`Missing jobId (${jobId}) or shopId (${shopId}) for auto-add watchers`);
|
||||||
@@ -61,7 +62,8 @@ const autoAddWatchers = async (req) => {
|
|||||||
const [notificationData, existingWatchersData] = await Promise.all([
|
const [notificationData, existingWatchersData] = await Promise.all([
|
||||||
gqlClient.request(GET_NOTIFICATION_WATCHERS, {
|
gqlClient.request(GET_NOTIFICATION_WATCHERS, {
|
||||||
shopId,
|
shopId,
|
||||||
employeeIds: notificationFollowers
|
employeeIds: notificationFollowers,
|
||||||
|
createdUserEmail
|
||||||
}),
|
}),
|
||||||
gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
|
gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user