Merge pull request 'wisdom' (#19) from wisdom into main
Reviewed-on: #19
This commit is contained in:
@@ -23,6 +23,11 @@ SECRET=secret
|
||||
# JWT refresh secret
|
||||
REFRESH_SECRET=refreshsecret
|
||||
|
||||
# IMAGEKIT
|
||||
IMAGEKIT_URL_ENDPOINT=https://ik.imagekit.io/j0hxk7x3p
|
||||
IMAGEKIT_PUBLIC_KEY=public_iMPQFBnXmdQy73TTB9w4SMQO4Jk=
|
||||
IMAGEKIT_PRIVATE_KEY=private_vhO/jXHnEoaVYptOHIuZDPMbxIA=
|
||||
|
||||
# mail server settings
|
||||
# SMTP_FROM=youremail
|
||||
# SMTP_USER=youremail
|
||||
|
||||
9
app.js
9
app.js
@@ -8,7 +8,8 @@ const helmet = require("helmet");
|
||||
const compression = require("compression");
|
||||
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
||||
const { handleError } = require("./helpers/error");
|
||||
const { checkConnection } = require("./config");
|
||||
const { checkConnection, mqttClient } = require("./config");
|
||||
const { onNotification } = require("./services/notifikasi-wa.service");
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -47,4 +48,10 @@ app.get("/check-db", async (req, res) => {
|
||||
app.use(unknownEndpoint);
|
||||
app.use(handleError);
|
||||
|
||||
// Saat pesan diterima
|
||||
mqttClient.on('message', (topic, message) => {
|
||||
console.log(`Received message on topic "${topic}":`, message.toString());
|
||||
onNotification(topic, message);
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
require("dotenv").config();
|
||||
const { default: mqtt } = require("mqtt");
|
||||
const sql = require("mssql");
|
||||
|
||||
const isProduction = process.env.NODE_ENV === "production";
|
||||
|
||||
const endPointWhatsapp = process.env.ENDPOINT_WHATSAPP;
|
||||
|
||||
// Config SQL Server
|
||||
const config = {
|
||||
user: process.env.SQL_USERNAME,
|
||||
@@ -284,6 +287,34 @@ async function generateKode(prefix, tableName, columnName) {
|
||||
return prefix + String(nextNumber).padStart(3, "0");
|
||||
}
|
||||
|
||||
// Koneksi ke broker MQTT
|
||||
const mqttOptions = {
|
||||
clientId: 'express_mqtt_client_' + Math.random().toString(16).substr(2, 8),
|
||||
clean: true,
|
||||
connectTimeout: 4000,
|
||||
username: '', // jika ada
|
||||
password: '', // jika ada
|
||||
};
|
||||
|
||||
const mqttUrl = 'ws://localhost:1884'; // Ganti dengan broker kamu
|
||||
const topic = 'morek';
|
||||
|
||||
const mqttClient = mqtt.connect(mqttUrl, mqttOptions);
|
||||
|
||||
// Saat terkoneksi
|
||||
mqttClient.on('connect', () => {
|
||||
console.log('MQTT connected');
|
||||
|
||||
// Subscribe ke topik tertentu
|
||||
mqttClient.subscribe(topic, (err) => {
|
||||
if (!err) {
|
||||
console.log(`Subscribed to topic "${topic}"`);
|
||||
} else {
|
||||
console.error('Subscribe error:', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
checkConnection,
|
||||
query,
|
||||
@@ -293,4 +324,6 @@ module.exports = {
|
||||
buildDynamicInsert,
|
||||
buildDynamicUpdate,
|
||||
generateKode,
|
||||
endPointWhatsapp,
|
||||
mqttClient
|
||||
};
|
||||
|
||||
@@ -2,6 +2,9 @@ const AuthService = require('../services/auth.service');
|
||||
const { setResponse, checkValidate } = require('../helpers/utils');
|
||||
const { registerSchema, loginSchema } = require('../validate/auth.schema');
|
||||
const { createCaptcha } = require('../utils/captcha');
|
||||
const JWTService = require('../utils/jwt');
|
||||
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
class AuthController {
|
||||
// Register
|
||||
@@ -94,6 +97,41 @@ class AuthController {
|
||||
const response = await setResponse({ svg, text }, 'Captcha generated');
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
static async verifyTokenRedirect(req, res) {
|
||||
const { tokenRedirect } = req.body;
|
||||
|
||||
const bytes = CryptoJS.AES.decrypt(tokenRedirect, process.env.VITE_KEY_SESSION);
|
||||
const decrypted = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
|
||||
|
||||
const userPhone = decrypted?.user_phone
|
||||
const userName = decrypted?.user_name
|
||||
const idData = decrypted?.id
|
||||
|
||||
const payload = {
|
||||
user_id: userPhone,
|
||||
user_fullname: userName,
|
||||
};
|
||||
|
||||
const tokens = JWTService.generateTokenPair(payload);
|
||||
|
||||
// Simpan refresh token di cookie
|
||||
res.cookie('refreshToken', tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: false,
|
||||
sameSite: 'lax',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000
|
||||
});
|
||||
|
||||
const response = await setResponse(
|
||||
{
|
||||
accessToken: tokens.accessToken
|
||||
},
|
||||
'Verify successful'
|
||||
);
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthController;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const BrandService = require('../services/brand.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { createFileUploadDb } = require('../db/file_uploads.db');
|
||||
const {
|
||||
insertBrandSchema,
|
||||
updateBrandSchema,
|
||||
@@ -30,7 +29,7 @@ class BrandController {
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create brand with nested error codes and solutions
|
||||
// Create brand
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertBrandSchema, req);
|
||||
|
||||
@@ -40,61 +39,28 @@ class BrandController {
|
||||
|
||||
value.created_by = req.user?.user_id || null;
|
||||
|
||||
const results = await BrandService.createBrandWithFullData(value);
|
||||
const results = await BrandService.createBrand(value);
|
||||
const response = await setResponse(results, 'Brand created successfully');
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update brand
|
||||
// Update brand
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = await checkValidate(updateBrandSchema, 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.updated_by = req.user?.user_id || null;
|
||||
|
||||
// Insert to file_upload table
|
||||
const fileData = {
|
||||
file_upload_name: file.originalname,
|
||||
createdBy: req.user?.user_id || null,
|
||||
};
|
||||
await createFileUploadDb(fileData);
|
||||
|
||||
if (value.error_code && Array.isArray(value.error_code)) {
|
||||
for (const errorCode of value.error_code) {
|
||||
if (errorCode.solution && Array.isArray(errorCode.solution)) {
|
||||
for (const solution of errorCode.solution) {
|
||||
if (solution.type_solution !== 'text' && (!solution.path_solution || solution.path_solution === '')) {
|
||||
solution.path_solution = pathDocument;
|
||||
solution.type_solution = typeDoc.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const results = await BrandService.updateBrand(id, value);
|
||||
const response = await setResponse(results, 'Brand updated successfully');
|
||||
|
||||
value.updated_by = req.user?.user_id || null;
|
||||
|
||||
const results = await BrandService.updateBrandWithFullData(id, value);
|
||||
const response = await setResponse(results, 'Brand updated successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
|
||||
} catch (error) {
|
||||
const response = setResponse([], error.message, error.statusCode || 500);
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete brand by ID
|
||||
|
||||
74
controllers/error_code.controller.js
Normal file
74
controllers/error_code.controller.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const ErrorCodeService = require('../services/error_code.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const {
|
||||
insertErrorCodeSchema,
|
||||
updateErrorCodeSchema,
|
||||
} = require('../validate/error_code.schema');
|
||||
|
||||
class ErrorCodeController {
|
||||
static async getByBrandId(req, res) {
|
||||
const { brandId } = req.params;
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await ErrorCodeService.getErrorCodesByBrandId(brandId, queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Error codes found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get error code by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
const result = await ErrorCodeService.getErrorCodeById(id);
|
||||
const response = setResponse(result, 'Error code found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create error code with solutions and spareparts
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertErrorCodeSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
const { brandId } = req.params;
|
||||
value.created_by = req.user?.user_id || null;
|
||||
|
||||
const result = await ErrorCodeService.createErrorCodeWithFullData(brandId, value);
|
||||
const response = setResponse(result, 'Error code created successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update error code with solutions and spareparts
|
||||
static async update(req, res) {
|
||||
const { error, value } = await checkValidate(updateErrorCodeSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
const { brandId, errorCodeId } = req.params;
|
||||
value.updated_by = req.user?.user_id || null;
|
||||
|
||||
const result = await ErrorCodeService.updateErrorCodeWithFullData(brandId, errorCodeId, value);
|
||||
const response = setResponse(result, 'Error code updated successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete error code
|
||||
static async delete(req, res) {
|
||||
const { brandId, errorCodeId } = req.params;
|
||||
const deletedBy = req.user?.user_id || null;
|
||||
|
||||
const result = await ErrorCodeService.deleteErrorCode(brandId, errorCodeId, deletedBy);
|
||||
const response = setResponse(result, 'Error code deleted successfully');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ErrorCodeController;
|
||||
@@ -32,8 +32,7 @@ const uploadFile = async (req, res) => {
|
||||
const response = await setResponse(
|
||||
{
|
||||
file_upload_name: file.originalname,
|
||||
path_document: pathDocument,
|
||||
path_solution: pathDocument
|
||||
path_document: pathDocument
|
||||
},
|
||||
"File berhasil diunggah"
|
||||
);
|
||||
@@ -45,8 +44,6 @@ const uploadFile = async (req, res) => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const getFileByPath = async (req, res) => {
|
||||
try {
|
||||
const { folder, filename } = req.params;
|
||||
|
||||
@@ -4,53 +4,102 @@ const { setResponsePaging } = require('../helpers/utils');
|
||||
class HistoryValueController {
|
||||
|
||||
static async getAllHistoryAlarm(req, res) {
|
||||
const queryParams = req.query;
|
||||
try {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await HistoryValue.getAllHistoryAlarm(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
const results = await HistoryValue.getAllHistoryAlarm(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
res.status(response.statusCode).json(response);
|
||||
} catch (error) {
|
||||
const statusCode = error.statusCode || 500;
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: error.message || 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getAllHistoryEvent(req, res) {
|
||||
const queryParams = req.query;
|
||||
try {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await HistoryValue.getAllHistoryEvent(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
const results = await HistoryValue.getAllHistoryEvent(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
res.status(response.statusCode).json(response);
|
||||
} catch (error) {
|
||||
const statusCode = error.statusCode || 500;
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: error.message || 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueReport(req, res) {
|
||||
const queryParams = req.query;
|
||||
try {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await HistoryValue.getHistoryValueReport(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
const results = await HistoryValue.getHistoryValueReport(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
res.status(response.statusCode).json(response);
|
||||
} catch (error) {
|
||||
const statusCode = error.statusCode || 500;
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: error.message || 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueReportPivot(req, res) {
|
||||
const queryParams = req.query;
|
||||
try {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await HistoryValue.getHistoryValueReportPivot(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
const results = await HistoryValue.getHistoryValueReportPivot(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
|
||||
response.columns = results.column
|
||||
if (results.column) {
|
||||
response.columns = results.column;
|
||||
}
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
res.status(response.statusCode).json(response);
|
||||
} catch (error) {
|
||||
const statusCode = error.statusCode || 500;
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: error.message || 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueTrendingPivot(req, res) {
|
||||
const queryParams = req.query;
|
||||
try {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await HistoryValue.getHistoryValueTrendingPivot(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
const results = await HistoryValue.getHistoryValueTrendingPivot(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
||||
|
||||
response.columns = results.column
|
||||
if (results.column) {
|
||||
response.columns = results.column;
|
||||
}
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
res.status(response.statusCode).json(response);
|
||||
} catch (error) {
|
||||
const statusCode = error.statusCode || 500;
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: error.message || 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HistoryValueController;
|
||||
module.exports = HistoryValueController;
|
||||
@@ -1,25 +1,76 @@
|
||||
const NotificationErrorService = require('../services/notification_error.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const NotificationErrorService = require("../services/notification_error.service");
|
||||
const {
|
||||
setResponse,
|
||||
setResponsePaging,
|
||||
checkValidate,
|
||||
} = require("../helpers/utils");
|
||||
const {
|
||||
insertNotificationSchema,
|
||||
updateNotificationSchema,
|
||||
} = require("../validate/notification.schema");
|
||||
|
||||
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')
|
||||
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');
|
||||
const response = await setResponse(
|
||||
results,
|
||||
"Notification retrieved successfully"
|
||||
);
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertNotificationSchema, req);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, "Validation failed", 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id;
|
||||
|
||||
const results = await NotificationErrorService.createNotificationError(
|
||||
value
|
||||
);
|
||||
const response = await setResponse(results, "Notification created successfully");
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateNotificationSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await NotificationErrorService.updateNotificationError(id, value);
|
||||
const response = await setResponse(results, 'Notification Error User updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NotificationErrorController;
|
||||
module.exports = NotificationErrorController;
|
||||
|
||||
@@ -55,7 +55,18 @@ class NotificationErrorLogController {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.created_by = req.user.user_id;
|
||||
let createdBy, contactPhone;
|
||||
|
||||
if (!isNaN(req.user.userId) && Number(req.user.userId) > 0) {
|
||||
createdBy = Number(req.user.userId);
|
||||
contactPhone = value.contact_phone || null;
|
||||
} else {
|
||||
createdBy = null;
|
||||
contactPhone = req.user.userId;
|
||||
}
|
||||
|
||||
value.created_by = createdBy;
|
||||
value.contact_phone = contactPhone;
|
||||
|
||||
const results = await NotificationErrorLogService.createNotificationErrorLog(value);
|
||||
const response = await setResponse(results, 'Notification Error Log created successfully')
|
||||
|
||||
71
controllers/notification_error_user.controller.js
Normal file
71
controllers/notification_error_user.controller.js
Normal file
@@ -0,0 +1,71 @@
|
||||
const NotificationErrorUserService = require('../services/notification_error_user.service');
|
||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||
const { insertNotificationErrorUserSchema, updateNotificationErrorUserSchema } = require('../validate/notification_error_user.schema');
|
||||
|
||||
class NotificationErrorUserController {
|
||||
// Get all NotificationErrorUser
|
||||
static async getAll(req, res) {
|
||||
const queryParams = req.query;
|
||||
|
||||
const results = await NotificationErrorUserService.getAllNotificationErrorUser(queryParams);
|
||||
const response = await setResponsePaging(queryParams, results, 'Notification Error User found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Get NotificationErrorUser by ID
|
||||
static async getById(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await NotificationErrorUserService.getNotificationErrorUserById(id);
|
||||
const response = await setResponse(results, 'Notification Error User found')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Create NotificationErrorUser
|
||||
static async create(req, res) {
|
||||
const { error, value } = await checkValidate(insertNotificationErrorUserSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await NotificationErrorUserService.createNotificationErrorUser(value);
|
||||
const response = await setResponse(results, 'Notification Error User created successfully')
|
||||
|
||||
return res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Update NotificationErrorUser
|
||||
static async update(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const { error, value } = checkValidate(updateNotificationErrorUserSchema, req)
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
||||
}
|
||||
|
||||
value.userId = req.user.user_id
|
||||
|
||||
const results = await NotificationErrorUserService.updateNotificationErrorUser(id, value);
|
||||
const response = await setResponse(results, 'Notification Error User updated successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
|
||||
// Soft delete Notification Error User
|
||||
static async delete(req, res) {
|
||||
const { id } = req.params;
|
||||
|
||||
const results = await NotificationErrorUserService.deleteNotificationErrorUser(id, req.user.user_id);
|
||||
const response = await setResponse(results, 'Notification Error User deleted successfully')
|
||||
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NotificationErrorUserController;
|
||||
@@ -1,8 +1,15 @@
|
||||
const ExcelJS = require("exceljs");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const ImageKit = require("imagekit");
|
||||
const imagekit = new ImageKit({
|
||||
publicKey: process.env.IMAGEKIT_PUBLIC_KEY,
|
||||
privateKey: process.env.IMAGEKIT_PRIVATE_KEY,
|
||||
urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT,
|
||||
});
|
||||
|
||||
const SparepartService = require("../services/sparepart.service");
|
||||
|
||||
const {
|
||||
setResponse,
|
||||
setResponsePaging,
|
||||
@@ -38,25 +45,28 @@ class SparepartController {
|
||||
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;
|
||||
const upload = await imagekit.upload({
|
||||
file: req.file.buffer,
|
||||
fileName: req.file.originalname,
|
||||
folder: "/sparepart",
|
||||
});
|
||||
|
||||
value.sparepart_foto = upload.url;
|
||||
}
|
||||
|
||||
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);
|
||||
return res
|
||||
.status(201)
|
||||
.json(setResponse(results, "Sparepart created successfully"));
|
||||
} catch (err) {
|
||||
const response = setResponse([], err.message, err.statusCode || 500);
|
||||
res.status(response.statusCode).json(response);
|
||||
return res
|
||||
.status(err.statusCode || 500)
|
||||
.json(setResponse([], err.message, err.statusCode || 500));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,25 +76,28 @@ class SparepartController {
|
||||
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;
|
||||
const upload = await imagekit.upload({
|
||||
file: req.file.buffer,
|
||||
fileName: req.file.originalname,
|
||||
folder: "/sparepart",
|
||||
});
|
||||
|
||||
value.sparepart_foto = upload.url;
|
||||
}
|
||||
|
||||
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);
|
||||
return res
|
||||
.status(200)
|
||||
.json(setResponse(results, "Sparepart updated successfully"));
|
||||
} catch (err) {
|
||||
const response = setResponse([], err.message, err.statusCode || 500);
|
||||
res.status(response.statusCode).json(response);
|
||||
return res
|
||||
.status(err.statusCode || 500)
|
||||
.json(setResponse([], err.message, err.statusCode || 500));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,16 +126,8 @@ class SparepartController {
|
||||
|
||||
worksheet.addRows(
|
||||
results.data.map((item) => ({
|
||||
sparepart_name: item.sparepart_name,
|
||||
sparepart_code: item.sparepart_code,
|
||||
sparepart_qty: item.sparepart_qty,
|
||||
sparepart_merk: item.sparepart_merk,
|
||||
sparepart_model: item.sparepart_model,
|
||||
sparepart_unit: item.sparepart_unit,
|
||||
sparepart_stok: item.sparepart_stok,
|
||||
...item,
|
||||
sparepart_foto: "",
|
||||
sparepart_item_type: item.sparepart_item_type,
|
||||
created_at: item.created_at,
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -132,27 +137,38 @@ class SparepartController {
|
||||
|
||||
if (!item.sparepart_foto) continue;
|
||||
|
||||
let foto = item.sparepart_foto.trim();
|
||||
let imageUrl = item.sparepart_foto;
|
||||
|
||||
foto = foto.replace(/^images\//, "");
|
||||
if (!imageUrl.startsWith("https")) continue;
|
||||
|
||||
const fullPath = path.resolve(process.cwd(), "uploads", "images", foto);
|
||||
let ext = path.extname(imageUrl).toLowerCase().replace(".", "");
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
const ext = fullPath.split(".").pop().toLowerCase();
|
||||
if (!ext) ext = "jpg";
|
||||
|
||||
const imageId = workbook.addImage({
|
||||
filename: fullPath,
|
||||
extension: ext,
|
||||
});
|
||||
|
||||
worksheet.addImage(imageId, {
|
||||
tl: { col: 7, row: rowNumber - 1 },
|
||||
ext: { width: 80, height: 80 },
|
||||
});
|
||||
|
||||
worksheet.getRow(rowNumber).height = 70;
|
||||
const supported = ["jpg", "jpeg", "png"];
|
||||
if (!supported.includes(ext)) {
|
||||
ext = "png";
|
||||
}
|
||||
|
||||
let buffer;
|
||||
try {
|
||||
const resp = await fetch(imageUrl);
|
||||
buffer = Buffer.from(await resp.arrayBuffer());
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const imageId = workbook.addImage({
|
||||
buffer,
|
||||
extension: ext === "jpg" ? "jpeg" : ext,
|
||||
});
|
||||
|
||||
worksheet.addImage(imageId, {
|
||||
tl: { col: 7, row: rowNumber - 1 },
|
||||
ext: { width: 80, height: 80 },
|
||||
});
|
||||
|
||||
worksheet.getRow(rowNumber).height = 70;
|
||||
}
|
||||
|
||||
worksheet.getRow(1).eachCell((cell) => {
|
||||
@@ -173,10 +189,124 @@ class SparepartController {
|
||||
|
||||
return res.send(buffer);
|
||||
} catch (error) {
|
||||
console.log("Export Excel Error:", error);
|
||||
return res.status(500).json({
|
||||
message: error.message,
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async importExcel(req, res) {
|
||||
try {
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(req.file.buffer);
|
||||
|
||||
const worksheet = workbook.getWorksheet(1);
|
||||
|
||||
const images = worksheet.getImages();
|
||||
const imageMap = {};
|
||||
images.forEach((imgObj) => {
|
||||
const imageId = imgObj.imageId;
|
||||
const range = imgObj.range;
|
||||
const row = range.tl.nativeRow + 1;
|
||||
const image = workbook.getImage(imageId);
|
||||
imageMap[row] = image;
|
||||
});
|
||||
|
||||
const spareparts = [];
|
||||
|
||||
worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => {
|
||||
if (rowNumber === 1) return;
|
||||
|
||||
const [
|
||||
sparepart_name,
|
||||
sparepart_code,
|
||||
sparepart_description,
|
||||
sparepart_qty_excel,
|
||||
sparepart_merk,
|
||||
sparepart_model,
|
||||
sparepart_unit,
|
||||
sparepart_stok_excel,
|
||||
sparepart_foto_excel,
|
||||
sparepart_item_type,
|
||||
] = row.values.slice(1);
|
||||
|
||||
if (!sparepart_name) return;
|
||||
|
||||
if (!sparepart_code) {
|
||||
return;
|
||||
}
|
||||
|
||||
spareparts.push({
|
||||
sparepart_name: sparepart_name || "",
|
||||
sparepart_code: sparepart_code || "",
|
||||
sparepart_description: sparepart_description || "",
|
||||
sparepart_qty: Number(sparepart_qty_excel) || 0,
|
||||
sparepart_merk: sparepart_merk || "",
|
||||
sparepart_model: sparepart_model || "",
|
||||
sparepart_unit: sparepart_unit || "",
|
||||
sparepart_stok: sparepart_stok_excel || "",
|
||||
sparepart_foto: sparepart_foto_excel || "",
|
||||
sparepart_item_type: sparepart_item_type || "",
|
||||
rowNumber,
|
||||
});
|
||||
});
|
||||
|
||||
if (spareparts.length === 0) {
|
||||
return res
|
||||
.status(400)
|
||||
.json(setResponse([], "Tidak ada data valid untuk diimport", 400));
|
||||
}
|
||||
|
||||
const results = [];
|
||||
|
||||
for (const data of spareparts) {
|
||||
let uploadedUrl = "";
|
||||
|
||||
try {
|
||||
const image = imageMap[data.rowNumber];
|
||||
if (image) {
|
||||
const fileName = `sparepart_${Date.now()}_${
|
||||
data.sparepart_code
|
||||
}.jpg`;
|
||||
const uploadResult = await imagekit.upload({
|
||||
file: image.buffer,
|
||||
fileName: fileName,
|
||||
folder: "/sparepart",
|
||||
});
|
||||
|
||||
uploadedUrl = uploadResult.url;
|
||||
}
|
||||
} catch (err) {
|
||||
err;
|
||||
}
|
||||
|
||||
data.sparepart_foto = uploadedUrl || "";
|
||||
|
||||
const { rowNumber, ...dbData } = data;
|
||||
|
||||
const created = await SparepartService.createSparepart(dbData);
|
||||
|
||||
if (created && created[0]) {
|
||||
results.push({
|
||||
sparepart_id: created[0].sparepart_id,
|
||||
sparepart_name: created[0].sparepart_name,
|
||||
sparepart_code: created[0].sparepart_code,
|
||||
sparepart_description: created[0].sparepart_description,
|
||||
sparepart_qty: created[0].sparepart_qty,
|
||||
sparepart_merk: created[0].sparepart_merk,
|
||||
sparepart_model: created[0].sparepart_model,
|
||||
sparepart_unit: created[0].sparepart_unit,
|
||||
sparepart_stok: created[0].sparepart_stok,
|
||||
sparepart_foto: created[0].sparepart_foto,
|
||||
sparepart_item_type: created[0].sparepart_item_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res.json(
|
||||
setResponse(results, `${results.length} Sparepart berhasil diimport`)
|
||||
);
|
||||
} catch (error) {
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,4 +323,5 @@ class SparepartController {
|
||||
res.status(response.statusCode).json(response);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SparepartController;
|
||||
|
||||
@@ -1,15 +1,55 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get error codes by brand ID
|
||||
const getErrorCodesByBrandIdDb = async (brandId) => {
|
||||
const getErrorCodesByBrandIdDb = async (brandId, searchParams = {}) => {
|
||||
let queryParams = [brandId];
|
||||
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [brandId, Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search across multiple columns
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.error_code", "a.error_code_name", "a.error_code_description"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter conditions
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.is_active", param: searchParams.status, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM brand_code a
|
||||
WHERE a.brand_id = $1 AND a.deleted_at IS NULL
|
||||
ORDER BY a.error_code_id
|
||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||
${whereOrConditions ? whereOrConditions : ''}
|
||||
ORDER BY a.error_code_id DESC
|
||||
${searchParams.limit ? `OFFSET $3 * $2 ROWS FETCH NEXT $2 ROWS ONLY` : ''}
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId]);
|
||||
|
||||
const result = await pool.query(queryText, queryParams);
|
||||
|
||||
// Return paginated format if limit is provided
|
||||
if (searchParams.limit) {
|
||||
const total = result?.recordset.length > 0 ? parseInt(result.recordset[0].total_data, 10) : 0;
|
||||
return { data: result.recordset, total };
|
||||
}
|
||||
|
||||
// Return simple array for backward compatibility
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
@@ -64,10 +104,71 @@ const getErrorCodeByIdDb = async (error_code_id) => {
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
const getErrorCodeByBrandAndCodeDb = async (brandId, errorCode) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*
|
||||
FROM brand_code a
|
||||
WHERE a.brand_id = $1 AND a.error_code = $2 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId, errorCode]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
// Get all error codes with pagination and search
|
||||
const getAllErrorCodesDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
// Pagination
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
// Search across multiple columns
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["a.error_code", "a.error_code_name", "a.error_code_description"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamOr ? whereParamOr : queryParams;
|
||||
|
||||
// Filter conditions
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.is_active", param: searchParams.status, type: "string" },
|
||||
{ column: "a.brand_id", param: searchParams.brand_id, type: "number" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
queryParams = whereParamAnd ? whereParamAnd : queryParams;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM brand_code a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? `AND ${whereConditions.join(' AND ')}` : ''}
|
||||
${whereOrConditions ? whereOrConditions : ''}
|
||||
ORDER BY a.error_code_id DESC
|
||||
${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 };
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getErrorCodesByBrandIdDb,
|
||||
getErrorCodeByIdDb,
|
||||
getErrorCodeByBrandAndCodeDb,
|
||||
createErrorCodeDb,
|
||||
updateErrorCodeDb,
|
||||
deleteErrorCodeDb,
|
||||
getAllErrorCodesDb,
|
||||
};
|
||||
@@ -1,9 +1,125 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get spareparts by brand_id
|
||||
const getSparepartsByBrandIdDb = async (brandId) => {
|
||||
// Get spareparts by error_code_id
|
||||
const getSparepartsByErrorCodeIdDb = async (errorCodeId) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
s.sparepart_id,
|
||||
s.sparepart_name,
|
||||
s.sparepart_code,
|
||||
s.sparepart_description,
|
||||
s.sparepart_model,
|
||||
s.sparepart_foto,
|
||||
s.sparepart_item_type,
|
||||
s.sparepart_qty,
|
||||
s.sparepart_unit,
|
||||
s.sparepart_merk,
|
||||
s.sparepart_stok,
|
||||
bs.created_at,
|
||||
bs.created_by
|
||||
FROM brand_sparepart bs
|
||||
JOIN m_sparepart s ON bs.sparepart_id = s.sparepart_id
|
||||
WHERE bs.error_code_id = $1
|
||||
AND s.deleted_at IS NULL
|
||||
ORDER BY s.sparepart_name
|
||||
`;
|
||||
const result = await pool.query(queryText, [errorCodeId]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Get error codes by sparepart_id
|
||||
const getErrorCodesBySparepartIdDb = async (sparepartId) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
ec.error_code_id,
|
||||
ec.error_code,
|
||||
ec.error_code_name,
|
||||
ec.error_code_description,
|
||||
ec.error_code_color,
|
||||
ec.path_icon,
|
||||
ec.is_active,
|
||||
ec.created_at,
|
||||
ec.updated_at
|
||||
FROM brand_sparepart bs
|
||||
JOIN m_error_codes ec ON bs.error_code_id = ec.error_code_id
|
||||
WHERE bs.sparepart_id = $1
|
||||
AND ec.deleted_at IS NULL
|
||||
ORDER BY ec.error_code
|
||||
`;
|
||||
const result = await pool.query(queryText, [sparepartId]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Insert error_code-spareparts relationship
|
||||
const insertErrorCodeSparepartDb = async (errorCodeId, sparepartId, createdBy) => {
|
||||
const queryText = `
|
||||
INSERT INTO brand_sparepart (error_code_id, sparepart_id, created_by, created_at)
|
||||
VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
|
||||
`;
|
||||
const result = await pool.query(queryText, [errorCodeId, sparepartId, createdBy]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Insert multiple error_code-spareparts relationships
|
||||
const insertMultipleErrorCodeSparepartsDb = async (errorCodeId, sparepartIds, createdBy) => {
|
||||
if (!sparepartIds || sparepartIds.length === 0) return [];
|
||||
|
||||
const values = sparepartIds.map((_, index) => `($1, $${index + 2}, $${sparepartIds.length + 2}, CURRENT_TIMESTAMP)`).join(', ');
|
||||
const queryText = `
|
||||
INSERT INTO brand_sparepart (error_code_id, sparepart_id, created_by, created_at)
|
||||
VALUES ${values}
|
||||
`;
|
||||
const params = [errorCodeId, ...sparepartIds, createdBy];
|
||||
const result = await pool.query(queryText, params);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Delete specific error_code-sparepart relationship
|
||||
const deleteErrorCodeSparepartDb = async (errorCodeId, sparepartId) => {
|
||||
const queryText = `
|
||||
DELETE FROM brand_sparepart
|
||||
WHERE error_code_id = $1 AND sparepart_id = $2
|
||||
`;
|
||||
const result = await pool.query(queryText, [errorCodeId, sparepartId]);
|
||||
return result.rowsAffected > 0;
|
||||
};
|
||||
|
||||
// Delete all spareparts for an error_code
|
||||
const deleteAllErrorCodeSparepartsDb = async (errorCodeId) => {
|
||||
const queryText = `
|
||||
DELETE FROM brand_sparepart
|
||||
WHERE error_code_id = $1
|
||||
`;
|
||||
const result = await pool.query(queryText, [errorCodeId]);
|
||||
return result.rowsAffected > 0;
|
||||
};
|
||||
|
||||
// Update error_code-spareparts (replace all)
|
||||
const updateErrorCodeSparepartsDb = async (errorCodeId, sparepartIds, updatedBy) => {
|
||||
// Delete existing relationships
|
||||
await deleteAllErrorCodeSparepartsDb(errorCodeId);
|
||||
|
||||
// Insert new relationships
|
||||
if (sparepartIds && sparepartIds.length > 0) {
|
||||
return await insertMultipleErrorCodeSparepartsDb(errorCodeId, sparepartIds, updatedBy);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const checkErrorCodeSparepartExistsDb = async (errorCodeId, sparepartId) => {
|
||||
const queryText = `
|
||||
SELECT 1
|
||||
FROM brand_spareparts
|
||||
WHERE error_code_id = $1 AND sparepart_id = $2
|
||||
`;
|
||||
const result = await pool.query(queryText, [errorCodeId, sparepartId]);
|
||||
return result.recordset.length > 0;
|
||||
};
|
||||
|
||||
const getSparepartsByBrandIdDb = async (brandId) => {
|
||||
const queryText = `
|
||||
SELECT DISTINCT
|
||||
s.sparepart_id,
|
||||
s.sparepart_name,
|
||||
s.sparepart_code,
|
||||
@@ -17,20 +133,22 @@ const getSparepartsByBrandIdDb = async (brandId) => {
|
||||
s.sparepart_stok,
|
||||
s.created_at,
|
||||
s.updated_at
|
||||
FROM brand_spareparts bs
|
||||
FROM brand_sparepart bs
|
||||
JOIN m_sparepart s ON bs.sparepart_id = s.sparepart_id
|
||||
WHERE bs.brand_id = $1
|
||||
JOIN m_error_codes ec ON bs.error_code_id = ec.error_code_id
|
||||
WHERE ec.brand_id = $1
|
||||
AND s.deleted_at IS NULL
|
||||
AND ec.deleted_at IS NULL
|
||||
ORDER BY s.sparepart_name
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Get brands by sparepart_id
|
||||
// Get brands by sparepart_id (now using error_code_id mapping)
|
||||
const getBrandsBySparepartIdDb = async (sparepartId) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
SELECT DISTINCT
|
||||
b.brand_id,
|
||||
b.brand_name,
|
||||
b.brand_type,
|
||||
@@ -40,9 +158,13 @@ const getBrandsBySparepartIdDb = async (sparepartId) => {
|
||||
b.is_active,
|
||||
b.created_at,
|
||||
b.updated_at
|
||||
FROM brand_spareparts bs
|
||||
JOIN m_brands b ON bs.brand_id = b.brand_id
|
||||
FROM brand_sparepart bs
|
||||
JOIN m_sparepart s ON bs.sparepart_id = s.sparepart_id
|
||||
JOIN m_error_codes ec ON bs.error_code_id = ec.error_code_id
|
||||
JOIN m_brands b ON ec.brand_id = b.brand_id
|
||||
WHERE bs.sparepart_id = $1
|
||||
AND s.deleted_at IS NULL
|
||||
AND ec.deleted_at IS NULL
|
||||
AND b.deleted_at IS NULL
|
||||
ORDER BY b.brand_name
|
||||
`;
|
||||
@@ -50,81 +172,16 @@ const getBrandsBySparepartIdDb = async (sparepartId) => {
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Insert brand-spareparts relationship
|
||||
const insertBrandSparepartDb = async (brandId, sparepartId, createdBy) => {
|
||||
const queryText = `
|
||||
INSERT INTO brand_spareparts (brand_id, sparepart_id, created_by, created_at)
|
||||
VALUES ($1, $2, $3, CURRENT_TIMESTAMP)
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId, sparepartId, createdBy]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Insert multiple brand-spareparts relationships
|
||||
const insertMultipleBrandSparepartsDb = async (brandId, sparepartIds, createdBy) => {
|
||||
if (!sparepartIds || sparepartIds.length === 0) return [];
|
||||
|
||||
const values = sparepartIds.map((_, index) => `($1, $${index + 2}, $${sparepartIds.length + 2}, CURRENT_TIMESTAMP)`).join(', ');
|
||||
const queryText = `
|
||||
INSERT INTO brand_spareparts (brand_id, sparepart_id, created_by, created_at)
|
||||
VALUES ${values}
|
||||
`;
|
||||
const params = [brandId, ...sparepartIds, createdBy];
|
||||
const result = await pool.query(queryText, params);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
// Delete specific brand-sparepart relationship
|
||||
const deleteBrandSparepartDb = async (brandId, sparepartId) => {
|
||||
const queryText = `
|
||||
DELETE FROM brand_spareparts
|
||||
WHERE brand_id = $1 AND sparepart_id = $2
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId, sparepartId]);
|
||||
return result.rowsAffected > 0;
|
||||
};
|
||||
|
||||
// Delete all spareparts for a brand
|
||||
const deleteAllBrandSparepartsDb = async (brandId) => {
|
||||
const queryText = `
|
||||
DELETE FROM brand_spareparts
|
||||
WHERE brand_id = $1
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId]);
|
||||
return result.rowsAffected > 0;
|
||||
};
|
||||
|
||||
// Update brand-spareparts (replace all)
|
||||
const updateBrandSparepartsDb = async (brandId, sparepartIds, updatedBy) => {
|
||||
// Delete existing relationships
|
||||
await deleteAllBrandSparepartsDb(brandId);
|
||||
|
||||
// Insert new relationships
|
||||
if (sparepartIds && sparepartIds.length > 0) {
|
||||
return await insertMultipleBrandSparepartsDb(brandId, sparepartIds, updatedBy);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Check if brand-sparepart relationship exists
|
||||
const checkBrandSparepartExistsDb = async (brandId, sparepartId) => {
|
||||
const queryText = `
|
||||
SELECT 1
|
||||
FROM brand_spareparts
|
||||
WHERE brand_id = $1 AND sparepart_id = $2
|
||||
`;
|
||||
const result = await pool.query(queryText, [brandId, sparepartId]);
|
||||
return result.recordset.length > 0;
|
||||
};
|
||||
// Deprecated functions removed - table structure changed to use error_code_id instead of brand_id
|
||||
|
||||
module.exports = {
|
||||
getSparepartsByBrandIdDb,
|
||||
getBrandsBySparepartIdDb,
|
||||
insertBrandSparepartDb,
|
||||
insertMultipleBrandSparepartsDb,
|
||||
deleteBrandSparepartDb,
|
||||
deleteAllBrandSparepartsDb,
|
||||
updateBrandSparepartsDb,
|
||||
checkBrandSparepartExistsDb,
|
||||
// New functions using error_code_id
|
||||
getSparepartsByErrorCodeIdDb,
|
||||
getErrorCodesBySparepartIdDb,
|
||||
insertErrorCodeSparepartDb,
|
||||
insertMultipleErrorCodeSparepartsDb,
|
||||
deleteErrorCodeSparepartDb,
|
||||
deleteAllErrorCodeSparepartsDb,
|
||||
updateErrorCodeSparepartsDb,
|
||||
checkErrorCodeSparepartExistsDb,
|
||||
};
|
||||
@@ -24,6 +24,7 @@ const getAllContactDb = async (searchParams = {}) => {
|
||||
[
|
||||
{ column: "a.contact_name", param: searchParams.name, type: "string" },
|
||||
{ column: "a.contact_type", param: searchParams.code, type: "string" },
|
||||
{ column: "a.is_active", param: searchParams.active, type: "boolean" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
@@ -115,160 +115,321 @@ const getHistoryEventDb = async (searchParams = {}) => {
|
||||
};
|
||||
|
||||
const checkTableNamedDb = async (tableName) => {
|
||||
const queryText = `
|
||||
SELECT *
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_NAME = $1;`;
|
||||
const result = await pool.query(queryText, [tableName]);
|
||||
return result.recordset;
|
||||
try {
|
||||
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||
throw new Error('Invalid table name format');
|
||||
}
|
||||
|
||||
const queryText = `
|
||||
SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_TYPE
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_NAME = $1
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, [tableName]);
|
||||
return result.recordset;
|
||||
} catch (error) {
|
||||
console.error('Error in checkTableNamedDb:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getHistoryValueReportDb = async (tableName, searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
try {
|
||||
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||
throw new Error('Invalid table name format');
|
||||
}
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"b.tag_name",
|
||||
"a.tagnum"
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
["b.tag_name", "CAST(a.tagnum AS VARCHAR)"],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "b.tag_name", param: searchParams.name, type: "string" },
|
||||
{ column: "b.tag_number", param: searchParams.name, type: "number" },
|
||||
{ column: "a.datetime", param: [searchParams.from, searchParams.to], type: "between" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "b.tag_name", param: searchParams.name, type: "string" },
|
||||
{ column: "b.tag_number", param: searchParams.name, type: "number" },
|
||||
{ column: "a.datetime", param: [searchParams.from, searchParams.to], type: "between" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.tag_name,
|
||||
b.tag_number,
|
||||
b.lim_low_crash,
|
||||
b.lim_low,
|
||||
b.lim_high,
|
||||
b.lim_high_crash,
|
||||
c.status_color
|
||||
FROM ${tableName} a
|
||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
||||
LEFT JOIN m_status c ON a.status = c.status_number AND c.deleted_at IS NULL
|
||||
WHERE a.datetime IS NOT NULL AND b.is_report = 1
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.datetime DESC
|
||||
${searchParams.limit ? `OFFSET $2 * $1 ROWS FETCH NEXT $1 ROWS ONLY` : ''}
|
||||
`;
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.tag_name,
|
||||
b.tag_number,
|
||||
b.lim_low_crash,
|
||||
b.lim_low,
|
||||
b.lim_high,
|
||||
b.lim_high_crash,
|
||||
c.status_color
|
||||
FROM ${tableName} a
|
||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
||||
LEFT JOIN m_status c ON a.status = c.status_number AND c.deleted_at IS NULL
|
||||
WHERE a.datetime IS NOT NULL AND b.is_report = 1
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.datetime DESC
|
||||
${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 =
|
||||
result?.recordset?.length > 0
|
||||
const total = result.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, total };
|
||||
return { data: result.recordset, total };
|
||||
} catch (error) {
|
||||
console.error('Error in getHistoryValueReportDb:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getHistoryValueReportPivotDb = async (tableName, searchParams = {}) => {
|
||||
let from = searchParams.from;
|
||||
let to = searchParams.to;
|
||||
const interval = Number(searchParams.interval ?? 10); // menit
|
||||
const limit = Number(searchParams.limit ?? 10);
|
||||
const page = Number(searchParams.page ?? 1);
|
||||
try {
|
||||
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||
throw new Error('Invalid table name format');
|
||||
}
|
||||
|
||||
// --- Normalisasi tanggal
|
||||
if (from.length === 10) from += ' 00:00:00';
|
||||
if (to.length === 10) to += ' 23:59:59';
|
||||
let from = searchParams.from || '';
|
||||
let to = searchParams.to || '';
|
||||
const interval = Math.max(1, Math.min(1440, Number(searchParams.interval ?? 10)));
|
||||
|
||||
// --- Ambil semua tag yang di-report
|
||||
const tags = await pool.query(`
|
||||
SELECT tag_name
|
||||
FROM m_tags
|
||||
WHERE is_report = 1 AND deleted_at IS NULL
|
||||
`);
|
||||
if (from.length === 10 && /^\d{4}-\d{2}-\d{2}$/.test(from)) {
|
||||
from += ' 00:00:00';
|
||||
}
|
||||
if (to.length === 10 && /^\d{4}-\d{2}-\d{2}$/.test(to)) {
|
||||
to += ' 23:59:59';
|
||||
}
|
||||
|
||||
if (tags.recordset.length === 0) {
|
||||
return { data: [], total: 0 };
|
||||
console.log('Table:', tableName);
|
||||
console.log('From:', from, '| To:', to, '| Interval:', interval);
|
||||
console.log('Filters:', searchParams);
|
||||
|
||||
const dateRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
||||
if (!dateRegex.test(from) || !dateRegex.test(to)) {
|
||||
throw new Error('Invalid date format. Expected: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS');
|
||||
}
|
||||
|
||||
const fromDate = new Date(from);
|
||||
const toDate = new Date(to);
|
||||
const daysDiff = (toDate - fromDate) / (1000 * 60 * 60 * 24);
|
||||
|
||||
if (daysDiff > 365) {
|
||||
throw new Error('Date range cannot exceed 1 year');
|
||||
}
|
||||
if (daysDiff < 0) {
|
||||
throw new Error('From date must be before to date');
|
||||
}
|
||||
|
||||
let tagQueryParams = [];
|
||||
let tagWhereConditions = [];
|
||||
|
||||
if (searchParams.plant_sub_section_id) {
|
||||
tagWhereConditions.push(`plant_sub_section_id = $${tagQueryParams.length + 1}`);
|
||||
tagQueryParams.push(searchParams.plant_sub_section_id);
|
||||
}
|
||||
|
||||
if (searchParams.plant_section_id) {
|
||||
tagWhereConditions.push(`plant_section_id = $${tagQueryParams.length + 1}`);
|
||||
tagQueryParams.push(searchParams.plant_section_id);
|
||||
}
|
||||
|
||||
if (searchParams.name) {
|
||||
const nameFilter = `(tag_name LIKE $${tagQueryParams.length + 1} OR CAST(tag_number AS VARCHAR) LIKE $${tagQueryParams.length + 2})`;
|
||||
tagWhereConditions.push(nameFilter);
|
||||
tagQueryParams.push(`%${searchParams.name}%`, `%${searchParams.name}%`);
|
||||
}
|
||||
|
||||
if (searchParams.criteria) {
|
||||
const criteriaFilter = `(tag_name LIKE $${tagQueryParams.length + 1} OR CAST(tag_number AS VARCHAR) LIKE $${tagQueryParams.length + 2})`;
|
||||
tagWhereConditions.push(criteriaFilter);
|
||||
tagQueryParams.push(`%${searchParams.criteria}%`, `%${searchParams.criteria}%`);
|
||||
}
|
||||
|
||||
const tagWhereClause = tagWhereConditions.length > 0
|
||||
? ` AND ${tagWhereConditions.join(" AND ")}`
|
||||
: '';
|
||||
|
||||
const tagsQuery = `
|
||||
SELECT tag_name, tag_number
|
||||
FROM m_tags
|
||||
WHERE is_report = 1 AND deleted_at IS NULL
|
||||
${tagWhereClause}
|
||||
ORDER BY tag_name
|
||||
`;
|
||||
|
||||
console.log('Tags Query:', tagsQuery);
|
||||
console.log('Tags Query Params:', tagQueryParams);
|
||||
|
||||
const tagsResult = await pool.query(tagsQuery, tagQueryParams);
|
||||
|
||||
console.log('Tags found:', tagsResult.recordset.length);
|
||||
|
||||
if (tagsResult.recordset.length === 0) {
|
||||
return { data: [], column: '' };
|
||||
}
|
||||
|
||||
const tagNames = tagsResult.recordset.map(r => `[${r.tag_name}]`).join(', ');
|
||||
const tagNamesColumn = tagsResult.recordset.map(r => r.tag_name).join(', ');
|
||||
const tagNumbers = tagsResult.recordset.map(r => r.tag_number);
|
||||
|
||||
console.log('Filtered tag numbers:', tagNumbers);
|
||||
console.log('Filtered tag names:', tagNamesColumn);
|
||||
|
||||
const tagNumbersFilter = tagNumbers.length > 0
|
||||
? ` AND a.tagnum IN (${tagNumbers.join(',')})`
|
||||
: '';
|
||||
|
||||
const queryText = `
|
||||
DECLARE
|
||||
@fromParam DATETIME = $1,
|
||||
@toParam DATETIME = $2,
|
||||
@intervalParam INT = $3;
|
||||
|
||||
SELECT TOP 10
|
||||
'DEBUG_AVERAGING' as info,
|
||||
b.tag_name,
|
||||
DATEADD(MINUTE,
|
||||
(DATEDIFF(MINUTE, @fromParam, CAST(a.datetime AS DATETIME)) / @intervalParam) * @intervalParam,
|
||||
@fromParam
|
||||
) AS waktu_group,
|
||||
COUNT(*) as data_points,
|
||||
AVG(CAST(a.val AS FLOAT)) as avg_val,
|
||||
MIN(CAST(a.val AS FLOAT)) as min_val,
|
||||
MAX(CAST(a.val AS FLOAT)) as max_val
|
||||
FROM ${tableName} a
|
||||
INNER JOIN m_tags b ON a.tagnum = b.tag_number
|
||||
AND b.deleted_at IS NULL
|
||||
AND b.is_report = 1
|
||||
WHERE CAST(a.datetime AS DATETIME) BETWEEN @fromParam AND @toParam
|
||||
AND a.val IS NOT NULL
|
||||
${tagNumbersFilter}
|
||||
GROUP BY
|
||||
b.tag_name,
|
||||
DATEADD(MINUTE,
|
||||
(DATEDIFF(MINUTE, @fromParam, CAST(a.datetime AS DATETIME)) / @intervalParam) * @intervalParam,
|
||||
@fromParam
|
||||
)
|
||||
ORDER BY b.tag_name, waktu_group;
|
||||
|
||||
;WITH TimeSeries AS (
|
||||
SELECT @fromParam AS waktu
|
||||
UNION ALL
|
||||
SELECT DATEADD(MINUTE, @intervalParam, waktu)
|
||||
FROM TimeSeries
|
||||
WHERE DATEADD(MINUTE, @intervalParam, waktu) <= @toParam
|
||||
),
|
||||
CleanData AS (
|
||||
SELECT
|
||||
CAST(a.datetime AS DATETIME) as datetime_clean,
|
||||
a.tagnum,
|
||||
CAST(a.val AS FLOAT) as val,
|
||||
b.tag_name
|
||||
FROM ${tableName} a
|
||||
INNER JOIN m_tags b ON a.tagnum = b.tag_number
|
||||
AND b.deleted_at IS NULL
|
||||
AND b.is_report = 1
|
||||
WHERE ISDATE(a.datetime) = 1
|
||||
AND a.val IS NOT NULL
|
||||
AND CAST(a.datetime AS DATETIME) BETWEEN @fromParam AND @toParam
|
||||
${tagNumbersFilter}
|
||||
),
|
||||
Averaged AS (
|
||||
SELECT
|
||||
DATEADD(MINUTE,
|
||||
(DATEDIFF(MINUTE, @fromParam, datetime_clean) / @intervalParam) * @intervalParam,
|
||||
@fromParam
|
||||
) AS waktu_group,
|
||||
tag_name,
|
||||
AVG(val) AS avg_val
|
||||
FROM CleanData
|
||||
GROUP BY
|
||||
DATEADD(MINUTE,
|
||||
(DATEDIFF(MINUTE, @fromParam, datetime_clean) / @intervalParam) * @intervalParam,
|
||||
@fromParam
|
||||
),
|
||||
tag_name
|
||||
),
|
||||
Pivoted AS (
|
||||
SELECT
|
||||
waktu_group,
|
||||
${tagNames}
|
||||
FROM Averaged
|
||||
PIVOT (
|
||||
MAX(avg_val)
|
||||
FOR tag_name IN (${tagNames})
|
||||
) AS p
|
||||
)
|
||||
SELECT
|
||||
CONVERT(VARCHAR(19), ts.waktu, 120) AS waktu,
|
||||
${tagNames}
|
||||
FROM TimeSeries ts
|
||||
LEFT JOIN Pivoted p ON ts.waktu = p.waktu_group
|
||||
ORDER BY ts.waktu
|
||||
OPTION (MAXRECURSION 0);
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, [from, to, interval]);
|
||||
|
||||
if (result.recordsets && result.recordsets.length >= 2) {
|
||||
console.log('Sample averaging data:');
|
||||
result.recordsets[0].slice(0, 10).forEach(row => {
|
||||
console.log(`${row.tag_name} @ ${row.waktu_group}: avg=${row.avg_val}, min=${row.min_val}, max=${row.max_val}, points=${row.data_points}`);
|
||||
});
|
||||
|
||||
console.log('\nPivot result sample:');
|
||||
if (result.recordsets[1] && result.recordsets[1].length > 0) {
|
||||
result.recordsets[1].slice(0, 5).forEach(row => {
|
||||
console.log(JSON.stringify(row, null, 2));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const rows = result.recordsets?.[1] || result.recordset;
|
||||
|
||||
if (!rows || rows.length === 0) {
|
||||
console.log('No pivot data');
|
||||
return { data: [], column: tagNamesColumn };
|
||||
}
|
||||
|
||||
const timeKey = 'waktu';
|
||||
const tagList = Object.keys(rows[0]).filter(k => k !== timeKey);
|
||||
|
||||
const nivoData = tagList.map(tag => ({
|
||||
id: tag,
|
||||
data: rows.map(row => ({
|
||||
x: row[timeKey],
|
||||
y: row[tag] !== null && row[tag] !== undefined ? Number(row[tag]) : null
|
||||
}))
|
||||
}));
|
||||
|
||||
nivoData.forEach(series => {
|
||||
const nonNull = series.data.filter(d => d.y !== null && d.y !== 0);
|
||||
const sampleVals = nonNull.slice(0, 3).map(d => d.y);
|
||||
console.log(`${series.id}: ${nonNull.length} non-zero values, sample: [${sampleVals.join(', ')}]`);
|
||||
});
|
||||
|
||||
return { data: nivoData, column: tagNamesColumn };
|
||||
} catch (error) {
|
||||
console.error('Error in getHistoryValueReportPivotDb:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const tagNames = tags.recordset.map(r => `[${r.tag_name}]`).join(', ');
|
||||
const tagNamesColumn = tags.recordset.map(r => `${r.tag_name}`).join(', ');
|
||||
|
||||
// --- Query utama
|
||||
const queryText = `
|
||||
DECLARE
|
||||
@fromParam DATETIME = '${from}',
|
||||
@toParam DATETIME = '${to}',
|
||||
@intervalParam INT = ${interval};
|
||||
|
||||
;WITH TimeSeries AS (
|
||||
SELECT @fromParam AS waktu
|
||||
UNION ALL
|
||||
SELECT DATEADD(MINUTE, @intervalParam, waktu)
|
||||
FROM TimeSeries
|
||||
WHERE DATEADD(MINUTE, @intervalParam, waktu) <= @toParam
|
||||
),
|
||||
Averaged AS (
|
||||
SELECT
|
||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0) AS waktu_group,
|
||||
b.tag_name,
|
||||
ROUND(AVG(a.val), 4) AS avg_val
|
||||
FROM ${tableName} a
|
||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
||||
WHERE a.datetime BETWEEN @fromParam AND @toParam
|
||||
GROUP BY
|
||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0),
|
||||
b.tag_name
|
||||
),
|
||||
Pivoted AS (
|
||||
SELECT
|
||||
waktu_group,
|
||||
${tagNames}
|
||||
FROM Averaged
|
||||
PIVOT (
|
||||
MAX(avg_val)
|
||||
FOR tag_name IN (${tagNames})
|
||||
) AS p
|
||||
),
|
||||
FinalResult AS (
|
||||
SELECT
|
||||
CONVERT(VARCHAR(16), ts.waktu, 120) AS datetime,
|
||||
${tagNames}
|
||||
FROM TimeSeries ts
|
||||
LEFT JOIN Pivoted p ON ts.waktu = p.waktu_group
|
||||
)
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
*
|
||||
FROM FinalResult
|
||||
ORDER BY datetime
|
||||
${searchParams.limit ? `OFFSET ${(page - 1) * limit} ROWS FETCH NEXT ${limit} ROWS ONLY` : ''}
|
||||
OPTION (MAXRECURSION 0);
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText);
|
||||
|
||||
const total =
|
||||
result?.recordset?.length > 0
|
||||
? parseInt(result.recordset[0].total_data, 10)
|
||||
: 0;
|
||||
|
||||
return { data: result.recordset, column: tagNamesColumn, total };
|
||||
};
|
||||
|
||||
const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
||||
@@ -282,18 +443,55 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
||||
if (from.length === 10) from += ' 00:00:00';
|
||||
if (to.length === 10) to += ' 23:59:59';
|
||||
|
||||
// --- Ambil semua tag yang di-report
|
||||
const tags = await pool.query(`
|
||||
SELECT tag_name
|
||||
let tagQueryParams = [];
|
||||
let tagWhereConditions = [];
|
||||
|
||||
if (searchParams.plant_sub_section_id) {
|
||||
tagWhereConditions.push(`plant_sub_section_id = $${tagQueryParams.length + 1}`);
|
||||
tagQueryParams.push(searchParams.plant_sub_section_id);
|
||||
}
|
||||
|
||||
if (searchParams.plant_section_id) {
|
||||
tagWhereConditions.push(`plant_section_id = $${tagQueryParams.length + 1}`);
|
||||
tagQueryParams.push(searchParams.plant_section_id);
|
||||
}
|
||||
|
||||
if (searchParams.name) {
|
||||
const nameFilter = `(tag_name LIKE $${tagQueryParams.length + 1} OR CAST(tag_number AS VARCHAR) LIKE $${tagQueryParams.length + 2})`;
|
||||
tagWhereConditions.push(nameFilter);
|
||||
tagQueryParams.push(`%${searchParams.name}%`, `%${searchParams.name}%`);
|
||||
}
|
||||
|
||||
if (searchParams.criteria) {
|
||||
const criteriaFilter = `(tag_name LIKE $${tagQueryParams.length + 1} OR CAST(tag_number AS VARCHAR) LIKE $${tagQueryParams.length + 2})`;
|
||||
tagWhereConditions.push(criteriaFilter);
|
||||
tagQueryParams.push(`%${searchParams.criteria}%`, `%${searchParams.criteria}%`);
|
||||
}
|
||||
|
||||
const tagWhereClause = tagWhereConditions.length > 0
|
||||
? ` AND ${tagWhereConditions.join(" AND ")}`
|
||||
: '';
|
||||
|
||||
const tagsQuery = `
|
||||
SELECT tag_name, tag_number
|
||||
FROM m_tags
|
||||
WHERE is_report = 1 AND deleted_at IS NULL
|
||||
`);
|
||||
${tagWhereClause}
|
||||
ORDER BY tag_name
|
||||
`;
|
||||
|
||||
const tags = await pool.query(tagsQuery, tagQueryParams);
|
||||
|
||||
if (tags.recordset.length === 0) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
const tagNames = tags.recordset.map(r => `[${r.tag_name}]`).join(', ');
|
||||
const tagNumbers = tags.recordset.map(r => r.tag_number);
|
||||
|
||||
const tagNumbersFilter = tagNumbers.length > 0
|
||||
? ` AND a.tagnum IN (${tagNumbers.join(',')})`
|
||||
: '';
|
||||
|
||||
const queryText = `
|
||||
DECLARE
|
||||
@@ -316,6 +514,7 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
||||
FROM ${tableName} a
|
||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
||||
WHERE a.datetime BETWEEN @fromParam AND @toParam
|
||||
${tagNumbersFilter}
|
||||
GROUP BY
|
||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0),
|
||||
b.tag_name
|
||||
@@ -359,7 +558,6 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
||||
return { data: nivoData };
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
getHistoryAlarmDb,
|
||||
getHistoryEventDb,
|
||||
@@ -367,4 +565,4 @@ module.exports = {
|
||||
getHistoryValueReportDb,
|
||||
getHistoryValueReportPivotDb,
|
||||
getHistoryValueTrendingPivotDb
|
||||
};
|
||||
};
|
||||
@@ -1,71 +1,52 @@
|
||||
const pool = require("../config");
|
||||
|
||||
const InsertNotificationErrorDb = async () => {
|
||||
const insertQuery = `
|
||||
INSERT INTO notification_error (
|
||||
error_code_id,
|
||||
is_active,
|
||||
is_delivered,
|
||||
is_read,
|
||||
is_send,
|
||||
message_error_issue
|
||||
)
|
||||
SELECT
|
||||
b.error_code_id,
|
||||
1 AS is_active,
|
||||
1 AS is_delivered,
|
||||
0 AS is_read,
|
||||
1 AS is_send,
|
||||
const InsertNotificationErrorDb = async (store) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert(
|
||||
"notification_error",
|
||||
store
|
||||
);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||
|
||||
CONCAT(
|
||||
COALESCE(b.error_code_description, '-'),
|
||||
' pada ',
|
||||
COALESCE(d.device_name, '-'),
|
||||
'. Pengecekan potensi kerusakan dibutuhkan'
|
||||
) AS message_error_issue
|
||||
|
||||
FROM brand_code b
|
||||
|
||||
LEFT JOIN notification_error a
|
||||
ON a.error_code_id = b.error_code_id
|
||||
AND a.deleted_at IS NULL
|
||||
|
||||
LEFT JOIN m_device d
|
||||
ON b.brand_id = d.brand_id
|
||||
AND d.deleted_at IS NULL
|
||||
|
||||
WHERE b.deleted_at IS NULL
|
||||
AND a.notification_error_id IS NULL;
|
||||
`;
|
||||
|
||||
await pool.query(insertQuery);
|
||||
return insertedId ? await getNotificationByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const getNotificationByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*
|
||||
a.*,
|
||||
b.plant_sub_section_id,
|
||||
c.plant_sub_section_name,
|
||||
d.device_code,
|
||||
d.device_name,
|
||||
d.device_location,
|
||||
d.listen_channel,
|
||||
e.brand_name
|
||||
|
||||
FROM notification_error a
|
||||
|
||||
LEFT JOIN m_tags b
|
||||
ON a.error_chanel = b.tag_number
|
||||
|
||||
LEFT JOIN m_plant_sub_section c
|
||||
ON b.plant_sub_section_id = c.plant_sub_section_id
|
||||
|
||||
LEFT JOIN m_device d
|
||||
ON a.error_chanel = d.listen_channel AND d.deleted_at IS NULL
|
||||
|
||||
LEFT JOIN m_brands e
|
||||
ON d.brand_id = e.brand_id AND d.deleted_at IS NULL
|
||||
|
||||
WHERE a.notification_error_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset[0];
|
||||
};
|
||||
|
||||
|
||||
const getAllNotificationDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
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];
|
||||
@@ -73,13 +54,13 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"a.message_error_issue",
|
||||
"a.is_send",
|
||||
"a.is_delivered",
|
||||
"a.is_read",
|
||||
"a.is_active",
|
||||
"b.error_code",
|
||||
"b.error_code_name",
|
||||
"c.solution_name",
|
||||
"COALESCE(a.is_send, 0)",
|
||||
"COALESCE(a.is_delivered, 0)",
|
||||
"COALESCE(a.is_read, 0)",
|
||||
"COALESCE(a.is_active, 0)",
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
@@ -88,10 +69,13 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ 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" },
|
||||
{ column: "a.message_error_issue", param: searchParams.message_error_issue, type: "string" },
|
||||
{ column: "a.is_send", param: searchParams.is_send, type: "number" },
|
||||
{ column: "a.is_delivered", param: searchParams.is_delivered, type: "number" },
|
||||
{ column: "a.is_read", param: searchParams.is_read, type: "number" },
|
||||
{ column: "a.is_active", param: searchParams.is_active, type: "number" },
|
||||
{ column: "b.error_code", param: searchParams.error_code, type: "string" },
|
||||
{ column: "b.error_code_name", param: searchParams.error_code_name, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
@@ -111,24 +95,35 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
||||
|
||||
b.error_code,
|
||||
b.error_code_name,
|
||||
b.error_code_color,
|
||||
b.path_icon,
|
||||
b.created_at,
|
||||
|
||||
c.solution_name,
|
||||
c.type_solution,
|
||||
c.path_solution,
|
||||
|
||||
d.device_code,
|
||||
d.device_name,
|
||||
d.device_location,
|
||||
d.listen_channel,
|
||||
e.brand_name,
|
||||
|
||||
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
|
||||
LEFT JOIN brand_code_solution c
|
||||
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
|
||||
FROM notification_error a
|
||||
|
||||
LEFT JOIN brand_code b
|
||||
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
|
||||
|
||||
LEFT JOIN m_device d
|
||||
ON a.error_chanel = d.listen_channel AND d.deleted_at IS NULL
|
||||
|
||||
LEFT JOIN m_brands e
|
||||
ON d.brand_id = e.brand_id AND d.deleted_at IS NULL
|
||||
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
@@ -149,8 +144,46 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
||||
return { data: result.recordset, total };
|
||||
};
|
||||
|
||||
const updateNotificationErrorDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { notification_error_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"notification_error",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getNotificationByIdDb(id);
|
||||
};
|
||||
|
||||
const getUsersNotificationErrorDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
b.notification_error_id,
|
||||
a.notification_error_user_id,
|
||||
a.contact_phone,
|
||||
a.contact_name,
|
||||
a.is_send
|
||||
|
||||
FROM notification_error_user a
|
||||
|
||||
LEFT JOIN notification_error b ON a.notification_error_id = b.notification_error_id
|
||||
|
||||
WHERE a.notification_error_id = $1
|
||||
AND a.deleted_at IS NULL
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getNotificationByIdDb,
|
||||
getAllNotificationDb,
|
||||
InsertNotificationErrorDb,
|
||||
updateNotificationErrorDb,
|
||||
getUsersNotificationErrorDb
|
||||
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ const getAllNotificationErrorLogDb = async () => {
|
||||
b.contact_name,
|
||||
b.contact_type
|
||||
FROM notification_error_log a
|
||||
LEFT JOIN contact b ON a.contact_id = b.contact_id
|
||||
LEFT JOIN contact b ON a.contact_phone = b.contact_phone
|
||||
WHERE a.deleted_at IS NULL
|
||||
ORDER BY a.notification_error_log_id DESC
|
||||
`;
|
||||
@@ -22,7 +22,7 @@ const getNotificationErrorLogByIdDb = async (id) => {
|
||||
b.contact_name,
|
||||
b.contact_type
|
||||
FROM notification_error_log a
|
||||
LEFT JOIN contact b ON a.contact_id = b.contact_id
|
||||
LEFT JOIN contact b ON a.contact_phone = b.contact_phone
|
||||
WHERE a.notification_error_log_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
@@ -36,7 +36,7 @@ const getNotificationErrorLogByNotificationErrorIdDb = async (notificationErrorI
|
||||
b.contact_name,
|
||||
b.contact_type
|
||||
FROM notification_error_log a
|
||||
LEFT JOIN contact b ON a.contact_id = b.contact_id
|
||||
LEFT JOIN contact b ON a.contact_phone = b.contact_phone
|
||||
WHERE a.notification_error_id = $1 AND a.deleted_at IS NULL
|
||||
ORDER BY a.created_at DESC
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
const pool = require("../config");
|
||||
|
||||
const insertNotificationErrorSparepartDb = async () => {
|
||||
const insertQuery = `
|
||||
INSERT INTO notification_error_sparepart (
|
||||
notification_error_id,
|
||||
brand_sparepart_id
|
||||
)
|
||||
SELECT
|
||||
ne.notification_error_id,
|
||||
bs.brand_sparepart_id
|
||||
|
||||
FROM notification_error ne
|
||||
|
||||
INNER JOIN brand_sparepart bs
|
||||
ON ne.error_code_id = bs.error_code_id
|
||||
|
||||
LEFT JOIN notification_error_sparepart nes
|
||||
ON nes.notification_error_id = ne.notification_error_id
|
||||
AND nes.brand_sparepart_id = bs.brand_sparepart_id
|
||||
AND nes.deleted_at IS NULL
|
||||
|
||||
WHERE ne.deleted_at IS NULL
|
||||
AND nes.notification_error_sparepart_id IS NULL;
|
||||
`;
|
||||
|
||||
await pool.query(insertQuery);
|
||||
};
|
||||
|
||||
const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||
await insertNotificationErrorSparepartDb();
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
@@ -11,10 +39,10 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"a.brand_sparepart_id",
|
||||
"a.contact_id",
|
||||
"a.device_id",
|
||||
"a.sparepart_id",
|
||||
"b.sparepart_name",
|
||||
"d.contact_name",
|
||||
"d.contact_type"
|
||||
"d.device_name",
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
@@ -25,11 +53,10 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.brand_sparepart_id", param: searchParams.name, type: "int" },
|
||||
{ column: "a.contact_id", param: searchParams.code, type: "int" },
|
||||
{ column: "a.device_id", param: searchParams.code, type: "int" },
|
||||
{ column: "a.unit", param: searchParams.unit, type: "string" },
|
||||
{ column: "b.sparepart_name", param: searchParams.device, type: "string" },
|
||||
{ column: "d.contact_name", param: searchParams.device, type: "string" },
|
||||
{ column: "d.contact_type", param: searchParams.device, type: "string" },
|
||||
{ column: "d.device_name", param: searchParams.device, type: "string" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
@@ -41,14 +68,23 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*,
|
||||
b.sparepart_name,
|
||||
b.brand_sparepart_description,
|
||||
d.contact_name,
|
||||
d.contact_type
|
||||
b.sparepart_foto,
|
||||
b.sparepart_stok,
|
||||
b.sparepart_qty,
|
||||
b.sparepart_description,
|
||||
b.sparepart_model,
|
||||
b.sparepart_merk,
|
||||
b.sparepart_unit,
|
||||
b.sparepart_item_type,
|
||||
d.device_name
|
||||
|
||||
FROM notification_error_sparepart a
|
||||
|
||||
LEFT JOIN brand_sparepart b ON a.brand_sparepart_id = b.brand_sparepart_id
|
||||
LEFT JOIN brand_sparepart c ON a.brand_sparepart_id = c.brand_sparepart_id
|
||||
|
||||
LEFT JOIN contact d on a.contact_id = d.contact_id
|
||||
LEFT JOIN m_sparepart b ON c.sparepart_id = b.sparepart_id
|
||||
|
||||
LEFT JOIN m_device d on c.device_id = d.device_id
|
||||
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
@@ -69,15 +105,27 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||
|
||||
const getNotificationErrorSparepartByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
SELECT
|
||||
a.*,
|
||||
b.sparepart_name,
|
||||
b.brand_sparepart_description,
|
||||
d.contact_name,
|
||||
d.contact_type
|
||||
b.sparepart_foto,
|
||||
b.sparepart_stok,
|
||||
b.sparepart_qty,
|
||||
b.sparepart_description,
|
||||
b.sparepart_model,
|
||||
b.sparepart_merk,
|
||||
b.sparepart_unit,
|
||||
b.sparepart_item_type,
|
||||
d.device_name
|
||||
|
||||
FROM notification_error_sparepart a
|
||||
LEFT JOIN brand_sparepart b ON a.brand_sparepart_id = b.brand_sparepart_id
|
||||
LEFT JOIN contact d on a.contact_id = d.contact_id
|
||||
|
||||
LEFT JOIN brand_sparepart c ON a.brand_sparepart_id = c.brand_sparepart_id
|
||||
|
||||
LEFT JOIN m_sparepart b ON c.sparepart_id = b.sparepart_id
|
||||
|
||||
LEFT JOIN m_device d on c.device_id = d.device_id
|
||||
|
||||
WHERE a.notification_error_sparepart_id = $1
|
||||
AND a.deleted_at IS NULL
|
||||
`;
|
||||
|
||||
105
db/notification_error_user.db.js
Normal file
105
db/notification_error_user.db.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const pool = require("../config");
|
||||
|
||||
// Get all Notification
|
||||
const getAllNotificationErrorUserDb = async (searchParams = {}) => {
|
||||
let queryParams = [];
|
||||
|
||||
if (searchParams.limit) {
|
||||
const page = Number(searchParams.page ?? 1) - 1;
|
||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||
}
|
||||
|
||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||
[
|
||||
"a.notification_error_id",
|
||||
"a.contact_id",
|
||||
],
|
||||
searchParams.criteria,
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamOr) queryParams = whereParamOr;
|
||||
|
||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||
[
|
||||
{ column: "a.notification_error_id", param: searchParams.name, type: "int" },
|
||||
{ column: "a.contact_id", param: searchParams.code, type: "int" },
|
||||
],
|
||||
queryParams
|
||||
);
|
||||
|
||||
if (whereParamAnd) queryParams = whereParamAnd;
|
||||
|
||||
const queryText = `
|
||||
SELECT
|
||||
COUNT(*) OVER() AS total_data,
|
||||
a.*
|
||||
FROM notification_error_user a
|
||||
WHERE a.deleted_at IS NULL
|
||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||
${whereOrConditions ? ` ${whereOrConditions}` : ""}
|
||||
ORDER BY a.notification_error_user_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 getNotificationErrorUserByIdDb = async (id) => {
|
||||
const queryText = `
|
||||
SELECT
|
||||
a.*
|
||||
FROM notification_error_user a
|
||||
WHERE a.notification_error_user_id = $1 AND a.deleted_at IS NULL
|
||||
`;
|
||||
const result = await pool.query(queryText, [id]);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
const createNotificationErrorUserDb = async (store) => {
|
||||
const { query: queryText, values } = pool.buildDynamicInsert("notification_error_user", store);
|
||||
const result = await pool.query(queryText, values);
|
||||
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||
|
||||
return insertedId ? await getNotificationErrorUserByIdDb(insertedId) : null;
|
||||
};
|
||||
|
||||
const updateNotificationErrorUserDb = async (id, data) => {
|
||||
const store = { ...data };
|
||||
const whereData = { notification_error_user_id: id };
|
||||
|
||||
const { query: queryText, values } = pool.buildDynamicUpdate(
|
||||
"notification_error_user",
|
||||
store,
|
||||
whereData
|
||||
);
|
||||
|
||||
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
|
||||
return getNotificationErrorUserByIdDb(id);
|
||||
};
|
||||
|
||||
// Soft delete tag
|
||||
const deleteNotificationErrorUserDb = async (id, deletedBy) => {
|
||||
const queryText = `
|
||||
UPDATE notification_error_user
|
||||
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
|
||||
WHERE notification_error_user_id = $2 AND deleted_at IS NULL
|
||||
`;
|
||||
await pool.query(queryText, [deletedBy, id]);
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllNotificationErrorUserDb,
|
||||
getNotificationErrorUserByIdDb,
|
||||
createNotificationErrorUserDb,
|
||||
updateNotificationErrorUserDb,
|
||||
deleteNotificationErrorUserDb,
|
||||
};
|
||||
59
db/notification_wa.db.js
Normal file
59
db/notification_wa.db.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const { default: axios } = require('axios');
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
const generateTokenRedirect = async (userPhone, userName, id) => {
|
||||
|
||||
const plain = {
|
||||
user_phone: userPhone,
|
||||
user_name: userName,
|
||||
id
|
||||
}
|
||||
|
||||
const tokenCrypt = CryptoJS.AES.encrypt(JSON.stringify(plain), process.env.VITE_KEY_SESSION).toString();
|
||||
return tokenCrypt
|
||||
}
|
||||
|
||||
const shortUrltiny = async (encodedToken) => {
|
||||
const url = `${process.env.ENDPOINT_FE}/redirect?token=${encodedToken}`
|
||||
|
||||
const encodedUrl = encodeURIComponent(url); // ⬅️ Encode dulu!
|
||||
|
||||
const response = await axios.get(`https://tinyurl.com/api-create.php?url=${encodedUrl}`);
|
||||
|
||||
let shortUrl = response.data;
|
||||
if (!shortUrl.startsWith('http')) {
|
||||
shortUrl = 'https://' + shortUrl;
|
||||
}
|
||||
|
||||
return shortUrl
|
||||
}
|
||||
|
||||
const sendNotifikasi = async (phone, message) => {
|
||||
const payload = {
|
||||
phone: phone,
|
||||
message: message
|
||||
};
|
||||
|
||||
// console.log('payload', payload);
|
||||
|
||||
const endPointWhatsapp = process.env.ENDPOINT_WHATSAPP;
|
||||
|
||||
const response = await axios.post(endPointWhatsapp, payload);
|
||||
// console.log('response', response);
|
||||
|
||||
try {
|
||||
const response = await axios.post(endPointWhatsapp, payload);
|
||||
// console.log(response.data);
|
||||
return response?.data
|
||||
} catch (error) {
|
||||
// console.error(error.response?.data || error.message);
|
||||
return error.response?.data || error.message
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
generateTokenRedirect,
|
||||
shortUrltiny,
|
||||
sendNotifikasi,
|
||||
};
|
||||
@@ -147,9 +147,39 @@ const deleteSparepartDb = async (id, deletedBy) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Get multiple spareparts by IDs
|
||||
const getSparepartsByIdsDb = async (sparepartIds) => {
|
||||
if (!sparepartIds || sparepartIds.length === 0) return [];
|
||||
|
||||
const placeholders = sparepartIds.map((_, index) => `$${index + 1}`).join(', ');
|
||||
const queryText = `
|
||||
SELECT
|
||||
sparepart_id,
|
||||
sparepart_name,
|
||||
sparepart_code,
|
||||
sparepart_description,
|
||||
sparepart_model,
|
||||
sparepart_foto,
|
||||
sparepart_item_type,
|
||||
sparepart_qty,
|
||||
sparepart_unit,
|
||||
sparepart_merk,
|
||||
sparepart_stok,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM m_sparepart
|
||||
WHERE sparepart_id IN (${placeholders})
|
||||
AND deleted_at IS NULL
|
||||
`;
|
||||
|
||||
const result = await pool.query(queryText, sparepartIds);
|
||||
return result.recordset;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getAllSparepartDb,
|
||||
getSparepartByIdDb,
|
||||
getSparepartsByIdsDb,
|
||||
checkSparepartNameExistsDb,
|
||||
createSparepartDb,
|
||||
updateSparepartDb,
|
||||
|
||||
5
middleware/upload.js
Normal file
5
middleware/upload.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const multer = require("multer");
|
||||
|
||||
const storage = multer.memoryStorage();
|
||||
|
||||
module.exports = multer({ storage });
|
||||
@@ -7,5 +7,6 @@ router.post('/login', AuthController.login);
|
||||
router.post('/register', AuthController.register);
|
||||
router.get('/generate-captcha', AuthController.generateCaptcha);
|
||||
router.post('/refresh-token', AuthController.refreshToken);
|
||||
router.post('/verify-redirect', AuthController.verifyTokenRedirect);
|
||||
|
||||
module.exports = router;
|
||||
@@ -2,7 +2,6 @@ const express = require('express');
|
||||
const BrandController = require('../controllers/brand.controller');
|
||||
const verifyToken = require('../middleware/verifyToken');
|
||||
const verifyAccess = require('../middleware/verifyAccess');
|
||||
const upload = require('../middleware/uploads');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -12,7 +11,7 @@ router.route('/')
|
||||
|
||||
router.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, BrandController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), upload.single('file'), BrandController.update)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), BrandController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), BrandController.delete);
|
||||
|
||||
module.exports = router;
|
||||
19
routes/error_code.route.js
Normal file
19
routes/error_code.route.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const express = require('express');
|
||||
const ErrorCodeController = require('../controllers/error_code.controller');
|
||||
const verifyToken = require('../middleware/verifyToken');
|
||||
const verifyAccess = require('../middleware/verifyAccess');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route('/brand/:brandId')
|
||||
.get(verifyToken.verifyAccessToken, ErrorCodeController.getByBrandId)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), ErrorCodeController.create);
|
||||
|
||||
router.route('/brand/:brandId/:errorCodeId')
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), ErrorCodeController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), ErrorCodeController.delete);
|
||||
|
||||
router.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, ErrorCodeController.getById);
|
||||
|
||||
module.exports = router;
|
||||
@@ -18,6 +18,8 @@ const notificationError = require("./notification_error.route")
|
||||
const notificationErrorSparepart = require("./notification_error_sparepart.route")
|
||||
const sparepart = require("./sparepart.route")
|
||||
const notificationErrorLog = require("./notification_error_log.route")
|
||||
const notificationErrorUser = require("./notification_error_user.route")
|
||||
const errorCode = require("./error_code.route")
|
||||
|
||||
router.use("/auth", auth);
|
||||
router.use("/user", users);
|
||||
@@ -38,5 +40,7 @@ router.use("/notification", notificationError)
|
||||
router.use("/notification-sparepart", notificationErrorSparepart)
|
||||
router.use("/sparepart", sparepart)
|
||||
router.use("/notification-log", notificationErrorLog)
|
||||
router.use("/notification-user", notificationErrorUser)
|
||||
router.use("/error-code", errorCode)
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -7,10 +7,15 @@ const router = express.Router();
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.get(verifyToken.verifyAccessToken, NotificationErrorController.getAll)
|
||||
.get(verifyToken.verifyAccessToken,verifyAccess(), NotificationErrorController.getAll)
|
||||
|
||||
router
|
||||
.route('/')
|
||||
.post(verifyToken.verifyAccessToken,verifyAccess(), NotificationErrorController.create)
|
||||
|
||||
router
|
||||
.route('/:id')
|
||||
.get(verifyToken.verifyAccessToken, NotificationErrorController.getById)
|
||||
.get(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorController.update)
|
||||
|
||||
module.exports = router;
|
||||
|
||||
17
routes/notification_error_user.route.js
Normal file
17
routes/notification_error_user.route.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const express = require('express');
|
||||
const NotificationErrorUserController = require('../controllers/notification_error_user.controller');
|
||||
const verifyToken = require("../middleware/verifyToken")
|
||||
const verifyAccess = require("../middleware/verifyAccess")
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/")
|
||||
.get(verifyToken.verifyAccessToken, NotificationErrorUserController.getAll)
|
||||
.post(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorUserController.create);
|
||||
|
||||
router.route("/:id")
|
||||
.get(verifyToken.verifyAccessToken, NotificationErrorUserController.getById)
|
||||
.put(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorUserController.update)
|
||||
.delete(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorUserController.delete);
|
||||
|
||||
module.exports = router;
|
||||
@@ -2,7 +2,8 @@ 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 upload = require("../middleware/upload");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get(
|
||||
@@ -11,6 +12,14 @@ router.get(
|
||||
SparepartController.exportExcel
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/import",
|
||||
verifyToken.verifyAccessToken,
|
||||
verifyAccess(),
|
||||
upload.single("file"),
|
||||
SparepartController.importExcel
|
||||
);
|
||||
|
||||
router
|
||||
.route("/")
|
||||
.get(verifyToken.verifyAccessToken, SparepartController.getAll)
|
||||
@@ -36,4 +45,4 @@ router
|
||||
SparepartController.delete
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
@@ -2,36 +2,11 @@
|
||||
const {
|
||||
getAllBrandsDb,
|
||||
getBrandByIdDb,
|
||||
getBrandByNameDb,
|
||||
createBrandDb,
|
||||
updateBrandDb,
|
||||
deleteBrandDb,
|
||||
checkBrandNameExistsDb,
|
||||
} = require("../db/brand.db");
|
||||
|
||||
const {
|
||||
insertMultipleBrandSparepartsDb,
|
||||
updateBrandSparepartsDb,
|
||||
deleteAllBrandSparepartsDb,
|
||||
getSparepartsByBrandIdDb,
|
||||
} = require("../db/brand_sparepart.db");
|
||||
|
||||
// Error code operations
|
||||
const {
|
||||
getErrorCodesByBrandIdDb,
|
||||
createErrorCodeDb,
|
||||
updateErrorCodeDb,
|
||||
deleteErrorCodeDb,
|
||||
} = 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");
|
||||
|
||||
class BrandService {
|
||||
@@ -40,86 +15,22 @@ class BrandService {
|
||||
try {
|
||||
const results = await getAllBrandsDb(param);
|
||||
|
||||
// Add spareparts data for each brand
|
||||
const brandsWithSpareparts = await Promise.all(
|
||||
results.data.map(async (brand) => {
|
||||
const spareparts = await getSparepartsByBrandIdDb(brand.brand_id);
|
||||
return {
|
||||
...brand,
|
||||
spareparts: spareparts
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...results,
|
||||
data: brandsWithSpareparts
|
||||
data: results.data
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get brand by ID with complete data
|
||||
// Get brand by ID (without error codes)
|
||||
static async getBrandById(id) {
|
||||
try {
|
||||
const brand = await getBrandByIdDb(id);
|
||||
if (!brand) throw new ErrorHandler(404, "Brand not found");
|
||||
|
||||
// Get spareparts for this brand
|
||||
const spareparts = await getSparepartsByBrandIdDb(brand.brand_id);
|
||||
|
||||
const errorCodes = await getErrorCodesByBrandIdDb(brand.brand_id);
|
||||
|
||||
const errorCodesWithSolutions = await Promise.all(
|
||||
errorCodes.map(async (errorCode) => {
|
||||
const solutions = await getSolutionsByErrorCodeIdDb(
|
||||
errorCode.error_code_id
|
||||
);
|
||||
|
||||
const solutionsWithFiles = await Promise.all(
|
||||
solutions.map(async (solution) => {
|
||||
let 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);
|
||||
}
|
||||
|
||||
const enhancedSolution = {
|
||||
...solution,
|
||||
file_upload_name: fileData?.file_upload_name || 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;
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...errorCode,
|
||||
solution: solutionsWithFiles,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...brand,
|
||||
spareparts: spareparts,
|
||||
error_code: errorCodesWithSolutions,
|
||||
};
|
||||
return brand;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
@@ -127,10 +38,8 @@ class BrandService {
|
||||
|
||||
|
||||
// Create brand
|
||||
static async createBrandWithFullData(data) {
|
||||
static async createBrand(data) {
|
||||
try {
|
||||
if (!data || typeof data !== "object") data = {};
|
||||
|
||||
if (data.brand_name) {
|
||||
const brandExists = await checkBrandNameExistsDb(data.brand_name);
|
||||
if (brandExists) {
|
||||
@@ -138,84 +47,23 @@ class BrandService {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
brand_manufacture: data.brand_manufacture,
|
||||
brand_model: data.brand_model,
|
||||
is_active: data.is_active,
|
||||
is_active: data.is_active !== undefined ? data.is_active : true,
|
||||
created_by: data.created_by,
|
||||
};
|
||||
|
||||
const createdBrand = await createBrandDb(brandData);
|
||||
if (!createdBrand) {
|
||||
throw new Error("Failed to create brand");
|
||||
throw new ErrorHandler(500, "Failed to create brand");
|
||||
}
|
||||
|
||||
const brandId = createdBrand.brand_id;
|
||||
|
||||
if (data.spareparts && Array.isArray(data.spareparts) && data.spareparts.length > 0) {
|
||||
await insertMultipleBrandSparepartsDb(brandId, data.spareparts, data.created_by);
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
if (!errorId) {
|
||||
throw new Error("Failed to create error code");
|
||||
}
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const createdBrandWithSpareparts = await this.getBrandById(brandId);
|
||||
return createdBrandWithSpareparts;
|
||||
return createdBrand;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(500, `Bulk insert failed: ${error.message}`);
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,8 +84,9 @@ class BrandService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update brand
|
||||
static async updateBrandWithFullData(id, data) {
|
||||
static async updateBrand(id, data) {
|
||||
try {
|
||||
const existingBrand = await getBrandByIdDb(id);
|
||||
if (!existingBrand) throw new ErrorHandler(404, "Brand not found");
|
||||
@@ -250,143 +99,22 @@ class BrandService {
|
||||
}
|
||||
|
||||
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,
|
||||
brand_name: data.brand_name || existingBrand.brand_name,
|
||||
brand_type: data.brand_type !== undefined ? data.brand_type : existingBrand.brand_type,
|
||||
brand_manufacture: data.brand_manufacture !== undefined ? data.brand_manufacture : existingBrand.brand_manufacture,
|
||||
brand_model: data.brand_model !== undefined ? data.brand_model : existingBrand.brand_model,
|
||||
is_active: data.is_active !== undefined ? data.is_active : existingBrand.is_active,
|
||||
updated_by: data.updated_by,
|
||||
};
|
||||
|
||||
await updateBrandDb(existingBrand.brand_name, brandData);
|
||||
|
||||
if (data.spareparts !== undefined) {
|
||||
await updateBrandSparepartsDb(existingBrand.brand_id, data.spareparts || [], data.updated_by);
|
||||
const updatedBrand = await updateBrandDb(existingBrand.brand_name, brandData);
|
||||
if (!updatedBrand) {
|
||||
throw new ErrorHandler(500, "Failed to update brand");
|
||||
}
|
||||
|
||||
if (data.error_code && Array.isArray(data.error_code)) {
|
||||
const existingErrorCodes = await getErrorCodesByBrandIdDb(id);
|
||||
const incomingErrorCodes = data.error_code.map((ec) => ec.error_code);
|
||||
|
||||
// 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) {
|
||||
// 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 (
|
||||
errorCodeData.solution &&
|
||||
Array.isArray(errorCodeData.solution)
|
||||
) {
|
||||
const existingSolutions = await getSolutionsByErrorCodeIdDb(
|
||||
existingEC.error_code_id
|
||||
);
|
||||
const incomingSolutionNames = errorCodeData.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
|
||||
);
|
||||
|
||||
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 {
|
||||
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 existingEC of existingErrorCodes) {
|
||||
if (!incomingErrorCodes.includes(existingEC.error_code)) {
|
||||
await deleteErrorCodeDb(id, existingEC.error_code, data.updated_by);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedBrandWithSpareparts = await this.getBrandById(id);
|
||||
return updatedBrandWithSpareparts;
|
||||
return updatedBrand;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(500, `Update failed: ${error.message}`);
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
276
services/error_code.service.js
Normal file
276
services/error_code.service.js
Normal file
@@ -0,0 +1,276 @@
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
const {
|
||||
getErrorCodesByBrandIdDb,
|
||||
getErrorCodeByIdDb,
|
||||
getErrorCodeByBrandAndCodeDb,
|
||||
createErrorCodeDb,
|
||||
updateErrorCodeDb,
|
||||
deleteErrorCodeDb,
|
||||
getAllErrorCodesDb,
|
||||
} = require("../db/brand_code.db");
|
||||
|
||||
const {
|
||||
getSolutionsByErrorCodeIdDb,
|
||||
createSolutionDb,
|
||||
updateSolutionDb,
|
||||
deleteSolutionDb,
|
||||
} = require("../db/brand_code_solution.db");
|
||||
|
||||
const {
|
||||
getSparepartsByErrorCodeIdDb,
|
||||
insertMultipleErrorCodeSparepartsDb,
|
||||
updateErrorCodeSparepartsDb,
|
||||
} = require("../db/brand_sparepart.db");
|
||||
|
||||
const { getFileUploadByPathDb } = require("../db/file_uploads.db");
|
||||
|
||||
class ErrorCodeService {
|
||||
static async getAllErrorCodes(param) {
|
||||
try {
|
||||
const results = await getAllErrorCodesDb(param);
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get error code by ID with complete data
|
||||
static async getErrorCodeById(id) {
|
||||
try {
|
||||
const errorCode = await getErrorCodeByIdDb(id);
|
||||
if (!errorCode) throw new ErrorHandler(404, "Error code not found");
|
||||
|
||||
const solutions = await getSolutionsByErrorCodeIdDb(errorCode.error_code_id);
|
||||
const spareparts = await getSparepartsByErrorCodeIdDb(errorCode.error_code_id);
|
||||
|
||||
const solutionsWithFiles = await Promise.all(
|
||||
solutions.map(async (solution) => {
|
||||
let fileData = null;
|
||||
|
||||
if (solution.path_solution && solution.type_solution !== "text") {
|
||||
fileData = await getFileUploadByPathDb(solution.path_solution);
|
||||
}
|
||||
|
||||
return {
|
||||
...solution,
|
||||
file_upload_name: fileData?.file_upload_name || null,
|
||||
path_document: fileData?.path_document || null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...errorCode,
|
||||
solution: solutionsWithFiles,
|
||||
spareparts: spareparts,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get error codes by brand ID
|
||||
static async getErrorCodesByBrandId(brandId, queryParams = {}) {
|
||||
try {
|
||||
const results = await getErrorCodesByBrandIdDb(brandId, queryParams);
|
||||
|
||||
if (results.data && results.total !== undefined) {
|
||||
return results;
|
||||
}
|
||||
|
||||
const errorCodesWithDetails = await Promise.all(
|
||||
results.map(async (errorCode) => {
|
||||
const solutions = await getSolutionsByErrorCodeIdDb(errorCode.error_code_id);
|
||||
const spareparts = await getSparepartsByErrorCodeIdDb(errorCode.error_code_id);
|
||||
|
||||
const solutionsWithFiles = await Promise.all(
|
||||
solutions.map(async (solution) => {
|
||||
let fileData = null;
|
||||
|
||||
if (solution.path_solution && solution.type_solution !== "text") {
|
||||
fileData = await getFileUploadByPathDb(solution.path_solution);
|
||||
}
|
||||
|
||||
return {
|
||||
...solution,
|
||||
file_upload_name: fileData?.file_upload_name || null,
|
||||
path_document: fileData?.path_document || null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
...errorCode,
|
||||
solution: solutionsWithFiles,
|
||||
spareparts: spareparts,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return errorCodesWithDetails;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create error code with solutions and spareparts
|
||||
static async createErrorCodeWithFullData(brandId, data) {
|
||||
try {
|
||||
if (!data || typeof data !== "object") data = {};
|
||||
|
||||
if (
|
||||
!data.solution ||
|
||||
!Array.isArray(data.solution) ||
|
||||
data.solution.length === 0
|
||||
) {
|
||||
throw new ErrorHandler(
|
||||
400,
|
||||
"Error code must have at least 1 solution"
|
||||
);
|
||||
}
|
||||
|
||||
const errorId = await createErrorCodeDb(brandId, {
|
||||
error_code: data.error_code,
|
||||
error_code_name: data.error_code_name,
|
||||
error_code_description: data.error_code_description,
|
||||
error_code_color: data.error_code_color,
|
||||
path_icon: data.path_icon,
|
||||
is_active: data.is_active,
|
||||
created_by: data.created_by,
|
||||
});
|
||||
|
||||
if (!errorId) {
|
||||
throw new Error("Failed to create error code");
|
||||
}
|
||||
if (data.spareparts && Array.isArray(data.spareparts)) {
|
||||
await insertMultipleErrorCodeSparepartsDb(errorId, data.spareparts, data.created_by);
|
||||
}
|
||||
|
||||
if (data.solution && Array.isArray(data.solution)) {
|
||||
for (const solutionData of data.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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const createdErrorCode = await this.getErrorCodeById(errorId);
|
||||
return createdErrorCode;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(500, `Create error code failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update error code with solutions and spareparts
|
||||
static async updateErrorCodeWithFullData(brandId, errorCodeId, data) {
|
||||
try {
|
||||
const existingErrorCode = await getErrorCodeByIdDb(errorCodeId);
|
||||
if (!existingErrorCode) throw new ErrorHandler(404, "Error code not found");
|
||||
|
||||
// Verify the error code belongs to the specified brand
|
||||
if (existingErrorCode.brand_id !== parseInt(brandId)) {
|
||||
throw new ErrorHandler(403, "Error code does not belong to specified brand");
|
||||
}
|
||||
|
||||
// Check if there are any error code fields to update
|
||||
const hasMainFieldUpdate =
|
||||
data.error_code !== undefined ||
|
||||
data.error_code_name !== undefined ||
|
||||
data.error_code_description !== undefined ||
|
||||
data.error_code_color !== undefined ||
|
||||
data.path_icon !== undefined ||
|
||||
data.is_active !== undefined;
|
||||
|
||||
if (hasMainFieldUpdate) {
|
||||
await updateErrorCodeDb(brandId, existingErrorCode.error_code, {
|
||||
error_code: data.error_code,
|
||||
error_code_name: data.error_code_name,
|
||||
error_code_description: data.error_code_description,
|
||||
error_code_color: data.error_code_color,
|
||||
path_icon: data.path_icon,
|
||||
is_active: data.is_active,
|
||||
updated_by: data.updated_by,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.spareparts && Array.isArray(data.spareparts)) {
|
||||
await updateErrorCodeSparepartsDb(existingErrorCode.error_code_id, data.spareparts, data.updated_by);
|
||||
}
|
||||
|
||||
if (data.solution && Array.isArray(data.solution)) {
|
||||
const existingSolutions = await getSolutionsByErrorCodeIdDb(existingErrorCode.error_code_id);
|
||||
const incomingSolutionNames = data.solution.map((s) => s.solution_name);
|
||||
|
||||
for (const solutionData of data.solution) {
|
||||
const existingSolution = existingSolutions.find(
|
||||
(s) => s.solution_name === solutionData.solution_name
|
||||
);
|
||||
|
||||
if (existingSolution) {
|
||||
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 {
|
||||
await createSolutionDb(existingErrorCode.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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const existingSolution of existingSolutions) {
|
||||
if (!incomingSolutionNames.includes(existingSolution.solution_name)) {
|
||||
await deleteSolutionDb(existingSolution.brand_code_solution_id, data.updated_by);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedErrorCode = await this.getErrorCodeById(existingErrorCode.error_code_id);
|
||||
return updatedErrorCode;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(500, `Update error code failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete error code
|
||||
static async deleteErrorCode(brandId, errorCodeId, deletedBy) {
|
||||
try {
|
||||
const errorCodeExist = await getErrorCodeByIdDb(errorCodeId);
|
||||
|
||||
if (!errorCodeExist) {
|
||||
throw new ErrorHandler(404, "Error code not found");
|
||||
}
|
||||
|
||||
// Verify the error code belongs to the specified brand
|
||||
if (errorCodeExist.brand_id !== parseInt(brandId)) {
|
||||
throw new ErrorHandler(403, "Error code does not belong to specified brand");
|
||||
}
|
||||
|
||||
const result = await deleteErrorCodeDb(brandId, errorCodeExist.error_code, deletedBy);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ErrorCodeService;
|
||||
@@ -1,4 +1,11 @@
|
||||
const { getHistoryAlarmDb, getHistoryEventDb, checkTableNamedDb, getHistoryValueReportDb, getHistoryValueReportPivotDb, getHistoryValueTrendingPivotDb } = require('../db/history_value.db');
|
||||
const {
|
||||
getHistoryAlarmDb,
|
||||
getHistoryEventDb,
|
||||
checkTableNamedDb,
|
||||
getHistoryValueReportDb,
|
||||
getHistoryValueReportPivotDb,
|
||||
getHistoryValueTrendingPivotDb
|
||||
} = require('../db/history_value.db');
|
||||
const { getSubSectionByIdDb } = require('../db/plant_sub_section.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
@@ -7,94 +14,128 @@ class HistoryValue {
|
||||
static async getAllHistoryAlarm(param) {
|
||||
try {
|
||||
const results = await getHistoryAlarmDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message || 'Error fetching alarm history');
|
||||
}
|
||||
}
|
||||
|
||||
static async getAllHistoryEvent(param) {
|
||||
try {
|
||||
const results = await getHistoryEventDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
throw new ErrorHandler(error.statusCode || 500, error.message || 'Error fetching event history');
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueReport(param) {
|
||||
try {
|
||||
if (!param.plant_sub_section_id) {
|
||||
throw new ErrorHandler(400, 'plant_sub_section_id is required');
|
||||
}
|
||||
|
||||
const plantSubSection = await getSubSectionByIdDb(param.plant_sub_section_id);
|
||||
|
||||
if (plantSubSection.length < 1) throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
if (!plantSubSection || plantSubSection.length < 1) {
|
||||
throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
}
|
||||
|
||||
const tabelExist = await checkTableNamedDb(plantSubSection[0]?.table_name_value);
|
||||
const tableNameValue = plantSubSection[0]?.table_name_value;
|
||||
|
||||
if (!tableNameValue) {
|
||||
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||
}
|
||||
|
||||
if (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
||||
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||
|
||||
const results = await getHistoryValueReportDb(tabelExist[0]?.TABLE_NAME, param);
|
||||
if (!tableExist || tableExist.length < 1) {
|
||||
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||
}
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
const results = await getHistoryValueReportDb(tableExist[0].TABLE_NAME, param);
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
throw new ErrorHandler(
|
||||
error.statusCode || 500,
|
||||
error.message || 'Error fetching history value report'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueReportPivot(param) {
|
||||
try {
|
||||
if (!param.plant_sub_section_id) {
|
||||
throw new ErrorHandler(400, 'plant_sub_section_id is required');
|
||||
}
|
||||
if (!param.from || !param.to) {
|
||||
throw new ErrorHandler(400, 'from and to date parameters are required');
|
||||
}
|
||||
|
||||
const plantSubSection = await getSubSectionByIdDb(param.plant_sub_section_id);
|
||||
|
||||
if (plantSubSection.length < 1) throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
if (!plantSubSection || plantSubSection.length < 1) {
|
||||
throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
}
|
||||
|
||||
const tabelExist = await checkTableNamedDb(plantSubSection[0]?.table_name_value);
|
||||
const tableNameValue = plantSubSection[0]?.table_name_value;
|
||||
|
||||
if (!tableNameValue) {
|
||||
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||
}
|
||||
|
||||
if (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
||||
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||
|
||||
const results = await getHistoryValueReportPivotDb(tabelExist[0]?.TABLE_NAME, param);
|
||||
if (!tableExist || tableExist.length < 1) {
|
||||
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||
}
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
const results = await getHistoryValueReportPivotDb(tableExist[0].TABLE_NAME, param);
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
throw new ErrorHandler(
|
||||
error.statusCode || 500,
|
||||
error.message || 'Error fetching history value report pivot'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static async getHistoryValueTrendingPivot(param) {
|
||||
try {
|
||||
if (!param.plant_sub_section_id) {
|
||||
throw new ErrorHandler(400, 'plant_sub_section_id is required');
|
||||
}
|
||||
if (!param.from || !param.to) {
|
||||
throw new ErrorHandler(400, 'from and to date parameters are required');
|
||||
}
|
||||
|
||||
const plantSubSection = await getSubSectionByIdDb(param.plant_sub_section_id);
|
||||
|
||||
if (plantSubSection.length < 1) throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
if (!plantSubSection || plantSubSection.length < 1) {
|
||||
throw new ErrorHandler(404, 'Plant sub section not found');
|
||||
}
|
||||
|
||||
const tabelExist = await checkTableNamedDb(plantSubSection[0]?.table_name_value);
|
||||
const tableNameValue = plantSubSection[0]?.table_name_value;
|
||||
|
||||
if (!tableNameValue) {
|
||||
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||
}
|
||||
|
||||
if (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
||||
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||
|
||||
const results = await getHistoryValueTrendingPivotDb(tabelExist[0]?.TABLE_NAME, param);
|
||||
if (!tableExist || tableExist.length < 1) {
|
||||
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||
}
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
const results = await getHistoryValueTrendingPivotDb(tableExist[0].TABLE_NAME, param);
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
throw new ErrorHandler(
|
||||
error.statusCode || 500,
|
||||
error.message || 'Error fetching history value trending pivot'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HistoryValue;
|
||||
module.exports = HistoryValue;
|
||||
@@ -1,6 +1,9 @@
|
||||
const {
|
||||
getAllNotificationDb,
|
||||
getNotificationByIdDb,
|
||||
InsertNotificationErrorDb,
|
||||
getUsersNotificationErrorDb,
|
||||
updateNotificationErrorDb,
|
||||
} = require('../db/notification_error.db');
|
||||
|
||||
const {
|
||||
@@ -17,6 +20,10 @@ const {
|
||||
getNotificationErrorLogByNotificationErrorIdDb,
|
||||
} = require('../db/notification_error_log.db');
|
||||
|
||||
const {
|
||||
getSparepartsByErrorCodeIdDb,
|
||||
} = require('../db/brand_sparepart.db');
|
||||
|
||||
const { getFileUploadByPathDb } = require('../db/file_uploads.db');
|
||||
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
@@ -36,6 +43,18 @@ class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
static async createNotificationError(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await InsertNotificationErrorDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get notification by ID
|
||||
static async getNotificationById(id) {
|
||||
try {
|
||||
@@ -45,6 +64,8 @@ class NotificationService {
|
||||
throw new ErrorHandler(404, 'Notification not found');
|
||||
}
|
||||
|
||||
const usersNotification = (await getUsersNotificationErrorDb(id))|| [];
|
||||
|
||||
// Get error code details if error_code_id exists
|
||||
if (notification.error_code_id) {
|
||||
const errorCode = await getErrorCodeByIdDb(notification.error_code_id);
|
||||
@@ -53,6 +74,8 @@ class NotificationService {
|
||||
// Get solutions for this error code
|
||||
const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || [];
|
||||
|
||||
const spareparts = (await getSparepartsByErrorCodeIdDb(errorCode.error_code_id)) || [];
|
||||
|
||||
const solutionsWithDetails = await Promise.all(
|
||||
solutions.map(async (solution) => {
|
||||
let fileData = null;
|
||||
@@ -71,9 +94,11 @@ class NotificationService {
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
notification.error_code = {
|
||||
...errorCode,
|
||||
solution: solutionsWithDetails
|
||||
solution: solutionsWithDetails,
|
||||
spareparts: spareparts,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -81,6 +106,8 @@ class NotificationService {
|
||||
// Get activity logs for this notification
|
||||
const notificationLogs = (await getNotificationErrorLogByNotificationErrorIdDb(id)) || [];
|
||||
|
||||
notification.users = usersNotification;
|
||||
|
||||
notification.activity_logs = notificationLogs;
|
||||
|
||||
return notification;
|
||||
@@ -88,6 +115,24 @@ class NotificationService {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
static async updateNotificationError(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getNotificationByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'NotificationErrorUser not found');
|
||||
}
|
||||
|
||||
const result = await updateNotificationErrorDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NotificationService;
|
||||
|
||||
@@ -46,7 +46,7 @@ class NotificationErrorLogService {
|
||||
|
||||
const store = {
|
||||
notification_error_id: data.notification_error_id,
|
||||
contact_id: data.contact_id,
|
||||
contact_phone: data.contact_phone,
|
||||
notification_error_log_description: data.notification_error_log_description,
|
||||
created_by: data.created_by
|
||||
};
|
||||
|
||||
@@ -6,31 +6,23 @@ const {
|
||||
deleteNotificationErrorSparepartDb,
|
||||
} = require("../db/notification_error_sparepart.db");
|
||||
|
||||
const { getContactByIdDb } = require("../db/contact.db");
|
||||
const { ErrorHandler } = require("../helpers/error");
|
||||
|
||||
class NotificationErrorSparepartService {
|
||||
static _checkAccess(contactType) {
|
||||
if (contactType !== "gudang") {
|
||||
throw new ErrorHandler(
|
||||
403,
|
||||
"Akses ditolak. Hanya contact_type 'gudang' yang dapat getAll/create/update/delete."
|
||||
);
|
||||
|
||||
static async getAll(param) {
|
||||
try {
|
||||
const results = await getAllNotificationErrorSparepartDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
static async getAll(param, contact_id) {
|
||||
const contactResult = await getContactByIdDb(contact_id);
|
||||
|
||||
if (!contactResult || contactResult.length < 1)
|
||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
||||
|
||||
const contact = contactResult[0];
|
||||
|
||||
this._checkAccess(contact.contact_type);
|
||||
return await getAllNotificationErrorSparepartDb(param);
|
||||
}
|
||||
|
||||
static async getById(id) {
|
||||
const result = await getNotificationErrorSparepartByIdDb(id);
|
||||
|
||||
@@ -40,46 +32,37 @@ class NotificationErrorSparepartService {
|
||||
return result;
|
||||
}
|
||||
|
||||
static async create(data) {
|
||||
const contactResult = await getContactByIdDb(data.contact_id);
|
||||
static async create(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
if (!contactResult || contactResult.length < 1)
|
||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
||||
const result = await createNotificationErrorSparepartDb(data);
|
||||
|
||||
const contact = contactResult[0];
|
||||
|
||||
this._checkAccess(contact.contact_type);
|
||||
|
||||
return await createNotificationErrorSparepartDb(data);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
static async update(id, data) {
|
||||
const contactResult = await getContactByIdDb(data.contact_id);
|
||||
static async update(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
if (!contactResult || contactResult.length < 1)
|
||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
||||
const dataExist = await getNotificationErrorSparepartByIdDb(id);
|
||||
|
||||
const contact = contactResult[0];
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'Notification Error Sparepart not found');
|
||||
}
|
||||
|
||||
this._checkAccess(contact.contact_type);
|
||||
const result = await updateNotificationErrorSparepartDb(id, data);
|
||||
|
||||
const exist = await getNotificationErrorSparepartByIdDb(id);
|
||||
|
||||
if (exist.length < 1)
|
||||
throw new ErrorHandler(404, "Notification Error Sparepart not found");
|
||||
|
||||
return await updateNotificationErrorSparepartDb(id, data);
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
static async delete(id, contact_id) {
|
||||
const contactResult = await getContactByIdDb(contact_id);
|
||||
|
||||
if (!contactResult || contactResult.length < 1)
|
||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
||||
|
||||
const contact = contactResult[0];
|
||||
|
||||
this._checkAccess(contact.contact_type);
|
||||
static async delete(id) {
|
||||
|
||||
const exist = await getNotificationErrorSparepartByIdDb(id);
|
||||
|
||||
|
||||
88
services/notification_error_user.service.js
Normal file
88
services/notification_error_user.service.js
Normal file
@@ -0,0 +1,88 @@
|
||||
const {
|
||||
getAllNotificationErrorUserDb,
|
||||
getNotificationErrorUserByIdDb,
|
||||
createNotificationErrorUserDb,
|
||||
updateNotificationErrorUserDb,
|
||||
deleteNotificationErrorUserDb
|
||||
} = require('../db/notification_error_user.db');
|
||||
const { ErrorHandler } = require('../helpers/error');
|
||||
|
||||
class NotificationErrorUserService {
|
||||
// Get all Contact
|
||||
static async getAllNotificationErrorUser(param) {
|
||||
try {
|
||||
const results = await getAllNotificationErrorUserDb(param);
|
||||
|
||||
results.data.map(element => {
|
||||
});
|
||||
|
||||
return results
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Get NotificationErrorUser by ID
|
||||
static async getNotificationErrorUserById(id) {
|
||||
try {
|
||||
const result = await getNotificationErrorUserByIdDb(id);
|
||||
|
||||
if (result.length < 1) throw new ErrorHandler(404, 'NotificationErrorUser not found');
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Create NotificationErrorUser
|
||||
static async createNotificationErrorUser(data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const result = await createNotificationErrorUserDb(data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Update NotificationErrorUser
|
||||
static async updateNotificationErrorUser(id, data) {
|
||||
try {
|
||||
if (!data || typeof data !== 'object') data = {};
|
||||
|
||||
const dataExist = await getNotificationErrorUserByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'NotificationErrorUser not found');
|
||||
}
|
||||
|
||||
const result = await updateNotificationErrorUserDb(id, data);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Soft delete NotificationErrorUser
|
||||
static async deleteNotificationErrorUser(id, userId) {
|
||||
try {
|
||||
const dataExist = await getNotificationErrorUserByIdDb(id);
|
||||
|
||||
if (dataExist.length < 1) {
|
||||
throw new ErrorHandler(404, 'NotificationErrorUser not found');
|
||||
}
|
||||
|
||||
const result = await deleteNotificationErrorUserDb(id, userId);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new ErrorHandler(error.statusCode, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NotificationErrorUserService;
|
||||
105
services/notifikasi-wa.service.js
Normal file
105
services/notifikasi-wa.service.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const { getAllContactDb } = require('../db/contact.db');
|
||||
const { InsertNotificationErrorDb } = require('../db/notification_error.db');
|
||||
const { createNotificationErrorUserDb, updateNotificationErrorUserDb } = require('../db/notification_error_user.db');
|
||||
const { generateTokenRedirect, shortUrltiny, sendNotifikasi } = require('../db/notification_wa.db');
|
||||
|
||||
class NotifikasiWaService {
|
||||
|
||||
async onNotification(topic, message) {
|
||||
|
||||
try {
|
||||
const paramDb = {
|
||||
limit: 100,
|
||||
page: 1,
|
||||
criteria: '',
|
||||
active: 1
|
||||
}
|
||||
|
||||
// const chanel = {
|
||||
// "time": "2025-12-11 11:10:58",
|
||||
// "c_4501": 4,
|
||||
// "c_5501": 3,
|
||||
// "c_6501": 0
|
||||
// }
|
||||
|
||||
if (topic === 'morek') {
|
||||
|
||||
const dataMqtt = JSON.parse(message);
|
||||
|
||||
const resultChanel = [];
|
||||
|
||||
Object.entries(dataMqtt).forEach(([key, value]) => {
|
||||
if (key.startsWith('c_')) {
|
||||
resultChanel.push({
|
||||
chanel_id: Number(key.slice(2)),
|
||||
value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const results = await getAllContactDb(paramDb);
|
||||
|
||||
const bodyMessage = `Hai Operator\n` +
|
||||
`Terjadi peringatan pada device, silahkan cek detail pada link berikut :\n`;
|
||||
|
||||
const dataUsers = results.data;
|
||||
|
||||
for (const chanel of resultChanel) {
|
||||
const data = {
|
||||
"error_code_id": chanel.value,
|
||||
"error_chanel": chanel.chanel_id,
|
||||
"message_error_issue": bodyMessage,
|
||||
"is_send": false,
|
||||
"is_delivered": false,
|
||||
"is_read": false,
|
||||
"is_active": true
|
||||
}
|
||||
|
||||
const resultNotificationError = await InsertNotificationErrorDb(data)
|
||||
|
||||
for (const dataUser of dataUsers) {
|
||||
if (dataUser.is_active) {
|
||||
|
||||
const param = {
|
||||
idData: resultNotificationError.notification_error_id,
|
||||
userPhone: dataUser.contact_phone,
|
||||
userName: dataUser.contact_name,
|
||||
bodyMessage: bodyMessage,
|
||||
}
|
||||
|
||||
const tokenRedirect = await generateTokenRedirect(param.userPhone, param.userName, param.idData)
|
||||
|
||||
const encodedToken = encodeURIComponent(tokenRedirect);
|
||||
|
||||
const shortUrl = await shortUrltiny(encodedToken)
|
||||
|
||||
let bodyWithUrl = `${param.bodyMessage}\n🔗 ${shortUrl}`;
|
||||
|
||||
param.bodyMessage = bodyWithUrl
|
||||
|
||||
const resultNotificationErrorUser = await createNotificationErrorUserDb({
|
||||
notification_error_id: resultNotificationError.notification_error_id,
|
||||
contact_phone: param.userPhone,
|
||||
contact_name: param.userName,
|
||||
is_send: false,
|
||||
});
|
||||
|
||||
const resultSend = await sendNotifikasi(param.userPhone, param.bodyMessage);
|
||||
|
||||
await updateNotificationErrorUserDb(resultNotificationErrorUser[0].notification_error_user_id, {
|
||||
is_send: resultSend?.error ? false : true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// throw new ErrorHandler(error.statusCode, error.message);
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new NotifikasiWaService();
|
||||
@@ -8,91 +8,18 @@ const insertBrandSchema = Joi.object({
|
||||
brand_type: Joi.string().max(50).optional().allow(""),
|
||||
brand_manufacture: Joi.string().max(100).required(),
|
||||
brand_model: Joi.string().max(100).optional().allow(""),
|
||||
is_active: Joi.boolean().required(),
|
||||
is_active: Joi.boolean().optional().default(true),
|
||||
description: Joi.string().max(255).optional().allow(""),
|
||||
spareparts: Joi.array().items(Joi.number().integer()).optional(), // Array of sparepart_id
|
||||
error_code: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
error_code: Joi.string().max(100).required(),
|
||||
error_code_name: Joi.string().max(100).required(),
|
||||
error_code_description: Joi.string().optional().allow(""),
|
||||
error_code_color: Joi.string().optional().allow(""),
|
||||
path_icon: Joi.string().optional().allow(""),
|
||||
is_active: Joi.boolean().required(),
|
||||
what_action_to_take: Joi.string().optional().allow(""),
|
||||
solution: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
solution_name: Joi.string().max(100).required(),
|
||||
type_solution: Joi.string()
|
||||
.valid("text", "pdf", "image", "video", "link")
|
||||
.required(),
|
||||
text_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().required(),
|
||||
otherwise: Joi.string().optional().allow(""),
|
||||
}),
|
||||
path_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().optional().allow(""),
|
||||
otherwise: Joi.string().required(),
|
||||
}),
|
||||
is_active: Joi.boolean().required(),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.required(),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.required(),
|
||||
});
|
||||
|
||||
// Update Brand Validation
|
||||
const updateBrandSchema = Joi.object({
|
||||
brand_name: Joi.string().max(100).required(),
|
||||
brand_name: Joi.string().max(100).optional(),
|
||||
brand_type: Joi.string().max(50).optional().allow(""),
|
||||
brand_manufacture: Joi.string().max(100).required(),
|
||||
brand_manufacture: Joi.string().max(100).optional(),
|
||||
brand_model: Joi.string().max(100).optional().allow(""),
|
||||
is_active: Joi.boolean().required(),
|
||||
is_active: Joi.boolean().optional(),
|
||||
description: Joi.string().max(255).optional().allow(""),
|
||||
spareparts: Joi.array().items(Joi.number().integer()).optional(), // Array of sparepart_id
|
||||
error_code: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
error_code: Joi.string().max(100).required(),
|
||||
error_code_name: Joi.string().max(100).required(),
|
||||
error_code_description: Joi.string().optional().allow(""),
|
||||
error_code_color: Joi.string().optional().allow(""),
|
||||
path_icon: Joi.string().optional().allow(""),
|
||||
is_active: Joi.boolean().required(),
|
||||
what_action_to_take: Joi.string().optional().allow(""),
|
||||
solution: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
solution_name: Joi.string().max(100).required(),
|
||||
type_solution: Joi.string()
|
||||
.valid("text", "pdf", "image", "video", "link")
|
||||
.required(),
|
||||
text_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().required(),
|
||||
otherwise: Joi.string().optional().allow(""),
|
||||
}),
|
||||
path_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().optional().allow(""),
|
||||
otherwise: Joi.string().required(),
|
||||
}),
|
||||
is_active: Joi.boolean().optional(),
|
||||
})
|
||||
)
|
||||
.min(1)
|
||||
.required(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
}).min(1);
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -13,20 +13,20 @@ const insertContactSchema = Joi.object({
|
||||
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
||||
}),
|
||||
is_active: Joi.boolean().required(),
|
||||
contact_type: Joi.string().max(255).required()
|
||||
contact_type: Joi.string().max(255).optional().allow(null)
|
||||
});
|
||||
|
||||
const updateContactSchema = Joi.object({
|
||||
contact_name: Joi.string().min(3).max(100).required(),
|
||||
contact_name: Joi.string().min(3).max(100).optional(),
|
||||
contact_phone: Joi.string()
|
||||
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
||||
.required()
|
||||
.optional()
|
||||
.messages({
|
||||
"string.pattern.base":
|
||||
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
||||
}),
|
||||
is_active: Joi.boolean().optional(),
|
||||
contact_type: Joi.string().max(255).optional()
|
||||
contact_type: Joi.string().max(255).optional().allow(null)
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -9,13 +9,14 @@ const insertDeviceSchema = Joi.object({
|
||||
is_active: Joi.boolean().required(),
|
||||
brand_id: Joi.number().integer().min(1),
|
||||
device_location: Joi.string().max(100).required(),
|
||||
device_description: Joi.string().required(),
|
||||
device_description: Joi.string(),
|
||||
ip_address: Joi.string()
|
||||
.ip({ version: ['ipv4', 'ipv6'] })
|
||||
.required()
|
||||
.messages({
|
||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||
})
|
||||
}),
|
||||
listen_channel: Joi.string().max(100).required()
|
||||
});
|
||||
|
||||
const updateDeviceSchema = Joi.object({
|
||||
@@ -28,11 +29,11 @@ const updateDeviceSchema = Joi.object({
|
||||
.ip({ version: ['ipv4', 'ipv6'] })
|
||||
.messages({
|
||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||
})
|
||||
}),
|
||||
listen_channel: Joi.string().max(100)
|
||||
}).min(1);
|
||||
|
||||
|
||||
// ✅ Export dengan CommonJS
|
||||
module.exports = {
|
||||
insertDeviceSchema, updateDeviceSchema
|
||||
};
|
||||
72
validate/error_code.schema.js
Normal file
72
validate/error_code.schema.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Error Code Validation
|
||||
// ========================
|
||||
|
||||
const solutionSchema = Joi.object({
|
||||
solution_name: Joi.string().max(100).required(),
|
||||
type_solution: Joi.string()
|
||||
.valid("text", "pdf", "image", "video", "link")
|
||||
.required(),
|
||||
text_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().required(),
|
||||
otherwise: Joi.string().optional().allow(""),
|
||||
}),
|
||||
path_solution: Joi.when("type_solution", {
|
||||
is: "text",
|
||||
then: Joi.string().optional().allow(""),
|
||||
otherwise: Joi.string().required(),
|
||||
}),
|
||||
is_active: Joi.boolean().default(true),
|
||||
});
|
||||
|
||||
const insertErrorCodeSchema = Joi.object({
|
||||
error_code: Joi.string().max(100).required(),
|
||||
error_code_name: Joi.string().max(100).required(),
|
||||
error_code_description: Joi.string().optional().allow(""),
|
||||
error_code_color: Joi.string().optional().allow(""),
|
||||
path_icon: Joi.string().optional().allow(""),
|
||||
is_active: Joi.boolean().default(true),
|
||||
solution: Joi.array()
|
||||
.items(solutionSchema)
|
||||
.min(1)
|
||||
.required()
|
||||
.messages({
|
||||
"array.min": "Error code must have at least 1 solution",
|
||||
}),
|
||||
spareparts: Joi.array()
|
||||
.items(Joi.number().integer())
|
||||
.optional(),
|
||||
}).messages({
|
||||
"object.unknown": "{{#child}} is not allowed",
|
||||
});
|
||||
|
||||
const updateErrorCodeSchema = Joi.object({
|
||||
error_code: Joi.string().max(100).optional(),
|
||||
error_code_name: Joi.string().max(100).optional(),
|
||||
error_code_description: Joi.string().optional().allow(""),
|
||||
error_code_color: Joi.string().optional().allow(""),
|
||||
path_icon: Joi.string().optional().allow(""),
|
||||
is_active: Joi.boolean().optional(),
|
||||
solution: Joi.array()
|
||||
.items(solutionSchema)
|
||||
.min(1)
|
||||
.optional()
|
||||
.messages({
|
||||
"array.min": "Error code must have at least 1 solution",
|
||||
}),
|
||||
spareparts: Joi.array()
|
||||
.items(Joi.number().integer())
|
||||
.optional(),
|
||||
}).min(1).messages({
|
||||
"object.min": "At least one field must be provided for update",
|
||||
"object.unknown": "{{#child}} is not allowed",
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
insertErrorCodeSchema,
|
||||
updateErrorCodeSchema,
|
||||
solutionSchema,
|
||||
};
|
||||
@@ -13,6 +13,8 @@ const insertNotificationSchema = Joi.object({
|
||||
"number.base": "error_code_id must be a number",
|
||||
}),
|
||||
|
||||
message_error_issue: Joi.string().max(255).optional(),
|
||||
|
||||
is_send: Joi.boolean().required().messages({
|
||||
"any.required": "is_send is required",
|
||||
"boolean.base": "is_send must be a boolean",
|
||||
@@ -38,10 +40,6 @@ const insertNotificationSchema = Joi.object({
|
||||
// Update Notification Schema
|
||||
// ========================
|
||||
const updateNotificationSchema = Joi.object({
|
||||
error_code_id: Joi.number().optional().messages({
|
||||
"number.base": "error_code_id must be a number",
|
||||
}),
|
||||
|
||||
is_send: Joi.boolean().optional().messages({
|
||||
"boolean.base": "is_send must be a boolean",
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,7 @@ const Joi = require("joi");
|
||||
|
||||
const insertNotificationErrorLogSchema = Joi.object({
|
||||
notification_error_id: Joi.number().integer().required(),
|
||||
contact_id: Joi.number().integer().required(),
|
||||
contact_phone: Joi.string().optional(),
|
||||
notification_error_log_description: Joi.string().required()
|
||||
});
|
||||
|
||||
|
||||
44
validate/notification_error_user.schema.js
Normal file
44
validate/notification_error_user.schema.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const Joi = require("joi");
|
||||
|
||||
// ========================
|
||||
// Insert Notification Error Schema
|
||||
// ========================
|
||||
const insertNotificationErrorUserSchema = Joi.object({
|
||||
notification_error_id: Joi.number().required().messages({
|
||||
"any.required": "notification_error_id is required",
|
||||
"number.base": "notification_error_id must be a number",
|
||||
}),
|
||||
|
||||
contact_id: Joi.number().required().messages({
|
||||
"any.required": "contact_id is required",
|
||||
"number.base": "contact_id must be a number",
|
||||
}),
|
||||
|
||||
is_send: Joi.boolean().required().messages({
|
||||
"any.required": "is_send is required",
|
||||
"boolean.base": "is_send must be a boolean",
|
||||
}),
|
||||
});
|
||||
|
||||
// ========================
|
||||
// Update Notification Error Schema
|
||||
// ========================
|
||||
const updateNotificationErrorUserSchema = Joi.object({
|
||||
notification_error_id: Joi.number().optional().messages({
|
||||
"number.base": "notification_error_id must be a number",
|
||||
}),
|
||||
|
||||
contact_id: Joi.number().required().messages({
|
||||
"any.required": "contact_id is required",
|
||||
"number.base": "contact_id must be a number",
|
||||
}),
|
||||
|
||||
is_send: Joi.boolean().optional().messages({
|
||||
"boolean.base": "is_send must be a boolean",
|
||||
}),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
insertNotificationErrorUserSchema,
|
||||
updateNotificationErrorUserSchema,
|
||||
};
|
||||
@@ -8,7 +8,7 @@ const insertSparepartSchema = Joi.object({
|
||||
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_qty: Joi.number().integer().min(0),
|
||||
sparepart_unit: Joi.string().max(255).optional(),
|
||||
sparepart_merk: Joi.string().max(255).optional(),
|
||||
sparepart_stok: Joi.string().max(255).optional(),
|
||||
@@ -21,7 +21,7 @@ const updateSparepartSchema = Joi.object({
|
||||
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_qty: Joi.number().integer().min(0),
|
||||
sparepart_unit: Joi.string().max(255).optional(),
|
||||
sparepart_merk: Joi.string().max(255).optional(),
|
||||
sparepart_stok: Joi.string().max(255).optional(),
|
||||
|
||||
Reference in New Issue
Block a user