diff --git a/backend/contoh_file_selfie/3-20-scaled_2da4536f-4747-4d77-bc8f-37bd921c97a01455633182276530793.jpg b/backend/contoh_file_selfie/3-20-scaled_2da4536f-4747-4d77-bc8f-37bd921c97a01455633182276530793.jpg new file mode 100644 index 0000000..f43974a Binary files /dev/null and b/backend/contoh_file_selfie/3-20-scaled_2da4536f-4747-4d77-bc8f-37bd921c97a01455633182276530793.jpg differ diff --git a/backend/graph/generated/generated.go b/backend/graph/generated/generated.go index 3bea1c4..9e04dd7 100644 --- a/backend/graph/generated/generated.go +++ b/backend/graph/generated/generated.go @@ -48,7 +48,7 @@ type DirectiveRoot struct { type ComplexityRoot struct { Mutation struct { LoginAttendance func(childComplexity int, email string, idGoogleSignIn string) int - MutationClockInAttendance func(childComplexity int, tTransactionMAbsensiTypeID string, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionDistance string, tTransactionSelfiePhoto *string, tTransactionNote *string) int + MutationClockInAttendance func(childComplexity int, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) int } Query struct { @@ -98,7 +98,7 @@ type ComplexityRoot struct { type MutationResolver interface { LoginAttendance(ctx context.Context, email string, idGoogleSignIn string) (*model.Staff, error) - MutationClockInAttendance(ctx context.Context, tTransactionMAbsensiTypeID string, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionDistance string, tTransactionSelfiePhoto *string, tTransactionNote *string) (*model.TransAbsensiResponse, error) + MutationClockInAttendance(ctx context.Context, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) (*model.TransAbsensiResponse, error) } type QueryResolver interface { SearchStaffByEmail(ctx context.Context, email string) (*model.Staff, error) @@ -148,7 +148,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.MutationClockInAttendance(childComplexity, args["T_TransactionM_AbsensiTypeID"].(string), args["T_TransactionM_StaffID"].(string), args["T_TransactionM_CompanyID"].(string), args["T_TransactionCurrentLatitude"].(string), args["T_TransactionCurrentLongitude"].(string), args["T_TransactionDistance"].(string), args["T_TransactionSelfiePhoto"].(*string), args["T_TransactionNote"].(*string)), true + return e.complexity.Mutation.MutationClockInAttendance(childComplexity, args["T_TransactionM_StaffID"].(string), args["T_TransactionM_CompanyID"].(string), args["T_TransactionCurrentLatitude"].(string), args["T_TransactionCurrentLongitude"].(string), args["T_TransactionCurrentDistance"].(string), args["T_TransactionSelfiePhoto"].(*string), args["token"].(string), args["isSelfie"].(string)), true case "Query.queryCheckDistance": if e.complexity.Query.QueryCheckDistance == nil { @@ -539,7 +539,7 @@ extend type Query { # mutation extend type Mutation { # untuk clock in absensi (absensi masuk) - mutationClockInAttendance(T_TransactionM_AbsensiTypeID:String!, T_TransactionM_StaffID:String!, T_TransactionM_CompanyID:String!, T_TransactionCurrentLatitude:String!, T_TransactionCurrentLongitude:String!, T_TransactionDistance:String!, T_TransactionSelfiePhoto:String, T_TransactionNote:String):TransAbsensiResponse! + mutationClockInAttendance(T_TransactionM_StaffID:String!, T_TransactionM_CompanyID:String!, T_TransactionCurrentLatitude:String!, T_TransactionCurrentLongitude:String!, T_TransactionCurrentDistance:String!, T_TransactionSelfiePhoto:String, token:String!, isSelfie:String!):TransAbsensiResponse! } `, BuiltIn: false}, } var parsedSchema = gqlparser.MustLoadSchema(sources...) @@ -576,77 +576,77 @@ func (ec *executionContext) field_Mutation_mutationClockInAttendance_args(ctx co var err error args := map[string]interface{}{} var arg0 string - if tmp, ok := rawArgs["T_TransactionM_AbsensiTypeID"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionM_AbsensiTypeID")) + if tmp, ok := rawArgs["T_TransactionM_StaffID"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionM_StaffID")) arg0, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionM_AbsensiTypeID"] = arg0 + args["T_TransactionM_StaffID"] = arg0 var arg1 string - if tmp, ok := rawArgs["T_TransactionM_StaffID"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionM_StaffID")) + if tmp, ok := rawArgs["T_TransactionM_CompanyID"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionM_CompanyID")) arg1, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionM_StaffID"] = arg1 + args["T_TransactionM_CompanyID"] = arg1 var arg2 string - if tmp, ok := rawArgs["T_TransactionM_CompanyID"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionM_CompanyID")) + if tmp, ok := rawArgs["T_TransactionCurrentLatitude"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionCurrentLatitude")) arg2, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionM_CompanyID"] = arg2 + args["T_TransactionCurrentLatitude"] = arg2 var arg3 string - if tmp, ok := rawArgs["T_TransactionCurrentLatitude"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionCurrentLatitude")) + if tmp, ok := rawArgs["T_TransactionCurrentLongitude"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionCurrentLongitude")) arg3, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionCurrentLatitude"] = arg3 + args["T_TransactionCurrentLongitude"] = arg3 var arg4 string - if tmp, ok := rawArgs["T_TransactionCurrentLongitude"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionCurrentLongitude")) + if tmp, ok := rawArgs["T_TransactionCurrentDistance"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionCurrentDistance")) arg4, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionCurrentLongitude"] = arg4 - var arg5 string - if tmp, ok := rawArgs["T_TransactionDistance"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionDistance")) - arg5, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } - } - args["T_TransactionDistance"] = arg5 - var arg6 *string + args["T_TransactionCurrentDistance"] = arg4 + var arg5 *string if tmp, ok := rawArgs["T_TransactionSelfiePhoto"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionSelfiePhoto")) - arg6, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + arg5, err = ec.unmarshalOString2ᚖstring(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionSelfiePhoto"] = arg6 - var arg7 *string - if tmp, ok := rawArgs["T_TransactionNote"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionNote")) - arg7, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + args["T_TransactionSelfiePhoto"] = arg5 + var arg6 string + if tmp, ok := rawArgs["token"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token")) + arg6, err = ec.unmarshalNString2string(ctx, tmp) if err != nil { return nil, err } } - args["T_TransactionNote"] = arg7 + args["token"] = arg6 + var arg7 string + if tmp, ok := rawArgs["isSelfie"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("isSelfie")) + arg7, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["isSelfie"] = arg7 return args, nil } @@ -909,7 +909,7 @@ func (ec *executionContext) _Mutation_mutationClockInAttendance(ctx context.Cont }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().MutationClockInAttendance(rctx, fc.Args["T_TransactionM_AbsensiTypeID"].(string), fc.Args["T_TransactionM_StaffID"].(string), fc.Args["T_TransactionM_CompanyID"].(string), fc.Args["T_TransactionCurrentLatitude"].(string), fc.Args["T_TransactionCurrentLongitude"].(string), fc.Args["T_TransactionDistance"].(string), fc.Args["T_TransactionSelfiePhoto"].(*string), fc.Args["T_TransactionNote"].(*string)) + return ec.resolvers.Mutation().MutationClockInAttendance(rctx, fc.Args["T_TransactionM_StaffID"].(string), fc.Args["T_TransactionM_CompanyID"].(string), fc.Args["T_TransactionCurrentLatitude"].(string), fc.Args["T_TransactionCurrentLongitude"].(string), fc.Args["T_TransactionCurrentDistance"].(string), fc.Args["T_TransactionSelfiePhoto"].(*string), fc.Args["token"].(string), fc.Args["isSelfie"].(string)) }) if err != nil { ec.Error(ctx, err) diff --git a/backend/graph/graphqls/transabsensi.graphqls b/backend/graph/graphqls/transabsensi.graphqls index c898bbe..8ab3fbf 100644 --- a/backend/graph/graphqls/transabsensi.graphqls +++ b/backend/graph/graphqls/transabsensi.graphqls @@ -23,5 +23,5 @@ extend type Query { # mutation extend type Mutation { # untuk clock in absensi (absensi masuk) - mutationClockInAttendance(T_TransactionM_AbsensiTypeID:String!, T_TransactionM_StaffID:String!, T_TransactionM_CompanyID:String!, T_TransactionCurrentLatitude:String!, T_TransactionCurrentLongitude:String!, T_TransactionDistance:String!, T_TransactionSelfiePhoto:String, T_TransactionNote:String):TransAbsensiResponse! + mutationClockInAttendance(T_TransactionM_StaffID:String!, T_TransactionM_CompanyID:String!, T_TransactionCurrentLatitude:String!, T_TransactionCurrentLongitude:String!, T_TransactionCurrentDistance:String!, T_TransactionSelfiePhoto:String, token:String!, isSelfie:String!):TransAbsensiResponse! } \ No newline at end of file diff --git a/backend/graph/resolver/transabsensi.resolvers.go b/backend/graph/resolver/transabsensi.resolvers.go index bd509ff..b7241e0 100644 --- a/backend/graph/resolver/transabsensi.resolvers.go +++ b/backend/graph/resolver/transabsensi.resolvers.go @@ -6,15 +6,16 @@ package resolver import ( "context" - "fmt" "com.sismedika.com.absensi/graph/model" transabsensiinternal "com.sismedika.com.absensi/internal/transabsensi" ) // MutationClockInAttendance is the resolver for the mutationClockInAttendance field. -func (r *mutationResolver) MutationClockInAttendance(ctx context.Context, tTransactionMAbsensiTypeID string, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionDistance string, tTransactionSelfiePhoto *string, tTransactionNote *string) (*model.TransAbsensiResponse, error) { - panic(fmt.Errorf("not implemented: MutationClockInAttendance - mutationClockInAttendance")) +func (r *mutationResolver) MutationClockInAttendance(ctx context.Context, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) (*model.TransAbsensiResponse, error) { + // panic(fmt.Errorf("not implemented: MutationClockInAttendance - mutationClockInAttendance")) + var transabsensiinternal transabsensiinternal.TransAbsensiResponse + return transabsensiinternal.ClockInAbsensi(tTransactionMStaffID, tTransactionMCompanyID, tTransactionCurrentLatitude, tTransactionCurrentLongitude, tTransactionCurrentDistance, *tTransactionSelfiePhoto, token, isSelfie) } // QueryCheckDistance is the resolver for the queryCheckDistance field. diff --git a/backend/internal/transabsensi/transabsensi.go b/backend/internal/transabsensi/transabsensi.go index 4f21efd..b0df491 100644 --- a/backend/internal/transabsensi/transabsensi.go +++ b/backend/internal/transabsensi/transabsensi.go @@ -1,16 +1,26 @@ 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 + +// 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 @@ -127,3 +137,190 @@ func (transabsensi *TransAbsensiCheckDistanceResponse) CheckDistance(M_StaffID s // 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 + + // check user token + qCheckTokenStaff := `SELECT + M_StaffToken, + 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 = "" + } + + // 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) + // if err != nil { + // log.Printf("here %v", err) + // return &ret, err + // } + // } + + 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 = 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_TransactionSelfiePhoto, + 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, + &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 +} diff --git a/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_16_29.jpg b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_16_29.jpg new file mode 100644 index 0000000..f43974a Binary files /dev/null and b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_16_29.jpg differ diff --git a/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_37_44.jpg b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_37_44.jpg new file mode 100644 index 0000000..f43974a Binary files /dev/null and b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_37_44.jpg differ diff --git a/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_39_40.jpg b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_39_40.jpg new file mode 100644 index 0000000..f43974a Binary files /dev/null and b/backend/selfie_attachment/202401/clockin_SS2022007_2024-01-23_11_39_40.jpg differ