diff --git a/backend/internal/transabsensi/transabsensi.go b/backend/internal/transabsensi/transabsensi.go index d355874..a53ba5a 100644 --- a/backend/internal/transabsensi/transabsensi.go +++ b/backend/internal/transabsensi/transabsensi.go @@ -1,6 +1,7 @@ package transabsensi import ( + "database/sql" "encoding/base64" "errors" "fmt" @@ -142,8 +143,8 @@ func (transabsensi *TransAbsensiCheckDistanceResponse) CheckDistance(M_StaffID s 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) { +// clock in v1 +func (transabsensi *TransAbsensiResponse) ClockInAbsensiV1(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 @@ -333,8 +334,315 @@ func (transabsensi *TransAbsensiResponse) ClockInAbsensi(T_TransactionM_StaffID 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) { +// 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 + var rMaxDistance int + var rMCompanyLongitude string + var rMCompanyLatitude string + var statusCheckDistance string + var statusDayOfWeek string + var statusLibur 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" { + + // check current distance + + // 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() + )` + + result, err := db.Handle.Exec(qInsertClockIn, + "1", + T_TransactionM_StaffID, + T_TransactionM_CompanyID, + &jamClockIn, + T_TransactionCurrentLatitude, + T_TransactionCurrentLongitude, + T_TransactionCurrentDistance, + &varFileName, + &varProsesFotoSelfie, + "Y", + ) + + // dapatkan id terakhir ketika sudah ditambahkan + lastInsertID, err := result.LastInsertId() + + // set status jika tidak error + if err == nil { + // jika distance not in range T_TransactionNeedConfirmation update to Y + q := `SELECT + M_CompanyLatitude, + M_CompanyLongitude, + M_CompanyMaxDistance + FROM m_company + WHERE M_CompanyIsActive = 'Y' + AND M_CompanyID = ?` + + row := db.Handle.QueryRow( + q, + T_TransactionM_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 + } + + // check distance + qCheckDistance := `SELECT IF(distance_v2(?, ?, ?, ?) < ?, 'range', 'not range') AS distance_status` + + rowx := db.Handle.QueryRow( + qCheckDistance, + T_TransactionCurrentLatitude, + T_TransactionCurrentLongitude, + rMCompanyLatitude, + rMCompanyLongitude, + rMaxDistance, + ) + db.LogSQL(qCheckDistance) + err = rowx.Scan( + &statusCheckDistance, + ) + if err != nil { + log.Printf("Executing query: %s\n", qCheckDistance) + return nil, err + } + + // check libur di table m_libur + qCheckLibur := `SELECT IF(COUNT(*) > 0, 'libur', 'tidak libur') as statusLibur + FROM m_libur + WHERE M_LiburIsActive = 'Y' + AND M_LiburDate = DATE_FORMAT(CURDATE(), '%Y-%m-%d')` + + rowx = db.Handle.QueryRow( + qCheckLibur, + ) + + db.LogSQL(qCheckLibur) + err = rowx.Scan( + &statusLibur, + ) + if err != nil { + if err == sql.ErrNoRows { + statusLibur = "tidak libur" + } else { + log.Printf("Executing query check status libur: %s\n", qCheckLibur) + return nil, err + } + } + + // check hari absen (hari ini) == hari sabtu atau minggu. + // hari sabtu dalam mysql day of week == hari ke 6 + // hari minggu dalam mysql day of week == hari ke 1 + + qCheckDayOfWeek := `SELECT DAYOFWEEK(CURDATE()) As statusDayOfWeek` + db.LogSQL(qCheckDayOfWeek) + rowx = db.Handle.QueryRow( + qCheckDayOfWeek, + ) + err = rowx.Scan( + &statusDayOfWeek, + ) + if err != nil { + if err == sql.ErrNoRows { + statusDayOfWeek = "" + } else { + log.Printf("Executing query check status day of week: %s\n", qCheckDayOfWeek) + return nil, err + } + } + + // jika tidak berada di lokasi kantor maka update T_TransactionNeedConfirmation = Y + if statusCheckDistance != "range" || statusLibur == "libur" || (statusDayOfWeek == "1" || statusDayOfWeek == "7") { + qUpdate := `UPDATE t_transaction + SET T_TransactionNeedConfirmation = 'Y' + WHERE T_TransactionID = ?` + + _, err := db.Handle.Exec(qUpdate, + lastInsertID, + ) + + if err != nil { + log.Printf("Executing query update T_TransactionNeedConfirmation : %s\n", qUpdate) + return nil, err + } + } + + *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 +} + +// clock out v1 +func (transabsensi *TransAbsensiResponse) ClockOutAbsensiV1(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 @@ -524,6 +832,311 @@ func (transabsensi *TransAbsensiResponse) ClockOutAbsensi(T_TransactionM_StaffID 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 + var rMaxDistance int + var rMCompanyLongitude string + var rMCompanyLatitude string + var statusCheckDistance string + var statusDayOfWeek string + var statusLibur 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() + )` + + result, err := db.Handle.Exec(qInsertClockIn, + "2", + T_TransactionM_StaffID, + T_TransactionM_CompanyID, + &jamClockOut, + T_TransactionCurrentLatitude, + T_TransactionCurrentLongitude, + T_TransactionCurrentDistance, + &varFileName, + &varProsesFotoSelfie, + "Y", + ) + + // dapatkan id terakhir ketika sudah ditambahkan + lastInsertID, err := result.LastInsertId() + + // set status jika tidak error + if err == nil { + // jika distance not in range T_TransactionNeedConfirmation update to Y + q := `SELECT + M_CompanyLatitude, + M_CompanyLongitude, + M_CompanyMaxDistance + FROM m_company + WHERE M_CompanyIsActive = 'Y' + AND M_CompanyID = ?` + + row := db.Handle.QueryRow( + q, + T_TransactionM_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 + } + + // check distance + qCheckDistance := `SELECT IF(distance_v2(?, ?, ?, ?) < ?, 'range', 'not range') AS distance_status` + + rowx := db.Handle.QueryRow( + qCheckDistance, + T_TransactionCurrentLatitude, + T_TransactionCurrentLongitude, + rMCompanyLatitude, + rMCompanyLongitude, + rMaxDistance, + ) + db.LogSQL(qCheckDistance) + err = rowx.Scan( + &statusCheckDistance, + ) + if err != nil { + log.Printf("Executing query: %s\n", qCheckDistance) + return nil, err + } + + // check libur di table m_libur + qCheckLibur := `SELECT 'libur' as statusLibur + FROM m_libur + WHERE M_LiburIsActive = 'Y' + AND M_LiburDate = DATE_FORMAT(CURDATE(), "%Y-%m-%d")` + + rowx = db.Handle.QueryRow( + qCheckLibur, + ) + + db.LogSQL(qCheckLibur) + err = rowx.Scan( + &statusLibur, + ) + if err != nil { + if err == sql.ErrNoRows { + statusLibur = "tidak libur" + } else { + log.Printf("Executing query check status libur: %s\n", qCheckLibur) + return nil, err + } + } + + // check hari absen (hari ini) == hari sabtu atau minggu. + // hari sabtu dalam mysql day of week == hari ke 6 + // hari minggu dalam mysql day of week == hari ke 1 + + qCheckDayOfWeek := `SELECT DAYOFWEEK(CURDATE()) As statusDayOfWeek` + rowx = db.Handle.QueryRow( + qCheckDayOfWeek, + ) + db.LogSQL(qCheckDayOfWeek) + err = rowx.Scan( + &statusDayOfWeek, + ) + if err != nil { + if err == sql.ErrNoRows { + statusDayOfWeek = "" + } else { + log.Printf("Executing query check status day of week: %s\n", qCheckDayOfWeek) + return nil, err + } + } + + // jika tidak berada di lokasi kantor maka update T_TransactionNeedConfirmation = Y + if statusCheckDistance != "range" || statusLibur == "libur" || (statusDayOfWeek == "1" || statusDayOfWeek == "7") { + qUpdate := `UPDATE t_transaction + SET T_TransactionNeedConfirmation = 'Y' + WHERE T_TransactionID = ?` + + _, err := db.Handle.Exec(qUpdate, + lastInsertID, + ) + + if err != nil { + log.Printf("Executing query update T_TransactionNeedConfirmation : %s\n", qUpdate) + return nil, err + } + } + + *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) {