package transabsensi import ( "database/sql" "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 type RekapKehadiranHomeScreen model.RekapKehadiranHomeScreen // 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 } // 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 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 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 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 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) { // 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 } // fungsi untuk rekap kehadiran home screen func (transabsensi *RekapKehadiranHomeScreen) RekapKehadiranHomeScreenV1(M_StaffID string, M_CompanyID string, token string) (*model.RekapKehadiranHomeScreen, error) { // inisialisasi var err error var ret model.RekapKehadiranHomeScreen var jumlah_saturday int = 0 var jumlah_sunday int = 0 var jumlah_hari_sampai_hari_ini int = 0 var jumlah_hari_sampai_hari_ini_point_a int = 0 var jumlah_hari_libur_sampai_hari_ini_point_b int = 0 var jumlah_absen_sampai_hari_ini_point_c int = 0 var total_ketidakhadiran int = 0 var total_lembur_sampai_hari_ini int = 0 // clear memory address ret.Kehadiran = new(string) ret.Lembur = new(string) ret.TidakHadir = new(string) ret.Status = new(string) ret.Message = new(string) // set value *ret.TidakHadir = "0" *ret.Kehadiran = "0" *ret.Lembur = "0" // query rekap lembur qLembur := `SELECT COUNT(T_TransactionLemburID) as total_lembur_sampai_hari_ini FROM t_transaction_lembur WHERE T_TransactionLemburIsActive = 'Y' AND T_TransactionLemburM_StaffID = ? AND T_TransactionLemburM_CompanyID = ? AND T_TransactionLemburM_AbsensiTypeID = 5 AND MONTH(T_TransactionTglLembur) = MONTH(CURDATE())` rowQLembur := db.Handle.QueryRow( qLembur, M_StaffID, M_CompanyID, ) if err != nil { log.Printf("Error select count lembur: %v", err) log.Printf("Executing query: %s\n", qLembur) return nil, err } db.LogSQL(qLembur) err = rowQLembur.Scan( &total_lembur_sampai_hari_ini, ) // query point A : jumlah hari sampai hari ini ( dalam bulan ini ) selain sabtu dan minggu qCountSaturday := `WITH RECURSIVE offdays as( SELECT LAST_DAY(CURDATE()-INTERVAL 1 MONTH) + INTERVAL 1 DAY AS Date, DAYNAME(LAST_DAY(CURDATE()-INTERVAL 1 MONTH) + INTERVAL 1 DAY) AS DayName UNION ALL SELECT Date + INTERVAL 1 DAY, DAYNAME(Date + INTERVAL 1 DAY) FROM offdays WHERE DATE < LAST_DAY(CURDATE()) ) SELECT count(*) as countSaturday FROM offdays where DAYNAME(DATE) = 'Saturday'` rowQCountSaturday := db.Handle.QueryRow( qCountSaturday, ) if err != nil { log.Printf("Error select count jumlah saturday : %v", err) log.Printf("Executing query: %s\n", qCountSaturday) return nil, err } db.LogSQL(qCountSaturday) err = rowQCountSaturday.Scan( &jumlah_saturday, ) qCountSunday := `WITH RECURSIVE offdays as( SELECT LAST_DAY(CURDATE()-INTERVAL 1 MONTH) + INTERVAL 1 DAY AS Date, DAYNAME(LAST_DAY(CURDATE()-INTERVAL 1 MONTH) + INTERVAL 1 DAY) AS DayName UNION ALL SELECT Date + INTERVAL 1 DAY, DAYNAME(Date + INTERVAL 1 DAY) FROM offdays WHERE DATE < LAST_DAY(CURDATE()) ) SELECT count(*) as countSaturday FROM offdays where DAYNAME(DATE) = 'Sunday'` rowQCountSunday := db.Handle.QueryRow( qCountSunday, ) if err != nil { log.Printf("Error select count jumlah saturday : %v", err) log.Printf("Executing query: %s\n", qCountSunday) return nil, err } db.LogSQL(qCountSunday) err = rowQCountSunday.Scan( &jumlah_sunday, ) qJumlahHariSampaiHariIni := `select timestampdiff(day, concat(year(now()),'-',month(now()),'-01'), date_add( concat(year(now()),'-',month(now()),'-01'), interval 1 month)) as jumlah_hari_sampai_hari_ini` rowqJumlahHariSampaiHariIni := db.Handle.QueryRow( qJumlahHariSampaiHariIni, ) if err != nil { log.Printf("Error select count jumlah_hari_sampai_hari_ini: %v", err) log.Printf("Executing query: %s\n", qJumlahHariSampaiHariIni) return nil, err } db.LogSQL(qJumlahHariSampaiHariIni) err = rowqJumlahHariSampaiHariIni.Scan( &jumlah_hari_sampai_hari_ini, ) // hitung point A jumlah_hari_sampai_hari_ini_point_a = jumlah_hari_sampai_hari_ini - jumlah_sunday - jumlah_saturday // query point B : jumlah hari libur selain sabtu dan minggu sampai hari ini qPointB := `SELECT COUNT(M_LiburID) as jumlah_hari_libur_sampai_hari_ini_point_b FROM m_libur WHERE M_LiburIsActive = 'Y' AND M_LiburDate <= CURDATE() AND DAYOFWEEK(CURDATE()) BETWEEN 2 AND 6 AND MONTH(M_LiburDate) = MONTH(CURDATE())` rowQPointB := db.Handle.QueryRow( qPointB, ) if err != nil { log.Printf("Error select count jumlah_hari_libur_sampai_hari_ini_point_b: %v", err) log.Printf("Executing query: %s\n", qPointB) return nil, err } db.LogSQL(qPointB) err = rowQPointB.Scan( &jumlah_hari_libur_sampai_hari_ini_point_b, ) // query point C : jumlah absen sampai hari ini ( bisa di lihat dari clock in ) qPointC := `SELECT COUNT(T_TransactionID) as jumlah_absen_sampai_hari_ini_point_c FROM t_transaction WHERE T_TransactionIsActive = 'Y' AND T_TransactionM_StaffID = ? AND T_TransactionM_CompanyID = ? AND ( T_TransactionDate >= DATE_FORMAT(CURDATE(), '%Y-%m-01') OR T_TransactionDate = CURDATE() ) AND ( T_TransactionM_AbsensiTypeID = 1 AND (T_TransactionClockAbsensi != '00:00:00' OR T_TransactionClockAbsensi IS NOT NULL) )` rowQPointC := db.Handle.QueryRow( qPointC, M_StaffID, M_CompanyID, ) if err != nil { log.Printf("Error select count jumlah_absen_sampai_hari_ini_point_c: %v", err) log.Printf("Executing query: %s\n", qPointC) return nil, err } db.LogSQL(qPointC) err = rowQPointC.Scan( &jumlah_absen_sampai_hari_ini_point_c, ) total_ketidakhadiran = jumlah_hari_sampai_hari_ini_point_a - jumlah_hari_libur_sampai_hari_ini_point_b - jumlah_absen_sampai_hari_ini_point_c log.Printf("Executing jumlah_saturday: %s\n", strconv.Itoa(jumlah_saturday)) log.Printf("Executing jumlah_sunday: %s\n", strconv.Itoa(jumlah_sunday)) log.Printf("Executing jumlah_hari_sampai_hari_ini: %s\n", strconv.Itoa(jumlah_hari_sampai_hari_ini)) log.Printf("Executing jumlah_hari_sampai_hari_ini_point_a: %s\n", strconv.Itoa(jumlah_hari_sampai_hari_ini_point_a)) log.Printf("Executing jumlah_hari_libur_sampai_hari_ini_point_b: %s\n", strconv.Itoa(jumlah_hari_libur_sampai_hari_ini_point_b)) log.Printf("Executing jumlah_absen_sampai_hari_ini_point_c: %s\n", strconv.Itoa(jumlah_absen_sampai_hari_ini_point_c)) if total_ketidakhadiran != 0 { *ret.TidakHadir = strconv.Itoa(total_ketidakhadiran) } if jumlah_absen_sampai_hari_ini_point_c != 0 { *ret.Kehadiran = strconv.Itoa(jumlah_absen_sampai_hari_ini_point_c) } if total_lembur_sampai_hari_ini != 0 { *ret.Lembur = strconv.Itoa(total_lembur_sampai_hari_ini) } *ret.Status = "OK" *ret.Message = "Rekap Kehadiran Muncul" return &ret, err } func (transabsensi *RekapKehadiranHomeScreen) RekapKehadiranHomeScreen(M_StaffID string, M_CompanyID string, token string) (*model.RekapKehadiranHomeScreen, error) { // inisialisasi var err error var ret model.RekapKehadiranHomeScreen var jumlah_saturday int = 0 var jumlah_sunday int = 0 var jumlah_hari_sampai_hari_ini int = 0 var jumlah_hari_sampai_hari_ini_point_a int = 0 // var jumlah_hari_libur_sampai_hari_ini_point_b int = 0 var jumlah_absen_sampai_hari_ini_point_c int = 0 var total_ketidakhadiran int = 0 var total_lembur_sampai_hari_ini int = 0 // clear memory address ret.Kehadiran = new(string) ret.Lembur = new(string) ret.TidakHadir = new(string) ret.Status = new(string) ret.Message = new(string) // set value *ret.TidakHadir = "0" *ret.Kehadiran = "0" *ret.Lembur = "0" // query rekap lembur qLembur := `SELECT COUNT(T_TransactionLemburID) as total_lembur_sampai_hari_ini FROM t_transaction_lembur WHERE T_TransactionLemburIsActive = 'Y' AND T_TransactionLemburM_StaffID = ? AND T_TransactionLemburM_CompanyID = ? AND T_TransactionLemburM_AbsensiTypeID = 5 AND MONTH(T_TransactionTglLembur) = MONTH(CURDATE())` rowQLembur := db.Handle.QueryRow( qLembur, M_StaffID, M_CompanyID, ) if err != nil { log.Printf("Error select count lembur: %v", err) log.Printf("Executing query: %s\n", qLembur) return nil, err } db.LogSQL(qLembur) err = rowQLembur.Scan( &total_lembur_sampai_hari_ini, ) // query point A : jumlah hari sampai hari ini ( dalam bulan ini ) selain sabtu dan minggu qCountSaturday := `WITH RECURSIVE offdays as( SELECT LAST_DAY(DATE_FORMAT(CURDATE(), '%Y-%m-01') - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS Date, DAYNAME(LAST_DAY(DATE_FORMAT(CURDATE(), '%Y-%m-01') - INTERVAL 1 MONTH) + INTERVAL 1 DAY) AS DayName UNION ALL SELECT Date + INTERVAL 1 DAY, DAYNAME(Date + INTERVAL 1 DAY) FROM offdays WHERE DATE < LAST_DAY(CURDATE()) ) SELECT count(*) as countSaturday FROM offdays where DAYNAME(DATE) = 'Saturday' AND Date BETWEEN DATE_FORMAT(CURDATE(), '%Y-%m-01') AND CURDATE()` rowQCountSaturday := db.Handle.QueryRow( qCountSaturday, ) if err != nil { log.Printf("Error select count jumlah saturday : %v", err) log.Printf("Executing query: %s\n", qCountSaturday) return nil, err } db.LogSQL(qCountSaturday) err = rowQCountSaturday.Scan( &jumlah_saturday, ) qCountSunday := `WITH RECURSIVE offdays AS ( SELECT LAST_DAY(DATE_FORMAT(CURDATE(), '%Y-%m-01') - INTERVAL 1 MONTH) + INTERVAL 1 DAY AS Date, DAYNAME(LAST_DAY(DATE_FORMAT(CURDATE(), '%Y-%m-01') - INTERVAL 1 MONTH) + INTERVAL 1 DAY) AS DayName UNION ALL SELECT Date + INTERVAL 1 DAY, DAYNAME(Date + INTERVAL 1 DAY) FROM offdays WHERE DATE < LAST_DAY(CURDATE()) ) SELECT COUNT(*) AS countSunday FROM offdays WHERE DAYNAME(Date) = 'Sunday' AND Date BETWEEN DATE_FORMAT(CURDATE(), '%Y-%m-01') AND CURDATE()` rowQCountSunday := db.Handle.QueryRow( qCountSunday, ) if err != nil { log.Printf("Error select count jumlah saturday : %v", err) log.Printf("Executing query: %s\n", qCountSunday) return nil, err } db.LogSQL(qCountSunday) err = rowQCountSunday.Scan( &jumlah_sunday, ) qJumlahHariSampaiHariIni := `SELECT DATEDIFF(CURDATE(), DATE_FORMAT(CURDATE(), '%Y-%m-01') ) + 1 AS total_hari` rowqJumlahHariSampaiHariIni := db.Handle.QueryRow( qJumlahHariSampaiHariIni, ) if err != nil { log.Printf("Error select count jumlah_hari_sampai_hari_ini: %v", err) log.Printf("Executing query: %s\n", qJumlahHariSampaiHariIni) return nil, err } db.LogSQL(qJumlahHariSampaiHariIni) err = rowqJumlahHariSampaiHariIni.Scan( &jumlah_hari_sampai_hari_ini, ) // hitung point A jumlah_hari_sampai_hari_ini_point_a = jumlah_hari_sampai_hari_ini - jumlah_sunday - jumlah_saturday // query point C : jumlah absen sampai hari ini ( bisa di lihat dari clock in ) qPointC := `SELECT COUNT(T_TransactionID) as jumlah_absen_sampai_hari_ini_point_c FROM t_transaction WHERE T_TransactionIsActive = 'Y' AND T_TransactionM_StaffID = ? AND T_TransactionM_CompanyID = ? AND ( T_TransactionDate >= DATE_FORMAT(CURDATE(), '%Y-%m-01') OR T_TransactionDate = CURDATE() ) AND ( T_TransactionM_AbsensiTypeID = 1 AND (T_TransactionClockAbsensi != '00:00:00' OR T_TransactionClockAbsensi IS NOT NULL) )` rowQPointC := db.Handle.QueryRow( qPointC, M_StaffID, M_CompanyID, ) if err != nil { log.Printf("Error select count jumlah_absen_sampai_hari_ini_point_c: %v", err) log.Printf("Executing query: %s\n", qPointC) return nil, err } db.LogSQL(qPointC) err = rowQPointC.Scan( &jumlah_absen_sampai_hari_ini_point_c, ) total_ketidakhadiran = jumlah_hari_sampai_hari_ini_point_a - jumlah_absen_sampai_hari_ini_point_c log.Printf("Executing jumlah_saturday: %s\n", strconv.Itoa(jumlah_saturday)) log.Printf("Executing jumlah_sunday: %s\n", strconv.Itoa(jumlah_sunday)) log.Printf("Executing jumlah_hari_sampai_hari_ini: %s\n", strconv.Itoa(jumlah_hari_sampai_hari_ini)) log.Printf("Executing jumlah_hari_sampai_hari_ini_point_a: %s\n", strconv.Itoa(jumlah_hari_sampai_hari_ini_point_a)) // log.Printf("Executing jumlah_hari_libur_sampai_hari_ini_point_b: %s\n", strconv.Itoa(jumlah_hari_libur_sampai_hari_ini_point_b)) log.Printf("Executing jumlah_absen_sampai_hari_ini_point_c: %s\n", strconv.Itoa(jumlah_absen_sampai_hari_ini_point_c)) if total_ketidakhadiran != 0 { *ret.TidakHadir = strconv.Itoa(total_ketidakhadiran) } if jumlah_absen_sampai_hari_ini_point_c != 0 { *ret.Kehadiran = strconv.Itoa(jumlah_absen_sampai_hari_ini_point_c) } if total_lembur_sampai_hari_ini != 0 { *ret.Lembur = strconv.Itoa(total_lembur_sampai_hari_ini) } *ret.Status = "OK" *ret.Message = "Rekap Kehadiran Muncul" return &ret, err }