647 lines
23 KiB
JavaScript
647 lines
23 KiB
JavaScript
import express from 'express';
|
|
import mysql from 'mysql2/promise';
|
|
import bcrypt from 'bcrypt';
|
|
import cors from 'cors';
|
|
import QRCode from 'qrcode';
|
|
|
|
// --- IMPORT UNTUK FILE UPLOAD ---
|
|
import multer from 'multer';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const app = express();
|
|
app.use(express.json());
|
|
app.use(cors()); // CORS aktif, React aman!
|
|
|
|
// --- BUAT FOLDER UPLOADS SECARA OTOMATIS JIKALAU BELUM ADA ---
|
|
const uploadDir = path.join(__dirname, 'uploads');
|
|
if (!fs.existsSync(uploadDir)) {
|
|
fs.mkdirSync(uploadDir);
|
|
}
|
|
|
|
// --- AKSI STATISKAN FOLDER UPLOADS AGAR GAMBAR BISA DIAKSES FRONTEND ---
|
|
app.use('/uploads', express.static(uploadDir));
|
|
|
|
// --- SETTING MULTER UNTUK MENANGKAP FOTO DARI FRONTEND ---
|
|
const storage = multer.diskStorage({
|
|
destination: (req, file, cb) => {
|
|
cb(null, uploadDir);
|
|
},
|
|
filename: (req, file, cb) => {
|
|
const namaUnik = `foto-${Date.now()}${path.extname(file.originalname)}`;
|
|
cb(null, namaUnik);
|
|
}
|
|
});
|
|
const upload = multer({ storage: storage });
|
|
|
|
// --- KONFIGURASI DATABASE CIFO ---
|
|
const db = mysql.createPool({
|
|
host: "sql.cifo.co.id",
|
|
port: 3307,
|
|
user: "pkl_bast",
|
|
password: "PklCifo2026",
|
|
database: "pkl_bast",
|
|
waitForConnections: true,
|
|
connectionLimit: 10,
|
|
queueLimit: 0
|
|
});
|
|
|
|
// Cek Koneksi Database
|
|
db.getConnection()
|
|
.then(conn => {
|
|
console.log("Koneksi CIFO Berhasil. Siap Tempur!");
|
|
conn.release();
|
|
})
|
|
.catch(err => {
|
|
console.error("GAGAL KONEK:", err.message);
|
|
});
|
|
|
|
// =======================================================
|
|
// 1. MODUL AUTHENTICATION (REGISTER & LOGIN)
|
|
// =======================================================
|
|
|
|
// API REGISTER
|
|
app.post("/api/register", async (req, res) => {
|
|
const { nama_lengkap, username, password, role, email, lembaga } = req.body;
|
|
|
|
if (!username || !password || !nama_lengkap) {
|
|
return res.status(400).json({ success: false, message: "Nama, Username, dan Password wajib diisi Bang!" });
|
|
}
|
|
|
|
try {
|
|
const [cekUser] = await db.query("SELECT * FROM User WHERE username = ?", [username]);
|
|
if (cekUser.length > 0) {
|
|
return res.status(400).json({ success: false, message: "Username sudah ada, cari yang lain!" });
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
|
|
const sql = `INSERT INTO User (nama_lengkap, username, password, role, email, lembaga)
|
|
VALUES (?, ?, ?, ?, ?, ?)`;
|
|
|
|
await db.query(sql, [
|
|
nama_lengkap,
|
|
username,
|
|
hashedPassword,
|
|
role || 'User',
|
|
email || null,
|
|
lembaga || null
|
|
]);
|
|
|
|
res.json({ success: true, message: "User Berhasil Terdaftar! Silakan Login." });
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// API LOGIN RESMI
|
|
app.post('/api/login', async (req, res) => {
|
|
const { username, password } = req.body;
|
|
|
|
if (!username || !password) {
|
|
return res.status(400).json({ success: false, message: "Username dan Password wajib diisi, Bang!" });
|
|
}
|
|
|
|
try {
|
|
const [rows] = await db.query("SELECT * FROM User WHERE username = ?", [username]);
|
|
|
|
if (rows.length === 0) {
|
|
return res.status(401).json({ success: false, message: "Username tidak ditemukan, Bang!" });
|
|
}
|
|
|
|
const user = rows[0];
|
|
const passwordCocok = await bcrypt.compare(password, user.password);
|
|
|
|
if (!passwordCocok) {
|
|
return res.status(401).json({ success: false, message: "Password salah, Bang!" });
|
|
}
|
|
|
|
const tokenKontrak = `SECRET_TOKEN_UKK_${user.user_id}_${Date.now()}`;
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Login Berhasil! Selamat Datang Kembali.",
|
|
token: tokenKontrak,
|
|
data: {
|
|
user_id: user.user_id,
|
|
username: user.username,
|
|
role: user.role,
|
|
nama_lengkap: user.nama_lengkap,
|
|
email: user.email
|
|
}
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// API GET PROFILE USER LOGIN
|
|
app.get('/api/user/:id', async (req, res) => {
|
|
|
|
const { id } = req.params;
|
|
|
|
try {
|
|
|
|
const [rows] = await db.query(
|
|
`
|
|
SELECT
|
|
user_id,
|
|
nama_lengkap,
|
|
username,
|
|
role,
|
|
email
|
|
FROM User
|
|
WHERE user_id = ?
|
|
`,
|
|
[id]
|
|
);
|
|
|
|
if (rows.length === 0) {
|
|
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: "User tidak ditemukan!"
|
|
});
|
|
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: rows[0]
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error);
|
|
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error.message
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// =======================================================
|
|
// 2. MODUL MANAJEMEN USER (CRUD USER)
|
|
// =======================================================
|
|
|
|
app.get("/api/users", async (req, res) => {
|
|
try {
|
|
const [rows] = await db.query("SELECT user_id, nama_lengkap, username, role, email, lembaga FROM User");
|
|
res.json({ success: true, data: rows });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
app.put("/api/users/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
const { nama_lengkap, username, role, email, lembaga } = req.body;
|
|
try {
|
|
await db.query(
|
|
"UPDATE User SET nama_lengkap = ?, username = ?, role = ?, email = ?, lembaga = ? WHERE user_id = ?",
|
|
[nama_lengkap, username, role, email, lembaga, id]
|
|
);
|
|
res.json({ success: true, message: "User Berhasil Diupdate!" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
app.delete("/api/users/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
await db.query("DELETE FROM User WHERE user_id = ?", [id]);
|
|
res.json({ success: true, message: "User Berhasil Dihapus!" });
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "Gagal hapus! User mungkin terikat dengan data transaksi lain."
|
|
});
|
|
}
|
|
});
|
|
|
|
// =======================================================
|
|
// 3. MODUL MASTER DATA BARANG (CRUD BARANG)
|
|
// =======================================================
|
|
|
|
app.get("/api/barang", async (req, res) => {
|
|
try {
|
|
const sql = `
|
|
SELECT b.barang_id, b.kode_barang, b.nama_barang, b.stok, b.status_barang, b.qr_image, b.foto_barang,
|
|
k.nama_kategori, l.nama_lokasi
|
|
FROM Barang b
|
|
LEFT JOIN Kategori k ON b.kategori_id = k.kategori_id
|
|
LEFT JOIN Lokasi l ON b.lokasi_id = l.lokasi_id
|
|
ORDER BY b.barang_id DESC`;
|
|
const [rows] = await db.query(sql);
|
|
res.json({ success: true, data: rows });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// GET DETAIL BARANG KHUSUS UNTUK SCAN QR CODE
|
|
app.get("/api/barang/detail/:kode", async (req, res) => {
|
|
const { kode } = req.params;
|
|
try {
|
|
const sqlBarang = `
|
|
SELECT b.barang_id, b.kode_barang, b.nama_barang, b.stok, b.status_barang, b.qr_image, b.foto_barang, b.dibuat_pada,
|
|
k.nama_kategori, l.nama_lokasi
|
|
FROM Barang b
|
|
LEFT JOIN Kategori k ON b.kategori_id = k.kategori_id
|
|
LEFT JOIN Lokasi l ON b.lokasi_id = l.lokasi_id
|
|
WHERE b.kode_barang = ?
|
|
LIMIT 1`;
|
|
const [rowsBarang] = await db.query(sqlBarang, [kode]);
|
|
|
|
if (rowsBarang.length === 0) {
|
|
return res.status(404).json({ success: false, message: "Barang tidak ditemukan!" });
|
|
}
|
|
|
|
const barang = rowsBarang[0];
|
|
|
|
const sqlBast = `
|
|
SELECT b.bast_id, b.status_serah, b.status_terima, b.dibuat_pada,
|
|
u1.nama_lengkap AS nama_penyerah, u2.nama_lengkap AS nama_penerima
|
|
FROM BAST b
|
|
LEFT JOIN User u1 ON b.user_serah_id = u1.user_id
|
|
LEFT JOIN User u2 ON b.user_terima_id = u2.user_id
|
|
WHERE b.barang_id = ?
|
|
ORDER BY b.dibuat_pada DESC`;
|
|
const [riwayatBast] = await db.query(sqlBast, [barang.barang_id]);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
...barang,
|
|
riwayat_bast: riwayatBast
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Error Detail Barang:", error);
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// API POST: Tambah Barang Baru + Auto Create Kategori & Lokasi
|
|
app.post("/api/barang", upload.single('foto_barang'), async (req, res) => {
|
|
const { kode_barang, nama_barang, nama_kategori, nama_lokasi, status_barang, stok } = req.body;
|
|
|
|
try {
|
|
if (!kode_barang || !nama_barang || !nama_kategori || !nama_lokasi) {
|
|
return res.status(400).json({ success: false, message: "Data nama, kategori, and lokasi wajib diisi lengkap, Bang!" });
|
|
}
|
|
|
|
let urlFotoBaru = "";
|
|
if (req.file) {
|
|
urlFotoBaru = `http://localhost:5000/uploads/${req.file.filename}`;
|
|
} else if (req.body.foto_barang && req.body.foto_barang.startsWith('http')) {
|
|
urlFotoBaru = req.body.foto_barang;
|
|
} else {
|
|
return res.status(400).json({ success: false, message: "File gambar foto_barang belum diupload, Bang!" });
|
|
}
|
|
|
|
// --- AUTO-CREATE KATEGORI ---
|
|
let finalKategoriId;
|
|
const [cekKategori] = await db.query("SELECT kategori_id FROM Kategori WHERE LOWER(nama_kategori) = LOWER(?)", [nama_kategori.trim()]);
|
|
|
|
if (cekKategori.length > 0) {
|
|
finalKategoriId = cekKategori[0].kategori_id;
|
|
} else {
|
|
const namaKategoriBersih = nama_kategori.trim().replace(/[^a-zA-Z0-9 ]/g, "").toUpperCase();
|
|
const kataKategori = namaKategoriBersih.split(" ").filter(k => k.length > 0);
|
|
|
|
let kodeKategoriOtomatis = "";
|
|
if (kataKategori.length >= 2) {
|
|
kodeKategoriOtomatis = (kataKategori[0].slice(0, 2) + kataKategori[1].slice(0, 1)).slice(0, 3);
|
|
} else {
|
|
kodeKategoriOtomatis = namaKategoriBersih.slice(0, 3);
|
|
}
|
|
|
|
if (kodeKategoriOtomatis.length < 3) {
|
|
kodeKategoriOtomatis = (kodeKategoriOtomatis + "XXX").slice(0, 3);
|
|
}
|
|
|
|
const [buatKategori] = await db.query(
|
|
"INSERT INTO Kategori (kode_kategori, nama_kategori) VALUES (?, ?)",
|
|
[kodeKategoriOtomatis, nama_kategori.trim()]
|
|
);
|
|
finalKategoriId = buatKategori.insertId;
|
|
}
|
|
|
|
// --- AUTO-CREATE LOKASI ---
|
|
let finalLokasiId;
|
|
const [cekLokasi] = await db.query("SELECT lokasi_id FROM Lokasi WHERE LOWER(nama_lokasi) = LOWER(?)", [nama_lokasi.trim()]);
|
|
|
|
if (cekLokasi.length > 0) {
|
|
finalLokasiId = cekLokasi[0].lokasi_id;
|
|
} else {
|
|
const namaLokasiBersih = nama_lokasi.trim().replace(/[^a-zA-Z0-9 ]/g, "").toUpperCase();
|
|
const kataLokasi = namaLokasiBersih.split(" ").filter(l => l.length > 0);
|
|
|
|
let kodeLokasiOtomatis = "";
|
|
if (kataLokasi.length >= 2) {
|
|
kodeLokasiOtomatis = (kataLokasi[0].slice(0, 2) + kataLokasi[1].slice(0, 1)).slice(0, 3);
|
|
} else {
|
|
kodeLokasiOtomatis = namaLokasiBersih.slice(0, 3);
|
|
}
|
|
|
|
if (kodeLokasiOtomatis.length < 3) {
|
|
kodeLokasiOtomatis = (kodeLokasiOtomatis + "XXX").slice(0, 3);
|
|
}
|
|
|
|
const [buatLokasi] = await db.query(
|
|
"INSERT INTO Lokasi (kode_lokasi, nama_lokasi) VALUES (?, ?)",
|
|
[kodeLokasiOtomatis, nama_lokasi.trim()]
|
|
);
|
|
finalLokasiId = buatLokasi.insertId;
|
|
}
|
|
|
|
// 5. Generate QR Code Otomatis menggunakan IP Lokal agar bisa discan HP
|
|
const qrUrl = `http://1.9.77.179:5173/detail/${encodeURIComponent(kode_barang)}`;
|
|
const qrImage = await QRCode.toDataURL(qrUrl);
|
|
|
|
// --- INSERT KE TABEL BARANG ---
|
|
const sql = `INSERT INTO Barang
|
|
(kode_barang, nama_barang, kategori_id, lokasi_id, status_barang, stok, qr_image, foto_barang)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
|
|
await db.query(sql, [
|
|
kode_barang,
|
|
nama_barang.trim(),
|
|
Number(finalKategoriId),
|
|
Number(finalLokasiId),
|
|
status_barang || 'Baik',
|
|
Number(stok) || 0,
|
|
qrImage,
|
|
urlFotoBaru
|
|
]);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Mantap Bang! Data masuk beserta Kategori dan Lokasi otomatis, Foto Online, dan QR Code ke database CIFO.",
|
|
kode_barang: kode_barang
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Eror Backend Simpan Barang:", error);
|
|
res.status(500).json({ success: false, message: "Gagal memproses data ke database!", error: error.message });
|
|
}
|
|
});
|
|
|
|
// API PUT: Edit Data Barang
|
|
app.put("/api/barang/:id", upload.single('foto_barang'), async (req, res) => {
|
|
const barangId = req.params.id;
|
|
const { nama_barang, stok, status_barang } = req.body;
|
|
|
|
try {
|
|
let sql = "UPDATE Barang SET nama_barang = ?, stok = ?, status_barang = ? WHERE barang_id = ?";
|
|
let params = [nama_barang, Number(stok), status_barang, barangId];
|
|
|
|
if (req.file) {
|
|
const urlFotoBaru = `http://localhost:5000/uploads/${req.file.filename}`;
|
|
sql = "UPDATE Barang SET nama_barang = ?, stok = ?, status_barang = ?, foto_barang = ? WHERE barang_id = ?";
|
|
params = [nama_barang, Number(stok), status_barang, urlFotoBaru, barangId];
|
|
} else if (req.body.foto_barang && req.body.foto_barang.startsWith('http')) {
|
|
sql = "UPDATE Barang SET nama_barang = ?, stok = ?, status_barang = ?, foto_barang = ? WHERE barang_id = ?";
|
|
params = [nama_barang, Number(stok), status_barang, req.body.foto_barang, barangId];
|
|
}
|
|
|
|
await db.query(sql, params);
|
|
const [rows] = await db.query("SELECT * FROM Barang WHERE barang_id = ?", [barangId]);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Perubahan data inventaris berhasil disimpan dengan sukses!",
|
|
data: rows[0]
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error("Error backend edit barang:", error);
|
|
res.status(500).json({ success: false, message: "Gagal memproses data ke database!", error: error.message });
|
|
}
|
|
});
|
|
|
|
// API DELETE: Hapus Barang
|
|
app.delete("/api/barang/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
await db.query("DELETE FROM Barang WHERE barang_id = ?", [id]);
|
|
res.json({ success: true, message: "Barang berhasil dihapus dari database!" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// GET SEMUA KATEGORI
|
|
app.get("/api/kategori", async (req, res) => {
|
|
try {
|
|
const [rows] = await db.query("SELECT * FROM Kategori");
|
|
res.json(rows);
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// CREATE Kategori
|
|
app.post("/api/kategori", async (req, res) => {
|
|
const { nama_kategori } = req.body;
|
|
if (!nama_kategori) return res.status(400).json({ success: false, message: "Nama kategori wajib" });
|
|
try {
|
|
const [result] = await db.query("INSERT INTO Kategori (nama_kategori) VALUES (?)", [nama_kategori]);
|
|
res.json({ success: true, message: "Kategori berhasil ditambah", id: result.insertId });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// UPDATE Kategori
|
|
app.put("/api/kategori/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
const { nama_kategori } = req.body;
|
|
if (!nama_kategori) return res.status(400).json({ success: false, message: "Nama kategori wajib" });
|
|
try {
|
|
await db.query("UPDATE Kategori SET nama_kategori = ? WHERE kategori_id = ?", [nama_kategori, id]);
|
|
res.json({ success: true, message: "Kategori berhasil diupdate" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// DELETE Kategori
|
|
app.delete("/api/kategori/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
await db.query("DELETE FROM Kategori WHERE kategori_id = ?", [id]);
|
|
res.json({ success: true, message: "Kategori berhasil dihapus" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// GET SEMUA LOKASI
|
|
app.get("/api/lokasi", async (req, res) => {
|
|
try {
|
|
const [rows] = await db.query("SELECT * FROM Lokasi");
|
|
res.json({ success: true, data: rows });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// CREATE Lokasi
|
|
app.post("/api/lokasi", async (req, res) => {
|
|
const { nama_lokasi } = req.body;
|
|
if (!nama_lokasi) return res.status(400).json({ success: false, message: "Nama lokasi wajib" });
|
|
try {
|
|
const [result] = await db.query("INSERT INTO Lokasi (nama_lokasi) VALUES (?)", [nama_lokasi]);
|
|
res.json({ success: true, message: "Lokasi berhasil ditambah", id: result.insertId });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// UPDATE Lokasi
|
|
app.put("/api/lokasi/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
const { nama_lokasi } = req.body;
|
|
if (!nama_lokasi) return res.status(400).json({ success: false, message: "Nama lokasi wajib" });
|
|
try {
|
|
await db.query("UPDATE Lokasi SET nama_lokasi = ? WHERE lokasi_id = ?", [nama_lokasi, id]);
|
|
res.json({ success: true, message: "Lokasi berhasil diupdate" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// DELETE Lokasi
|
|
app.delete("/api/lokasi/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
await db.query("DELETE FROM Lokasi WHERE lokasi_id = ?", [id]);
|
|
res.json({ success: true, message: "Lokasi berhasil dihapus" });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// =======================================================
|
|
// 4. MODUL TRANSAKSI BAST RESMI (SOAL UKK PPLG)
|
|
// =======================================================
|
|
|
|
// --- SINKRONISASI 1: MENYEDIAKAN USER_SERAH_ID DAN USER_TERIMA_ID KE FRONTEND ---
|
|
app.get("/api/bast", async (req, res) => {
|
|
try {
|
|
const sql = `
|
|
SELECT b.bast_id, b.barang_id, b.user_serah_id, b.user_terima_id,
|
|
b.status_serah, b.status_terima, b.dibuat_pada,
|
|
brg.nama_barang, brg.kode_barang, brg.foto_barang,
|
|
k.nama_kategori,
|
|
l.nama_lokasi,
|
|
u1.nama_lengkap AS nama_penyerah,
|
|
u2.nama_lengkap AS nama_penerima
|
|
FROM BAST b
|
|
LEFT JOIN Barang brg ON b.barang_id = brg.barang_id
|
|
LEFT JOIN Kategori k ON brg.kategori_id = k.kategori_id
|
|
LEFT JOIN Lokasi l ON brg.lokasi_id = l.lokasi_id
|
|
LEFT JOIN User u1 ON b.user_serah_id = u1.user_id
|
|
LEFT JOIN User u2 ON b.user_terima_id = u2.user_id
|
|
ORDER BY b.bast_id DESC`;
|
|
const [rows] = await db.query(sql);
|
|
res.json({ success: true, data: rows });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// API POST: Membuat Transaksi BAST baru oleh Admin
|
|
app.post("/api/bast", async (req, res) => {
|
|
const { barang_id, user_serah_id, user_terima_id } = req.body;
|
|
|
|
if (!barang_id || !user_serah_id || !user_terima_id) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: "Barang, User Serah, dan User Terima wajib diisi sesuai standar UKK!"
|
|
});
|
|
}
|
|
|
|
try {
|
|
const sql = `INSERT INTO BAST (barang_id, user_serah_id, user_terima_id, status_serah, status_terima, dibuat_pada)
|
|
VALUES (?, ?, ?, 'Menunggu', 'Menunggu', NOW())`;
|
|
|
|
await db.query(sql, [barang_id, user_serah_id, user_terima_id]);
|
|
res.json({ success: true, message: "Dokumen BAST berhasil dibuat oleh Admin! Menunggu Approval." });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// --- SINKRONISASI 2: DYNAMIC ROUTE APPROVAL (DAPAT UPDATE REJECTED MAUPUN APPROVED SISI PENYERAH/PENERIMA) ---
|
|
app.put("/api/bast/:id/status", async (req, res) => {
|
|
const { id } = req.params;
|
|
const { peran, status } = req.body; // peran = 'serah' / 'terima', status = 'Approved' / 'Rejected'
|
|
|
|
if (!peran || !status) {
|
|
return res.status(400).json({ success: false, message: "Parameter keputusan data tidak lengkap." });
|
|
}
|
|
|
|
try {
|
|
let sql = "";
|
|
if (peran === "serah") {
|
|
sql = "UPDATE BAST SET status_serah = ? WHERE bast_id = ?";
|
|
} else if (peran === "terima") {
|
|
sql = "UPDATE BAST SET status_terima = ? WHERE bast_id = ?";
|
|
} else {
|
|
return res.status(400).json({ success: false, message: "Peran user tidak terdefinisi secara sah!" });
|
|
}
|
|
|
|
await db.query(sql, [status, id]);
|
|
res.json({ success: true, message: `Berhasil melakukan update persetujuan sebagai pihak ${peran}!` });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
app.get("/api/bast/cetak/:id", async (req, res) => {
|
|
const { id } = req.params;
|
|
try {
|
|
const sql = `
|
|
SELECT b.bast_id, b.status_serah, b.status_terima, b.dibuat_pada,
|
|
brg.nama_barang, brg.kode_barang,
|
|
u1.nama_lengkap AS nama_penyerah,
|
|
u2.nama_lengkap AS nama_penerima
|
|
FROM BAST b
|
|
LEFT JOIN Barang brg ON b.barang_id = brg.barang_id
|
|
LEFT JOIN User u1 ON b.user_serah_id = u1.user_id
|
|
LEFT JOIN User u2 ON b.user_terima_id = u2.user_id
|
|
WHERE b.bast_id = ?`;
|
|
|
|
const [rows] = await db.query(sql, [id]);
|
|
|
|
if (rows.length === 0) {
|
|
return res.status(404).json({ success: false, message: "Data BAST tidak ditemukan, Bang!" });
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Data cetak dokumen berhasil disiapkan!",
|
|
data: rows[0]
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
app.listen(5000, () => {
|
|
console.log("Server backend berjalan di port 5000...");
|
|
}); |