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 }