Files
be_absensi_sas/backend/internal/transabsensi/transabsensi.go

675 lines
17 KiB
Go

package transabsensi
import (
"encoding/base64"
"errors"
"fmt"
"log"
"os"
"strconv"
"strings"
"time"
"com.sismedika.com.absensi/graph/model"
"com.sismedika.com.absensi/pkg/config"
db "com.sismedika.com.absensi/pkg/database"
)
// diambil dari graph/model/struct nya TransAbsensiResponse
type TransAbsensiCheckDistanceResponse model.TransAbsensiCheckDistanceResponse
type TransAbsensiResponse model.TransAbsensiResponse
type TransAbsensiCheckTimeAttendanceResponse model.TransAbsensiCheckTimeAttendanceResponse
// fungsi untuk Check Distance antara kantor dengan current user location
func (transabsensi *TransAbsensiCheckDistanceResponse) CheckDistance(M_StaffID string, M_CompanyID string, CurrentLatitude string, CurrentLongitude string) (*model.TransAbsensiCheckDistanceResponse, error) {
// inisialisasi
var err error
var ret model.TransAbsensiCheckDistanceResponse
var rMaxDistance int
var rCurrentDistance float64
var rMCompanyLongitude string
var rMCompanyLatitude string
var statusCheck string
q := `SELECT
M_CompanyLatitude,
M_CompanyLongitude,
M_CompanyMaxDistance
FROM m_company
WHERE M_CompanyIsActive = 'Y'
AND M_CompanyID = ?`
row := db.Handle.QueryRow(
q,
M_CompanyID)
db.LogSQL(q)
err = row.Scan(
&rMCompanyLatitude,
&rMCompanyLongitude,
&rMaxDistance,
)
if err != nil {
log.Printf("Error m_company select max distance: %v", err)
log.Printf("Executing query: %s\n", q)
return nil, err
}
// get distance
qCurrentDistance := `SELECT distance_v2(?, ?, ?, ?)`
rowxCurrentDistance := db.Handle.QueryRow(
qCurrentDistance,
CurrentLatitude,
CurrentLongitude,
rMCompanyLatitude,
rMCompanyLongitude,
)
db.LogSQL(qCurrentDistance)
err = rowxCurrentDistance.Scan(
&rCurrentDistance,
)
if err != nil {
log.Printf("Error m_staff select: %v", err)
log.Printf("Executing query: %s\n", qCurrentDistance)
return nil, err
}
// check distance
qCheckDistance := `SELECT IF(distance_v2(?, ?, ?, ?) < ?, 'range', 'not range') AS distance_status`
rowx := db.Handle.QueryRow(
qCheckDistance,
CurrentLatitude,
CurrentLongitude,
rMCompanyLatitude,
rMCompanyLongitude,
rMaxDistance,
)
db.LogSQL(qCheckDistance)
err = rowx.Scan(
&statusCheck,
)
if err != nil {
log.Printf("Error m_staff select: %v", err)
log.Printf("Executing query: %s\n", qCheckDistance)
return nil, err
}
if statusCheck == "range" {
// buat di set isi nya kosong dulu
ret.Status = new(string)
ret.Message = new(string)
ret.Selfie = new(string)
ret.CurrentDistance = new(string)
ret.MaxDistance = new(string)
ret.Unit = new(string)
// di set kan langsung dengan * atau pointer
*ret.Status = "OK"
*ret.Selfie = "FALSE"
*ret.Unit = "m"
*ret.CurrentDistance = strconv.FormatFloat(rCurrentDistance, 'f', -1, 64)
*ret.MaxDistance = strconv.Itoa(rMaxDistance)
*ret.Message = "Masih dalam jarak absensi"
}
if statusCheck != "range" {
// buat di set isi nya kosong dulu
ret.Status = new(string)
ret.Message = new(string)
ret.Selfie = new(string)
ret.CurrentDistance = new(string)
ret.MaxDistance = new(string)
ret.Unit = new(string)
// di set kan langsung dengan * atau pointer
*ret.Status = "OK"
*ret.Selfie = "TRUE"
*ret.Unit = "m"
*ret.CurrentDistance = strconv.FormatFloat(rCurrentDistance, 'f', -1, 64)
*ret.MaxDistance = strconv.Itoa(rMaxDistance)
*ret.Message = "Max Distance : " + strconv.Itoa(rMaxDistance) + " meter. Current Distance: " + strconv.FormatFloat(rCurrentDistance, 'f', -1, 64) + ". Jauh dari area absensi, perlu foto selfie"
}
// hasil return nya
return &ret, err
}
// fungsi untuk absen masuk clock in
func (transabsensi *TransAbsensiResponse) ClockInAbsensi(T_TransactionM_StaffID string, T_TransactionM_CompanyID string, T_TransactionCurrentLatitude string, T_TransactionCurrentLongitude string, T_TransactionCurrentDistance string, T_TransactionSelfiePhoto string, token string, isSelfie string) (*model.TransAbsensiResponse, error) {
// inisialisasi
var err error
var ret model.TransAbsensiResponse
var varGetToken string
var varGetM_StaffNIP string
var varProsesFotoSelfie string
var jamClockIn string
var varFileName string
// check user token
qCheckTokenStaff := `SELECT
M_StaffToken,
LOWER(M_StaffNIP) AS M_StaffNIP
FROM m_staff
WHERE M_StaffIsActive = 'Y'
AND M_StaffToken = ?
`
rowCheckTokenStaff := db.Handle.QueryRow(
qCheckTokenStaff,
token,
)
db.LogSQL(qCheckTokenStaff)
err = rowCheckTokenStaff.Scan(
&varGetToken,
&varGetM_StaffNIP,
)
if err != nil {
log.Printf("Error m_staff select token: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
// check varGetToken jk M_StaffToken kosong
if varGetToken == "" {
log.Printf("Error Token Kosong, Silahkan Login Dulu: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
ret.Status = new(string)
ret.Message = new(string)
// lanjut proses absen
if varGetToken != "" {
// tanpa selfie T_TransactionSelfiePhoto di set kosong
if isSelfie != "TRUE" {
T_TransactionSelfiePhoto = ""
varProsesFotoSelfie = ""
// setting jam absen
currentTime := time.Now()
jam := currentTime.Hour()
menit := currentTime.Minute()
detik := currentTime.Second()
jamClockIn = fmt.Sprintf("%02d:%02d:%02d", jam, menit, detik)
}
// selfie true
if isSelfie == "TRUE" {
// pecah string
strToArrayFoto := strings.Split(T_TransactionSelfiePhoto, ",")
// check apakah sudah sesuai apa belum
if len(strToArrayFoto) == 0 {
log.Printf("Eror Tidak Sesuai Format %v", err)
return &ret, err
}
// jika sesuai ambil strToArrayFoto ke 1 atau index ke 1
if len(strToArrayFoto) > 0 {
base64DataFoto := strToArrayFoto[1]
T_TransactionSelfiePhoto = base64DataFoto
}
currentTime := time.Now()
// setting jam absen
jam := currentTime.Hour()
menit := currentTime.Minute()
detik := currentTime.Second()
jamClockIn = fmt.Sprintf("%02d:%02d:%02d", jam, menit, detik)
formattedTime := currentTime.Format("2006-01-02_15_04_05")
// buat nama file
varProsesFotoSelfie = "clockin_" + varGetM_StaffNIP + "_" + formattedTime
strPhoto := varProsesFotoSelfie + ".jpg"
// folder upload file nya selfie_attachment akan otomatis sesuai bulan dan tahun
folderName := currentTime.Format("200601")
folder := "selfie_attachment/" + folderName + "/"
photoPath := config.Data.Get("document") + folder
log.Printf("documentPath: %s", photoPath)
// create folder selfie_attachment jika belum ada
if _, err := os.Stat(photoPath); errors.Is(err, os.ErrNotExist) {
err = os.MkdirAll(photoPath, os.ModePerm.Perm())
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
}
photoPath = photoPath + strPhoto
// proses decode
decodedByte, err := base64.StdEncoding.DecodeString(T_TransactionSelfiePhoto)
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
fs, err := os.Create(photoPath)
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
defer fs.Close()
fs.Write(decodedByte)
fs.Sync()
varProsesFotoSelfie = "selfie_attachment/" + folderName + "/" + strPhoto
varFileName = strPhoto
}
qInsertClockIn := `INSERT INTO t_transaction (
T_TransactionM_AbsensiTypeID,
T_TransactionM_StaffID,
T_TransactionM_CompanyID,
T_TransactionClockAbsensi,
T_TransactionDate,
T_TransactionCurrentLatitude,
T_TransactionCurrentLongitude,
T_TransactionCurrentDistance,
T_TransactionFileName,
T_TransactionSelfiePhotoPath,
T_TransactionIsActive,
T_TransactionCreated
) VALUES (
?,
?,
?,
?,
NOW(),
?,
?,
?,
?,
?,
?,
NOW()
)`
_, err = db.Handle.Exec(qInsertClockIn,
"1",
T_TransactionM_StaffID,
T_TransactionM_CompanyID,
&jamClockIn,
T_TransactionCurrentLatitude,
T_TransactionCurrentLongitude,
T_TransactionCurrentDistance,
&varFileName,
&varProsesFotoSelfie,
"Y",
)
// set status jika tidak error
if err == nil {
*ret.Status = "OK"
*ret.Message = "Proses Absensi Masuk Berhasil"
}
// set status jika error
if err != nil {
*ret.Status = "ERR"
*ret.Message = "ERROR : " + err.Error()
}
}
return &ret, err
}
// fungsi untuk absen keluar clock out
func (transabsensi *TransAbsensiResponse) ClockOutAbsensi(T_TransactionM_StaffID string, T_TransactionM_CompanyID string, T_TransactionCurrentLatitude string, T_TransactionCurrentLongitude string, T_TransactionCurrentDistance string, T_TransactionSelfiePhoto string, token string, isSelfie string) (*model.TransAbsensiResponse, error) {
// inisialisasi
var err error
var ret model.TransAbsensiResponse
var varGetToken string
var varGetM_StaffNIP string
var varProsesFotoSelfie string
var jamClockOut string
var varFileName string
// check user token
qCheckTokenStaff := `SELECT
M_StaffToken,
LOWER(M_StaffNIP) AS M_StaffNIP
FROM m_staff
WHERE M_StaffIsActive = 'Y'
AND M_StaffToken = ?
`
rowCheckTokenStaff := db.Handle.QueryRow(
qCheckTokenStaff,
token,
)
db.LogSQL(qCheckTokenStaff)
err = rowCheckTokenStaff.Scan(
&varGetToken,
&varGetM_StaffNIP,
)
if err != nil {
log.Printf("Error m_staff select token: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
// check varGetToken jk M_StaffToken kosong
if varGetToken == "" {
log.Printf("Error Token Kosong, Silahkan Login Dulu: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
ret.Status = new(string)
ret.Message = new(string)
// lanjut proses absen
if varGetToken != "" {
// tanpa selfie T_TransactionSelfiePhoto di set kosong
if isSelfie != "TRUE" {
T_TransactionSelfiePhoto = ""
varProsesFotoSelfie = ""
// setting jam absen
currentTime := time.Now()
jam := currentTime.Hour()
menit := currentTime.Minute()
detik := currentTime.Second()
jamClockOut = fmt.Sprintf("%02d:%02d:%02d", jam, menit, detik)
}
// selfie true
if isSelfie == "TRUE" {
// pecah string
strToArrayFoto := strings.Split(T_TransactionSelfiePhoto, ",")
// check apakah sudah sesuai apa belum
if len(strToArrayFoto) == 0 {
log.Printf("Eror Tidak Sesuai Format %v", err)
return &ret, err
}
// jika sesuai ambil strToArrayFoto ke 1 atau index ke 1
if len(strToArrayFoto) > 0 {
base64DataFoto := strToArrayFoto[1]
T_TransactionSelfiePhoto = base64DataFoto
}
currentTime := time.Now()
// setting jam absen
jam := currentTime.Hour()
menit := currentTime.Minute()
detik := currentTime.Second()
jamClockOut = fmt.Sprintf("%02d:%02d:%02d", jam, menit, detik)
formattedTime := currentTime.Format("2006-01-02_15_04_05")
// buat nama file
varProsesFotoSelfie = "clockout_" + varGetM_StaffNIP + "_" + formattedTime
strPhoto := varProsesFotoSelfie + ".jpg"
// folder upload file nya selfie_attachment akan otomatis sesuai bulan dan tahun
folderName := currentTime.Format("200601")
folder := "selfie_attachment/" + folderName + "/"
photoPath := config.Data.Get("document") + folder
log.Printf("documentPath: %s", photoPath)
// create folder selfie_attachment jika belum ada
if _, err := os.Stat(photoPath); errors.Is(err, os.ErrNotExist) {
err = os.MkdirAll(photoPath, os.ModePerm.Perm())
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
}
photoPath = photoPath + strPhoto
// proses decode
decodedByte, err := base64.StdEncoding.DecodeString(T_TransactionSelfiePhoto)
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
fs, err := os.Create(photoPath)
if err != nil {
log.Printf("here %v", err)
return &ret, err
}
defer fs.Close()
fs.Write(decodedByte)
fs.Sync()
varProsesFotoSelfie = "selfie_attachment/" + folderName + "/" + strPhoto
varFileName = strPhoto
}
qInsertClockIn := `INSERT INTO t_transaction (
T_TransactionM_AbsensiTypeID,
T_TransactionM_StaffID,
T_TransactionM_CompanyID,
T_TransactionClockAbsensi,
T_TransactionDate,
T_TransactionCurrentLatitude,
T_TransactionCurrentLongitude,
T_TransactionCurrentDistance,
T_TransactionFileName,
T_TransactionSelfiePhotoPath,
T_TransactionIsActive,
T_TransactionCreated
) VALUES (
?,
?,
?,
?,
NOW(),
?,
?,
?,
?,
?,
?,
NOW()
)`
_, err = db.Handle.Exec(qInsertClockIn,
"2",
T_TransactionM_StaffID,
T_TransactionM_CompanyID,
&jamClockOut,
T_TransactionCurrentLatitude,
T_TransactionCurrentLongitude,
T_TransactionCurrentDistance,
&varFileName,
&varProsesFotoSelfie,
"Y",
)
// set status jika tidak error
if err == nil {
*ret.Status = "OK"
*ret.Message = "Proses Absensi Pulang Berhasil"
}
// set status jika error
if err != nil {
*ret.Status = "ERR"
*ret.Message = "ERROR : " + err.Error()
}
}
return &ret, err
}
// fungsi untuk get jam absen dan pulang
func (transabsensi *TransAbsensiCheckTimeAttendanceResponse) CheckTimeAttendance(M_StaffID string, M_CompanyID string, token string) (*model.TransAbsensiCheckTimeAttendanceResponse, error) {
// inisialisasi
var err error
var ret model.TransAbsensiCheckTimeAttendanceResponse
var var_jam_clock_in string
var var_jam_clock_out string
var var_is_absen_clock_in string = "FALSE"
var var_is_absen_clock_out string = "FALSE"
var varGetToken string
var resultsArrayAttendance []struct {
T_TransactionM_AbsensiTypeID string
T_TransactionClockAbsensi string
}
ret.Status = new(string)
ret.Message = new(string)
ret.JamClockIn = new(string)
ret.JamClockOut = new(string)
ret.IsAbsenClockIn = new(string)
ret.IsAbsenClockOut = new(string)
qCheckTokenStaff := `SELECT
M_StaffToken
FROM m_staff
WHERE M_StaffIsActive = 'Y'
AND M_StaffToken = ?
`
rowCheckTokenStaff := db.Handle.QueryRow(
qCheckTokenStaff,
token,
)
db.LogSQL(qCheckTokenStaff)
err = rowCheckTokenStaff.Scan(
&varGetToken,
)
if err != nil {
log.Printf("Error m_staff select token: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
// check varGetToken jk M_StaffToken kosong
if varGetToken == "" {
log.Printf("Error Token Kosong, Silahkan Login Dulu: %v", err)
log.Printf("Executing query: %s\n", qCheckTokenStaff)
return nil, err
}
// proses check attendance
if varGetToken != "" {
q := `SELECT T_TransactionM_AbsensiTypeID,
DATE_FORMAT(T_TransactionClockAbsensi,'%H:%i') as T_TransactionClockAbsensi
FROM t_transaction
WHERE T_TransactionIsActive = 'Y'
AND T_TransactionM_AbsensiTypeID IN (1, 2)
AND T_TransactionM_StaffID = ?
AND T_TransactionM_CompanyID = ?
AND T_TransactionDate BETWEEN DATE_FORMAT(NOW(), "%Y-%m-%d 01:00:00")
AND DATE_FORMAT(NOW(), "%Y-%m-%d 23:00:00")
ORDER BY T_TransactionM_AbsensiTypeID ASC`
db.LogSQL(q, M_StaffID,
M_CompanyID)
rows, err := db.Handle.Query(
q,
M_StaffID,
M_CompanyID)
if err != nil {
log.Printf("Error m_company select jam_clock_in dan jam_clock_out: %v", err)
log.Printf("Executing query: %s\n", q)
return nil, err
}
defer rows.Close()
for rows.Next() {
var resultTmp struct {
T_TransactionM_AbsensiTypeID string
T_TransactionClockAbsensi string
}
if err := rows.Scan(
&resultTmp.T_TransactionM_AbsensiTypeID,
&resultTmp.T_TransactionClockAbsensi,
); err != nil {
log.Printf("Error scanning row: %v", err)
return nil, err
}
// array push
resultsArrayAttendance = append(resultsArrayAttendance, resultTmp)
}
if err := rows.Err(); err != nil {
log.Printf("Error after iterating rows attendance : %v", err)
return nil, err
}
// check length resultsArrayAttendance
if len(resultsArrayAttendance) == 0 {
log.Printf("Error Data Tidak Ada: %v", err)
*ret.Status = "OK"
*ret.Message = "Belum Ada Absen"
*ret.JamClockIn = ""
*ret.JamClockOut = ""
*ret.IsAbsenClockIn = "FALSE"
*ret.IsAbsenClockOut = "FALSE"
}
if len(resultsArrayAttendance) > 0 {
for _, result := range resultsArrayAttendance {
// clock in (absen masuk)
if result.T_TransactionM_AbsensiTypeID == "1" {
var_jam_clock_in = result.T_TransactionClockAbsensi
if var_is_absen_clock_in != "" {
var_is_absen_clock_in = "TRUE"
}
}
// clock out (absen pulang)
if result.T_TransactionM_AbsensiTypeID == "2" {
var_jam_clock_out = result.T_TransactionClockAbsensi
if var_is_absen_clock_out != "" {
var_is_absen_clock_out = "TRUE"
}
}
}
*ret.Status = "OK"
*ret.Message = "Sudah Ada Absen"
*ret.JamClockIn = var_jam_clock_in
*ret.JamClockOut = var_jam_clock_out
*ret.IsAbsenClockIn = var_is_absen_clock_in
*ret.IsAbsenClockOut = var_is_absen_clock_out
}
}
return &ret, err
}