package domain import ( "context" "errors" "nearle/db" "nearle/models" "nearle/utils" "net/http" "strconv" firebase "firebase.google.com/go" "firebase.google.com/go/messaging" "google.golang.org/api/option" "gorm.io/gorm" ) var ( firebaseApp *firebase.App fcmClient *messaging.Client ) func getFCMClient() (*messaging.Client, error) { if fcmClient != nil { return fcmClient, nil } opt := option.WithCredentialsFile("nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json") app, err := firebase.NewApp(context.Background(), nil, opt) if err != nil { return nil, errors.New("error initializing firebase app: " + err.Error()) } firebaseApp = app client, err := app.Messaging(context.Background()) if err != nil { return nil, errors.New("error getting messaging client: " + err.Error()) } fcmClient = client return fcmClient, nil } func GetApplocations(aid int) []models.Applocations { var data []models.Applocations var q1 string if aid != 0 { q1 = "Select * from app_location where status='Active' and applocationid=?" db.DB.Raw(q1, aid).Find(&data) } else { q1 = "Select * from app_location where status='Active'" db.DB.Raw(q1).Find(&data) } utils.Logger.Debugf("Query executed for aid: %d", aid) return data } func GetApplocationsv2(userid int) []models.Applocations { var data []models.Applocations var q1 string if userid != 0 { q1 = ` SELECT b.* FROM app_locationconfig a INNER JOIN app_location b ON a.applocationid = b.applocationid WHERE b.status = 'Active' AND a.userid = ?` db.DB.Raw(q1, userid).Find(&data) } else { q1 = ` SELECT * FROM app_location WHERE status = 'Active' ` db.DB.Raw(q1).Find(&data) } utils.Logger.Debugf("Query executed for userid: %d", userid) return data } func GetApplocationConfig(aid int) models.Applocations { var data models.Applocations var q1, q2 string if aid != 0 { q1 = `Select * from app_location where status='Active' and applocationid=` + strconv.Itoa(aid) q2 = `SELECT distinct a.userid,a.userfcmtoken,b.notify,b.applocationid,c.opentime,c.closetime FROM app_users a INNER JOIN app_locationconfig b ON a.userid=b.userid INNER JOiN app_location c on a.applocationid= c.applocationid WHERE b.notify='true' AND b.applocationid=` + strconv.Itoa(aid) } else { q1 = `Select * from app_location where status='Active'` } // print(q1) // db.DB.Raw(q1).Preload("Applocationadmins").Find(&data) db.DB.Raw(q1).Preload("Applocationadmins", func(db *gorm.DB) *gorm.DB { return db.Raw(q2) }).Find(&data) return data } func GetAppPricing(aid, cid, pid int) []models.Apppricing { var data []models.Apppricing var q1 string if aid != 0 { q1 = `SELECT a.* FROM app_pricing a JOIN ( SELECT pricingtypeid, MAX(pricingdate) AS maxpricingdate FROM app_pricing WHERE applocationid = ? GROUP BY pricingtypeid ) AS maxpricing ON a.pricingtypeid = maxpricing.pricingtypeid AND a.pricingdate = maxpricing.maxpricingdate WHERE a.applocationid = ?` db.DB.Raw(q1, aid, aid).Find(&data) } else if cid != 0 { q1 = `SELECT * FROM app_pricing WHERE pricingdate = (SELECT MAX(pricingdate) FROM app_pricing where configid= ?)` db.DB.Raw(q1, cid).Find(&data) } // print(q1) db.DB.Raw(q1).Find(&data) return data } func CreateAppProcing(input models.Apppricing) bool { err := db.DB.Table("app_pricing").Create(&input).Error return err == nil } func GetAllAppPricing(aid int) []models.Apppricing { var data []models.Apppricing var q1 string if aid != 0 { q1 = `SELECT a.*,b.locationname as applocation, d.appname FROM app_pricing a inner join app_location b on a.applocationid=b.applocationid Inner Join app_types c on a.pricingtypeid=c.apptypeid INNER JOIN app_config d ON a.configid=d.configid where a.applocationid=? ORDER BY b.locationname ASC` db.DB.Raw(q1, aid).Find(&data) } else { q1 = `SELECT a.*,b.locationname as applocation, d.appname FROM app_pricing a inner join app_location b on a.applocationid=b.applocationid Inner Join app_types c on a.pricingtypeid=c.apptypeid INNER JOIN app_config d ON a.configid=d.configid ORDER BY b.locationname ASC` db.DB.Raw(q1).Find(&data) } utils.Logger.Debugf("Query executed for aid: %d", aid) return data } func GetApplocationadmins(aid int) []models.Applocationadmins { var data []models.Applocationadmins var q1 string if aid != 0 { q1 = `SELECT distinct a.userid,a.userfcmtoken,b.notify,b.applocationid,c.opentime,c.closetime FROM app_users a INNER JOIN app_locationconfig b ON a.userid=b.userid INNER JOiN app_location c on a.applocationid= c.applocationid WHERE b.notify='true' AND b.applocationid=?` db.DB.Raw(q1, aid).Find(&data) } return data } func GetAppconfig(cid int) models.Appconfig { var data models.Appconfig var q1 string if cid != 0 { q1 = `SELECT a.configid,a.Appname,a.paymentdevkey,a.paymentlivekey,a.fcmkey,a.googleapikey,a.applocationradius, b.providerid,b.providerapi,b.providerkey FROM app_config a LEFT JOIN smsproviders b ON a.smsproviderid=b.providerid WHERE a.configid=?` db.DB.Raw(q1, cid).Find(&data) } else { q1 = `SELECT a.configid,a.Appname,a.paymentdevkey,a.paymentlivekey,a.fcmkey,a.googleapikey,a.applocationradius, b.providerid,b.providerapi,b.providerkey FROM app_config a LEFT JOIN smsproviders b ON a.smsproviderid=b.providerid order by configid asc` db.DB.Raw(q1).Find(&data) } utils.Logger.Debugf("Query executed for cid: %d", cid) return data } func GetAllAppconfig() []models.Appconfig { var data []models.Appconfig q1 := `SELECT a.configid,a.Appname,a.paymentdevkey,a.paymentlivekey,a.fcmkey,a.googleapikey,a.applocationradius, b.providerid,b.providerapi,b.providerkey FROM app_config a LEFT JOIN smsproviders b ON a.smsproviderid=b.providerid order by configid asc` db.DB.Raw(q1).Find(&data) return data } func GetAppModule() []models.AppModule { var data []models.AppModule q1 := `SELECT * FROM app_module where status='Active'` db.DB.Raw(q1).Find(&data) return data } func GetApptypes(tag string) []models.Apptypes { var data []models.Apptypes q1 := "Select * from app_types where status ='Active' and tag=?" db.DB.Raw(q1, tag).Find(&data) return data } func GetSubcategories(moduleid int, categoryid int) []models.Appsubcategories { var data []models.Appsubcategories query := ` SELECT a.subcategoryid,a.categoryid,a.subcategoryname,b.categoryname,a.status,c.moduleid FROM app_subcategory a INNER JOIN app_category b ON a.categoryid = b.categoryid INNER JOIN app_module c ON a.categoryid = c.categoryid WHERE 1=1` var params []interface{} if moduleid != 0 { query += " AND c.moduleid = ?" params = append(params, moduleid) } if categoryid != 0 { query += " AND a.categoryid = ?" params = append(params, categoryid) } db.DB.Raw(query, params...).Scan(&data) return data } func GetCategories(mid int) []models.AppCategory { var data []models.AppCategory if mid == 0 { // No moduleid filter → return all categories q := ` SELECT a.*, b.moduleid FROM app_category a INNER JOIN app_module b ON a.categoryid = b.categoryid ` db.DB.Raw(q).Scan(&data) } else { // Filter by moduleid q := ` SELECT a.*, b.moduleid FROM app_category a INNER JOIN app_module b ON a.categoryid = b.categoryid WHERE b.moduleid = ? ` db.DB.Raw(q, mid).Scan(&data) } return data } func Insertnotification(input models.Data) bool { db.DB.Table("tenantnotifications").Create(&input) return true } func SendLoginNotification(regid, accessid string, pin int) error { if regid == "" { return errors.New("no FCM token provided") } client, err := getFCMClient() if err != nil { return err } // Construct the notification payload body := "Your passcode to Nearle app is: " + strconv.Itoa(pin) + "." title := "Nearle Code" // Prepare FCM message message := &messaging.Message{ Notification: &messaging.Notification{ Title: title, Body: body, }, Token: regid, Android: &messaging.AndroidConfig{ Priority: "high", Notification: &messaging.AndroidNotification{ Sound: "ring", }, }, } // Send the FCM notification response, err := client.Send(context.Background(), message) if err != nil { utils.Logger.Errorf("FCM send error: %v", err) return errors.New("failed to send FCM: " + err.Error()) } utils.Logger.Infof("Successfully sent message: %v", response) return nil } func SendSingleNotification(regid, accessid string, Partner string) bool { client, err := getFCMClient() if err != nil { utils.Logger.Errorf("getFCMClient error: %v", err) return false } title := "Nearle Admin" body := "Orders had been placed for delivery by " + Partner message := &messaging.Message{ Notification: &messaging.Notification{ Title: title, Body: body, }, Token: regid, Android: &messaging.AndroidConfig{ Priority: "high", Notification: &messaging.AndroidNotification{ Sound: "ring", }, }, } response, err := client.Send(context.Background(), message) if err != nil { utils.Logger.Errorf("Error sending single notification: %v", err) return false } utils.Logger.Infof("Successfully sent single message: %v", response) return true } func NotifyUser(fcm models.NotifyUser) bool { client, err := getFCMClient() if err != nil { utils.Logger.Errorf("getFCMClient error: %v", err) return false } message := &messaging.Message{ Notification: &messaging.Notification{ Title: fcm.Notification.Notify.Title, Body: fcm.Notification.Notify.Body, }, Token: fcm.Notification.To, Android: &messaging.AndroidConfig{ Priority: fcm.Notification.Priority, Notification: &messaging.AndroidNotification{ Sound: fcm.Notification.Notify.Sound, }, }, } response, err := client.Send(context.Background(), message) if err != nil { utils.Logger.Errorf("Error notifying user: %v", err) return false } utils.Logger.Infof("Successfully notified user: %v", response) return true } func SendNotifications(fcm models.Notifications) bool { client, err := getFCMClient() if err != nil { utils.Logger.Errorf("getFCMClient error: %v", err) return false } successCount := 0 for _, token := range fcm.Registrationids { if token == "" { continue } message := &messaging.Message{ Notification: &messaging.Notification{ Title: fcm.Notify.Title, Body: fcm.Notify.Body, }, Token: token, Android: &messaging.AndroidConfig{ Priority: fcm.Priority, Notification: &messaging.AndroidNotification{ Sound: fcm.Notify.Sound, }, }, } _, err := client.Send(context.Background(), message) if err != nil { utils.Logger.Errorf("Error sending notification to token %s: %v", token, err) continue } successCount++ } utils.Logger.Infof("Successfully sent %d notifications out of %d", successCount, len(fcm.Registrationids)) return successCount > 0 } func SendNotification(fcm models.Notifications, accessid string) models.Result { var result models.Result client, err := getFCMClient() if err != nil { utils.Logger.Errorf("getFCMClient error: %v", err) result.Code = http.StatusInternalServerError result.Message = "Error initializing FCM client" result.Status = false return result } // Assuming we send to the first registration ID if multiple provided if len(fcm.Registrationids) == 0 { result.Code = http.StatusBadRequest result.Message = "No registration IDs provided" result.Status = false return result } message := &messaging.Message{ Notification: &messaging.Notification{ Title: fcm.Notify.Title, Body: fcm.Notify.Body, }, Token: fcm.Registrationids[0], Android: &messaging.AndroidConfig{ Priority: fcm.Priority, Notification: &messaging.AndroidNotification{ Sound: fcm.Notify.Sound, }, }, } response, err := client.Send(context.Background(), message) if err != nil { utils.Logger.Errorf("Error sending single notification: %v", err) result.Code = http.StatusInternalServerError result.Message = "Error sending notification: " + err.Error() result.Status = false return result } utils.Logger.Infof("Successfully sent notification: %v", response) status := Insertnotification(fcm.Data) if !status { result.Code = http.StatusConflict result.Message = "Notification sent but Insert UnSuccessfully" result.Status = false return result } result.Code = http.StatusCreated result.Message = "Notification Sent and Inserted Successfully" result.Status = true return result } func Getconfigs(categoryid int) models.Appconfig { var q1 string var data models.Appconfig q1 = `SELECT a.configid,a.appname,a.smsproviderid,COALESCE(a.fcmkey,'') AS fcmkey,COALESCE(a.clientid,'') AS clientid,COALESCE(a.googleapikey,'') AS googleapikey,COALESCE(a.paymentdevkey,'') AS paymentdevkey,COALESCE(a.paymentlivekey,'') AS paymentlivekey,a.applocationradius,COALESCE(b.providername,'') AS providername,COALESCE(b.providerapi,'') AS providerapi,COALESCE(b.providerkey,'') AS providerkey, COALESCE(b.username,'') AS username,COALESCE(b.passcode,'') AS passcode,COALESCE(b.defaultprovider,false) AS defaultprovider FROM app_config a LEFT OUTER JOIN smsproviders b ON a.smsproviderid = b.providerid AND b.status='Active' WHERE a.configid=?` db.DB.Raw(q1, categoryid).Find(&data) return data } func UpdateSeqno(tid int, prefix string) error { var field string if prefix == "ORD" { field = "orderseqno" } else if prefix == "INV" { field = "invoiceseqno" } else { return errors.New("invalid prefix: " + prefix) } result := db.DB. Table("ordersequences"). Where("tenantid = ?", tid). Update(field, gorm.Expr(field+" + 1")) if result.Error != nil { utils.Logger.Errorf("UpdateSeqno failed for tenantid %d, prefix %s: %v", tid, prefix, result.Error) return result.Error } if result.RowsAffected == 0 { utils.Logger.Infof("No rows updated for tenantid %d with prefix %s", tid, prefix) return errors.New("no rows updated for tenantid " + strconv.Itoa(tid)) } return nil } func GetSequenceno(tid int, prefix string) string { type SeqResult struct { Orderseqno string } var q1 string if prefix == "ORD" { q1 = ` SELECT COALESCE(CONCAT(tenantid, '-', subprefix, COALESCE(MAX(orderseqno) + 1, 1)), '') AS orderseqno FROM ordersequences WHERE tenantid = ? GROUP BY tenantid, subprefix ` } else if prefix == "INV" { q1 = ` SELECT COALESCE(CONCAT(tenantid, '-', subprefix, COALESCE(MAX(invoiceseqno) + 1, 1)), '') AS orderseqno FROM ordersequences WHERE tenantid = ? GROUP BY tenantid, subprefix ` } var result SeqResult db.DB.Raw(q1, tid).Scan(&result) return result.Orderseqno } func GetTenantNotifications(tenantid, locationid, customerid, moduleid, pageno, pagesize int) ([]models.TenantNotification, int) { var data []models.TenantNotification var total int offset := (pageno - 1) * pagesize baseQuery := db.DB.Table("tenantnotifications"). Select("*"). Where("tenantid = ?", tenantid) if locationid != 0 { baseQuery = baseQuery.Where("locationid = ?", locationid) } if customerid != 0 { baseQuery = baseQuery.Where("customerid = ?", customerid) } if moduleid != 0 { baseQuery = baseQuery.Where("moduleid = ?", moduleid) } err := baseQuery. Order("notificationdate DESC"). Limit(pagesize). Offset(offset). Find(&data).Error if err != nil { utils.Logger.Errorf("DB Error GetTenantNotifications: %v", err) } return data, total } func GetUserBonusSummary(userid int) (models.Riderinfo, error) { var user models.Riderinfo query := ` SELECT COALESCE(SUM(a.bonuspts), 0) AS bonuspts,b.* FROM deliveries a LEFT JOIN app_users b ON a.userid = b.userid WHERE a.userid = ? GROUP BY b.userid ` err := db.DB.Raw(query, userid).Scan(&user).Error if err != nil { return user, err } return user, nil } func CreateAppLocationConfig(input models.AppLocationConfigRequest) bool { if input.Notify == "" { input.Notify = "true" } if input.Status == "" { input.Status = "Active" } for _, locID := range input.Applocationids { data := models.AppLocationConfig{ Applocationid: locID, Configid: input.Configid, Userid: input.Userid, Partnerid: input.Partnerid, Notify: input.Notify, Status: input.Status, } if err := db.DB.Table("app_locationconfig").Create(&data).Error; err != nil { return false } } return true } func UpdateAppLocationConfig(input models.AppLocationConfigRequest) bool { if input.Status == "" { input.Status = "Active" } for _, locID := range input.Applocationids { if err := db.DB.Table("app_locationconfig"). Where("userid = ? AND applocationid = ?", input.Userid, locID). Update("status", input.Status).Error; err != nil { utils.Logger.Errorf("Error updating app_locationconfig: %v", err) return false } } return true } func GetUserRoles() ([]models.UserRoles, error) { var roles []models.UserRoles query := `SELECT * FROM app_roles ` err := db.DB.Raw(query).Scan(&roles).Error if err != nil { return roles, err } return roles, nil }