diff --git a/controllers/notification.controller.js b/controllers/notification.controller.js deleted file mode 100644 index aff649d..0000000 --- a/controllers/notification.controller.js +++ /dev/null @@ -1,80 +0,0 @@ -const NotificationService = require('../services/notification.service'); -const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); -const { insertNotificationSchema, updateNotificationSchema } = require('../validate/notification.schema'); - -class NotificationController { - static async getAll(req, res) { - const queryParams = req.query; - - const results = await NotificationService.getAllNotification(queryParams); - const response = await setResponsePaging(queryParams, results, 'Notification found') - - res.status(response.statusCode).json(response); - } - - - static async getById(req, res) { - try { - const { id } = req.params; - const results = await NotificationService.getNotificationById(id); - const response = await setResponse(results, 'Notification retrieved successfully'); - return res.status(response.statusCode).json(response); - } catch (err) { - console.error(" Notification Error:", err.message); - return res.status(500).json(setResponse(err, 'Failed to fetch notification', 500)); - } - } - - static async create(req, res) { - try { - const { error, value } = await checkValidate(insertNotificationSchema, req); - - if (error) { - return res.status(400).json(setResponse(error, 'Validation failed', 400)); - } - - value.created_by = req.user?.user_id || 'system'; - - const results = await NotificationService.createNotification(value); - const response = await setResponse(results, 'Notification created successfully'); - return res.status(response.statusCode).json(response); - } catch (err) { - console.error("Notification Error:", err.message); - return res.status(500).json(setResponse(err, 'Failed to create notification', 500)); - } - } - - static async update(req, res) { - try { - const { id } = req.params; - const { error, value } = await checkValidate(updateNotificationSchema, req); - - if (error) { - return res.status(400).json(setResponse(error, 'Validation failed', 400)); - } - - value.updated_by = req.user?.user_id || 'system'; - - const results = await NotificationService.updateNotification(id, value); - const response = await setResponse(results, 'Notification updated successfully'); - return res.status(response.statusCode).json(response); - } catch (err) { - console.error("Notification Error:", err.message); - return res.status(500).json(setResponse(err, 'Failed to update notification', 500)); - } - } - - static async delete(req, res) { - try { - const { id } = req.params; - const results = await NotificationService.deleteNotification(id, req.user?.user_id || 'system'); - const response = await setResponse(results, 'Notification deleted successfully'); - return res.status(response.statusCode).json(response); - } catch (err) { - console.error("Notification Error:", err.message); - return res.status(500).json(setResponse(err, 'Failed to delete notification', 500)); - } - } -} - -module.exports = NotificationController; diff --git a/controllers/notification_error.controller.js b/controllers/notification_error.controller.js new file mode 100644 index 0000000..d37a36c --- /dev/null +++ b/controllers/notification_error.controller.js @@ -0,0 +1,25 @@ +const NotificationErrorService = require('../services/notification_error.service'); +const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); + +class NotificationErrorController { + static async getAll(req, res) { + const queryParams = req.query; + + const results = await NotificationErrorService.getAllNotification(queryParams); + const response = await setResponsePaging(queryParams, results, 'Notification found') + + res.status(response.statusCode).json(response); + } + + static async getById(req, res) { + const { id } = req.params; + + const results = await NotificationErrorService.getNotificationById(id); + const response = await setResponse(results, 'Notification retrieved successfully'); + + return res.status(response.statusCode).json(response); + } + +} + +module.exports = NotificationErrorController; \ No newline at end of file diff --git a/controllers/sparepart.controller.js b/controllers/sparepart.controller.js new file mode 100644 index 0000000..a1d5fe0 --- /dev/null +++ b/controllers/sparepart.controller.js @@ -0,0 +1,80 @@ +const SparepartService = require('../services/sparepart.service'); +const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); + +const { + insertSparepartSchema, + updateSparepartSchema, +} = require('../validate/sparepart.schema'); + +class SparepartController { + static async getAll(req, res) { + const queryParams = req.query; + const results = await SparepartService.getAllSparepart(queryParams); + const response = await setResponsePaging(queryParams, results, 'Sparepart found'); + res.status(response.statusCode).json(response); + } + + static async getById(req, res) { + const { id } = req.params; + const results = await SparepartService.getSparepartById(id); + const response = await setResponse(results, 'Sparepart found'); + res.status(response.statusCode).json(response); + } + + static async create(req, res) { + const { error, value } = await checkValidate(insertSparepartSchema, req); + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + try { + if (req.file) { + const file = req.file; + const ext = require('path').extname(file.originalname).toLowerCase(); + const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE"; + const folder = typeDoc === "PDF" ? "pdf" : "images"; + const pathDocument = `${folder}/${file.filename}`; + value.sparepart_foto = pathDocument; + } + value.userId = req.user.user_id + const results = await SparepartService.createSparepart(value); + const response = await setResponse(results, 'Sparepart created successfully'); + return res.status(response.statusCode).json(response); + } catch (err) { + const response = setResponse([], err.message, err.statusCode || 500); + res.status(response.statusCode).json(response); + } + } + + static async update(req, res) { + const { id } = req.params; + const { error, value } = await checkValidate(updateSparepartSchema, req); + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + try { + if (req.file) { + const file = req.file; + const ext = require('path').extname(file.originalname).toLowerCase(); + const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE"; + const folder = typeDoc === "PDF" ? "pdf" : "images"; + const pathDocument = `${folder}/${file.filename}`; + value.sparepart_foto = pathDocument; + } + value.userId = req.user.user_id + const results = await SparepartService.updateSparepart(id, value); + const response = await setResponse(results, 'Sparepart updated successfully'); + res.status(response.statusCode).json(response); + } catch (err) { + const response = setResponse([], err.message, err.statusCode || 500); + res.status(response.statusCode).json(response); + } + } + + static async delete(req, res) { + const { id } = req.params; + const results = await SparepartService.deleteSparepart(id, req.user.user_id); + const response = await setResponse(results, 'Sparepart deleted successfully'); + res.status(response.statusCode).json(response); + } +} +module.exports = SparepartController; \ No newline at end of file diff --git a/db/brand_code.db.js b/db/brand_code.db.js index 7c4de4c..4d8925a 100644 --- a/db/brand_code.db.js +++ b/db/brand_code.db.js @@ -13,8 +13,6 @@ const getErrorCodesByBrandIdDb = async (brandId) => { return result.recordset; }; - -// Create error code for brand const createErrorCodeDb = async (brandId, data) => { const store = { brand_id: brandId, @@ -33,7 +31,6 @@ const createErrorCodeDb = async (brandId, data) => { return insertedId; }; -// Update error code by brand ID and error code const updateErrorCodeDb = async (brandId, errorCode, data) => { const store = { ...data }; const whereData = { @@ -46,7 +43,6 @@ const updateErrorCodeDb = async (brandId, errorCode, data) => { return true; }; -// Soft delete error code by brand ID and error code const deleteErrorCodeDb = async (brandId, errorCode, deletedBy) => { const queryText = ` UPDATE brand_code @@ -57,7 +53,6 @@ const deleteErrorCodeDb = async (brandId, errorCode, deletedBy) => { return true; }; -// Get error code by error_code_id const getErrorCodeByIdDb = async (error_code_id) => { const queryText = ` SELECT diff --git a/db/brand_sparepart.db.js b/db/brand_sparepart.db.js deleted file mode 100644 index a0b17e4..0000000 --- a/db/brand_sparepart.db.js +++ /dev/null @@ -1,59 +0,0 @@ -const pool = require("../config"); - -// Get solutions by error code ID -const getSparePartnsByErrorCodeIdDb = async (errorCodeId) => { - const queryText = ` - SELECT - a.* - FROM brand_sparepart a - WHERE a.error_code_id = $1 AND a.deleted_at IS NULL - ORDER BY a.brand_sparepart_id - `; - const result = await pool.query(queryText, [errorCodeId]); - return result.recordset; -}; - -// Create solution for error code -const createSparePartDb = async (errorCodeId, data) => { - const store = { - error_code_id: errorCodeId, - sparepart_name: data.sparepart_name, - brand_sparepart_description: data.brand_sparepart_description, - path_foto: data.path_foto, - is_active: data.is_active, - created_by: data.created_by - }; - - const { query: queryText, values } = pool.buildDynamicInsert("brand_sparepart", store); - const result = await pool.query(queryText, values); - const insertedId = result.recordset[0]?.inserted_id; - return insertedId; -}; - -// Update SparePartn -const updateSparePartDb = async (SparePartId, data) => { - const store = { ...data }; - const whereData = { brand_sparepart_id: SparePartId }; - - const { query: queryText, values } = pool.buildDynamicUpdate("brand_sparepart", store, whereData); - await pool.query(`${queryText} AND deleted_at IS NULL`, values); - return true; -}; - -// Soft delete SparePartn -const deleteSparePartDb = async (SparePartId, deletedBy) => { - const queryText = ` - UPDATE brand_sparepart - SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1 - WHERE brand_sparepart_id = $2 AND deleted_at IS NULL - `; - await pool.query(queryText, [deletedBy, SparePartId]); - return true; -}; - -module.exports = { - getSparePartnsByErrorCodeIdDb, - createSparePartDb, - updateSparePartDb, - deleteSparePartDb, -}; \ No newline at end of file diff --git a/db/notification.db.js b/db/notification_error.db.js similarity index 82% rename from db/notification.db.js rename to db/notification_error.db.js index 36d9951..45d8449 100644 --- a/db/notification.db.js +++ b/db/notification_error.db.js @@ -57,12 +57,20 @@ const getAllNotificationDb = async (searchParams = {}) => { await InsertNotificationErrorDb(); + const boolFields = ["is_send", "is_delivered", "is_read", "is_active"]; + + boolFields.forEach((f) => { + if (searchParams[f] !== undefined && searchParams[f] !== null && searchParams[f] !== "") { + const v = searchParams[f]; + searchParams[f] = v == "1" ? 1 : v == "0" ? 0 : null; + } + }); + if (searchParams.limit) { const page = Number(searchParams.page ?? 1) - 1; queryParams = [Number(searchParams.limit ?? 10), page]; } - // Build dynamic WHERE OR const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike( [ "b.error_code", @@ -71,26 +79,24 @@ const getAllNotificationDb = async (searchParams = {}) => { "COALESCE(a.is_send, 0)", "COALESCE(a.is_delivered, 0)", "COALESCE(a.is_read, 0)", - "COALESCE(a.is_active, 0)" + "COALESCE(a.is_active, 0)", ], searchParams.criteria, queryParams ); if (whereParamOr) queryParams = whereParamOr; - // Build dynamic WHERE AND const { whereConditions, whereParamAnd } = pool.buildFilterQuery( [ - { column: "COALESCE(a.is_send, 0)", param: searchParams.is_send, type: "int" }, - { column: "COALESCE(a.is_delivered, 0)", param: searchParams.is_delivered, type: "int" }, - { column: "COALESCE(a.is_read, 0)", param: searchParams.is_read, type: "int" }, - { column: "COALESCE(a.is_active, 0)", param: searchParams.is_active, type: "int" }, + { column: "COALESCE(a.is_send, 0)", param: searchParams.is_send, type: "number" }, + { column: "COALESCE(a.is_delivered, 0)", param: searchParams.is_delivered, type: "number" }, + { column: "COALESCE(a.is_read, 0)", param: searchParams.is_read, type: "number" }, + { column: "COALESCE(a.is_active, 0)", param: searchParams.is_active, type: "number" }, ], queryParams ); if (whereParamAnd) queryParams = whereParamAnd; - const queryText = ` SELECT COUNT(*) OVER() AS total_data, @@ -117,25 +123,15 @@ const getAllNotificationDb = async (searchParams = {}) => { COALESCE(d.device_name, '') + ' - ' + COALESCE(b.error_code_name, '') AS device_name_error FROM notification_error a - LEFT JOIN brand_code b - ON a.error_code_id = b.error_code_id - AND b.deleted_at IS NULL - + ON a.error_code_id = b.error_code_id AND b.deleted_at IS NULL LEFT JOIN brand_code_solution c - ON b.error_code_id = c.error_code_id - AND c.deleted_at IS NULL - + ON b.error_code_id = c.error_code_id AND c.deleted_at IS NULL LEFT JOIN m_device d - ON b.brand_id = d.brand_id - AND d.deleted_at IS NULL + ON b.brand_id = d.brand_id AND d.deleted_at IS NULL WHERE a.deleted_at IS NULL - ${ - whereConditions.length > 0 - ? ` AND ${whereConditions.join(" AND ")}` - : "" - } + ${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""} ${whereOrConditions ? ` ${whereOrConditions}` : ""} ORDER BY a.notification_error_id DESC @@ -153,6 +149,7 @@ const getAllNotificationDb = async (searchParams = {}) => { return { data: result.recordset, total }; }; + module.exports = { getNotificationByIdDb, getAllNotificationDb, diff --git a/db/sparepart.db.js b/db/sparepart.db.js new file mode 100644 index 0000000..3dc92b6 --- /dev/null +++ b/db/sparepart.db.js @@ -0,0 +1,157 @@ +const pool = require("../config"); + +// Get all devices +const getAllSparepartDb = async (searchParams = {}) => { + let queryParams = []; + + // Pagination + if (searchParams.limit) { + const page = Number(searchParams.page ?? 1) - 1; + queryParams = [Number(searchParams.limit ?? 10), page]; + } + + // Search + const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike( + [ + "a.sparepart_name", + "a.sparepart_code", + "a.sparepart_model", + "a.updated_at", + ], + searchParams.criteria, + queryParams + ); + + queryParams = whereParamOr ? whereParamOr : queryParams; + + // Filter + const { whereConditions, whereParamAnd } = pool.buildFilterQuery( + [ + { column: "a.sparepart_name", param: searchParams.code, type: "string" }, + { + column: "a.sparepart_code", + param: searchParams.location, + type: "string", + }, + { + column: "b.sparepart_model", + param: searchParams.brand, + type: "string", + }, + { column: "a.updated_at", param: searchParams.status, type: "string" }, + ], + queryParams + ); + + queryParams = whereParamAnd ? whereParamAnd : queryParams; + + const queryText = ` + SELECT + COUNT(*) OVER() AS total_data, + a.*, + COALESCE(a.sparepart_code, '') + ' - ' + COALESCE(a.sparepart_name, '') AS sparepart_code_name + FROM m_sparepart a + WHERE a.deleted_at IS NULL + ${ + whereConditions.length > 0 ? `AND ${whereConditions.join(" AND ")}` : "" + } + ${whereOrConditions ? whereOrConditions : ""} + ORDER BY a.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 getSparepartByIdDb = async (id) => { + const queryText = ` + SELECT + a.*, + COALESCE(a.sparepart_code, '') + ' - ' + COALESCE(a.sparepart_name, '') AS sparepart_code_name + FROM m_sparepart a + WHERE a.sparepart_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset; +}; + +const checkSparepartNameExistsDb = async (sparePartName, excludeId = null) => { + let queryText = ` + SELECT sparepart_id + FROM m_sparepart + WHERE sparepart_name = $1 AND deleted_at IS NULL + `; + let values = [sparePartName]; + if (excludeId) { + queryText += ` AND sparepart_id != $2`; + values.push(excludeId); + } + const result = await pool.query(queryText, values); + return result.recordset.length > 0; +}; + +const createSparepartDb = async (data) => { + const newCode = await pool.generateKode( + "SPAREPART", + "m_sparepart", + "sparepart_code" + ); + + const store = { + ...data, + sparepart_code: newCode, + }; + + const { query: queryText, values } = pool.buildDynamicInsert( + "m_sparepart", + store + ); + const result = await pool.query(queryText, values); + const insertedId = result.recordset[0]?.inserted_id; + return insertedId ? await getSparepartByIdDb(insertedId) : null; +}; + +const updateSparepartDb = async (id, data) => { + const store = { + ...data, + }; + + // Kondisi WHERE + const whereData = { + sparepart_id: id, + }; + + const { query: queryText, values } = pool.buildDynamicUpdate( + "m_sparepart", + store, + whereData + ); + await pool.query(`${queryText} AND deleted_at IS NULL`, values); + return getSparepartByIdDb(id); +}; + +const deleteSparepartDb = async (id, deletedBy) => { + const queryText = ` + UPDATE m_sparepart + SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1 + WHERE sparepart_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [deletedBy, id]); + return true; +}; + +module.exports = { + getAllSparepartDb, + getSparepartByIdDb, + checkSparepartNameExistsDb, + createSparepartDb, + updateSparepartDb, + deleteSparepartDb, +}; diff --git a/routes/index.js b/routes/index.js index 6ea3e3e..1520898 100644 --- a/routes/index.js +++ b/routes/index.js @@ -14,8 +14,9 @@ const unit = require("./unit.route") const UserSchedule = require("./user_schedule.route") const historyValue = require("./history_value.route") const contact = require("./contact.route") -const notification = require("./notification.route") +const notificationError = require("./notification_error.route") const notificationErrorSparepart = require("./notification_error_sparepart.route") +const sparepart = require("./sparepart.route") router.use("/auth", auth); router.use("/user", users); @@ -32,7 +33,8 @@ router.use("/unit", unit); router.use("/user-schedule", UserSchedule) router.use("/history", historyValue) router.use("/contact", contact) -router.use("/notification", notification) +router.use("/notification", notificationError) router.use("/notification-sparepart", notificationErrorSparepart) +router.use("/sparepart", sparepart) module.exports = router; diff --git a/routes/notification.route.js b/routes/notification.route.js deleted file mode 100644 index e6f1db0..0000000 --- a/routes/notification.route.js +++ /dev/null @@ -1,23 +0,0 @@ -const express = require('express'); -const NotificationController = require('../controllers/notification.controller'); -const verifyToken = require('../middleware/verifyToken'); -const verifyAccess = require('../middleware/verifyAccess'); - -const router = express.Router(); - -// =========================== -// Notification Routes -// =========================== - -router - .route('/') - .get(verifyToken.verifyAccessToken, NotificationController.getAll) - .post(verifyToken.verifyAccessToken, verifyAccess(), NotificationController.create); - -router - .route('/:id') - .get(verifyToken.verifyAccessToken, NotificationController.getById) - .put(verifyToken.verifyAccessToken, verifyAccess(), NotificationController.update) - .delete(verifyToken.verifyAccessToken, verifyAccess(), NotificationController.delete); - -module.exports = router; diff --git a/routes/notification_error.route.js b/routes/notification_error.route.js new file mode 100644 index 0000000..a0e8f83 --- /dev/null +++ b/routes/notification_error.route.js @@ -0,0 +1,16 @@ +const express = require('express'); +const NotificationErrorController = require('../controllers/notification_error.controller'); +const verifyToken = require('../middleware/verifyToken'); +const verifyAccess = require('../middleware/verifyAccess'); + +const router = express.Router(); + +router + .route('/') + .get(verifyToken.verifyAccessToken, NotificationErrorController.getAll) + +router + .route('/:id') + .get(verifyToken.verifyAccessToken, NotificationErrorController.getById) + +module.exports = router; diff --git a/routes/sparepart.route.js b/routes/sparepart.route.js new file mode 100644 index 0000000..27a34ce --- /dev/null +++ b/routes/sparepart.route.js @@ -0,0 +1,19 @@ +const express = require('express'); +const SparepartController = require('../controllers/sparepart.controller'); +const verifyToken = require('../middleware/verifyToken'); +const verifyAccess = require('../middleware/verifyAccess'); +const upload = require('../middleware/uploads'); +const router = express.Router(); + +router.route('/') + .get(verifyToken.verifyAccessToken, SparepartController.getAll) + .post(verifyToken.verifyAccessToken, verifyAccess(), upload.single('sparepart_foto') +, SparepartController.create); + +router.route('/:id') + .get(verifyToken.verifyAccessToken, SparepartController.getById) + .put(verifyToken.verifyAccessToken, verifyAccess(), upload.single('sparepart_foto') +, SparepartController.update) + .delete(verifyToken.verifyAccessToken, verifyAccess(), SparepartController.delete); + +module.exports = router; \ No newline at end of file diff --git a/services/brand.service.js b/services/brand.service.js index 92cfedd..9bf28b2 100644 --- a/services/brand.service.js +++ b/services/brand.service.js @@ -1,4 +1,4 @@ -// BrandService.js +// Brand operations const { getAllBrandsDb, getBrandByIdDb, @@ -7,308 +7,352 @@ const { updateBrandDb, deleteBrandDb, checkBrandNameExistsDb, -} = require('../db/brand.db'); +} = require("../db/brand.db"); +// Error code operations const { getErrorCodesByBrandIdDb, createErrorCodeDb, updateErrorCodeDb, deleteErrorCodeDb, -} = require('../db/brand_code.db'); - -const { - getSparePartnsByErrorCodeIdDb, - createSparePartDb, - updateSparePartDb, - deleteSparePartDb, -} = require('../db/brand_sparepart.db'); +} = require("../db/brand_code.db"); +// Solution operations const { getSolutionsByErrorCodeIdDb, createSolutionDb, updateSolutionDb, deleteSolutionDb, -} = require('../db/brand_code_solution.db'); - -const { getFileUploadByPathDb } = require('../db/file_uploads.db'); -const { ErrorHandler } = require('../helpers/error'); +} = require("../db/brand_code_solution.db"); +const { getFileUploadByPathDb } = require("../db/file_uploads.db"); +const { ErrorHandler } = require("../helpers/error"); class BrandService { + // Get all brands static async getAllBrands(param) { try { const results = await getAllBrandsDb(param); + + results.data.map((element) => {}); + return results; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message || 'Failed to get brands'); + throw new ErrorHandler(error.statusCode, error.message); } } + // Get brand by ID with complete data static async getBrandById(id) { try { const brand = await getBrandByIdDb(id); - if (!brand) throw new ErrorHandler(404, 'Brand not found'); + 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( + const errorCodesWithSolutions = await Promise.all( errorCodes.map(async (errorCode) => { - const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || []; - const solutionsWithFile = await Promise.all( - solutions.map(async (s) => { + const solutions = await getSolutionsByErrorCodeIdDb( + errorCode.error_code_id + ); + + const solutionsWithFiles = await Promise.all( + solutions.map(async (solution) => { let fileData = null; - if (s.path_solution && s.type_solution && s.type_solution !== 'text') { - try { - fileData = await getFileUploadByPathDb(s.path_solution); - } catch (e) { - fileData = null; - } + // console.log('Processing solution:', { + // solution_id: solution.brand_code_solution_id, + // path_solution: solution.path_solution, + // type_solution: solution.type_solution + // }); + + if (solution.path_solution && solution.type_solution !== "text") { + fileData = await getFileUploadByPathDb(solution.path_solution); + console.log("File data found:", fileData); } - return { - ...s, + + const enhancedSolution = { + ...solution, file_upload_name: fileData?.file_upload_name || null, - path_document: fileData?.path_document || null + path_document: fileData?.path_document || null, }; + + // console.log('Enhanced solution:', { + // solution_id: enhancedSolution.brand_code_solution_id, + // original_path_solution: enhancedSolution.path_solution, + // path_document: enhancedSolution.path_document, + // file_upload_name: enhancedSolution.file_upload_name + // }); + + return enhancedSolution; }) ); - const spareparts = (await getSparePartnsByErrorCodeIdDb(errorCode.error_code_id)) || []; - return { ...errorCode, - solution: solutionsWithFile, - sparepart: spareparts + solution: solutionsWithFiles, }; }) ); return { ...brand, - error_code: errorCodesWithDetails + error_code: errorCodesWithSolutions, }; } catch (error) { - throw new ErrorHandler(error.statusCode || 500, error.message || 'Failed to get brand detail'); + throw new ErrorHandler(error.statusCode, error.message); } } + // Create brand static async createBrandWithFullData(data) { try { - if (!data || typeof data !== 'object') data = {}; + if (!data || typeof data !== "object") data = {}; - 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 (!Array.isArray(data.error_code) || data.error_code.length === 0) { - throw new ErrorHandler(400, 'Brand must have at least 1 error code with solution'); + if (data.brand_name) { + const brandExists = await checkBrandNameExistsDb(data.brand_name); + if (brandExists) { + throw new ErrorHandler(400, "Brand name already exists"); + } } - for (const ec of data.error_code) { - if (!Array.isArray(ec.solution) || ec.solution.length === 0) { - throw new ErrorHandler(400, `Error code ${ec.error_code} must have at least 1 solution`); + if ( + !data.error_code || + !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 errorCode of data.error_code) { + if ( + !errorCode.solution || + !Array.isArray(errorCode.solution) || + errorCode.solution.length === 0 + ) { + throw new ErrorHandler( + 400, + `Error code ${errorCode.error_code} must have at least 1 solution` + ); } } const brandData = { brand_name: data.brand_name, - brand_type: data.brand_type || null, - brand_manufacture: data.brand_manufacture || null, - brand_model: data.brand_model || null, + brand_type: data.brand_type, + brand_manufacture: data.brand_manufacture, + brand_model: data.brand_model, is_active: data.is_active, - created_by: data.created_by || null + created_by: data.created_by, }; const createdBrand = await createBrandDb(brandData); - if (!createdBrand || !createdBrand.brand_id) { - throw new Error('Failed to create brand'); + if (!createdBrand) { + throw new Error("Failed to create brand"); } const brandId = createdBrand.brand_id; - 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 - }; + for (const errorCodeData of data.error_code) { + const errorId = await createErrorCodeDb(brandId, { + error_code: errorCodeData.error_code, + error_code_name: errorCodeData.error_code_name, + error_code_description: errorCodeData.error_code_description, + error_code_color: errorCodeData.error_code_color, + path_icon: errorCodeData.path_icon, + is_active: errorCodeData.is_active, + created_by: data.created_by, + }); - const errorId = await createErrorCodeDb(brandId, errorCodePayload); if (!errorId) { - throw new Error('Failed to create error code'); + throw new Error("Failed to create error code"); } - // 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); + // Create solutions for this error code + if (errorCodeData.solution && Array.isArray(errorCodeData.solution)) { + for (const solutionData of errorCodeData.solution) { + await createSolutionDb(errorId, { + solution_name: solutionData.solution_name, + type_solution: solutionData.type_solution, + text_solution: solutionData.text_solution || null, + path_solution: solutionData.path_solution || null, + is_active: solutionData.is_active, + created_by: data.created_by, + }); } } } return await this.getBrandById(brandId); } catch (error) { - if (error instanceof ErrorHandler) throw error; - throw new ErrorHandler(500, error.message || 'Create brand failed'); + throw new ErrorHandler(500, `Bulk insert failed: ${error.message}`); } } + // Soft delete brand by ID + static async deleteBrand(id, userId) { + try { + 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); + } + } + + // Update brand 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"); 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 (brandExists) { + throw new ErrorHandler(400, "Brand name already exists"); + } } - 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 + const brandData = { + 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, }; - await updateBrandDb(existingBrand.brand_name, brandUpdatePayload); - if (!Array.isArray(data.error_code)) { - return await this.getBrandById(id); - } + await updateBrandDb(existingBrand.brand_name, brandData); - const existingErrorCodes = await getErrorCodesByBrandIdDb(id) || []; - const incomingErrorCodes = data.error_code.map(ec => ec.error_code); + if (data.error_code && Array.isArray(data.error_code)) { + const existingErrorCodes = await getErrorCodesByBrandIdDb(id); + const incomingErrorCodes = data.error_code.map((ec) => ec.error_code); - for (const ec of data.error_code) { - const existingEC = existingErrorCodes.find(e => e.error_code === ec.error_code); - let errorId = null; + // Create/update/delete error codes + for (const errorCodeData of data.error_code) { + // Check if error code already exists + const existingEC = existingErrorCodes.find( + (ec) => ec.error_code === errorCodeData.error_code + ); - 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 { - const createPayload = { - 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.updated_by || null - }; - errorId = await createErrorCodeDb(id, createPayload); - } + if (existingEC) { + // Update existing error code using separate db function + await updateErrorCodeDb( + existingEC.brand_id, + existingEC.error_code, + { + error_code_name: errorCodeData.error_code_name, + error_code_description: errorCodeData.error_code_description, + error_code_color: errorCodeData.error_code_color, + path_icon: errorCodeData.path_icon, + is_active: errorCodeData.is_active, + updated_by: data.updated_by, + } + ); - if (!errorId) throw new Error(`Failed to create/update error code ${ec.error_code}`); + if ( + errorCodeData.solution && + Array.isArray(errorCodeData.solution) + ) { + const existingSolutions = await getSolutionsByErrorCodeIdDb( + existingEC.error_code_id + ); + const incomingSolutionNames = errorCodeData.solution.map( + (s) => s.solution_name + ); - const existingSolutions = await getSolutionsByErrorCodeIdDb(errorId) || []; - const incomingSolutionNames = (ec.solution || []).map(s => s.solution_name); + // Update or create solutions + for (const solutionData of errorCodeData.solution) { + const existingSolution = existingSolutions.find( + (s) => s.solution_name === solutionData.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, solPayload); + if (existingSolution) { + // Update existing solution + await updateSolutionDb( + existingSolution.brand_code_solution_id, + { + solution_name: solutionData.solution_name, + type_solution: solutionData.type_solution, + text_solution: solutionData.text_solution || null, + path_solution: solutionData.path_solution || null, + is_active: solutionData.is_active, + updated_by: data.updated_by, + } + ); + } else { + // Create new solution + await createSolutionDb(existingEC.error_code_id, { + solution_name: solutionData.solution_name, + type_solution: solutionData.type_solution, + text_solution: solutionData.text_solution || null, + path_solution: solutionData.path_solution || null, + is_active: solutionData.is_active, + created_by: data.updated_by, + }); + } + } + + // Delete solutions that are not in the incoming request + for (const existingSolution of existingSolutions) { + if ( + !incomingSolutionNames.includes( + existingSolution.solution_name + ) + ) { + await deleteSolutionDb( + existingSolution.brand_code_solution_id, + data.updated_by + ); + } + } + } } else { - await createSolutionDb(errorId, solPayload); + const errorId = await createErrorCodeDb(id, { + error_code: errorCodeData.error_code, + error_code_name: errorCodeData.error_code_name, + error_code_description: errorCodeData.error_code_description, + error_code_color: errorCodeData.error_code_color, + path_icon: errorCodeData.path_icon, + is_active: errorCodeData.is_active, + created_by: data.updated_by, + }); + + if ( + errorCodeData.solution && + Array.isArray(errorCodeData.solution) + ) { + for (const solutionData of errorCodeData.solution) { + await createSolutionDb(errorId, { + solution_name: solutionData.solution_name, + type_solution: solutionData.type_solution, + text_solution: solutionData.text_solution || null, + path_solution: solutionData.path_solution || null, + is_active: solutionData.is_active, + created_by: data.updated_by, + }); + } + } } } - for (const es of existingSolutions) { - if (!incomingSolutionNames.includes(es.solution_name)) { - await deleteSolutionDb(es.brand_code_solution_id, data.updated_by); + for (const existingEC of existingErrorCodes) { + if (!incomingErrorCodes.includes(existingEC.error_code)) { + await deleteErrorCodeDb(id, existingEC.error_code, data.updated_by); } } - - 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, spPayload); - } else { - await createSparePartDb(errorId, spPayload); - } - } - - for (const es of existingSpareparts) { - if (!incomingSparepartNames.includes(es.sparepart_name)) { - await deleteSparePartDb(es.brand_sparepart_id, data.updated_by); - } - } - } - - for (const ec of existingErrorCodes) { - if (!incomingErrorCodes.includes(ec.error_code)) { - await deleteErrorCodeDb(id, ec.error_code, data.updated_by); - } } return await this.getBrandById(id); } catch (error) { - if (error instanceof ErrorHandler) throw error; - throw new ErrorHandler(500, `Update failed: ${error.message || 'unknown error'}`); - } - } - - static async deleteBrand(id, userId) { - try { - 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 || 500, error.message || 'Delete failed'); + throw new ErrorHandler(500, `Update failed: ${error.message}`); } } } diff --git a/services/notification.service.js b/services/notification_error.service.js similarity index 67% rename from services/notification.service.js rename to services/notification_error.service.js index 02c9d54..7d96f76 100644 --- a/services/notification.service.js +++ b/services/notification_error.service.js @@ -1,10 +1,7 @@ const { getAllNotificationDb, getNotificationByIdDb, - insertNotificationDb, - updateNotificationDb, - deleteNotificationDb, -} = require('../db/notification.db'); +} = require('../db/notification_error.db'); const { getErrorCodeByIdDb, @@ -16,7 +13,7 @@ const { const { getSparePartnsByErrorCodeIdDb, -} = require('../db/brand_sparepart.db'); +} = require('../db/sparepart.db'); const { getAllNotificationErrorLogDb, @@ -98,49 +95,6 @@ class NotificationService { throw new ErrorHandler(error.statusCode, error.message); } } - - static async createNotification(data) { - try { - if (!data || typeof data !== 'object') data = {}; - - const result = await insertNotificationDb(data); - return result; - } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); - } - } - - static async updateNotification(id, data) { - try { - if (!data || typeof data !== 'object') data = {}; - - const dataExist = await getNotificationByIdDb(id); - - if (!dataExist || (Array.isArray(dataExist) && dataExist.length < 1)) { - throw new ErrorHandler(404, 'Notification not found'); - } - - const result = await updateNotificationDb(id, data); - return result; - } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); - } - } - - static async deleteNotification(id, userId) { - try { - const dataExist = await getNotificationByIdDb(id); - - if (!dataExist || (Array.isArray(dataExist) && dataExist.length < 1)) { - throw new ErrorHandler(404, 'Notification not found'); - } - - const result = await deleteNotificationDb(id, userId); - return result; - } catch (error) { - throw new ErrorHandler(error.statusCode, error.message); - } - } } module.exports = NotificationService; diff --git a/services/sparepart.service.js b/services/sparepart.service.js new file mode 100644 index 0000000..e380091 --- /dev/null +++ b/services/sparepart.service.js @@ -0,0 +1,88 @@ +const { + getAllSparepartDb, + getSparepartByIdDb, + createSparepartDb, + updateSparepartDb, + deleteSparepartDb, + checkSparepartNameExistsDb, +} = require("../db/sparepart.db"); + +const { ErrorHandler } = require("../helpers/error"); + +class SparepartService { + static async getAllSparepart(param) { + try { + const results = await getAllSparepartDb(param); + results.data.map((item) => {}); + return results; + } catch (error) { + throw new ErrorHandler(error.statusCode || 500, error.message); + } + } + + static async getSparepartById(id) { + try { + const sparepart = await getSparepartByIdDb(id); + + if (!sparepart || sparepart.length === 0) { + throw new ErrorHandler(404, "Sparepart not found"); + } + + return sparepart[0]; + } catch (error) { + throw new ErrorHandler(error.statusCode || 500, error.message); + } + } + + static async createSparepart(data) { + try { + if (!data || typeof data !== "object") data = {}; + if (data.sparepart_name) { + const exists = await checkSparepartNameExistsDb(data.sparepart_name); + if (exists) + throw new ErrorHandler(400, "Sparepart name already exists"); + } + const created = await createSparepartDb(data); + if (!created) throw new ErrorHandler(500, "Failed to create Sparepart"); + return created; + } catch (error) { + throw new ErrorHandler(error.statusCode || 500, error.message); + } + } + + static async updateSparepart(id, data) { + try { + const existing = await getSparepartByIdDb(id); + if (!existing) throw new ErrorHandler(404, "Sparepart not found"); + if ( + data.sparepart_name && + data.sparepart_name !== existing.sparepart_name + ) { + const exists = await checkSparepartNameExistsDb( + data.sparepart_name, + id + ); + if (exists) + throw new ErrorHandler(400, "Sparepart name already exists"); + } + const updated = await updateSparepartDb(id, data); + if (!updated) throw new ErrorHandler(500, "Failed to update Sparepart"); + return await this.getSparepartById(id); + } catch (error) { + throw new ErrorHandler(error.statusCode || 500, error.message); + } + } + + static async deleteSparepart(id, userId) { + try { + const existing = await getSparepartByIdDb(id); + if (!existing) throw new ErrorHandler(404, "Sparepart not found"); + const deleted = await deleteSparepartDb(id, userId); + if (!deleted) throw new ErrorHandler(500, "Failed to delete Sparepart"); + return deleted; + } catch (error) { + throw new ErrorHandler(error.statusCode || 500, error.message); + } + } +} +module.exports = SparepartService; diff --git a/uploads/images/img-35ee1427-2025-11-13_14-00-21.jpg b/uploads/images/img-35ee1427-2025-11-13_14-00-21.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-35ee1427-2025-11-13_14-00-21.jpg and /dev/null differ diff --git a/uploads/images/img-1eb18866-2025-11-13_13-53-40.jpg b/uploads/images/img-83d4f845-2025-11-24_16-42-41.jpg similarity index 100% rename from uploads/images/img-1eb18866-2025-11-13_13-53-40.jpg rename to uploads/images/img-83d4f845-2025-11-24_16-42-41.jpg diff --git a/uploads/images/img-93e423c3-2025-11-13_14-00-29.jpg b/uploads/images/img-93e423c3-2025-11-13_14-00-29.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-93e423c3-2025-11-13_14-00-29.jpg and /dev/null differ diff --git a/uploads/images/img-c5161c64-2025-11-13_14-07-43.jpg b/uploads/images/img-c5161c64-2025-11-13_14-07-43.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-c5161c64-2025-11-13_14-07-43.jpg and /dev/null differ diff --git a/uploads/images/img-c541e270-2025-11-13_13-42-29.jpg b/uploads/images/img-c541e270-2025-11-13_13-42-29.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-c541e270-2025-11-13_13-42-29.jpg and /dev/null differ diff --git a/uploads/images/img-c6fbc08f-2025-11-13_13-43-24.jpg b/uploads/images/img-c6fbc08f-2025-11-13_13-43-24.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-c6fbc08f-2025-11-13_13-43-24.jpg and /dev/null differ diff --git a/uploads/images/img-cc246c15-2025-11-13_14-07-49.jpg b/uploads/images/img-cc246c15-2025-11-13_14-07-49.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-cc246c15-2025-11-13_14-07-49.jpg and /dev/null differ diff --git a/uploads/images/img-d59610e1-2025-11-13_13-53-49.jpg b/uploads/images/img-d59610e1-2025-11-13_13-53-49.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-d59610e1-2025-11-13_13-53-49.jpg and /dev/null differ diff --git a/uploads/images/img-e102e31c-2025-11-25_09-19-36.jpg b/uploads/images/img-e102e31c-2025-11-25_09-19-36.jpg new file mode 100644 index 0000000..320a119 Binary files /dev/null and b/uploads/images/img-e102e31c-2025-11-25_09-19-36.jpg differ diff --git a/uploads/images/img-e37c76ce-2025-11-25_09-20-26.jpg b/uploads/images/img-e37c76ce-2025-11-25_09-20-26.jpg new file mode 100644 index 0000000..320a119 Binary files /dev/null and b/uploads/images/img-e37c76ce-2025-11-25_09-20-26.jpg differ diff --git a/uploads/images/img-f377189e-2025-11-13_13-53-49.jpg b/uploads/images/img-f377189e-2025-11-13_13-53-49.jpg deleted file mode 100644 index 88e8a50..0000000 Binary files a/uploads/images/img-f377189e-2025-11-13_13-53-49.jpg and /dev/null differ diff --git a/validate/brand.schema.js b/validate/brand.schema.js index b9209ad..3f5d99a 100644 --- a/validate/brand.schema.js +++ b/validate/brand.schema.js @@ -42,18 +42,6 @@ const insertBrandSchema = Joi.object({ ) .min(1) .required(), - sparepart: Joi.array() - .items( - Joi.object({ - sparepart_name: Joi.string().max(100).optional(), - brand_sparepart_description: Joi.string() - .max(255) - .optional(), - path_foto: Joi.string().optional().allow(""), - is_active: Joi.boolean().required(), - }) - ) - .optional(), }) ) .min(1) @@ -100,18 +88,6 @@ const updateBrandSchema = Joi.object({ ) .min(1) .required(), - sparepart: Joi.array() - .items( - Joi.object({ - sparepart_name: Joi.string().max(100).optional(), - brand_sparepart_description: Joi.string() - .max(255) - .optional(), - path_foto: Joi.string().optional().allow(""), - is_active: Joi.boolean().required(), - }) - ) - .optional(), }) ) .optional(), diff --git a/validate/sparepart.schema.js b/validate/sparepart.schema.js new file mode 100644 index 0000000..8c7c617 --- /dev/null +++ b/validate/sparepart.schema.js @@ -0,0 +1,32 @@ +const Joi = require("joi"); +// ======================== +// Sparepart Validation +// ======================== +const insertSparepartSchema = Joi.object({ + sparepart_name: Joi.string().max(255).required(), + sparepart_description: Joi.string().max(255).required(), + sparepart_model: Joi.string().max(255).required(), + sparepart_foto: Joi.string().max(255).optional().allow(""), + sparepart_item_type: Joi.string().max(255).required(), + sparepart_qty: Joi.number().integer().min(1), + sparepart_unit: Joi.string().max(255).required(), + sparepart_merk: Joi.string().max(255).required(), + sparepart_stok: Joi.string().max(255).required(), +}); + +// Update Validation +const updateSparepartSchema = Joi.object({ + sparepart_name: Joi.string().max(255).optional(), + sparepart_description: Joi.string().max(255).optional(), + sparepart_model: Joi.string().max(255).optional(), + sparepart_foto: Joi.string().max(255).optional().allow(''), + sparepart_item_type: Joi.string().max(255).optional(), + sparepart_qty: Joi.number().integer().min(1), + sparepart_unit: Joi.string().max(255).required(), + sparepart_merk: Joi.string().max(255).required(), + sparepart_stok: Joi.string().max(255).required(), +}); +module.exports = { + insertSparepartSchema, + updateSparepartSchema, +};