diff --git a/controllers/notification_error_sparepart.controller.js b/controllers/notification_error_sparepart.controller.js new file mode 100644 index 0000000..373d72f --- /dev/null +++ b/controllers/notification_error_sparepart.controller.js @@ -0,0 +1,62 @@ +const NotificationErrorSparepart = require('../services/notification_error_sparepart.service'); +const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); +const { updateNotificationErrorSparepartSchema, insertNotificationErrorSparepartSchema } = require('../validate/notification_error_sparepart.schema'); + +class NotificationErrorSparepartController { + + static async getAll(req, res) { + const { contact_id } = req.body; + const queryParams = req.query; + const results = await NotificationErrorSparepart.getAll(queryParams, contact_id); + const response = await setResponsePaging(queryParams, results, 'Notification Error Sparepart found'); + + res.status(response.statusCode).json(response); + } + + static async getById(req, res) { + const { id } = req.params; + const results = await NotificationErrorSparepart.getById(id); + const response = await setResponse(results, 'Notification Error Sparepart found'); + + res.status(response.statusCode).json(response); + } + + static async create(req, res) { + const { error, value } = await checkValidate(insertNotificationErrorSparepartSchema, req); + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + const results = await NotificationErrorSparepart.create(value); + const response = await setResponse(results, 'Notification Error Sparepart created successfully'); + + res.status(response.statusCode).json(response); + } + + static async update(req, res) { + const { id } = req.params; + const { error, value } = await checkValidate(updateNotificationErrorSparepartSchema, req); + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + const results = await NotificationErrorSparepart.update(id, value); + const response = await setResponse(results, 'Notification Error Sparepart updated successfully'); + + res.status(response.statusCode).json(response); + } + + static async delete(req, res) { + const { id } = req.params; + const { contact_id } = req.body; + + const results = await NotificationErrorSparepart.delete(id, contact_id); + const response = await setResponse(results, 'Notification Error Sparepart deleted successfully'); + + res.status(response.statusCode).json(response); + } +} + +module.exports = NotificationErrorSparepartController; diff --git a/db/brand_code.db.js b/db/brand_code.db.js index 5b3b01d..7c4de4c 100644 --- a/db/brand_code.db.js +++ b/db/brand_code.db.js @@ -57,9 +57,21 @@ const deleteErrorCodeDb = async (brandId, errorCode, deletedBy) => { return true; }; +// Get error code by error_code_id +const getErrorCodeByIdDb = async (error_code_id) => { + const queryText = ` + SELECT + a.* + FROM brand_code a + WHERE a.error_code_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [error_code_id]); + return result.recordset[0]; +}; module.exports = { getErrorCodesByBrandIdDb, + getErrorCodeByIdDb, createErrorCodeDb, updateErrorCodeDb, deleteErrorCodeDb, diff --git a/db/notification.db.js b/db/notification.db.js index b745ec3..36d9951 100644 --- a/db/notification.db.js +++ b/db/notification.db.js @@ -41,7 +41,16 @@ const InsertNotificationErrorDb = async () => { await pool.query(insertQuery); }; - +const getNotificationByIdDb = async (id) => { + const queryText = ` + SELECT + a.* + FROM notification_error a + WHERE a.notification_error_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset[0]; +}; const getAllNotificationDb = async (searchParams = {}) => { let queryParams = []; @@ -145,5 +154,6 @@ const getAllNotificationDb = async (searchParams = {}) => { }; module.exports = { + getNotificationByIdDb, getAllNotificationDb, }; diff --git a/db/notification_error_log.db.js b/db/notification_error_log.db.js new file mode 100644 index 0000000..55a4a45 --- /dev/null +++ b/db/notification_error_log.db.js @@ -0,0 +1,66 @@ +const pool = require("../config"); + +const createNotificationErrorLogDb = async (data) => { + const store = { + notification_error_id: data.notification_error_id, + contact_id: data.contact_id, + notification_error_log_description: data.notification_error_log_description, + created_by: data.created_by + }; + + const { query: queryText, values } = pool.buildDynamicInsert("notification_error_log", store); + const result = await pool.query(queryText, values); + return result.recordset[0]; +}; + +const getAllNotificationErrorLogDb = async () => { + const queryText = ` + SELECT + a.* + FROM notification_error_log a + WHERE a.deleted_at IS NULL + ORDER BY a.notification_error_log_id DESC + `; + const result = await pool.query(queryText); + return result.recordset; +}; + +const getNotificationErrorLogByIdDb = async (id) => { + const queryText = ` + SELECT + a.* + FROM notification_error_log a + WHERE a.notification_error_log_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset[0]; +}; + +const updateNotificationErrorLogDb = async (id, data) => { + const store = { ...data }; + const whereData = { + notification_error_log_id: id + }; + + const { query: queryText, values } = pool.buildDynamicUpdate("notification_error_log", store, whereData); + await pool.query(`${queryText} AND deleted_at IS NULL`, values); + return true; +}; + +const deleteNotificationErrorLogDb = async (id, deletedBy) => { + const queryText = ` + UPDATE notification_error_log + SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1 + WHERE notification_error_log_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [deletedBy, id]); + return true; +}; + +module.exports = { + createNotificationErrorLogDb, + getAllNotificationErrorLogDb, + getNotificationErrorLogByIdDb, + updateNotificationErrorLogDb, + deleteNotificationErrorLogDb, +}; \ No newline at end of file diff --git a/db/notification_error_sparepart.db.js b/db/notification_error_sparepart.db.js new file mode 100644 index 0000000..17b9288 --- /dev/null +++ b/db/notification_error_sparepart.db.js @@ -0,0 +1,128 @@ +const pool = require("../config"); + +const getAllNotificationErrorSparepartDb = async (searchParams = {}) => { + let queryParams = []; + + if (searchParams.limit) { + const page = Number(searchParams.page ?? 1) - 1; + queryParams = [Number(searchParams.limit ?? 10), page]; + } + + const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike( + [ + "a.brand_sparepart_id", + "a.contact_id", + "b.sparepart_name", + "d.contact_name", + "d.contact_type" + ], + searchParams.criteria, + queryParams + ); + + if (whereParamOr) queryParams = whereParamOr; + + const { whereConditions, whereParamAnd } = pool.buildFilterQuery( + [ + { column: "a.brand_sparepart_id", param: searchParams.name, type: "int" }, + { column: "a.contact_id", param: searchParams.code, type: "int" }, + { column: "a.unit", param: searchParams.unit, type: "string" }, + { column: "b.sparepart_name", param: searchParams.device, type: "string" }, + { column: "d.contact_name", param: searchParams.device, type: "string" }, + { column: "d.contact_type", param: searchParams.device, type: "string" }, + ], + queryParams + ); + + if (whereParamAnd) queryParams = whereParamAnd; + + const queryText = ` + SELECT + COUNT(*) OVER() AS total_data, + a.*, + b.sparepart_name, + b.brand_sparepart_description, + d.contact_name, + d.contact_type + FROM notification_error_sparepart a + + LEFT JOIN brand_sparepart b ON a.brand_sparepart_id = b.brand_sparepart_id + + LEFT JOIN contact d on a.contact_id = d.contact_id + + WHERE a.deleted_at IS NULL + ${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""} + ${whereOrConditions ? ` ${whereOrConditions}` : ""} + ORDER BY a.notification_error_sparepart_id ASC + ${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''} + `; + + const result = await pool.query(queryText, queryParams); + + const total = + result?.recordset?.length > 0 + ? parseInt(result.recordset[0].total_data, 10) + : 0; + + return { data: result.recordset, total }; +}; + +const getNotificationErrorSparepartByIdDb = async (id) => { + const queryText = ` + SELECT + a.*, + b.sparepart_name, + b.brand_sparepart_description, + d.contact_name, + d.contact_type + FROM notification_error_sparepart a + LEFT JOIN brand_sparepart b ON a.brand_sparepart_id = b.brand_sparepart_id + LEFT JOIN contact d on a.contact_id = d.contact_id + WHERE a.notification_error_sparepart_id = $1 + AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset?.[0] || null; +}; + + +const createNotificationErrorSparepartDb = async (store) => { + const { query: queryText, values } = pool.buildDynamicInsert("notification_error_sparepart", store); + const result = await pool.query(queryText, values); + const insertedId = result.recordset?.[0]?.inserted_id; + + return insertedId ? await getNotificationErrorSparepartByIdDb(insertedId) : null; +}; + +const updateNotificationErrorSparepartDb = async (id, data) => { + const store = { ...data }; + const whereData = { notification_error_sparepart_id: id }; + + const { query: queryText, values } = pool.buildDynamicUpdate( + "notification_error_sparepart", + store, + whereData + ); + + await pool.query(`${queryText} AND deleted_at IS NULL`, values); + return getNotificationErrorSparepartByIdDb(id); +}; + +// Soft delete tag +const deleteNotificationErrorSparepartDb = async (id, deletedBy) => { + const queryText = ` + UPDATE notification_error_sparepart + SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1 + WHERE notification_error_sparepart_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [deletedBy, id]); + return true; +}; + +module.exports = { + getAllNotificationErrorSparepartDb, + getNotificationErrorSparepartByIdDb, + createNotificationErrorSparepartDb, + updateNotificationErrorSparepartDb, + deleteNotificationErrorSparepartDb, +}; diff --git a/routes/index.js b/routes/index.js index 141c7f5..6ea3e3e 100644 --- a/routes/index.js +++ b/routes/index.js @@ -15,6 +15,7 @@ const UserSchedule = require("./user_schedule.route") const historyValue = require("./history_value.route") const contact = require("./contact.route") const notification = require("./notification.route") +const notificationErrorSparepart = require("./notification_error_sparepart.route") router.use("/auth", auth); router.use("/user", users); @@ -32,6 +33,6 @@ router.use("/user-schedule", UserSchedule) router.use("/history", historyValue) router.use("/contact", contact) router.use("/notification", notification) - +router.use("/notification-sparepart", notificationErrorSparepart) module.exports = router; diff --git a/routes/notification_error_sparepart.route.js b/routes/notification_error_sparepart.route.js new file mode 100644 index 0000000..4798692 --- /dev/null +++ b/routes/notification_error_sparepart.route.js @@ -0,0 +1,23 @@ +const express = require('express'); +const NotificationErrorSparepartController = require('../controllers/notification_error_sparepart.controller'); +const verifyToken = require('../middleware/verifyToken'); +const verifyAccess = require('../middleware/verifyAccess'); + +const router = express.Router(); + +// =========================== +// Notification Erro rSparepart Routes +// =========================== + +router + .route('/') + .get(verifyToken.verifyAccessToken, NotificationErrorSparepartController.getAll) + .post(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorSparepartController.create); + +router + .route('/:id') + .get(verifyToken.verifyAccessToken, NotificationErrorSparepartController.getById) + .put(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorSparepartController.update) + .delete(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorSparepartController.delete); + +module.exports = router; diff --git a/services/brand.service.js b/services/brand.service.js index 3fb2b2e..92cfedd 100644 --- a/services/brand.service.js +++ b/services/brand.service.js @@ -1,4 +1,4 @@ -// Brand operations +// BrandService.js const { getAllBrandsDb, getBrandByIdDb, @@ -9,7 +9,6 @@ const { checkBrandNameExistsDb, } = require('../db/brand.db'); -// Error code operations const { getErrorCodesByBrandIdDb, createErrorCodeDb, @@ -17,7 +16,6 @@ const { deleteErrorCodeDb, } = require('../db/brand_code.db'); -// Sparepart operations const { getSparePartnsByErrorCodeIdDb, createSparePartDb, @@ -25,7 +23,6 @@ const { deleteSparePartDb, } = require('../db/brand_sparepart.db'); -// Solution operations const { getSolutionsByErrorCodeIdDb, createSolutionDb, @@ -37,13 +34,12 @@ const { getFileUploadByPathDb } = require('../db/file_uploads.db'); const { ErrorHandler } = require('../helpers/error'); class BrandService { - static async getAllBrands(param) { try { const results = await getAllBrandsDb(param); return results; } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); + throw new ErrorHandler(error.statusCode || 500, error.message || 'Failed to get brands'); } } @@ -52,20 +48,21 @@ class BrandService { const brand = await getBrandByIdDb(id); if (!brand) throw new ErrorHandler(404, 'Brand not found'); - const errorCodes = await getErrorCodesByBrandIdDb(brand.brand_id); + const errorCodes = await getErrorCodesByBrandIdDb(brand.brand_id) || []; const errorCodesWithDetails = await Promise.all( errorCodes.map(async (errorCode) => { - const solutions = await getSolutionsByErrorCodeIdDb(errorCode.error_code_id); - + const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || []; const solutionsWithFile = await Promise.all( solutions.map(async (s) => { let fileData = null; - - if (s.path_solution && s.type_solution !== "text") { - fileData = await getFileUploadByPathDb(s.path_solution); + if (s.path_solution && s.type_solution && s.type_solution !== 'text') { + try { + fileData = await getFileUploadByPathDb(s.path_solution); + } catch (e) { + fileData = null; + } } - return { ...s, file_upload_name: fileData?.file_upload_name || null, @@ -74,7 +71,7 @@ class BrandService { }) ); - const spareparts = await getSparePartnsByErrorCodeIdDb(errorCode.error_code_id); + const spareparts = (await getSparePartnsByErrorCodeIdDb(errorCode.error_code_id)) || []; return { ...errorCode, @@ -88,155 +85,173 @@ class BrandService { ...brand, error_code: errorCodesWithDetails }; - } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); + throw new ErrorHandler(error.statusCode || 500, error.message || 'Failed to get brand detail'); } } static async createBrandWithFullData(data) { try { - if (!data || typeof data !== "object") data = {}; + if (!data || typeof data !== 'object') data = {}; - if (data.brand_name) { - const exists = await checkBrandNameExistsDb(data.brand_name); - if (exists) throw new ErrorHandler(400, "Brand name already exists"); - } + if (!data.brand_name) throw new ErrorHandler(400, 'brand_name is required'); + const exists = await checkBrandNameExistsDb(data.brand_name); + if (exists) throw new ErrorHandler(400, 'Brand name already exists'); - if (!data.error_code || !Array.isArray(data.error_code)) { - throw new ErrorHandler(400, "Brand must have at least 1 error code"); + if (!Array.isArray(data.error_code) || data.error_code.length === 0) { + throw new ErrorHandler(400, 'Brand must have at least 1 error code with solution'); } for (const ec of data.error_code) { - if (!ec.solution || ec.solution.length === 0) { + if (!Array.isArray(ec.solution) || ec.solution.length === 0) { throw new ErrorHandler(400, `Error code ${ec.error_code} must have at least 1 solution`); } } - const createdBrand = await createBrandDb({ + const brandData = { brand_name: data.brand_name, - brand_type: data.brand_type, - brand_manufacture: data.brand_manufacture, - brand_model: data.brand_model, + brand_type: data.brand_type || null, + brand_manufacture: data.brand_manufacture || null, + brand_model: data.brand_model || null, is_active: data.is_active, - created_by: data.created_by - }); + created_by: data.created_by || null + }; + + const createdBrand = await createBrandDb(brandData); + if (!createdBrand || !createdBrand.brand_id) { + throw new Error('Failed to create brand'); + } const brandId = createdBrand.brand_id; - for (const ec of data.error_code) { - const errorId = await createErrorCodeDb(brandId, { - error_code: ec.error_code, - error_code_name: ec.error_code_name, - error_code_description: ec.error_code_description, - error_code_color: ec.error_code_color, - path_icon: ec.path_icon, - is_active: ec.is_active, - created_by: data.created_by - }); - for (const s of ec.solution) { - await createSolutionDb(errorId, { - solution_name: s.solution_name, - type_solution: s.type_solution, - text_solution: s.text_solution || null, - path_solution: s.path_solution || null, - is_active: s.is_active, - created_by: data.created_by - }); + for (const ec of data.error_code) { + const errorCodePayload = { + error_code: ec.error_code, + error_code_name: ec.error_code_name || null, + error_code_description: ec.error_code_description || null, + error_code_color: ec.error_code_color || null, + path_icon: ec.path_icon || null, + is_active: ec.is_active, + created_by: data.created_by || null + }; + + const errorId = await createErrorCodeDb(brandId, errorCodePayload); + if (!errorId) { + throw new Error('Failed to create error code'); } - if (Array.isArray(ec.sparepart)) { - for (const sp of ec.sparepart) { - await createSparePartDb(errorId, { - sparepart_name: sp.sparepart_name, - brand_sparepart_description: sp.brand_sparepart_description, - path_foto: sp.path_foto || null, - is_active: sp.is_active, - created_by: data.created_by - }); + // solutions + if (Array.isArray(ec.solution)) { + for (const s of ec.solution) { + const solPayload = { + solution_name: s.solution_name, + type_solution: s.type_solution, + text_solution: s.text_solution || null, + path_solution: s.path_solution || null, + is_active: s.is_active, + created_by: data.created_by || null + }; + await createSolutionDb(errorId, solPayload); } } + // spareparts + if (Array.isArray(ec.sparepart)) { + for (const sp of ec.sparepart) { + const spPayload = { + sparepart_name: sp.sparepart_name, + brand_sparepart_description: sp.brand_sparepart_description || null, + path_foto: sp.path_foto || null, + is_active: sp.is_active, + created_by: data.created_by || null + }; + await createSparePartDb(errorId, spPayload); + } + } } return await this.getBrandById(brandId); - } catch (error) { - throw new ErrorHandler(500, `Bulk insert failed: ${error.message}`); + if (error instanceof ErrorHandler) throw error; + throw new ErrorHandler(500, error.message || 'Create brand failed'); } } static async updateBrandWithFullData(id, data) { try { const existingBrand = await getBrandByIdDb(id); - if (!existingBrand) throw new ErrorHandler(404, "Brand not found"); + if (!existingBrand) throw new ErrorHandler(404, 'Brand not found'); - await updateBrandDb(existingBrand.brand_name, { - brand_name: data.brand_name, - brand_type: data.brand_type, - brand_manufacture: data.brand_manufacture, - brand_model: data.brand_model, - is_active: data.is_active, - updated_by: data.updated_by - }); + if (data.brand_name && data.brand_name !== existingBrand.brand_name) { + const brandExists = await checkBrandNameExistsDb(data.brand_name, id); + if (brandExists) throw new ErrorHandler(400, 'Brand name already exists'); + } - if (!data.error_code) return await this.getBrandById(id); + const brandUpdatePayload = { + brand_name: data.brand_name ?? existingBrand.brand_name, + brand_type: data.brand_type ?? existingBrand.brand_type, + brand_manufacture: data.brand_manufacture ?? existingBrand.brand_manufacture, + brand_model: data.brand_model ?? existingBrand.brand_model, + is_active: typeof data.is_active === 'boolean' ? data.is_active : existingBrand.is_active, + updated_by: data.updated_by || null + }; + await updateBrandDb(existingBrand.brand_name, brandUpdatePayload); - const existingErrorCodes = await getErrorCodesByBrandIdDb(id); + if (!Array.isArray(data.error_code)) { + return await this.getBrandById(id); + } + const existingErrorCodes = await getErrorCodesByBrandIdDb(id) || []; const incomingErrorCodes = data.error_code.map(ec => ec.error_code); for (const ec of data.error_code) { - const existsEC = existingErrorCodes.find(e => e.error_code === ec.error_code); - + const existingEC = existingErrorCodes.find(e => e.error_code === ec.error_code); let errorId = null; - if (existsEC) { - await updateErrorCodeDb(existingBrand.brand_id, existsEC.error_code, { - error_code_name: ec.error_code_name, - error_code_description: ec.error_code_description, - error_code_color: ec.error_code_color, - path_icon: ec.path_icon, - is_active: ec.is_active, - updated_by: data.updated_by - }); - errorId = existsEC.error_code_id; + if (existingEC) { + const updatePayload = { + error_code_name: ec.error_code_name ?? existingEC.error_code_name, + error_code_description: ec.error_code_description ?? existingEC.error_code_description, + error_code_color: ec.error_code_color ?? existingEC.error_code_color, + path_icon: ec.path_icon ?? existingEC.path_icon, + is_active: existingEC.is_active, + updated_by: data.updated_by || null + }; + await updateErrorCodeDb(existingEC.brand_id, existingEC.error_code, updatePayload); + errorId = existingEC.error_code_id; } else { - errorId = await createErrorCodeDb(id, { + const createPayload = { error_code: ec.error_code, - error_code_name: ec.error_code_name, - error_code_description: ec.error_code_description, - error_code_color: ec.error_code_color, - path_icon: ec.path_icon, + error_code_name: ec.error_code_name || null, + error_code_description: ec.error_code_description || null, + error_code_color: ec.error_code_color || null, + path_icon: ec.path_icon || null, is_active: ec.is_active, - created_by: data.updated_by - }); + created_by: data.updated_by || null + }; + errorId = await createErrorCodeDb(id, createPayload); } - const existingSolutions = await getSolutionsByErrorCodeIdDb(errorId); - const incomingSolutionNames = ec.solution?.map(s => s.solution_name) || []; + if (!errorId) throw new Error(`Failed to create/update error code ${ec.error_code}`); - for (const s of ec.solution || []) { + const existingSolutions = await getSolutionsByErrorCodeIdDb(errorId) || []; + const incomingSolutionNames = (ec.solution || []).map(s => s.solution_name); + + for (const s of (ec.solution || [])) { const existsSolution = existingSolutions.find(es => es.solution_name === s.solution_name); - + const solPayload = { + solution_name: s.solution_name, + type_solution: s.type_solution, + text_solution: s.text_solution || null, + path_solution: s.path_solution || null, + is_active: s.is_active, + updated_by: data.updated_by || null, + created_by: data.updated_by || null + }; if (existsSolution) { - await updateSolutionDb(existsSolution.brand_code_solution_id, { - solution_name: s.solution_name, - type_solution: s.type_solution, - text_solution: s.text_solution || null, - path_solution: s.path_solution || null, - is_active: s.is_active, - updated_by: data.updated_by - }); + await updateSolutionDb(existsSolution.brand_code_solution_id, solPayload); } else { - await createSolutionDb(errorId, { - solution_name: s.solution_name, - type_solution: s.type_solution, - text_solution: s.text_solution || null, - path_solution: s.path_solution || null, - is_active: s.is_active, - created_by: data.updated_by - }); + await createSolutionDb(errorId, solPayload); } } @@ -246,28 +261,23 @@ class BrandService { } } - const existingSpareparts = await getSparePartnsByErrorCodeIdDb(errorId); - const incomingSparepartNames = ec.sparepart?.map(sp => sp.sparepart_name) || []; - - for (const sp of ec.sparepart || []) { - const existsSP = existingSpareparts.find(es => es.sparepart_name === sp.sparepart_name); + const existingSpareparts = await getSparePartnsByErrorCodeIdDb(errorId) || []; + const incomingSparepartNames = (ec.sparepart || []).map(sp => sp.sparepart_name); + for (const sp of (ec.sparepart || [])) { + const existsSP = existingSpareparts.find(e => e.sparepart_name === sp.sparepart_name); + const spPayload = { + sparepart_name: sp.sparepart_name, + brand_sparepart_description: sp.brand_sparepart_description || null, + path_foto: sp.path_foto || null, + is_active: sp.is_active, + updated_by: data.updated_by || null, + created_by: data.updated_by || null + }; if (existsSP) { - await updateSparePartDb(existsSP.brand_sparepart_id, { - sparepart_name: sp.sparepart_name, - brand_sparepart_description: sp.brand_sparepart_description, - path_foto: sp.path_foto || null, - is_active: sp.is_active, - updated_by: data.updated_by - }); + await updateSparePartDb(existsSP.brand_sparepart_id, spPayload); } else { - await createSparePartDb(errorId, { - sparepart_name: sp.sparepart_name, - brand_sparepart_description: sp.brand_sparepart_description, - path_foto: sp.path_foto || null, - is_active: sp.is_active, - created_by: data.updated_by - }); + await createSparePartDb(errorId, spPayload); } } @@ -276,7 +286,6 @@ class BrandService { await deleteSparePartDb(es.brand_sparepart_id, data.updated_by); } } - } for (const ec of existingErrorCodes) { @@ -286,21 +295,20 @@ class BrandService { } return await this.getBrandById(id); - } catch (error) { - throw new ErrorHandler(500, `Update failed: ${error.message}`); + if (error instanceof ErrorHandler) throw error; + throw new ErrorHandler(500, `Update failed: ${error.message || 'unknown error'}`); } } static async deleteBrand(id, userId) { try { - const exist = await getBrandByIdDb(id); - if (!exist) throw new ErrorHandler(404, "Brand not found"); - - return await deleteBrandDb(id, userId); - + const brandExist = await getBrandByIdDb(id); + if (!brandExist) throw new ErrorHandler(404, 'Brand not found'); + const result = await deleteBrandDb(id, userId); + return result; } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); + throw new ErrorHandler(error.statusCode || 500, error.message || 'Delete failed'); } } } diff --git a/services/notification.service.js b/services/notification.service.js index a7319d0..02c9d54 100644 --- a/services/notification.service.js +++ b/services/notification.service.js @@ -6,6 +6,24 @@ const { deleteNotificationDb, } = require('../db/notification.db'); +const { + getErrorCodeByIdDb, +} = require('../db/brand_code.db'); + +const { + getSolutionsByErrorCodeIdDb, +} = require('../db/brand_code_solution.db'); + +const { + getSparePartnsByErrorCodeIdDb, +} = require('../db/brand_sparepart.db'); + +const { + getAllNotificationErrorLogDb, +} = require('../db/notification_error_log.db'); + +const { getFileUploadByPathDb } = require('../db/file_uploads.db'); + const { ErrorHandler } = require('../helpers/error'); class NotificationService { @@ -26,13 +44,56 @@ class NotificationService { // Get notification by ID static async getNotificationById(id) { try { - const result = await getNotificationByIdDb(id); + const notification = await getNotificationByIdDb(id); - if (!result || (Array.isArray(result) && result.length < 1)) { + if (!notification || (Array.isArray(notification) && notification.length < 1)) { throw new ErrorHandler(404, 'Notification not found'); } - return result; + // Get error code details if error_code_id exists + if (notification.error_code_id) { + const errorCode = await getErrorCodeByIdDb(notification.error_code_id); + + if (errorCode) { + // Get solutions for this error code + const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || []; + + const solutionsWithDetails = await Promise.all( + solutions.map(async (solution) => { + let fileData = null; + if (solution.path_solution && solution.type_solution && solution.type_solution !== 'text') { + try { + fileData = await getFileUploadByPathDb(solution.path_solution); + } catch (e) { + fileData = null; + } + } + return { + ...solution, + file_upload_name: fileData?.file_upload_name || null, + path_document: fileData?.path_document || null + }; + }) + ); + + // Get spareparts for this error code + const spareparts = (await getSparePartnsByErrorCodeIdDb(errorCode.error_code_id)) || []; + + notification.error_code = { + ...errorCode, + solution: solutionsWithDetails, + sparepart: spareparts + }; + } + } + + // Get activity logs for this notification + const activityLogs = (await getAllNotificationErrorLogDb()) || []; + const notificationLogs = activityLogs.filter(log => log.notification_error_id === parseInt(id)); + + notification.activity_logs = notificationLogs; + + return notification; } catch (error) { throw new ErrorHandler(error.statusCode, error.message); } diff --git a/services/notification_error_sparepart.service.js b/services/notification_error_sparepart.service.js new file mode 100644 index 0000000..a5208c4 --- /dev/null +++ b/services/notification_error_sparepart.service.js @@ -0,0 +1,93 @@ +const { + getAllNotificationErrorSparepartDb, + getNotificationErrorSparepartByIdDb, + createNotificationErrorSparepartDb, + updateNotificationErrorSparepartDb, + deleteNotificationErrorSparepartDb, +} = require("../db/notification_error_sparepart.db"); + +const { getContactByIdDb } = require("../db/contact.db"); +const { ErrorHandler } = require("../helpers/error"); + +class NotificationErrorSparepartService { + static _checkAccess(contactType) { + if (contactType !== "gudang") { + throw new ErrorHandler( + 403, + "Akses ditolak. Hanya contact_type 'gudang' yang dapat getAll/create/update/delete." + ); + } + } + + static async getAll(param, contact_id) { + const contactResult = await getContactByIdDb(contact_id); + + if (!contactResult || contactResult.length < 1) + throw new ErrorHandler(404, "Contact tidak ditemukan"); + + const contact = contactResult[0]; + + this._checkAccess(contact.contact_type); + return await getAllNotificationErrorSparepartDb(param); + } + + static async getById(id) { + const result = await getNotificationErrorSparepartByIdDb(id); + + if (result.length < 1) + throw new ErrorHandler(404, "Notification Error Sparepart not found"); + + return result; + } + + static async create(data) { + const contactResult = await getContactByIdDb(data.contact_id); + + if (!contactResult || contactResult.length < 1) + throw new ErrorHandler(404, "Contact tidak ditemukan"); + + const contact = contactResult[0]; + + this._checkAccess(contact.contact_type); + + return await createNotificationErrorSparepartDb(data); + } + + static async update(id, data) { + const contactResult = await getContactByIdDb(data.contact_id); + + if (!contactResult || contactResult.length < 1) + throw new ErrorHandler(404, "Contact tidak ditemukan"); + + const contact = contactResult[0]; + + this._checkAccess(contact.contact_type); + + const exist = await getNotificationErrorSparepartByIdDb(id); + + if (exist.length < 1) + throw new ErrorHandler(404, "Notification Error Sparepart not found"); + + return await updateNotificationErrorSparepartDb(id, data); + } + + static async delete(id, contact_id) { + const contactResult = await getContactByIdDb(contact_id); + + if (!contactResult || contactResult.length < 1) + throw new ErrorHandler(404, "Contact tidak ditemukan"); + + const contact = contactResult[0]; + + this._checkAccess(contact.contact_type); + + const exist = await getNotificationErrorSparepartByIdDb(id); + + if (exist.length < 1) + throw new ErrorHandler(404, "Notification Error Sparepart not found"); + + return await deleteNotificationErrorSparepartDb(id); + } +} + +module.exports = NotificationErrorSparepartService; diff --git a/uploads/pdf/pdf-dd41285d-2025-11-13_13-38-05.pdf b/uploads/pdf/pdf-d0ded6ba-2025-10-26_17-11-14.pdf similarity index 100% rename from uploads/pdf/pdf-dd41285d-2025-11-13_13-38-05.pdf rename to uploads/pdf/pdf-d0ded6ba-2025-10-26_17-11-14.pdf diff --git a/validate/notification_error_sparepart.schema.js b/validate/notification_error_sparepart.schema.js new file mode 100644 index 0000000..3751b1b --- /dev/null +++ b/validate/notification_error_sparepart.schema.js @@ -0,0 +1,30 @@ +const Joi = require("joi"); + +const insertNotificationErrorSparepartSchema = Joi.object({ + contact_id: Joi.number().required(), + + brand_sparepart_id: Joi.number().required(), + + is_verification: Joi.boolean().required(), + + is_available: Joi.boolean().required(), + + is_active: Joi.boolean().required(), +}); + +const updateNotificationErrorSparepartSchema = Joi.object({ + contact_id: Joi.number().required(), + + brand_sparepart_id: Joi.number().optional(), + + is_verification: Joi.boolean().optional(), + + is_available: Joi.boolean().optional(), + + is_active: Joi.boolean().optional(), +}); + +module.exports = { + insertNotificationErrorSparepartSchema, + updateNotificationErrorSparepartSchema, +};