feature/IO-3255-simplified-parts-management Deprovision route can now remove jobs, joblines, and audit trail

This commit is contained in:
Dave
2025-08-19 11:17:29 -04:00
parent c0a215d20d
commit f6d6b548be
2 changed files with 167 additions and 71 deletions

View File

@@ -1,71 +1,20 @@
const admin = require("firebase-admin");
const client = require("../../../graphql-client/graphql-client").client;
const { DELETE_SHOP } = require("../partsManagement.queries");
// Define corrected DELETE_VENDORS_BY_SHOP locally
const DELETE_VENDORS_BY_SHOP = `
mutation DELETE_VENDORS_BY_SHOP($shopId: uuid!) {
delete_vendors(where: {bodyshopid: {_eq: $shopId}}) {
affected_rows
}
}
`;
// New queries for deprovisioning
const GET_BODYSHOP = `
query GetBodyshop($id: uuid!) {
bodyshops_by_pk(id: $id) {
external_shop_id
shopname
}
}
`;
const GET_ASSOCIATED_USERS = `
query GetAssociatedUsers($shopId: uuid!) {
associations(where: {shopid: {_eq: $shopId}}) {
user {
authid
email
}
}
}
`;
const DELETE_ASSOCIATIONS_BY_SHOP = `
mutation DeleteAssociationsByShop($shopId: uuid!) {
delete_associations(where: {shopid: {_eq: $shopId}}) {
affected_rows
}
}
`;
const GET_USER_ASSOCIATIONS_COUNT = `
query GetUserAssociationsCount($userEmail: String!) {
associations_aggregate(where: {useremail: {_eq: $userEmail}}) {
aggregate {
count
}
}
}
`;
const DELETE_USER = `
mutation DeleteUser($email: String!) {
delete_users(where: {email: {_eq: $email}}) {
affected_rows
}
}
`;
const GET_VENDORS = `
query GetVendors($shopId: uuid!) {
vendors(where: {bodyshopid: {_eq: $shopId}}) {
name
}
}
`;
const {
DELETE_SHOP,
DELETE_VENDORS_BY_SHOP,
GET_BODYSHOP,
GET_ASSOCIATED_USERS,
DELETE_ASSOCIATIONS_BY_SHOP,
GET_USER_ASSOCIATIONS_COUNT,
DELETE_USER,
GET_VENDORS,
GET_JOBS_BY_SHOP,
DELETE_JOBLINES_BY_JOB_IDS,
DELETE_JOBS_BY_IDS,
DELETE_AUDIT_TRAIL_BY_SHOP
} = require("../partsManagement.queries");
/**
* Deletes a Firebase user by UID.
@@ -94,6 +43,38 @@ const deleteBodyshop = async (shopId) => {
await client.request(DELETE_SHOP, { id: shopId });
};
/**
* Fetch job ids for a given shop
* @param shopId
* @returns {Promise<string[]>}
*/
const getJobIdsForShop = async (shopId) => {
const resp = await client.request(GET_JOBS_BY_SHOP, { shopId });
return resp.jobs.map((j) => j.id);
};
/**
* Delete joblines for the given job ids
* @param jobIds {string[]}
* @returns {Promise<number>} affected rows
*/
const deleteJoblinesForJobs = async (jobIds) => {
if (!jobIds.length) return 0;
const resp = await client.request(DELETE_JOBLINES_BY_JOB_IDS, { jobIds });
return resp.delete_joblines.affected_rows;
};
/**
* Delete jobs for the given job ids
* @param jobIds {string[]}
* @returns {Promise<number>} affected rows
*/
const deleteJobsByIds = async (jobIds) => {
if (!jobIds.length) return 0;
const resp = await client.request(DELETE_JOBS_BY_IDS, { jobIds });
return resp.delete_jobs.affected_rows;
};
/**
* Handles deprovisioning a shop for parts management.
* @param req
@@ -156,6 +137,15 @@ const partsManagementDeprovisioning = async (req, res) => {
}
}
// Get all job ids for this shop, then delete joblines and jobs (joblines first)
const jobIds = await getJobIdsForShop(p.shopId);
const joblinesDeleted = await deleteJoblinesForJobs(jobIds);
const jobsDeleted = await deleteJobsByIds(jobIds);
// Delete any audit trail entries tied to this bodyshop to avoid FK violations
const auditResp = await client.request(DELETE_AUDIT_TRAIL_BY_SHOP, { shopId: p.shopId });
const auditDeleted = auditResp.delete_audit_trail.affected_rows;
// Delete vendors
await deleteVendorsByShop(p.shopId);
@@ -163,16 +153,26 @@ const partsManagementDeprovisioning = async (req, res) => {
await deleteBodyshop(p.shopId);
// Summary log
console.log(
`Deleted bodyshop ${p.shopId} (${shop.shopname}), ${associationsDeleted} associations, ${deletedUsers.length} users (${deletedUsers.join(", ") || "none"}), ${deletedVendors.length} vendors (${deletedVendors.join(", ") || "none"}).`
);
logger.log("admin-delete-shop-summary", "info", null, null, {
shopId: p.shopId,
shopname: shop.shopname,
associationsDeleted,
deletedUsers,
deletedVendors,
joblinesDeleted,
jobsDeleted,
auditDeleted
});
return res.status(200).json({
message: `Bodyshop ${p.shopId} and associated resources deleted successfully.`,
deletedShop: { id: p.shopId, name: shop.shopname },
deletedAssociationsCount: associationsDeleted,
deletedUsers: deletedUsers,
deletedVendors: deletedVendors
deletedVendors: deletedVendors,
deletedJoblinesCount: joblinesDeleted,
deletedJobsCount: jobsDeleted,
deletedAuditTrailCount: auditDeleted
});
} catch (err) {
logger.log("admin-delete-shop-error", "error", null, null, {

View File

@@ -126,7 +126,7 @@ const CREATE_SHOP = `
const DELETE_VENDORS_BY_SHOP = `
mutation DELETE_VENDORS($shopId: uuid!) {
delete_vendors(where: { shopid: { _eq: $shopId } }) {
delete_vendors(where: { bodyshopid: { _eq: $shopId } }) {
affected_rows
}
}
@@ -147,6 +147,92 @@ const CREATE_USER = `
}
`;
const GET_BODYSHOP = `
query GetBodyshop($id: uuid!) {
bodyshops_by_pk(id: $id) {
external_shop_id
shopname
}
}
`;
const GET_ASSOCIATED_USERS = `
query GetAssociatedUsers($shopId: uuid!) {
associations(where: {shopid: {_eq: $shopId}}) {
user {
authid
email
}
}
}
`;
const DELETE_ASSOCIATIONS_BY_SHOP = `
mutation DeleteAssociationsByShop($shopId: uuid!) {
delete_associations(where: {shopid: {_eq: $shopId}}) {
affected_rows
}
}
`;
const GET_USER_ASSOCIATIONS_COUNT = `
query GetUserAssociationsCount($userEmail: String!) {
associations_aggregate(where: {useremail: {_eq: $userEmail}}) {
aggregate {
count
}
}
}
`;
const DELETE_USER = `
mutation DeleteUser($email: String!) {
delete_users(where: {email: {_eq: $email}}) {
affected_rows
}
}
`;
const GET_VENDORS = `
query GetVendors($shopId: uuid!) {
vendors(where: {bodyshopid: {_eq: $shopId}}) {
name
}
}
`;
const GET_JOBS_BY_SHOP = `
query GetJobsByShop($shopId: uuid!) {
jobs(where: {shopid: {_eq: $shopId}}) {
id
}
}
`;
const DELETE_JOBLINES_BY_JOB_IDS = `
mutation DeleteJoblinesByJobIds($jobIds: [uuid!]!) {
delete_joblines(where: {jobid: {_in: $jobIds}}) {
affected_rows
}
}
`;
const DELETE_JOBS_BY_IDS = `
mutation DeleteJobsByIds($jobIds: [uuid!]!) {
delete_jobs(where: {id: {_in: $jobIds}}) {
affected_rows
}
}
`;
const DELETE_AUDIT_TRAIL_BY_SHOP = `
mutation DeleteAuditTrailByShop($shopId: uuid!) {
delete_audit_trail(where: {bodyshopid: {_eq: $shopId}}) {
affected_rows
}
}
`;
module.exports = {
GET_BODYSHOP_STATUS,
GET_VEHICLE_BY_SHOP_VIN,
@@ -162,5 +248,15 @@ module.exports = {
CREATE_SHOP,
DELETE_VENDORS_BY_SHOP,
DELETE_SHOP,
CREATE_USER
CREATE_USER,
GET_BODYSHOP,
GET_ASSOCIATED_USERS,
DELETE_ASSOCIATIONS_BY_SHOP,
GET_USER_ASSOCIATIONS_COUNT,
DELETE_USER,
GET_VENDORS,
GET_JOBS_BY_SHOP,
DELETE_JOBLINES_BY_JOB_IDS,
DELETE_JOBS_BY_IDS,
DELETE_AUDIT_TRAIL_BY_SHOP
};