wisdom #19
@@ -23,6 +23,11 @@ SECRET=secret
|
|||||||
# JWT refresh secret
|
# JWT refresh secret
|
||||||
REFRESH_SECRET=refreshsecret
|
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
|
# mail server settings
|
||||||
# SMTP_FROM=youremail
|
# SMTP_FROM=youremail
|
||||||
# SMTP_USER=youremail
|
# SMTP_USER=youremail
|
||||||
|
|||||||
9
app.js
9
app.js
@@ -8,7 +8,8 @@ const helmet = require("helmet");
|
|||||||
const compression = require("compression");
|
const compression = require("compression");
|
||||||
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
const unknownEndpoint = require("./middleware/unKnownEndpoint");
|
||||||
const { handleError } = require("./helpers/error");
|
const { handleError } = require("./helpers/error");
|
||||||
const { checkConnection } = require("./config");
|
const { checkConnection, mqttClient } = require("./config");
|
||||||
|
const { onNotification } = require("./services/notifikasi-wa.service");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
@@ -47,4 +48,10 @@ app.get("/check-db", async (req, res) => {
|
|||||||
app.use(unknownEndpoint);
|
app.use(unknownEndpoint);
|
||||||
app.use(handleError);
|
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;
|
module.exports = app;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
const { default: mqtt } = require("mqtt");
|
||||||
const sql = require("mssql");
|
const sql = require("mssql");
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === "production";
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
|
const endPointWhatsapp = process.env.ENDPOINT_WHATSAPP;
|
||||||
|
|
||||||
// Config SQL Server
|
// Config SQL Server
|
||||||
const config = {
|
const config = {
|
||||||
user: process.env.SQL_USERNAME,
|
user: process.env.SQL_USERNAME,
|
||||||
@@ -284,6 +287,34 @@ async function generateKode(prefix, tableName, columnName) {
|
|||||||
return prefix + String(nextNumber).padStart(3, "0");
|
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 = {
|
module.exports = {
|
||||||
checkConnection,
|
checkConnection,
|
||||||
query,
|
query,
|
||||||
@@ -293,4 +324,6 @@ module.exports = {
|
|||||||
buildDynamicInsert,
|
buildDynamicInsert,
|
||||||
buildDynamicUpdate,
|
buildDynamicUpdate,
|
||||||
generateKode,
|
generateKode,
|
||||||
|
endPointWhatsapp,
|
||||||
|
mqttClient
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ const AuthService = require('../services/auth.service');
|
|||||||
const { setResponse, checkValidate } = require('../helpers/utils');
|
const { setResponse, checkValidate } = require('../helpers/utils');
|
||||||
const { registerSchema, loginSchema } = require('../validate/auth.schema');
|
const { registerSchema, loginSchema } = require('../validate/auth.schema');
|
||||||
const { createCaptcha } = require('../utils/captcha');
|
const { createCaptcha } = require('../utils/captcha');
|
||||||
|
const JWTService = require('../utils/jwt');
|
||||||
|
|
||||||
|
const CryptoJS = require('crypto-js');
|
||||||
|
|
||||||
class AuthController {
|
class AuthController {
|
||||||
// Register
|
// Register
|
||||||
@@ -94,6 +97,41 @@ class AuthController {
|
|||||||
const response = await setResponse({ svg, text }, 'Captcha generated');
|
const response = await setResponse({ svg, text }, 'Captcha generated');
|
||||||
res.status(response.statusCode).json(response);
|
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;
|
module.exports = AuthController;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const BrandService = require('../services/brand.service');
|
const BrandService = require('../services/brand.service');
|
||||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
||||||
const { createFileUploadDb } = require('../db/file_uploads.db');
|
|
||||||
const {
|
const {
|
||||||
insertBrandSchema,
|
insertBrandSchema,
|
||||||
updateBrandSchema,
|
updateBrandSchema,
|
||||||
@@ -30,7 +29,7 @@ class BrandController {
|
|||||||
res.status(response.statusCode).json(response);
|
res.status(response.statusCode).json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create brand with nested error codes and solutions
|
// Create brand
|
||||||
static async create(req, res) {
|
static async create(req, res) {
|
||||||
const { error, value } = await checkValidate(insertBrandSchema, req);
|
const { error, value } = await checkValidate(insertBrandSchema, req);
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ class BrandController {
|
|||||||
|
|
||||||
value.created_by = req.user?.user_id || null;
|
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');
|
const response = await setResponse(results, 'Brand created successfully');
|
||||||
|
|
||||||
return res.status(response.statusCode).json(response);
|
return res.status(response.statusCode).json(response);
|
||||||
@@ -51,50 +50,17 @@ class BrandController {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const { error, value } = await checkValidate(updateBrandSchema, req);
|
const { error, value } = await checkValidate(updateBrandSchema, req);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return res.status(400).json(setResponse(error, 'Validation failed', 400));
|
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}`;
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value.updated_by = req.user?.user_id || null;
|
value.updated_by = req.user?.user_id || null;
|
||||||
|
|
||||||
const results = await BrandService.updateBrandWithFullData(id, value);
|
const results = await BrandService.updateBrand(id, value);
|
||||||
const response = await setResponse(results, 'Brand updated successfully');
|
const response = await setResponse(results, 'Brand updated successfully');
|
||||||
|
|
||||||
res.status(response.statusCode).json(response);
|
res.status(response.statusCode).json(response);
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
const response = setResponse([], error.message, error.statusCode || 500);
|
|
||||||
res.status(response.statusCode).json(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Soft delete brand by ID
|
// 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(
|
const response = await setResponse(
|
||||||
{
|
{
|
||||||
file_upload_name: file.originalname,
|
file_upload_name: file.originalname,
|
||||||
path_document: pathDocument,
|
path_document: pathDocument
|
||||||
path_solution: pathDocument
|
|
||||||
},
|
},
|
||||||
"File berhasil diunggah"
|
"File berhasil diunggah"
|
||||||
);
|
);
|
||||||
@@ -45,8 +44,6 @@ const uploadFile = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getFileByPath = async (req, res) => {
|
const getFileByPath = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { folder, filename } = req.params;
|
const { folder, filename } = req.params;
|
||||||
|
|||||||
@@ -4,52 +4,101 @@ const { setResponsePaging } = require('../helpers/utils');
|
|||||||
class HistoryValueController {
|
class HistoryValueController {
|
||||||
|
|
||||||
static async getAllHistoryAlarm(req, res) {
|
static async getAllHistoryAlarm(req, res) {
|
||||||
|
try {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await HistoryValue.getAllHistoryAlarm(queryParams);
|
const results = await HistoryValue.getAllHistoryAlarm(queryParams);
|
||||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
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) {
|
static async getAllHistoryEvent(req, res) {
|
||||||
|
try {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await HistoryValue.getAllHistoryEvent(queryParams);
|
const results = await HistoryValue.getAllHistoryEvent(queryParams);
|
||||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
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) {
|
static async getHistoryValueReport(req, res) {
|
||||||
|
try {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await HistoryValue.getHistoryValueReport(queryParams);
|
const results = await HistoryValue.getHistoryValueReport(queryParams);
|
||||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
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) {
|
static async getHistoryValueReportPivot(req, res) {
|
||||||
|
try {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await HistoryValue.getHistoryValueReportPivot(queryParams);
|
const results = await HistoryValue.getHistoryValueReportPivot(queryParams);
|
||||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
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) {
|
static async getHistoryValueTrendingPivot(req, res) {
|
||||||
|
try {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await HistoryValue.getHistoryValueTrendingPivot(queryParams);
|
const results = await HistoryValue.getHistoryValueTrendingPivot(queryParams);
|
||||||
const response = await setResponsePaging(queryParams, results, 'Data found');
|
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'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,76 @@
|
|||||||
const NotificationErrorService = require('../services/notification_error.service');
|
const NotificationErrorService = require("../services/notification_error.service");
|
||||||
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
|
const {
|
||||||
|
setResponse,
|
||||||
|
setResponsePaging,
|
||||||
|
checkValidate,
|
||||||
|
} = require("../helpers/utils");
|
||||||
|
const {
|
||||||
|
insertNotificationSchema,
|
||||||
|
updateNotificationSchema,
|
||||||
|
} = require("../validate/notification.schema");
|
||||||
|
|
||||||
class NotificationErrorController {
|
class NotificationErrorController {
|
||||||
static async getAll(req, res) {
|
static async getAll(req, res) {
|
||||||
const queryParams = req.query;
|
const queryParams = req.query;
|
||||||
|
|
||||||
const results = await NotificationErrorService.getAllNotification(queryParams);
|
const results = await NotificationErrorService.getAllNotification(
|
||||||
const response = await setResponsePaging(queryParams, results, 'Notification found')
|
queryParams
|
||||||
|
);
|
||||||
|
const response = await setResponsePaging(
|
||||||
|
queryParams,
|
||||||
|
results,
|
||||||
|
"Notification found"
|
||||||
|
);
|
||||||
|
|
||||||
res.status(response.statusCode).json(response);
|
res.status(response.statusCode).json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static async getById(req, res) {
|
static async getById(req, res) {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const results = await NotificationErrorService.getNotificationById(id);
|
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);
|
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));
|
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 results = await NotificationErrorLogService.createNotificationErrorLog(value);
|
||||||
const response = await setResponse(results, 'Notification Error Log created successfully')
|
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 ExcelJS = require("exceljs");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
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 SparepartService = require("../services/sparepart.service");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setResponse,
|
setResponse,
|
||||||
setResponsePaging,
|
setResponsePaging,
|
||||||
@@ -38,25 +45,28 @@ class SparepartController {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return res.status(400).json(setResponse(error, "Validation failed", 400));
|
return res.status(400).json(setResponse(error, "Validation failed", 400));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
const file = req.file;
|
const upload = await imagekit.upload({
|
||||||
const ext = require("path").extname(file.originalname).toLowerCase();
|
file: req.file.buffer,
|
||||||
const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE";
|
fileName: req.file.originalname,
|
||||||
const folder = typeDoc === "PDF" ? "pdf" : "images";
|
folder: "/sparepart",
|
||||||
const pathDocument = `${folder}/${file.filename}`;
|
});
|
||||||
value.sparepart_foto = pathDocument;
|
|
||||||
|
value.sparepart_foto = upload.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
value.userId = req.user.user_id;
|
value.userId = req.user.user_id;
|
||||||
|
|
||||||
const results = await SparepartService.createSparepart(value);
|
const results = await SparepartService.createSparepart(value);
|
||||||
const response = await setResponse(
|
return res
|
||||||
results,
|
.status(201)
|
||||||
"Sparepart created successfully"
|
.json(setResponse(results, "Sparepart created successfully"));
|
||||||
);
|
|
||||||
return res.status(response.statusCode).json(response);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const response = setResponse([], err.message, err.statusCode || 500);
|
return res
|
||||||
res.status(response.statusCode).json(response);
|
.status(err.statusCode || 500)
|
||||||
|
.json(setResponse([], err.message, err.statusCode || 500));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,25 +76,28 @@ class SparepartController {
|
|||||||
if (error) {
|
if (error) {
|
||||||
return res.status(400).json(setResponse(error, "Validation failed", 400));
|
return res.status(400).json(setResponse(error, "Validation failed", 400));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
const file = req.file;
|
const upload = await imagekit.upload({
|
||||||
const ext = require("path").extname(file.originalname).toLowerCase();
|
file: req.file.buffer,
|
||||||
const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE";
|
fileName: req.file.originalname,
|
||||||
const folder = typeDoc === "PDF" ? "pdf" : "images";
|
folder: "/sparepart",
|
||||||
const pathDocument = `${folder}/${file.filename}`;
|
});
|
||||||
value.sparepart_foto = pathDocument;
|
|
||||||
|
value.sparepart_foto = upload.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
value.userId = req.user.user_id;
|
value.userId = req.user.user_id;
|
||||||
|
|
||||||
const results = await SparepartService.updateSparepart(id, value);
|
const results = await SparepartService.updateSparepart(id, value);
|
||||||
const response = await setResponse(
|
return res
|
||||||
results,
|
.status(200)
|
||||||
"Sparepart updated successfully"
|
.json(setResponse(results, "Sparepart updated successfully"));
|
||||||
);
|
|
||||||
res.status(response.statusCode).json(response);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const response = setResponse([], err.message, err.statusCode || 500);
|
return res
|
||||||
res.status(response.statusCode).json(response);
|
.status(err.statusCode || 500)
|
||||||
|
.json(setResponse([], err.message, err.statusCode || 500));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,16 +126,8 @@ class SparepartController {
|
|||||||
|
|
||||||
worksheet.addRows(
|
worksheet.addRows(
|
||||||
results.data.map((item) => ({
|
results.data.map((item) => ({
|
||||||
sparepart_name: item.sparepart_name,
|
...item,
|
||||||
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,
|
|
||||||
sparepart_foto: "",
|
sparepart_foto: "",
|
||||||
sparepart_item_type: item.sparepart_item_type,
|
|
||||||
created_at: item.created_at,
|
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -132,18 +137,30 @@ class SparepartController {
|
|||||||
|
|
||||||
if (!item.sparepart_foto) continue;
|
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)) {
|
if (!ext) ext = "jpg";
|
||||||
const ext = fullPath.split(".").pop().toLowerCase();
|
|
||||||
|
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({
|
const imageId = workbook.addImage({
|
||||||
filename: fullPath,
|
buffer,
|
||||||
extension: ext,
|
extension: ext === "jpg" ? "jpeg" : ext,
|
||||||
});
|
});
|
||||||
|
|
||||||
worksheet.addImage(imageId, {
|
worksheet.addImage(imageId, {
|
||||||
@@ -153,7 +170,6 @@ class SparepartController {
|
|||||||
|
|
||||||
worksheet.getRow(rowNumber).height = 70;
|
worksheet.getRow(rowNumber).height = 70;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
worksheet.getRow(1).eachCell((cell) => {
|
worksheet.getRow(1).eachCell((cell) => {
|
||||||
cell.font = { bold: true };
|
cell.font = { bold: true };
|
||||||
@@ -173,10 +189,124 @@ class SparepartController {
|
|||||||
|
|
||||||
return res.send(buffer);
|
return res.send(buffer);
|
||||||
} catch (error) {
|
} 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);
|
res.status(response.statusCode).json(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SparepartController;
|
module.exports = SparepartController;
|
||||||
|
|||||||
@@ -1,15 +1,55 @@
|
|||||||
const pool = require("../config");
|
const pool = require("../config");
|
||||||
|
|
||||||
// Get error codes by brand ID
|
// 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 = `
|
const queryText = `
|
||||||
SELECT
|
SELECT
|
||||||
|
COUNT(*) OVER() AS total_data,
|
||||||
a.*
|
a.*
|
||||||
FROM brand_code a
|
FROM brand_code a
|
||||||
WHERE a.brand_id = $1 AND a.deleted_at IS NULL
|
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;
|
return result.recordset;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,10 +104,71 @@ const getErrorCodeByIdDb = async (error_code_id) => {
|
|||||||
return result.recordset[0];
|
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 = {
|
module.exports = {
|
||||||
getErrorCodesByBrandIdDb,
|
getErrorCodesByBrandIdDb,
|
||||||
getErrorCodeByIdDb,
|
getErrorCodeByIdDb,
|
||||||
|
getErrorCodeByBrandAndCodeDb,
|
||||||
createErrorCodeDb,
|
createErrorCodeDb,
|
||||||
updateErrorCodeDb,
|
updateErrorCodeDb,
|
||||||
deleteErrorCodeDb,
|
deleteErrorCodeDb,
|
||||||
|
getAllErrorCodesDb,
|
||||||
};
|
};
|
||||||
@@ -1,9 +1,125 @@
|
|||||||
const pool = require("../config");
|
const pool = require("../config");
|
||||||
|
|
||||||
// Get spareparts by brand_id
|
// Get spareparts by error_code_id
|
||||||
const getSparepartsByBrandIdDb = async (brandId) => {
|
const getSparepartsByErrorCodeIdDb = async (errorCodeId) => {
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT
|
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_id,
|
||||||
s.sparepart_name,
|
s.sparepart_name,
|
||||||
s.sparepart_code,
|
s.sparepart_code,
|
||||||
@@ -17,20 +133,22 @@ const getSparepartsByBrandIdDb = async (brandId) => {
|
|||||||
s.sparepart_stok,
|
s.sparepart_stok,
|
||||||
s.created_at,
|
s.created_at,
|
||||||
s.updated_at
|
s.updated_at
|
||||||
FROM brand_spareparts bs
|
FROM brand_sparepart bs
|
||||||
JOIN m_sparepart s ON bs.sparepart_id = s.sparepart_id
|
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 s.deleted_at IS NULL
|
||||||
|
AND ec.deleted_at IS NULL
|
||||||
ORDER BY s.sparepart_name
|
ORDER BY s.sparepart_name
|
||||||
`;
|
`;
|
||||||
const result = await pool.query(queryText, [brandId]);
|
const result = await pool.query(queryText, [brandId]);
|
||||||
return result.recordset;
|
return result.recordset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get brands by sparepart_id
|
// Get brands by sparepart_id (now using error_code_id mapping)
|
||||||
const getBrandsBySparepartIdDb = async (sparepartId) => {
|
const getBrandsBySparepartIdDb = async (sparepartId) => {
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT
|
SELECT DISTINCT
|
||||||
b.brand_id,
|
b.brand_id,
|
||||||
b.brand_name,
|
b.brand_name,
|
||||||
b.brand_type,
|
b.brand_type,
|
||||||
@@ -40,9 +158,13 @@ const getBrandsBySparepartIdDb = async (sparepartId) => {
|
|||||||
b.is_active,
|
b.is_active,
|
||||||
b.created_at,
|
b.created_at,
|
||||||
b.updated_at
|
b.updated_at
|
||||||
FROM brand_spareparts bs
|
FROM brand_sparepart bs
|
||||||
JOIN m_brands b ON bs.brand_id = b.brand_id
|
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
|
WHERE bs.sparepart_id = $1
|
||||||
|
AND s.deleted_at IS NULL
|
||||||
|
AND ec.deleted_at IS NULL
|
||||||
AND b.deleted_at IS NULL
|
AND b.deleted_at IS NULL
|
||||||
ORDER BY b.brand_name
|
ORDER BY b.brand_name
|
||||||
`;
|
`;
|
||||||
@@ -50,81 +172,16 @@ const getBrandsBySparepartIdDb = async (sparepartId) => {
|
|||||||
return result.recordset;
|
return result.recordset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert brand-spareparts relationship
|
// Deprecated functions removed - table structure changed to use error_code_id instead of brand_id
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getSparepartsByBrandIdDb,
|
// New functions using error_code_id
|
||||||
getBrandsBySparepartIdDb,
|
getSparepartsByErrorCodeIdDb,
|
||||||
insertBrandSparepartDb,
|
getErrorCodesBySparepartIdDb,
|
||||||
insertMultipleBrandSparepartsDb,
|
insertErrorCodeSparepartDb,
|
||||||
deleteBrandSparepartDb,
|
insertMultipleErrorCodeSparepartsDb,
|
||||||
deleteAllBrandSparepartsDb,
|
deleteErrorCodeSparepartDb,
|
||||||
updateBrandSparepartsDb,
|
deleteAllErrorCodeSparepartsDb,
|
||||||
checkBrandSparepartExistsDb,
|
updateErrorCodeSparepartsDb,
|
||||||
|
checkErrorCodeSparepartExistsDb,
|
||||||
};
|
};
|
||||||
@@ -24,6 +24,7 @@ const getAllContactDb = async (searchParams = {}) => {
|
|||||||
[
|
[
|
||||||
{ column: "a.contact_name", param: searchParams.name, type: "string" },
|
{ column: "a.contact_name", param: searchParams.name, type: "string" },
|
||||||
{ column: "a.contact_type", param: searchParams.code, type: "string" },
|
{ column: "a.contact_type", param: searchParams.code, type: "string" },
|
||||||
|
{ column: "a.is_active", param: searchParams.active, type: "boolean" },
|
||||||
],
|
],
|
||||||
queryParams
|
queryParams
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -115,15 +115,31 @@ const getHistoryEventDb = async (searchParams = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const checkTableNamedDb = async (tableName) => {
|
const checkTableNamedDb = async (tableName) => {
|
||||||
|
try {
|
||||||
|
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||||
|
throw new Error('Invalid table name format');
|
||||||
|
}
|
||||||
|
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT *
|
SELECT TABLE_NAME, TABLE_SCHEMA, TABLE_TYPE
|
||||||
FROM INFORMATION_SCHEMA.TABLES
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
WHERE TABLE_NAME = $1;`;
|
WHERE TABLE_NAME = $1
|
||||||
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText, [tableName]);
|
const result = await pool.query(queryText, [tableName]);
|
||||||
return result.recordset;
|
return result.recordset;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in checkTableNamedDb:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHistoryValueReportDb = async (tableName, searchParams = {}) => {
|
const getHistoryValueReportDb = async (tableName, searchParams = {}) => {
|
||||||
|
try {
|
||||||
|
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||||
|
throw new Error('Invalid table name format');
|
||||||
|
}
|
||||||
|
|
||||||
let queryParams = [];
|
let queryParams = [];
|
||||||
|
|
||||||
if (searchParams.limit) {
|
if (searchParams.limit) {
|
||||||
@@ -132,10 +148,7 @@ const getHistoryValueReportDb = async (tableName, searchParams = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||||
[
|
["b.tag_name", "CAST(a.tagnum AS VARCHAR)"],
|
||||||
"b.tag_name",
|
|
||||||
"a.tagnum"
|
|
||||||
],
|
|
||||||
searchParams.criteria,
|
searchParams.criteria,
|
||||||
queryParams
|
queryParams
|
||||||
);
|
);
|
||||||
@@ -176,45 +189,144 @@ const getHistoryValueReportDb = async (tableName, searchParams = {}) => {
|
|||||||
|
|
||||||
const result = await pool.query(queryText, queryParams);
|
const result = await pool.query(queryText, queryParams);
|
||||||
|
|
||||||
const total =
|
const total = result.recordset?.length > 0
|
||||||
result?.recordset?.length > 0
|
|
||||||
? parseInt(result.recordset[0].total_data, 10)
|
? parseInt(result.recordset[0].total_data, 10)
|
||||||
: 0;
|
: 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 = {}) => {
|
const getHistoryValueReportPivotDb = async (tableName, searchParams = {}) => {
|
||||||
let from = searchParams.from;
|
try {
|
||||||
let to = searchParams.to;
|
if (!tableName || !/^[a-zA-Z0-9_]+$/.test(tableName)) {
|
||||||
const interval = Number(searchParams.interval ?? 10); // menit
|
throw new Error('Invalid table name format');
|
||||||
const limit = Number(searchParams.limit ?? 10);
|
|
||||||
const page = Number(searchParams.page ?? 1);
|
|
||||||
|
|
||||||
// --- Normalisasi tanggal
|
|
||||||
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
|
|
||||||
FROM m_tags
|
|
||||||
WHERE is_report = 1 AND deleted_at IS NULL
|
|
||||||
`);
|
|
||||||
|
|
||||||
if (tags.recordset.length === 0) {
|
|
||||||
return { data: [], total: 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagNames = tags.recordset.map(r => `[${r.tag_name}]`).join(', ');
|
let from = searchParams.from || '';
|
||||||
const tagNamesColumn = tags.recordset.map(r => `${r.tag_name}`).join(', ');
|
let to = searchParams.to || '';
|
||||||
|
const interval = Math.max(1, Math.min(1440, Number(searchParams.interval ?? 10)));
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
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(',')})`
|
||||||
|
: '';
|
||||||
|
|
||||||
// --- Query utama
|
|
||||||
const queryText = `
|
const queryText = `
|
||||||
DECLARE
|
DECLARE
|
||||||
@fromParam DATETIME = '${from}',
|
@fromParam DATETIME = $1,
|
||||||
@toParam DATETIME = '${to}',
|
@toParam DATETIME = $2,
|
||||||
@intervalParam INT = ${interval};
|
@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 (
|
;WITH TimeSeries AS (
|
||||||
SELECT @fromParam AS waktu
|
SELECT @fromParam AS waktu
|
||||||
@@ -223,17 +335,36 @@ const getHistoryValueReportPivotDb = async (tableName, searchParams = {}) => {
|
|||||||
FROM TimeSeries
|
FROM TimeSeries
|
||||||
WHERE DATEADD(MINUTE, @intervalParam, waktu) <= @toParam
|
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 (
|
Averaged AS (
|
||||||
SELECT
|
SELECT
|
||||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0) AS waktu_group,
|
DATEADD(MINUTE,
|
||||||
b.tag_name,
|
(DATEDIFF(MINUTE, @fromParam, datetime_clean) / @intervalParam) * @intervalParam,
|
||||||
ROUND(AVG(a.val), 4) AS avg_val
|
@fromParam
|
||||||
FROM ${tableName} a
|
) AS waktu_group,
|
||||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
tag_name,
|
||||||
WHERE a.datetime BETWEEN @fromParam AND @toParam
|
AVG(val) AS avg_val
|
||||||
|
FROM CleanData
|
||||||
GROUP BY
|
GROUP BY
|
||||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0),
|
DATEADD(MINUTE,
|
||||||
b.tag_name
|
(DATEDIFF(MINUTE, @fromParam, datetime_clean) / @intervalParam) * @intervalParam,
|
||||||
|
@fromParam
|
||||||
|
),
|
||||||
|
tag_name
|
||||||
),
|
),
|
||||||
Pivoted AS (
|
Pivoted AS (
|
||||||
SELECT
|
SELECT
|
||||||
@@ -244,31 +375,61 @@ const getHistoryValueReportPivotDb = async (tableName, searchParams = {}) => {
|
|||||||
MAX(avg_val)
|
MAX(avg_val)
|
||||||
FOR tag_name IN (${tagNames})
|
FOR tag_name IN (${tagNames})
|
||||||
) AS p
|
) AS p
|
||||||
),
|
)
|
||||||
FinalResult AS (
|
|
||||||
SELECT
|
SELECT
|
||||||
CONVERT(VARCHAR(16), ts.waktu, 120) AS datetime,
|
CONVERT(VARCHAR(19), ts.waktu, 120) AS waktu,
|
||||||
${tagNames}
|
${tagNames}
|
||||||
FROM TimeSeries ts
|
FROM TimeSeries ts
|
||||||
LEFT JOIN Pivoted p ON ts.waktu = p.waktu_group
|
LEFT JOIN Pivoted p ON ts.waktu = p.waktu_group
|
||||||
)
|
ORDER BY ts.waktu
|
||||||
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);
|
OPTION (MAXRECURSION 0);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(queryText);
|
const result = await pool.query(queryText, [from, to, interval]);
|
||||||
|
|
||||||
const total =
|
if (result.recordsets && result.recordsets.length >= 2) {
|
||||||
result?.recordset?.length > 0
|
console.log('Sample averaging data:');
|
||||||
? parseInt(result.recordset[0].total_data, 10)
|
result.recordsets[0].slice(0, 10).forEach(row => {
|
||||||
: 0;
|
console.log(`${row.tag_name} @ ${row.waktu_group}: avg=${row.avg_val}, min=${row.min_val}, max=${row.max_val}, points=${row.data_points}`);
|
||||||
|
});
|
||||||
|
|
||||||
return { data: result.recordset, column: tagNamesColumn, total };
|
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 getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
||||||
@@ -282,18 +443,55 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
|||||||
if (from.length === 10) from += ' 00:00:00';
|
if (from.length === 10) from += ' 00:00:00';
|
||||||
if (to.length === 10) to += ' 23:59:59';
|
if (to.length === 10) to += ' 23:59:59';
|
||||||
|
|
||||||
// --- Ambil semua tag yang di-report
|
let tagQueryParams = [];
|
||||||
const tags = await pool.query(`
|
let tagWhereConditions = [];
|
||||||
SELECT tag_name
|
|
||||||
|
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
|
FROM m_tags
|
||||||
WHERE is_report = 1 AND deleted_at IS NULL
|
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) {
|
if (tags.recordset.length === 0) {
|
||||||
return { data: [] };
|
return { data: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagNames = tags.recordset.map(r => `[${r.tag_name}]`).join(', ');
|
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 = `
|
const queryText = `
|
||||||
DECLARE
|
DECLARE
|
||||||
@@ -316,6 +514,7 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
|||||||
FROM ${tableName} a
|
FROM ${tableName} a
|
||||||
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
LEFT JOIN m_tags b ON a.tagnum = b.tag_number AND b.deleted_at IS NULL
|
||||||
WHERE a.datetime BETWEEN @fromParam AND @toParam
|
WHERE a.datetime BETWEEN @fromParam AND @toParam
|
||||||
|
${tagNumbersFilter}
|
||||||
GROUP BY
|
GROUP BY
|
||||||
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0),
|
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, CAST(a.datetime AS DATETIME)) / @intervalParam * @intervalParam, 0),
|
||||||
b.tag_name
|
b.tag_name
|
||||||
@@ -359,7 +558,6 @@ const getHistoryValueTrendingPivotDb = async (tableName, searchParams = {}) => {
|
|||||||
return { data: nivoData };
|
return { data: nivoData };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getHistoryAlarmDb,
|
getHistoryAlarmDb,
|
||||||
getHistoryEventDb,
|
getHistoryEventDb,
|
||||||
|
|||||||
@@ -1,71 +1,52 @@
|
|||||||
const pool = require("../config");
|
const pool = require("../config");
|
||||||
|
|
||||||
const InsertNotificationErrorDb = async () => {
|
const InsertNotificationErrorDb = async (store) => {
|
||||||
const insertQuery = `
|
const { query: queryText, values } = pool.buildDynamicInsert(
|
||||||
INSERT INTO notification_error (
|
"notification_error",
|
||||||
error_code_id,
|
store
|
||||||
is_active,
|
);
|
||||||
is_delivered,
|
const result = await pool.query(queryText, values);
|
||||||
is_read,
|
const insertedId = result.recordset?.[0]?.inserted_id;
|
||||||
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,
|
|
||||||
|
|
||||||
CONCAT(
|
return insertedId ? await getNotificationByIdDb(insertedId) : null;
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNotificationByIdDb = async (id) => {
|
const getNotificationByIdDb = async (id) => {
|
||||||
const queryText = `
|
const queryText = `
|
||||||
SELECT
|
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
|
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
|
WHERE a.notification_error_id = $1 AND a.deleted_at IS NULL
|
||||||
`;
|
`;
|
||||||
const result = await pool.query(queryText, [id]);
|
const result = await pool.query(queryText, [id]);
|
||||||
return result.recordset[0];
|
return result.recordset[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getAllNotificationDb = async (searchParams = {}) => {
|
const getAllNotificationDb = async (searchParams = {}) => {
|
||||||
let queryParams = [];
|
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) {
|
if (searchParams.limit) {
|
||||||
const page = Number(searchParams.page ?? 1) - 1;
|
const page = Number(searchParams.page ?? 1) - 1;
|
||||||
queryParams = [Number(searchParams.limit ?? 10), page];
|
queryParams = [Number(searchParams.limit ?? 10), page];
|
||||||
@@ -73,13 +54,13 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
|||||||
|
|
||||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
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",
|
||||||
"b.error_code_name",
|
"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,
|
searchParams.criteria,
|
||||||
queryParams
|
queryParams
|
||||||
@@ -88,10 +69,13 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
|||||||
|
|
||||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||||
[
|
[
|
||||||
{ column: "COALESCE(a.is_send, 0)", param: searchParams.is_send, type: "number" },
|
{ column: "a.message_error_issue", param: searchParams.message_error_issue, type: "string" },
|
||||||
{ column: "COALESCE(a.is_delivered, 0)", param: searchParams.is_delivered, type: "number" },
|
{ column: "a.is_send", param: searchParams.is_send, type: "number" },
|
||||||
{ column: "COALESCE(a.is_read, 0)", param: searchParams.is_read, type: "number" },
|
{ column: "a.is_delivered", param: searchParams.is_delivered, type: "number" },
|
||||||
{ column: "COALESCE(a.is_active, 0)", param: searchParams.is_active, 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
|
queryParams
|
||||||
);
|
);
|
||||||
@@ -111,24 +95,35 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
|||||||
|
|
||||||
b.error_code,
|
b.error_code,
|
||||||
b.error_code_name,
|
b.error_code_name,
|
||||||
|
b.error_code_color,
|
||||||
|
b.path_icon,
|
||||||
b.created_at,
|
b.created_at,
|
||||||
|
|
||||||
c.solution_name,
|
c.solution_name,
|
||||||
c.type_solution,
|
c.type_solution,
|
||||||
c.path_solution,
|
c.path_solution,
|
||||||
|
|
||||||
|
d.device_code,
|
||||||
d.device_name,
|
d.device_name,
|
||||||
d.device_location,
|
d.device_location,
|
||||||
|
d.listen_channel,
|
||||||
|
e.brand_name,
|
||||||
|
|
||||||
COALESCE(d.device_name, '') + ' - ' + COALESCE(b.error_code_name, '') AS device_name_error
|
COALESCE(d.device_name, '') + ' - ' + COALESCE(b.error_code_name, '') AS device_name_error
|
||||||
|
|
||||||
FROM notification_error a
|
FROM notification_error a
|
||||||
|
|
||||||
LEFT JOIN brand_code b
|
LEFT JOIN brand_code b
|
||||||
ON a.error_code_id = b.error_code_id AND b.deleted_at IS NULL
|
ON a.error_code_id = b.error_code_id AND b.deleted_at IS NULL
|
||||||
|
|
||||||
LEFT JOIN brand_code_solution c
|
LEFT JOIN brand_code_solution c
|
||||||
ON b.error_code_id = c.error_code_id AND c.deleted_at IS NULL
|
ON b.error_code_id = c.error_code_id AND c.deleted_at IS NULL
|
||||||
|
|
||||||
LEFT JOIN m_device d
|
LEFT JOIN m_device d
|
||||||
ON b.brand_id = d.brand_id AND d.deleted_at IS NULL
|
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
|
WHERE a.deleted_at IS NULL
|
||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
@@ -149,8 +144,46 @@ const getAllNotificationDb = async (searchParams = {}) => {
|
|||||||
return { data: result.recordset, total };
|
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 = {
|
module.exports = {
|
||||||
getNotificationByIdDb,
|
getNotificationByIdDb,
|
||||||
getAllNotificationDb,
|
getAllNotificationDb,
|
||||||
|
InsertNotificationErrorDb,
|
||||||
|
updateNotificationErrorDb,
|
||||||
|
getUsersNotificationErrorDb
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const getAllNotificationErrorLogDb = async () => {
|
|||||||
b.contact_name,
|
b.contact_name,
|
||||||
b.contact_type
|
b.contact_type
|
||||||
FROM notification_error_log a
|
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
|
WHERE a.deleted_at IS NULL
|
||||||
ORDER BY a.notification_error_log_id DESC
|
ORDER BY a.notification_error_log_id DESC
|
||||||
`;
|
`;
|
||||||
@@ -22,7 +22,7 @@ const getNotificationErrorLogByIdDb = async (id) => {
|
|||||||
b.contact_name,
|
b.contact_name,
|
||||||
b.contact_type
|
b.contact_type
|
||||||
FROM notification_error_log a
|
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
|
WHERE a.notification_error_log_id = $1 AND a.deleted_at IS NULL
|
||||||
`;
|
`;
|
||||||
const result = await pool.query(queryText, [id]);
|
const result = await pool.query(queryText, [id]);
|
||||||
@@ -36,7 +36,7 @@ const getNotificationErrorLogByNotificationErrorIdDb = async (notificationErrorI
|
|||||||
b.contact_name,
|
b.contact_name,
|
||||||
b.contact_type
|
b.contact_type
|
||||||
FROM notification_error_log a
|
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
|
WHERE a.notification_error_id = $1 AND a.deleted_at IS NULL
|
||||||
ORDER BY a.created_at DESC
|
ORDER BY a.created_at DESC
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
const pool = require("../config");
|
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 = {}) => {
|
const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
||||||
|
await insertNotificationErrorSparepartDb();
|
||||||
let queryParams = [];
|
let queryParams = [];
|
||||||
|
|
||||||
if (searchParams.limit) {
|
if (searchParams.limit) {
|
||||||
@@ -11,10 +39,10 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
|||||||
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
|
||||||
[
|
[
|
||||||
"a.brand_sparepart_id",
|
"a.brand_sparepart_id",
|
||||||
"a.contact_id",
|
"a.device_id",
|
||||||
|
"a.sparepart_id",
|
||||||
"b.sparepart_name",
|
"b.sparepart_name",
|
||||||
"d.contact_name",
|
"d.device_name",
|
||||||
"d.contact_type"
|
|
||||||
],
|
],
|
||||||
searchParams.criteria,
|
searchParams.criteria,
|
||||||
queryParams
|
queryParams
|
||||||
@@ -25,11 +53,10 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
|||||||
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
|
||||||
[
|
[
|
||||||
{ column: "a.brand_sparepart_id", param: searchParams.name, type: "int" },
|
{ 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: "a.unit", param: searchParams.unit, type: "string" },
|
||||||
{ column: "b.sparepart_name", param: searchParams.device, type: "string" },
|
{ column: "b.sparepart_name", param: searchParams.device, type: "string" },
|
||||||
{ column: "d.contact_name", param: searchParams.device, type: "string" },
|
{ column: "d.device_name", param: searchParams.device, type: "string" },
|
||||||
{ column: "d.contact_type", param: searchParams.device, type: "string" },
|
|
||||||
],
|
],
|
||||||
queryParams
|
queryParams
|
||||||
);
|
);
|
||||||
@@ -41,14 +68,23 @@ const getAllNotificationErrorSparepartDb = async (searchParams = {}) => {
|
|||||||
COUNT(*) OVER() AS total_data,
|
COUNT(*) OVER() AS total_data,
|
||||||
a.*,
|
a.*,
|
||||||
b.sparepart_name,
|
b.sparepart_name,
|
||||||
b.brand_sparepart_description,
|
b.sparepart_foto,
|
||||||
d.contact_name,
|
b.sparepart_stok,
|
||||||
d.contact_type
|
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
|
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
|
WHERE a.deleted_at IS NULL
|
||||||
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
|
||||||
@@ -72,12 +108,24 @@ const getNotificationErrorSparepartByIdDb = async (id) => {
|
|||||||
SELECT
|
SELECT
|
||||||
a.*,
|
a.*,
|
||||||
b.sparepart_name,
|
b.sparepart_name,
|
||||||
b.brand_sparepart_description,
|
b.sparepart_foto,
|
||||||
d.contact_name,
|
b.sparepart_stok,
|
||||||
d.contact_type
|
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
|
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
|
WHERE a.notification_error_sparepart_id = $1
|
||||||
AND a.deleted_at IS NULL
|
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;
|
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 = {
|
module.exports = {
|
||||||
getAllSparepartDb,
|
getAllSparepartDb,
|
||||||
getSparepartByIdDb,
|
getSparepartByIdDb,
|
||||||
|
getSparepartsByIdsDb,
|
||||||
checkSparepartNameExistsDb,
|
checkSparepartNameExistsDb,
|
||||||
createSparepartDb,
|
createSparepartDb,
|
||||||
updateSparepartDb,
|
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.post('/register', AuthController.register);
|
||||||
router.get('/generate-captcha', AuthController.generateCaptcha);
|
router.get('/generate-captcha', AuthController.generateCaptcha);
|
||||||
router.post('/refresh-token', AuthController.refreshToken);
|
router.post('/refresh-token', AuthController.refreshToken);
|
||||||
|
router.post('/verify-redirect', AuthController.verifyTokenRedirect);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -2,7 +2,6 @@ const express = require('express');
|
|||||||
const BrandController = require('../controllers/brand.controller');
|
const BrandController = require('../controllers/brand.controller');
|
||||||
const verifyToken = require('../middleware/verifyToken');
|
const verifyToken = require('../middleware/verifyToken');
|
||||||
const verifyAccess = require('../middleware/verifyAccess');
|
const verifyAccess = require('../middleware/verifyAccess');
|
||||||
const upload = require('../middleware/uploads');
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -12,7 +11,7 @@ router.route('/')
|
|||||||
|
|
||||||
router.route('/:id')
|
router.route('/:id')
|
||||||
.get(verifyToken.verifyAccessToken, BrandController.getById)
|
.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);
|
.delete(verifyToken.verifyAccessToken, verifyAccess(), BrandController.delete);
|
||||||
|
|
||||||
module.exports = router;
|
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 notificationErrorSparepart = require("./notification_error_sparepart.route")
|
||||||
const sparepart = require("./sparepart.route")
|
const sparepart = require("./sparepart.route")
|
||||||
const notificationErrorLog = require("./notification_error_log.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("/auth", auth);
|
||||||
router.use("/user", users);
|
router.use("/user", users);
|
||||||
@@ -38,5 +40,7 @@ router.use("/notification", notificationError)
|
|||||||
router.use("/notification-sparepart", notificationErrorSparepart)
|
router.use("/notification-sparepart", notificationErrorSparepart)
|
||||||
router.use("/sparepart", sparepart)
|
router.use("/sparepart", sparepart)
|
||||||
router.use("/notification-log", notificationErrorLog)
|
router.use("/notification-log", notificationErrorLog)
|
||||||
|
router.use("/notification-user", notificationErrorUser)
|
||||||
|
router.use("/error-code", errorCode)
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ const router = express.Router();
|
|||||||
|
|
||||||
router
|
router
|
||||||
.route('/')
|
.route('/')
|
||||||
.get(verifyToken.verifyAccessToken, NotificationErrorController.getAll)
|
.get(verifyToken.verifyAccessToken,verifyAccess(), NotificationErrorController.getAll)
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/')
|
||||||
|
.post(verifyToken.verifyAccessToken,verifyAccess(), NotificationErrorController.create)
|
||||||
|
|
||||||
router
|
router
|
||||||
.route('/:id')
|
.route('/:id')
|
||||||
.get(verifyToken.verifyAccessToken, NotificationErrorController.getById)
|
.get(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorController.getById)
|
||||||
|
.put(verifyToken.verifyAccessToken, verifyAccess(), NotificationErrorController.update)
|
||||||
|
|
||||||
module.exports = router;
|
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 SparepartController = require("../controllers/sparepart.controller");
|
||||||
const verifyToken = require("../middleware/verifyToken");
|
const verifyToken = require("../middleware/verifyToken");
|
||||||
const verifyAccess = require("../middleware/verifyAccess");
|
const verifyAccess = require("../middleware/verifyAccess");
|
||||||
const upload = require("../middleware/uploads");
|
const upload = require("../middleware/upload");
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@@ -11,6 +12,14 @@ router.get(
|
|||||||
SparepartController.exportExcel
|
SparepartController.exportExcel
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/import",
|
||||||
|
verifyToken.verifyAccessToken,
|
||||||
|
verifyAccess(),
|
||||||
|
upload.single("file"),
|
||||||
|
SparepartController.importExcel
|
||||||
|
);
|
||||||
|
|
||||||
router
|
router
|
||||||
.route("/")
|
.route("/")
|
||||||
.get(verifyToken.verifyAccessToken, SparepartController.getAll)
|
.get(verifyToken.verifyAccessToken, SparepartController.getAll)
|
||||||
|
|||||||
@@ -2,36 +2,11 @@
|
|||||||
const {
|
const {
|
||||||
getAllBrandsDb,
|
getAllBrandsDb,
|
||||||
getBrandByIdDb,
|
getBrandByIdDb,
|
||||||
getBrandByNameDb,
|
|
||||||
createBrandDb,
|
createBrandDb,
|
||||||
updateBrandDb,
|
updateBrandDb,
|
||||||
deleteBrandDb,
|
deleteBrandDb,
|
||||||
checkBrandNameExistsDb,
|
checkBrandNameExistsDb,
|
||||||
} = require("../db/brand.db");
|
} = 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");
|
const { ErrorHandler } = require("../helpers/error");
|
||||||
|
|
||||||
class BrandService {
|
class BrandService {
|
||||||
@@ -40,86 +15,22 @@ class BrandService {
|
|||||||
try {
|
try {
|
||||||
const results = await getAllBrandsDb(param);
|
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 {
|
return {
|
||||||
...results,
|
...results,
|
||||||
data: brandsWithSpareparts
|
data: results.data
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
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) {
|
static async getBrandById(id) {
|
||||||
try {
|
try {
|
||||||
const brand = await getBrandByIdDb(id);
|
const brand = await getBrandByIdDb(id);
|
||||||
if (!brand) throw new ErrorHandler(404, "Brand not found");
|
if (!brand) throw new ErrorHandler(404, "Brand not found");
|
||||||
|
|
||||||
// Get spareparts for this brand
|
return 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,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
}
|
}
|
||||||
@@ -127,10 +38,8 @@ class BrandService {
|
|||||||
|
|
||||||
|
|
||||||
// Create brand
|
// Create brand
|
||||||
static async createBrandWithFullData(data) {
|
static async createBrand(data) {
|
||||||
try {
|
try {
|
||||||
if (!data || typeof data !== "object") data = {};
|
|
||||||
|
|
||||||
if (data.brand_name) {
|
if (data.brand_name) {
|
||||||
const brandExists = await checkBrandNameExistsDb(data.brand_name);
|
const brandExists = await checkBrandNameExistsDb(data.brand_name);
|
||||||
if (brandExists) {
|
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 = {
|
const brandData = {
|
||||||
brand_name: data.brand_name,
|
brand_name: data.brand_name,
|
||||||
brand_type: data.brand_type,
|
brand_type: data.brand_type,
|
||||||
brand_manufacture: data.brand_manufacture,
|
brand_manufacture: data.brand_manufacture,
|
||||||
brand_model: data.brand_model,
|
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,
|
created_by: data.created_by,
|
||||||
};
|
};
|
||||||
|
|
||||||
const createdBrand = await createBrandDb(brandData);
|
const createdBrand = await createBrandDb(brandData);
|
||||||
if (!createdBrand) {
|
if (!createdBrand) {
|
||||||
throw new Error("Failed to create brand");
|
throw new ErrorHandler(500, "Failed to create brand");
|
||||||
}
|
}
|
||||||
|
|
||||||
const brandId = createdBrand.brand_id;
|
return createdBrand;
|
||||||
|
|
||||||
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;
|
|
||||||
} catch (error) {
|
} 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
|
// Update brand
|
||||||
static async updateBrandWithFullData(id, data) {
|
static async updateBrand(id, data) {
|
||||||
try {
|
try {
|
||||||
const existingBrand = await getBrandByIdDb(id);
|
const existingBrand = await getBrandByIdDb(id);
|
||||||
if (!existingBrand) throw new ErrorHandler(404, "Brand not found");
|
if (!existingBrand) throw new ErrorHandler(404, "Brand not found");
|
||||||
@@ -250,143 +99,22 @@ class BrandService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const brandData = {
|
const brandData = {
|
||||||
brand_name: data.brand_name,
|
brand_name: data.brand_name || existingBrand.brand_name,
|
||||||
brand_type: data.brand_type,
|
brand_type: data.brand_type !== undefined ? data.brand_type : existingBrand.brand_type,
|
||||||
brand_manufacture: data.brand_manufacture,
|
brand_manufacture: data.brand_manufacture !== undefined ? data.brand_manufacture : existingBrand.brand_manufacture,
|
||||||
brand_model: data.brand_model,
|
brand_model: data.brand_model !== undefined ? data.brand_model : existingBrand.brand_model,
|
||||||
is_active: data.is_active,
|
is_active: data.is_active !== undefined ? data.is_active : existingBrand.is_active,
|
||||||
updated_by: data.updated_by,
|
updated_by: data.updated_by,
|
||||||
};
|
};
|
||||||
|
|
||||||
await updateBrandDb(existingBrand.brand_name, brandData);
|
const updatedBrand = await updateBrandDb(existingBrand.brand_name, brandData);
|
||||||
|
if (!updatedBrand) {
|
||||||
if (data.spareparts !== undefined) {
|
throw new ErrorHandler(500, "Failed to update brand");
|
||||||
await updateBrandSparepartsDb(existingBrand.brand_id, data.spareparts || [], data.updated_by);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.error_code && Array.isArray(data.error_code)) {
|
return updatedBrand;
|
||||||
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;
|
|
||||||
} catch (error) {
|
} 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 { getSubSectionByIdDb } = require('../db/plant_sub_section.db');
|
||||||
const { ErrorHandler } = require('../helpers/error');
|
const { ErrorHandler } = require('../helpers/error');
|
||||||
|
|
||||||
@@ -7,92 +14,126 @@ class HistoryValue {
|
|||||||
static async getAllHistoryAlarm(param) {
|
static async getAllHistoryAlarm(param) {
|
||||||
try {
|
try {
|
||||||
const results = await getHistoryAlarmDb(param);
|
const results = await getHistoryAlarmDb(param);
|
||||||
|
return results;
|
||||||
results.data.map(element => {
|
|
||||||
});
|
|
||||||
|
|
||||||
return results
|
|
||||||
} catch (error) {
|
} 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) {
|
static async getAllHistoryEvent(param) {
|
||||||
try {
|
try {
|
||||||
const results = await getHistoryEventDb(param);
|
const results = await getHistoryEventDb(param);
|
||||||
|
return results;
|
||||||
results.data.map(element => {
|
|
||||||
});
|
|
||||||
|
|
||||||
return results
|
|
||||||
} catch (error) {
|
} 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) {
|
static async getHistoryValueReport(param) {
|
||||||
try {
|
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);
|
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 (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
if (!tableNameValue) {
|
||||||
|
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||||
|
}
|
||||||
|
|
||||||
const results = await getHistoryValueReportDb(tabelExist[0]?.TABLE_NAME, param);
|
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||||
|
|
||||||
results.data.map(element => {
|
if (!tableExist || tableExist.length < 1) {
|
||||||
});
|
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||||
|
}
|
||||||
|
|
||||||
return results
|
const results = await getHistoryValueReportDb(tableExist[0].TABLE_NAME, param);
|
||||||
|
return results;
|
||||||
} catch (error) {
|
} 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) {
|
static async getHistoryValueReportPivot(param) {
|
||||||
try {
|
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);
|
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 (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
if (!tableNameValue) {
|
||||||
|
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||||
|
}
|
||||||
|
|
||||||
const results = await getHistoryValueReportPivotDb(tabelExist[0]?.TABLE_NAME, param);
|
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||||
|
|
||||||
results.data.map(element => {
|
if (!tableExist || tableExist.length < 1) {
|
||||||
});
|
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||||
|
}
|
||||||
|
|
||||||
return results
|
const results = await getHistoryValueReportPivotDb(tableExist[0].TABLE_NAME, param);
|
||||||
|
return results;
|
||||||
} catch (error) {
|
} 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) {
|
static async getHistoryValueTrendingPivot(param) {
|
||||||
try {
|
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);
|
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 (tabelExist.length < 1) throw new ErrorHandler(404, 'Value not found');
|
if (!tableNameValue) {
|
||||||
|
throw new ErrorHandler(404, 'Table name not configured for this sub section');
|
||||||
|
}
|
||||||
|
|
||||||
const results = await getHistoryValueTrendingPivotDb(tabelExist[0]?.TABLE_NAME, param);
|
const tableExist = await checkTableNamedDb(tableNameValue);
|
||||||
|
|
||||||
results.data.map(element => {
|
if (!tableExist || tableExist.length < 1) {
|
||||||
});
|
throw new ErrorHandler(404, `Value table '${tableNameValue}' not found`);
|
||||||
|
}
|
||||||
|
|
||||||
return results
|
const results = await getHistoryValueTrendingPivotDb(tableExist[0].TABLE_NAME, param);
|
||||||
|
return results;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new ErrorHandler(error.statusCode, error.message);
|
throw new ErrorHandler(
|
||||||
|
error.statusCode || 500,
|
||||||
|
error.message || 'Error fetching history value trending pivot'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
const {
|
const {
|
||||||
getAllNotificationDb,
|
getAllNotificationDb,
|
||||||
getNotificationByIdDb,
|
getNotificationByIdDb,
|
||||||
|
InsertNotificationErrorDb,
|
||||||
|
getUsersNotificationErrorDb,
|
||||||
|
updateNotificationErrorDb,
|
||||||
} = require('../db/notification_error.db');
|
} = require('../db/notification_error.db');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -17,6 +20,10 @@ const {
|
|||||||
getNotificationErrorLogByNotificationErrorIdDb,
|
getNotificationErrorLogByNotificationErrorIdDb,
|
||||||
} = require('../db/notification_error_log.db');
|
} = require('../db/notification_error_log.db');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getSparepartsByErrorCodeIdDb,
|
||||||
|
} = require('../db/brand_sparepart.db');
|
||||||
|
|
||||||
const { getFileUploadByPathDb } = require('../db/file_uploads.db');
|
const { getFileUploadByPathDb } = require('../db/file_uploads.db');
|
||||||
|
|
||||||
const { ErrorHandler } = require('../helpers/error');
|
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
|
// Get notification by ID
|
||||||
static async getNotificationById(id) {
|
static async getNotificationById(id) {
|
||||||
try {
|
try {
|
||||||
@@ -45,6 +64,8 @@ class NotificationService {
|
|||||||
throw new ErrorHandler(404, 'Notification not found');
|
throw new ErrorHandler(404, 'Notification not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const usersNotification = (await getUsersNotificationErrorDb(id))|| [];
|
||||||
|
|
||||||
// Get error code details if error_code_id exists
|
// Get error code details if error_code_id exists
|
||||||
if (notification.error_code_id) {
|
if (notification.error_code_id) {
|
||||||
const errorCode = await getErrorCodeByIdDb(notification.error_code_id);
|
const errorCode = await getErrorCodeByIdDb(notification.error_code_id);
|
||||||
@@ -53,6 +74,8 @@ class NotificationService {
|
|||||||
// Get solutions for this error code
|
// Get solutions for this error code
|
||||||
const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || [];
|
const solutions = (await getSolutionsByErrorCodeIdDb(errorCode.error_code_id)) || [];
|
||||||
|
|
||||||
|
const spareparts = (await getSparepartsByErrorCodeIdDb(errorCode.error_code_id)) || [];
|
||||||
|
|
||||||
const solutionsWithDetails = await Promise.all(
|
const solutionsWithDetails = await Promise.all(
|
||||||
solutions.map(async (solution) => {
|
solutions.map(async (solution) => {
|
||||||
let fileData = null;
|
let fileData = null;
|
||||||
@@ -71,9 +94,11 @@ class NotificationService {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
notification.error_code = {
|
notification.error_code = {
|
||||||
...errorCode,
|
...errorCode,
|
||||||
solution: solutionsWithDetails
|
solution: solutionsWithDetails,
|
||||||
|
spareparts: spareparts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +106,8 @@ class NotificationService {
|
|||||||
// Get activity logs for this notification
|
// Get activity logs for this notification
|
||||||
const notificationLogs = (await getNotificationErrorLogByNotificationErrorIdDb(id)) || [];
|
const notificationLogs = (await getNotificationErrorLogByNotificationErrorIdDb(id)) || [];
|
||||||
|
|
||||||
|
notification.users = usersNotification;
|
||||||
|
|
||||||
notification.activity_logs = notificationLogs;
|
notification.activity_logs = notificationLogs;
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
@@ -88,6 +115,24 @@ class NotificationService {
|
|||||||
throw new ErrorHandler(error.statusCode, error.message);
|
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;
|
module.exports = NotificationService;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class NotificationErrorLogService {
|
|||||||
|
|
||||||
const store = {
|
const store = {
|
||||||
notification_error_id: data.notification_error_id,
|
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,
|
notification_error_log_description: data.notification_error_log_description,
|
||||||
created_by: data.created_by
|
created_by: data.created_by
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,31 +6,23 @@ const {
|
|||||||
deleteNotificationErrorSparepartDb,
|
deleteNotificationErrorSparepartDb,
|
||||||
} = require("../db/notification_error_sparepart.db");
|
} = require("../db/notification_error_sparepart.db");
|
||||||
|
|
||||||
const { getContactByIdDb } = require("../db/contact.db");
|
|
||||||
const { ErrorHandler } = require("../helpers/error");
|
const { ErrorHandler } = require("../helpers/error");
|
||||||
|
|
||||||
class NotificationErrorSparepartService {
|
class NotificationErrorSparepartService {
|
||||||
static _checkAccess(contactType) {
|
|
||||||
if (contactType !== "gudang") {
|
static async getAll(param) {
|
||||||
throw new ErrorHandler(
|
try {
|
||||||
403,
|
const results = await getAllNotificationErrorSparepartDb(param);
|
||||||
"Akses ditolak. Hanya contact_type 'gudang' yang dapat getAll/create/update/delete."
|
|
||||||
);
|
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) {
|
static async getById(id) {
|
||||||
const result = await getNotificationErrorSparepartByIdDb(id);
|
const result = await getNotificationErrorSparepartByIdDb(id);
|
||||||
|
|
||||||
@@ -41,45 +33,36 @@ class NotificationErrorSparepartService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async create(data) {
|
static async create(data) {
|
||||||
const contactResult = await getContactByIdDb(data.contact_id);
|
try {
|
||||||
|
if (!data || typeof data !== 'object') data = {};
|
||||||
|
|
||||||
if (!contactResult || contactResult.length < 1)
|
const result = await createNotificationErrorSparepartDb(data);
|
||||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
|
||||||
|
|
||||||
const contact = contactResult[0];
|
return result;
|
||||||
|
} catch (error) {
|
||||||
this._checkAccess(contact.contact_type);
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
return await createNotificationErrorSparepartDb(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async update(id, data) {
|
static async update(id, data) {
|
||||||
const contactResult = await getContactByIdDb(data.contact_id);
|
try {
|
||||||
|
if (!data || typeof data !== 'object') data = {};
|
||||||
|
|
||||||
if (!contactResult || contactResult.length < 1)
|
const dataExist = await getNotificationErrorSparepartByIdDb(id);
|
||||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
|
||||||
|
|
||||||
const contact = contactResult[0];
|
if (dataExist.length < 1) {
|
||||||
|
throw new ErrorHandler(404, 'Notification Error Sparepart not found');
|
||||||
this._checkAccess(contact.contact_type);
|
|
||||||
|
|
||||||
const exist = await getNotificationErrorSparepartByIdDb(id);
|
|
||||||
|
|
||||||
if (exist.length < 1)
|
|
||||||
throw new ErrorHandler(404, "Notification Error Sparepart not found");
|
|
||||||
|
|
||||||
return await updateNotificationErrorSparepartDb(id, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async delete(id, contact_id) {
|
const result = await updateNotificationErrorSparepartDb(id, data);
|
||||||
const contactResult = await getContactByIdDb(contact_id);
|
|
||||||
|
|
||||||
if (!contactResult || contactResult.length < 1)
|
return result;
|
||||||
throw new ErrorHandler(404, "Contact tidak ditemukan");
|
} catch (error) {
|
||||||
|
throw new ErrorHandler(error.statusCode, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const contact = contactResult[0];
|
static async delete(id) {
|
||||||
|
|
||||||
this._checkAccess(contact.contact_type);
|
|
||||||
|
|
||||||
const exist = await getNotificationErrorSparepartByIdDb(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_type: Joi.string().max(50).optional().allow(""),
|
||||||
brand_manufacture: Joi.string().max(100).required(),
|
brand_manufacture: Joi.string().max(100).required(),
|
||||||
brand_model: Joi.string().max(100).optional().allow(""),
|
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(""),
|
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
|
// Update Brand Validation
|
||||||
const updateBrandSchema = Joi.object({
|
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_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(""),
|
brand_model: Joi.string().max(100).optional().allow(""),
|
||||||
is_active: Joi.boolean().required(),
|
|
||||||
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(),
|
is_active: Joi.boolean().optional(),
|
||||||
})
|
description: Joi.string().max(255).optional().allow(""),
|
||||||
)
|
|
||||||
.min(1)
|
|
||||||
.required(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
}).min(1);
|
}).min(1);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -13,20 +13,20 @@ const insertContactSchema = Joi.object({
|
|||||||
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
||||||
}),
|
}),
|
||||||
is_active: Joi.boolean().required(),
|
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({
|
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()
|
contact_phone: Joi.string()
|
||||||
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
.pattern(/^(?:\+62|0)8\d{7,10}$/)
|
||||||
.required()
|
.optional()
|
||||||
.messages({
|
.messages({
|
||||||
"string.pattern.base":
|
"string.pattern.base":
|
||||||
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
"Phone number must be a valid Indonesian number in format +628XXXXXXXXX",
|
||||||
}),
|
}),
|
||||||
is_active: Joi.boolean().optional(),
|
is_active: Joi.boolean().optional(),
|
||||||
contact_type: Joi.string().max(255).optional()
|
contact_type: Joi.string().max(255).optional().allow(null)
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ const insertDeviceSchema = Joi.object({
|
|||||||
is_active: Joi.boolean().required(),
|
is_active: Joi.boolean().required(),
|
||||||
brand_id: Joi.number().integer().min(1),
|
brand_id: Joi.number().integer().min(1),
|
||||||
device_location: Joi.string().max(100).required(),
|
device_location: Joi.string().max(100).required(),
|
||||||
device_description: Joi.string().required(),
|
device_description: Joi.string(),
|
||||||
ip_address: Joi.string()
|
ip_address: Joi.string()
|
||||||
.ip({ version: ['ipv4', 'ipv6'] })
|
.ip({ version: ['ipv4', 'ipv6'] })
|
||||||
.required()
|
.required()
|
||||||
.messages({
|
.messages({
|
||||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||||
})
|
}),
|
||||||
|
listen_channel: Joi.string().max(100).required()
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateDeviceSchema = Joi.object({
|
const updateDeviceSchema = Joi.object({
|
||||||
@@ -28,11 +29,11 @@ const updateDeviceSchema = Joi.object({
|
|||||||
.ip({ version: ['ipv4', 'ipv6'] })
|
.ip({ version: ['ipv4', 'ipv6'] })
|
||||||
.messages({
|
.messages({
|
||||||
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
'string.ip': 'IP address must be a valid IPv4 or IPv6 address'
|
||||||
})
|
}),
|
||||||
|
listen_channel: Joi.string().max(100)
|
||||||
}).min(1);
|
}).min(1);
|
||||||
|
|
||||||
|
|
||||||
// ✅ Export dengan CommonJS
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
insertDeviceSchema, updateDeviceSchema
|
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",
|
"number.base": "error_code_id must be a number",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
message_error_issue: Joi.string().max(255).optional(),
|
||||||
|
|
||||||
is_send: Joi.boolean().required().messages({
|
is_send: Joi.boolean().required().messages({
|
||||||
"any.required": "is_send is required",
|
"any.required": "is_send is required",
|
||||||
"boolean.base": "is_send must be a boolean",
|
"boolean.base": "is_send must be a boolean",
|
||||||
@@ -38,10 +40,6 @@ const insertNotificationSchema = Joi.object({
|
|||||||
// Update Notification Schema
|
// Update Notification Schema
|
||||||
// ========================
|
// ========================
|
||||||
const updateNotificationSchema = Joi.object({
|
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({
|
is_send: Joi.boolean().optional().messages({
|
||||||
"boolean.base": "is_send must be a boolean",
|
"boolean.base": "is_send must be a boolean",
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const Joi = require("joi");
|
|||||||
|
|
||||||
const insertNotificationErrorLogSchema = Joi.object({
|
const insertNotificationErrorLogSchema = Joi.object({
|
||||||
notification_error_id: Joi.number().integer().required(),
|
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()
|
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_model: Joi.string().max(255).optional(),
|
||||||
sparepart_foto: Joi.string().max(255).optional().allow(""),
|
sparepart_foto: Joi.string().max(255).optional().allow(""),
|
||||||
sparepart_item_type: Joi.string().max(255).optional(),
|
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_unit: Joi.string().max(255).optional(),
|
||||||
sparepart_merk: Joi.string().max(255).optional(),
|
sparepart_merk: Joi.string().max(255).optional(),
|
||||||
sparepart_stok: 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_model: Joi.string().max(255).optional(),
|
||||||
sparepart_foto: Joi.string().max(255).optional().allow(''),
|
sparepart_foto: Joi.string().max(255).optional().allow(''),
|
||||||
sparepart_item_type: Joi.string().max(255).optional(),
|
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_unit: Joi.string().max(255).optional(),
|
||||||
sparepart_merk: Joi.string().max(255).optional(),
|
sparepart_merk: Joi.string().max(255).optional(),
|
||||||
sparepart_stok: Joi.string().max(255).optional(),
|
sparepart_stok: Joi.string().max(255).optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user