diff --git a/backend/graph/generated/generated.go b/backend/graph/generated/generated.go index 9e04dd7..543bc57 100644 --- a/backend/graph/generated/generated.go +++ b/backend/graph/generated/generated.go @@ -47,8 +47,9 @@ type DirectiveRoot struct { type ComplexityRoot struct { Mutation struct { - LoginAttendance func(childComplexity int, email string, idGoogleSignIn string) int - MutationClockInAttendance func(childComplexity int, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) int + LoginAttendance func(childComplexity int, email string, idGoogleSignIn string) int + MutationClockInAttendance func(childComplexity int, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) int + MutationClockOutAttendance func(childComplexity int, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) int } Query struct { @@ -99,6 +100,7 @@ type ComplexityRoot struct { type MutationResolver interface { LoginAttendance(ctx context.Context, email string, idGoogleSignIn string) (*model.Staff, error) MutationClockInAttendance(ctx context.Context, tTransactionMStaffID string, tTransactionMCompanyID string, tTransactionCurrentLatitude string, tTransactionCurrentLongitude string, tTransactionCurrentDistance string, tTransactionSelfiePhoto *string, token string, isSelfie string) (*model.TransAbsensiResponse, error) + MutationClockOutAttendance(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) @@ -150,6 +152,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in 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 "Mutation.mutationClockOutAttendance": + if e.complexity.Mutation.MutationClockOutAttendance == nil { + break + } + + args, err := ec.field_Mutation_mutationClockOutAttendance_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.MutationClockOutAttendance(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 { break @@ -540,6 +554,9 @@ extend type Query { extend type Mutation { # untuk clock in absensi (absensi masuk) 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! + + # untuk clock out absensi (absensi pulang) + mutationClockOutAttendance(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...) @@ -650,6 +667,84 @@ func (ec *executionContext) field_Mutation_mutationClockInAttendance_args(ctx co return args, nil } +func (ec *executionContext) field_Mutation_mutationClockOutAttendance_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + 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_StaffID"] = arg0 + var arg1 string + 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_CompanyID"] = arg1 + var arg2 string + 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_TransactionCurrentLatitude"] = arg2 + var arg3 string + 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_TransactionCurrentLongitude"] = arg3 + var arg4 string + 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_TransactionCurrentDistance"] = arg4 + var arg5 *string + if tmp, ok := rawArgs["T_TransactionSelfiePhoto"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("T_TransactionSelfiePhoto")) + arg5, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + 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["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 +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -956,6 +1051,67 @@ func (ec *executionContext) fieldContext_Mutation_mutationClockInAttendance(ctx return fc, nil } +func (ec *executionContext) _Mutation_mutationClockOutAttendance(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_mutationClockOutAttendance(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MutationClockOutAttendance(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) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.TransAbsensiResponse) + fc.Result = res + return ec.marshalNTransAbsensiResponse2ᚖcomᚗsismedikaᚗcomᚗabsensiᚋgraphᚋmodelᚐTransAbsensiResponse(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_mutationClockOutAttendance(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "status": + return ec.fieldContext_TransAbsensiResponse_status(ctx, field) + case "message": + return ec.fieldContext_TransAbsensiResponse_message(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TransAbsensiResponse", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_mutationClockOutAttendance_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_searchStaffByEmail(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_searchStaffByEmail(ctx, field) if err != nil { @@ -4272,6 +4428,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "mutationClockOutAttendance": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_mutationClockOutAttendance(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/backend/graph/graphqls/transabsensi.graphqls b/backend/graph/graphqls/transabsensi.graphqls index 8ab3fbf..bd4ce07 100644 --- a/backend/graph/graphqls/transabsensi.graphqls +++ b/backend/graph/graphqls/transabsensi.graphqls @@ -24,4 +24,7 @@ extend type Query { extend type Mutation { # untuk clock in absensi (absensi masuk) 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! + + # untuk clock out absensi (absensi pulang) + mutationClockOutAttendance(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 b7241e0..b994bfe 100644 --- a/backend/graph/resolver/transabsensi.resolvers.go +++ b/backend/graph/resolver/transabsensi.resolvers.go @@ -18,6 +18,13 @@ func (r *mutationResolver) MutationClockInAttendance(ctx context.Context, tTrans return transabsensiinternal.ClockInAbsensi(tTransactionMStaffID, tTransactionMCompanyID, tTransactionCurrentLatitude, tTransactionCurrentLongitude, tTransactionCurrentDistance, *tTransactionSelfiePhoto, token, isSelfie) } +// MutationClockOutAttendance is the resolver for the mutationClockOutAttendance field. +func (r *mutationResolver) MutationClockOutAttendance(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: MutationClockOutAttendance - mutationClockOutAttendance")) + var transabsensiinternal transabsensiinternal.TransAbsensiResponse + return transabsensiinternal.ClockOutAbsensi(tTransactionMStaffID, tTransactionMCompanyID, tTransactionCurrentLatitude, tTransactionCurrentLongitude, tTransactionCurrentDistance, *tTransactionSelfiePhoto, token, isSelfie) +} + // QueryCheckDistance is the resolver for the queryCheckDistance field. func (r *queryResolver) QueryCheckDistance(ctx context.Context, mStaffID string, mCompanyID string, currentLatitude string, currentLongitude string) (*model.TransAbsensiCheckDistanceResponse, error) { // panic(fmt.Errorf("not implemented: QueryCheckDistance - queryCheckDistance")) diff --git a/backend/internal/transabsensi/transabsensi.go b/backend/internal/transabsensi/transabsensi.go index 830d5f7..08790fe 100644 --- a/backend/internal/transabsensi/transabsensi.go +++ b/backend/internal/transabsensi/transabsensi.go @@ -191,6 +191,14 @@ func (transabsensi *TransAbsensiResponse) ClockInAbsensi(T_TransactionM_StaffID 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 @@ -320,3 +328,194 @@ 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) { + // 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 +} diff --git a/backend/selfie_attachment/202401/clockout_ss2022007_2024-01-23_15_15_53.jpg b/backend/selfie_attachment/202401/clockout_ss2022007_2024-01-23_15_15_53.jpg new file mode 100644 index 0000000..f43974a Binary files /dev/null and b/backend/selfie_attachment/202401/clockout_ss2022007_2024-01-23_15_15_53.jpg differ