From 26f7420688a6be21c981f583801d1add4b7f9e01 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Fri, 24 Oct 2025 12:46:34 +0700 Subject: [PATCH 1/3] fix: plant sub section validate --- controllers/plant_sub_section.controller.js | 2 +- validate/plant_sub_section.schema.js | 65 +++++++++++++++++++++ validate/sub_section.schema.js | 35 ----------- 3 files changed, 66 insertions(+), 36 deletions(-) create mode 100644 validate/plant_sub_section.schema.js delete mode 100644 validate/sub_section.schema.js diff --git a/controllers/plant_sub_section.controller.js b/controllers/plant_sub_section.controller.js index e175307..919b3fa 100644 --- a/controllers/plant_sub_section.controller.js +++ b/controllers/plant_sub_section.controller.js @@ -1,6 +1,6 @@ const SubSectionService = require('../services/plant_sub_section.service'); const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); -const { insertSubSectionSchema, updateSubSectionSchema } = require('../validate/sub_section.schema'); +const { insertSubSectionSchema, updateSubSectionSchema } = require('../validate/plant_sub_section.schema'); class SubSectionController { // Get all sub sections diff --git a/validate/plant_sub_section.schema.js b/validate/plant_sub_section.schema.js new file mode 100644 index 0000000..8a78b65 --- /dev/null +++ b/validate/plant_sub_section.schema.js @@ -0,0 +1,65 @@ +const Joi = require("joi"); + +// ======================== +// Plant Sub Section Validation +// ======================== +const insertSubSectionSchema = Joi.object({ + plant_sub_section_name: Joi.string() + .max(200) + .required() + .messages({ + "string.base": "Sub section name must be a string", + "string.max": "Sub section name cannot exceed 200 characters", + "any.required": "Sub section name is required" + }), + plant_sub_section_description: Joi.string() + .max(200) + .allow('') + .optional() + .messages({ + "string.base": "Description must be a string", + "string.max": "Description cannot exceed 200 characters" + }), + table_name_value: Joi.string() + .max(255) + .allow('') + .optional() + .messages({ + "string.base": "Table name value must be a string", + "string.max": "Table name value cannot exceed 255 characters" + }), + is_active: Joi.boolean() + .default(true) + .optional() +}); + +const updateSubSectionSchema = Joi.object({ + plant_sub_section_name: Joi.string() + .max(200) + .messages({ + "string.base": "Sub section name must be a string", + "string.max": "Sub section name cannot exceed 200 characters", + }).optional(), + plant_sub_section_description: Joi.string() + .max(200) + .allow('') + .optional() + .messages({ + "string.base": "Description must be a string", + "string.max": "Description cannot exceed 200 characters" + }), + table_name_value: Joi.string() + .max(255) + .allow('') + .optional() + .messages({ + "string.base": "Table name value must be a string", + "string.max": "Table name value cannot exceed 255 characters" + }), + is_active: Joi.boolean().optional() +}).min(1); + +module.exports = { + insertSubSectionSchema, + updateSubSectionSchema +}; diff --git a/validate/sub_section.schema.js b/validate/sub_section.schema.js deleted file mode 100644 index 1eac3ea..0000000 --- a/validate/sub_section.schema.js +++ /dev/null @@ -1,35 +0,0 @@ -const Joi = require("joi"); - -// ======================== -// Plant Sub Section Validation -// ======================== -const insertSubSectionSchema = Joi.object({ - plant_sub_section_name: Joi.string() - .max(200) - .required() - .messages({ - "string.base": "Sub section name must be a string", - "string.max": "Sub section name cannot exceed 200 characters", - "any.required": "Sub section name is required" - }).required(), - is_active: Joi.boolean().required(), - value: Joi.string().max(100).optional() -}); - -const updateSubSectionSchema = Joi.object({ - plant_sub_section_namesub_section_name: Joi.string() - .max(200) - .messages({ - "string.base": "Sub section name must be a string", - "string.max": "Sub section name cannot exceed 200 characters", - }).optional(), - is_active: Joi.boolean().optional(), - value: Joi.string().max(100).optional() -}).min(1).messages({ - "object.min": "At least one field must be provided to update", -}); - -module.exports = { - insertSubSectionSchema, - updateSubSectionSchema -}; From 3b9f3474a6708224612b52806e403955ab705a81 Mon Sep 17 00:00:00 2001 From: Muhammad Afif Date: Fri, 24 Oct 2025 16:21:20 +0700 Subject: [PATCH 2/3] repair json body user_schedule and validasi user_id tidak boleh sama di satu shift_id --- db/user_schedule.db.js | 67 ++++++++++++++++++++++++++----- services/user_schedule.service.js | 33 +++++++++++++-- validate/user_schedule.schema.js | 2 +- 3 files changed, 86 insertions(+), 16 deletions(-) diff --git a/db/user_schedule.db.js b/db/user_schedule.db.js index 3305ff7..dc7b403 100644 --- a/db/user_schedule.db.js +++ b/db/user_schedule.db.js @@ -21,7 +21,7 @@ const getAllUserScheduleDb = async (searchParams = {}) => { { column: "a.user_id", param: searchParams.user_id, type: "int" }, { column: "a.user_schedule_id", param: searchParams.user_schedule_id, type: "int" }, { column: "a.schedule_id", param: searchParams.schedule_id, type: "int" }, - { column: "a.user_schedule_id", param: searchParams.user_schedule_id, type: "int" }, + { column: "a.shift_id", param: searchParams.shift_id, type: "int" }, ], queryParams ); @@ -30,12 +30,14 @@ const getAllUserScheduleDb = async (searchParams = {}) => { const queryText = ` SELECT COUNT(*) OVER() AS total_data, - a.*, - b.schedule_date, + a.shift_id, c.shift_name, c.start_time, c.end_time, - d.user_fullname + d.user_id, + d.user_fullname, + d.user_name, + d.user_phone FROM user_schedule a LEFT JOIN schedule b ON a.schedule_id = b.schedule_id LEFT JOIN m_shift c ON a.shift_id = c.shift_id @@ -43,19 +45,58 @@ const getAllUserScheduleDb = async (searchParams = {}) => { WHERE a.deleted_at IS NULL ${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""} ${whereOrConditions ? ` ${whereOrConditions}` : ""} - ORDER BY a.user_schedule_id ASC + ORDER BY c.shift_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; + const records = result.recordset || []; - return { data: result.recordset, total }; + const total = records.length > 0 ? parseInt(records[0].total_data, 10) : 0; + + const groupedData = Object.values( + records.reduce((acc, row) => { + if (!acc[row.shift_id]) { + acc[row.shift_id] = { + shift_id: row.shift_id, + shift_name: row.shift_name, + start_time: row.start_time, + end_time: row.end_time, + users: [], + }; + } + + acc[row.shift_id].users.push({ + user_id: row.user_id, + user_fullname: row.user_fullname, + user_name: row.user_name, + user_phone: row.user_phone, + }); + + return acc; + }, {}) + ); + + return { data: groupedData, total }; }; +const getUserScheduleById = async (userId, shiftId) => { + const queryText = ` + SELECT + a.user_schedule_id, + a.user_id, + a.shift_id + FROM user_schedule a + WHERE a.user_id = $1 + AND a.shift_id = $2 + AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [userId, shiftId]); + return result.recordset || []; +}; + + + const getUserScheduleByIdDb = async (id) => { const queryText = ` SELECT @@ -64,7 +105,9 @@ const getUserScheduleByIdDb = async (id) => { c.shift_name, c.start_time, c.end_time, - d.user_fullname + d.user_fullname, + d.user_name, + d.user_phone FROM user_schedule a LEFT JOIN schedule b ON a.schedule_id = b.schedule_id LEFT JOIN m_shift c ON a.shift_id = c.shift_id @@ -107,10 +150,12 @@ const deleteUserScheduleDb = async (id, deletedBy) => { return true; }; + module.exports = { getAllUserScheduleDb, getUserScheduleByIdDb, insertUserScheduleDb, updateUserScheduleDb, deleteUserScheduleDb, + getUserScheduleById, }; diff --git a/services/user_schedule.service.js b/services/user_schedule.service.js index 6c07048..8872ff0 100644 --- a/services/user_schedule.service.js +++ b/services/user_schedule.service.js @@ -1,6 +1,7 @@ const { getAllUserScheduleDb, getUserScheduleByIdDb, + getUserScheduleById, insertUserScheduleDb, updateUserScheduleDb, deleteUserScheduleDb @@ -35,20 +36,30 @@ const { } } - // Create device static async createUserSchedules(data) { try { if (!data || typeof data !== 'object') data = {}; - const result = await insertUserScheduleDb(data); + const exist = await getUserScheduleById( + data.user_id, + data.shift_id + ); + if (exist.length > 0) { + throw new ErrorHandler( + 400, + `User dengan ID ${data.user_id} sudah terdaftar pada shift ${data.shift_id}` + ); + } + + const result = await insertUserScheduleDb(data); return result; } catch (error) { throw new ErrorHandler(error.statusCode, error.message); } } - // Update device + // Update user schedule static async updateUserSchedules(id, data) { try { if (!data || typeof data !== 'object') data = {}; @@ -59,8 +70,22 @@ const { throw new ErrorHandler(404, 'UserSchedules not found'); } - const result = await updateUserScheduleDb(id, data); + // 🧩 VALIDASI SAAT UPDATE + if (data.user_id && data.shift_id) { + const exist = await getUserScheduleById( + data.user_id, + data.shift_id + ); + if (exist.length > 0 && exist[0].id !== Number(id)) { + throw new ErrorHandler( + 400, + `User dengan ID ${data.user_id} sudah terdaftar pada shift ${data.shift_id}` + ); + } + } + + const result = await updateUserScheduleDb(id, data); return result; } catch (error) { throw new ErrorHandler(error.statusCode, error.message); diff --git a/validate/user_schedule.schema.js b/validate/user_schedule.schema.js index 7e8b6af..3d9373a 100644 --- a/validate/user_schedule.schema.js +++ b/validate/user_schedule.schema.js @@ -5,7 +5,7 @@ const Joi = require("joi"); // ======================== const insertUserScheduleSchema = Joi.object({ user_id: Joi.number().integer().required(), - schedule_id: Joi.number().integer().required(), + schedule_id: Joi.number().integer().optional(), shift_id: Joi.number().required(), }); From 3633590a8f61c64971b4cd3890686f5c321832e8 Mon Sep 17 00:00:00 2001 From: Fachba Date: Sat, 25 Oct 2025 15:35:15 +0700 Subject: [PATCH 3/3] fixing validate unit and tag --- validate/tags.schema.js | 2 +- validate/unit.schema.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/validate/tags.schema.js b/validate/tags.schema.js index e3ccf50..ef6a86d 100644 --- a/validate/tags.schema.js +++ b/validate/tags.schema.js @@ -22,7 +22,7 @@ const insertTagsSchema = Joi.object({ }); const updateTagsSchema = Joi.object({ - device_id: Joi.number().required(), + device_id: Joi.number().optional(), tag_name: Joi.string().max(200), tag_number: Joi.number(), is_active: Joi.boolean(), diff --git a/validate/unit.schema.js b/validate/unit.schema.js index 85cf4d9..d22c816 100644 --- a/validate/unit.schema.js +++ b/validate/unit.schema.js @@ -5,16 +5,14 @@ const Joi = require("joi"); // ======================== const insertUnitSchema = Joi.object({ unit_name: Joi.string().max(100).required(), - tag_id: Joi.number().integer().optional(), is_active: Joi.boolean().required(), - unit_description: Joi. string().max(255).optional() + unit_description: Joi.string().max(255).allow('') }); const updateUnitSchema = Joi.object({ unit_name: Joi.string().max(100).optional(), - tag_id: Joi.number().integer().optional(), is_active: Joi.boolean().optional(), - unit_description: Joi. string().max(255).optional() + unit_description: Joi.string().max(255).allow('') }).min(1); module.exports = {