updated absensi
This commit is contained in:
@@ -6,6 +6,10 @@ const history = async (req, res) => {
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAll = async (req, res) => {
|
||||||
|
const response = await services.getAll(req, res)
|
||||||
|
}
|
||||||
|
|
||||||
const create = async (req, res) => {
|
const create = async (req, res) => {
|
||||||
const response = await services.create(req, res);
|
const response = await services.create(req, res);
|
||||||
return response;
|
return response;
|
||||||
@@ -26,10 +30,17 @@ const destroy = async (req, res) => {
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkLocation = async (req, res) => {
|
||||||
|
const response = await services.checkLocation(req, res)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create,
|
create,
|
||||||
history,
|
history,
|
||||||
update,
|
update,
|
||||||
destroy,
|
destroy,
|
||||||
clockOut
|
clockOut,
|
||||||
|
getAll,
|
||||||
|
checkLocation
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,18 @@ router.get('/history', apiKey, jwt, (req, res) => {
|
|||||||
controller.history(req, res);
|
controller.history(req, res);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.get('/', apiKey, (req, res) => {
|
||||||
|
controller.getAll(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
router.post('/', jwt, apiKey, upload.single('attendances'), (req, res) => {
|
router.post('/', jwt, apiKey, upload.single('attendances'), (req, res) => {
|
||||||
controller.create(req, res)
|
controller.create(req, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.get('/check-location', apiKey, jwt, (req, res) => {
|
||||||
|
controller.checkLocation(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
router.post('/clock-out', jwt, apiKey, (req, res) => {
|
router.post('/clock-out', jwt, apiKey, (req, res) => {
|
||||||
controller.clockOut(req, res)
|
controller.clockOut(req, res)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ const moment = require('moment-timezone')
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
|
|
||||||
|
|
||||||
const User = db.User
|
const User = db.User
|
||||||
const Attedances = db.Attedances
|
const Attedances = db.Attedances
|
||||||
const Branch = db.Branch
|
const Branch = db.Branch
|
||||||
@@ -90,6 +88,7 @@ const create = async (req, res) => {
|
|||||||
return response.failed(res, 400, `Lokasi di luar area kantor (${distance.toFixed(2)} meter)`);
|
return response.failed(res, 400, `Lokasi di luar area kantor (${distance.toFixed(2)} meter)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let finalPhotoUrl = null;
|
let finalPhotoUrl = null;
|
||||||
|
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
@@ -112,8 +111,6 @@ const create = async (req, res) => {
|
|||||||
finalPhotoUrl = filePath.replace("public", "").replace(/\\/g, "/");
|
finalPhotoUrl = filePath.replace("public", "").replace(/\\/g, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// === Absen masuk ===
|
// === Absen masuk ===
|
||||||
attendance = await Attedances.create({
|
attendance = await Attedances.create({
|
||||||
user_id,
|
user_id,
|
||||||
@@ -130,6 +127,7 @@ const create = async (req, res) => {
|
|||||||
await t.commit();
|
await t.commit();
|
||||||
return response.success(res, {
|
return response.success(res, {
|
||||||
...attendance.toJSON(),
|
...attendance.toJSON(),
|
||||||
|
branch_name: branch.name,
|
||||||
clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
clock_out: attendance.clock_out
|
clock_out: attendance.clock_out
|
||||||
? moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
|
? moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss')
|
||||||
@@ -165,6 +163,7 @@ const clockOut = async (req, res) => {
|
|||||||
await t.rollback();
|
await t.rollback();
|
||||||
return response.failed(res, 400, 'Sudah absen pulang hari ini');
|
return response.failed(res, 400, 'Sudah absen pulang hari ini');
|
||||||
}
|
}
|
||||||
|
const branch = await Branch.findOne({ where: { id: user.branch_id } });
|
||||||
|
|
||||||
// Set jam pulang (tanpa cek lokasi)
|
// Set jam pulang (tanpa cek lokasi)
|
||||||
attendance.clock_out = now.toDate();
|
attendance.clock_out = now.toDate();
|
||||||
@@ -180,6 +179,7 @@ const clockOut = async (req, res) => {
|
|||||||
|
|
||||||
return response.success(res, {
|
return response.success(res, {
|
||||||
...attendance.toJSON(),
|
...attendance.toJSON(),
|
||||||
|
branch_name: branch.name,
|
||||||
clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
clock_in: moment(attendance.clock_in).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
clock_out: moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
clock_out: moment(attendance.clock_out).tz('Asia/Jakarta').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
work_duration: attendance.work_duration,
|
work_duration: attendance.work_duration,
|
||||||
@@ -246,7 +246,31 @@ const history = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAll = async (req, res) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const today = moment().tz('Asia/Jakarta').format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
const attendances = await Attedances.findAll({
|
||||||
|
where: {
|
||||||
|
date: today
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: User,
|
||||||
|
as: 'user',
|
||||||
|
attributes: ['id', 'name', 'email']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
order: [['created_at', 'DESC']]
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.success(res, attendances, 'List kehadiran hari ini');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return response.error(res, 'Gagal mengambil data absensi hari ini');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const update = async (req, res) => {
|
const update = async (req, res) => {
|
||||||
const t = await sequelize.transaction();
|
const t = await sequelize.transaction();
|
||||||
@@ -255,18 +279,18 @@ const update = async (req, res) => {
|
|||||||
const user_id = req.user.id;
|
const user_id = req.user.id;
|
||||||
const body = req.body;
|
const body = req.body;
|
||||||
|
|
||||||
const categories = await Category.findOne({
|
const attedances = await Attedances.findOne({
|
||||||
where: { id },
|
where: { id },
|
||||||
transaction: t
|
transaction: t
|
||||||
});
|
});
|
||||||
|
|
||||||
const categoriesUpdate = await categories.update({
|
const attedancesUpdate = await attedances.update({
|
||||||
...body,
|
...body,
|
||||||
user_id,
|
user_id,
|
||||||
}, { transaction: t });
|
}, { transaction: t });
|
||||||
|
|
||||||
await t.commit();
|
await t.commit();
|
||||||
return response.success(res, categoriesUpdate, 'Category Berhasil Di update');
|
return response.success(res, attedancesUpdate, 'Category Berhasil Di update');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await t.rollback();
|
await t.rollback();
|
||||||
errorHandler(error, req, res);
|
errorHandler(error, req, res);
|
||||||
@@ -278,20 +302,20 @@ const destroy = async (req, res) => {
|
|||||||
const t = await sequelize.transaction();
|
const t = await sequelize.transaction();
|
||||||
try {
|
try {
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
const category = await Category.findOne({
|
const attendance = await Attedances.findOne({
|
||||||
where: { id },
|
where: { id },
|
||||||
transaction: t,
|
transaction: t,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!category) {
|
if (!attendance) {
|
||||||
await t.rollback();
|
await t.rollback();
|
||||||
return response.failed(res, 404, 'Category tidak ditemukan');
|
return response.failed(res, 404, 'Attendance tidak ditemukan');
|
||||||
}
|
}
|
||||||
|
|
||||||
await category.destroy({ transaction: t });
|
await attendance.destroy({ transaction: t });
|
||||||
await t.commit();
|
await t.commit();
|
||||||
|
|
||||||
return response.success(res, null, 'Category berhasil dihapus');
|
return response.success(res, null, 'Attendance berhasil dihapus');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await t.rollback();
|
await t.rollback();
|
||||||
errorHandler(error, req, res);
|
errorHandler(error, req, res);
|
||||||
@@ -299,11 +323,61 @@ const destroy = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkLocation = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const user_id = req.user.id;
|
||||||
|
const { lat, lng } = req.query;
|
||||||
|
|
||||||
|
if (!lat || !lng) {
|
||||||
|
return response.failed(res, 400, 'Latitude dan longitude wajib dikirim');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findOne({ where: { id: user_id } });
|
||||||
|
if (!user) return response.failed(res, 404, 'User tidak ditemukan');
|
||||||
|
|
||||||
|
const branch = await Branch.findOne({ where: { id: user.branch_id } });
|
||||||
|
if (!branch) return response.failed(res, 404, 'Data kantor tidak ditemukan');
|
||||||
|
|
||||||
|
// Hitung jarak user dengan kantor (dalam meter)
|
||||||
|
const distanceMeters = getDistance(branch.lat, branch.lng, lat, lng);
|
||||||
|
|
||||||
|
// Otomatis ubah ke km jika lebih dari 1000 meter
|
||||||
|
const isKm = distanceMeters >= 1000;
|
||||||
|
const distance = isKm ? distanceMeters / 1000 : distanceMeters;
|
||||||
|
const unit = isKm ? 'km' : 'm';
|
||||||
|
|
||||||
|
// Radius tetap pakai meter (biar konsisten)
|
||||||
|
const allowedRadius = parseFloat(process.env.ABSENCE_RADIUS) || 100;
|
||||||
|
|
||||||
|
if (distanceMeters <= allowedRadius) {
|
||||||
|
return response.success(res, {
|
||||||
|
inOffice: true,
|
||||||
|
branch_name: branch.name,
|
||||||
|
distance: distance.toFixed(2),
|
||||||
|
unit,
|
||||||
|
message: `Anda sedang berada di lokasi ${branch.name}, silakan absen.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.success(res, {
|
||||||
|
inOffice: false,
|
||||||
|
branch_name: branch.name,
|
||||||
|
distance: distance.toFixed(2),
|
||||||
|
unit,
|
||||||
|
message: `Anda berada di luar area kantor ${distance.toFixed(2)} ${unit} dari ${branch.name}.`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
errorHandler(error, req, res);
|
||||||
|
return response.failed(res, 500, error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create,
|
create,
|
||||||
destroy,
|
destroy,
|
||||||
history,
|
history,
|
||||||
update,
|
update,
|
||||||
clockOut
|
clockOut,
|
||||||
|
getAll,
|
||||||
|
checkLocation
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ router.put('/:id', apiKey, jwt, (req, res) => {
|
|||||||
controller.update(req, res)
|
controller.update(req, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.put('/:id', apiKey, jwt, (req, res) => {
|
router.delete('/:id', apiKey, jwt, (req, res) => {
|
||||||
controller.destroy(req, res)
|
controller.destroy(req, res)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user