@@ -31,11 +31,11 @@ const poolPromise = new sql.ConnectionPool(config)
|
|||||||
async function checkConnection() {
|
async function checkConnection() {
|
||||||
try {
|
try {
|
||||||
const pool = await poolPromise;
|
const pool = await poolPromise;
|
||||||
await pool.request().query('SELECT 1 AS isConnected');
|
await pool.request().query("SELECT 1 AS isConnected");
|
||||||
console.log('🔍 SQL Server terkoneksi dengan baik');
|
console.log("🔍 SQL Server terkoneksi dengan baik");
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('⚠️ Gagal cek koneksi SQL Server:', error);
|
console.error("⚠️ Gagal cek koneksi SQL Server:", error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,13 +58,16 @@ async function query(text, params = []) {
|
|||||||
return request.query(sqlText);
|
return request.query(sqlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validasi tanggal
|
||||||
|
*/
|
||||||
function isValidDate(dateStr) {
|
function isValidDate(dateStr) {
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
return !isNaN(d.getTime()); // true kalau valid
|
return !isNaN(d.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build filter query
|
* Build filter query (AND)
|
||||||
*/
|
*/
|
||||||
function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
||||||
let whereConditions = [];
|
let whereConditions = [];
|
||||||
@@ -76,7 +79,9 @@ function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
|||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "string":
|
case "string":
|
||||||
queryParams.push(`%${f.param}%`);
|
queryParams.push(`%${f.param}%`);
|
||||||
whereConditions.push(`${f.column} LIKE $${queryParams.length} COLLATE SQL_Latin1_General_CP1_CI_AS`);
|
whereConditions.push(
|
||||||
|
`${f.column} LIKE $${queryParams.length} COLLATE SQL_Latin1_General_CP1_CI_AS`
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
@@ -89,10 +94,9 @@ function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
|||||||
whereConditions.push(`${f.column} = $${queryParams.length}`);
|
whereConditions.push(`${f.column} = $${queryParams.length}`);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'between':
|
case "between":
|
||||||
if (Array.isArray(f.param) && f.param.length === 2) {
|
if (Array.isArray(f.param) && f.param.length === 2) {
|
||||||
const from = f.param[0];
|
const [from, to] = f.param;
|
||||||
const to = f.param[1];
|
|
||||||
if (isValidDate(from) && isValidDate(to)) {
|
if (isValidDate(from) && isValidDate(to)) {
|
||||||
queryParams.push(from);
|
queryParams.push(from);
|
||||||
queryParams.push(to);
|
queryParams.push(to);
|
||||||
@@ -112,7 +116,7 @@ function buildFilterQuery(filterQuery = [], fixedParams = []) {
|
|||||||
* Build OR ILIKE (SQL Server pakai LIKE + COLLATE)
|
* Build OR ILIKE (SQL Server pakai LIKE + COLLATE)
|
||||||
*/
|
*/
|
||||||
function buildStringOrIlike(columnParam, criteria, fixedParams = []) {
|
function buildStringOrIlike(columnParam, criteria, fixedParams = []) {
|
||||||
if (!criteria) return { whereClause: "", whereParam: fixedParams };
|
if (!criteria) return { whereOrConditions: "", whereParamOr: fixedParams };
|
||||||
|
|
||||||
let orStringConditions = [];
|
let orStringConditions = [];
|
||||||
let queryParams = [...fixedParams];
|
let queryParams = [...fixedParams];
|
||||||
@@ -120,7 +124,9 @@ function buildStringOrIlike(columnParam, criteria, fixedParams = []) {
|
|||||||
columnParam.forEach((column) => {
|
columnParam.forEach((column) => {
|
||||||
if (!column) return;
|
if (!column) return;
|
||||||
queryParams.push(`%${criteria}%`);
|
queryParams.push(`%${criteria}%`);
|
||||||
orStringConditions.push(`${column} LIKE $${queryParams.length} COLLATE SQL_Latin1_General_CP1_CI_AS`);
|
orStringConditions.push(
|
||||||
|
`${column} LIKE $${queryParams.length} COLLATE SQL_Latin1_General_CP1_CI_AS`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const whereClause = orStringConditions.length
|
const whereClause = orStringConditions.length
|
||||||
@@ -130,12 +136,60 @@ function buildStringOrIlike(columnParam, criteria, fixedParams = []) {
|
|||||||
return { whereOrConditions: whereClause, whereParamOr: queryParams };
|
return { whereOrConditions: whereClause, whereParamOr: queryParams };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Date Filter (harian / mingguan / bulanan)
|
||||||
|
*/
|
||||||
|
function buildDateFilter(column, type, dateValue, fixedParams = []) {
|
||||||
|
let whereCondition = "";
|
||||||
|
let queryParams = [...fixedParams];
|
||||||
|
|
||||||
|
if (!dateValue && type !== "monthly") {
|
||||||
|
return { whereDateCondition: "", whereDateParams: queryParams };
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "daily": {
|
||||||
|
queryParams.push(dateValue);
|
||||||
|
whereCondition = `CAST(${column} AS DATE) = $${queryParams.length}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "weekly": {
|
||||||
|
const startDate = new Date(dateValue);
|
||||||
|
if (!isNaN(startDate.getTime())) {
|
||||||
|
const endDate = new Date(startDate);
|
||||||
|
endDate.setDate(startDate.getDate() + 6);
|
||||||
|
|
||||||
|
queryParams.push(startDate.toISOString().split("T")[0]);
|
||||||
|
queryParams.push(endDate.toISOString().split("T")[0]);
|
||||||
|
|
||||||
|
whereCondition = `CAST(${column} AS DATE) BETWEEN $${queryParams.length - 1} AND $${queryParams.length}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "monthly": {
|
||||||
|
const [year, month] = dateValue.split("-");
|
||||||
|
if (year && month) {
|
||||||
|
queryParams.push(parseInt(year), parseInt(month));
|
||||||
|
whereCondition = `YEAR(${column}) = $${queryParams.length - 1} AND MONTH(${column}) = $${queryParams.length}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
whereCondition = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return { whereDateCondition: whereCondition, whereDateParams: queryParams };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build dynamic UPDATE
|
* Build dynamic UPDATE
|
||||||
*/
|
*/
|
||||||
function buildDynamicUpdate(table, data, where) {
|
function buildDynamicUpdate(table, data, where) {
|
||||||
|
data.updated_by = data.userId;
|
||||||
data.updated_by = data.userId
|
|
||||||
delete data.userId;
|
delete data.userId;
|
||||||
|
|
||||||
const setParts = [];
|
const setParts = [];
|
||||||
@@ -153,7 +207,6 @@ function buildDynamicUpdate(table, data, where) {
|
|||||||
throw new Error("Tidak ada kolom untuk diupdate");
|
throw new Error("Tidak ada kolom untuk diupdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
// updated_at otomatis pakai CURRENT_TIMESTAMP
|
|
||||||
setParts.push(`updated_at = CURRENT_TIMESTAMP`);
|
setParts.push(`updated_at = CURRENT_TIMESTAMP`);
|
||||||
|
|
||||||
const whereParts = [];
|
const whereParts = [];
|
||||||
@@ -175,9 +228,8 @@ function buildDynamicUpdate(table, data, where) {
|
|||||||
* Build dynamic INSERT
|
* Build dynamic INSERT
|
||||||
*/
|
*/
|
||||||
function buildDynamicInsert(table, data) {
|
function buildDynamicInsert(table, data) {
|
||||||
|
data.created_by = data.userId;
|
||||||
data.created_by = data.userId
|
data.updated_by = data.userId;
|
||||||
data.updated_by = data.userId
|
|
||||||
delete data.userId;
|
delete data.userId;
|
||||||
|
|
||||||
const columns = [];
|
const columns = [];
|
||||||
@@ -197,7 +249,6 @@ function buildDynamicInsert(table, data) {
|
|||||||
throw new Error("Tidak ada kolom untuk diinsert");
|
throw new Error("Tidak ada kolom untuk diinsert");
|
||||||
}
|
}
|
||||||
|
|
||||||
// created_at & updated_at otomatis
|
|
||||||
columns.push("created_at", "updated_at");
|
columns.push("created_at", "updated_at");
|
||||||
placeholders.push("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP");
|
placeholders.push("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
@@ -238,6 +289,7 @@ module.exports = {
|
|||||||
checkConnection,
|
checkConnection,
|
||||||
query,
|
query,
|
||||||
buildFilterQuery,
|
buildFilterQuery,
|
||||||
|
buildDateFilter,
|
||||||
buildStringOrIlike,
|
buildStringOrIlike,
|
||||||
buildDynamicInsert,
|
buildDynamicInsert,
|
||||||
buildDynamicUpdate,
|
buildDynamicUpdate,
|
||||||
|
|||||||
71
controllers/user_schedule.controller.js
Normal file
71
controllers/user_schedule.controller.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
const UserScheduleService = require('../services/user_schedule.service');
|
||||||
|
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||||
|
const { insertUserScheduleSchema, updateUserScheduleSchema } = require('../validate/user_schedule.schema');
|
||||||
|
|
||||||
|
class UserScheduleController {
|
||||||
|
// Get all User Schedule
|
||||||
|
static async getAll(req, res) {
|
||||||
|
const queryParams = req.query;
|
||||||
|
|
||||||
|
const results = await UserScheduleService.getAllUserScheduleDb(queryParams);
|
||||||
|
const response = await setResponsePaging(queryParams, results, 'User Schedule found')
|
||||||
|
|
||||||
|
res.status(response.statusCode).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get User Schedule by ID
|
||||||
|
static async getById(req, res) {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const results = await UserScheduleService.getUserScheduleByID(id);
|
||||||
|
const response = await setResponse(results, 'User Schedule found')
|
||||||
|
|
||||||
|
res.status(response.statusCode).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create User Schedule
|
||||||
|
static async create(req, res) {
|
||||||
|
const { error, value } = await checkValidate(insertUserScheduleSchema, req)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
value.userId = req.user.user_id
|
||||||
|
|
||||||
|
const results = await UserScheduleService.createUserSchedules(value);
|
||||||
|
const response = await setResponse(results, 'User Schedule created successfully')
|
||||||
|
|
||||||
|
return res.status(response.statusCode).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update User Schedule
|
||||||
|
static async update(req, res) {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const { error, value } = checkValidate(updateUserScheduleSchema, req)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
value.userId = req.user.user_id
|
||||||
|
|
||||||
|
const results = await UserScheduleService.updateUserSchedules(id, value);
|
||||||
|
const response = await setResponse(results, 'User Schedule updated successfully')
|
||||||
|
|
||||||
|
res.status(response.statusCode).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft delete User Schedule
|
||||||
|
static async delete(req, res) {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const results = await UserScheduleService.deleteUserSchedules(id, req.user.user_id);
|
||||||
|
const response = await setResponse(results, 'User Schedule deleted successfully')
|
||||||
|
|
||||||
|
res.status(response.statusCode).json(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UserScheduleController;
|
||||||
@@ -34,7 +34,7 @@ const getAllBrandsDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? whereOrConditions : ""}
|
${whereOrConditions ? whereOrConditions : ""}
|
||||||
ORDER BY b.brand_id ASC
|
ORDER BY b.brand_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const getAllDevicesDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||||
${whereOrConditions ? whereOrConditions : ''}
|
${whereOrConditions ? whereOrConditions : ''}
|
||||||
ORDER BY a.device_id ASC
|
ORDER BY a.device_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const getAllRolesDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||||
ORDER BY a.role_id ASC
|
ORDER BY a.role_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
const pool = require("../config");
|
const pool = require("../config");
|
||||||
const { formattedDate } = require("../utils/date");
|
const { formattedDate } = require("../utils/date");
|
||||||
|
|
||||||
// Get all schedules
|
const normalizeClause = (clause) => {
|
||||||
|
if (!clause) return "";
|
||||||
|
return clause.replace(/^\s*(?:AND|WHERE)\s*/i, "").trim();
|
||||||
|
};
|
||||||
|
|
||||||
const getAllScheduleDb = async (searchParams = {}) => {
|
const getAllScheduleDb = async (searchParams = {}) => {
|
||||||
let queryParams = [];
|
let queryParams = [];
|
||||||
|
|
||||||
@@ -18,11 +22,51 @@ const getAllScheduleDb = async (searchParams = {}) => {
|
|||||||
if (whereParamOr) queryParams = whereParamOr;
|
if (whereParamOr) queryParams = whereParamOr;
|
||||||
|
|
||||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||||
[{ column: "a.schedule_date", param: searchParams.name, type: "date" }],
|
[
|
||||||
|
{
|
||||||
|
column: "a.schedule_date",
|
||||||
|
param: searchParams.name,
|
||||||
|
type: "date",
|
||||||
|
},
|
||||||
|
],
|
||||||
queryParams
|
queryParams
|
||||||
);
|
);
|
||||||
if (whereParamAnd) queryParams = whereParamAnd;
|
if (whereParamAnd) queryParams = whereParamAnd;
|
||||||
|
|
||||||
|
const { whereDateCondition, whereDateParams } = pool.buildDateFilter(
|
||||||
|
"a.schedule_date",
|
||||||
|
searchParams.dateType,
|
||||||
|
searchParams.dateValue,
|
||||||
|
queryParams
|
||||||
|
);
|
||||||
|
if (whereDateParams) queryParams = whereDateParams;
|
||||||
|
|
||||||
|
const whereParts = [];
|
||||||
|
|
||||||
|
whereParts.push("a.deleted_at IS NULL");
|
||||||
|
|
||||||
|
if (Array.isArray(whereConditions) && whereConditions.length > 0) {
|
||||||
|
const joined = whereConditions.join(" AND ");
|
||||||
|
const norm = normalizeClause(joined);
|
||||||
|
if (norm) whereParts.push(norm);
|
||||||
|
} else if (typeof whereConditions === "string" && whereConditions.trim()) {
|
||||||
|
const norm = normalizeClause(whereConditions);
|
||||||
|
if (norm) whereParts.push(norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whereOrConditions && String(whereOrConditions).trim()) {
|
||||||
|
const norm = normalizeClause(whereOrConditions);
|
||||||
|
if (norm) whereParts.push(norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whereDateCondition && String(whereDateCondition).trim()) {
|
||||||
|
const norm = normalizeClause(whereDateCondition);
|
||||||
|
if (norm) whereParts.push(norm);
|
||||||
|
}
|
||||||
|
|
||||||
|
const whereClause =
|
||||||
|
whereParts.length > 0 ? `WHERE ${whereParts.join(" AND ")}` : "";
|
||||||
|
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(*) OVER() AS total_data,
|
COUNT(*) OVER() AS total_data,
|
||||||
@@ -32,14 +76,13 @@ const getAllScheduleDb = async (searchParams = {}) => {
|
|||||||
b.end_time
|
b.end_time
|
||||||
FROM schedule a
|
FROM schedule a
|
||||||
LEFT JOIN m_shift b ON a.shift_id = b.shift_id
|
LEFT JOIN m_shift b ON a.shift_id = b.shift_id
|
||||||
WHERE a.deleted_at IS NULL
|
${whereClause}
|
||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
|
||||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
|
||||||
ORDER BY a.schedule_id ASC
|
ORDER BY a.schedule_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|
||||||
const total =
|
const total =
|
||||||
result?.recordset?.length > 0
|
result?.recordset?.length > 0
|
||||||
? parseInt(result.recordset[0].total_data, 10)
|
? parseInt(result.recordset[0].total_data, 10)
|
||||||
@@ -48,6 +91,7 @@ const getAllScheduleDb = async (searchParams = {}) => {
|
|||||||
return { data: result.recordset, total };
|
return { data: result.recordset, total };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get by ID
|
||||||
const getScheduleByIdDb = async (id) => {
|
const getScheduleByIdDb = async (id) => {
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -63,8 +107,9 @@ const getScheduleByIdDb = async (id) => {
|
|||||||
return result.recordset?.[0] || null;
|
return result.recordset?.[0] || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Insert (bisa multi hari)
|
||||||
const insertScheduleDb = async (store) => {
|
const insertScheduleDb = async (store) => {
|
||||||
const nextDays = Number(store.next_day ?? 0); // default 0 kalau tidak diisi
|
const nextDays = Number(store.next_day ?? 0);
|
||||||
const insertedRecords = [];
|
const insertedRecords = [];
|
||||||
|
|
||||||
for (let i = 0; i <= nextDays; i++) {
|
for (let i = 0; i <= nextDays; i++) {
|
||||||
@@ -72,11 +117,7 @@ const insertScheduleDb = async (store) => {
|
|||||||
nextDate.setDate(nextDate.getDate() + i);
|
nextDate.setDate(nextDate.getDate() + i);
|
||||||
|
|
||||||
const formatted = formattedDate(nextDate);
|
const formatted = formattedDate(nextDate);
|
||||||
|
const newStore = { ...store, schedule_date: formatted };
|
||||||
const newStore = {
|
|
||||||
...store,
|
|
||||||
schedule_date: formatted,
|
|
||||||
};
|
|
||||||
delete newStore.next_day;
|
delete newStore.next_day;
|
||||||
|
|
||||||
const { query: queryText, values } = pool.buildDynamicInsert("schedule", newStore);
|
const { query: queryText, values } = pool.buildDynamicInsert("schedule", newStore);
|
||||||
@@ -92,6 +133,7 @@ const insertScheduleDb = async (store) => {
|
|||||||
return insertedRecords;
|
return insertedRecords;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update
|
||||||
const updateScheduleDb = async (id, data) => {
|
const updateScheduleDb = async (id, data) => {
|
||||||
const store = { ...data };
|
const store = { ...data };
|
||||||
const whereData = { schedule_id: id };
|
const whereData = { schedule_id: id };
|
||||||
@@ -106,7 +148,7 @@ const updateScheduleDb = async (id, data) => {
|
|||||||
return getScheduleByIdDb(id);
|
return getScheduleByIdDb(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Soft delete schedule
|
// Soft delete
|
||||||
const deleteScheduleDb = async (id, deletedBy) => {
|
const deleteScheduleDb = async (id, deletedBy) => {
|
||||||
const queryText = `
|
const queryText = `
|
||||||
UPDATE schedule
|
UPDATE schedule
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const getAllShiftDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||||
ORDER BY a.shift_id ASC
|
ORDER BY a.shift_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const getAllStatusDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||||
${whereOrConditions ? whereOrConditions : ''}
|
${whereOrConditions ? whereOrConditions : ''}
|
||||||
ORDER BY a.status_id ASC
|
ORDER BY a.status_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const getAllSubSectionsDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? whereOrConditions : ""}
|
${whereOrConditions ? whereOrConditions : ""}
|
||||||
ORDER BY a.sub_section_id ASC
|
ORDER BY a.sub_section_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const getAllTagsDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||||
ORDER BY a.tag_id ASC
|
ORDER BY a.tag_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const getAllUnitsDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? `AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
${whereOrConditions ? whereOrConditions : ""}
|
${whereOrConditions ? whereOrConditions : ""}
|
||||||
ORDER BY a.unit_id ASC
|
ORDER BY a.unit_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const getAllUsersDb = async (searchParams = {}) => {
|
|||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(' AND ')}` : ''}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(' AND ')}` : ''}
|
||||||
${whereOrConditions ? whereOrConditions : ''}
|
${whereOrConditions ? whereOrConditions : ''}
|
||||||
ORDER BY u.user_id ASC
|
ORDER BY u.user_id ASC
|
||||||
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ''};
|
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|||||||
116
db/user_schedule.db.js
Normal file
116
db/user_schedule.db.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
const pool = require("../config");
|
||||||
|
|
||||||
|
const getAllUserScheduleDb = async (searchParams = {}) => {
|
||||||
|
let queryParams = [];
|
||||||
|
|
||||||
|
// Handle pagination
|
||||||
|
if (searchParams.limit) {
|
||||||
|
const page = Number(searchParams.page ?? 1) - 1;
|
||||||
|
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||||
|
["a.user_id", "a.user_schedule_id", "a.schedule_id", "a.user_schedule_id"],
|
||||||
|
searchParams.criteria,
|
||||||
|
queryParams
|
||||||
|
);
|
||||||
|
if (whereParamOr) queryParams = whereParamOr;
|
||||||
|
|
||||||
|
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||||
|
[
|
||||||
|
{ 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" },
|
||||||
|
],
|
||||||
|
queryParams
|
||||||
|
);
|
||||||
|
if (whereParamAnd) queryParams = whereParamAnd;
|
||||||
|
|
||||||
|
const queryText = `
|
||||||
|
SELECT
|
||||||
|
COUNT(*) OVER() AS total_data,
|
||||||
|
a.*,
|
||||||
|
b.schedule_date,
|
||||||
|
c.shift_name,
|
||||||
|
c.start_time,
|
||||||
|
c.end_time,
|
||||||
|
d.user_fullname
|
||||||
|
FROM user_schedule a
|
||||||
|
LEFT JOIN schedule b ON a.user_schedule_id = b.schedule_id
|
||||||
|
LEFT JOIN m_shift c ON a.user_schedule_id = c.shift_id
|
||||||
|
LEFT JOIN m_users d ON a.user_schedule_id = d.user_id
|
||||||
|
WHERE a.deleted_at IS NULL
|
||||||
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
|
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||||
|
ORDER BY a.user_schedule_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 getUserScheduleByIdDb = async (id) => {
|
||||||
|
const queryText = `
|
||||||
|
SELECT
|
||||||
|
a.*,
|
||||||
|
b.schedule_date,
|
||||||
|
c.shift_name,
|
||||||
|
c.start_time,
|
||||||
|
c.end_time,
|
||||||
|
d.user_fullname
|
||||||
|
FROM user_schedule a
|
||||||
|
LEFT JOIN schedule b ON a.user_schedule_id = b.schedule_id
|
||||||
|
LEFT JOIN m_shift c ON a.user_schedule_id = c.shift_id
|
||||||
|
LEFT JOIN m_users d ON a.user_schedule_id = d.user_id
|
||||||
|
WHERE a.user_schedule_id = $1 AND a.deleted_at IS NULL
|
||||||
|
`;
|
||||||
|
const result = await pool.query(queryText, [id]);
|
||||||
|
return result.recordset?.[0] || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const insertUserScheduleDb = async (store) => {
|
||||||
|
const { query: queryText, values } = pool.buildDynamicInsert("user_schedule", store);
|
||||||
|
const result = await pool.query(queryText, values);
|
||||||
|
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||||
|
|
||||||
|
return insertedId ? await getUserScheduleByIdDb(insertedId) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateUserScheduleDb = async (id, data) => {
|
||||||
|
const store = { ...data };
|
||||||
|
const whereData = { user_schedule_id: id };
|
||||||
|
|
||||||
|
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||||
|
"user_schedule",
|
||||||
|
store,
|
||||||
|
whereData
|
||||||
|
);
|
||||||
|
|
||||||
|
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||||
|
return getUserScheduleByIdDb(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteUserScheduleDb = async (id, deletedBy) => {
|
||||||
|
const queryText = `
|
||||||
|
UPDATE user_schedule
|
||||||
|
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||||
|
WHERE user_schedule_id = $2 AND deleted_at IS NULL
|
||||||
|
`;
|
||||||
|
await pool.query(queryText, [deletedBy, id]);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAllUserScheduleDb,
|
||||||
|
getUserScheduleByIdDb,
|
||||||
|
insertUserScheduleDb,
|
||||||
|
updateUserScheduleDb,
|
||||||
|
deleteUserScheduleDb,
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ const shift = require("./shift.route");
|
|||||||
const schedule = require("./schedule.route");
|
const schedule = require("./schedule.route");
|
||||||
const status = require("./status.route");
|
const status = require("./status.route");
|
||||||
const unit = require("./unit.route")
|
const unit = require("./unit.route")
|
||||||
|
const UserSchedule = require("./user_schedule.route")
|
||||||
|
|
||||||
router.use("/auth", auth);
|
router.use("/auth", auth);
|
||||||
router.use("/user", users);
|
router.use("/user", users);
|
||||||
@@ -20,6 +21,7 @@ router.use("/shift", shift);
|
|||||||
router.use("/schedule", schedule);
|
router.use("/schedule", schedule);
|
||||||
router.use("/status", status);
|
router.use("/status", status);
|
||||||
router.use("/unit", unit);
|
router.use("/unit", unit);
|
||||||
|
router.use("/user-schedule", UserSchedule)
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
17
routes/user_schedule.route.js
Normal file
17
routes/user_schedule.route.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const UserSchedulesController = require('../controllers/user_schedule.controller');
|
||||||
|
const verifyToken = require("../middleware/verifyToken")
|
||||||
|
const verifyAccess = require("../middleware/verifyAccess")
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.route("/")
|
||||||
|
.get(verifyToken.verifyAccessToken, UserSchedulesController.getAll)
|
||||||
|
.post(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.create);
|
||||||
|
|
||||||
|
router.route("/:id")
|
||||||
|
.get(verifyToken.verifyAccessToken, UserSchedulesController.getById)
|
||||||
|
.put(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.update)
|
||||||
|
.delete(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.delete);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
89
services/user_schedule.service.js
Normal file
89
services/user_schedule.service.js
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
const {
|
||||||
|
getAllUserScheduleDb,
|
||||||
|
getUserScheduleByIdDb,
|
||||||
|
insertUserScheduleDb,
|
||||||
|
updateUserScheduleDb,
|
||||||
|
deleteUserScheduleDb
|
||||||
|
} = require('../db/user_schedule.db');
|
||||||
|
const { ErrorHandler } = require('../helpers/error');
|
||||||
|
|
||||||
|
class UserScheduleService {
|
||||||
|
// Get all devices
|
||||||
|
static async getAllUserScheduleDb(param) {
|
||||||
|
try {
|
||||||
|
const results = await getAllUserScheduleDb(param);
|
||||||
|
|
||||||
|
results.data.map(element => {
|
||||||
|
});
|
||||||
|
|
||||||
|
return results
|
||||||
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device by ID
|
||||||
|
static async getUserScheduleByID(id) {
|
||||||
|
try {
|
||||||
|
const result = await getUserScheduleByIdDb(id);
|
||||||
|
|
||||||
|
if (result.length < 1) throw new ErrorHandler(404, 'User Schedule not found');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create device
|
||||||
|
static async createUserSchedules(data) {
|
||||||
|
try {
|
||||||
|
if (!data || typeof data !== 'object') data = {};
|
||||||
|
|
||||||
|
const result = await insertUserScheduleDb(data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device
|
||||||
|
static async updateUserSchedules(id, data) {
|
||||||
|
try {
|
||||||
|
if (!data || typeof data !== 'object') data = {};
|
||||||
|
|
||||||
|
const dataExist = await getUserScheduleByIdDb(id);
|
||||||
|
|
||||||
|
if (dataExist.length < 1) {
|
||||||
|
throw new ErrorHandler(404, 'UserSchedules not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateUserScheduleDb(id, data);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft delete device
|
||||||
|
static async deleteUserSchedules(id, userId) {
|
||||||
|
try {
|
||||||
|
const dataExist = await getUserScheduleByIdDb(id);
|
||||||
|
|
||||||
|
if (dataExist.length < 1) {
|
||||||
|
throw new ErrorHandler(404, 'UserSchedules not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteUserScheduleDb(id, userId);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UserScheduleService;
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ const crypto = require('crypto');
|
|||||||
|
|
||||||
const tokenSettings = {
|
const tokenSettings = {
|
||||||
access: {
|
access: {
|
||||||
expiresIn: '15m',
|
expiresIn: '12h',
|
||||||
type: 'access',
|
type: 'access',
|
||||||
secret: process.env.SECRET
|
secret: process.env.SECRET
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const insertSubSectionSchema = Joi.object({
|
|||||||
"any.required": "Sub section name is required"
|
"any.required": "Sub section name is required"
|
||||||
}).required(),
|
}).required(),
|
||||||
is_active: Joi.boolean().required(),
|
is_active: Joi.boolean().required(),
|
||||||
|
value: Joi.string().max(100).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateSubSectionSchema = Joi.object({
|
const updateSubSectionSchema = Joi.object({
|
||||||
@@ -23,6 +24,7 @@ const updateSubSectionSchema = Joi.object({
|
|||||||
"string.max": "Sub section name cannot exceed 200 characters",
|
"string.max": "Sub section name cannot exceed 200 characters",
|
||||||
}).optional(),
|
}).optional(),
|
||||||
is_active: Joi.boolean().optional(),
|
is_active: Joi.boolean().optional(),
|
||||||
|
value: Joi.string().max(100).optional()
|
||||||
}).min(1).messages({
|
}).min(1).messages({
|
||||||
"object.min": "At least one field must be provided to update",
|
"object.min": "At least one field must be provided to update",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ const insertTagsSchema = Joi.object({
|
|||||||
data_type: Joi.string().max(50).required(),
|
data_type: Joi.string().max(50).required(),
|
||||||
unit: Joi.string().max(50).required(),
|
unit: Joi.string().max(50).required(),
|
||||||
sub_section_id: Joi.number().optional(),
|
sub_section_id: Joi.number().optional(),
|
||||||
is_alarm: Joi.boolean().required()
|
is_alarm: Joi.boolean().required(),
|
||||||
|
is_report: Joi.boolean().required(),
|
||||||
|
is_history: Joi.boolean().required(),
|
||||||
|
lim_low_crash: Joi.number().optional(),
|
||||||
|
lim_low: Joi.number().optional(),
|
||||||
|
lim_high: Joi.number().optional(),
|
||||||
|
lim_high_crash: Joi.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateTagsSchema = Joi.object({
|
const updateTagsSchema = Joi.object({
|
||||||
@@ -24,6 +30,12 @@ const updateTagsSchema = Joi.object({
|
|||||||
unit: Joi.string().max(50),
|
unit: Joi.string().max(50),
|
||||||
is_alarm: Joi.boolean().optional(),
|
is_alarm: Joi.boolean().optional(),
|
||||||
sub_section_id: Joi.number().optional(),
|
sub_section_id: Joi.number().optional(),
|
||||||
|
is_report: Joi.boolean().optional(),
|
||||||
|
is_history: Joi.boolean().optional(),
|
||||||
|
lim_low_crash: Joi.number().optional(),
|
||||||
|
lim_low: Joi.number().optional(),
|
||||||
|
lim_high: Joi.number().optional(),
|
||||||
|
lim_high_crash: Joi.number().optional(),
|
||||||
}).min(1);
|
}).min(1);
|
||||||
|
|
||||||
// ✅ Export dengan CommonJS
|
// ✅ Export dengan CommonJS
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ const insertUnitSchema = Joi.object({
|
|||||||
unit_name: Joi.string().max(100).required(),
|
unit_name: Joi.string().max(100).required(),
|
||||||
tag_id: Joi.number().integer().optional(),
|
tag_id: Joi.number().integer().optional(),
|
||||||
is_active: Joi.boolean().required(),
|
is_active: Joi.boolean().required(),
|
||||||
|
description: Joi. string().max(100).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateUnitSchema = Joi.object({
|
const updateUnitSchema = Joi.object({
|
||||||
unit_name: Joi.string().max(100).optional(),
|
unit_name: Joi.string().max(100).optional(),
|
||||||
tag_id: Joi.number().integer().optional(),
|
tag_id: Joi.number().integer().optional(),
|
||||||
is_active: Joi.boolean().optional()
|
is_active: Joi.boolean().optional(),
|
||||||
|
description: Joi. string().max(100).optional()
|
||||||
}).min(1);
|
}).min(1);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
21
validate/user_schedule.schema.js
Normal file
21
validate/user_schedule.schema.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
const Joi = require("joi");
|
||||||
|
|
||||||
|
// ========================
|
||||||
|
// User Schedule Validation
|
||||||
|
// ========================
|
||||||
|
const insertUserScheduleSchema = Joi.object({
|
||||||
|
user_id: Joi.number().integer().required(),
|
||||||
|
schedule_id: Joi.number().integer().required(),
|
||||||
|
shift_id: Joi.number().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateUserScheduleSchema = Joi.object({
|
||||||
|
user_id: Joi.number().integer().optional(),
|
||||||
|
schedule_id: Joi.number().integer().optional(),
|
||||||
|
shift_id: Joi.number().optional()
|
||||||
|
}).min(1);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
insertUserScheduleSchema,
|
||||||
|
updateUserScheduleSchema
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user