commit 0d42ac84e123e7677b54cb566228911cca9a0d3d Author: Gokul Date: Mon May 25 11:52:26 2026 +0530 initial commit diff --git a/.env b/.env new file mode 100644 index 0000000..e45fc82 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +APP_PORT=1009 +DB_HOST=66.116.207.225 +DB_PORT=5433 +DB_NAME=nearledb +DB_USER=admin +DB_PASSWORD="Package@123#" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b24d71e --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Maven +target/ +dist/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fbe2be6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# ---------- Build Stage ---------- +FROM golang:1.22 AS builder + +WORKDIR /app +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux go build -o server + +# ---------- Runtime Stage ---------- +FROM alpine:latest +RUN apk add --no-cache tzdata +WORKDIR /app + +COPY --from=builder /app/server /app +COPY nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json . + +EXPOSE 1009 + +CMD ["/app/server"] diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..5152979 --- /dev/null +++ b/config/config.go @@ -0,0 +1,49 @@ +package config + +import ( + "log" + "os" +) + +type Config struct { + Env string + Port string + DBName string + DBUser string + DBPassword string + DBPort string + DBHost string + UserContextKey string + JWTSecret string +} + +func Load() *Config { + cfg := &Config{ + Env: getEnv("ENV", "production"), + Port: getEnv("APP_PORT", "1009"), + + // ✅ STANDARDIZED DB ENV KEYS + DBName: getEnv("DB_NAME", ""), + DBUser: getEnv("DB_USER", ""), + DBPassword: getEnv("DB_PASSWORD", ""), + DBPort: getEnv("DB_PORT", "5432"), + DBHost: getEnv("DB_HOST", "localhost"), + + UserContextKey: getEnv("USER_CONTEXT_KEY", "nearle"), + JWTSecret: getEnv("JWT_SECRET_KEY", ""), + } + + // ✅ Correct validation + if cfg.DBPassword == "" { + log.Println("Warning: DB_PASSWORD is not set") + } + + return cfg +} + +func getEnv(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} diff --git a/controllers/customerController.go b/controllers/customerController.go new file mode 100644 index 0000000..be28079 --- /dev/null +++ b/controllers/customerController.go @@ -0,0 +1,271 @@ +package controllers + +import ( + "nearle/models" + "nearle/services" + "net/http" + "strconv" + "time" + + "github.com/gofiber/fiber/v2" +) + +type CustomerController struct { + customerService services.CustomerService +} + +func NewCustomerController(customerService services.CustomerService) *CustomerController { + return &CustomerController{customerService: customerService} +} + +func (ctl *CustomerController) GetCustomer(c *fiber.Ctx) error { + cid, _ := strconv.Atoi(c.Query("customerid")) + cno := c.Query("contactno") + + result, err := ctl.customerService.GetCustomer(cid, cno) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": err.Error(), + "status": false, + "details": fiber.Map{}, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *CustomerController) UpdateCustomer(c *fiber.Ctx) error { + var data models.Customers + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + result := ctl.customerService.UpdateCustomer(data) + + if !result.Status { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": result.Message, + }) + } + + return c.Status(http.StatusAccepted).JSON(fiber.Map{ + "status": true, + "code": http.StatusAccepted, + "message": result.Message, + }) +} + +func (ctl *CustomerController) GetCustomerLocations(c *fiber.Ctx) error { + cid, err := strconv.Atoi(c.Query("customerid")) + if err != nil || cid == 0 { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "status": false, + "message": "Invalid customerid", + "details": []interface{}{}, + }) + } + + result := ctl.customerService.GetCustomerLocations(cid) + return c.Status(result.Code).JSON(result) +} + +func (ctl *CustomerController) CreateCustomerLocation(c *fiber.Ctx) error { + var data models.Customerlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + id, err := ctl.customerService.CreateCustomerLocation(data) + + if id == -1 { + // Duplicate address + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": "Address already exists", + }) + } + + if err != nil || id == 0 { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": "Failed", + }) + } + + result := ctl.customerService.GetCustomerLocations(id) + return c.Status(result.Code).JSON(result) +} + + +func (ctl *CustomerController) CreateCustomerRequest(c *fiber.Ctx) error { + var req models.CustomerRequest + + if err := c.BodyParser(&req); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + now := time.Now() + req.Created = now + req.Updated = now + + createdReq, err := ctl.customerService.CreateCustomerRequest(req) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": "Failed to create customer request", + "status": false, + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "code": http.StatusCreated, + "message": "Customer request created successfully", + "status": true, + "data": createdReq, + }) +} + +func (ctl *CustomerController) GetCustomerRequests(c *fiber.Ctx) error { + customerIDStr := c.Query("customerid") + pageNoStr := c.Query("pageno", "1") + pageSizeStr := c.Query("pagesize", "10") + + pageNo, _ := strconv.Atoi(pageNoStr) + pageSize, _ := strconv.Atoi(pageSizeStr) + + requests, total, err := ctl.customerService.GetCustomerRequests(customerIDStr, pageNo, pageSize) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": 200, + "message": "Customer requests fetched successfully", + "status": true, + "data": requests, + "total": total, + }) +} + +func (ctl *CustomerController) GetTenantCustomers(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + lid, _ := strconv.Atoi(c.Query("locationid")) + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + keyword := c.Query("keyword") + + result := ctl.customerService.GetTenantCustomers(tid, lid, pageno, pagesize, keyword) + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *CustomerController) SearchCustomer(c *fiber.Ctx) error { + keyword := c.Query("keyword") + tid, _ := strconv.Atoi(c.Query("tenantid")) + + result := ctl.customerService.SearchCustomer(keyword, tid) + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *CustomerController) CreateCustomer(c *fiber.Ctx) error { + var data models.Customers + + if err := c.BodyParser(&data); err != nil { + return err + } + + result, err := ctl.customerService.CreateCustomer(data) + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusCreated, + "message": "Successful", + "details": result, + }) +} + +func (ctl *CustomerController) CustomerLogin(c *fiber.Ctx) error { + var body struct { + Contactno string `json:"contactno"` + } + + if err := c.BodyParser(&body); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusBadRequest, + "status": false, + "message": "Invalid request", + "error": err.Error(), + }) + } + + customer, err := ctl.customerService.LoginCustomer(body.Contactno) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "message": "Failed to login", + "error": err.Error(), + }) + } + + if customer == nil { + return c.JSON(fiber.Map{ + "code": http.StatusConflict, + "status": false, + "message": "Account not found", + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "status": true, + "message": "Success", + "details": customer, + }) +} diff --git a/controllers/deliveriesController.go b/controllers/deliveriesController.go new file mode 100644 index 0000000..ddb4297 --- /dev/null +++ b/controllers/deliveriesController.go @@ -0,0 +1,261 @@ +package controllers + +import ( + "net/http" + "strconv" + + "nearle/models" + "nearle/services" + + "github.com/gofiber/fiber/v2" +) + +type DeliveriesController struct { + deliveriesService services.DeliveriesService +} + +func NewDeliveriesController(deliveriesService services.DeliveriesService) *DeliveriesController { + return &DeliveriesController{deliveriesService: deliveriesService} +} + +func (ctl *DeliveriesController) CreateDeliveries(c *fiber.Ctx) error { + var deliveries []models.Deliveries + + if err := c.BodyParser(&deliveries); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "status": false, + "message": "Invalid request body", + }) + } + + err := ctl.deliveriesService.CreateDeliveriesService(deliveries) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "status": false, + "message": err.Error(), + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": 200, + "status": true, + "message": "Deliveries created successfully", + }) +} + +func (ctl *DeliveriesController) UpdateDelivery(c *fiber.Ctx) error { + var data models.UpdateDeliveryStatus + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + err := ctl.deliveriesService.UpdateDeliveryService(data) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "code": http.StatusCreated, + "message": "Success", + "status": true, + }) +} + +func (ctl *DeliveriesController) GetDeliverySummary(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + pid, _ := strconv.Atoi(c.Query("partnerid")) + uid, _ := strconv.Atoi(c.Query("userid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + lid, _ := strconv.Atoi(c.Query("locationid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + data, err := ctl.deliveriesService.GetDeliverySummary(tid, pid, uid, aid, lid, fdate, tdate) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *DeliveriesController) GetDeliveryInsight(c *fiber.Ctx) error { + // Get tenantid from query param + tenantIDStr := c.Query("tenantid") + tenantID, _ := strconv.Atoi(tenantIDStr) + + // Call service with tenantID + locations, err := ctl.deliveriesService.GetDeliveryInsightService(tenantID) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": locations, + }) +} + +func (ctl *DeliveriesController) GetLocationDeliverySummary(c *fiber.Ctx) error { + tenantIDStr := c.Query("tenantid") + tenantID, _ := strconv.Atoi(tenantIDStr) + + data, err := ctl.deliveriesService.GetLocationDeliverySummary(tenantID) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *DeliveriesController) GetReportSummary(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + pid, _ := strconv.Atoi(c.Query("partnerid")) + uid, _ := strconv.Atoi(c.Query("userid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + data, err := ctl.deliveriesService.GetReportSummary(tid, pid, uid, aid, fdate, tdate) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *DeliveriesController) GetRiderSummary(c *fiber.Ctx) error { + aid, _ := strconv.Atoi(c.Query("applocationid")) + pid, _ := strconv.Atoi(c.Query("partnerid")) + tid, _ := strconv.Atoi(c.Query("tenantid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + data, err := ctl.deliveriesService.GetRiderSummary(aid, pid, tid, fdate, tdate) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *DeliveriesController) GetDeliveries(c *fiber.Ctx) error { + + pid, _ := strconv.Atoi(c.Query("partnerid")) + tid, _ := strconv.Atoi(c.Query("tenantid")) + uid, _ := strconv.Atoi(c.Query("userid")) + cid, _ := strconv.Atoi(c.Query("customerid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + aud, _ := strconv.Atoi(c.Query("appuserid")) + lid, _ := strconv.Atoi(c.Query("locationid")) // 👈 NEW + + pageno, _ := strconv.Atoi(c.Query("pageno", "1")) + pagesize, _ := strconv.Atoi(c.Query("pagesize", "")) + + fdate := c.Query("fromdate") + tdate := c.Query("todate") + stat := c.Query("status") + keyword := c.Query("keyword") + + info := models.DeliveryQuery{ + Partnerid: pid, + Tenantid: tid, + UserID: uid, + Customerid: cid, + Appuserid: aud, + Applocationid: aid, + Locationid: lid, // 👈 NEW + Fromdate: fdate, + ToDate: tdate, + Status: stat, + Pageno: pageno, + Pagesize: pagesize, + Keyword: keyword, + } + + result := ctl.deliveriesService.GetDeliveries(info) + if result == nil { + result = []models.Deliveryinfo{} + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *DeliveriesController) GetDeliveryQueues(c *fiber.Ctx) error { + fdate := c.Query("fromdate") + tdate := c.Query("todate") + uid, _ := strconv.Atoi(c.Query("userid")) + + result, err := ctl.deliveriesService.GetDeliveryQueues(uid, fdate, tdate) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} diff --git a/controllers/orderController.go b/controllers/orderController.go new file mode 100644 index 0000000..ec987cf --- /dev/null +++ b/controllers/orderController.go @@ -0,0 +1,356 @@ +package controllers + +import ( + "log" + "nearle/models" + "net/http" + "strconv" + "strings" + "time" + + "nearle/services" + + "github.com/gofiber/fiber/v2" +) + +type OrderController struct { + orderService services.OrderService +} + +func NewOrderController(orderService services.OrderService) *OrderController { + return &OrderController{orderService: orderService} +} + +func (ctl *OrderController) GetOrders(c *fiber.Ctx) error { + + tid, _ := strconv.Atoi(c.Query("tenantid")) + pid, _ := strconv.Atoi(c.Query("partnerid")) + cid, _ := strconv.Atoi(c.Query("customerid")) + mid, _ := strconv.Atoi(c.Query("moduleid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + uid, _ := strconv.Atoi(c.Query("appuserid")) + lid, _ := strconv.Atoi(c.Query("locationid")) + configid, _ := strconv.Atoi(c.Query("configid")) + + stat := c.Query("status") + fdate := c.Query("fromdate") + tdate := c.Query("todate") + keyword := c.Query("keyword") + + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + + // Build dynamic query struct + query := models.DeliveryQuery{ + Tenantid: tid, + Partnerid: pid, + Customerid: cid, + Moduleid: mid, + Applocationid: aid, + Locationid: lid, + UserID: uid, + Appuserid: uid, + Configid: configid, + Fromdate: fdate, + ToDate: tdate, + Status: stat, + Keyword: keyword, + Pageno: pageno, + Pagesize: pagesize, + } + + var ( + orders []models.OrderInfo + err error + ) + + // -------------------------- + // 🔥 DYNAMIC ROUTING LOGIC + // -------------------------- + + if tid != 0 && lid != 0 { + // ⭐ Both tenant & location → special handler + orders, err = ctl.orderService.GetTenantLocationOrders(query) + + } else if tid != 0 { + // Tenant only + orders, err = ctl.orderService.GetTenantOrders(query) + + } else if pid != 0 { + // Partner + orders, err = ctl.orderService.GetPartnerOrders(stat, fdate, tdate, pid, pageno, pagesize, keyword) + + } else if cid != 0 { + // Customer + orders, err = ctl.orderService.GetCustomerOrders(stat, fdate, tdate, cid, mid, pageno, pagesize, keyword) + + } else if aid != 0 { + // App-location orders + orders, err = ctl.orderService.GetAdminOrders(stat, fdate, tdate, aid, pageno, pagesize, keyword) + + } else if uid != 0 { + // User orders + orders, err = ctl.orderService.GetUserOrders(stat, fdate, tdate, uid, pageno, pagesize, keyword) + + } else { + // All orders + orders, err = ctl.orderService.GetAllOrders(stat, fdate, tdate, pageno, pagesize, keyword) + } + + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Success", + "details": orders, + }) +} + + +func (ctl *OrderController) GetOrderSummary(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + pid, _ := strconv.Atoi(c.Query("partnerid")) + cid, _ := strconv.Atoi(c.Query("customerid")) + lid, _ := strconv.Atoi(c.Query("locationid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + data, err := ctl.orderService.GetOrderSummary(tid, pid, cid, lid, fdate, tdate) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + + +func (ctl *OrderController) GetlocationOrderSummary(c *fiber.Ctx) error { + tenantIDStr := c.Query("tenantid") + tenantID, _ := strconv.Atoi(tenantIDStr) + + data, err := ctl.orderService.GetLocationOrderSummary(tenantID) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *OrderController) GetOrderInsights(c *fiber.Ctx) error { + tenantIDStr := c.Query("tenantid") + tenantID, _ := strconv.Atoi(tenantIDStr) + + insights, err := ctl.orderService.GetOrderInsights(tenantID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "message": "Failed to fetch order insights", + "error": err.Error(), + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "status": true, + "message": "Success", + "details": insights, + }) +} + +func (ctl *OrderController) GetOrderDetails(c *fiber.Ctx) error { + orderHeaderIDStr := c.Query("orderheaderid") + if orderHeaderIDStr == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "message": "orderheaderid is required", + "status": false, + "details": []interface{}{}, + }) + } + + orderHeaderID, err := strconv.Atoi(orderHeaderIDStr) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "message": "invalid orderheaderid", + "status": false, + "details": []interface{}{}, + }) + } + + details, err := ctl.orderService.GetOrderDetails(orderHeaderID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch order details", + "status": false, + "details": []interface{}{}, + }) + } + + var orderAmount, totalTaxAmount float64 + if len(details) > 0 { + orderAmount = details[0].Orderamount + totalTaxAmount = details[0].Totaltaxamount + } + + return c.JSON(fiber.Map{ + "code": 200, + "pricedetails": fiber.Map{ + "orderamount": orderAmount, + "totaltaxamount": totalTaxAmount, + }, + "details": details, + "message": "Success", + "status": true, + }) +} + +func (ctl *OrderController) UpdateOrder(c *fiber.Ctx) error { + var order models.Orders + + if err := c.BodyParser(&order); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.orderService.UpdateOrder(&order); err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": "Error updating order", + "status": false, + }) + } + + return c.Status(http.StatusAccepted).JSON(fiber.Map{ + "code": http.StatusAccepted, + "message": "Success", + "status": true, + }) +} + +func (ctl *OrderController) CreateOrderv3(c *fiber.Ctx) error { + var data models.Orders + + // 🛠️ Strategy 1: Try parsing direct object (for Apidog) + if err := c.BodyParser(&data); err != nil { + log.Println("BodyParser strategy 1 error:", err) + } + + // 🛠️ Strategy 2: If Strategy 1 didn't find a Tenantid, try parsing wrapped object (for Mobile App) + if data.Tenantid == 0 { + type OrderWrapper struct { + Orders models.Orders `json:"orders"` + } + var wrapper OrderWrapper + if err := c.BodyParser(&wrapper); err == nil && wrapper.Orders.Tenantid != 0 { + data = wrapper.Orders + } + } + + // Double check we have the required ID + if data.Tenantid == 0 { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": "Tenant ID is required", + "status": false, + }) + } + + if strings.TrimSpace(data.Orderdate) == "" { + data.Orderdate = time.Now().Format("2006-01-02 15:04:05") + } + if strings.TrimSpace(data.Deliverytime) == "" { + data.Deliverytime = time.Now().Format("2006-01-02 15:04:05") + } + + order, err := ctl.orderService.CreateOrder(data) + if err != nil { + log.Println("CreateOrder service error:", err) + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Order created successfully", + "status": true, + "details": order, + }) +} + +func (ctl *OrderController) GetCustomerOrders(c *fiber.Ctx) error { + customerID := c.Query("customerid") + tenantID := c.Query("tenantid") + moduleID := c.Query("moduleid") + fromDate := c.Query("fromdate") + toDate := c.Query("todate") + orderStatus := c.Query("orderstatus") + keyword := c.Query("keyword") + + pageNo, _ := strconv.Atoi(c.Query("pageno", "1")) + pageSize, _ := strconv.Atoi(c.Query("pagesize", "10")) + + if pageNo < 1 { + pageNo = 1 + } + if pageSize < 1 { + pageSize = 10 + } + offset := (pageNo - 1) * pageSize + + orders, err := ctl.orderService.GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword, pageSize, offset) + if err != nil { + log.Println("GetCustomerOrders error:", err) + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "status": false, + "message": "Failed to fetch customer orders", + "error": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "status": true, + "message": "Customer orders fetched successfully", + "data": orders, + }) +} diff --git a/controllers/partnerController.go b/controllers/partnerController.go new file mode 100644 index 0000000..dae14fa --- /dev/null +++ b/controllers/partnerController.go @@ -0,0 +1,156 @@ +package controllers + +import ( + "nearle/services" + "net/http" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +type PartnerController struct { + partnerService services.PartnerService +} + +func NewPartnerController(partnerService services.PartnerService) *PartnerController { + return &PartnerController{partnerService: partnerService} +} + +func (ctl *PartnerController) GetActiveRiders(c *fiber.Ctx) error { + pid, _ := strconv.Atoi(c.Query("partnerid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + uid, _ := strconv.Atoi(c.Query("userid")) + tid,_ := strconv.Atoi(c.Query("tenantid")) + result, err := ctl.partnerService.GetActiveRiders(pid, aid, uid, tid) + + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) +} + +func (ctl *PartnerController) GetPartners(c *fiber.Ctx) error { + pid, _ := strconv.Atoi(c.Query("partnerid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + uid, _ := strconv.Atoi(c.Query("userid")) + + result, err := ctl.partnerService.GetPartners(aid, pid, uid) + + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) +} + +func (ctl *PartnerController) GetRiderShifts(c *fiber.Ctx) error { + + aid, _ := strconv.Atoi(c.Query("applocationid")) + + result, err := ctl.partnerService.GetRiderShifts(aid) + + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) + +} + +func (ctl *PartnerController) GetLocationConfig(c *fiber.Ctx) error { + + uid, _ := strconv.Atoi(c.Query("userid")) + cid, _ := strconv.Atoi(c.Query("configid")) + + result, err := ctl.partnerService.GetLocationConfig(uid, cid) + + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) + +} + + +func (ctl *PartnerController) GetRiderLogs(c *fiber.Ctx) error { + pid, _ := strconv.Atoi(c.Query("partnerid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + fdate := c.Query("fromdate") + tdate := c.Query("fromdate") + + data, err := ctl.partnerService.GetRiderLogs(pid, aid, fdate, tdate) + + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": data, + }) +} + + +func (ctl *PartnerController) GetRiderInfo(c *fiber.Ctx) error { + uid, _ := strconv.Atoi(c.Query("userid")) + + result, err := ctl.partnerService.GetRiderInfo(uid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) +} diff --git a/controllers/productController.go b/controllers/productController.go new file mode 100644 index 0000000..32f9ba3 --- /dev/null +++ b/controllers/productController.go @@ -0,0 +1,516 @@ +package controllers + +import ( + "net/http" + "strconv" + + "nearle/models" + "nearle/services" + + "github.com/gofiber/fiber/v2" +) + +type ProductController struct { + productService services.ProductService +} + +func NewProductController(productService services.ProductService) *ProductController { + return &ProductController{productService: productService} +} + +func (ctl *ProductController) GetProductSubCategory(c *fiber.Ctx) error { + categoryID, _ := strconv.Atoi(c.Query("categoryid", "0")) + tenantID, _ := strconv.Atoi(c.Query("tenantid", "0")) + + data, err := ctl.productService.GetProductSubCategory(categoryID, tenantID) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": 200, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *ProductController) GetProductCount(c *fiber.Ctx) error { + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + categoryID, _ := strconv.Atoi(c.Query("categoryid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + // locationID, _ := strconv.Atoi(c.Query("locationid")) + approve := c.Query("approve", "") + + data, err := ctl.productService.GetProductCount(tenantID, categoryID, subcategoryID, approve) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusConflict, + "message": "failed", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *ProductController) GetProductCategory(c *fiber.Ctx) error { + data, err := ctl.productService.GetProductCategory() + if err != nil { + return c.JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch categories", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *ProductController) GetProductVariants(c *fiber.Ctx) error { + + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + + data, err := ctl.productService.GetProductVariants(tenantID, subcategoryID) + if err != nil { + return c.JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch Product varients", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *ProductController) GetCatalougeProducts(c *fiber.Ctx) error { + + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + locationID, _ := strconv.Atoi(c.Query("locationid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + keyword := c.Query("keyword") + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + + data, err := ctl.productService.GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) + if err != nil { + return c.JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch Catalouge Product", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) + +} + +func (ctl *ProductController) GetProductStocks(c *fiber.Ctx) error { + tenantID := c.Query("tenantid") + locationID := c.Query("locationid") + + stocks, err := ctl.productService.GetProductStocks(tenantID, locationID) + if err != nil { + return c.JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch product stocks", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": 200, + "message": "Product stocks fetched successfully", + "status": true, + "data": stocks, + }) +} + +func (ctl *ProductController) CreateProductStock(c *fiber.Ctx) error { + var stocks []models.Productstock + + if err := c.BodyParser(&stocks); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.productService.CreateProductStock(stocks); err != nil { + return c.JSON(fiber.Map{ + "code": 500, + "message": "Failed to create product stocks", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": 201, + "message": "Product stocks created successfully", + "status": true, + "details": stocks, + }) +} + +func (ctl *ProductController) CreateProduct(c *fiber.Ctx) error { + var product models.Products + + if err := c.BodyParser(&product); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.productService.CreateProduct(product); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": "Failed to create product", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusCreated, + "message": "Product created successfully", + "status": true, + "data": product, + }) +} + +func (ctl *ProductController) UpdateProduct(c *fiber.Ctx) error { + var product models.Products + + if err := c.BodyParser(&product); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.productService.UpdateProduct(product); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusAccepted, + "message": "Product updated successfully", + "status": true, + "data": product, + }) +} + +func (ctl *ProductController) DeleteProduct(c *fiber.Ctx) error { + + pidStr := c.Query("productid") + pid, err := strconv.Atoi(pidStr) + if err != nil || pid <= 0 { + return c.JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid product ID", + "status": false, + }) + } + + if err := ctl.productService.DeleteProduct(pid); err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Product deleted successfully", + "status": true, + }) +} + +func (ctl *ProductController) GetStockStatement(c *fiber.Ctx) error { + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + locationID, _ := strconv.Atoi(c.Query("locationid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + keyword := c.Query("keyword") + + data, err := ctl.productService.GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *ProductController) GetLocationProducts(c *fiber.Ctx) error { + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + locationID, _ := strconv.Atoi(c.Query("locationid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + keyword := c.Query("keyword") + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + + result, err := ctl.productService.GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Success", + "details": result, + }) +} + +func (ctl *ProductController) GetLocationProductSummary(c *fiber.Ctx) error { + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + locationID, _ := strconv.Atoi(c.Query("locationid")) + + result, err := ctl.productService.GetLocationProductSummary(tenantID, locationID) + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusInternalServerError, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Success", + "details": result, + }) +} + +func (ctl *ProductController) GetAllProducts(c *fiber.Ctx) error { + categoryID, _ := strconv.Atoi(c.Query("categoryid")) + subcategoryID, _ := strconv.Atoi(c.Query("subcategoryid")) + productID, _ := strconv.Atoi(c.Query("productid")) + applocationID, _ := strconv.Atoi(c.Query("applocationid")) + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + locationID, _ := strconv.Atoi(c.Query("locationid")) + keyword := c.Query("keyword", "") + productStatus := c.Query("productstatus", "") + approve := c.Query("approve", "") + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + + details, err := ctl.productService.FetchFilteredProducts( + categoryID, subcategoryID, productID, applocationID, tenantID, + locationID, keyword, productStatus, approve, pageno, pagesize, + ) + if err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Success", + "data": details, + }) +} + +func (ctl *ProductController) GetProductByVariant(c *fiber.Ctx) error { + + tenantID, _ := strconv.Atoi(c.Query("tenantid")) + variantid, _ := strconv.Atoi(c.Query("variantid")) + + result, err := ctl.productService.GetProductByVariant(tenantID, variantid) + + if err != nil { + + return c.JSON(fiber.Map{ + "code": http.StatusConflict, + "message": "failed", + "status": false, + }) + + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) + +} + +func (ctl *ProductController) GetProductsBySubcategory(c *fiber.Ctx) error { + categoryID, err := strconv.Atoi(c.Query("categoryid")) + if err != nil || categoryID == 0 { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "status": false, + "message": "Valid categoryid is required", + "data": fiber.Map{}, + }) + } + + params := models.ProductFilter{ + CategoryID: categoryID, + TenantID: parseInt(c.Query("tenantid")), + AppLocationID: parseInt(c.Query("applocationid")), + ProductID: parseInt(c.Query("productid")), + Keyword: c.Query("keyword"), + LocationID: parseInt(c.Query("locationid")), + } + + result, err := ctl.productService.GetProductsBySubcategory(params) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "status": false, + "message": err.Error(), + "data": fiber.Map{}, + }) + } + + return c.JSON(fiber.Map{ + "code": 200, + "status": true, + "message": "Success", + "data": result, + }) +} + +func parseInt(s string) int { + if s == "" { + return 0 + } + val, _ := strconv.Atoi(s) + return val +} + +func (ctl *ProductController) UpdateProductLocation(c *fiber.Ctx) error { + var data models.Productlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + if err := ctl.productService.UpdateProductLocation(data); err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.Status(http.StatusAccepted).JSON(fiber.Map{ + "status": true, + "code": http.StatusAccepted, + "message": "Product update successful", + }) +} + +func (ctl *ProductController) CreateProductLocation(c *fiber.Ctx) error { + var data []models.Productlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.productService.CreateProductLocation(data); err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "status": true, + "code": http.StatusCreated, + "message": "Success", + }) +} + +func (ctl *ProductController) CreateProductVariant(c *fiber.Ctx) error { + var input models.Productvariant + + // Parse request body + if err := c.BodyParser(&input); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + // Call service layer + if err := ctl.productService.CreateProductVariant(input); err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + }) +} diff --git a/controllers/tenantController.go b/controllers/tenantController.go new file mode 100644 index 0000000..a349acf --- /dev/null +++ b/controllers/tenantController.go @@ -0,0 +1,436 @@ +package controllers + +import ( + "fmt" + "log" + "nearle/models" + "nearle/services" + "net/http" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" +) + +type TenantController struct { + tenantService services.TenantService +} + +func NewTenantController(tenantService services.TenantService) *TenantController { + return &TenantController{tenantService: tenantService} +} + +func (ctl *TenantController) SearchTenant(c *fiber.Ctx) error { + status := c.Query("status") + searchStr := c.Query("keyword") + + data, err := ctl.tenantService.SearchTenant(status, searchStr) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": fmt.Sprintf("Error searching tenants: %v", err), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) GetAllTenants(c *fiber.Ctx) error { + pageno, _ := strconv.Atoi(c.Query("pageno")) + pagesize, _ := strconv.Atoi(c.Query("pagesize")) + status := c.Query("status") + aid, _ := strconv.Atoi(c.Query("applocationid")) + tenanttype := c.Query("tenanttype") + keyword := c.Query("keyword") + + details, err := ctl.tenantService.GetAllTenants(pageno, pagesize, aid, status, tenanttype, keyword) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": fmt.Sprintf("Error getting all tenants: %v", err), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": details, + }) +} + +func (ctl *TenantController) GetTenantLocations(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + + data, err := ctl.tenantService.GetTenantLocations(tid) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": fmt.Sprintf("Error getting tenant locations: %v", err), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) GetTenantSlot(c *fiber.Ctx) error { + + data, err := ctl.tenantService.GetTenantSlot() + + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": fmt.Sprintf("Error getting tenant slots: %v", err), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) CreateTenantCustomer(c *fiber.Ctx) error { + var req models.CreateTenantCustomerRequest + + if err := c.BodyParser(&req); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "status": false, + "message": "Invalid request body", + "data": fiber.Map{}, + }) + } + + tenantCustomer, err := ctl.tenantService.CreateTenantCustomer(req) + if err != nil { + if strings.Contains(err.Error(), "already exists for this location") { + return c.JSON(fiber.Map{ + "code": 409, + "status": false, + "message": "Customer already assigned to this location", + "data": fiber.Map{}, + }) + } + + log.Println("Error inserting tenant customer:", err) + return c.JSON(fiber.Map{ + "code": 500, + "status": false, + "message": "Failed to create tenant customer", + "data": fiber.Map{}, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": 200, + "status": true, + "message": "Tenant customer created successfully", + "data": tenantCustomer, + }) +} + +func (ctl *TenantController) GetCustomerTenants(c *fiber.Ctx) error { + customerID, err := strconv.Atoi(c.Query("customerid")) + if err != nil || customerID == 0 { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "message": "Invalid customerid", + "status": false, + "details": []interface{}{}, + }) + } + + categoryID, _ := strconv.Atoi(c.Query("categoryid")) + tenantFlag, _ := strconv.Atoi(c.Query("tenant")) // 0 = all tenants, 1 = tenants with orders + + data, err := ctl.tenantService.GetCustomerTenants(customerID, categoryID, tenantFlag) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": err.Error(), + "status": false, + "details": []interface{}{}, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data.Details, + }) +} + +func (ctl *TenantController) GetTenantPricing(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + aid, _ := strconv.Atoi(c.Query("applocationid")) + + data, err := ctl.tenantService.GetTenantPricing(tid, aid) + if err != nil { + return c.JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) UpdateLocation(c *fiber.Ctx) error { + var data models.Tenantlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + if err := ctl.tenantService.UpdateLocation(data); err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusAccepted, + "message": "Location update successful", + }) +} + +func (ctl *TenantController) CreateLocation(c *fiber.Ctx) error { + var data models.Tenantlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + err := ctl.tenantService.CreateLocation(data) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "code": http.StatusCreated, + "message": "Location Successfully Created", + "status": true, + }) +} + +func (ctl *TenantController) GetStaffs(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + + data, err := ctl.tenantService.GetStaffs(tid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) CreateStaff(c *fiber.Ctx) error { + var data models.User + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + if err := ctl.tenantService.CreateStaff(data); err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusCreated, + "message": "Staff created successfully", + "status": true, + }) +} + +func (ctl *TenantController) UpdateStaff(c *fiber.Ctx) error { + var data models.User + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + if err := ctl.tenantService.UpdateStaff(data); err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusAccepted, + "message": "Staff updated successfully", + }) +} + +func (ctl *TenantController) CreateTenantLocation(c *fiber.Ctx) error { + var data models.Tenantlocations + + if err := c.BodyParser(&data); err != nil { + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "code": 400, + "message": "Invalid request body", + "status": false, + }) + } + + resp := ctl.tenantService.CreateTenantLocation(data) + return c.Status(fiber.StatusOK).JSON(resp) +} + +func (ctl *TenantController) UpdateTenantLocation(c *fiber.Ctx) error { + var data models.Tenantlocations + + // Parse JSON body + if err := c.BodyParser(&data); err != nil { + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "status": false, + "code": 400, + "message": "Invalid request body", + }) + } + + // Call service layer + resp := ctl.tenantService.UpdateTenantLocation(data) + + // Always return HTTP 200 (as per your API pattern) + return c.Status(fiber.StatusOK).JSON(resp) +} + +func (ctl *TenantController) CreateTenantUser(c *fiber.Ctx) error { + var data models.Tenants + + if err := c.BodyParser(&data); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "status": false, + "message": "Invalid request body", + }) + } + + result, err := ctl.tenantService.CreateTenantUser(data) + if err != nil { + if err.Error() == "Tenant Already Exists" { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": 409, + "status": false, + "message": err.Error(), + }) + } + + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "status": false, + "message": err.Error(), + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "code": 201, + "status": true, + "message": "Successfully Created", + "details": result, + }) +} + +func (ctl *TenantController) GetTenantInfo(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(strings.TrimSpace(c.Query("tenantid"))) + locationid, _ := strconv.Atoi(strings.TrimSpace(c.Query("locationid"))) + + data, err := ctl.tenantService.GetTenantByID(tid, locationid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": "Error fetching tenant info", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} + +func (ctl *TenantController) GetTenantByKeyword(c *fiber.Ctx) error { + keyword := c.Query("keyword") + + data, err := ctl.tenantService.GetTenantByKeyword(keyword) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": "Error searching tenants by keyword", + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": data, + }) +} diff --git a/controllers/userController.go b/controllers/userController.go new file mode 100644 index 0000000..776b79e --- /dev/null +++ b/controllers/userController.go @@ -0,0 +1,230 @@ +package controllers + +import ( + "net/http" + "strconv" + + "nearle/models" + "nearle/services" + + "github.com/gofiber/fiber/v2" +) + +type UserController struct { + userService services.UserService +} + +func NewUserController(userService services.UserService) *UserController { + return &UserController{userService: userService} +} + +func (ctl *UserController) GetAllUsers(c *fiber.Ctx) error { + roleID, _ := strconv.Atoi(c.Query("roleid", "0")) + tenantID, _ := strconv.Atoi(c.Query("tenantid", "0")) + pageno, _ := strconv.Atoi(c.Query("pageno", "1")) + pagesize, _ := strconv.Atoi(c.Query("pagesize", "10")) + keyword := c.Query("keyword", "") + + users, err := ctl.userService.GetAllUsers(roleID, tenantID, pageno, pagesize, keyword) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": users, + }) +} + +func (ctl *UserController) GetUserInfo(c *fiber.Ctx) error { + uid, err := strconv.Atoi(c.Query("userid")) + if err != nil || uid <= 0 { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid userid", + "status": false, + }) + } + + user, err := ctl.userService.GetUserByID(uid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": user, + }) +} + +func (ctl *UserController) Login(c *fiber.Ctx) error { + var user models.User + if err := c.BodyParser(&user); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid request body", + "status": false, + }) + } + + info, err := ctl.userService.Login(user) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": "User not found", + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": info, + }) +} + +func (ctl *UserController) TenantLogin(c *fiber.Ctx) error { + var user models.User + if err := c.BodyParser(&user); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + info, err := ctl.userService.TenantLogin(user) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": info, + }) +} + +func (ctl *UserController) UpdateStaff(c *fiber.Ctx) error { + var user models.User + if err := c.BodyParser(&user); err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": http.StatusBadRequest, + "message": "Invalid request body", + }) + } + + if err := ctl.userService.UpdateStaff(user); err != nil { + return c.JSON(fiber.Map{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusAccepted, + "message": "User update successful", + }) +} + +func (ctl *UserController) AppLogin(c *fiber.Ctx) error { + var user models.User + + if err := c.BodyParser(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": 400, + "status": false, + "message": "Invalid request body", + }) + } + + _, resp, err := ctl.userService.AppLogin(user) + if err != nil { + // Use resp.Code if present, fallback to 409 + code := http.StatusConflict + if v, ok := resp["code"].(int); ok { + code = v + } + return c.Status(code).JSON(resp) + } + + // ✅ Always return resp + return c.Status(http.StatusOK).JSON(resp) +} + +func (ctl *UserController) CreateUser(c *fiber.Ctx) error { + var user models.User + + // Parse request body + if err := c.BodyParser(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "status": false, + "message": "Invalid request body", + }) + } + + // Call service + info, err := ctl.userService.CreateUser(user) + if err != nil { + return c.Status(http.StatusConflict).JSON(fiber.Map{ + "code": http.StatusConflict, + "status": false, + "message": "Failed", + }) + } + + return c.Status(http.StatusCreated).JSON(fiber.Map{ + "code": http.StatusCreated, + "status": true, + "message": "Success", + "details": info, + }) +} + +func (ctl *UserController) TenantWebLogin(c *fiber.Ctx) error { + var user models.User + if err := c.BodyParser(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "status": false, + "code": fiber.StatusBadRequest, + "message": "Invalid request body", + }) + } + + info, resp := ctl.userService.TenantWebLogin(user) + + // Ensure the response map contains the correct status code + code, ok := resp["code"].(int) + if !ok { + code = fiber.StatusInternalServerError + } + + // Include tenant user info if login successful (code 200) + if code == fiber.StatusOK { + resp["details"] = info + } + + return c.Status(code).JSON(resp) +} diff --git a/controllers/utilsController.go b/controllers/utilsController.go new file mode 100644 index 0000000..56d615f --- /dev/null +++ b/controllers/utilsController.go @@ -0,0 +1,314 @@ +package controllers + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "nearle/models" + "nearle/services" + "net/http" + "strconv" + + "github.com/gofiber/fiber/v2" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" +) + +type UtilsController struct { + utilsService services.UtilsService +} + +func NewUtilsController(utilsService services.UtilsService) *UtilsController { + return &UtilsController{utilsService: utilsService} +} + +const ( + fcmURL = "https://fcm.googleapis.com/v1/projects/nearle-gear/messages:send" + scope = "https://www.googleapis.com/auth/firebase.messaging" + serviceAcc = "nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json" // Path to your service account JSON +) + +var client *http.Client + +func (ctl *UtilsController) GetAppTypes(c *fiber.Ctx) error { + + tag := c.Query("tag") + + result, err := ctl.utilsService.GetApptypes(tag) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) + +} + +func (ctl *UtilsController) NotifyUser(c *fiber.Ctx) error { + var body struct { + Token string `json:"token"` + Notification models.FcmNotification `json:"notification"` + Data map[string]string `json:"data"` + } + + if err := c.BodyParser(&body); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": fmt.Sprintf("Error parsing request body: %v", err), + "status": false, + }) + } + + err := ctl.utilsService.SendNotification(body.Token, body.Notification, body.Data) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": fmt.Sprintf("Error sending notification: %v", err), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "FCM message sent successfully!", + "status": true, + }) +} + +func (ctl *UtilsController) GetSubcategories(c *fiber.Ctx) error { + mid, _ := strconv.Atoi(c.Query("moduleid")) + cid, _ := strconv.Atoi(c.Query("categoryid")) + + result, err := ctl.utilsService.GetSubcategories(mid, cid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *UtilsController) GetApplocations(c *fiber.Ctx) error { + aid, err := strconv.Atoi(c.Query("applocationid")) + if err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid applocationid", + "status": false, + }) + } + + result, err := ctl.utilsService.GetApplocations(aid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) +} + +func (ctl *UtilsController) GetApplocationConfig(c *fiber.Ctx) error { + aid, err := strconv.Atoi(c.Query("applocationid")) + if err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid applocationid", + "status": false, + }) + } + + result, err := ctl.utilsService.GetApplocationConfig(aid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": http.StatusInternalServerError, + "message": err.Error(), + "status": false, + }) + } + + return c.JSON(fiber.Map{ + "status": true, + "code": http.StatusOK, + "message": "Successful", + "details": result, + }) +} + +type Notification struct { + Title string `json:"title"` + Body string `json:"body"` +} + +type FCMRequestBody struct { + Message struct { + Notification Notification `json:"notification"` + Token string `json:"token"` + } `json:"message"` +} + +func GetClientFromServiceAccount() (*http.Client, error) { + data, err := ioutil.ReadFile(serviceAcc) + if err != nil { + return nil, fmt.Errorf("failed to read service account file: %v", err) + } + + config, err := google.JWTConfigFromJSON(data, scope) + if err != nil { + return nil, fmt.Errorf("failed to parse service account JSON: %v", err) + } + + + client := config.Client(oauth2.NoContext) + return client, nil +} + +func sendFCMMessage(token, title, body string) error { + if client == nil { + var err error + client, err = GetClientFromServiceAccount() + if err != nil { + return fmt.Errorf("failed to get client: %v", err) + } + } + + notification := Notification{ + Title: title, + Body: body, + } + + fcmRequest := FCMRequestBody{} + fcmRequest.Message.Notification = notification + fcmRequest.Message.Token = token + + requestBody, err := json.Marshal(fcmRequest) + if err != nil { + return err + } + + req, err := http.NewRequest("POST", fcmURL, bytes.NewBuffer(requestBody)) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + bodyBytes, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("failed to send message: %s", string(bodyBytes)) + } + + return nil +} + +func (ctl *UtilsController) NotifyAdmin(c *fiber.Ctx) error { + body := new(struct { + Token []string `json:"token"` + Notification struct { + Title string `json:"title"` + Body string `json:"body"` + Sound string `json:"sound"` + Type string `json:"type"` + } `json:"notification"` + }) + + if err := c.BodyParser(body); err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") + } + + if len(body.Token) == 0 { + return fiber.NewError(fiber.StatusBadRequest, "No tokens provided") + } + + + var successCount, failureCount int + for _, token := range body.Token { + err := sendFCMMessage(token, body.Notification.Title, body.Notification.Body) + + if err != nil { + log.Printf("❌ Failed to send to %s: %v", token, err) + failureCount++ + } else { + log.Printf("✅ Notification sent to %s", token) + successCount++ + } + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "message": "Admin notifications sent", + "success": successCount, + "failure": failureCount, + }) +} + +func (ctl *UtilsController) GetAppConfig(c *fiber.Ctx) error { + cid, _ := strconv.Atoi(c.Query("configid")) + + result, err := ctl.utilsService.GetAppConfig(cid) + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch app config", + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + +func (ctl *UtilsController) GetAppCategory(c *fiber.Ctx) error { + result, err := ctl.utilsService.GetAppCategory() + if err != nil { + return c.Status(http.StatusInternalServerError).JSON(fiber.Map{ + "code": 500, + "message": "Failed to fetch app categories", + "status": false, + }) + } + + return c.Status(http.StatusOK).JSON(fiber.Map{ + "code": http.StatusOK, + "message": "Success", + "status": true, + "details": result, + }) +} + + + diff --git a/db/connect.go b/db/connect.go new file mode 100644 index 0000000..846a2c8 --- /dev/null +++ b/db/connect.go @@ -0,0 +1,85 @@ +package db + +import ( + "fmt" + "log" + "os" + "time" + + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +var ( + DB *gorm.DB +) + +// -------------------- +// DATABASE CONNECTION +// -------------------- + +func Connect() { + dsn := fmt.Sprintf( + "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Kolkata", + mustEnv("DB_HOST"), + mustEnv("DB_USER"), + mustEnv("DB_PASSWORD"), + mustEnv("DB_NAME"), + getEnv("DB_PORT", "5433"), + ) + + var err error + DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatal("❌ Could not connect to database:", err) + } + + setupDB(DB) + fmt.Println("✅ Database connected") +} + +func setupDB(database *gorm.DB) { + sqlDB, err := database.DB() + if err != nil { + log.Fatal("❌ Failed to get DB from GORM:", err) + } + sqlDB.SetMaxIdleConns(100) + sqlDB.SetMaxOpenConns(1000) + sqlDB.SetConnMaxLifetime(time.Minute * 5) +} + +// -------------------- +// DATABASE SHUTDOWN +// -------------------- + +func CloseDB() { + if DB == nil { + return + } + sqlDB, err := DB.DB() + if err != nil { + log.Println("Error retrieving sql.DB:", err) + return + } + fmt.Println("Connection closed Successfully") + sqlDB.Close() +} + +// -------------------- +// ENV HELPERS +// -------------------- + +func mustEnv(key string) string { + val := os.Getenv(key) + if val == "" { + log.Fatalf("Missing required env variable: %s", key) + } + return val +} + +func getEnv(key, fallback string) string { + if val := os.Getenv(key); val != "" { + return val + } + return fallback +} diff --git a/facade/container.go b/facade/container.go new file mode 100644 index 0000000..1c73ecc --- /dev/null +++ b/facade/container.go @@ -0,0 +1,74 @@ +package facade + +import ( + "nearle/controllers" + "nearle/repositories" + "nearle/services" + + "gorm.io/gorm" +) + +type Facade struct { + UserController *controllers.UserController + ProductController *controllers.ProductController + OrderController *controllers.OrderController + DeliveriesController *controllers.DeliveriesController + UtilsController *controllers.UtilsController + TenantController *controllers.TenantController + PartnerController *controllers.PartnerController + CustomerController *controllers.CustomerController +} + +func NewFacade(db *gorm.DB) *Facade { + + // User Module + userRepo := repositories.NewUserRepository(db) + userService := services.NewUserService(userRepo) + userController := controllers.NewUserController(userService) + + // Product Module + productRepo := repositories.NewProductRepository(db) + productService := services.NewProductService(productRepo) + productController := controllers.NewProductController(productService) + + // Order Module + orderRepo := repositories.NewOrderRepository(db) + orderService := services.NewOrderService(orderRepo) + orderController := controllers.NewOrderController(orderService) + + // Deliveries Module + deliveriesRepo := repositories.NewDeliveriesRepository(db) + deliveriesService := services.NewDeliveriesService(deliveriesRepo) + deliveriesController := controllers.NewDeliveriesController(deliveriesService) + + // Utils Module + utilsRepo := repositories.NewUtilsRepository(db) + utilsService := services.NewUtilsService(utilsRepo) + utilsController := controllers.NewUtilsController(utilsService) + + //Tenant Module + tenantRepo := repositories.NewTenantRepository(db) + tenantService := services.NewTenantService(tenantRepo) + tenantController := controllers.NewTenantController(tenantService) + + //Partner Module + partnerRepo := repositories.NewPartnerRepository(db) + partnerService := services.NewPartnerService(partnerRepo) + partnerController := controllers.NewPartnerController(partnerService) + + //Customer Module + customerRepo := repositories.NewCustomerRepository(db) + customerService := services.NewCustomerService(customerRepo) + customerController := controllers.NewCustomerController(customerService) + + return &Facade{ + UserController: userController, + ProductController: productController, + OrderController: orderController, + DeliveriesController: deliveriesController, + UtilsController: utilsController, + TenantController: tenantController, + PartnerController: partnerController, + CustomerController: customerController, + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1012eac --- /dev/null +++ b/go.mod @@ -0,0 +1,83 @@ +module nearle + +go 1.21 + +require gorm.io/gorm v1.25.10 + +require ( + cloud.google.com/go v0.110.7 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/firestore v1.13.0 // indirect + cloud.google.com/go/iam v1.1.1 // indirect + cloud.google.com/go/longrunning v0.5.1 // indirect + cloud.google.com/go/storage v1.30.1 // indirect + firebase.google.com/go v3.13.0+incompatible // indirect + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/gofiber/fiber v1.14.6 // indirect + github.com/gofiber/utils v0.0.10 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/gorilla/schema v1.1.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.50.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/api v0.143.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/grpc v1.58.2 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gorm.io/driver/postgres v1.6.0 // indirect +) + +require ( + github.com/gofiber/fiber/v2 v2.50.0 + github.com/jinzhu/copier v0.4.0 + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/spf13/viper v1.17.0 + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/mysql v1.5.2 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a41aaf4 --- /dev/null +++ b/go.sum @@ -0,0 +1,635 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o= +cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.13.0 h1:/3S4RssUV4GO/kvgJZB+tayjhOfyAHs+KcpJgRVu/Qk= +cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= +firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o= +github.com/gofiber/fiber v1.14.6/go.mod h1:Yw2ekF1YDPreO9V6TMYjynu94xRxZBdaa8X5HhHsjCM= +github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= +github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= +github.com/gofiber/utils v0.0.10 h1:3Mr7X7JdCUo7CWf/i5sajSaDmArEDtti8bM1JUVso2U= +github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.143.0 h1:o8cekTkqhywkbZT6p1UHJPZ9+9uuCAJs/KYomxZB8fA= +google.golang.org/api v0.143.0/go.mod h1:FoX9DO9hT7DLNn97OuoZAGSDuNAXdJRuGK98rSUgurk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= +google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= +google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= +gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= +gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..0bf7695 --- /dev/null +++ b/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "fmt" + "log" + "nearle/db" + "nearle/facade" + "nearle/routes" + "os" + "os/signal" + "strings" + "syscall" + "time" + _ "time/tzdata" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/joho/godotenv" + "gorm.io/gorm" +) + +func init() { + godotenv.Load() +} + +func main() { + + app := fiber.New() + + app.Use(cors.New(cors.Config{ + AllowHeaders: "Origin,Content-Type,Accept,Content-Length,Accept-Language,Accept-Encoding,Connection,Access-Control-Allow-Origin", + AllowOrigins: "*", + AllowCredentials: true, + AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS", + })) + + fmt.Println("🌐 Connecting to databases...") + db.Connect() + fmt.Println("✅ Database connections established!") + + f := facade.NewFacade(db.DB) + + routes.RegisterRoutes(app, f) + + // Start server + go func() { + if err := app.Listen(":1122"); err != nil { + log.Fatal("Server failed to start:", err) + } + }() + + gracefulShutdown() +} + +func selectDBMiddleware(c *fiber.Ctx) error { + path := c.Path() + result := strings.Split(path, "/") + + var flavour string + if len(result) > 1 { + flavour = result[1] + } + + var currentDB *gorm.DB + if flavour == "dev" { + currentDB = db.DB + } else if flavour == "live" { + currentDB = db.DB + } + + if currentDB != nil { + c.Locals("DB", currentDB) + } + + return c.Next() +} + +func gracefulShutdown() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + + <-c + fmt.Println("\nShutting down gracefully...") + + // Normally: close db.DB_DEV and db.DB_LIVE + // Example: + // closeDB(db.DB_DEV) + // closeDB(db.DB_LIVE) + + time.Sleep(2 * time.Second) + fmt.Println("Shutdown complete.") + + os.Exit(0) +} diff --git a/models/common.go b/models/common.go new file mode 100644 index 0000000..475777d --- /dev/null +++ b/models/common.go @@ -0,0 +1,7 @@ +package models + +type Result struct { + Code int `json:"code"` + Status bool `json:"status"` + Message string `json:"message"` +} diff --git a/models/customer.go b/models/customer.go new file mode 100644 index 0000000..f9a631b --- /dev/null +++ b/models/customer.go @@ -0,0 +1,123 @@ +package models + +import "time" + +type Customers struct { + Customerid int `json:"customerid" gorm:"Primary_Key"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Profileimage string `json:"profileimage"` + Gender string `json:"gender"` + Dob string `json:"dob"` + Dialcode string `json:"dialcode"` + Contactno string `json:"contactno"` + Email string `json:"email"` + Deviceid string `json:"deviceid"` + Devicetype string `json:"devicetype"` + Authmode int `json:"authmode"` + Configid int `json:"configid"` + Customertoken string `json:"customertoken"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Landmark string `json:"landmark"` + Doorno string `json:"doorno"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Applocationid int `json:"applocationid"` + Locationid int `json:"locationid,omitempty" gorm:"-"` + Defaultaddress string `json:"defaultaddress,omitempty" gorm:"-"` + Primaryaddress int `json:"primaryaddress,omitempty" gorm:"-"` + Tenantid int `json:"tenantid,omitempty" gorm:"-"` + Status int `json:"status"` + Intro string `json:"intro"` + Qrmode int `json:"qrmode,omitempty" gorm:"-"` +} + +type CustomerInfo struct { + Customerid int `json:"customerid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Profileimage string `json:"profileimage"` + Gender string `json:"gender"` + Dob string `json:"dob"` + Dialcode string `json:"dialcode"` + Contactno string `json:"contactno"` + Email string `json:"email"` + Deviceid string `json:"deviceid"` + Devicetype string `json:"devicetype"` + Authmode int `json:"authmode"` + Configid int `json:"configid"` + Customertoken string `json:"customertoken"` + Deliverylocationid int `json:"deliverylocationid"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Landmark string `json:"landmark"` + Doorno string `json:"doorno"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Applocationid int `json:"applocationid"` + Allocationid int `json:"allocationid"` + Primaryaddress int `json:"primaryaddress"` + Tenantlocationid int `json:"tenantlocationid"` + Tenantid int `json:"tenantid"` + Status int `json:"status"` + Intro string `json:"intro"` + Selectedlatitude string `json:"selectedlatitude"` + Selectedlongitude string `json:"selectedlongitude"` + Radius string `json:"radius"` + Qrmode int `json:"qrmode"` +} + +type CustomerLocationResult struct { + Code int `json:"code"` + Status bool `json:"status"` + Message string `json:"message"` + Details []Customerlocations `json:"details"` +} + +type Customerlocations struct { + Locationid int `json:"locationid" gorm:"Primary_Key"` + Customerid int `json:"customerid"` + Applocationid int `json:"applocationid" gorm:"-"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Landmark string `json:"landmark"` + Doorno string `json:"doorno"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Primaryaddress int `json:"primaryaddress"` + Status int `json:"status"` +} + +type CustomerRequest struct { + Customerrequestid int `json:"customerrequestid" gorm:"primaryKey;autoIncrement"` + Referencedate time.Time `json:"referencedate"` + Referencetype string `json:"referencetype"` + Customerid int `json:"customerid"` + Tenantid int `json:"tenantid"` + Apptypeid int `json:"apptypeid"` + Locationid int `json:"locationid"` + Subject string `json:"subject"` + Remarks string `json:"remarks"` + Status int `json:"status"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` +} + +type Tenantcustomers struct { + Tenantcustomerid int `json:"tenantcustomerid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid" ` + Locationid int `json:"locationid"` + Customerid int `json:"customerid"` + Moduleid int `json:"moduleid"` + Status int `json:"status"` +} diff --git a/models/deliveries.go b/models/deliveries.go new file mode 100644 index 0000000..0c23e40 --- /dev/null +++ b/models/deliveries.go @@ -0,0 +1,243 @@ +package models + +type Deliveries struct { + Deliveryid int `json:"deliveryid" gorm:"Primary_Key"` + Orderheaderid int `json:"orderheaderid"` + Applocationid int `json:"applocationid"` + Configid int `json:"configid"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Moduleid int `json:"moduleid"` + Locationid int `json:"locationid"` + Categoryid int `json:"categoryid"` + Userid int `json:"userid"` + Subcategoryid int `json:"subcategoryid"` + Orderid string `json:"orderid"` + Deliverydate string `json:"deliverydate"` + Orderstatus string `json:"orderstatus"` + Assigntime string `json:"assigntime"` + Starttime string `json:"starttime"` + Arrivaltime string `json:"arrivaltime"` + Pickuptime string `json:"pickuptime"` + Deliverytime string `json:"deliverytime"` + Canceltime string `json:"canceltime"` + Itemcount int `json:"itemcount"` + Orderamount float32 `json:"orderamount"` + Customerid int `json:"customerid"` + Pickupcustomer string `json:"pickupcustomer"` + Pickupcontactno string `json:"pickupcontactno"` + Pickuplocationid int `json:"pickuplocationid"` + Pickupaddress string `json:"Pickupaddress"` + Pickuplocation string `json:"pickuplocation"` + Pickuplat string `json:"pickuplat"` + Pickuplon string `json:"pickuplon"` + Deliverycustomerid int `json:"deliverycustomerid"` + Deliverylocationid int `json:"deliverylocationid"` + Deliverycustomer string `json:"deliverycustomer"` + Deliverycontactno string `json:"deliverycontactno"` + Deliveryaddress string `json:"deliveryaddress"` + Deliverylocation string `json:"deliverylocation"` + Droplat string `json:"droplat"` + Droplon string `json:"droplon"` + Deliverylat string `json:"deliverylat"` + Deliverylong string `json:"deliverylong"` + Deliverycharges float32 `json:"deliverycharges"` + Deliveryamt float32 `json:"deliveryamt"` + Deliverytype string `json:"deliverytype"` + Notes string `json:"notes"` + Ordernotes string `json:"ordernotes"` + Riderslat string `json:"riderslat"` + Riderslon string `json:"riderslon"` + Firstmilekm float32 `json:"firstmilekm"` + Firstmilecharges float32 `json:"firstmilecharges"` + Lastmilecharges float32 `json:"lastmilecharges"` + Ridercharges float32 `json:"ridercharges"` + Kms string `json:"kms"` + Actualkms string `json:"actualkms"` + Smsdelivery int `json:"smsdelivery"` + Paymenttype int `json:"paymenttype"` +} + +type Deliveryqueues struct { + Queueid int `json:"queueid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Orderheaderid int `json:"orderheaderid"` + Deliveryid int `json:"deliveryid"` + Userid int `json:"userid"` + Partnerid int `json:"partnerid"` + Orderid string `json:"orderid"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Accept int `json:"accept"` + Decline int `json:"decline"` + Queuestatus int `json:"queuestatus"` +} + +type Updateorderstatus struct { + Orderheaderid int `json:"Orderheaderid"` + Orderstatus string `json:"Orderstatus"` + Pending string `json:"pending"` + Processing string `json:"processing"` + Cancelled string `json:"cancelled"` + Delivered string `json:"delivered"` +} + +type UpdateDeliveryStatus struct { + Deliveryid int `json:"deliveryid"` + Deliverytype string `json:"deliverytype"` + Pickuplocationid int `json:"pickuplocationid"` + Deliverylocationid int `json:"deliverylocationid"` + Orderheaderid int `json:"orderheaderid"` + Userid int `json:"userid"` + Orderstatus string `json:"orderstatus"` + Assigntime string `json:"assigntime"` + Starttime string `json:"starttime"` + Arrivaltime string `json:"arrivaltime"` + Pickuptime string `json:"pickuptime"` + Deliverytime string `json:"deliverytime"` + Canceltime string `json:"canceltime"` + Pickuplat string `json:"pickuplat"` + Pickuplon string `json:"picklon"` + Riderslat string `json:"riderslat"` + Riderslon string `json:"riderslon"` + Deliverylat string `json:"deliverylat"` + Deliverylong string `json:"deliverylong"` + Address string `json:"address" gorm:"<-:false"` + Suburb string `json:"suburb" gorm:"<-:false"` + City string `json:"city" gorm:"<-:false"` + State string `json:"state" gorm:"<-:false"` + Postcode string `json:"postcode" gorm:"<-:false"` + Deliveryamt float32 `json:"deliveryamt"` + Kms string `json:"kms"` + Actualkms string `json:"actualkms"` + Riderkms string `json:"riderkms"` + Kmcal string `json:"kmcal"` + Notes string `json:"notes"` + Feedback string `json:"feedback"` + Smsdelivery int `json:"smsdelivery"` +} + +type DeliverySummary struct { + Total int `json:"total"` + Created int `json:"created"` + Pending int `json:"pending"` + Accepted int `json:"accepted"` + Arrived int `json:"arrived"` + Picked int `json:"picked"` + Active int `json:"active"` + Delivered int `json:"delivered"` + Cancelled int `json:"cancelled"` +} + +type ReportSummary struct { + Tenantid int `json:"tenantid"` + Tenantname string `json:"tenantname"` + Totalorders int `json:"totalorders"` + Orderscreated int `json:"orderscreated"` + Orderspending int `json:"Orderspending"` + Orderscompleted int `json:"orderscompleted"` + Orderscancelled int `json:"orderscancelled"` + Deliveriespending int `json:"deliveriespending"` + Deliveriescompleted int `json:"deliveriescompleted"` + Deliveriescancelled int `json:"deliveriescancelled"` + Paylater float32 `json:"paylater"` + Payondelivery float32 `json:"payondelivery"` + Kms float32 `json:"kms"` + Actualkms float32 `json:"actualkms"` + Charges float32 `json:"charges"` +} + +type Ridersummary struct { + Userid int `json:"userid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Deliveries int `json:"deliveries"` + Assigned int `json:"Assigened"` + Accepted int `json:"Accepted"` + Picked int `json:"Picked"` + Delivered int `json:"delivered"` + Kms float32 `json:"kms"` + Actualkms float32 `json:"actualkms"` + Payondelivery float32 `json:"payondelivery"` + Paylater float32 `json:"Paylater"` + Deliveryamt float32 `json:"Deliveryamt"` + Status string `json:"status"` +} + +type Deliveryinfo struct { + Deliveryid int `json:"deliveryid"` + Orderheaderid int `json:"orderheaderid"` + Applocationid int `json:"applocationid"` + Applocation string `json:"applocation"` + Configid int `json:"configid"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Moduleid int `json:"moduleid"` + Locationid int `json:"locationid"` + Categoryid int `json:"categoryid"` + Userid int `json:"userid"` + Subcategoryid int `json:"subcategoryid"` + Orderid string `json:"orderid"` + Deliverydate string `json:"deliverydate"` + Orderstatus string `json:"orderstatus"` + Assigntime string `json:"assigntime"` + Starttime string `json:"starttime"` + Arrivaltime string `json:"arrivaltime"` + Pickuptime string `json:"pickuptime"` + Deliverytime string `json:"deliverytime"` + Canceltime string `json:"canceltime"` + Itemcount int `json:"itemcount"` + Orderamount float32 `json:"orderamount"` + Customerid int `json:"customerid"` + Pickupcustomer string `json:"pickupcustomer"` + Pickupcontactno string `json:"pickupcontactno"` + Pickuplocationid int `json:"pickuplocationid"` + Pickupaddress string `json:"Pickupaddress"` + Pickuplocation string `json:"pickuplocation"` + Pickuplat string `json:"pickuplat"` + Pickuplon string `json:"pickuplon"` + Deliverycustomerid int `json:"deliverycustomerid"` + Deliverylocationid int `json:"deliverylocationid"` + Deliverycustomer string `json:"deliverycustomer"` + Deliverycontactno string `json:"deliverycontactno"` + Deliveryaddress string `json:"deliveryaddress"` + Deliverylocation string `json:"deliverylocation"` + Droplat string `json:"droplat"` + Droplon string `json:"droplon"` + Deliverylat string `json:"deliverylat"` + Deliverylong string `json:"deliverylong"` + Deliverycharges float32 `json:"deliverycharges"` + Deliveryamt float32 `json:"deliveryamt"` + Deliverytype string `json:"deliverytype"` + Notes string `json:"notes"` + Ordernotes string `json:"ordernotes"` + Riderslat string `json:"riderslat"` + Riderslon string `json:"riderslon"` + Firstmilekm float32 `json:"firstmilekm"` + Firstmilecharges float32 `json:"firstmilecharges"` + Lastmilecharges float32 `json:"lastmilecharges"` + Ridercharges float32 `json:"ridercharges"` + Kms string `json:"kms"` + Actualkms string `json:"actualkms"` + Riderkms string `json:"riderkms"` + Paymenttype int `json:"paymenttype"` + Tenantname string `json:"tenantname"` + Tenantcontactno string `json:"tenantcontactno"` + Tenanttoken string `json:"tenanttoken"` + Tenantsuburb string `json:"tenantsuburb"` + Tenantcity string `json:"tenantcity"` + Locationname string `json:"locationname"` + Locationcontactno string `json:"locationcontactno"` + Locationsuburb string `json:"locationsuburb"` + Ridername string `json:"ridername"` + Userfcmtoken string `json:"userfcmtoken"` + Queueid int `json:"queueid"` + Smsdelivery int `json:"smsdelivery"` + Customertoken string `json:"customertoken"` + Ridercontact string `json:"ridercontact"` + Expecteddeliverytime string `json:"expecteddeliverytime"` + Profit int `json:"profit"` + Transitminutes int `json:"transitminutes"` + Calculationdistancekm int `json:"calculationdistancekm"` +} diff --git a/models/order.go b/models/order.go new file mode 100644 index 0000000..7f54691 --- /dev/null +++ b/models/order.go @@ -0,0 +1,440 @@ +package models + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "strconv" + + "gorm.io/gorm" +) + +// FlexibleString handles JSON that could be either a number or a string, +// and ensures it can be saved/read from the database as a string. +type FlexibleString string + +func (fs *FlexibleString) UnmarshalJSON(b []byte) error { + if len(b) == 0 { + return nil + } + // If it's a string in JSON + if b[0] == '"' { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + *fs = FlexibleString(s) + return nil + } + // If it's a number in JSON + var f float64 + if err := json.Unmarshal(b, &f); err != nil { + return err + } + *fs = FlexibleString(strconv.FormatFloat(f, 'f', -1, 64)) + return nil +} + +func (fs FlexibleString) MarshalJSON() ([]byte, error) { + return json.Marshal(string(fs)) +} + +// 🔥 Database Logic: Handle reading from DB +func (fs *FlexibleString) Scan(value interface{}) error { + if value == nil { + *fs = "" + return nil + } + switch v := value.(type) { + case []byte: + *fs = FlexibleString(v) + case string: + *fs = FlexibleString(v) + case float64: + *fs = FlexibleString(strconv.FormatFloat(v, 'f', -1, 64)) + case int64: + *fs = FlexibleString(strconv.FormatInt(v, 10)) + default: + *fs = FlexibleString(fmt.Sprintf("%v", v)) + } + return nil +} + +// 🔥 Database Logic: Handle writing to DB +func (fs FlexibleString) Value() (driver.Value, error) { + return string(fs), nil +} + +func (fs FlexibleString) String() string { + return string(fs) +} + +type OrderInfo struct { + Orderheaderid int `json:"orderheaderid"` + Applocationid int `json:"applocationid"` + Applocation string `json:"applocation"` + Tenantid int `json:"tenantid"` + Partnerid int `json:"partnerid"` + Locationid int `json:"locationid"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Moduleid int `json:"moduleid"` + Configid int `json:"configid"` + Orderid string `json:"orderid"` + Orderdate string `json:"orderdate"` + Deliverydate string `json:"deliverydate"` + Orderstatus string `json:"orderstatus"` + Deliverystatus string `json:"deliverystatus"` + Deliveryamt float64 `json:"deliveryamt"` + Itemcount int `json:"itemcount"` + Ordernotes string `json:"ordernotes"` + Kms FlexibleString `json:"kms"` + Actualkms FlexibleString `json:"actualkms"` + Pending string `json:"Pending"` + Processing string `json:"processing"` + Ready string `json:"ready"` + Cancelled string `json:"cancelled"` + Delivered string `json:"delivered"` + Assigntime string `json:"assigntime"` + Starttime string `json:"starttime"` + Arrivaltime string `json:"arrivaltime"` + Pickuptime string `json:"pickuptime"` + Deliverytime string `json:"deliverytime"` + Canceltime string `json:"canceltime"` + Deliverycharge float32 `json:"deliverycharge"` + Orderamount float32 `json:"orderamount"` + Customerid int `json:"customerid"` + Pickupcustomer string `json:"pickupcustomer"` + Pickupcontactno string `json:"pickupcontactno"` + Pickuplocationid int `json:"pickuplocationid"` + Pickupaddress string `json:"pickupaddress"` + Pickupsuburb string `json:"pickupsuburb"` + Pickupcity string `json:"pickupcity"` + Pickuplat FlexibleString `json:"pickuplat"` + Pickuplong FlexibleString `json:"pickuplong"` + Deliveryid int `json:"deliveryid"` + Deliverycustomerid int `json:"deliverycustomerid"` + Deliverycustomer string `json:"deliverycustomer"` + Deliverycontactno string `json:"deliverycontactno"` + Deliverylocationid int `json:"deliverylocationid"` + Deliveryaddress string `json:"deliveryaddress"` + Deliverysuburb string `json:"deliverysuburb"` + Droplat FlexibleString `json:"droplat"` + Droplon FlexibleString `json:"droplon"` + Deliverylat FlexibleString `json:"deliverylat"` + Deliverylong FlexibleString `json:"deliverylong"` + Deliverytype string `json:"deliverytype"` + Paymenttype int `json:"paymenttype"` + Tenantname string `json:"tenantname"` + Tenanttoken string `json:"tenanttoken"` + Tenantsuburb string `json:"tenantsuburb"` + Tenantcity string `json:"tenantcity"` + Tenantcontactno string `json:"tenantcontactno"` + Tenantpostcode string `json:"tenantpostcode"` + Locationname string `json:"locationname"` + Locationsuburb string `json:"locationsuburb"` + Locationcity string `json:"locationcity"` + Locationcontactno string `json:"locationcontactno"` + Rider string `json:"rider"` + Ridercontactno string `json:"ridercontactno"` + Riderkms FlexibleString `json:"riderkms"` + Smsdelivery int `json:"smsdelivery"` + Customertoken string `json:"customertoken"` + Ridertoken string `json:"ridertoken"` +} + +type DeliveryQuery struct { + Tenantid int + Customerid int + Partnerid int + UserID int + Appuserid int + Applocationid int + Moduleid int + Locationid int + Configid int + Fromdate string + ToDate string + Status string + Pageno int + Pagesize int + Conn *gorm.DB + Keyword string `json:"keyword"` +} + +type Ordersummary struct { + Total int `json:"total"` + Created int `json:"created"` + Pending int `json:"pending"` + Processing int `json:"processing"` + Delivered int `json:"delivered"` + Cancelled int `json:"cancelled"` + // Locationid int `json:"locationid"` + Locationname string `json:"locationname"` + Applocationid string `json:"applocationid"` +} + +type Ordersummarylocation struct { + Total int `json:"total"` + Created int `json:"created"` + Pending int `json:"pending"` + Processing int `json:"processing"` + Delivered int `json:"delivered"` + Cancelled int `json:"cancelled"` + Locationid int `json:"locationid"` + Locationname string `json:"locationname"` +} + +type Ordersummarydaily struct { + Total int `json:"total"` + Created int `json:"created"` + Pending int `json:"pending"` + Processing int `json:"processing"` + Delivered int `json:"delivered"` + Cancelled int `json:"cancelled"` + Tenantid int `json:"tenantid"` + Tenantname string `json:"tenantname"` + Locationid int `json:"locationid"` + Locationname string `json:"locationname"` +} + +type OrderInsight struct { + Applocationid int `json:"applocationid" gorm:"Primary_Key"` + Locationname string `json:"locationname"` + Ordermonths *Ordermonths `json:"ordermonths" gorm:"-"` +} + +type OrderInsightv1 struct { + Locationid int `json:"locationid" gorm:"Primary_Key"` + Locationname string `json:"locationname"` + Ordermonths *Ordermonths `json:"ordermonths" gorm:"-"` +} + +type Ordermonths struct { + Jan int `json:"jan"` + Feb int `json:"feb"` + Mar int `json:"mar"` + Apr int `json:"apr"` + May int `json:"may"` + Jun int `json:"jun"` + Jul int `json:"jul"` + Aug int `Json:"aug"` + Sep int `Json:"sep"` + Oct int `Json:"oct"` + Nov int `Json:"nov"` + Dece int `Json:"dece"` + // Applocationid int `json:"applocationid"` +} + +type Orders struct { + Orderheaderid int `json:"orderheaderid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Applocationid int `json:"applocationid"` + Moduleid int `json:"moduleid"` + Partnerid int `json:"partnerid"` + Configid int `json:"configid"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Orderid string `json:"orderid"` + Orderdate string `json:"orderdate,omitempty"` + Deliverytime string `json:"deliverytime"` + Deliverytype string `json:"deliverytype"` + Orderstatus string `json:"orderstatus"` + Pending string `json:"pending"` + Processing string `json:"processing"` + Ready string `json:"ready"` + Delivered string `json:"delivered"` + Cancelled string `json:"cancelled"` + Customerid int `json:"customerid"` + Pickupaddress string `json:"pickupaddress"` + Pickuplat FlexibleString `json:"pickuplat"` + Pickuplong FlexibleString `json:"pickuplong"` + Pickupcustomer string `json:"pickupcustomer"` + Pickupcontactno string `json:"pickupcontactno"` + Pickuplocation string `json:"pickupsuburb"` // alias + Pickupcity string `json:"pickupcity"` + Deliveryid int `json:"deliverycustomerid"` // alias + Deliverycustomer string `json:"deliverycustomer"` + Deliverycontactno string `json:"deliverycontactno"` + Deliveryaddress string `json:"deliveryaddress"` + Deliverylocation string `json:"deliverylocation"` + Deliverycity string `json:"deliverycity"` + Deliverylocationid int `json:"deliverylocationid"` + Deliverylat FlexibleString `json:"deliverylat"` + Deliverylong FlexibleString `json:"deliverylong"` + Promotionid int `json:"promotionid"` + Promoname string `json:"promoname"` + Promoterms string `json:"promoterms"` + Promovalue int `json:"promovalue"` + Promoamount float32 `json:"promoamount"` + Orderamount float32 `json:"orderamount"` + Taxamount float32 `json:"taxamount"` + Ordercharges float32 `json:"ordercharges"` + Ordervalue float32 `json:"ordervalue"` + Itemcount int `json:"itemcount"` + Paymenttype int `json:"paymenttype"` + Paymentstatus int `json:"paymentstatus"` + Deliverycharge float32 `json:"deliverycharge"` + Ordernotes string `json:"ordernotes"` + Kms FlexibleString `json:"kms"` + Remarks string `json:"remarks"` + Tenantuserid int `json:"tenantuserid"` + Partneruserid int `json:"partneruserid"` + Smsdelivery int `json:"smsdelivery" gorm:"default:0"` + Items []OrderDetail `json:"items" gorm:"-"` +} + +type OrderDetails struct { + Orderdetailid int `json:"orderdetailid" gorm:"primaryKey;autoIncrement"` + Orderheaderid int `json:"orderheaderid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Productid int `json:"productid"` + Productname string `json:"productname"` + Productdescription string `json:"productdescription"` + Supplyqty float64 `json:"supplyqty"` + Balanceqty float64 `json:"balanceqty"` + Orderqty float64 `json:"orderqty"` + Price float64 `json:"price"` + Unitid int `json:"unitid"` + Unitname string `json:"unitname"` + Productaddonid int `json:"productaddonid"` + Addontypeid int `json:"addontypeid"` + Productmapid int `json:"productmapid"` + Productvariantid int `json:"productvariantid"` + Productaddondescription string `json:"productaddondescription"` + Discountid int `json:"discountid"` + Discountname string `json:"discountname"` + Discountcode string `json:"discountcode"` + Discountterms string `json:"discountterms"` + Discountpercentage float64 `json:"discountpercentage"` + Discountamount float64 `json:"discountamount"` + Landingamount float64 `json:"landingamount"` + Taxpercentage float64 `json:"taxpercentage"` + Taxamount float64 `json:"taxamount"` + Totaltaxamount float64 `json:"totaltaxamount"` + Productsumprice float64 `json:"productsumprice"` + Itemstatus string `json:"itemstatus"` + Delivered string `json:"delivered"` + Orderamount float64 `json:"Orderamount"` + Productimage string `json:"productimage"` +} + +type OrderDetail struct { + Orderdetailid int `json:"orderdetailid" gorm:"primaryKey;autoIncrement"` + Orderheaderid int `json:"orderheaderid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Productid int `json:"productid"` + Productname string `json:"productname"` + Productdescription string `json:"productdescription"` + Supplyqty float64 `json:"supplyqty"` + Balanceqty float64 `json:"balanceqty"` + Orderqty float64 `json:"orderqty"` + Price float64 `json:"price"` + Unitid int `json:"unitid"` + Unitname string `json:"unitname"` + Productaddonid int `json:"productaddonid"` + Addontypeid int `json:"addontypeid"` + Productmapid int `json:"productmapid"` + Productvariantid int `json:"productvariantid"` + Productaddondescription string `json:"productaddondescription"` + Discountid int `json:"discountid"` + Discountname string `json:"discountname"` + Discountcode string `json:"discountcode"` + Discountterms string `json:"discountterms"` + Discountpercentage float64 `json:"discountpercentage"` + Discountamount float64 `json:"discountamount"` + Landingamount float64 `json:"landingamount"` + Taxpercentage float64 `json:"taxpercentage"` + Taxamount float64 `json:"taxamount"` + Productsumprice float64 `json:"productsumprice"` + Itemstatus string `json:"itemstatus"` + Delivered string `json:"delivered"` + Orderamount float64 `json:"-" gorm:"-"` + Productimage string `json:"productimage" gorm:"-"` // only for JSON +} + +type CustomerOrder struct { + Orderheaderid int `json:"orderheaderid" gorm:"Primary_Key"` + Applocationid int `json:"applocationid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Partnerid int `json:"partnerid"` + Configid int `json:"configid"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Moduleid int `json:"moduleid"` + Orderid string `json:"orderid"` + Orderstatus string `json:"orderstatus"` + Orderdate string `json:"orderdate,omitempty"` + Ordernotes string `json:"ordernotes"` + Itemcount int `json:"itemcount"` + Deliverytime string `json:"deliverytime"` // alias for delivered + Pending string `json:"pending"` + Processing string `json:"processing"` + Ready string `json:"ready"` + Delivered string `json:"delivered"` // original delivered column + Cancelled string `json:"cancelled"` + Deliverycharge float32 `json:"deliverycharge"` + Kms FlexibleString `json:"kms"` + + Smsdelivery int `json:"smsdelivery"` + + // Tenant Info + Tenantname string `json:"tenantname"` + Tenanttoken string `json:"tenanttoken"` + Tenantcontactno string `json:"tenantcontactno"` + Tenantpostcode string `json:"tenantpostcode"` + Tenantsuburb string `json:"tenantsuburb"` + Tenantcity string `json:"tenantcity"` + Registrationno string `json:"registrationno"` + + // Location Info + Locationname string `json:"locationname"` + Locationcontactno string `json:"locationcontactno"` + Locationpostcode string `json:"locationpostcode"` + Locationsuburb string `json:"locationsuburb"` + Locationcity string `json:"locationcity"` + + // App Location + Applocation string `json:"applocation"` + + // Delivery Info (from `deliveries` table) + Deliverystatus string `json:"deliverystatus"` + DeliveryUserID int `json:"deliveryid"` // alias + Assigntime string `json:"assigntime"` + Starttime string `json:"starttime"` + Arrivaltime string `json:"arrivaltime"` + Pickuptime string `json:"pickuptime"` + Finaldeliverytime string `json:"finaldeliverytime"` // alias for f.deliverytime + Canceltime string `json:"canceltime"` + Riderkms FlexibleString `json:"riderkms"` + // Actualkms sql.NullFloat64 `json:"actualkms"` + // Actualkms sql.NullFloat64 `json:"actualkms"` + // Riderkms sql.NullFloat64 `json:"riderkms"` + // Deliveryamt sql.NullFloat64 `json:"deliveryamt"` + Droplat FlexibleString `json:"droplat"` + Droplon FlexibleString `json:"droplon"` + + // Rider + Rider string `json:"rider"` + Orderamount float64 `json:"orderamount"` + Taxamount float64 `json:"taxamount"` + Totaltaxamount float64 `json:"totaltaxamount"` + Riderkms_v2 FlexibleString `json:"-" gorm:"column:riderkms"` // Extra safety + + // Items if applicable + OrderDetails []OrderDetails `json:"orderdetails" gorm:"-"` +} + +type Ordersequences struct { + Sequenceid int `json:"sequenceid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Orderprefix string `json:"orderprefix" gorm:"default:ORD"` + Customerprefix string `json:"customerprefix" gorm:"default:CUS"` + Appointmentprefix string `json:"appointmentprefix" gorm:"default:ORD"` + Receiptprefix string `json:"receiptprefix" gorm:"default:REC"` + Paymentprefix string `json:"paymentprefix" gorm:"default:PAY"` +} diff --git a/models/partner.go b/models/partner.go new file mode 100644 index 0000000..b3c5646 --- /dev/null +++ b/models/partner.go @@ -0,0 +1,106 @@ +package models + +type RiderInfo struct { + Userid int `json:"userid"` + Authname string `json:"authname"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Fullname string `json:"fullname"` + Password string `json:"password"` + Email string `json:"email"` + Contactno string `json:"contactno"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Userfcmtoken string `json:"userfcmtoken"` + Pin int `json:"pin"` + Partnerid int `json:"partnerid"` + Identificationno string `json:"identificationno"` + Vehiclename string `json:"vehiclename"` + Vehicleno string `json:"vehicleno"` + Licenseno string `json:"licenseno"` + Insuranceno string `json:"insoranceno"` + Insurancedate string `json:"insurancedate"` + Shiftid int `json:"shiftid"` + Starttime string `json:"starttime"` + Endtime string `json:"endtime"` + Shifthours float32 `json:"shifthours"` + Basefare float32 `json:"basefare"` + Additionalcharges float32 `json:"additionalcharges"` + Orders int `json:"orders"` + Fuelcharge float32 `json:"fuelcharge"` + Logdate string `json:"logdate"` + Applocationid int `json:"applocationid"` + Applocation string `json:"applocation"` + Logseconds int `json:"logseconds"` + Riderid int `json:"riderid"` + Status string `json:"status"` + Tenantid int `json:"tenantid"` +} + +type Partnerinfo struct { + Partnerid int `json:"partnerid" gorm:"Primary_Key"` + Partnertypeid int `json:"partnertypeid"` + Applocationid int `json:"applocationid"` + Partnername string `json:"partnername"` + Registrationno string `json:"registrationno"` + Primarycontact string `json:"primartcontact"` + Primaryemail string `json:"primaryemail"` + Contactno string `json:"contactno"` + Bizcategoryid int `json:"bizcategoryid"` + Bizsubcategoryid int `json:"bizsubcategoryid"` + Adderss string `json:"address"` + Suburb string `json:"suburb"` + State string `json:"state"` + City string `json:"city"` + Postcode string `json:"postcode"` + Partnerinfo string `json:"partnerinfo"` + Partnerimage string `json:"partnerimage"` +} + +type Ridershifts struct { + Shiftid int `json:"shiftid" gorm:"Primary_Key"` + Shiftdate string `json:"shiftdate"` + Starttime string `json:"starttime"` + Endtime string `json:"endtime"` + Shifthours float32 `json:"shifthours"` + Basefare float32 `json:"basefare"` + Additionalkm float32 `json:"additionalkm"` + Additionalcharges float32 `json:"additionalcharges"` + Orders int `json:"orders"` + Fuelcharge float32 `json:"fuelcharge"` + Shiftname string `json:"shiftname" gorm:"<-:false"` +} + +type Locationconfigs struct { + Applocationid int `json:"applocationid"` + Configid int `json:"configid"` + Locationname string `json:"locationname"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Status string `json:"status"` +} + + +type RiderlogDetails struct { + Logid int `json:"logid"` + Logdate string `json:"logdate"` + Userid int `json:"userid"` + Username string `json:"username"` + Partnerid int `json:"partnerid"` + Shiftid int `json:"shiftid"` + Shifthours float32 `json:"shifthours"` + Login string `json:"login"` + Logout string `json:"logout"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Workhours float32 `json:"workhours"` + Shorthours float32 `json:"shorthours"` + Breakhours float32 `json:"breakhours"` + Logstatus int `json:"logstatus"` +} \ No newline at end of file diff --git a/models/product.go b/models/product.go new file mode 100644 index 0000000..15d5d16 --- /dev/null +++ b/models/product.go @@ -0,0 +1,284 @@ +package models + +import "time" + +type ProductSubCategory struct { + Subcatid int `json:"subcatid"` + Categoryid int `json:"categoryid,omitempty"` + Tenantid int `json:"tenantid,omitempty"` + Subcatname string `json:"subcatname,omitempty"` + Image string `json:"image,omitempty"` + Status string `json:"status,omitempty"` + Sortorder int `json:"sortorder,omitempty"` + Createdby int `json:"createdby,omitempty"` + Created time.Time `json:"created,omitempty"` + Updated time.Time `json:"updated,omitempty"` +} + +type Productcount struct { + Total int `json:"total"` + Available int `json:"available"` + Outofstock int `json:"outofstock"` +} + +type ProductCategory struct { + Categoryid int `json:"categoryid"` + Moduleid int `json:"moduleid"` + Tenantid int `json:"tenantid,omitempty"` + Categorytypeid int `json:"categorytypeid,omitempty"` + Categoryname string `json:"categoryname,omitempty"` + Image string `json:"image,omitempty"` + Catalougecategoryid int `json:"catalougecategoryid,omitempty"` + Sortorder int `json:"sortorder,omitempty"` + Status string `json:"status"` + Createdby int `json:"createdby,omitempty"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` +} + +type Productvariant struct { + Variantid int `json:"variantid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Variantname string `json:"variantname"` + Categoryid int `json:"categoryid" gorm:"default:0"` + Categoryname string `json:"categoryname" gorm:"-"` + Subcategoryid int `json:"subcategoryid"` + Status string `json:"status" gorm:"default:active"` +} + +type Products struct { + Productid int `json:"productid" gorm:"primaryKey;autoIncrement"` + AppLocationid int `json:"applocationid" gorm:"column:applocationid"` + Productlocationid int `json:"productlocationid" gorm:"->"` + Tenantid int `json:"tenantid,omitempty"` + Categoryid int `json:"categoryid"` + Categoryname string `json:"categoryname" gorm:"->"` + Subcategoryid int `json:"subcategoryid,omitempty"` + Subcategoryname string `json:"Subcategoryname" gorm:"->"` + Catalogueid int `json:"catalogueid,omitempty"` + Addonid int `json:"addonid,omitempty"` + Discountid int `json:"discountid"` + Discountvalue float64 `json:"discountvalue"` + Pricingid int `json:"pricingid,omitempty"` + Productname string `json:"productname,omitempty"` + Productimage string `json:"productimage,omitempty"` + Productdesc string `json:"productdesc,omitempty"` + Productsku string `json:"productsku,omitempty"` + Brandid int `json:"brandid,omitempty"` + Productbrand string `json:"productbrand,omitempty"` + Productunit string `json:"productunit,omitempty"` + Unitvalue string `json:"unitvalue,omitempty"` + Toppicks string `json:"toppicks,omitempty"` + Productcost float64 `json:"productcost,omitempty"` + Taxamount float64 `json:"taxamount,omitempty"` + Taxpercent float64 `json:"taxpercent,omitempty"` + Producttax int `json:"producttax" gorm:"default:0"` + Productstock int `json:"productstock" gorm:"default:0"` + Productcombo int `json:"productcombo" gorm:"default:0"` + Variants int `json:"variants" gorm:"default:0"` + Quantity int `json:"quantity"` + Retailprice float64 `json:"retailprice,omitempty"` + Diffprice float64 `json:"diffprice,omitempty"` + Diffpercent float64 `json:"diffpercent,omitempty"` + Othercost float64 `json:"othercost,omitempty"` + Approve int `json:"approve"` + Productstatus string `json:"productstatus" ` + // Status string `json:"status" gorm:"default:InActive"` + // Status string `json:"status" gorm:"-"` +} + +type Locationproducts struct { + Productid int `json:"productid"` + AppLocationid int `json:"applocationid" gorm:"column:applocationid"` + Productlocationid int `json:"productlocationid" gorm:"->"` + Tenantid int `json:"tenantid,omitempty"` + Categoryid int `json:"categoryid"` + Categoryname string `json:"categoryname" gorm:"->"` + Subcategoryid int `json:"subcategoryid,omitempty"` + Subcategoryname string `json:"Subcategoryname" gorm:"->"` + Catalogueid int `json:"catalogueid,omitempty"` + Addonid int `json:"addonid,omitempty"` + Discountid int `json:"discountid,omitempty"` + Pricingid int `json:"pricingid,omitempty"` + Productname string `json:"productname,omitempty"` + Productimage string `json:"productimage,omitempty"` + Productdesc string `json:"productdesc,omitempty"` + Productsku string `json:"productsku,omitempty"` + Brandid int `json:"brandid,omitempty"` + Productbrand string `json:"productbrand,omitempty"` + Productunit string `json:"productunit,omitempty"` + Unitvalue string `json:"unitvalue,omitempty"` + Toppicks string `json:"toppicks,omitempty"` + Productcost float64 `json:"productcost,omitempty"` + Taxamount float64 `json:"taxamount,omitempty"` + Taxpercent float64 `json:"taxpercent,omitempty"` + Producttax int `json:"producttax" gorm:"default:0"` + Productstock int `json:"productstock" gorm:"default:0"` + Productcombo int `json:"productcombo" gorm:"default:0"` + Variants int `json:"variants" gorm:"default:0"` + Quantity int `json:"quantity"` + Retailprice float64 `json:"retailprice,omitempty"` + Diffprice float64 `json:"diffprice,omitempty"` + Diffpercent float64 `json:"diffpercent,omitempty"` + Othercost float64 `json:"othercost,omitempty"` + Approve int `json:"approve" gorm:"default:0"` + // Productstatus string `json:"productstatus" gorm:"default:available"` + Status string `json:"status" gorm:"default:outofstock"` +} + +type Productstocks struct { + Productstockid int `json:"productstockid" gorm:"Primary_Key"` + Locationid int `json:"locationid"` + Tenantid int `json:"tenantid"` + Stockdate string `json:"stockdate"` + Productid int `json:"productid"` + Quantity int `json:"quantity"` + Stocktype string `json:"stocktype"` + Status string `json:"status"` + AppLocationid int `json:"applocationid"` + Categoryid int `json:"categoryid"` + Categoryname string `json:"categoryname" gorm:"->"` + Subcategoryid int `json:"subcategoryid,omitempty"` + Subcategoryname string `json:"Subcategoryname" ` + Productname string `json:"productname,omitempty"` + Productimage string `json:"productimage,omitempty"` + Productdesc *string `json:"productdesc,omitempty"` + Productsku *string `json:"productsku,omitempty"` + Brandid *int `json:"brandid,omitempty"` + Productbrand *string `json:"productbrand,omitempty"` + Productunit *string `json:"productunit,omitempty"` + Unitvalue *string `json:"unitvalue,omitempty"` + Toppicks *string `json:"toppicks,omitempty"` + Productcost *float64 `json:"productcost,omitempty"` + Taxamount *float64 `json:"taxamount,omitempty"` + Taxpercent *float64 `json:"taxpercent,omitempty"` + Producttax *int `json:"producttax,omitempty"` + Productstock *int `json:"productstock,omitempty"` + Productcombo *int `json:"productcombo,omitempty"` + Variants int `json:"variants"` + Retailprice float64 `json:"retailprice,omitempty"` + Diffprice float64 `json:"diffprice,omitempty"` + Diffpercent float64 `json:"diffpercent,omitempty"` + Othercost float64 `json:"othercost,omitempty"` + Approve *int `json:"approve"` +} + +type Productstock struct { + Productstockid int `json:"productstockid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Stockdate time.Time `json:"stockdate"` + Locationid int `json:"locationid"` + Productid int `json:"productid"` + Quantity int `json:"quantity"` + Stocktype string `json:"stocktype"` + Status string `json:"status"` +} + +type Productstockstatement struct { + Productid int `json:"productid"` + Productname string `json:"productname"` + Productimage string `json:"productimage"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Productunit string `json:"productunit"` + Unitvalue string `json:"unitvalue"` + Productcost float32 `json:"productcost"` + Taxpercent float32 `json:"taxpercent"` + Taxamount float32 `json:"taxamount"` + Retailprice float32 `json:"retailprice"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Opening int `json:"opening"` + Credit int `json:"credit"` + Debit int `json:"debit"` + Closing int `json:"closing"` +} + +type ProductSummary struct { + Subcategoryid int `json:"subcategoryid"` + Subcategroyname string `json:"subcategroyname"` + Image string `json:"image"` + Productcount int `json:"productcount"` +} + +type Tenantproducts struct { + Tenant TenantInfo `json:"tenant"` + Products []Products `json:"products"` + // Locationproducts []Locationproducts `json:"locationproducts"` +} + +type TenantInfo struct { + Tenantid int `json:"tenantid"` + Tenantname string `json:"tenantname"` + Userfcmtoken string `json:"userfcmtoken"` + Address string `json:"address"` + Licenseno string `json:"licenseno"` + Primaryemail string `json:"primaryemail"` + Primarycontact string `json:"primarycontact"` + Pickuplocationid int `json:"pickuplocationid"` + Applocationid int `json:"applocationid"` + Suburb string `json:"suburb"` + City string `json:"city"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Postcode string `json:"postcode"` + Tenantimage string `json:"tenantimage"` + Locationid int `json:"locationid"` + Locationname string `json:"locationname"` + Subcategoryid int `json:"subcategoryid"` + Categoryid int `json:"categoryid"` + Registrationno string `json:"registrationno"` + Orderscount int `json:"orderscount"` + // Products []Products `json:"products" gorm:"-"` + ProductSubcategory []ProductSubcategory `json:"productsubcategory" gorm:"-"` +} + +type SubcategoryProductResponse struct { + SubcategoryID int `json:"subcategoryid"` + SubcategoryName string `json:"subcategoryname"` + Image string `json:"image"` + Products []Products `json:"products"` +} + +type ProductFilter struct { + CategoryID int + TenantID int + AppLocationID int + ProductID int + Keyword string + LocationID int +} + +type Subcategory struct { + Subcategoryid int `json:"subcategoryid" gorm:"column:subcatid"` + Subcategoryname string `json:"subcategoryname" gorm:"column:subcatname"` + Categoryid int `json:"categoryid" gorm:"column:categoryid"` + Image string `json:"image" gorm:"column:image"` +} + +type Productlocations struct { + Productlocationid int `json:"productlocationid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Productid int `json:"productid"` + Catlougeid int `json:"catlougeid"` + Minquantity int `json:"minquantity" gorm:"default:0"` + Maxquantity int `json:"maxquantity" gorm:"default:0"` + Price float32 `json:"price" gorm:"default:0.0"` + Quantity int `json:"quantity" gorm:"<-:false"` + Stocktype string `json:"stocktype" gorm:"<-:false"` + Status string `json:"status"` +} + +type ProductSubcategory struct { + Subcatid int `json:"subcatid"` + Categoryid int `json:"categoryid"` + Tenantid int `json:"tenantid"` + Subcatname string `json:"subcatname"` + Status string `json:"status"` + Sortorder int `json:"sortorder"` + Createdby int `json:"createdby"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + Image string `json:"image"` +} diff --git a/models/tenant.go b/models/tenant.go new file mode 100644 index 0000000..40ce920 --- /dev/null +++ b/models/tenant.go @@ -0,0 +1,218 @@ +package models + +import "time" + +type Tenantinfo struct { + Tenantid int `json:"tenantid" gorm:"Primary_Key"` + Locationid int `json:"locationid"` + Tenantname string `json:"tenantname"` + Locationname string `json:"locationname"` + Tenanttype string `json:"tenanttype"` + Registrationno string `json:"registrationno"` + Tenanttoken string `json:"tenanttoken"` + Companyname string `json:"companyname"` + Primaryemail string `json:"primaryemail"` + Primarycontact string `json:"primarycontact"` + Locationcatact string `json:"locationcontact"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Tenantimage string `json:"tenantimage"` + Tenantinfo string `json:"tenantinfo"` + Paymenttype int `json:"paymenttype"` + Paymode1 int `json:"paymode1"` + Paymode2 int `json:"paymode2"` + Promotion int `json:"promotion"` + Partnerid int `json:"partnerid"` + Minorder int `json:"minorder"` + Applocationid int `json:"applolcationid"` + Applocation string `json:"applocation"` + Approved int `json:"approved"` + Moduleid int `json:"moduleid"` + Subcategoryname string `json:"subcategoryname"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Accountname string `json:"Accountname"` + Status string `json:"status"` + Allocationid int `json:"allocationid"` + Allocationtype string `json:"allocationtype"` + Allocationmode int `json:"allocationmode"` +} + +type Tenantlocations struct { + Locationid int `json:"locationid" gorm:"Primary_Key"` + Tenantid int `json:"tenantid"` + Applocationid int `json:"applocationid"` + Moduleid int `json:"moduleid"` + Roleid int `json:"roleid" gorm:"-"` + Locationname string `json:"locationname"` + Email string `json:"email"` + Contactno string `json:"contactno"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Opentime string `json:"opentime"` + Closetime string `json:"closetime"` + Partnerid int `json:"partnerid"` + Deliveryradius int `json:"deliveryradius"` + Deliverymins int `json:"deliverymins"` + Cancelsecs int `json:"cancelsecs"` + Status string `json:"status" gorm:"default:Active"` +} + +type Tenantslot struct { + Slot1 string `json:"slot1"` + Slot2 string `json:"slot2"` + Slot3 string `json:"slot3"` +} + +type CreateTenantCustomerRequest struct { + ModuleID int `json:"moduleid"` + TenantID int `json:"tenantid"` + LocationID int `json:"locationid"` + CustomerID int `json:"customerid"` + CustomerLocationID int `json:"customerlocationid"` + Status int `json:"status"` +} + +type Tenantcustomer struct { + TenantCustomerID int `json:"tenantcustomerid" gorm:"primaryKey;column:tenantcustomerid"` + ModuleID int `json:"moduleid" gorm:"column:moduleid"` + TenantID int `json:"tenantid" gorm:"column:tenantid"` + LocationID int `json:"locationid" gorm:"column:locationid"` + CustomerID int `json:"customerid" gorm:"column:customerid"` + CustomerLocationID int `json:"customerlocationid" gorm:"column:customerlocationid"` + Status int `json:"status" gorm:"column:status"` + Created time.Time `json:"created" gorm:"column:created;autoCreateTime"` + Updated time.Time `json:"updated" gorm:"column:updated;autoUpdateTime"` +} + +type CustomerTenantResponse struct { + // Customer Customers `json:"customer"` + Details []TenantInfo `json:"details"` +} + +type Tenantpricing struct { + Tenantpricingid int `json:"tenantpricingid" gorm:"Primary_Key"` + Pricingid int `json:"pricingid"` + Tenantid int `json:"tenantid"` + Applocationid int `json:"applocationid"` + Pricingtypeid int `json:"pricingtypeid"` + Slab string `json:"slab"` + Configid int `json:"configid"` + Pricingdate string `json:"pricingdate"` + Baseprice float32 `json:"baseprice"` + Priceperkm float32 `json:"priceperkm"` + Minkm int `json:"minkm"` + Maxkm int `json:"maxkm"` + Orders int `json:"orders"` + Othercharges float32 `json:"othercharges"` +} + +type StaffInfo struct { + Userid int `json:"userid"` + Authname string `json:"authname"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Fullname string `json:"fullname"` + Password string `json:"password"` + Email string `json:"email"` + Contactno string `json:"contactno"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Userfcmtoken string `json:"userfcmtoken"` + Pin int `json:"pin"` + Applocationid int `json:"applocationid"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Locationname string `json:"locationname"` +} + +type Tenantuser struct { + Userid int `json:"userid" gorm:"Primary_Key"` + Authname string `json:"authname"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Password string `json:"password"` + Email string `json:"email"` + Dialcode string `json:"dialcode"` + Contactno string `json:"contactno"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Pin int `json:"pin"` + Deviceid string `json:"deviceid"` + Devicetype string `json:"devicetype"` + Userfcmtoken string `json:"userfcmtoken"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Applocationid int `json:"applocationid"` + Status string `json:"status"` +} + +type Tenants struct { + Tenantid int `json:"tenantid" gorm:"Primary_Key"` + Tenantname string `json:"tenantname"` + Configid int `json:"configid"` + Partnerid int `json:"partnerid"` + Moduleid int `json:"moduleid"` + Tenanttype string `json:"tenanttype"` + Registrationno string `json:"registrationno"` + Tenanttoken string `json:"tenanttoken"` + Companyname string `json:"companyname"` + Devicetype string `json:"devicetype"` + Deviceid string `json:"deviceid"` + Firstname string `json:"firstname"` + Primaryemail string `json:"primaryemail"` + Primarycontact string `json:"primarycontact"` + Categoryid int `json:"categoryid"` + Subcategoryid int `json:"subcategoryid"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Tenantimage string `json:"tenantimage"` + Tenantinfo string `json:"tenantinfo"` + Paymode1 int `json:"paymode1"` + Paymode2 int `json:"paymode2"` + Promotion int `json:"promotion"` + Minorder int `json:"minorder"` + Applocationid int `json:"applocationid"` + Approved *int `json:"approved" gorm:"default:0"` + Status string `json:"status" gorm:"default:Active"` + Partneruserid int `json:"partneruserid"` + Tenantlocations Tenantlocations `json:"tenantlocations" gorm:"ForeignKey:tenantid"` + // Tenantsubscriptions Tenantsubscriptions `json:"tenantsubscriptions" gorm:"ForeignKey:tenantid"` +} + +type TenantSearch struct { + Tenantname string `json:"tenantname"` + Productname string `json:"productname"` + Subcatname string `json:"subcatname"` +} diff --git a/models/users.go b/models/users.go new file mode 100644 index 0000000..0bcb32c --- /dev/null +++ b/models/users.go @@ -0,0 +1,102 @@ +package models + +type UserInfo struct { + Userid int `json:"userid"` + Authname string `json:"authname"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Fullname string `json:"fullname"` + Password string `json:"password"` + Email string `json:"email"` + Contactno string `json:"contactno"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Userfcmtoken string `json:"userfcmtoken"` + Shiftid int `json:"shiftid"` + Shiftname string `json:"shiftname"` + Pin int `json:"pin"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Applocationid int `json:"applocationid"` + Applocation string `json:"applocation"` + Applatitude string `json:"applatitude"` + Applongitude string `json:"applongitude"` + Appradius int `json:"appradius"` + Status string `json:"status"` +} + +type User struct { + Userid int `json:"userid" gorm:"Primary_Key"` + Authname string `json:"authname"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Password string `json:"password"` + Email string `json:"email"` + Dialcode string `json:"dialcode"` + Contactno string `json:"contactno"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Pin int `json:"pin"` + Deviceid string `json:"deviceid"` + Devicetype string `json:"devicetype"` + Userfcmtoken string `json:"userfcmtoken"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Partnerid int `json:"partnerid"` + Tenantid int `json:"tenantid"` + Locationid int `json:"locationid"` + Applocationid int `json:"applocationid"` + Status string `json:"status"` + Shiftid int `json:"shiftid"` +} + +type TenantUserInfo struct { + Userid int `json:"userid"` + Authname string `json:"authname"` + Configid int `json:"configid"` + Authmode int `json:"authmode"` + Roleid int `json:"roleid"` + Firstname string `json:"firstname"` + Lastname string `json:"lastname"` + Fullname string `json:"fullname"` + Password string `json:"password"` + Email string `json:"email"` + Contactno string `json:"contactno"` + Address string `json:"address"` + Suburb string `json:"suburb"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Userfcmtoken string `json:"userfcmtoken"` + Pin int `json:"pin"` + Partnerid int `json:"partnerid"` + Locationid int `json:"locationid"` + Applocationid int `json:"applocationid"` + Tenantid int `json:"tenantid"` + Tenantname string `json:"tenantname"` + Tenantaddress string `json:"tenantaddress"` + Tenantcity string `json:"tenantcity"` + Tenantpostcode string `json:"tenantpostcode"` + Tenantlat string `json:"tenantlat"` + Tenantlong string `json:"tenantlong"` + Locationname string `json:"locationname"` + Applocation string `json:"applocation"` + Applatitude string `json:"applatitude"` + Applongitude string `json:"applongitude"` + Appradius int `json:"appradius"` + Moduleid int `json:"moduleid"` + Categoryid int `json:"categoryid"` + Categoryname string `json:"categoryname"` + Subcategoryid int `json:"subcategoryid"` +} diff --git a/models/utils.go b/models/utils.go new file mode 100644 index 0000000..fbf6acc --- /dev/null +++ b/models/utils.go @@ -0,0 +1,84 @@ +package models + +// Utils model +type Utils struct { + AppVersion string `json:"app_version"` +} + +type Apptypes struct { + Apptypeid int `json:"apptypeid"` + Typename string `json:"typename"` + Mapid int `json:"mapid"` + Tag string `json:"tag"` + Status string `json:"status"` +} + +type FcmNotification struct { + Title string `json:"title"` + Body string `json:"body"` +} + +type Appsubcategories struct { + Subcategoryid int `json:"subcategoryid"` + Categoryid int `json:"categoryid"` + Subcategoryname string `json:"subcategoryname"` + Categoryname string `json:"catgeoryname"` + Moduleid int `json:"moduleid"` + Status string `json:"status"` +} + +type Applocations struct { + Applocationid int `json:"applocationid" gorm:"Primary_Key"` + Locationname string `json:"locationname"` + Image string `json:"image"` + City string `json:"city"` + State string `json:"state"` + Postcode string `json:"postcode"` + Latitude string `json:"latitude"` + Longitude string `json:"longitude"` + Opentime string `json:"opentime"` + Closetime string `json:"closetime"` + Radius int `json:"radius"` + Applocationadmins []Applocationadmins `json:"applocationadmins" gorm:"ForeignKey:applocationid"` +} + +type Applocationadmins struct { + Applocationconfigid int `json:"applocationconfigid" gorm:"Primary_Key"` + Applocationid int `json:"applocationid"` + Userid int `json:"userid"` + Userfcmtoken string `json:"userfcmtokem"` + Notify string `json:"notify"` +} + +type Appconfig struct { + Configid int `json:"configid"` + Appname string `json:"appname"` + Paymentdevkey string `json:"paymentdevkey"` + Paymentlivekey string `json:"paymentlivekey"` + Fcmkey string `json:"fcmkey"` + Googleapikey string `json:"googleapikey"` + Applocationradius int `json:"applocationradius"` + Smsproviderid int `json:"smsproviderid"` + Providerapi string `json:"providerapi"` + Providerkey string `json:"providerkey"` + Sender string `json:"sender"` + Templateid string `json:"templateid"` + Defaultprovider int `json:"defaultprovider"` + Created string `json:"created"` + Updated string `json:"updated"` +} + +type AppCategory struct { + Categoryid int `json:"categoryid" gorm:"Primary_Key"` + Categoryname string `json:"categoryname"` + Categorytype int `json:"categorytype"` + Sortorder int `json:"sortorder"` + Imageurl string `json:"imageurl"` + Iconurl string `json:"iconurl"` + Profileurl string `json:"profileurl"` + Crossaxis int `json:"crossaxis"` + Mainaxis int `json:"mainaxis"` + Status string `json:"status"` + Created string `json:"created"` + Updated string `json:"updated"` +} diff --git a/nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json b/nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json new file mode 100644 index 0000000..be1e9fe --- /dev/null +++ b/nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "nearle-gear", + "private_key_id": "23ca3b3609076b93b29a730f63e1da76d68e4baa", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDfsSE/Otsp0LTJ\nrTBUoOcdd0MF3rRZtcL4/WCekyEUh8tTh0YtSqaaDoO2lpg2Ym9jS031z1U0UOqg\n83L9IwyQD0UV0rYFkOm9LWNU60ZvL/KjE8YcRf0j8nj/Sv6S9rrrQmQKKDXINs2t\noXyBi7R4WTGxJmRzyAr9jTgS/aghGpvo57z7aln2veYXUVxw1kVDEou9dFEu2eHC\nPY4Bb6ACDFvxtGNAClwHy3pYHv+6QirF5O2z+JPjGuYJXSOqiT6SOhLHnv9P7W4y\nUY5/wFiRpvK6iYjRFEnpw3c950UH3y19njiPyZN+pS6tCqyE6w0tQPIaxX7c39dg\nmq9esOGbAgMBAAECggEAVzfIP9Ah/fbbVrtJWeX21x1WK6a+6S+emtioYIv7XPbp\nh2L6MNSniu/X2Ux0gtf0mGGXPx2dGi3mneTuU9bjohPiGvUydY8gI9vbnBO0PcwT\nLhSarRF49xgmp1vFUOYU0X/sY61z8uGzZlzNn/Ep57bXUjwm9KHt7xit4XG3qVfs\n84+hYWGl6vy15LfgmRb5MzTWexYMfQnaxk0we0mUzSIXmOCvI/OrkQwKenkgkR2F\n7r/0LZQBbVhZ/kB9avWBNnsj89nDNld9MtuKHqjcwFfHFgBzSz+3Qy2LolCVtN5Z\nMEjJ3/t1cocDAnKRBJeA9B3Hj2ud1BCxSQceTnb22QKBgQD3tOa/KWQbgmfoFh/w\nni2EJM0KDKvibxJfIC+p6z4/EiXENmJDQx32yqURCO75mwtBX/VYxGSLb1wLTfIn\nGVRu5KWs5oLV8FqyAQPYh5Urz6DVqg6CQRQadCz0emMMbjRk2XDE2j7f18VOB4DV\nZCxpx1Bv1wiU2ITNxczlteH6zwKBgQDnLmXTw1jfZ1DS69idby5L8PpcnP3GWV4t\nxAHH6QJhqUrMo5/eFKOL6aaSR4cIg47NdtP4yKo71TyaFl68xo4VWyoy2xJffdyd\niGf1kG2prNmHAtcU6W9QdgK4qgHpYVpqZufAn2XW/KMrsyhd4jwVR/UFNogswLI5\nXXy8Tx7vdQKBgGRHHL64n+kvEqd3BXkgX+bGCyeNV6w0MOjHm+Qa8rkPvLBBH+iA\n7ElzYf2Sc0QjCxxtH2LPJrD35PhClsxTScYW1Cc5ri+zvNOg65Cl2rLAvCijTnpW\npC/NZkGWpjBrENTe3fMjMx7lN9/N088PXZd488xC7htrx9+RutAnoJMrAoGAd8Px\nvONXB2Xe2WaVsfoHYhBVo+UxE7D4uXzx7z8nnLC3r4yVJdhLYhCJ2v5zVlXRhWAq\nMJjEmHrACpPMQMAcm7O/CNm1iwMJaBNiyDUqmtyRVQCDrLHCmUyJ3GE23FEzJixp\no8DwYZBAeEM4hmrN9bhxl2HI6mZp7o4gMO5MeIECgYAuqqv/hq9t57qnJhk2S5mi\nSmDMk+Zz/pVFYfpUlRL33k/t+8c/8+qTdSruRZzLzSAtdZY6XE85lidt2zwdCgqO\nEuviAb474iF8UOY0hf4mQkrIHmlojd2nZmUQbPjPTR+kiUvhJGvQuaJywbX54+wR\n+8tH+pbwRbkrENWHqJHxhg==\n-----END PRIVATE KEY-----\n", + "client_email": "firebase-adminsdk-l9oha@nearle-gear.iam.gserviceaccount.com", + "client_id": "118373484295110637143", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-l9oha%40nearle-gear.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +} diff --git a/repositories/customerRepository.go b/repositories/customerRepository.go new file mode 100644 index 0000000..9d4fc08 --- /dev/null +++ b/repositories/customerRepository.go @@ -0,0 +1,359 @@ +package repositories + +import ( + "fmt" + "nearle/models" + "strings" + + "gorm.io/gorm" +) + +type CustomerRepository interface { + GetCustomer(cid int, cno string) (*models.CustomerInfo, error) + UpdateCustomer(input models.Customers) error + GetCustomerLocations(cid int) ([]models.Customerlocations, error) + CreateCustomerLocation(input models.Customerlocations) (int, error) + CreateCustomerRequest(req *models.CustomerRequest) error + GetCustomerRequests(customerID int, pageNo, pageSize int) ([]models.CustomerRequest, int64, error) + GetTenantCustomers(tid, lid, pageno, pagesize int, keyword string) []models.CustomerInfo + SearchCustomer(keyword string, tid int) []models.CustomerInfo + CheckCustomer(contactno string) int + CheckTenantCustomer(cid, tid int) int + CreateTenantCustomer(input models.Customers) int + CreateCustomer(input models.Customers) int + GetCustomerByContactNo(contactNo string) (*models.Customers, error) +} + +type customerRepository struct { + db *gorm.DB +} + +func NewCustomerRepository(db *gorm.DB) CustomerRepository { + return &customerRepository{db: db} +} + +func (r *customerRepository) GetCustomer(cid int, cno string) (*models.CustomerInfo, error) { + var data models.CustomerInfo + + q := ` + SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email,a.profileimage,a.dialcode, + a.deviceid,a.devicetype,a.authmode,a.configid,a.customertoken,a.intro,a.gender,a.dob, + b.locationid as deliverylocationid,b.address,b.suburb,b.city,b.state,b.landmark,b.doorno,b.postcode, + b.latitude,b.longitude,a.applocationid, + c.locationid as tenantlocationid,c.tenantid, + a.status, + d.qrmode,d.latitude as selectedlatitude,d.longitude as selectedlongitude,d.radius,d.applocationid, + e.allocationid + FROM customers a + INNER JOIN customerlocations b ON a.customerid=b.customerid AND b.primaryaddress=1 + INNER JOIN tenantcustomers c ON a.customerid=c.customerid + LEFT JOIN app_location d ON a.applocationid=d.applocationid + LEFT JOIN tenants e ON e.tenantid=c.tenantid + WHERE %s LIMIT 1 + ` + + var err error + if cid != 0 { + err = r.db.Raw(fmt.Sprintf(q, "a.customerid = ?"), cid).Scan(&data).Error + } else { + err = r.db.Raw(fmt.Sprintf(q, "a.contactno = ?"), cno).Scan(&data).Error + } + + if err != nil { + return nil, err + } + return &data, nil +} + +func (r *customerRepository) UpdateCustomer(input models.Customers) error { + var custloc models.Customerlocations + tx := r.db.Begin() + + if err := tx.Where("customerid=?", input.Customerid).Updates(&input).Error; err != nil { + tx.Rollback() + return err + } + + custloc.Customerid = input.Customerid + custloc.Address = input.Address + custloc.Suburb = input.Suburb + custloc.City = input.City + custloc.State = input.State + custloc.Postcode = input.Postcode + custloc.Latitude = input.Latitude + custloc.Longitude = input.Longitude + custloc.Doorno = input.Doorno + custloc.Landmark = input.Landmark + + if err := tx.Table("customerlocations"). + Where("customerid=? and primaryaddress=?", input.Customerid, 1). + Updates(&custloc).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + fmt.Println("Commit failed:", err) + return err + } + + return nil +} + +func (r *customerRepository) GetCustomerLocations(cid int) ([]models.Customerlocations, error) { + var data []models.Customerlocations + + if cid == 0 { + return data, nil + } + + query := `SELECT a.locationid, a.customerid, b.applocationid, a.address, a.suburb, + a.city, a.state, a.landmark, a.doorno, a.postcode, a.latitude, + a.longitude, a.defaultaddress, a.primaryaddress, a.status + FROM customerlocations a + LEFT JOIN customers b ON a.customerid = b.customerid + WHERE a.customerid = ?` + + if err := r.db.Raw(query, cid).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *customerRepository) CreateCustomerLocation(input models.Customerlocations) (int, error) { + + // Duplicate address checking + var count int + err := r.db.Raw(` + SELECT COUNT(*) FROM customerlocations + WHERE customerid = ? + AND address = ? + AND suburb = ? + AND city = ? + AND state = ? + AND postcode = ? + AND landmark = ? + AND doorno = ? + `, input.Customerid, input.Address, input.Suburb, input.City, input.State, + input.Postcode, input.Landmark, input.Doorno). + Scan(&count).Error + + if err != nil { + return 0, err + } + + if count > 0 { + // Duplicate found + return -1, nil + } + + // Insert new address + if err := r.db.Create(&input).Error; err != nil { + return 0, err + } + + return input.Customerid, nil +} + +func (r *customerRepository) CreateCustomerRequest(req *models.CustomerRequest) error { + return r.db.Table("customerrequest").Create(req).Error +} + +func (r *customerRepository) GetCustomerRequests(customerID, pageNo, pageSize int) ([]models.CustomerRequest, int64, error) { + var requests []models.CustomerRequest + var total int64 + + query := r.db.Table("customerrequest") + + if customerID != 0 { + query = query.Where("customerid = ?", customerID) + } + + query.Count(&total) + + offset := (pageNo - 1) * pageSize + if err := query.Order("created desc").Offset(offset).Limit(pageSize).Find(&requests).Error; err != nil { + return nil, 0, err + } + + return requests, total, nil +} + +func (r *customerRepository) GetTenantCustomers(tid, lid, pageno, pagesize int, keyword string) []models.CustomerInfo { + var data []models.CustomerInfo + offset := (pageno - 1) * pagesize + + var q1 string + var args []interface{} + searchLike := "%" + keyword + "%" + + if lid != 0 { + q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + b.locationid as deliverylocationid,b.address,b.suburb,b.city,b.state,b.landmark,b.doorno,b.postcode, + b.latitude,b.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + FROM customers a + LEFT JOIN customerlocations b ON a.customerid=b.customerid + INNER JOIN tenantcustomers c ON a.customerid=c.customerid + WHERE c.locationid = ? AND c.tenantid = ?` + + args = append(args, lid, tid) + + if keyword != "" { + q1 += ` AND (a.firstname LIKE ? OR a.contactno LIKE ? OR b.suburb LIKE ?)` + args = append(args, searchLike, searchLike, searchLike) + } + + q1 += ` ORDER BY a.customerid DESC LIMIT ? OFFSET ?` + args = append(args, pagesize, offset) + + } else { + q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + a.address,a.suburb,a.city,a.state,a.landmark,a.doorno,a.postcode, + a.latitude,a.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + FROM customers a + INNER JOIN tenantcustomers c ON a.customerid=c.customerid + WHERE c.tenantid = ?` + + args = append(args, tid) + + if keyword != "" { + searchLike := "%" + strings.ToLower(keyword) + "%" + q1 += ` AND (LOWER(a.firstname) LIKE ? OR LOWER(a.contactno) LIKE ? OR LOWER(a.suburb) LIKE ?)` + args = append(args, searchLike, searchLike, searchLike) + } + + q1 += ` ORDER BY a.customerid DESC LIMIT ? OFFSET ?` + args = append(args, pagesize, offset) + } + + print(q1) + + r.db.Raw(q1, args...).Find(&data) + return data +} + +func (r *customerRepository) SearchCustomer(keyword string, tid int) []models.CustomerInfo { + var data []models.CustomerInfo + searchLike := strings.ToLower(keyword) + "%" + + var q1 string + var args []interface{} + + if tid != 0 { + q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + a.address,a.suburb,a.city,a.state,a.landmark,a.doorno,a.postcode, + a.latitude,a.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + FROM customers a + INNER JOIN tenantcustomers c ON a.customerid=c.customerid + WHERE c.tenantid = ? AND (LOWER(a.firstname) LIKE ? OR LOWER(a.contactno) LIKE ?)` + args = append(args, tid, searchLike, searchLike) + } else { + q1 = `SELECT a.customerid,a.firstname,a.lastname,a.contactno,a.email, + a.address,a.suburb,a.city,a.state,a.landmark,a.doorno,a.postcode, + a.latitude,a.longitude,a.applocationid,c.locationid as tenantlocationid,a.status + FROM customers a + INNER JOIN tenantcustomers c ON a.customerid=c.customerid + WHERE (LOWER(a.firstname) LIKE ? OR LOWER(a.contactno) LIKE ?)` + args = append(args, searchLike, searchLike) + } + + r.db.Raw(q1, args...).Find(&data) + return data +} + +func (r *customerRepository) CheckCustomer(contactno string) int { + var data models.Customers + q1 := `SELECT a.customerid FROM customers a WHERE a.contactno = ?` + r.db.Raw(q1, contactno).Find(&data) + return data.Customerid +} + +func (r *customerRepository) CheckTenantCustomer(cid, tid int) int { + var data models.Customers + q1 := `SELECT a.customerid FROM tenantcustomers a WHERE a.customerid = ? AND a.tenantid = ?` + r.db.Raw(q1, cid, tid).Find(&data) + return data.Customerid +} + +func (r *customerRepository) CreateTenantCustomer(input models.Customers) int { + var tcust models.Tenantcustomers + tcust.Customerid = input.Customerid + tcust.Tenantid = input.Tenantid + + tx := r.db.Begin() + if err := tx.Table("tenantcustomers").Create(&tcust).Error; err != nil { + fmt.Println(err) + tx.Rollback() + return 0 + } + tx.Commit() + return input.Customerid +} + +func (r *customerRepository) CreateCustomer(input models.Customers) int { + var custloc models.Customerlocations + var tcust models.Tenantcustomers + + tx := r.db.Begin() + if err := tx.Create(&input).Error; err != nil { + fmt.Println(err) + tx.Rollback() + return 0 + } + + custloc = models.Customerlocations{ + Customerid: input.Customerid, + Address: input.Address, + Suburb: input.Suburb, + City: input.City, + State: input.State, + Postcode: input.Postcode, + Latitude: input.Latitude, + Longitude: input.Longitude, + Doorno: input.Doorno, + Landmark: input.Landmark, + Primaryaddress: 1, + } + if err := tx.Table("customerlocations").Create(&custloc).Error; err != nil { + fmt.Println(err) + tx.Rollback() + return 0 + } + + tcust = models.Tenantcustomers{ + Customerid: input.Customerid, + Tenantid: input.Tenantid, + } + if err := tx.Table("tenantcustomers").Create(&tcust).Error; err != nil { + fmt.Println(err) + tx.Rollback() + return 0 + } + + tx.Commit() + return input.Customerid +} + +func (r *customerRepository) GetCustomerByContactNo(contactNo string) (*models.Customers, error) { + var customer models.Customers + + query := ` + SELECT a.customerid, a.authmode, a.configid, a.deviceid, a.devicetype, a.customertoken, + a.firstname, a.lastname, a.contactno, a.profileimage, a.address, a.suburb, + a.city, a.state, a.landmark, a.doorno, a.postcode, a.latitude, a.longitude, + a.applocationid, a.status, a.profileimage, a.dialcode, a.intro, + b.tenantid, b.locationid, + c.qrmode + FROM customers a + INNER JOIN tenantcustomers b ON a.customerid = b.customerid + LEFT JOIN app_location c ON a.applocationid = c.applocationid + WHERE a.configid = 2 AND a.contactno = ? + ` + + if err := r.db.Raw(query, contactNo).Scan(&customer).Error; err != nil { + return nil, err + } + + return &customer, nil +} diff --git a/repositories/deliveriesRepository.go b/repositories/deliveriesRepository.go new file mode 100644 index 0000000..8f79c5d --- /dev/null +++ b/repositories/deliveriesRepository.go @@ -0,0 +1,922 @@ +package repositories + +import ( + "fmt" + "log" + "nearle/models" + "strconv" + "strings" + + "github.com/jinzhu/copier" + "gorm.io/gorm" +) + +type DeliveriesRepository interface { + CreateDeliveries(data []models.Deliveries) error + UpdateDelivery(data models.UpdateDeliveryStatus) error + GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) + GetDeliveryInsight(tid int) ([]models.OrderInsightv1, error) + GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) + GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) + GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) + GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) + GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo +} + +type deliveriesRepository struct { + db *gorm.DB +} + +func NewDeliveriesRepository(db *gorm.DB) DeliveriesRepository { + return &deliveriesRepository{db: db} +} + +const ( + core = `SELECT COUNT(*) AS total, + SUM(CASE WHEN orderstatus = 'pending' THEN 1 ELSE 0 END) AS pending, + SUM(CASE WHEN orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted, + SUM(CASE WHEN orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived, + SUM(CASE WHEN orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked, + SUM(CASE WHEN orderstatus = 'active' THEN 1 ELSE 0 END) AS active, + SUM(CASE WHEN orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered, + SUM(CASE WHEN orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS cancelled` + + reports = `SELECT b.tenantid,b.tenantname, + COUNT(*) AS totalorders, + SUM(CASE WHEN a.orderstatus = 'created' THEN 1 ELSE 0 END) AS orderscreated, + SUM(CASE WHEN a.orderstatus = 'pending' THEN 1 ELSE 0 END) AS orderspending, + SUM(CASE WHEN a.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS orderscancelled, + SUM(CASE WHEN a.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS orderscompleted, + SUM(CASE WHEN c.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveriespending, + SUM(CASE WHEN c.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS deliveriescompleted, + SUM(CASE WHEN c.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS deliveriescancelled, + SUM(CASE WHEN c.paymenttype = 64 THEN c.deliveryamt ELSE 0 END) AS paylater, + SUM(CASE WHEN c.paymenttype = 43 THEN c.deliveryamt ELSE 0 END) AS payondelivery, + ROUND(SUM(c.kms), 2) AS kms, + ROUND(SUM(c.actualkms), 2) AS actualkms, + SUM(c.deliveryamt) AS charges + FROM + tenants b + right JOIN orders a ON b.tenantid = a.tenantid + right JOIN deliveries c ON a.orderheaderid = c.orderheaderid ` + + Ridersummary = `SELECT a.userid, a.firstname, a.lastname, + CONCAT(a.firstname, ' ', a.lastname) AS fullname,a.status, + SUM(CASE WHEN b.orderstatus = 'pending' THEN 1 ELSE 0 END) AS deliveries, + SUM(CASE WHEN b.orderstatus = 'assigned' THEN 1 ELSE 0 END) AS assigned, + SUM(CASE WHEN b.orderstatus = 'accepted' THEN 1 ELSE 0 END) AS accepted, + SUM(CASE WHEN b.orderstatus = 'arrived' THEN 1 ELSE 0 END) AS arrived, + SUM(CASE WHEN b.orderstatus = 'picked' THEN 1 ELSE 0 END) AS picked, + SUM(CASE WHEN b.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered, + SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.actualkms ELSE 0 END) AS actualkms, + SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.kms ELSE 0 END) AS kms, + SUM(CASE WHEN b.paymenttype = 64 THEN b.deliveryamt ELSE 0 END) AS paylater, + SUM(CASE WHEN b.paymenttype = 43 THEN b.deliveryamt ELSE 0 END) AS payondelivery, + SUM(CASE WHEN b.orderstatus = 'delivered' THEN b.deliveryamt ELSE 0 END) AS deliveryamt + FROM app_users a + LEFT JOIN deliveries b ON a.userid = b.userid` +) + +const ( + deliveries = `SELECT distinct a.deliveryid,a.applocationid,f.locationname AS applocation,a.orderheaderid,a.configid,a.tenantid,a.partnerid,a.locationid,a.userid,a.categoryid,a.subcategoryid,a.moduleid, + a.orderid,a.deliverydate,a.orderstatus,a.assigntime,a.starttime,a.arrivaltime,a.pickuptime,a.deliverytime,a.canceltime, + a.customerid,a.pickupcustomer,a.pickupcontactno,a.pickuplocationid,a.pickupaddress,a.pickuplocation,a.pickuplat,a.pickuplon, + a.deliverycustomerid,a.deliverycustomer,a.deliverycontactno,a.deliverylocationid,a.deliveryaddress,a.deliverylocation, + a.droplat,a.droplon,a.deliverylat,a.deliverylong, + a.riderslat,a.riderslon,a.deliveryamt,a.kms,a.actualkms,a.riderkms,a.deliverycharges,a.deliverytype,a.paymenttype,a.smsdelivery, + a.expecteddeliverytime,a.profit,a.transitminutes,a.calculationdistancekm, + a.notes,a.ordernotes,b.tenantname,b.primarycontact as tenantcontactno,b.tenanttoken,b.suburb as tenantsuburb,b.city as tenantcity, + c.firstname AS ridername,c.userfcmtoken,e.locationname,e.suburb AS locationsuburb,e.contactno AS locationcontactno + FROM deliveries a + INNER JOIN tenants b ON a.tenantid=b.tenantid + INNER JOIN app_users c ON a.userid=c.userid + INNER JOIN tenantlocations e ON a.locationid=e.locationid + INNER JOIN app_location f ON a.applocationid = f.applocationid + INNER JOIN app_locationconfig g ON f.applocationid = g.applocationid` +) + +func (r *deliveriesRepository) CreateDeliveries(data []models.Deliveries) error { + var que []models.Deliveryqueues + var ord models.Updateorderstatus + + tx := r.db.Begin() + + for i := range data { + + if err := tx.Create(&data[i]).Error; err != nil { + tx.Rollback() + return err + } + + if err := copier.Copy(&que, &data[i]); err != nil { + tx.Rollback() + return err + } + + if err := tx.Create(&que).Error; err != nil { + tx.Rollback() + return err + } + + ord.Orderstatus = data[i].Orderstatus + ord.Pending = data[i].Deliverydate + + if err := tx.Table("orders"). + Where("orderheaderid=?", data[i].Orderheaderid). + Updates(&ord).Error; err != nil { + tx.Rollback() + return err + } + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *deliveriesRepository) UpdateDelivery(data models.UpdateDeliveryStatus) error { + var ord models.Updateorderstatus + var cloc models.Customerlocations + + tx := r.db.Begin() + + if err := tx.Table("deliveries").Where("deliveryid = ?", data.Deliveryid).Updates(&data).Error; err != nil { + tx.Rollback() + return err + } + + switch data.Orderstatus { + case "pending": + ord.Orderstatus = data.Orderstatus + ord.Pending = data.Assigntime + if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + tx.Rollback() + return err + } + + case "picked": + if data.Pickuplocationid != 0 && data.Deliverytype != "B" { + cloc.Latitude = data.Riderslat + cloc.Longitude = data.Riderslon + cloc.Address = data.Address + cloc.Suburb = data.Suburb + cloc.City = data.City + cloc.State = data.State + cloc.Postcode = data.Postcode + + if err := tx.Table("customerlocations").Where("locationid = ?", data.Pickuplocationid).Updates(&cloc).Error; err != nil { + tx.Rollback() + return err + } + } + + case "delivered": + ord.Orderstatus = data.Orderstatus + ord.Delivered = data.Deliverytime + if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + tx.Rollback() + return err + } + + if data.Deliverylocationid != 0 && data.Deliverytype != "B" { + cloc.Latitude = data.Deliverylat + cloc.Longitude = data.Deliverylong + cloc.Address = data.Address + cloc.Suburb = data.Suburb + cloc.City = data.City + cloc.State = data.State + cloc.Postcode = data.Postcode + + if err := tx.Table("customerlocations").Where("locationid = ?", data.Deliverylocationid).Updates(&cloc).Error; err != nil { + tx.Rollback() + return err + } + } + + case "cancelled": + ord.Orderstatus = data.Orderstatus + ord.Cancelled = data.Canceltime + if err := tx.Table("orders").Where("orderheaderid = ?", data.Orderheaderid).Updates(&ord).Error; err != nil { + tx.Rollback() + return err + } + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *deliveriesRepository) GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) { + var data models.DeliverySummary + var q1 string + var params []interface{} + + // Base query + q1 = core + " FROM deliveries WHERE configid = 1" + + // Filters (allow multiple together) + if tid != 0 { + q1 += " AND tenantid = ?" + params = append(params, tid) + } + if pid != 0 { + q1 += " AND partnerid = ?" + params = append(params, pid) + } + if uid != 0 { + q1 += " AND userid = ?" + params = append(params, uid) + } + if aid != 0 { + q1 += " AND applocationid = ?" + params = append(params, aid) + } + if lid != 0 { + q1 += " AND locationid = ?" + params = append(params, lid) + } + + // Date filter + if fdate != "" && tdate != "" { + q1 += " AND deliverydate::date BETWEEN ? AND ?" + params = append(params, fdate, tdate) + } + + // Execute query + if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil { + return data, err + } + + print(q1) + + return data, nil +} + +func (r *deliveriesRepository) GetDeliveryInsight(tenantID int) ([]models.OrderInsightv1, error) { + var locations []models.OrderInsightv1 + var params []interface{} + + // Base query for fetching active locations + q1 := ` + SELECT DISTINCT l.locationid, l.locationname + FROM tenantlocations l + LEFT JOIN deliveries d ON l.locationid = d.locationid + WHERE l.status = 'Active' + ` + + // Add tenant filter if provided + if tenantID != 0 { + q1 += " AND l.tenantid = ?" + params = append(params, tenantID) + } + + if err := r.db.Raw(q1, params...).Scan(&locations).Error; err != nil { + return nil, err + } + + // Query for monthly deliveries + q2 := ` + SELECT + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 1 THEN 1 ELSE 0 END), 0) AS jan, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 2 THEN 1 ELSE 0 END), 0) AS feb, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 3 THEN 1 ELSE 0 END), 0) AS mar, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 4 THEN 1 ELSE 0 END), 0) AS apr, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 5 THEN 1 ELSE 0 END), 0) AS may, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 6 THEN 1 ELSE 0 END), 0) AS jun, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 7 THEN 1 ELSE 0 END), 0) AS jul, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 8 THEN 1 ELSE 0 END), 0) AS aug, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 9 THEN 1 ELSE 0 END), 0) AS sep, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 10 THEN 1 ELSE 0 END), 0) AS oct, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 11 THEN 1 ELSE 0 END), 0) AS nov, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM d.deliverydate) = 12 THEN 1 ELSE 0 END), 0) AS dece + FROM deliveries d + WHERE d.locationid = ? + AND EXTRACT(YEAR FROM d.deliverydate) = EXTRACT(YEAR FROM CURRENT_DATE) + ` + + if tenantID != 0 { + q2 += " AND d.tenantid = ?" + } + + // Attach month-wise insights for each location + for i := range locations { + var orderMonths models.Ordermonths + + var err error + if tenantID != 0 { + err = r.db.Raw(q2, locations[i].Locationid, tenantID).Scan(&orderMonths).Error + } else { + err = r.db.Raw(q2, locations[i].Locationid).Scan(&orderMonths).Error + } + + if err != nil { + return nil, err + } + + locations[i].Ordermonths = &orderMonths + } + + return locations, nil +} + +func (r *deliveriesRepository) GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) { + var data []models.Ordersummarylocation + var params []interface{} + + q1 := ` + SELECT + b.locationid, + b.locationname, + COALESCE(COUNT(a.orderid), 0) AS total, + COALESCE(SUM(CASE WHEN a.orderstatus = 'created' THEN 1 ELSE 0 END), 0) AS created, + COALESCE(SUM(CASE WHEN a.orderstatus = 'pending' THEN 1 ELSE 0 END), 0) AS pending, + COALESCE(SUM(CASE WHEN a.orderstatus = 'processing' THEN 1 ELSE 0 END), 0) AS processing, + COALESCE(SUM(CASE WHEN a.orderstatus = 'delivered' THEN 1 ELSE 0 END), 0) AS delivered, + COALESCE(SUM(CASE WHEN a.orderstatus = 'cancelled' THEN 1 ELSE 0 END), 0) AS cancelled + FROM tenantlocations b + LEFT JOIN deliveries a + ON a.locationid = b.locationid + AND a.configid = 1 + ` + + // ✅ Only filter tenant locations in WHERE + if tenantID != 0 { + q1 += " WHERE b.tenantid = ?" + params = append(params, tenantID) + } + + q1 += " GROUP BY b.locationid, b.locationname ORDER BY b.locationid" + + if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *deliveriesRepository) GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) { + var data []models.ReportSummary + var q1 string + + switch { + case tid != 0: + if fdate != "" && tdate != "" { + q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) + + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname` + } else { + q1 = reports + ` WHERE a.tenantid=` + strconv.Itoa(tid) + ` GROUP BY a.tenantid, b.tenantname` + } + + case pid != 0: + if fdate != "" && tdate != "" { + q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) + + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname` + } else { + q1 = reports + ` WHERE a.partnerid=` + strconv.Itoa(pid) + ` GROUP BY a.tenantid, b.tenantname` + } + + case uid != 0: + if fdate != "" && tdate != "" { + q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) + + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname` + } else { + q1 = reports + ` WHERE c.userid=` + strconv.Itoa(uid) + ` GROUP BY a.tenantid, b.tenantname` + } + + case aid != 0: + if fdate != "" && tdate != "" { + q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) + + ` AND a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname` + } else { + q1 = reports + ` WHERE c.applocationid=` + strconv.Itoa(aid) + ` GROUP BY a.tenantid, b.tenantname` + } + + default: + if fdate != "" && tdate != "" { + q1 = reports + ` WHERE a.orderdate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.tenantid, b.tenantname` + } else { + q1 = reports + ` GROUP BY a.tenantid, b.tenantname` + } + } + + if err := r.db.Raw(q1).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *deliveriesRepository) GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) { + var data []models.Ridersummary + var q1 string + + switch { + case aid != 0: + if fdate != "" && tdate != "" { + q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) + + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` + } else { + q1 = Ridersummary + ` WHERE a.configid=6 AND a.applocationid=` + strconv.Itoa(aid) + + ` GROUP BY a.userid, a.firstname, a.lastname` + } + + case pid != 0: + if fdate != "" && tdate != "" { + q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) + + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` + } else { + q1 = Ridersummary + ` WHERE a.configid=6 AND a.partnerid=` + strconv.Itoa(pid) + + ` GROUP BY a.userid, a.firstname, a.lastname` + } + + case tid != 0: + if fdate != "" && tdate != "" { + q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) + + ` AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` + } else { + q1 = Ridersummary + ` WHERE a.configid=6 AND b.tenantid=` + strconv.Itoa(tid) + + ` GROUP BY a.userid, a.firstname, a.lastname` + } + + default: + if fdate != "" && tdate != "" { + q1 = Ridersummary + ` WHERE a.configid=6 AND b.deliverydate::date BETWEEN '` + fdate + `' AND '` + tdate + `' GROUP BY a.userid, a.firstname, a.lastname` + } else { + q1 = Ridersummary + ` WHERE a.configid=6 GROUP BY a.userid, a.firstname, a.lastname` + } + } + + if err := r.db.Raw(q1).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *deliveriesRepository) GetTenantDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + fmt.Println("🔍 Fetching Tenant Deliveries") + + var data []models.Deliveryinfo + var queryBuilder strings.Builder + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + baseQuery := ` + SELECT a.*, b.tenantname, c.firstname AS ridername, c.contactno AS ridercontact + FROM deliveries a + JOIN tenants b ON a.tenantid = b.tenantid + JOIN app_users c ON a.userid = c.userid + WHERE a.tenantid = ? + ` + queryBuilder.WriteString(baseQuery) + params = append(params, input.Tenantid) + + if input.Status != "" { + queryBuilder.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Fromdate != "" && input.ToDate != "" { + queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + if input.Keyword != "" { + k := "%" + input.Keyword + "%" + queryBuilder.WriteString(` + AND ( + a.pickupcustomer LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + ) + `) + for i := 0; i < 5; i++ { + params = append(params, k) + } + } + + queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") + if input.Pagesize != 0 { + queryBuilder.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + query := queryBuilder.String() + fmt.Println("Final query:", query) + fmt.Println("Params:", params) + + r.db.Raw(query, params...).Scan(&data) + return data +} + +func (r *deliveriesRepository) GetPartnerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + fmt.Println("🔍 Fetching Partner Deliveries") + + var data []models.Deliveryinfo + var queryBuilder strings.Builder + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + queryBuilder.WriteString(deliveries + ` WHERE a.partnerid = ?`) + params = append(params, input.Partnerid) + + if input.Status != "" { + queryBuilder.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Fromdate != "" && input.ToDate != "" { + queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") + + if input.Pagesize > 0 { + queryBuilder.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + finalQuery := queryBuilder.String() + log.Println("Final Partner Query:", finalQuery, "Params:", params) + + r.db.Raw(finalQuery, params...).Scan(&data) + return data +} + +func (r *deliveriesRepository) GetCustomerDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + fmt.Println("🔍 Fetching Customer Deliveries") + + var data []models.Deliveryinfo + var queryBuilder strings.Builder + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + queryBuilder.WriteString(deliveries + ` WHERE a.customerid = ?`) + params = append(params, input.Customerid) + + if input.Partnerid != 0 { + queryBuilder.WriteString(" AND a.partnerid = ?") + params = append(params, input.Partnerid) + } + + if input.Status != "" { + queryBuilder.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Fromdate != "" && input.ToDate != "" { + queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + if input.Keyword != "" { + k := "%" + input.Keyword + "%" + queryBuilder.WriteString(` AND ( + a.pickupcustomer LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )`) + for i := 0; i < 5; i++ { + params = append(params, k) + } + } + + queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") + if input.Pagesize != 0 { + queryBuilder.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + query := queryBuilder.String() + fmt.Println("Final query:", query) + fmt.Println("Params:", params) + + r.db.Raw(query, params...).Scan(&data) + return data +} + +func (r *deliveriesRepository) GetAdminDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + fmt.Println("Admin Deliveries") + + var data []models.Deliveryinfo + var queryBuilder strings.Builder + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + queryBuilder.WriteString(` + SELECT a.*, b.tenantname, c.firstname as ridername + FROM deliveries a + JOIN tenants b ON a.tenantid = b.tenantid + JOIN app_users c ON c.userid = a.userid + WHERE a.applocationid = ? + `) + params = append(params, input.Applocationid) + + if input.Status != "" { + queryBuilder.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Fromdate != "" && input.ToDate != "" { + queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + if input.Keyword != "" { + k := "%" + input.Keyword + "%" + queryBuilder.WriteString(` + AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + ) + `) + for i := 0; i < 6; i++ { + params = append(params, k) + } + } + + queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") + + if input.Pagesize > 0 { + queryBuilder.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + finalQuery := queryBuilder.String() + + log.Println("AdminDeliveries query:", finalQuery) + log.Println("Params:", params) + + r.db.Raw(finalQuery, params...).Scan(&data) + return data +} + +func (r *deliveriesRepository) GetUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + var q1 string + + offset := (input.Pageno - 1) * input.Pagesize + + var data []models.Deliveryinfo + + if input.Status != "" { + + if input.Pagesize != 0 { + + fmt.Println("Rider Deliveriesv2") + + q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' + and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid + desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) + + } else { + + fmt.Println("Rider Deliveriesv1") + + q1 = deliveries + ` where a.userid=` + strconv.Itoa(input.UserID) + ` and a.orderstatus='` + input.Status + `' + and a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid asc` + + } + + } else { + + if input.Pagesize != 0 { + fmt.Println("Rider Deliveriesv2") + + q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and + a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid + desc LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) + + } else { + + fmt.Println("Rider Deliveriesv1") + + q1 = deliveries + ` where a.orderstatus in ('accepted','arrived','picked') and a.userid=` + strconv.Itoa(input.UserID) + ` and + a.deliverydate::date between '` + input.Fromdate + `' and '` + input.ToDate + `' order by deliveryid desc` + + } + + } + + r.db.Raw(q1).Find(&data) + + return data +} + +func (r *deliveriesRepository) GetAppUserDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + var data []models.Deliveryinfo + + fmt.Println("Appsuser Deliveries -new") + + var q1 string + offset := (input.Pageno - 1) * input.Pagesize + + q1 = deliveries + ` WHERE g.status = 'Active' AND g.userid=` + strconv.Itoa(input.Appuserid) + + if input.Status != "" { + q1 += ` AND a.orderstatus='` + input.Status + `'` + } + + if input.Fromdate != "" && input.ToDate != "" { + q1 += ` AND a.deliverydate::date BETWEEN '` + input.Fromdate + `' AND '` + input.ToDate + `'` + } + + if input.Keyword != "" { + k := "%" + input.Keyword + "%" + q1 += ` AND ( + a.pickupcustomer LIKE '` + k + `' OR + b.tenantname LIKE '` + k + `' OR + a.deliverycustomer LIKE '` + k + `' OR + a.pickupcontactno LIKE '` + k + `' OR + a.deliverycontactno LIKE '` + k + `' OR + CAST(a.orderid AS TEXT) LIKE '` + k + `' + )` + } + + q1 += ` ORDER BY a.deliveryid DESC` + if input.Pagesize != 0 { + q1 += ` LIMIT ` + strconv.Itoa(input.Pagesize) + ` OFFSET ` + strconv.Itoa(offset) + } + + log.Println("Final Query:", q1) + + r.db.Raw(q1).Find(&data) + return data +} + +func (r *deliveriesRepository) GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + var data []models.Deliveryinfo + var queryBuilder strings.Builder + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + baseQuery := ` + SELECT a.*, b.tenantname, c.firstname as ridername, c.contactno as ridercontact, d.customertoken + FROM deliveries a + JOIN tenants b ON a.tenantid = b.tenantid + Join app_users c ON a.userid = c.userid + JOIN customer d ON a.deliverycustomerid = d.customerid + WHERE 1=1 + ` + + queryBuilder.WriteString(baseQuery) + + if input.Fromdate != "" && input.ToDate != "" { + queryBuilder.WriteString(" AND a.deliverydate::date BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + if input.Status != "" { + queryBuilder.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Keyword != "" { + k := "%" + input.Keyword + "%" + queryBuilder.WriteString(` + AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + ) + `) + for i := 0; i < 6; i++ { + params = append(params, k) + } + } + + queryBuilder.WriteString(" ORDER BY a.deliveryid DESC") + + if input.Pagesize != 0 { + queryBuilder.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + query := queryBuilder.String() + + log.Println("Final query:", query) + log.Println("With params:", params) + + r.db.Raw(query, params...).Scan(&data) + + return data +} + +func (r *deliveriesRepository) GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) { + var data []models.Deliveryinfo + var q1 string + + if fdate != "" && tdate != "" { + q1 = deliveries + ` + WHERE a.orderstatus='pending' + AND a.userid=? + AND DATE(a.deliverydate) BETWEEN ? AND ? + ORDER BY a.deliveryid ASC` + if err := r.db.Raw(q1, uid, fdate, tdate).Find(&data).Error; err != nil { + return nil, err + } + } else { + q1 = deliveries + ` + WHERE a.orderstatus='pending' + AND a.userid=? + AND DATE(a.deliverydate)=CURDATE() + ORDER BY a.deliveryid ASC` + if err := r.db.Raw(q1, uid).Find(&data).Error; err != nil { + return nil, err + } + } + + return data, nil +} + +func (r *deliveriesRepository) GetTenantLocationDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + + var data []models.Deliveryinfo + var params []interface{} + var qb strings.Builder + + offset := (input.Pageno - 1) * input.Pagesize + + qb.WriteString(` + SELECT a.*, b.tenantname, c.firstname AS ridername, + c.contactno AS ridercontact, d.customertoken + FROM deliveries a + JOIN tenants b ON a.tenantid = b.tenantid + JOIN app_users c ON a.userid = c.userid + JOIN customers d ON a.deliverycustomerid = d.customerid + WHERE a.tenantid = ? AND a.locationid = ? + `) + + params = append(params, input.Tenantid, input.Locationid) + + if input.Fromdate != "" && input.ToDate != "" { + qb.WriteString(" AND DATE(a.deliverydate) BETWEEN ? AND ?") + params = append(params, input.Fromdate, input.ToDate) + } + + if input.Status != "" { + qb.WriteString(" AND a.orderstatus = ?") + params = append(params, input.Status) + } + + if input.Keyword != "" { + like := "%" + input.Keyword + "%" + qb.WriteString(` + AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR m + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + ) + `) + for i := 0; i < 6; i++ { + params = append(params, like) + } + } + + qb.WriteString(" ORDER BY a.deliveryid DESC") + + if input.Pagesize != 0 { + qb.WriteString(" LIMIT ? OFFSET ?") + params = append(params, input.Pagesize, offset) + } + + query := qb.String() + + log.Println("Tenant+Location Query:", query) + log.Println("Params:", params) + + r.db.Raw(query, params...).Scan(&data) + + return data +} diff --git a/repositories/orderRepository.go b/repositories/orderRepository.go new file mode 100644 index 0000000..d884279 --- /dev/null +++ b/repositories/orderRepository.go @@ -0,0 +1,1133 @@ +package repositories + +import ( + "fmt" + "log" + "nearle/models" + "strconv" + "time" + + "gorm.io/gorm" +) + +type OrderRepository interface { + GetTenantOrders(q models.DeliveryQuery) ([]models.OrderInfo, error) + GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) + GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetOrderSummary(tid, pid, cid, aid int, fdate, tdate string) ([]models.Ordersummarydaily, error) + GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error) + GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error) + GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error) + UpdateOrder(order *models.Orders) error + CreateOrder(order models.Orders) (models.Orders, error) + GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) +} + +type orderRepository struct { + db *gorm.DB +} + +func NewOrderRepository(db *gorm.DB) OrderRepository { + return &orderRepository{db: db} +} + +const ( + base = `SELECT DISTINCT a.orderheaderid, a.applocationid, h.locationname AS applocation, a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid, + a.orderid, a.orderstatus, a.orderdate, a.ordernotes, a.itemcount, a.deliverytime AS deliverydate, + a.pending, a.processing, a.ready, a.delivered AS completed, a.cancelled, + a.deliverycharge, a.kms, + a.customerid, a.pickuplocationid, a.pickupaddress, a.pickuplat, a.pickuplong, + a.pickupcustomer, a.pickupcontactno, a.pickuplocation as pickupsuburb, a.pickupcity, + a.deliveryid AS deliverycustomerid, a.deliveryaddress, a.deliverylat, a.deliverylong, a.deliverytype, + a.deliverycustomer,a.deliverycontactno,a.deliverylocation as deliverysuburb, a.deliverycity, a.paymenttype, a.smsdelivery, b.customertoken, + c.tenantname, c.tenanttoken, c.primarycontact AS tenantcontactno, c.postcode AS tenantpostcode, c.suburb AS tenantsuburb, c.city AS tenantcity, + d.locationname, d.contactno AS locationcontactno, d.postcode AS locationpostcode, d.suburb AS locationsuburb, d.city AS locationcity + FROM orders a + INNER JOIN customers b ON a.customerid = b.customerid + INNER JOIN tenants c ON a.tenantid = c.tenantid + INNER JOIN tenantlocations d ON a.locationid = d.locationid + + INNER JOIN app_location h ON a.applocationid = h.applocationid + INNER JOIN app_locationconfig i ON a.applocationid = i.applocationid` + + orderdetails = `SELECT DISTINCT a.orderheaderid, a.applocationid, + a.tenantid, a.locationid, a.partnerid, a.configid, a.categoryid, a.subcategoryid, a.moduleid, + a.orderid, a.orderstatus, a.orderdate, a.ordernotes, a.itemcount, a.deliverytime AS deliverydate, + a.pending, a.processing, a.ready, a.delivered AS completed, a.cancelled, + a.deliverycharge, a.kms, + a.customerid, a.pickupaddress, a.pickuplat, a.pickuplong, + a.pickupcustomer, a.pickupcontactno, a.pickuplocation as pickupsuburb, a.pickupcity, + a.deliveryid AS deliverycustomerid, a.deliveryaddress, a.deliverylat, a.deliverylong, a.deliverytype, + a.deliverycustomer,a.deliverycontactno,a.deliverylocation as deliverysuburb, a.deliverycity,a.paymenttype, a.smsdelivery, a.orderamount, + b.tenantname, b.tenanttoken, b.primarycontact AS tenantcontactno, b.postcode AS tenantpostcode, b.suburb AS tenantsuburb,b.city AS tenantcity, + c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode, c.suburb AS locationsuburb, c.city AS locationcity, + d.locationname AS applocation + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid` +) + +func (r *orderRepository) GetTenantOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) { + var data []models.OrderInfo + var query string + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + baseQuery := base + ` WHERE a.tenantid = ?` + params = append(params, input.Tenantid) + + if input.Status == "ongoing" { + query = baseQuery + ` AND a.orderstatus IN ('pending','processing','ready')` + } else { + query = baseQuery + ` AND a.orderstatus = ?` + params = append(params, input.Status) + } + + query += ` AND a.deliverytime::date BETWEEN ? AND ?` + params = append(params, input.Fromdate, input.ToDate) + + if input.Keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + c.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + input.Keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + if input.Configid != 0 { + query += ` AND a.configid = ?` + params = append(params, input.Configid) + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, input.Pagesize, offset) + + fmt.Println("Executing:", query) + res := r.db.Raw(query, params...).Find(&data) + + return data, res.Error +} + +func (r *orderRepository) GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + var data []models.OrderInfo + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + fmt.Println("Getting partner order details") + + query := orderdetails + ` WHERE a.partnerid = ?` + params := []interface{}{pid} + + if fdate != "" && tdate != "" { + query += ` AND a.deliverytime::date BETWEEN ? AND ?` + params = append(params, fdate, tdate) + } + if stat != "" { + query += ` AND a.orderstatus = ?` + params = append(params, stat) + } + if keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + var total int64 + countQuery := ` + SELECT COUNT(DISTINCT a.orderheaderid) + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid + LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid + LEFT JOIN app_users g ON f.userid = g.userid + WHERE a.partnerid = ?` + countParams := []interface{}{pid} + + if fdate != "" && tdate != "" { + countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?` + countParams = append(countParams, fdate, tdate) + } + if stat != "" { + countQuery += ` AND a.orderstatus = ?` + countParams = append(countParams, stat) + } + if keyword != "" { + countQuery += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + countParams = append(countParams, like, like, like, like, like, like) + } + + r.db.Raw(countQuery, countParams...).Scan(&total) + + if int64(offset) >= total { + offset = 0 + pageno = 1 + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, pagesize, offset) + + fmt.Println("QUERY:", query) + fmt.Println("PARAMS:", params) + + r.db.Raw(query, params...).Find(&data) + fmt.Println("RESULT COUNT:", len(data)) + + return data, nil +} + +func (r *orderRepository) GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + var data []models.OrderInfo + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + fmt.Println("Getting customer order details") + + query := orderdetails + ` WHERE a.customerid = ?` + params := []interface{}{cid} + + if mid != 0 { + query += ` AND a.moduleid = ?` + params = append(params, mid) + } + if fdate != "" && tdate != "" { + query += ` AND a.orderdate::date BETWEEN ? AND ?` + params = append(params, fdate, tdate) + } + if stat != "" { + query += ` AND a.orderstatus = ?` + params = append(params, stat) + } + if keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + var total int64 + countQuery := ` + SELECT COUNT(DISTINCT a.orderheaderid) + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid + WHERE a.customerid = ?` + countParams := []interface{}{cid} + + if mid != 0 { + countQuery += ` AND a.moduleid = ?` + countParams = append(countParams, mid) + } + if fdate != "" && tdate != "" { + countQuery += ` AND a.orderdate::date BETWEEN ? AND ?` + countParams = append(countParams, fdate, tdate) + } + if stat != "" { + countQuery += ` AND a.orderstatus = ?` + countParams = append(countParams, stat) + } + if keyword != "" { + countQuery += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + countParams = append(countParams, like, like, like, like, like, like) + } + + r.db.Raw(countQuery, countParams...).Scan(&total) + + if int64(offset) >= total { + offset = 0 + pageno = 1 + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, pagesize, offset) + + fmt.Println("QUERY:", query) + fmt.Println("PARAMS:", params) + + res := r.db.Raw(query, params...).Find(&data) + if res.Error != nil { + fmt.Println("ERROR:", res.Error) + } + fmt.Println("RESULT COUNT:", len(data)) + + return data, nil +} + +func (r *orderRepository) GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + var data []models.OrderInfo + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + fmt.Println("Getting admin order details") + + query := orderdetails + ` WHERE 1=1` + params := []interface{}{} + + if aid != 0 { + query += ` AND a.applocationid = ?` + params = append(params, aid) + } + + if fdate != "" && tdate != "" { + query += ` AND a.deliverytime::date BETWEEN ? AND ?` + params = append(params, fdate, tdate) + } + + if stat != "" { + query += ` AND a.orderstatus = ?` + params = append(params, stat) + } + + if keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + var total int64 + countQuery := ` + SELECT COUNT(DISTINCT a.orderheaderid) + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid + LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid + LEFT JOIN app_users g ON f.userid = g.userid + WHERE 1=1 + ` + countParams := []interface{}{} + + if aid != 0 { + countQuery += ` AND a.applocationid = ?` + countParams = append(countParams, aid) + } + if fdate != "" && tdate != "" { + countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?` + countParams = append(countParams, fdate, tdate) + } + if stat != "" { + countQuery += ` AND a.orderstatus = ?` + countParams = append(countParams, stat) + } + if keyword != "" { + countQuery += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + countParams = append(countParams, like, like, like, like, like, like) + } + + r.db.Raw(countQuery, countParams...).Scan(&total) + + if int64(offset) >= total { + offset = 0 + pageno = 1 + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, pagesize, offset) + + fmt.Println("QUERY:", query) + fmt.Println("PARAMS:", params) + + r.db.Raw(query, params...).Find(&data) + fmt.Println("RESULT COUNT:", len(data)) + + return data, nil +} + +func (r *orderRepository) GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + var data []models.OrderInfo + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + fmt.Println("Getting user order details") + + query := orderdetails + ` WHERE e.status = 'Active' AND e.userid = ?` + params := []interface{}{uid} + + if fdate != "" && tdate != "" { + query += ` AND a.deliverytime::date BETWEEN ? AND ?` + params = append(params, fdate, tdate) + } + if stat != "" { + query += ` AND a.orderstatus = ?` + params = append(params, stat) + } + if keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + var total int64 + countQuery := ` + SELECT COUNT(DISTINCT a.orderheaderid) + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid + LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid + LEFT JOIN app_users g ON f.userid = g.userid + WHERE e.status = 'Active' AND e.userid = ? + ` + countParams := []interface{}{uid} + + if fdate != "" && tdate != "" { + countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?` + countParams = append(countParams, fdate, tdate) + } + if stat != "" { + countQuery += ` AND a.orderstatus = ?` + countParams = append(countParams, stat) + } + if keyword != "" { + countQuery += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + countParams = append(countParams, like, like, like, like, like, like) + } + + r.db.Raw(countQuery, countParams...).Scan(&total) + + if int64(offset) >= total { + offset = 0 + pageno = 1 + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, pagesize, offset) + + fmt.Println("QUERY:", query) + fmt.Println("PARAMS:", params) + + r.db.Raw(query, params...).Find(&data) + fmt.Println("RESULT COUNT:", len(data)) + + return data, nil +} + +func (r *orderRepository) GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + var data []models.OrderInfo + + if pageno <= 0 { + pageno = 1 + } + if pagesize <= 0 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + fmt.Println("Getting all orders") + + query := orderdetails + ` WHERE 1=1` + params := []interface{}{} + + if fdate != "" && tdate != "" { + query += ` AND a.deliverytime::date BETWEEN ? AND ?` + params = append(params, fdate, tdate) + } + + if stat != "" { + query += ` AND a.orderstatus = ?` + params = append(params, stat) + } + + if keyword != "" { + query += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + params = append(params, like, like, like, like, like, like) + } + + var total int64 + countQuery := ` + SELECT COUNT(DISTINCT a.orderheaderid) + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + INNER JOIN app_locationconfig e ON d.applocationid = e.applocationid + LEFT JOIN deliveries f ON a.orderheaderid = f.orderheaderid + LEFT JOIN app_users g ON f.userid = g.userid + WHERE 1=1 + ` + countParams := []interface{}{} + + if fdate != "" && tdate != "" { + countQuery += ` AND a.deliverytime::date BETWEEN ? AND ?` + countParams = append(countParams, fdate, tdate) + } + if stat != "" { + countQuery += ` AND a.orderstatus = ?` + countParams = append(countParams, stat) + } + if keyword != "" { + countQuery += ` AND ( + a.pickupcustomer LIKE ? OR + b.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + like := "%" + keyword + "%" + countParams = append(countParams, like, like, like, like, like, like) + } + + r.db.Raw(countQuery, countParams...).Scan(&total) + + if int64(offset) >= total { + offset = 0 + pageno = 1 + } + + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, pagesize, offset) + + fmt.Println("QUERY:", query) + fmt.Println("PARAMS:", params) + + r.db.Raw(query, params...).Find(&data) + fmt.Println("RESULT COUNT:", len(data)) + + return data, nil +} + +func (r *orderRepository) GetOrderSummary(tid, pid, cid, lid int, fdate, tdate string) ([]models.Ordersummarydaily, error) { + var data []models.Ordersummarydaily + var q1 string + + // Base SELECT + const base = ` + SELECT + COUNT(*) AS total, + SUM(CASE WHEN o.orderstatus = 'created' THEN 1 ELSE 0 END) AS created, + SUM(CASE WHEN o.orderstatus = 'pending' THEN 1 ELSE 0 END) AS pending, + SUM(CASE WHEN o.orderstatus = 'processing' THEN 1 ELSE 0 END) AS processing, + SUM(CASE WHEN o.orderstatus = 'delivered' THEN 1 ELSE 0 END) AS delivered, + SUM(CASE WHEN o.orderstatus = 'cancelled' THEN 1 ELSE 0 END) AS cancelled, + t.tenantid, + t.tenantname + FROM orders o + INNER JOIN tenants t ON o.tenantid = t.tenantid + ` + + // Apply filters + if tid != 0 { + q1 = base + " WHERE o.configid = 1 AND o.tenantid = " + strconv.Itoa(tid) + } else if pid != 0 { + q1 = base + " WHERE o.configid = 1 AND o.partnerid = " + strconv.Itoa(pid) + } else if cid != 0 { + q1 = base + " WHERE o.configid = 1 AND o.customerid = " + strconv.Itoa(cid) + } else if lid != 0 { + q1 = base + " WHERE o.configid = 1 AND o.locationid = " + strconv.Itoa(lid) + } else { + q1 = base + " WHERE o.configid = 1" + } + + // Date filter + if fdate != "" && tdate != "" { + q1 += " AND o.orderdate::date BETWEEN '" + fdate + "' AND '" + tdate + "'" + } + + // Group by tenant + q1 += " GROUP BY t.tenantid, t.tenantname" + + // Debug + fmt.Println("Executing GetOrderSummary query:", q1) + + if err := r.db.Raw(q1).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *orderRepository) GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error) { + var data []models.Ordersummarylocation + var params []interface{} + + q1 := ` + SELECT + l.locationid, + l.locationname, + COALESCE(COUNT(o.orderid), 0) AS total, + COALESCE(SUM(CASE WHEN o.orderstatus = 'created' THEN 1 ELSE 0 END), 0) AS created, + COALESCE(SUM(CASE WHEN o.orderstatus = 'pending' THEN 1 ELSE 0 END), 0) AS pending, + COALESCE(SUM(CASE WHEN o.orderstatus = 'processing' THEN 1 ELSE 0 END), 0) AS processing, + COALESCE(SUM(CASE WHEN o.orderstatus = 'delivered' THEN 1 ELSE 0 END), 0) AS delivered, + COALESCE(SUM(CASE WHEN o.orderstatus = 'cancelled' THEN 1 ELSE 0 END), 0) AS cancelled + FROM tenantlocations l + LEFT JOIN orders o + ON l.locationid = o.locationid + AND l.tenantid = o.tenantid + AND o.configid = 1 + ` + + if tenantID != 0 { + q1 += " WHERE l.tenantid = ?" + params = append(params, tenantID) + } + + q1 += " GROUP BY l.locationid, l.locationname ORDER BY l.locationid" + + fmt.Println("Executing query:", q1) + + if err := r.db.Raw(q1, params...).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *orderRepository) GetDistinctLocations() ([]models.OrderInsight, error) { + var locations []models.OrderInsight + + q1 := ` + SELECT DISTINCT a.applocationid, b.locationname + FROM orders a + INNER JOIN app_location b ON a.applocationid = b.applocationid + WHERE b.status = 'Active'` + + if err := r.db.Raw(q1).Scan(&locations).Error; err != nil { + return nil, err + } + return locations, nil +} + +func (r *orderRepository) GetMonthlyOrders(applocationid string) (*models.Ordermonths, error) { + var orderMonths models.Ordermonths + + q2 := ` + SELECT + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 1 THEN 1 ELSE 0 END), 0) AS jan, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 2 THEN 1 ELSE 0 END), 0) AS feb, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 3 THEN 1 ELSE 0 END), 0) AS mar, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 4 THEN 1 ELSE 0 END), 0) AS apr, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 5 THEN 1 ELSE 0 END), 0) AS may, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 6 THEN 1 ELSE 0 END), 0) AS jun, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 7 THEN 1 ELSE 0 END), 0) AS jul, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 8 THEN 1 ELSE 0 END), 0) AS aug, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 9 THEN 1 ELSE 0 END), 0) AS sep, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 10 THEN 1 ELSE 0 END), 0) AS oct, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 11 THEN 1 ELSE 0 END), 0) AS nov, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 12 THEN 1 ELSE 0 END), 0) AS dece + FROM orders a + WHERE a.applocationid = ? + AND EXTRACT(YEAR FROM a.orderdate) = EXTRACT(YEAR FROM CURRENT_DATE)` + + if err := r.db.Raw(q2, applocationid).Scan(&orderMonths).Error; err != nil { + return nil, err + } + + return &orderMonths, nil +} + +func (r *orderRepository) GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error) { + var locations []models.OrderInsightv1 + var params []interface{} + + // ✅ Query 1: Get all locations (even without orders) + q1 := `SELECT b.locationid, b.locationname + FROM tenantlocations b + WHERE b.status = 'Active'` + + if tenantID != 0 { + q1 += " AND b.tenantid = ?" + params = append(params, tenantID) + } + + if err := r.db.Raw(q1, params...).Scan(&locations).Error; err != nil { + return nil, err + } + + // ✅ Query 2: Monthly order counts per location + q2 := `SELECT + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 1 THEN 1 ELSE 0 END), 0) AS jan, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 2 THEN 1 ELSE 0 END), 0) AS feb, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 3 THEN 1 ELSE 0 END), 0) AS mar, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 4 THEN 1 ELSE 0 END), 0) AS apr, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 5 THEN 1 ELSE 0 END), 0) AS may, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 6 THEN 1 ELSE 0 END), 0) AS jun, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 7 THEN 1 ELSE 0 END), 0) AS jul, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 8 THEN 1 ELSE 0 END), 0) AS aug, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 9 THEN 1 ELSE 0 END), 0) AS sep, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 10 THEN 1 ELSE 0 END), 0) AS oct, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 11 THEN 1 ELSE 0 END), 0) AS nov, + COALESCE(SUM(CASE WHEN EXTRACT(MONTH FROM a.orderdate) = 12 THEN 1 ELSE 0 END), 0) AS dece + FROM orders a + WHERE a.locationid = ? AND EXTRACT(YEAR FROM a.orderdate) = EXTRACT(YEAR FROM CURRENT_DATE)` + + if tenantID != 0 { + q2 += " AND a.tenantid = ?" + } + + // ✅ Attach monthly order counts for each location + for i := range locations { + var orderMonths models.Ordermonths + + if tenantID != 0 { + if err := r.db.Raw(q2, locations[i].Locationid, tenantID).Scan(&orderMonths).Error; err != nil { + return nil, err + } + } else { + if err := r.db.Raw(q2, locations[i].Locationid).Scan(&orderMonths).Error; err != nil { + return nil, err + } + } + + locations[i].Ordermonths = &orderMonths + } + + return locations, nil +} + +func (r *orderRepository) GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error) { + var details []models.OrderDetails + + query := ` + SELECT a.orderdetailid, a.orderheaderid, a.tenantid, a.locationid, a.productid, a.productname, a.productdescription, a.supplyqty, a.balanceqty, + a.orderqty, a.price, a.unitid, a.unitname, a.productaddonid, a.addontypeid, a.productmapid, a.productvariantid, a.productaddondescription, + a.discountid, a.discountname, a.discountcode, a.discountterms, a.discountpercentage, a.discountamount, a.landingamount, a.taxpercentage, + a.taxamount, a.productsumprice, a.itemstatus, a.delivered, COALESCE(b.orderamount, 0) as orderamount, COALESCE(b.taxamount, 0) as totaltaxamount, + c.productimage + FROM orderdetails a + LEFT JOIN orders b ON b.orderheaderid = a.orderheaderid + LEFT JOIN products c ON a.productid = c.productid + WHERE a.orderheaderid = ? + ` + + if err := r.db.Raw(query, orderHeaderID).Scan(&details).Error; err != nil { + return nil, err + } + + return details, nil +} + +func (r *orderRepository) UpdateOrder(order *models.Orders) error { + tx := r.db.Begin() + + if err := tx.Where("orderheaderid = ?", order.Orderheaderid).Updates(order).Error; err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} + +func (r *orderRepository) getSequenceno(tid int, prefix string) string { + type SeqResult struct { + Orderseqno string + } + + var q1 string + // Formats the ID as tenantid-subprefix+seqno (e.g., 908-20245189) + if prefix == "ORD" { + q1 = `SELECT CONCAT(tenantid, '-', + CASE WHEN subprefix IS NULL OR CAST(subprefix AS TEXT) IN ('0', '0.0', '') THEN '' ELSE CAST(subprefix AS TEXT) END, + COALESCE(MAX(orderseqno) + 1, 1)) AS orderseqno + FROM ordersequences WHERE tenantid = ? + GROUP BY tenantid, subprefix` + } else if prefix == "INV" { + q1 = `SELECT CONCAT(tenantid, '-', + CASE WHEN subprefix IS NULL OR CAST(subprefix AS TEXT) IN ('0', '0.0', '') THEN '' ELSE CAST(subprefix AS TEXT) END, + COALESCE(MAX(invoiceseqno) + 1, 1)) AS orderseqno + FROM ordersequences WHERE tenantid = ? + GROUP BY tenantid, subprefix` + } + + var result SeqResult + r.db.Raw(q1, tid).Scan(&result) + + // Fallback if no row exists in the database + if result.Orderseqno == "" { + return fmt.Sprintf("%d-1", tid) + } + + return result.Orderseqno +} + +func (r *orderRepository) updateSeqno(tid int, prefix string) error { + var field string + if prefix == "ORD" { + field = "orderseqno" + } else if prefix == "INV" { + field = "invoiceseqno" + } else { + return fmt.Errorf("invalid prefix: %s", prefix) + } + + // 🛠️ Improved: Check if row exists, if not, create it + var count int64 + r.db.Table("ordersequences").Where("tenantid = ?", tid).Count(&count) + + if count == 0 { + newSeq := map[string]interface{}{ + "tenantid": tid, + field: 1, + "subprefix": nil, // Use NULL so CONCAT ignores it or set default here + } + return r.db.Table("ordersequences").Create(&newSeq).Error + } + + // If it exists, perform the update + return r.db.Table("ordersequences"). + Where("tenantid = ?", tid). + Update(field, gorm.Expr(fmt.Sprintf("%s + 1", field))).Error +} + +func (r *orderRepository) CreateOrder(data models.Orders) (models.Orders, error) { + tx := r.db.Begin() + + data.Orderid = r.getSequenceno(data.Tenantid, "ORD") + + if err := tx.Create(&data).Error; err != nil { + tx.Rollback() + return models.Orders{}, err + } + + for _, item := range data.Items { + item.Orderheaderid = data.Orderheaderid + item.Tenantid = data.Tenantid + item.Locationid = data.Locationid + + if err := tx.Table("orderdetails").Create(&item).Error; err != nil { + tx.Rollback() + return models.Orders{}, err + } + + stock := models.Productstock{ + Tenantid: data.Tenantid, + Stockdate: time.Now(), + Locationid: data.Locationid, + Productid: item.Productid, + Quantity: int(item.Orderqty), + Stocktype: "out", + Status: "Active", + } + if err := tx.Table("productstocks").Create(&stock).Error; err != nil { + tx.Rollback() + return models.Orders{}, err + } + } + + if err := r.updateSeqno(data.Tenantid, "ORD"); err != nil { + log.Println("updateSeqno error:", err) + } + + if err := tx.Commit().Error; err != nil { + return models.Orders{}, err + } + + var order models.Orders + if err := r.db.Where("orderheaderid = ?", data.Orderheaderid).First(&order).Error; err != nil { + return models.Orders{}, err + } + var items []models.OrderDetail + if err := r.db.Table("orderdetails").Where("orderheaderid = ?", data.Orderheaderid).Find(&items).Error; err == nil { + order.Items = items + } + + return order, nil +} + +func (r *orderRepository) getOrderDetailsByHeaderID(orderHeaderID int) ([]models.OrderDetails, float64, float64, error) { + var details []models.OrderDetails + var orderAmount float64 + var totalTaxAmount float64 + + query := ` + SELECT a.orderdetailid,a.orderheaderid,a.tenantid,a.locationid,a.productid,a.productname,a.productdescription,a.supplyqty,a.balanceqty,a.orderqty,a.price,a.unitid, + a.unitname,a.productaddonid,a.addontypeid,a.productmapid,a.productvariantid,a.productaddondescription,a.discountid,a.discountname,a.discountcode,a.discountterms, + a.discountpercentage,a.discountamount,a.landingamount,a.taxpercentage,a.taxamount,a.productsumprice,a.itemstatus,a.delivered,COALESCE(b.orderamount, 0) as orderamount, + COALESCE(b.taxamount, 0) as totaltaxamount,c.productimage + FROM orderdetails a + LEFT JOIN orders b ON b.orderheaderid = a.orderheaderid + LEFT JOIN products c ON a.productid = c.productid + WHERE a.orderheaderid = ?` + + err := r.db.Raw(query, orderHeaderID).Scan(&details).Error + if err != nil { + return nil, 0, 0, err + } + + if len(details) > 0 { + orderAmount = details[0].Orderamount + totalTaxAmount = details[0].Totaltaxamount + } + + return details, orderAmount, totalTaxAmount, nil +} + +func (r *orderRepository) GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) { + + orders := make([]models.CustomerOrder, 0) + + baseQuery := ` + SELECT a.orderheaderid, a.applocationid, a.tenantid, a.locationid, a.partnerid, a.configid, + a.categoryid, a.subcategoryid, a.moduleid, a.orderid, a.orderstatus, a.orderdate, + a.ordernotes, a.itemcount, a.deliverytime, a.pending, a.processing, a.ready, + a.delivered, a.cancelled, a.deliverycharge, a.kms, a.customerid, a.pickupaddress, + a.pickuplat, a.pickuplong, a.pickupcustomer, a.pickupcontactno, a.pickuplocation AS pickupsuburb, + a.pickupcity, a.deliveryid AS deliverycustomerid, a.deliveryaddress, a.deliverylat, + a.deliverylong, a.deliverytype, a.deliverycustomer, a.deliverycontactno, + a.deliverylocation AS deliverysuburb, a.deliverycity, a.paymenttype, a.smsdelivery, + a.taxamount, + b.tenantname, b.tenanttoken, b.primarycontact AS tenantcontactno, + b.postcode AS tenantpostcode, b.suburb AS tenantsuburb, b.city AS tenantcity, b.registrationno, + c.locationname, c.contactno AS locationcontactno, c.postcode AS locationpostcode, + c.suburb AS locationsuburb, c.city AS locationcity, + d.locationname AS applocation + FROM orders a + INNER JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + INNER JOIN app_location d ON a.applocationid = d.applocationid + WHERE 1=1 + ` + + params := []interface{}{} + + if customerID != "" { + baseQuery += " AND a.customerid = ?" + params = append(params, customerID) + } + + if tenantID != "" && tenantID != "0" { + baseQuery += " AND a.tenantid = ?" + params = append(params, tenantID) + } + + if moduleID != "" { + baseQuery += " AND a.moduleid = ?" + params = append(params, moduleID) + } + + if fromDate != "" && toDate != "" { + baseQuery += " AND DATE(a.orderdate) BETWEEN ? AND ?" + params = append(params, fromDate, toDate) + } + + if orderStatus != "" { + baseQuery += " AND a.orderstatus = ?" + params = append(params, orderStatus) + } + + if keyword != "" { + baseQuery += " AND EXISTS (SELECT 1 FROM orderdetails od WHERE od.orderheaderid = a.orderheaderid AND od.productname LIKE ?)" + params = append(params, "%"+keyword+"%") + } + + baseQuery += " ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?" + params = append(params, pageSize, offset) + + if err := r.db.Raw(baseQuery, params...).Scan(&orders).Error; err != nil { + return nil, err + } + + // 🚀 NO LOOPING YET — Now fetch all details in one query + return r.attachOrderDetails(orders) +} + +func (r *orderRepository) attachOrderDetails(orders []models.CustomerOrder) ([]models.CustomerOrder, error) { + + if len(orders) == 0 { + return orders, nil + } + + orderIDs := make([]int, 0, len(orders)) + for _, o := range orders { + orderIDs = append(orderIDs, o.Orderheaderid) + } + + var details []models.OrderDetails + query := ` + SELECT a.*, p.productimage + FROM orderdetails a + LEFT JOIN products p ON a.productid = p.productid + WHERE a.orderheaderid IN ? + ` + + if err := r.db.Raw(query, orderIDs).Scan(&details).Error; err != nil { + return nil, err + } + + // map orderheaderid → []details + detailMap := map[int][]models.OrderDetails{} + for _, d := range details { + detailMap[d.Orderheaderid] = append(detailMap[d.Orderheaderid], d) + } + + for i := range orders { + orders[i].OrderDetails = detailMap[orders[i].Orderheaderid] + + if len(orders[i].OrderDetails) > 0 { + orders[i].Orderamount = orders[i].OrderDetails[0].Orderamount + orders[i].Totaltaxamount = orders[i].OrderDetails[0].Totaltaxamount + } + } + + return orders, nil +} + +func (r *orderRepository) GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) { + + var data []models.OrderInfo + var params []interface{} + + offset := (input.Pageno - 1) * input.Pagesize + + // Start building SQL + query := base + ` + WHERE a.tenantid = ? + AND a.locationid = ? + ` + params = append(params, input.Tenantid, input.Locationid) + + // Status filter + if input.Status == "ongoing" { + query += ` AND a.orderstatus IN ('pending','processing','ready')` + } else if input.Status != "" { + query += ` AND a.orderstatus = ?` + params = append(params, input.Status) + } + + // Applocation filter + if input.Applocationid != 0 { + query += ` AND a.applocationid = ?` + params = append(params, input.Applocationid) + } + + // Config filter + if input.Configid != 0 { + query += ` AND a.configid = ?` + params = append(params, input.Configid) + } + + // Date filter + query += ` AND DATE(a.deliverytime) BETWEEN ? AND ?` + params = append(params, input.Fromdate, input.ToDate) + + // Keyword filter + if input.Keyword != "" { + like := "%" + input.Keyword + "%" + query += ` AND ( + a.pickupcustomer LIKE ? OR + c.tenantname LIKE ? OR + a.deliverycustomer LIKE ? OR + a.pickupcontactno LIKE ? OR + a.deliverycontactno LIKE ? OR + a.orderid LIKE ? + )` + params = append(params, like, like, like, like, like, like) + } + + // Sorting + pagination + query += ` ORDER BY a.orderheaderid DESC LIMIT ? OFFSET ?` + params = append(params, input.Pagesize, offset) + + fmt.Println("Executing Tenant+Location SQL:", query) + + result := r.db.Raw(query, params...).Find(&data) + return data, result.Error +} diff --git a/repositories/partnerRepository.go b/repositories/partnerRepository.go new file mode 100644 index 0000000..fadd25d --- /dev/null +++ b/repositories/partnerRepository.go @@ -0,0 +1,199 @@ +package repositories + +import ( + "fmt" + "nearle/models" + "strconv" + + "gorm.io/gorm" +) + +type PartnerRepository interface { + GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error) + GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error) + GetRiderShifts(aid int) ([]models.Ridershifts, error) + GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) + GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) + GetRiderInfo(userid int) (models.RiderInfo, error) +} + +type partnerRepository struct { + db *gorm.DB +} + +func NewPartnerRepository(db *gorm.DB) PartnerRepository { + return &partnerRepository{db: db} +} + +func (r *partnerRepository) GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error) { + var data []models.RiderInfo + var q1 string + + const riders = `SELECT DISTINCT b.poolid, a.userid, a.firstname, a.lastname,CONCAT(a.firstname, ' ', a.lastname) AS fullname,a.contactno, a.userfcmtoken, a.partnerid, a.applocationid, + c.identificationno, c.registrationno, c.licenseno, c.vehiclename, c.vehicleno,d.shiftid, d.starttime, d.endtime, d.shifthours, d.basefare, d.fuelcharge,e.logdate, + e.login, e.logout, e.workhours, e.shorthours, e.logstatus, a.status,f.locationname AS applocation + FROM app_users a + INNER JOIN app_userpools b ON a.userid = b.userid + INNER JOIN ridersettings c ON a.userid = c.userid + INNER JOIN ridershifts d ON c.shiftid = d.shiftid + INNER JOIN ( + SELECT r1.* + FROM riderlogs r1 + INNER JOIN ( + SELECT userid, MAX(logdate) AS max_logdate + FROM riderlogs + WHERE logdate::date = CURRENT_DATE + GROUP BY userid + ) r2 ON r1.userid = r2.userid AND r1.logdate = r2.max_logdate + ) e ON a.userid = e.userid + INNER JOIN app_location f ON a.applocationid = f.applocationid + INNER JOIN app_locationconfig g ON g.applocationid = f.applocationid` + + if aid != 0 { + q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1 + AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.applocationid = ` + strconv.Itoa(aid) + } else if partnerid != 0 { + q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1 + AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.partnerid = ` + strconv.Itoa(partnerid) + } else if tid != 0 { + q1 = riders + ` WHERE a.configid = 6 AND a.status = 'Active' AND b.onduty = 1 + AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND a.tenantid = ` + strconv.Itoa(tid) + } else { + q1 = riders + ` WHERE g.status = 'Active' AND a.configid = 6 AND a.status = 'Active' + AND b.onduty = 1 AND e.logdate::date = CURRENT_DATE AND e.logstatus = 0 AND g.userid = ` + strconv.Itoa(uid) + } + + err := r.db.Raw(q1).Find(&data).Error + if err != nil { + return nil, err + } + + return data, nil +} + +func (r *partnerRepository) GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error) { + var data []models.Partnerinfo + var q1 string + var args []interface{} + + if pid != 0 { + q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail, + contactno,address,suburb,state,city,partnerimage + from partnerinfo where status='Active' and partnerid=?` + args = append(args, pid) + + } else if aid != 0 { + q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail, + contactno,address,suburb,state,city,partnerimage + from partnerinfo where status='Active' and applocationid=?` + args = append(args, aid) + + } else { + q1 = `select partnerid,applocationid,partnertypeid,partnername,primarycontact,primaryemail, + contactno,address,suburb,state,city,partnerimage + from partnerinfo where status='Active'` + } + + err := r.db.Raw(q1, args...).Find(&data).Error + if err != nil { + return nil, err + } + + print(q1) + return data, nil +} + +func (r *partnerRepository) GetRiderShifts(aid int) ([]models.Ridershifts, error) { + + var data []models.Ridershifts + + q1 := `Select a.*,concat(a.starttime,'-',a.endtime) as shiftname from ridershifts a where a.applocationid=` + strconv.Itoa(aid) + + err := r.db.Raw(q1).Find(&data).Error + + if err != nil { + return nil, err + } + + print(q1) + + return data, nil +} + +func (r *partnerRepository) GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) { + + var data []models.Locationconfigs + + q1 := fmt.Sprintf(`SELECT a.applocationid,a.locationname FROM app_location a + inner join app_locationconfig b ON a.applocationid=b.applocationid + WHERE b.status='Active' and b.userid=%d`, uid) + + err := r.db.Raw(q1).Find(&data).Error + if err != nil { + return nil, err + } + + print(q1) + return data, nil +} + +func (r *partnerRepository) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) { + var data []models.RiderlogDetails + var args []interface{} + + baseQuery := ` + SELECT a.*, b.*, CONCAT(b.firstname,' ',b.lastname) AS username, + COALESCE(SUM(c.breakhours),0) AS breakhours + FROM riderlogs a + INNER JOIN app_users b ON a.userid = b.userid + LEFT JOIN riderbreaks c ON a.logid = c.logid AND a.userid = c.userid + WHERE 1=1 + ` + + if pid != 0 { + baseQuery += " AND a.partnerid = ?" + args = append(args, pid) + } + + if aid != 0 { + baseQuery += " AND b.applocationid = ?" + args = append(args, aid) + } + + if fdate != "" && tdate != "" { + baseQuery += " AND logdate::date BETWEEN ? AND ?" + args = append(args, fdate, tdate) + } else { + baseQuery += " AND a.logdate::date = CURRENT_DATE" + } + + baseQuery += " GROUP BY a.userid ORDER BY logid ASC" + + if err := r.db.Raw(baseQuery, args...).Find(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *partnerRepository) GetRiderInfo(userid int) (models.RiderInfo, error) { + var data models.RiderInfo + + q1 := `SELECT a.userid,a.firstname,a.lastname, CONCAT(a.firstname,' ',a.lastname) as fullname, + a.partnerid,a.configid,a.contactno, + a.address,a.suburb,a.city,a.state,a.postcode,a.latitude,a.longitude, + b.identificationno,b.vehicleno,b.vehiclename,b.licenseno,b.insuranceno,b.insurancedate, + c.shiftid,c.starttime,c.endtime,c.shifthours,c.basefare,c.additionalcharges,c.orders,c.fuelcharge,a.status, + a.applocationid,d.locationname as applocation,d.logseconds + FROM app_users a + INNER JOIN ridersettings b ON a.userid=b.userid + INNER JOIN ridershifts c ON b.shiftid=c.shiftid + INNER JOIN app_location d ON a.applocationid=d.applocationid + WHERE a.userid = ?` + + if err := r.db.Raw(q1, userid).Scan(&data).Error; err != nil { + return models.RiderInfo{}, err + } + + return data, nil +} diff --git a/repositories/productRepository.go b/repositories/productRepository.go new file mode 100644 index 0000000..3318b56 --- /dev/null +++ b/repositories/productRepository.go @@ -0,0 +1,747 @@ +package repositories + +import ( + "fmt" + "strconv" + "strings" + "time" + + "nearle/models" + + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type ProductRepository interface { + GetProductSubCategory(categoryID, tenantID int) ([]models.ProductSubCategory, error) + GetProductCount(tenantID, categoryID, subcategoryID int, approve string) ([]models.Productcount, error) + GetProductCategory() ([]models.ProductCategory, error) + GetProductVariants(tenantID, subcategoryID int) ([]models.Productvariant, error) + GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Products, error) + GetProductStocks(tenantID, locationID string) ([]models.Productstocks, error) + CreateProductStock(stocks []models.Productstock) error + UpdateProductStatus(productIDs []int, status string) error + CreateProduct(product models.Products) error + UpdateProduct(product models.Products) error + DeleteProduct(productID int) error + GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Productstockstatement, error) + GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Locationproducts, error) + GetLocationProductSummary(tenantID, locationID int) ([]models.ProductSummary, error) + FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID int, keyword, productStatus, approve string, pageno, pagesize int) ([]models.Tenantproducts, error) + GetProductByVariant(tenantid, variantid int) ([]models.Products, error) + GetSubcategories(categoryID int) ([]models.Subcategory, error) + GetProducts(params models.ProductFilter) ([]models.Products, error) + GetTenantInfo(tenantID, applocationID int) (map[string]interface{}, error) + UpdateProductLocation(input models.Productlocations) error + CreateProductLocation(input []models.Productlocations) error + CreateProductVariant(input models.Productvariant) error +} + +type productRepository struct { + db *gorm.DB +} + +func NewProductRepository(db *gorm.DB) ProductRepository { + return &productRepository{db: db} +} + +func (r *productRepository) GetProductSubCategory(categoryID, tenantID int) ([]models.ProductSubCategory, error) { + var data []models.ProductSubCategory + var query strings.Builder + var args []interface{} + + query.WriteString("SELECT * FROM productsubcategories WHERE 1=1") + + if tenantID != 0 { + query.WriteString(" AND tenantid = ?") + args = append(args, tenantID) + } + if categoryID != 0 { + query.WriteString(" AND categoryid = ?") + args = append(args, categoryID) + } + + if err := r.db.Raw(query.String(), args...).Scan(&data).Error; err != nil { + return nil, err + } + + // print() + return data, nil +} + +func (r *productRepository) GetProductCount(tenantid, categoryid, subcategory int, approve string) ([]models.Productcount, error) { + var data []models.Productcount + + baseQuery := ` + SELECT + COUNT(*) AS total, + SUM(CASE WHEN a.productstatus = 'available' THEN 1 ELSE 0 END) AS available, + SUM(CASE WHEN a.productstatus = 'outofstock' THEN 1 ELSE 0 END) AS outofstock + FROM products a + WHERE 1 = 1 + ` + + var conditions []string + var params []interface{} + + if tenantid != 0 { + conditions = append(conditions, "a.tenantid = ?") + params = append(params, tenantid) + } + if categoryid != 0 { + conditions = append(conditions, "a.categoryid = ?") + params = append(params, categoryid) + } + if subcategory != 0 { + conditions = append(conditions, "a.subcategoryid = ?") + params = append(params, subcategory) + } + if approve != "" { + conditions = append(conditions, "a.approve = ?") + params = append(params, approve) + } + + if len(conditions) > 0 { + baseQuery += " AND " + strings.Join(conditions, " AND ") + } + + if err := r.db.Raw(baseQuery, params...).Scan(&data).Error; err != nil { + return nil, err + } + + print(baseQuery) + + return data, nil +} + +func (r *productRepository) GetProductCategory() ([]models.ProductCategory, error) { + var data []models.ProductCategory + q1 := `SELECT * FROM productcategories WHERE moduleid = 2 AND status = 'Active'` + r.db.Raw(q1).Scan(&data) + print(q1) + return data, nil +} + +func (r *productRepository) GetProductVariants(tenantID int, subcategoryID int) ([]models.Productvariant, error) { + + var data []models.Productvariant + + var query string + var params []interface{} + + query = ` + SELECT a.*, b.categoryname + FROM productvariants a + JOIN app_category b ON a.categoryid = b.categoryid + WHERE a.tenantid = ? + ` + params = append(params, tenantID) + + if subcategoryID != 0 { + query += " AND a.subcategoryid = ?" + params = append(params, subcategoryID) + } + + r.db.Raw(query, params...).Scan(&data) + + //print(query) + + return data, nil +} + +func (r *productRepository) GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Products, error) { + var data []models.Products + + if pageno < 1 { + pageno = 1 + } + if pagesize < 1 { + pagesize = 10 + } + + offset := (pageno - 1) * pagesize + params := []interface{}{locationID, tenantID} + + // Base query + query := ` + SELECT a.* + FROM products a + LEFT JOIN productlocations b + ON a.productid = b.productid + AND b.locationid = ? + AND b.tenantid = a.tenantid + WHERE a.approve = 1 + AND a.tenantid = ? + AND b.productid IS NULL + ` + + // Optional filters + if subcategoryID != 0 { + query += " AND a.subcategoryid = ?" + params = append(params, subcategoryID) + } + + if keyword != "" { + query += " AND LOWER(a.productname) LIKE ?" + params = append(params, "%"+strings.ToLower(keyword)+"%") + } + + // Pagination + query += " ORDER BY a.productid DESC LIMIT " + strconv.Itoa(pagesize) + " OFFSET " + strconv.Itoa(offset) + + // Debug logs + fmt.Println("Executing query:", query) + fmt.Println("Params:", params) + + // Execute query + if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *productRepository) GetProductStocks(tenantID, locationID string) ([]models.Productstocks, error) { + var stocks []models.Productstocks + var params []interface{} + var conditions []string + + query := ` + SELECT + a.productid, a.tenantid, MAX(a.stockdate) AS stockdate, a.locationid, a.stocktype, a.maxquantity, a.minquantity, a.status, + b.applocationid, b.categoryid, b.subcategoryid, b.catalogueid, b.addonid, b.discountid, b.pricingid, + b.productname, b.productimage, b.productdesc, b.productsku, b.brandid, b.productbrand, b.productunit, + b.unitvalue, b.toppicks, b.productcost, b.taxamount, b.taxpercent, b.producttax, b.productstock, + b.productcombo, b.variants, b.retailprice, b.diffprice, b.diffpercent, b.othercost, b.approve, + b.productstatus, b.created, b.updated, c.subcatname AS subcategoryname, + SUM(CASE WHEN a.stocktype = 'in' THEN a.quantity ELSE 0 END) - + SUM(CASE WHEN a.stocktype = 'out' THEN a.quantity ELSE 0 END) AS quantity + FROM productstocks a + JOIN products b ON a.productid = b.productid + INNER JOIN productsubcategories c ON c.subcatid = b.subcategoryid + ` + + if tenantID != "" { + conditions = append(conditions, "a.tenantid = ?") + params = append(params, tenantID) + } + + if locationID != "" { + conditions = append(conditions, "a.locationid = ?") + params = append(params, locationID) + } + + if len(conditions) > 0 { + query += " WHERE " + strings.Join(conditions, " AND ") + } + + query += " GROUP BY a.productid" + + if err := r.db.Raw(query, params...).Scan(&stocks).Error; err != nil { + return nil, err + } + + return stocks, nil +} + +func (r *productRepository) CreateProductStock(stocks []models.Productstock) error { + return r.db.Table("productstocks").Create(&stocks).Error +} + +func (r *productRepository) UpdateProductStatus(productIDs []int, status string) error { + return r.db.Table("products"). + Where("productid IN ?", productIDs). + Update("productstatus", status).Error +} + +func (r *productRepository) CreateProduct(product models.Products) error { + tx := r.db.Begin() + + if err := tx.Create(&product).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *productRepository) UpdateProduct(product models.Products) error { + tx := r.db.Begin() + + if err := tx.Table("productlocations"). + Where("productid = ?", product.Productid). + Select("status"). // only update 'approve' field + Updates(product).Error; err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} + +func (r *productRepository) DeleteProduct(productID int) error { + tx := r.db.Begin() + + if err := tx.Table("products").Where("productid = ?", productID).Delete(&models.Products{}).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *productRepository) GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Productstockstatement, error) { + data := make([]models.Productstockstatement, 0) + + if pageno < 1 { + pageno = 1 + } + if pagesize < 1 { + pagesize = 10 + } + + offset := (pageno - 1) * pagesize + + params := []interface{}{tenantID, locationID} + + query := `SELECT a.productid,a.productname,a.productimage,a.categoryid,a.subcategoryid,a.productunit,a.unitvalue,a.productcost,a.taxpercent,a.taxamount,a.retailprice,b.tenantid,b.locationid, + COALESCE( SUM(CASE WHEN UPPER(c.stocktype) = 'IN' AND c.stockdate::date <= CURRENT_DATE THEN c.quantity ELSE 0 END) - + SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' AND c.stockdate::date <= CURRENT_DATE THEN c.quantity ELSE 0 END),0 ) + AS opening, + COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'IN' AND c.stockdate::date = CURRENT_DATE THEN c.quantity ELSE 0 END), 0) AS credit, + COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' AND c.stockdate::date = CURRENT_DATE THEN c.quantity ELSE 0 END), 0) AS debit, + COALESCE( + ( SUM(CASE WHEN UPPER(c.stocktype) = 'IN' AND c.stockdate::date < CURRENT_DATE THEN c.quantity ELSE 0 END) - + SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' AND c.stockdate::date < CURRENT_DATE THEN c.quantity ELSE 0 END) + ) + + SUM(CASE WHEN UPPER(c.stocktype) = 'IN' AND c.stockdate::date = CURRENT_DATE THEN c.quantity ELSE 0 END) - + SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' AND c.stockdate::date = CURRENT_DATE THEN c.quantity ELSE 0 END), + 0 + ) AS closing + FROM products a + JOIN productlocations b ON a.productid = b.productid AND a.tenantid = b.tenantid + LEFT JOIN productstocks c ON a.productid = c.productid AND b.locationid = c.locationid AND b.tenantid = c.tenantid + WHERE b.tenantid = ? AND b.locationid = ?` + + if subcategoryID != 0 { + query += " AND a.subcategoryid = ?" + params = append(params, subcategoryID) + } + + if keyword != "" { + query += " AND (CAST(a.productid AS TEXT) LIKE ? OR LOWER(a.productname) LIKE ?)" + likeParam := "%" + strings.ToLower(keyword) + "%" + params = append(params, likeParam, likeParam) + } + + query += ` + GROUP BY + a.productid, a.productname, a.productimage, + a.categoryid, a.subcategoryid, a.productunit, + a.productcost, a.taxpercent, a.taxamount, + a.retailprice, b.tenantid, b.locationid + ORDER BY a.productid DESC LIMIT ` + strconv.Itoa(pagesize) + ` OFFSET ` + strconv.Itoa(offset) + + if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil { + return nil, err + } + + print(query) + + return data, nil +} + +func (r *productRepository) GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Locationproducts, error) { + var data []models.Locationproducts + + if pageno < 1 { + pageno = 1 + } + if pagesize < 1 { + pagesize = 10 + } + offset := (pageno - 1) * pagesize + + params := []interface{}{tenantID, locationID} + + query := `SELECT a.*, b.productlocationid, b.status, + COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'IN' THEN c.quantity ELSE 0 END), 0) AS total_in, + COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' THEN c.quantity ELSE 0 END), 0) AS total_out, + COALESCE(SUM(CASE WHEN UPPER(c.stocktype) = 'IN' THEN c.quantity ELSE 0 END) - + SUM(CASE WHEN UPPER(c.stocktype) = 'OUT' THEN c.quantity ELSE 0 END), 0) AS productstock + FROM products a + INNER JOIN productlocations b ON a.productid = b.productid AND a.tenantid = b.tenantid + LEFT JOIN productstocks c ON a.productid = c.productid AND b.locationid = c.locationid AND a.tenantid = c.tenantid + WHERE a.approve=1 AND a.tenantid = ? AND b.locationid = ?` + + if subcategoryID != 0 { + query += " AND a.subcategoryid = ?" + params = append(params, subcategoryID) + } + + if keyword != "" { + query += " AND LOWER(a.productname) LIKE ?" + params = append(params, "%"+strings.ToLower(keyword)+"%") + } + + query += ` GROUP BY a.productid, a.productname, a.productimage, a.categoryid, a.subcategoryid, + a.productunit, a.productcost, a.taxpercent, a.taxamount, a.retailprice, + b.tenantid, b.locationid, b.productlocationid, b.status + ORDER BY a.productid DESC LIMIT ? OFFSET ?` + + params = append(params, pagesize, offset) + + if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil { + return nil, err + } + + print(query) + + return data, nil +} + +func (r *productRepository) GetLocationProductSummary(tenantID, locationID int) ([]models.ProductSummary, error) { + data := make([]models.ProductSummary, 0) + + query := ` + SELECT + a.subcatid AS subcategoryid, + a.subcatname AS subcategroyname, + a.image, + COUNT(DISTINCT c.productid) AS productcount + FROM productsubcategories a + LEFT JOIN products b + ON a.subcatid = b.subcategoryid + AND b.approve = 1 + AND b.tenantid = ? + LEFT JOIN productlocations c + ON b.productid = c.productid + AND c.tenantid = ? + AND c.locationid = ? + WHERE a.categoryid = 2 + GROUP BY a.subcatid, a.subcatname, a.image + ORDER BY a.subcatid; +` + + // Only 3 params: tenantID for products, tenantID for locations, locationID + params := []interface{}{tenantID, tenantID, locationID} + + if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil { + return nil, err + } + + // Correct "All" count: sum of only products that exist in this location + total := 0 + for _, d := range data { + total += d.Productcount + } + + all := models.ProductSummary{ + Subcategoryid: 0, + Subcategroyname: "All", + Productcount: total, + } + data = append([]models.ProductSummary{all}, data...) + + return data, nil +} + +func (r *productRepository) FetchFilteredProducts( + categoryID, subcategoryID, productID, applocationID, tenantID, + locationID int, keyword, productStatus, approve string, pageno, pagesize int, +) ([]models.Tenantproducts, error) { + + offset := (pageno - 1) * pagesize + results := make([]models.Tenantproducts, 0) + + if tenantID == 0 { + return results, nil + } + + // Fetch tenant info + var tenant models.TenantInfo + if err := r.db.Table("tenants").Where("tenantid = ?", tenantID).First(&tenant).Error; err != nil { + return nil, err + } + + // Build product query + var products []models.Products + + query := r.db. + Table("products a"). + Select(` + a.*, + b.status, + c.categoryname, + d.subcatname AS subcategoryname, + ps.locationid, + COALESCE(ps.quantity, 0) AS quantity + `). + Joins("LEFT JOIN productlocations b ON a.productid = b.productid"). + Joins("LEFT JOIN productcategories c ON a.categoryid = c.categoryid"). + Joins("LEFT JOIN productsubcategories d ON a.subcategoryid = d.subcatid"). + Joins(` + LEFT JOIN ( + SELECT + productid, + locationid, + SUM(CASE WHEN stocktype = 'in' THEN quantity ELSE 0 END) - + SUM(CASE WHEN stocktype = 'out' THEN quantity ELSE 0 END) AS quantity + FROM productstocks + GROUP BY productid, locationid + ) ps ON ps.productid = a.productid + `). + Where("a.tenantid = ?", tenantID). + Order("a.productid DESC") + + if categoryID != 0 { + query = query.Where("a.categoryid = ?", categoryID) + } + if subcategoryID != 0 { + query = query.Where("a.subcategoryid = ?", subcategoryID) + } + if productID != 0 { + query = query.Where("a.productid = ?", productID) + } + if productStatus != "" { + query = query.Where("a.productstatus = ?", productStatus) + } + if locationID != 0 { + query = query.Where("e.locationid = ?", locationID) + } + if approve != "" { + query = query.Where("a.approve = ?", approve) + } + + if keyword != "" { + like := "%" + strings.ToLower(keyword) + "%" + query = query.Where( + r.db.Where("LOWER(a.productname) LIKE ?", like). + Or("LOWER(a.unitvalue) LIKE ?", like). + Or("LOWER(CAST(a.productcost AS TEXT)) LIKE ?", like), + ) + } + + if pagesize > 0 && offset >= 0 { + query = query.Limit(pagesize).Offset(offset) + } + + if err := query.Scan(&products).Error; err != nil { + return nil, err + } + + if products == nil { + products = []models.Products{} + } + + results = append(results, models.Tenantproducts{ + Tenant: tenant, + Products: products, + }) + + print(query) + + return results, nil +} + +func (r *productRepository) GetProductByVariant(tenantid, variantid int) ([]models.Products, error) { + + var data []models.Products + + err := r.db. + Table("products p"). + Select(` + p.*, + c.categoryname, + d.subcatname AS subcategoryname, + COALESCE(pd.discountvalue, 0) AS discountvalue, + pd.discountid + `). + Joins("LEFT JOIN productcategories c ON p.categoryid = c.categoryid"). + Joins("LEFT JOIN productsubcategories d ON p.subcategoryid = d.subcatid"). + Joins("LEFT JOIN productdiscounts pd ON pd.productid = p.productid"). + Where("p.tenantid = ? AND p.variants = ?", tenantid, variantid). + Order("p.productid DESC"). + Scan(&data).Error + + if err != nil { + return nil, err + } + + return data, nil +} + +func (r *productRepository) GetSubcategories(categoryID int) ([]models.Subcategory, error) { + var subcats []models.Subcategory + err := r.db.Table("productsubcategories"). + Where("categoryid = ?", categoryID). + Find(&subcats).Error + return subcats, err +} + +func (r *productRepository) GetProducts(params models.ProductFilter) ([]models.Products, error) { + var products []models.Products + + q := r.db.Table("products a"). + Joins("LEFT JOIN productlocations pl ON pl.productid = a.productid"). + Joins("LEFT JOIN productdiscounts pd ON pd.productid = a.productid"). + Joins("LEFT JOIN productcategories c ON a.categoryid = c.categoryid"). + Where("a.categoryid = ?", params.CategoryID) + + if params.TenantID > 0 { + q = q.Where("a.tenantid = ?", params.TenantID) + } + if params.LocationID > 0 { + q = q.Where("pl.locationid = ?", params.LocationID) + } + if params.AppLocationID > 0 { + q = q.Where("a.applocationid = ?", params.AppLocationID) + } + if params.ProductID > 0 { + q = q.Where("a.productid = ?", params.ProductID) + } + if params.Keyword != "" { + like := "%" + strings.ToLower(params.Keyword) + "%" + q = q.Where( + r.db.Where("LOWER(a.productname) LIKE ?", like). + Or("LOWER(a.unitvalue) LIKE ?", like). + Or("LOWER(CAST(a.productcost AS TEXT)) LIKE ?", like), + ) + } + + err := q.Select(` + a.*, + COALESCE(pd.discountvalue, 0) AS discountvalue + `).Find(&products).Error + + return products, err +} + +func (r *productRepository) GetTenantInfo(tenantID, applocationID int) (map[string]interface{}, error) { + var tenant struct { + Tenantname string + Address string + Licenseno string + Primaryemail string + Primarycontact string + Locationname string + Pickuplocationid int + Suburb string + City string + Latitude string + Longitude string + Postcode string + } + + err := r.db.Raw(` + SELECT t.tenantname, t.address, t.licenseno, t.primaryemail, t.primarycontact, + l.locationid AS pickuplocationid, l.suburb, l.city, l.latitude, l.longitude, l.postcode, + a.locationname + FROM tenants t + LEFT JOIN tenantlocations l ON t.tenantid = l.tenantid + LEFT JOIN app_location a ON l.applocationid = a.applocationid + WHERE t.tenantid = ? AND t.applocationid = ? + LIMIT 1 + `, tenantID, applocationID).Scan(&tenant).Error + + if err != nil { + return nil, err + } + + return map[string]interface{}{ + "tenantname": tenant.Tenantname, + "address": tenant.Address, + "licenseno": tenant.Licenseno, + "primaryemail": tenant.Primaryemail, + "primarycontact": tenant.Primarycontact, + "locationname": tenant.Locationname, + "pickuplocationid": tenant.Pickuplocationid, + "suburb": tenant.Suburb, + "city": tenant.City, + "pickuplat": tenant.Latitude, + "pickuplong": tenant.Longitude, + "postcode": tenant.Postcode, + }, nil +} + +func (r *productRepository) UpdateProductLocation(input models.Productlocations) error { + tx := r.db.Begin() + + t1 := tx.Where("productlocationid = ?", input.Productlocationid).Updates(&input) + if t1.Error != nil { + tx.Rollback() + return t1.Error + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *productRepository) CreateProductLocation(input []models.Productlocations) error { + var stk []models.Productstock + tx := r.db.Begin() + + // Insert or update product location + if err := tx.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "tenantid"}, {Name: "locationid"}, {Name: "productid"}}, + DoUpdates: clause.AssignmentColumns([]string{"price", "minquantity", "maxquantity"}), + }).Create(&input).Error; err != nil { + tx.Rollback() + return err + } + + // Prepare product stock entries + for _, loc := range input { + if loc.Quantity > 0 { + stk = append(stk, models.Productstock{ + Tenantid: loc.Tenantid, + Stockdate: time.Now(), + Locationid: loc.Locationid, + Productid: loc.Productid, + Quantity: loc.Quantity, + Stocktype: loc.Stocktype, + }) + } + } + + // Insert stock records if available + if len(stk) > 0 { + if err := tx.Create(&stk).Error; err != nil { + tx.Rollback() + return err + } + } + + // Commit transaction + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *productRepository) CreateProductVariant(input models.Productvariant) error { + tx := r.db.Begin() + + if err := tx.Create(&input).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} diff --git a/repositories/tenantRepository.go b/repositories/tenantRepository.go new file mode 100644 index 0000000..2f6a86a --- /dev/null +++ b/repositories/tenantRepository.go @@ -0,0 +1,737 @@ +package repositories + +import ( + "errors" + "fmt" + "nearle/models" + "strconv" + "strings" + + "github.com/jinzhu/copier" + "gorm.io/gorm" +) + +type TenantRepository interface { + SearchTenant(status, searchstr string) ([]models.Tenantinfo, error) + GetAllTenants(pageno, pagesize, aid int, status, tenanttype, keyword string) ([]models.Tenantinfo, error) + GetTenantLocations(tid int) ([]models.Tenantlocations, error) + GetTenantSlot() (models.Tenantslot, error) + CreateTenantCustomer(customer models.Tenantcustomer) (*models.Tenantcustomer, error) + GetCustomerTenants(customerID int, categoryID int, tenantFlag int) ([]models.TenantInfo, error) + GetTenantPricing(tid, aid int) (*models.Tenantpricing, error) + UpdateLocation(input models.Tenantlocations) error + CreateLocation(data models.Tenantlocations) error + GetStaffs(tid int) ([]models.StaffInfo, error) + CreateStaff(user models.User) error + UpdateStaff(user models.User) error + CreateTenantLocation(data models.Tenantlocations) error + UpdateTenantLocation(data models.Tenantlocations) error + CheckTenantByNo(cno string) int + CreateTenantUser(data models.Tenants) (bool, error) + GetUserByNo(cno string) models.UserInfo + GetTenantByID(tid int, locationid int) (models.Tenantinfo, error) + GetTenantByKeyword(keyword string) ([]models.TenantSearch, error) +} + +type tenantRepository struct { + db *gorm.DB +} + +func NewTenantRepository(db *gorm.DB) TenantRepository { + return &tenantRepository{db: db} +} + +func (r *tenantRepository) SearchTenant(status, keyword string) ([]models.Tenantinfo, error) { + var data []models.Tenantinfo + var query string + + searchStr := strings.ToLower(keyword) + + if strings.ToLower(status) != "pending" { + query = ` + SELECT a.*, b.subcategoryname, c.firstname, c.lastname, + CONCAT(c.firstname, ' ', c.lastname) AS accountname + FROM tenants a + INNER JOIN app_subcategory b ON a.subcategoryid = b.subcategoryid + LEFT JOIN app_users c ON c.userid = a.partneruserid + WHERE a.approved = 1 + AND LOWER(a.status) = ? + AND LOWER(a.tenantname) LIKE ? + ` + r.db.Raw(query, strings.ToLower(status), searchStr+"%").Scan(&data) + } else { + query = ` + SELECT a.*, b.subcategoryname, c.firstname, c.lastname, + CONCAT(c.firstname, ' ', c.lastname) AS accountname + FROM tenants a + INNER JOIN app_subcategory b ON a.subcategoryid = b.subcategoryid + LEFT JOIN app_users c ON c.userid = a.partneruserid + WHERE a.approved = 0 + AND LOWER(a.tenantname) LIKE ? + ` + r.db.Raw(query, searchStr+"%").Scan(&data) + } + + return data, nil +} + +func (r *tenantRepository) GetAllTenants(pageno, pagesize, aid int, status, tenanttype, keyword string) ([]models.Tenantinfo, error) { + offset := (pageno - 1) * pagesize + + var data []models.Tenantinfo + + base := `SELECT * FROM tenants a WHERE 1 = 1` + + var ( + conds []string + params []interface{} + ) + + switch strings.ToLower(status) { + case "active": + conds = append(conds, "a.approved = 1 AND a.status = 'Active'") + case "inactive": + conds = append(conds, "a.approved = 1 AND a.status = 'InActive'") + case "pending": + conds = append(conds, "a.approved = 0") + } + + if aid != 0 { + conds = append(conds, "a.applocationid = ?") + params = append(params, aid) + } + + if tenanttype != "" { + conds = append(conds, "a.tenanttype = ?") + params = append(params, tenanttype) + } + + if keyword != "" { + + kw := "%" + strings.ToLower(keyword) + "%" + + conds = append(conds, + "(LOWER(a.tenantname) LIKE ? OR LOWER(a.primarycontact) LIKE ?)") + + params = append(params, kw, kw) + } + + if len(conds) > 0 { + base += " AND " + strings.Join(conds, " AND ") + } + + base += " ORDER BY a.tenantid DESC LIMIT ? OFFSET ?" + params = append(params, pagesize, offset) + + err := r.db.Raw(base, params...).Scan(&data).Error + if err != nil { + return nil, err + } + + return data, nil +} + +func (r *tenantRepository) GetTenantLocations(tid int) ([]models.Tenantlocations, error) { + var data []models.Tenantlocations + + q1 := `SELECT a.*, b.roleid FROM tenantlocations a + LEFT JOIN app_users b ON a.locationid = b.locationid + AND a.tenantid = b.tenantid + WHERE a.tenantid = ?` + + if err := r.db.Raw(q1, tid).Find(&data).Error; err != nil { + return nil, err + } + + print(q1) + + return data, nil +} + +func (r *tenantRepository) GetTenantSlot() (models.Tenantslot, error) { + var data models.Tenantslot + + err := r.db.Raw(`SELECT * FROM tenantslot`).Find(&data).Error + + if err != nil { + return models.Tenantslot{}, err + } + + return data, nil +} + +func (r *tenantRepository) CreateTenantCustomer(customer models.Tenantcustomer) (*models.Tenantcustomer, error) { + var existing models.Tenantcustomer + + // 🔍 Step 1: Check if a record already exists with same customerid and locationid + err := r.db. + Where("customerid = ? AND locationid = ?", customer.CustomerID, customer.LocationID). + First(&existing).Error + + // If record found, prevent insertion + if err == nil { + return nil, fmt.Errorf("customer already exists for this location") + } + + // If error other than record not found, return it + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + // ✅ Step 2: Insert new record if no duplicate found + if err := r.db.Create(&customer).Error; err != nil { + return nil, err + } + + return &customer, nil +} + +func (r *tenantRepository) GetCustomerTenants(customerID int, categoryID int, tenantFlag int) ([]models.TenantInfo, error) { + var tenants []models.TenantInfo + + query := ` + SELECT a.customerid, a.locationid, b.tenantid,b.tenantname,b.address,b.licenseno, + b.primaryemail,b.primarycontact,b.applocationid,b.suburb,b.city, + b.latitude,b.longitude,b.postcode,b.tenantimage,b.subcategoryid, + b.categoryid,b.registrationno,d.userfcmtoken,c.locationname, + COALESCE(o.orderscount, 0) AS orderscount + FROM tenantcustomers a + LEFT JOIN tenants b ON a.tenantid = b.tenantid + INNER JOIN tenantlocations c ON a.locationid = c.locationid + LEFT JOIN ( + SELECT tenantid, customerid, COUNT(*) AS orderscount + FROM orders + GROUP BY tenantid, customerid + ) o ON b.tenantid = o.tenantid AND o.customerid = a.customerid + LEFT JOIN ( + SELECT locationid, + MAX(userfcmtoken) AS userfcmtoken + FROM app_users + GROUP BY locationid + ) d ON d.locationid = a.locationid + WHERE a.customerid = ? + AND b.tenantid IS NOT NULL + ` + + args := []interface{}{customerID} + + if categoryID != 0 { + query += " AND b.categoryid = ?" + args = append(args, categoryID) + } + + if tenantFlag == 1 { + query += " AND COALESCE(o.orderscount,0) > 0" + } + + if err := r.db.Raw(query, args...).Scan(&tenants).Error; err != nil { + return nil, err + } + + if tenants == nil { + return []models.TenantInfo{}, nil + } + + // Attach top 5 subcategories + if len(tenants) > 0 { + var subcategories []models.ProductSubcategory + if err := r.db.Table("productsubcategories"). + Order("subcatid ASC"). + Limit(5). + Find(&subcategories).Error; err != nil { + return nil, err + } + + for i := range tenants { + tenants[i].ProductSubcategory = subcategories + } + } + + print(tenants) + + return tenants, nil +} + +func (r *tenantRepository) GetTenantPricing(tid, aid int) (*models.Tenantpricing, error) { + var data models.Tenantpricing + var q1 string + + if tid != 0 { + q1 = `SELECT * + FROM tenantpricing + WHERE pricingdate = ( + SELECT MAX(pricingdate) + FROM tenantpricing + WHERE tenantid=` + strconv.Itoa(tid) + ` + ) + AND tenantid=? + ORDER BY tenantpricingid DESC` + } + + if err := r.db.Raw(q1, tid).Find(&data).Error; err != nil { + return nil, err + } + + return &data, nil +} + +func (r *tenantRepository) UpdateLocation(input models.Tenantlocations) error { + tx := r.db.Begin() + + if err := tx.Where("locationid=?", input.Locationid).Updates(&input).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error { + var cust models.Customers + var tcust models.Tenantcustomers + var custloc models.Customerlocations + + tx := r.db.Begin() + + if err := tx.Create(&data).Error; err != nil { + tx.Rollback() + return err + } + + cust.Firstname = data.Locationname + cust.Email = data.Email + cust.Contactno = data.Contactno + cust.Address = data.Address + cust.Suburb = data.Suburb + cust.City = data.City + cust.State = data.State + cust.Postcode = data.Postcode + cust.Applocationid = data.Applocationid + cust.Latitude = data.Latitude + cust.Longitude = data.Longitude + cust.Primaryaddress = 0 + + if err := tx.Table("customers").Create(&cust).Error; err != nil { + tx.Rollback() + return err + } + + if err := copier.Copy(&custloc, &cust); err != nil { + tx.Rollback() + return err + } + + if err := tx.Table("customerlocations").Create(&custloc).Error; err != nil { + tx.Rollback() + return err + } + + tcust.Customerid = cust.Customerid + tcust.Tenantid = data.Tenantid + tcust.Locationid = data.Locationid + tcust.Moduleid = data.Moduleid + + if err := tx.Table("tenantcustomers").Create(&tcust).Error; err != nil { + tx.Rollback() + return err + } + + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *tenantRepository) GetStaffs(tid int) ([]models.StaffInfo, error) { + var data []models.StaffInfo + + q1 := `SELECT a.userid,a.firstname,a.lastname, + CONCAT(a.firstname,' ',a.lastname) AS fullname, + a.email,a.contactno,a.address,a.suburb,a.city, + a.state,a.postcode,a.userfcmtoken,a.pin,a.applocationid, + a.roleid,a.partnerid,a.tenantid,a.locationid, + b.locationname + FROM app_users a + INNER JOIN tenantlocations b ON a.locationid = b.locationid + WHERE a.tenantid = ?` + + if err := r.db.Raw(q1, tid).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} + +func (r *tenantRepository) CreateStaff(user models.User) error { + if err := r.db.Table("app_users").Create(&user).Error; err != nil { + return err + } + return nil +} + +func (r *tenantRepository) UpdateStaff(user models.User) error { + if err := r.db.Table("app_users").Where("userid = ?", user.Userid).Updates(&user).Error; err != nil { + return err + } + return nil +} + +func (r *tenantRepository) CreateTenantLocation(data models.Tenantlocations) error { + var user models.Tenantuser + tx := r.db.Begin() + + // Set status BEFORE insert + data.Status = "InActive" + + // Step 1: Insert into tenantlocations + if err := tx.Create(&data).Error; err != nil { + tx.Rollback() + return err + } + + // Step 2: Insert into app_users + user.Authname = data.Email + user.Firstname = data.Locationname + user.Email = data.Email + user.Contactno = data.Contactno + user.Address = data.Address + user.Suburb = data.Suburb + user.City = data.City + user.State = data.State + user.Postcode = data.Postcode + user.Partnerid = data.Partnerid + user.Tenantid = data.Tenantid + user.Locationid = data.Locationid + user.Applocationid = data.Applocationid + user.Configid = 1 + user.Status = "InActive" + user.Roleid = 0 + user.Authmode = 0 + user.Password = "" + user.Dialcode = "+91" + + if err := tx.Table("app_users").Create(&user).Error; err != nil { + tx.Rollback() + return err + } + + // Commit + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +func (r *tenantRepository) UpdateTenantLocation(input models.Tenantlocations) error { + tx := r.db.Begin() + + // ✅ Step 1: Prepare map for tenantlocations update + locationUpdate := make(map[string]interface{}) + + if input.Locationname != "" { + locationUpdate["locationname"] = input.Locationname + } + if input.Email != "" { + locationUpdate["email"] = input.Email + } + if input.Contactno != "" { + locationUpdate["contactno"] = input.Contactno + } + if input.Address != "" { + locationUpdate["address"] = input.Address + } + if input.Suburb != "" { + locationUpdate["suburb"] = input.Suburb + } + if input.City != "" { + locationUpdate["city"] = input.City + } + if input.State != "" { + locationUpdate["state"] = input.State + } + if input.Postcode != "" { + locationUpdate["postcode"] = input.Postcode + } + if input.Partnerid != 0 { + locationUpdate["partnerid"] = input.Partnerid + } + if input.Tenantid != 0 { + locationUpdate["tenantid"] = input.Tenantid + } + if input.Applocationid != 0 { + locationUpdate["applocationid"] = input.Applocationid + } + if input.Status != "" { + locationUpdate["status"] = input.Status + } + + // ✅ Step 2: Update tenantlocations (only provided fields) + if len(locationUpdate) > 0 { + if err := tx.Table("tenantlocations"). + Where("locationid = ?", input.Locationid). + Updates(locationUpdate).Error; err != nil { + tx.Rollback() + return err + } + } + + // ✅ Step 3: Prepare map for app_users update (only matching fields) + userUpdate := make(map[string]interface{}) + + if input.Locationname != "" { + userUpdate["firstname"] = input.Locationname + } + if input.Email != "" { + userUpdate["email"] = input.Email + } + if input.Contactno != "" { + userUpdate["contactno"] = input.Contactno + } + if input.Address != "" { + userUpdate["address"] = input.Address + } + if input.Suburb != "" { + userUpdate["suburb"] = input.Suburb + } + if input.City != "" { + userUpdate["city"] = input.City + } + if input.State != "" { + userUpdate["state"] = input.State + } + if input.Postcode != "" { + userUpdate["postcode"] = input.Postcode + } + if input.Tenantid != 0 { + userUpdate["tenantid"] = input.Tenantid + } + if input.Partnerid != 0 { + userUpdate["partnerid"] = input.Partnerid + } + if input.Applocationid != 0 { + userUpdate["applocationid"] = input.Applocationid + } + if input.Status != "" { + userUpdate["status"] = input.Status + } + // ✅ Step 4: Update app_users (only provided fields) + if len(userUpdate) > 0 { + if err := tx.Table("app_users"). + Where("locationid = ?", input.Locationid). + Updates(userUpdate).Error; err != nil { + tx.Rollback() + return err + } + } + + // ✅ Commit transaction + if err := tx.Commit().Error; err != nil { + return err + } + + return nil +} + +// ✅ Check if tenant exists +func (r *tenantRepository) CheckTenantByNo(cno string) int { + var id int + q1 := "SELECT tenantid FROM tenants WHERE primarycontact = '" + cno + `'` + r.db.Raw(q1).Find(&id) + return id +} + +// ✅ Create tenant + user + customer records +func (r *tenantRepository) CreateTenantUser(data models.Tenants) (bool, error) { + var seq models.Ordersequences + var user models.User + var cust models.Customers + var custloc models.Customerlocations + var tcust models.Tenantcustomers + + tx := r.db.Begin() + + // Step 1: Insert into tenants + if err := tx.Create(&data).Error; err != nil { + tx.Rollback() + return false, errors.New("error in tenant creation") + } + + // Step 2: Create order sequence + seq.Tenantid = data.Tenantid + if err := tx.Table("ordersequences").Create(&seq).Error; err != nil { + tx.Rollback() + return false, errors.New("error in sequence") + } + + // Step 3: Create app_user + if err := copier.Copy(&user, &data); err != nil { + tx.Rollback() + return false, err + } + user.Userfcmtoken = data.Tenanttoken + user.Contactno = data.Primarycontact + user.Email = data.Primaryemail + user.Authname = data.Primaryemail + user.Deviceid = data.Deviceid + user.Tenantid = data.Tenantid + user.Locationid = data.Tenantlocations.Locationid + user.Roleid = 1 + + if err := tx.Table("app_users").Create(&user).Error; err != nil { + tx.Rollback() + return false, errors.New("error in user creation") + } + + // Step 4: Create / Update customers + cust.Configid = data.Configid + cust.Firstname = data.Tenantname + cust.Email = data.Primaryemail + cust.Contactno = data.Primarycontact + cust.Deviceid = data.Deviceid + cust.Devicetype = data.Devicetype + cust.Customertoken = data.Tenanttoken + cust.Profileimage = data.Tenantimage + cust.Address = data.Address + cust.Suburb = data.Suburb + cust.City = data.City + cust.State = data.State + cust.Postcode = data.Postcode + cust.Applocationid = data.Applocationid + cust.Latitude = data.Latitude + cust.Longitude = data.Longitude + cust.Primaryaddress = 1 + + cid := r.CheckCustomer(data.Primarycontact) + + if cid == 0 { + if err := tx.Table("customers").Create(&cust).Error; err != nil { + tx.Rollback() + return false, errors.New("error in customer creation") + } + + if err := copier.Copy(&custloc, &cust); err != nil { + tx.Rollback() + return false, err + } + + if err := tx.Table("customerlocations").Create(&custloc).Error; err != nil { + tx.Rollback() + return false, errors.New("error in customer location") + } + } else { + if err := tx.Table("customers").Where("customerid=?", cid).Updates(&cust).Error; err != nil { + tx.Rollback() + return false, errors.New("error updating customer") + } + + if err := copier.Copy(&custloc, &cust); err != nil { + tx.Rollback() + return false, err + } + + if err := tx.Table("customerlocations").Where("customerid=?", cid).Updates(&custloc).Error; err != nil { + tx.Rollback() + return false, errors.New("error updating customer location") + } + } + + // Step 5: Create tenant-customer link + tcust.Customerid = cust.Customerid + tcust.Tenantid = data.Tenantid + tcust.Locationid = data.Tenantlocations.Locationid + + if err := tx.Table("tenantcustomers").Create(&tcust).Error; err != nil { + tx.Rollback() + return false, errors.New("error in tenant customer") + } + + // ✅ Commit transaction + if err := tx.Commit().Error; err != nil { + return false, errors.New("error in tenant creation") + } + + return true, nil +} + +// ✅ Check if customer exists +func (r *tenantRepository) CheckCustomer(cno string) int { + var id int + q := "SELECT customerid FROM customers WHERE contactno = '" + cno + `'` + r.db.Raw(q).Find(&id) + return id +} + +// ✅ Get user by contact number +func (r *tenantRepository) GetUserByNo(cno string) models.UserInfo { + var user models.UserInfo + + q1 := `SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno, + a.firstname,a.lastname,CONCAT(a.firstname,' ',a.lastname) AS fullname, + a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.locationid, + b.partnerid,b.moduleid,b.categoryid,b.subcategoryid, + b.applocationid,b.tenantname,b.address AS tenantaddress,b.state AS tenantstate,b.city AS tenantcity, + b.postcode AS tenantpostcode,b.latitude AS tenantlat,b.longitude AS tenantlong + FROM app_users a + LEFT JOIN tenants b ON a.tenantid = b.tenantid + WHERE a.contactno = '` + cno + `'` + + r.db.Raw(q1).Find(&user) + return user +} + +func (r *tenantRepository) GetTenantByID(tid int, locationid int) (models.Tenantinfo, error) { + var data models.Tenantinfo + + q1 := ` + SELECT a.*,b.categoryname,c.locationname AS applocation, d.allocationid AS allocationmode,e.typename AS allocationtype,e.mapid AS allocationid,f.locationid, + f.locationname, f.contactno as locationcontact + FROM tenants a + INNER JOIN app_category b ON a.categoryid = b.categoryid + INNER JOIN app_location c ON a.applocationid = c.applocationid + LEFT JOIN partnerinfo d ON a.partnerid = d.partnerid + LEFT JOIN app_types e ON d.allocationid = e.apptypeid + LEFT JOIN tenantlocations f ON a.tenantid = f.tenantid + WHERE a.tenantid = ? + ` + + var args []interface{} + args = append(args, tid) + + if locationid != 0 { + q1 += " AND f.locationid = ?" + args = append(args, locationid) + } + + if err := r.db.Raw(q1, args...).Find(&data).Error; err != nil { + return data, err + } + + return data, nil +} + +func (r *tenantRepository) GetTenantByKeyword(keyword string) ([]models.TenantSearch, error) { + var data []models.TenantSearch + + kw := "%" + strings.ToLower(keyword) + "%" + + query := ` + SELECT a.tenantname, b.productname, c.subcatname + FROM tenants a + LEFT JOIN products b ON a.tenantid = b.tenantid + LEFT JOIN productsubcategories c ON b.subcategoryid = c.subcatid + WHERE c.categoryid = 2 + AND (LOWER(a.tenantname) LIKE ? OR LOWER(b.productname) LIKE ? OR LOWER(c.subcatname) LIKE ?) + ` + + if err := r.db.Raw(query, kw, kw, kw).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} diff --git a/repositories/userRepository.go b/repositories/userRepository.go new file mode 100644 index 0000000..6ae513a --- /dev/null +++ b/repositories/userRepository.go @@ -0,0 +1,279 @@ +package repositories + +import ( + "fmt" + "strings" + + "nearle/models" + + "gorm.io/gorm" +) + +type UserRepository interface { + GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error) + GetUserByID(uid int) (models.UserInfo, error) + Login(user models.User) (models.UserInfo, error) + FindUserID(authname, contactno string, configid int) (int, error) + GetTenantUserByID(userid int) (models.TenantUserInfo, error) + UpdateStaff(user models.User) error + GetUserByAuthname(authname string, configid int) (int, string, string) + GetUserByContactNo(contactno string, configid int) (int, string, string) + UpdateFCMToken(userid int, token string) error + GetTenantUserById(userid int) models.TenantUserInfo + CreateUser(user models.User) (int, error) + GetUserById(uid int) (models.UserInfo, error) + GetUserLogin(field, value string, configid int) (int, string, string, int) + UpdateUserFcmToken(uid int, token string) error +} + +type userRepository struct { + db *gorm.DB +} + +func NewUserRepository(db *gorm.DB) UserRepository { + return &userRepository{db: db} +} + +func (r *userRepository) GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error) { + var users []models.UserInfo + var params []interface{} + var queryBuilder strings.Builder + + offset := (pageno - 1) * pagesize + + queryBuilder.WriteString(`SELECT + a.userid, a.authname, a.email, a.configid, a.roleid, a.authmode, a.contactno, + a.firstname, a.lastname, CONCAT(a.firstname, ' ', a.lastname) AS fullname, + a.address, a.suburb, a.city, a.state, a.postcode, + a.userfcmtoken, a.pin, a.deviceid, a.devicetype, a.tenantid, a.status, a.shiftid, + a.applocationid, b.locationname AS applocation, b.latitude AS applatitude, concat(c.starttime, ' - ', c.endtime) as shiftname, + b.longitude AS applongitude, b.radius AS appradius + FROM app_users a + LEFT JOIN app_location b ON a.applocationid = b.applocationid + LEFT JOIN ridershifts c ON a.shiftid = c.shiftid + WHERE 1=1`) + + if roleID != 0 { + queryBuilder.WriteString(" AND a.roleid = ?") + params = append(params, roleID) + } + + if tenantID != 0 { + queryBuilder.WriteString(" AND a.tenantid = ?") + params = append(params, tenantID) + } + + if keyword != "" { + queryBuilder.WriteString(` AND ( + LOWER(a.firstname) LIKE ? OR + LOWER(a.contactno) LIKE ? OR + LOWER(a.suburb) LIKE ? + )`) + search := "%" + strings.ToLower(keyword) + "%" + params = append(params, search, search, search) + } + + queryBuilder.WriteString(" ORDER BY a.userid DESC LIMIT ? OFFSET ?") + params = append(params, pagesize, offset) + + print(queryBuilder.String()) + + if err := r.db.Raw(queryBuilder.String(), params...).Scan(&users).Error; err != nil { + return nil, err + } + return users, nil +} + +func (r *userRepository) GetUserByID(uid int) (models.UserInfo, error) { + var user models.UserInfo + + q := `SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno, + a.firstname,a.lastname,concat(a.firstname,' ',a.lastname) as fullname,a.password,a.address,a.suburb,a.city,a.state,a.postcode, + a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.shiftid,a.locationid, + a.applocationid,b.locationname as applocation,b.latitude as applatitude,b.longitude as applongitude, b.radius as appradius, + concat(c.starttime, ' - ', c.endtime) as shiftname + FROM app_users a + INNER JOIN app_location b ON a.applocationid = b.applocationid + LEFT JOIN ridershifts c ON a.shiftid = c.shiftid + WHERE a.userid = ?` + + if err := r.db.Raw(q, uid).Scan(&user).Error; err != nil { + return models.UserInfo{}, err + } + + return user, nil +} + +func (r *userRepository) Login(user models.User) (models.UserInfo, error) { + var uid int + var userInfo models.UserInfo + + var q string + if user.Authname != "" { + q = `SELECT a.userid FROM app_users a + WHERE a.authname = ? AND a.configid = ?` + if err := r.db.Raw(q, user.Authname, user.Configid).Scan(&uid).Error; err != nil { + return models.UserInfo{}, err + } + } else { + q = `SELECT a.userid FROM app_users a + WHERE a.contactno = ? AND a.configid = ?` + if err := r.db.Raw(q, user.Contactno, user.Configid).Scan(&uid).Error; err != nil { + return models.UserInfo{}, err + } + } + + if uid == 0 { + return models.UserInfo{}, gorm.ErrRecordNotFound + } + + // ✅ Update FCM token in app_users table if provided + if user.Userfcmtoken != "" { + if err := r.db.Table("app_users"). + Where("userid = ?", uid). + Update("userfcmtoken", user.Userfcmtoken).Error; err != nil { + return models.UserInfo{}, err + } + } + + userInfo, err := r.GetUserByID(uid) + if err != nil { + return models.UserInfo{}, err + } + + return userInfo, nil +} + + +func (r *userRepository) FindUserID(authname, contactno string, configid int) (int, error) { + var uid int + var query string + + if authname != "" { + query = `SELECT a.userid FROM app_users a WHERE a.authname = ? AND a.configid = ?` + if err := r.db.Raw(query, authname, configid).Scan(&uid).Error; err != nil { + return 0, err + } + } else { + query = `SELECT a.userid FROM app_users a WHERE a.contactno = ? AND a.configid = ?` + if err := r.db.Raw(query, contactno, configid).Scan(&uid).Error; err != nil { + return 0, err + } + } + return uid, nil +} + +func (r *userRepository) GetTenantUserByID(userid int) (models.TenantUserInfo, error) { + var info models.TenantUserInfo + query := `SELECT a.userid, a.authname, a.contactno, a.tenantid, t.tenantname + FROM app_users a + LEFT JOIN tenants t ON a.tenantid = t.tenantid + WHERE a.userid = ?` + + if err := r.db.Raw(query, userid).Scan(&info).Error; err != nil { + return info, err + } + return info, nil +} + +func (r *userRepository) UpdateStaff(user models.User) error { + return r.db.Table("app_users").Where("userid = ?", user.Userid).Updates(&user).Error +} + +func (r *userRepository) GetUserByAuthname(authname string, configid int) (int, string, string) { + var uid int + var password, status string + query := `SELECT userid, password, status FROM app_users WHERE authname = ? AND configid = ?` + r.db.Raw(query, authname, configid).Row().Scan(&uid, &password, &status) + return uid, password, status +} + +func (r *userRepository) GetUserByContactNo(contactno string, configid int) (int, string, string) { + var uid int + var password, status string + query := `SELECT userid, password, status FROM app_users WHERE contactno = ? AND configid = ?` + r.db.Raw(query, contactno, configid).Row().Scan(&uid, &password, &status) + return uid, password, status +} + +func (r *userRepository) UpdateFCMToken(userid int, token string) error { + query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?` + return r.db.Exec(query, token, userid).Error +} + +func (r *userRepository) GetTenantUserById(userid int) models.TenantUserInfo { + var info models.TenantUserInfo + + query := ` + SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno, + a.firstname,a.lastname,concat(a.firstname,' ',a.lastname) as fullname, + a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.locationid,a.applocationid, + b.partnerid,b.moduleid,b.categoryid as categoryid,b.subcategoryid as subcategoryid, + b.applocationid,b.tenantname,b.address as tenantaddress,b.state as tenantstate,b.city as tenantcity, + b.postcode as tenantpostcode,b.latitude as tenantlat,b.longitude as tenantlong,c.locationname AS applocation, + c.latitude as applatitude,c.longitude as applongitude,c.radius as appradius, d.categoryname, e.locationname + from app_users a + LEFT JOIN tenants b ON a.tenantid=b.tenantid + INNER JOIN app_location c on c.applocationid=b.applocationid + LEFT JOIN app_category d ON b.categoryid=d.categoryid + LEFT JOIN tenantlocations e ON a.locationid=e.locationid + WHERE a.userid = ? + ` + + r.db.Raw(query, userid).Scan(&info) + print(query) + return info +} + +func (r *userRepository) CreateUser(user models.User) (int, error) { + tx := r.db.Begin() + + if err := tx.Table("app_users").Create(&user).Error; err != nil { + tx.Rollback() + return 0, err + } + + if err := tx.Commit().Error; err != nil { + return 0, err + } + + return user.Userid, nil +} + +func (r *userRepository) GetUserById(uid int) (models.UserInfo, error) { + var user models.UserInfo + + q1 := `SELECT a.userid,a.authname,a.email,a.configid,a.roleid,a.authmode,a.contactno, + a.firstname,a.lastname,concat(a.firstname,' ',a.lastname) as fullname,a.password,a.address,a.suburb,a.city,a.state,a.postcode, + a.userfcmtoken,a.pin,a.deviceid,a.devicetype,a.tenantid,a.shiftid, + a.applocationid,b.locationname as applocation,b.latitude as applatitude,b.longitude as applongitude, b.radius as appradius , concat(c.starttime, ' - ', c.endtime) as shiftname + FROM app_users a + INNER JOIN app_location b on a.applocationid=b.applocationid + LEFT JOIN ridershifts c ON a.shiftid = c.shiftid + WHERE a.userid= ?` + + if err := r.db.Raw(q1, uid).Scan(&user).Error; err != nil { + return models.UserInfo{}, err + } + + return user, nil +} + +func (r *userRepository) GetUserLogin(field, value string, configid int) (int, string, string, int) { + var uid, roleid int + var password, status string + + query := fmt.Sprintf(` + SELECT userid, password, status, roleid + FROM app_users + WHERE %s = ? AND configid = ?`, field) + + r.db.Raw(query, value, configid).Row().Scan(&uid, &password, &status, &roleid) + + return uid, password, status, roleid +} + +func (r *userRepository) UpdateUserFcmToken(userid int, fcmToken string) error { + query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?` + return r.db.Exec(query, fcmToken, userid).Error +} diff --git a/repositories/utilsRepository.go b/repositories/utilsRepository.go new file mode 100644 index 0000000..a227e43 --- /dev/null +++ b/repositories/utilsRepository.go @@ -0,0 +1,203 @@ +package repositories + +import ( + "context" + "fmt" + "nearle/models" + "strconv" + + firebase "firebase.google.com/go" + "firebase.google.com/go/messaging" + "google.golang.org/api/option" + "gorm.io/gorm" +) + +type UtilsRepository interface { + GetApptypes(tag string) ([]models.Apptypes, error) + SendNotification(token string, notification models.FcmNotification, data map[string]string) error + GetSubcategories(moduleid int, categoryid int) ([]models.Appsubcategories, error) + GetApplocations(aid int) ([]models.Applocations, error) + GetApplocationConfig(aid int) ([]models.Applocations, error) + GetAppConfig(configID int) (models.Appconfig, error) + GetAppCategory() ([]models.AppCategory, error) +} + +type utilsRepository struct { + db *gorm.DB +} + +func NewUtilsRepository(db *gorm.DB) UtilsRepository { + return &utilsRepository{db: db} +} + +func (r *utilsRepository) GetApptypes(tag string) ([]models.Apptypes, error) { + + var data []models.Apptypes + + q1 := `Select * from app_types where status ='Active' and tag='` + tag + `'` + + print(q1) + + err := r.db.Raw(q1).Find(&data).Error + if err != nil { + return nil, err + } + + return data, nil + +} + +func (r *utilsRepository) SendNotification(token string, notification models.FcmNotification, data map[string]string) error { + + opt := option.WithCredentialsFile("nearle-gear-firebase-adminsdk-l9oha-23ca3b3609.json") + app, err := firebase.NewApp(context.Background(), nil, opt) + if err != nil { + return fmt.Errorf("error initializing Firebase app: %v", err) + } + + client, err := app.Messaging(context.Background()) + if err != nil { + return fmt.Errorf("error getting Messaging client: %v", err) + } + + message := &messaging.Message{ + Token: token, + Notification: &messaging.Notification{ + Title: notification.Title, + Body: notification.Body, + }, + Android: &messaging.AndroidConfig{ + Priority: "high", + Notification: &messaging.AndroidNotification{ + Sound: "ring", + }, + }, + Data: data, + } + + _, err = client.Send(context.Background(), message) + if err != nil { + return fmt.Errorf("error sending FCM message: %v", err) + } + + return nil +} + +func (r *utilsRepository) GetSubcategories(moduleid int, categoryid int) ([]models.Appsubcategories, error) { + 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) + } + + if err := r.db.Raw(query, params...).Scan(&data).Error; err != nil { + return nil, err + } + print(query) + return data, nil +} + +func (r *utilsRepository) GetApplocations(aid int) ([]models.Applocations, error) { + var data []models.Applocations + + q1 := `Select * from app_location where status='Active'` + if aid != 0 { + q1 += ` and applocationid = ?` + params := []interface{}{aid} + err := r.db.Raw(q1, params...).Scan(&data).Error + if err != nil { + return nil, err + } + } else { + err := r.db.Raw(q1).Scan(&data).Error + if err != nil { + return nil, err + } + } + print(q1) + return data, nil +} + +func (r *utilsRepository) GetApplocationConfig(aid int) ([]models.Applocations, error) { + var data []models.Applocations + var q1, q2 string + + if aid != 0 { + q1 = `SELECT * FROM app_location WHERE status='Active' AND applocationid=` + strconv.Itoa(aid) + } else { + q1 = `SELECT * FROM app_location WHERE status='Active'` + } + + if err := r.db.Raw(q1).Scan(&data).Error; err != nil { + return nil, err + } + + for i := range data { + q2 = ` + SELECT a.userid, a.userfcmtoken, b.notify, b.applocationid + FROM app_users a + INNER JOIN app_locationconfig b ON a.userid = b.userid + WHERE b.notify='true' AND b.applocationid=` + strconv.Itoa(data[i].Applocationid) + + var users []models.Applocationadmins + if err := r.db.Raw(q2).Scan(&users).Error; err != nil { + return nil, err + } + + data[i].Applocationadmins = users + } + print(q1) + return data, nil +} + +func (r *utilsRepository) GetAppConfig(configID int) (models.Appconfig, error) { + var data models.Appconfig + var q1 string + + if configID != 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=` + strconv.Itoa(configID) + } 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` + } + + fmt.Println(q1) + if err := r.db.Raw(q1).Find(&data).Error; err != nil { + return data, err + } + return data, nil +} + +func (r *utilsRepository) GetAppCategory() ([]models.AppCategory, error) { + var data []models.AppCategory + + query := `SELECT * FROM app_category WHERE status = 'Active' ORDER BY sortorder ASC` + + if err := r.db.Raw(query).Scan(&data).Error; err != nil { + return nil, err + } + + return data, nil +} diff --git a/routes/customerroutes.go b/routes/customerroutes.go new file mode 100644 index 0000000..8e60c2b --- /dev/null +++ b/routes/customerroutes.go @@ -0,0 +1,27 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterCustomerRoutes(api fiber.Router, f *facade.Facade) { + + customer := api.Group("/v1/mob/customers") + customer.Get("/getbyid", f.CustomerController.GetCustomer) + customer.Put("/update", f.CustomerController.UpdateCustomer) + customer.Get("/getcustomerlocation", f.CustomerController.GetCustomerLocations) + customer.Post("/createlocations", f.CustomerController.CreateCustomerLocation) + customer.Post("/createcustomerrequest", f.CustomerController.CreateCustomerRequest) + customer.Get("/getcustomerrequests", f.CustomerController.GetCustomerRequests) + customer.Get("/gettenantcustomers", f.CustomerController.GetTenantCustomers) + customer.Get("/search", f.CustomerController.SearchCustomer) + customer.Post("/login", f.CustomerController.CustomerLogin) + customer.Post("/create", f.CustomerController.CreateCustomer) + + + customer = api.Group("/v1/web/customers") + customer.Get("/gettenantcustomers", f.CustomerController.GetTenantCustomers) + +} diff --git a/routes/deliveriesroutes.go b/routes/deliveriesroutes.go new file mode 100644 index 0000000..c7036dc --- /dev/null +++ b/routes/deliveriesroutes.go @@ -0,0 +1,30 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterDeliveriesRoutes(api fiber.Router, f *facade.Facade) { + + deliveries := api.Group("/v1/web/deliveries") + + deliveries.Post("/createdeliveries", f.DeliveriesController.CreateDeliveries) + deliveries.Put("/updatedelivery", f.DeliveriesController.UpdateDelivery) + deliveries.Get("/deliverysummary", f.DeliveriesController.GetDeliverySummary) + deliveries.Get("/getdeliveryinsight", f.DeliveriesController.GetDeliveryInsight) + deliveries.Get("/getlocationsummary", f.DeliveriesController.GetLocationDeliverySummary) + deliveries.Get("/getreportsummary", f.DeliveriesController.GetReportSummary) + deliveries.Get("/getridersummary", f.DeliveriesController.GetRiderSummary) + deliveries.Get("/getdeliveries", f.DeliveriesController.GetDeliveries) + + deliveries = api.Group("/v1/mob/deliveries") + + deliveries.Get("/deliverysummary", f.DeliveriesController.GetDeliverySummary) + deliveries.Get("/getdeliveries", f.DeliveriesController.GetDeliveries) + deliveries.Get("/getdeliveryqueues", f.DeliveriesController.GetDeliveryQueues) + deliveries.Put("/updatedelivery", f.DeliveriesController.UpdateDelivery) + deliveries.Post("/createdeliveries", f.DeliveriesController.CreateDeliveries) + +} diff --git a/routes/orderroutes.go b/routes/orderroutes.go new file mode 100644 index 0000000..9e08bfe --- /dev/null +++ b/routes/orderroutes.go @@ -0,0 +1,34 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterOrderRoutes(api fiber.Router, f *facade.Facade) { + + orders := api.Group("/v1/web/orders") + + orders.Get("tenant/getorders", f.OrderController.GetOrders) + orders.Get("partner/getorders", f.OrderController.GetOrders) + orders.Get("customer/getorders", f.OrderController.GetOrders) + orders.Get("user/getorders", f.OrderController.GetOrders) + orders.Get("admin/getorders", f.OrderController.GetOrders) + orders.Get("/getorders", f.OrderController.GetOrders) + orders.Get("/getordersummary", f.OrderController.GetOrderSummary) + orders.Get("/getlocationsummary", f.OrderController.GetlocationOrderSummary) + orders.Get("/getorderinsight", f.OrderController.GetOrderInsights) + orders.Get("/getorderdetails", f.OrderController.GetOrderDetails) + orders.Put("/updateorder", f.OrderController.UpdateOrder) + orders.Post("/createorder", f.OrderController.CreateOrderv3) + + orders = api.Group("/v1/mob/orders") + + orders.Post("/createorder", f.OrderController.CreateOrderv3) + orders.Get("/getcustomerorders", f.OrderController.GetCustomerOrders) + orders.Get("tenant/getorders", f.OrderController.GetOrders) + orders.Put("/updateorder", f.OrderController.UpdateOrder) + orders.Get("/getorderdetails", f.OrderController.GetOrderDetails) + +} diff --git a/routes/partnerroutes.go b/routes/partnerroutes.go new file mode 100644 index 0000000..1c7530d --- /dev/null +++ b/routes/partnerroutes.go @@ -0,0 +1,26 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterPartnerRoutes(api fiber.Router, f *facade.Facade) { + + partner := api.Group("/v1/web/partners") + + partner.Get("/getriders", f.PartnerController.GetActiveRiders) + partner.Get("/getpartners", f.PartnerController.GetPartners) + partner.Get("/getridershifts", f.PartnerController.GetRiderShifts) + partner.Get("/getlocations", f.PartnerController.GetLocationConfig) + partner.Get("/getriderlogs", f.PartnerController.GetRiderLogs) + + partner = api.Group("/v1/mob/partners") + + partner.Get("/getpartners", f.PartnerController.GetPartners) + partner.Get("/getriderlogs", f.PartnerController.GetRiderLogs) + partner.Get("/getriderinfo", f.PartnerController.GetRiderInfo) + partner.Get("/getriders", f.PartnerController.GetActiveRiders) + +} diff --git a/routes/productroutes.go b/routes/productroutes.go new file mode 100644 index 0000000..cbeddea --- /dev/null +++ b/routes/productroutes.go @@ -0,0 +1,41 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterProductRoutes(api fiber.Router, f *facade.Facade) { + + products := api.Group("/v1/web/products") + + products.Get("/getproductsubcategories", f.ProductController.GetProductSubCategory) + products.Get("/getproductscount", f.ProductController.GetProductCount) + products.Get("/getproductcategories", f.ProductController.GetProductCategory) + products.Get("/getproductvariants", f.ProductController.GetProductVariants) + products.Get("/getcatalougeproducts", f.ProductController.GetCatalougeProducts) + products.Get("/getproductstocks", f.ProductController.GetProductStocks) + products.Post("/createproductstock", f.ProductController.CreateProductStock) + products.Post("/create", f.ProductController.CreateProduct) + products.Put("/update", f.ProductController.UpdateProduct) + products.Delete("/delete", f.ProductController.DeleteProduct) + products.Get("/getstockstatement", f.ProductController.GetStockStatement) + products.Get("/getlocationproducts", f.ProductController.GetLocationProducts) + products.Get("/getlocationproductsummary", f.ProductController.GetLocationProductSummary) + products.Get("/getallproducts", f.ProductController.GetAllProducts) + products.Put("/updateproductlocation", f.ProductController.UpdateProductLocation) + products.Post("/createproductlocation", f.ProductController.CreateProductLocation) + products.Post("/createproductvariant", f.ProductController.CreateProductVariant) + + products = api.Group("/v1/mob/products") + + products.Get("/getproductbyvariant", f.ProductController.GetProductByVariant) + products.Get("/getproductsubcategories", f.ProductController.GetProductSubCategory) + products.Get("/getallproducts", f.ProductController.GetAllProducts) + products.Get("/getproductsbysubcategory", f.ProductController.GetProductsBySubcategory) + products.Put("/update", f.ProductController.UpdateProduct) + products.Get("/getlocationproducts", f.ProductController.GetLocationProducts) + products.Put("/updateproductlocation", f.ProductController.UpdateProductLocation) + +} diff --git a/routes/routes.go b/routes/routes.go new file mode 100644 index 0000000..04ab3a5 --- /dev/null +++ b/routes/routes.go @@ -0,0 +1,21 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterRoutes(app *fiber.App, f *facade.Facade) { + + api := app.Group("/live/api") + + RegisterUserRoutes(api, f) + RegisterProductRoutes(api, f) + RegisterOrderRoutes(api, f) + RegisterDeliveriesRoutes(api, f) + RegisterUtilsRoutes(api, f) + RegisterTenantRoutes(api, f) + RegisterPartnerRoutes(api, f) + RegisterCustomerRoutes(api, f) +} diff --git a/routes/tenantroutes.go b/routes/tenantroutes.go new file mode 100644 index 0000000..32b22fb --- /dev/null +++ b/routes/tenantroutes.go @@ -0,0 +1,38 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterTenantRoutes(api fiber.Router, f *facade.Facade) { + + tenant := api.Group("/v1/web/tenants") + + tenant.Get("/search", f.TenantController.SearchTenant) + tenant.Get("/searchbykeyword", f.TenantController.GetTenantByKeyword) + tenant.Get("/getalltenants", f.TenantController.GetAllTenants) + tenant.Get("/gettenantlocations", f.TenantController.GetTenantLocations) + tenant.Post("/createtenantcustomer", f.TenantController.CreateTenantCustomer) + tenant.Put("/updatelocation", f.TenantController.UpdateLocation) + tenant.Post("/createlocation", f.TenantController.CreateLocation) + tenant.Post("/createtenantlocation", f.TenantController.CreateTenantLocation) + tenant.Put("/updatetenantlocation", f.TenantController.UpdateTenantLocation) + + tenant = api.Group("/v1/mob/tenants") + + tenant.Get("/gettenantslot", f.TenantController.GetTenantSlot) + tenant.Get("/searchbykeyword", f.TenantController.GetTenantByKeyword) + tenant.Post("/createtenantcustomer", f.TenantController.CreateTenantCustomer) + tenant.Get("/getcustomertenants", f.TenantController.GetCustomerTenants) + tenant.Get("/gettenantlocations", f.TenantController.GetTenantLocations) + tenant.Get("/gettenantpricing", f.TenantController.GetTenantPricing) + tenant.Put("/updatelocation", f.TenantController.UpdateLocation) + tenant.Post("/createlocation", f.TenantController.CreateLocation) + tenant.Get("/getstaffs", f.TenantController.GetStaffs) + tenant.Post("/createstaff", f.TenantController.CreateStaff) + tenant.Post("/createtenantuser", f.TenantController.CreateTenantUser) + tenant.Get("/gettenantinfo", f.TenantController.GetTenantInfo) + +} diff --git a/routes/userroutes.go b/routes/userroutes.go new file mode 100644 index 0000000..0e47066 --- /dev/null +++ b/routes/userroutes.go @@ -0,0 +1,24 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterUserRoutes(api fiber.Router, f *facade.Facade) { + + users := api.Group("/v1/web/users") + users.Get("/getallusers", f.UserController.GetAllUsers) + users.Get("/getusers", f.UserController.GetUserInfo) + users.Post("/applogin", f.UserController.AppLogin) + users.Post("/create", f.UserController.CreateUser) + users.Post("/tenant/weblogin", f.UserController.TenantWebLogin) + users.Put("/update", f.UserController.UpdateStaff) + + users = api.Group("/v1/mob/users") + users.Post("/tenant/login", f.UserController.Login) + users.Get("/getusers", f.UserController.GetUserInfo) + users.Post("/create", f.UserController.CreateUser) + users.Put("/update", f.UserController.UpdateStaff) +} diff --git a/routes/utilsroutes.go b/routes/utilsroutes.go new file mode 100644 index 0000000..fe20429 --- /dev/null +++ b/routes/utilsroutes.go @@ -0,0 +1,29 @@ +package routes + +import ( + "nearle/facade" + + "github.com/gofiber/fiber/v2" +) + +func RegisterUtilsRoutes(api fiber.Router, f *facade.Facade) { + + utils := api.Group("/v1/web/utils") + + utils.Get("/getapptypes", f.UtilsController.GetAppTypes) + // utils.Post("/notifyuser", f.UtilsController.NotifyUser) + utils.Get("/getsubcategories", f.UtilsController.GetSubcategories) + utils.Get("/getapplocations", f.UtilsController.GetApplocations) + utils.Get("/getappcategories", f.UtilsController.GetAppCategory) + + utils = api.Group("/v1/mob/utils") + + utils.Get("/getapplocationconfig", f.UtilsController.GetApplocationConfig) + // utils.Post("/notifyadmin", f.UtilsController.NotifyAdmin) + utils.Get("/getapplocations", f.UtilsController.GetApplocations) + utils.Get("/getapptypes", f.UtilsController.GetAppTypes) + utils.Get("/getappconfig", f.UtilsController.GetAppConfig) + utils.Get("/getsubcategories", f.UtilsController.GetSubcategories) + utils.Get("/getappcategories", f.UtilsController.GetAppCategory) + +} diff --git a/services/customerService.go b/services/customerService.go new file mode 100644 index 0000000..528e181 --- /dev/null +++ b/services/customerService.go @@ -0,0 +1,148 @@ +package services + +import ( + "errors" + "nearle/models" + "nearle/repositories" + "net/http" + "strconv" +) + +type CustomerService interface { + GetCustomer(cid int, cno string) (*models.CustomerInfo, error) + UpdateCustomer(input models.Customers) models.Result + GetCustomerLocations(cid int) models.CustomerLocationResult + CreateCustomerLocation(input models.Customerlocations) (int, error) + CreateCustomerRequest(req models.CustomerRequest) (models.CustomerRequest, error) + GetCustomerRequests(customerIDStr string, pageNo, pageSize int) ([]models.CustomerRequest, int64, error) + GetTenantCustomers(tid, lid, pageno, pagesize int, keyword string) []models.CustomerInfo + SearchCustomer(keyword string, tid int) []models.CustomerInfo + CreateCustomer(input models.Customers) (models.CustomerInfo, error) + LoginCustomer(contactNo string) (*models.Customers, error) +} + +type customerService struct { + repo repositories.CustomerRepository +} + +func NewCustomerService(repo repositories.CustomerRepository) CustomerService { + return &customerService{repo: repo} +} + +func (s *customerService) GetCustomer(cid int, cno string) (*models.CustomerInfo, error) { + return s.repo.GetCustomer(cid, cno) +} + +func (s *customerService) UpdateCustomer(input models.Customers) models.Result { + err := s.repo.UpdateCustomer(input) + if err != nil { + return models.Result{ + Code: http.StatusConflict, + Status: false, + Message: "Update Customer Failed", + } + } + + return models.Result{ + Code: http.StatusAccepted, + Status: true, + Message: "Customer update successful", + } +} + +func (s *customerService) CreateCustomerLocation(input models.Customerlocations) (int, error) { + return s.repo.CreateCustomerLocation(input) +} + +func (s *customerService) GetCustomerLocations(cid int) models.CustomerLocationResult { + data, err := s.repo.GetCustomerLocations(cid) + if err != nil { + return models.CustomerLocationResult{ + Code: http.StatusInternalServerError, + Status: false, + Message: "Failed to fetch customer locations", + Details: []models.Customerlocations{}, + } + } + + return models.CustomerLocationResult{ + Code: http.StatusOK, + Status: true, + Message: "Successful", + Details: data, + } +} + +func (s *customerService) CreateCustomerRequest(req models.CustomerRequest) (models.CustomerRequest, error) { + err := s.repo.CreateCustomerRequest(&req) + if err != nil { + return models.CustomerRequest{}, err + } + return req, nil +} + +func (s *customerService) GetCustomerRequests(customerIDStr string, pageNo, pageSize int) ([]models.CustomerRequest, int64, error) { + if pageNo < 1 { + pageNo = 1 + } + if pageSize < 1 { + pageSize = 10 + } + + var customerID int + var err error + if customerIDStr != "" { + customerID, err = strconv.Atoi(customerIDStr) + if err != nil { + return nil, 0, errors.New("invalid customerid") + } + } + + return s.repo.GetCustomerRequests(customerID, pageNo, pageSize) +} + +func (s *customerService) GetTenantCustomers(tid, lid, pageno, pagesize int, keyword string) []models.CustomerInfo { + return s.repo.GetTenantCustomers(tid, lid, pageno, pagesize, keyword) +} + +func (s *customerService) SearchCustomer(keyword string, tid int) []models.CustomerInfo { + return s.repo.SearchCustomer(keyword, tid) +} + +func (s *customerService) CreateCustomer(input models.Customers) (models.CustomerInfo, error) { + + cid := s.repo.CheckCustomer(input.Contactno) + if cid != 0 { + input.Customerid = cid + + tcid := s.repo.CheckTenantCustomer(input.Customerid, input.Tenantid) + if tcid != 0 { + return models.CustomerInfo{}, errors.New("Customer Already available") + } + + s.repo.CreateTenantCustomer(input) + } else { + + cid = s.repo.CreateCustomer(input) + } + + result, err := s.repo.GetCustomer(cid, "") + if err != nil { + return models.CustomerInfo{}, err + } + return *result, nil + +} + +func (s *customerService) LoginCustomer(contactNo string) (*models.Customers, error) { + customer, err := s.repo.GetCustomerByContactNo(contactNo) + if err != nil { + return nil, err + } + + if customer.Customerid == 0 { + return nil, nil // handle "not found" in controller + } + + return customer, nil +} diff --git a/services/deliveriesService.go b/services/deliveriesService.go new file mode 100644 index 0000000..5e9f586 --- /dev/null +++ b/services/deliveriesService.go @@ -0,0 +1,87 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" +) + +type DeliveriesService interface { + CreateDeliveriesService(data []models.Deliveries) error + UpdateDeliveryService(data models.UpdateDeliveryStatus) error + GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) + GetDeliveryInsightService(tid int) ([]models.OrderInsightv1, error) + GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) + GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) + GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) + GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo + GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) +} + +type deliveriesService struct { + repo repositories.DeliveriesRepository +} + +func NewDeliveriesService(repo repositories.DeliveriesRepository) DeliveriesService { + return &deliveriesService{repo: repo} +} + +func (s *deliveriesService) CreateDeliveriesService(data []models.Deliveries) error { + return s.repo.CreateDeliveries(data) +} + +func (s *deliveriesService) UpdateDeliveryService(data models.UpdateDeliveryStatus) error { + return s.repo.UpdateDelivery(data) +} + +func (s *deliveriesService) GetDeliverySummary(tid, pid, uid, aid, lid int, fdate, tdate string) (models.DeliverySummary, error) { + return s.repo.GetDeliverySummary(tid, pid, uid, aid, lid, fdate, tdate) +} + +func (s *deliveriesService) GetDeliveryInsightService(tid int) ([]models.OrderInsightv1, error) { + return s.repo.GetDeliveryInsight(tid) +} + +func (s *deliveriesService) GetLocationDeliverySummary(tenantID int) ([]models.Ordersummarylocation, error) { + return s.repo.GetLocationDeliverySummary(tenantID) +} + +func (s *deliveriesService) GetReportSummary(tid, pid, uid, aid int, fdate, tdate string) ([]models.ReportSummary, error) { + return s.repo.GetReportSummary(tid, pid, uid, aid, fdate, tdate) +} + +func (s *deliveriesService) GetRiderSummary(aid, pid, tid int, fdate, tdate string) ([]models.Ridersummary, error) { + return s.repo.GetRiderSummary(aid, pid, tid, fdate, tdate) +} + +func (s *deliveriesService) GetDeliveries(input models.DeliveryQuery) []models.Deliveryinfo { + switch { + case input.Tenantid != 0 && input.Locationid != 0: + return s.repo.GetTenantLocationDeliveries(input) // 👈 NEW + + case input.Tenantid != 0: + return s.repo.GetTenantDeliveries(input) + + case input.Partnerid != 0: + return s.repo.GetPartnerDeliveries(input) + + case input.Customerid != 0: + return s.repo.GetCustomerDeliveries(input) + + case input.Applocationid != 0: + return s.repo.GetAdminDeliveries(input) + + case input.UserID != 0: + return s.repo.GetUserDeliveries(input) + + case input.Appuserid != 0: + return s.repo.GetAppUserDeliveries(input) + + default: + return s.repo.GetDeliveries(input) + } +} + + +func (s *deliveriesService) GetDeliveryQueues(uid int, fdate, tdate string) ([]models.Deliveryinfo, error) { + return s.repo.GetDeliveryQueues(uid, fdate, tdate) +} diff --git a/services/orderService.go b/services/orderService.go new file mode 100644 index 0000000..c4f6ab1 --- /dev/null +++ b/services/orderService.go @@ -0,0 +1,88 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" +) + +type OrderService interface { + GetTenantOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) + GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) + GetOrderSummary(tid, pid, cid, lid int, fdate, tdate string) ([]models.Ordersummarydaily, error) + GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error) + GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error) + GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error) + UpdateOrder(order *models.Orders) error + CreateOrder(order models.Orders) (models.Orders, error) + GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) + GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) + +} + +type orderService struct { + repo repositories.OrderRepository +} + +func NewOrderService(repo repositories.OrderRepository) OrderService { + return &orderService{repo: repo} +} + +func (s *orderService) GetTenantOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) { + return s.repo.GetTenantOrders(input) +} + +func (s *orderService) GetPartnerOrders(stat, fdate, tdate string, pid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + return s.repo.GetPartnerOrders(stat, fdate, tdate, pid, pageno, pagesize, keyword) +} + +func (s *orderService) GetCustomerOrders(stat, fdate, tdate string, cid, mid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + return s.repo.GetCustomerOrders(stat, fdate, tdate, cid, mid, pageno, pagesize, keyword) +} + +func (s *orderService) GetAdminOrders(stat, fdate, tdate string, aid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + return s.repo.GetAdminOrders(stat, fdate, tdate, aid, pageno, pagesize, keyword) +} + +func (s *orderService) GetUserOrders(stat, fdate, tdate string, uid, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + return s.repo.GetUserOrders(stat, fdate, tdate, uid, pageno, pagesize, keyword) +} + +func (s *orderService) GetAllOrders(stat, fdate, tdate string, pageno, pagesize int, keyword string) ([]models.OrderInfo, error) { + return s.repo.GetAllOrders(stat, fdate, tdate, pageno, pagesize, keyword) +} + +func (s *orderService) GetOrderSummary(tid, pid, cid, lid int, fdate, tdate string) ([]models.Ordersummarydaily, error) { + return s.repo.GetOrderSummary(tid, pid, cid, lid, fdate, tdate) +} + +func (s *orderService) GetLocationOrderSummary(tenantID int) ([]models.Ordersummarylocation, error) { + return s.repo.GetLocationOrderSummary(tenantID) +} + +func (s *orderService) GetOrderInsights(tenantID int) ([]models.OrderInsightv1, error) { + return s.repo.GetOrderInsights(tenantID) +} + +func (s *orderService) GetOrderDetails(orderHeaderID int) ([]models.OrderDetails, error) { + return s.repo.GetOrderDetails(orderHeaderID) +} + +func (s *orderService) UpdateOrder(order *models.Orders) error { + return s.repo.UpdateOrder(order) +} + +func (s *orderService) CreateOrder(order models.Orders) (models.Orders, error) { + return s.repo.CreateOrder(order) +} + +func (s *orderService) GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) { + return s.repo.GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword, pageSize, offset) +} + +func (s *orderService) GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) { + return s.repo.GetTenantLocationOrders(input) +} diff --git a/services/partnerService.go b/services/partnerService.go new file mode 100644 index 0000000..0387e79 --- /dev/null +++ b/services/partnerService.go @@ -0,0 +1,55 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" +) + +type PartnerService interface { + GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error) + GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error) + GetRiderShifts(aid int) ([]models.Ridershifts, error) + GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) + GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) + GetRiderInfo(userid int) (models.RiderInfo, error) +} + +type partnerService struct { + repo repositories.PartnerRepository +} + +func NewPartnerService(repo repositories.PartnerRepository) PartnerService { + return &partnerService{repo: repo} +} + +func (s *partnerService) GetActiveRiders(partnerid, aid, uid, tid int) ([]models.RiderInfo, error) { + return s.repo.GetActiveRiders(partnerid, aid, uid, tid) +} + +func (s *partnerService) GetPartners(aid, pid, uid int) ([]models.Partnerinfo, error) { + result, err := s.repo.GetPartners(aid, pid, uid) + + if err != nil { + return nil, err + } + + return result, nil +} + +func (s *partnerService) GetRiderShifts(aid int) ([]models.Ridershifts, error) { + + return s.repo.GetRiderShifts(aid) +} + +func (s *partnerService) GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) { + + return s.repo.GetLocationConfig(uid, cid) +} + +func (s *partnerService) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) { + return s.repo.GetRiderLogs(pid, aid, fdate, tdate) +} + +func (s *partnerService) GetRiderInfo(userid int) (models.RiderInfo, error) { + return s.repo.GetRiderInfo(userid) +} diff --git a/services/productService.go b/services/productService.go new file mode 100644 index 0000000..18c8750 --- /dev/null +++ b/services/productService.go @@ -0,0 +1,208 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" + "time" +) + +type ProductService interface { + GetProductSubCategory(categoryID, tenantID int) ([]models.ProductSubCategory, error) + GetProductCount(tenantID, categoryID, subcategoryID int, approve string) ([]models.Productcount, error) + GetProductCategory() ([]models.ProductCategory, error) + GetProductVariants(tenantID, subcategoryID int) ([]models.Productvariant, error) + GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Products, error) + GetProductStocks(tenantID, locationID string) ([]models.Productstocks, error) + UpdateProductStatus(productIDs []int, status string) error + CreateProductStock(stocks []models.Productstock) error + CreateProduct(product models.Products) error + UpdateProduct(product models.Products) error + DeleteProduct(productID int) error + GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Productstockstatement, error) + GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Locationproducts, error) + GetLocationProductSummary(tenantID, locationID int) ([]models.ProductSummary, error) + FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID int, keyword, productStatus, approve string, pageno, pagesize int) ([]models.Tenantproducts, error) + GetProductByVariant(tenantid, variantid int) ([]models.Products, error) + GetProductsBySubcategory(params models.ProductFilter) (map[string]interface{}, error) + UpdateProductLocation(input models.Productlocations) error + CreateProductLocation(input []models.Productlocations) error + CreateProductVariant(input models.Productvariant) error +} +type productService struct { + repo repositories.ProductRepository +} + +func NewProductService(repo repositories.ProductRepository) ProductService { + return &productService{repo: repo} +} + +func (s *productService) GetProductSubCategory(categoryID, tenantID int) ([]models.ProductSubCategory, error) { + return s.repo.GetProductSubCategory(categoryID, tenantID) +} + +func (s *productService) GetProductCount(tenantID, categoryID, subcategoryID int, approve string) ([]models.Productcount, error) { + return s.repo.GetProductCount(tenantID, categoryID, subcategoryID, approve) +} + +func (s *productService) GetProductCategory() ([]models.ProductCategory, error) { + return s.repo.GetProductCategory() +} + +func (s *productService) GetProductVariants(tenantID, subcategoryID int) ([]models.Productvariant, error) { + return s.repo.GetProductVariants(tenantID, subcategoryID) +} + +func (s *productService) GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Products, error) { + return s.repo.GetCatalougeProducts(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) +} + +func (s *productService) GetProductStocks(tenantID, locationID string) ([]models.Productstocks, error) { + return s.repo.GetProductStocks(tenantID, locationID) +} + +func (s *productService) CreateProductStock(stocks []models.Productstock) error { + + for i := range stocks { + stocks[i].Stockdate = time.Now() + } + + if err := s.repo.CreateProductStock(stocks); err != nil { + return err + } + + idMap := make(map[int]struct{}) + var productIDs []int + for _, s := range stocks { + if s.Productid > 0 { + if _, exists := idMap[s.Productid]; !exists { + idMap[s.Productid] = struct{}{} + productIDs = append(productIDs, s.Productid) + } + } + } + + if len(productIDs) > 0 { + if err := s.repo.UpdateProductStatus(productIDs, "available"); err != nil { + return err + } + } + + return nil +} + +func (s *productService) UpdateProductStatus(productIDs []int, status string) error { + return s.repo.UpdateProductStatus(productIDs, status) +} + +func (s *productService) CreateProduct(product models.Products) error { + return s.repo.CreateProduct(product) +} + +func (s *productService) UpdateProduct(product models.Products) error { + return s.repo.UpdateProduct(product) +} + +func (s *productService) DeleteProduct(productID int) error { + return s.repo.DeleteProduct(productID) +} + +func (s *productService) GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Productstockstatement, error) { + return s.repo.GetStockStatement(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) +} + +func (s *productService) GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize int, keyword string) ([]models.Locationproducts, error) { + return s.repo.GetLocationProducts(tenantID, locationID, subcategoryID, pageno, pagesize, keyword) +} + +func (s *productService) GetLocationProductSummary(tenantID, locationID int) ([]models.ProductSummary, error) { + return s.repo.GetLocationProductSummary(tenantID, locationID) +} + +func (s *productService) FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID int, keyword, productStatus, + approve string, pageno, pagesize int) ([]models.Tenantproducts, error) { + return s.repo.FetchFilteredProducts(categoryID, subcategoryID, productID, applocationID, tenantID, locationID, keyword, productStatus, approve, pageno, pagesize) +} + +func (s *productService) GetProductByVariant(tenantid, variantid int) ([]models.Products, error) { + + var data []models.Products + + result, err := s.repo.GetProductByVariant(tenantid, variantid) + + if err != nil { + return nil, err + } + + data = result + + return data, nil + +} + +func (s *productService) GetProductsBySubcategory(params models.ProductFilter) (map[string]interface{}, error) { + + subcategories, err := s.repo.GetSubcategories(params.CategoryID) + if err != nil { + return nil, err + } + + products, err := s.repo.GetProducts(params) + if err != nil { + return nil, err + } + + var details []models.SubcategoryProductResponse + var uncategorized []models.Products + + for _, sub := range subcategories { + var subProducts []models.Products + for _, p := range products { + if p.Subcategoryid == sub.Subcategoryid { + subProducts = append(subProducts, p) + } + } + if len(subProducts) > 0 { + details = append(details, models.SubcategoryProductResponse{ + SubcategoryID: sub.Subcategoryid, + SubcategoryName: sub.Subcategoryname, + Image: sub.Image, + Products: subProducts, + }) + } + } + + for _, p := range products { + if p.Subcategoryid == 0 { + uncategorized = append(uncategorized, p) + } + } + if len(uncategorized) > 0 { + details = append(details, models.SubcategoryProductResponse{ + SubcategoryID: 0, + SubcategoryName: "Uncategorized", + Products: uncategorized, + }) + } + + if params.TenantID > 0 { + tenantInfo, err := s.repo.GetTenantInfo(params.TenantID, params.AppLocationID) + if err == nil && tenantInfo != nil { + tenantInfo["details"] = details + return tenantInfo, nil + } + } + + return map[string]interface{}{"details": details}, nil +} + +func (s *productService) UpdateProductLocation(input models.Productlocations) error { + return s.repo.UpdateProductLocation(input) +} + +func (s *productService) CreateProductLocation(input []models.Productlocations) error { + return s.repo.CreateProductLocation(input) +} + +func (s *productService) CreateProductVariant(input models.Productvariant) error { + return s.repo.CreateProductVariant(input) +} diff --git a/services/tenantService.go b/services/tenantService.go new file mode 100644 index 0000000..dd24fb4 --- /dev/null +++ b/services/tenantService.go @@ -0,0 +1,162 @@ +package services + +import ( + "errors" + "nearle/models" + "nearle/repositories" + "net/http" +) + +type TenantService interface { + SearchTenant(status, keyword string) ([]models.Tenantinfo, error) + GetAllTenants(pageno, pagesize, aid int, status, tenanttype, keyword string) ([]models.Tenantinfo, error) + GetTenantLocations(tid int) ([]models.Tenantlocations, error) + GetTenantSlot() (models.Tenantslot, error) + CreateTenantCustomer(req models.CreateTenantCustomerRequest) (*models.Tenantcustomer, error) + GetCustomerTenants(customerID int, categoryID int, tenantFlag int) (*models.CustomerTenantResponse, error) + GetTenantPricing(tid, aid int) (models.Tenantpricing, error) + UpdateLocation(input models.Tenantlocations) error + CreateLocation(data models.Tenantlocations) error + GetStaffs(tid int) ([]models.StaffInfo, error) + CreateStaff(user models.User) error + UpdateStaff(user models.User) error + CreateTenantLocation(data models.Tenantlocations) map[string]interface{} + UpdateTenantLocation(data models.Tenantlocations) map[string]interface{} + CreateTenantUser(data models.Tenants) (models.UserInfo, error) + GetTenantByID(tid int, locationid int) (models.Tenantinfo, error) + GetTenantByKeyword(keyword string) ([]models.TenantSearch, error) +} + +type tenantService struct { + repo repositories.TenantRepository +} + +func NewTenantService(repo repositories.TenantRepository) TenantService { + return &tenantService{repo: repo} +} + +func (s *tenantService) SearchTenant(status, keyword string) ([]models.Tenantinfo, error) { + return s.repo.SearchTenant(status, keyword) +} + +func (s *tenantService) GetAllTenants(pageno, pagesize, aid int, status, tenanttype, keyword string) ([]models.Tenantinfo, error) { + return s.repo.GetAllTenants(pageno, pagesize, aid, status, tenanttype, keyword) +} + +func (s *tenantService) GetTenantLocations(tid int) ([]models.Tenantlocations, error) { + return s.repo.GetTenantLocations(tid) +} + +func (s *tenantService) GetTenantSlot() (models.Tenantslot, error) { + return s.repo.GetTenantSlot() +} + +func (s *tenantService) CreateTenantCustomer(req models.CreateTenantCustomerRequest) (*models.Tenantcustomer, error) { + tenantCustomer := models.Tenantcustomer{ + TenantID: req.TenantID, + LocationID: req.LocationID, + CustomerID: req.CustomerID, + CustomerLocationID: req.CustomerLocationID, + Status: req.Status, + } + return s.repo.CreateTenantCustomer(tenantCustomer) +} + +func (s *tenantService) GetCustomerTenants(customerID int, categoryID int, tenantFlag int) (*models.CustomerTenantResponse, error) { + details, err := s.repo.GetCustomerTenants(customerID, categoryID, tenantFlag) + if err != nil { + return nil, err + } + + return &models.CustomerTenantResponse{ + Details: details, + }, nil +} + +func (s *tenantService) GetTenantPricing(tid, aid int) (models.Tenantpricing, error) { + result, err := s.repo.GetTenantPricing(tid, aid) + if err != nil { + return models.Tenantpricing{}, err + } + return *result, nil +} + +func (s *tenantService) UpdateLocation(input models.Tenantlocations) error { + return s.repo.UpdateLocation(input) +} + +func (s *tenantService) CreateLocation(data models.Tenantlocations) error { + return s.repo.CreateLocation(data) +} + +func (s *tenantService) GetStaffs(tid int) ([]models.StaffInfo, error) { + return s.repo.GetStaffs(tid) +} + +func (s *tenantService) CreateStaff(user models.User) error { + return s.repo.CreateStaff(user) +} + +func (s *tenantService) UpdateStaff(user models.User) error { + return s.repo.UpdateStaff(user) +} + +func (s *tenantService) CreateTenantLocation(data models.Tenantlocations) map[string]interface{} { + err := s.repo.CreateTenantLocation(data) + if err != nil { + return map[string]interface{}{ + "code": http.StatusConflict, + "message": err.Error(), + "status": false, + } + } + + return map[string]interface{}{ + "code": http.StatusCreated, + "message": "Tenant Location Successfully Created", + "status": true, + } +} + +func (s *tenantService) UpdateTenantLocation(data models.Tenantlocations) map[string]interface{} { + err := s.repo.UpdateTenantLocation(data) + if err != nil { + return map[string]interface{}{ + "status": false, + "code": http.StatusConflict, + "message": err.Error(), + } + } + + return map[string]interface{}{ + "status": true, + "code": http.StatusAccepted, + "message": "Tenant Location update successful", + } +} + +func (s *tenantService) CreateTenantUser(data models.Tenants) (models.UserInfo, error) { + // ✅ Check if tenant already exists + exists := s.repo.CheckTenantByNo(data.Primarycontact) + if exists != 0 { + return models.UserInfo{}, errors.New("Tenant Already Exists") + } + + // ✅ Create Tenant User + status, err := s.repo.CreateTenantUser(data) + if err != nil || !status { + return models.UserInfo{}, err + } + + // ✅ Get user details by contact number + result := s.repo.GetUserByNo(data.Primarycontact) + return result, nil +} + +func (s *tenantService) GetTenantByID(tid int, locationid int) (models.Tenantinfo, error) { + return s.repo.GetTenantByID(tid, locationid) +} + +func (s *tenantService) GetTenantByKeyword(keyword string) ([]models.TenantSearch, error) { + return s.repo.GetTenantByKeyword(keyword) +} diff --git a/services/userService.go b/services/userService.go new file mode 100644 index 0000000..9ab58db --- /dev/null +++ b/services/userService.go @@ -0,0 +1,267 @@ +package services + +import ( + "errors" + "nearle/models" + "nearle/repositories" + "strings" + + "github.com/gofiber/fiber" +) + +type UserService interface { + GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error) + GetUserByID(uid int) (models.UserInfo, error) + Login(user models.User) (models.UserInfo, error) + TenantLogin(user models.User) (models.TenantUserInfo, error) + UpdateStaff(user models.User) error + AppLogin(user models.User) (models.TenantUserInfo, fiber.Map, error) + CreateUser(user models.User) (models.UserInfo, error) + TenantWebLogin(user models.User) (models.TenantUserInfo, map[string]interface{}) +} + +type userService struct { + repo repositories.UserRepository +} + +func NewUserService(repo repositories.UserRepository) UserService { + return &userService{repo: repo} +} + +func (s *userService) GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error) { + return s.repo.GetAllUsers(roleID, tenantID, pageno, pagesize, keyword) +} + +func (s *userService) GetUserByID(uid int) (models.UserInfo, error) { + return s.repo.GetUserByID(uid) +} + +func (s *userService) Login(user models.User) (models.UserInfo, error) { + return s.repo.Login(user) +} + +func (s *userService) TenantLogin(user models.User) (models.TenantUserInfo, error) { + uid, err := s.repo.FindUserID(user.Authname, user.Contactno, user.Configid) + if err != nil { + return models.TenantUserInfo{}, err + } + + if uid == 0 { + return models.TenantUserInfo{}, errors.New("user not found") + } + + return s.repo.GetTenantUserByID(uid) +} + +func (s *userService) UpdateStaff(user models.User) error { + return s.repo.UpdateStaff(user) +} + +func (s *userService) AppLogin(user models.User) (models.TenantUserInfo, fiber.Map, error) { + var uid int + var status, dbPassword string + + // Get user by authname or contactno + if user.Authname != "" { + uid, dbPassword, status = s.repo.GetUserByAuthname(user.Authname, user.Configid) + } else if user.Contactno != "" { + uid, dbPassword, status = s.repo.GetUserByContactNo(user.Contactno, user.Configid) + } else { + resp := fiber.Map{ + "code": 400, + "status": false, + "message": "authname or contactno required", + } + return models.TenantUserInfo{}, resp, errors.New("missing authname or contactno") + } + + // Invalid user + if uid == 0 { + resp := fiber.Map{ + "status": false, + "code": 409, + "message": "Invalid Email", + "tenantform": true, + } + return models.TenantUserInfo{}, resp, errors.New("invalid user") + } + + // Inactive account + if status == "InActive" { + resp := fiber.Map{ + "status": false, + "code": 403, + "message": "Inactive Account. Contact admin.", + } + return models.TenantUserInfo{}, resp, errors.New("inactive account") + } + + // No password set + if strings.TrimSpace(dbPassword) == "" { + resp := fiber.Map{ + "status": true, + "code": 409, + "message": "Please setup a password.", + "tenantform": true, + "details": fiber.Map{ + "userid": uid, + "setup": true, + }, + } + return models.TenantUserInfo{}, resp, nil + } + + // Empty request password + if strings.TrimSpace(user.Password) == "" { + resp := fiber.Map{ + "status": true, + "code": 401, + "message": "Password is required", + "tenantform": true, + } + return models.TenantUserInfo{}, resp, nil + } + + // Incorrect password + if user.Password != dbPassword { + resp := fiber.Map{ + "status": false, + "code": 401, + "message": "Incorrect password", + "tenantform": true, + } + return models.TenantUserInfo{}, resp, errors.New("incorrect password") + } + + // Update FCM token + if user.Userfcmtoken != "" { + _ = s.repo.UpdateFCMToken(uid, user.Userfcmtoken) + } + + + + // ✅ Fetch tenant user info + info := s.repo.GetTenantUserById(uid) + + // ✅ Return success response + resp := fiber.Map{ + "status": true, + "code": 200, + "message": "Login successful", + "details": info, + } + + return info, resp, nil +} + +func (s *userService) CreateUser(user models.User) (models.UserInfo, error) { + // Call repository to create user + userid, err := s.repo.CreateUser(user) + if err != nil { + return models.UserInfo{}, err + } + + // Get user info by id + info, err := s.repo.GetUserById(userid) + if err != nil { + return models.UserInfo{}, err + } + + return info, nil +} + + +func (s *userService) TenantWebLogin(user models.User) (models.TenantUserInfo, map[string]interface{}) { + tenantFormExists := true + + uid, dbPassword, status, roleid := 0, "", "", 0 + + // Step 1: Login by authname or contactno + if user.Authname != "" { + uid, dbPassword, status, roleid = s.repo.GetUserLogin("authname", user.Authname, user.Configid) + } else if user.Contactno != "" { + uid, dbPassword, status, roleid = s.repo.GetUserLogin("contactno", user.Contactno, user.Configid) + } else { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": true, + "code": 400, + "message": "authname or contactno required", + } + } + + // Step 2: Validate user + if uid == 0 { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": false, + "code": 409, + "message": "Invalid Email", + "tenantform": tenantFormExists, + } + } + + if status == "InActive" { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": false, + "code": 403, + "message": "Inactive Account. Contact admin.", + } + } + + if user.Roleid != roleid { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": false, + "code": 403, + "message": "Unauthorized email.", + } + } + + // Step 3: Password checks + if strings.TrimSpace(dbPassword) == "" { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": true, + "code": 409, + "message": "Please setup a password.", + "tenantform": tenantFormExists, + "details": map[string]interface{}{ + "userid": uid, + "setup": true, + }, + } + } + + if strings.TrimSpace(user.Password) == "" { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": true, + "code": 401, + "message": "Password is required", + "tenantform": tenantFormExists, + } + } + + if user.Password != dbPassword { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": false, + "code": 401, + "message": "Incorrect password", + "tenantform": tenantFormExists, + } + } + + user.Userid = uid + + // Step 4: Update FCM if provided + if user.Userfcmtoken != "" { + _ = s.repo.UpdateUserFcmToken(uid, user.Userfcmtoken) + } + + // Step 5: Get full tenant info + info := s.repo.GetTenantUserById(uid) + + return info, map[string]interface{}{ + "status": true, + "code": 200, + "message": "Login successful", + } +} + + diff --git a/services/utilsService.go b/services/utilsService.go new file mode 100644 index 0000000..09012cf --- /dev/null +++ b/services/utilsService.go @@ -0,0 +1,52 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" +) + +type UtilsService interface { + GetApptypes(tag string) ([]models.Apptypes, error) + SendNotification(token string, notification models.FcmNotification, data map[string]string) error + GetSubcategories(moduleid int, categoryid int) ([]models.Appsubcategories, error) + GetApplocations(aid int) ([]models.Applocations, error) + GetApplocationConfig(aid int) ([]models.Applocations, error) + GetAppConfig(configID int) (models.Appconfig, error) + GetAppCategory() ([]models.AppCategory, error) +} + +type utilsService struct { + repo repositories.UtilsRepository +} + +func NewUtilsService(repo repositories.UtilsRepository) UtilsService { + return &utilsService{repo: repo} +} + +func (s *utilsService) GetApptypes(tag string) ([]models.Apptypes, error) { + return s.repo.GetApptypes(tag) +} + +func (s *utilsService) SendNotification(token string, notification models.FcmNotification, data map[string]string) error { + return s.repo.SendNotification(token, notification, data) +} + +func (s *utilsService) GetSubcategories(moduleid int, categoryid int) ([]models.Appsubcategories, error) { + return s.repo.GetSubcategories(moduleid, categoryid) +} + +func (s *utilsService) GetApplocations(aid int) ([]models.Applocations, error) { + return s.repo.GetApplocations(aid) +} + +func (s *utilsService) GetApplocationConfig(aid int) ([]models.Applocations, error) { + return s.repo.GetApplocationConfig(aid) +} + +func (s *utilsService) GetAppConfig(configID int) (models.Appconfig, error) { + return s.repo.GetAppConfig(configID) +} + +func (s *utilsService) GetAppCategory() ([]models.AppCategory, error) { + return s.repo.GetAppCategory() +} diff --git a/utils/config.go b/utils/config.go new file mode 100644 index 0000000..f4a3471 --- /dev/null +++ b/utils/config.go @@ -0,0 +1,51 @@ +package utils + +import ( + "fmt" + "os" + + "github.com/spf13/viper" +) + +func DevConfig() (Port, DBname, Password, Username, Host, key, secret string) { + + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + err := viper.ReadInConfig() + if err != nil { + fmt.Println("fatal error config file: default \n", err) + os.Exit(1) + } + + dbname := viper.GetString("DEV.DATABASE_NAME") + dbpassword := viper.GetString("DEV.DATABASE_PASSWORD") + dbusername := viper.GetString("DEV.DATABASE_USERNAME") + dbport := viper.GetString("DEV.DATABASE_PORT") + dbhost := viper.GetString("DEV.DATABASE_SERVER_HOST") + contextkey := viper.GetString("DEV.USER_CONTEXT_KEY") + jwtkey := viper.GetString("DEV.JWT_SECRET_KEY") + return dbport, dbname, dbpassword, dbusername, dbhost, contextkey, jwtkey +} + +func LiveConfig() (Port, DBname, Password, Username, Host, key, secret string) { + + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + err := viper.ReadInConfig() + if err != nil { + fmt.Println("fatal error config file: default \n", err) + os.Exit(1) + } + dbname := viper.GetString("APP.DATABASE_NAME") + dbpassword := viper.GetString("APP.DATABASE_PASSWORD") + dbusername := viper.GetString("APP.DATABASE_USERNAME") + dbport := viper.GetString("APP.DATABASE_PORT") + dbhost := viper.GetString("APP.DATABASE_SERVER_HOST") + contextkey := viper.GetString("APP.USER_CONTEXT_KEY") + jwtkey := viper.GetString("APP.JWT_SECRET_KEY") + return dbport, dbname, dbpassword, dbusername, dbhost, contextkey, jwtkey +}