From abe2cb997bdcde2ab6f3846df69cafd48f868c0a Mon Sep 17 00:00:00 2001 From: abhishek Date: Sat, 4 Jul 2026 17:49:54 +0530 Subject: [PATCH] stock request table created --- controllers/orderController.go | 31 +++++++- controllers/partnerController.go | 26 ++++++- controllers/stockrequestController.go | 69 +++++++++++++++++ controllers/tenantController.go | 34 ++++++++ create_table.go | 32 ++++++++ facade/container.go | 13 +++- main.go | 5 +- models/order.go | 18 +++++ models/partner.go | 27 ++++++- models/stockrequest.go | 20 +++++ repositories/orderRepository.go | 107 ++++++++++++++++++++++++-- repositories/partnerRepository.go | 98 +++++++++++++++++++++++ repositories/stockrequest.go | 56 ++++++++++++++ repositories/tenantRepository.go | 54 +++---------- repositories/userRepository.go | 10 +++ routes/orderroutes.go | 3 + routes/partnerroutes.go | 1 + routes/productroutes.go | 4 + routes/tenantroutes.go | 1 + services/orderService.go | 5 ++ services/partnerService.go | 5 ++ services/stockrequestService.go | 58 ++++++++++++++ services/tenantService.go | 5 ++ services/userService.go | 27 ++++++- 24 files changed, 648 insertions(+), 61 deletions(-) create mode 100644 controllers/stockrequestController.go create mode 100644 create_table.go create mode 100644 models/stockrequest.go create mode 100644 repositories/stockrequest.go create mode 100644 services/stockrequestService.go diff --git a/controllers/orderController.go b/controllers/orderController.go index f1b4824..6484f6d 100644 --- a/controllers/orderController.go +++ b/controllers/orderController.go @@ -377,7 +377,6 @@ func (ctl *OrderController) GetRevenueSummary(c *fiber.Ctx) error { "status": false, }) } - return c.Status(http.StatusOK).JSON(fiber.Map{ "code": http.StatusOK, "message": "Success", @@ -386,3 +385,33 @@ func (ctl *OrderController) GetRevenueSummary(c *fiber.Ctx) error { }) } +func (ctl *OrderController) GetSalesSummary(c *fiber.Ctx) error { + tid, _ := strconv.Atoi(c.Query("tenantid")) + lid, _ := strconv.Atoi(c.Query("locationid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + if tid == 0 { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "tenantid query parameter is required", + "status": false, + }) + } + + data, err := ctl.orderService.GetSalesSummary(tid, 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, + }) +} diff --git a/controllers/partnerController.go b/controllers/partnerController.go index dae14fa..66cd560 100644 --- a/controllers/partnerController.go +++ b/controllers/partnerController.go @@ -20,7 +20,7 @@ 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")) + tid, _ := strconv.Atoi(c.Query("tenantid")) result, err := ctl.partnerService.GetActiveRiders(pid, aid, uid, tid) if err != nil { @@ -109,7 +109,6 @@ func (ctl *PartnerController) GetLocationConfig(c *fiber.Ctx) error { } - func (ctl *PartnerController) GetRiderLogs(c *fiber.Ctx) error { pid, _ := strconv.Atoi(c.Query("partnerid")) aid, _ := strconv.Atoi(c.Query("applocationid")) @@ -134,6 +133,29 @@ func (ctl *PartnerController) GetRiderLogs(c *fiber.Ctx) error { }) } +func (ctl *PartnerController) GetFleetSummary(c *fiber.Ctx) error { + aid, _ := strconv.Atoi(c.Query("applocationid")) + tid, _ := strconv.Atoi(c.Query("tenantid")) + fdate := c.Query("fromdate") + tdate := c.Query("todate") + + result, err := ctl.partnerService.GetFleetSummary(aid, tid, 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": result, + }) +} func (ctl *PartnerController) GetRiderInfo(c *fiber.Ctx) error { uid, _ := strconv.Atoi(c.Query("userid")) diff --git a/controllers/stockrequestController.go b/controllers/stockrequestController.go new file mode 100644 index 0000000..2684b27 --- /dev/null +++ b/controllers/stockrequestController.go @@ -0,0 +1,69 @@ +package controllers + +import ( + "net/http" + "strconv" + + "nearle/models" + "nearle/services" + + "github.com/gofiber/fiber/v2" +) + +type StockRequestController struct { + stockRequestService services.StockRequestService +} + +func NewStockRequestController(stockRequestService services.StockRequestService) *StockRequestController { + return &StockRequestController{stockRequestService: stockRequestService} +} + +func (ctl *StockRequestController) CreateStockRequest(c *fiber.Ctx) error { + var input models.StockRequest + if err := c.BodyParser(&input); err != nil { + return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false}) + } + + if input.Status == "" { + input.Status = "Pending" + } + + err := ctl.stockRequestService.CreateStockRequest(&input) + if err != nil { + return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false}) + } + + return c.JSON(fiber.Map{"code": 200, "message": "Stock request created", "status": true, "details": input}) +} + +func (ctl *StockRequestController) GetStockRequests(c *fiber.Ctx) error { + tenantID, _ := strconv.Atoi(c.Query("tenantid", "0")) + locationID, _ := strconv.Atoi(c.Query("locationid", "0")) + status := c.Query("status", "") + pageNo, _ := strconv.Atoi(c.Query("pageno", "1")) + pageSize, _ := strconv.Atoi(c.Query("pagesize", "50")) + + data, err := ctl.stockRequestService.GetStockRequests(tenantID, locationID, status, pageNo, pageSize) + 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 *StockRequestController) UpdateStockRequest(c *fiber.Ctx) error { + var input struct { + RequestID int `json:"requestid"` + Status string `json:"status"` + } + if err := c.BodyParser(&input); err != nil { + return c.JSON(fiber.Map{"code": http.StatusBadRequest, "message": "Invalid input", "status": false}) + } + + err := ctl.stockRequestService.UpdateStockRequest(input.RequestID, input.Status) + if err != nil { + return c.JSON(fiber.Map{"code": http.StatusInternalServerError, "message": err.Error(), "status": false}) + } + + return c.JSON(fiber.Map{"code": 200, "message": "Stock request updated", "status": true}) +} diff --git a/controllers/tenantController.go b/controllers/tenantController.go index a349acf..2973d57 100644 --- a/controllers/tenantController.go +++ b/controllers/tenantController.go @@ -252,6 +252,40 @@ func (ctl *TenantController) CreateLocation(c *fiber.Ctx) error { }) } +func (ctl *TenantController) DeleteLocation(c *fiber.Ctx) error { + locationid, err := strconv.Atoi(c.Query("locationid")) + if err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid location ID", + "status": false, + }) + } + + tenantid, err := strconv.Atoi(c.Query("tenantid")) + if err != nil { + return c.Status(http.StatusBadRequest).JSON(fiber.Map{ + "code": http.StatusBadRequest, + "message": "Invalid tenant ID", + "status": false, + }) + } + + if err := ctl.tenantService.DeleteLocation(locationid, tenantid); 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": "Location Successfully Deleted", + "status": true, + }) +} + func (ctl *TenantController) GetStaffs(c *fiber.Ctx) error { tid, _ := strconv.Atoi(c.Query("tenantid")) diff --git a/create_table.go b/create_table.go new file mode 100644 index 0000000..6f306c6 --- /dev/null +++ b/create_table.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "log" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +func CreateStockRequestsTable() { + dsn := "host=66.116.207.225 user=admin password=Package@123# dbname=nearledb port=5433 sslmode=disable TimeZone=Asia/Kolkata" + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) + if err != nil { + log.Fatalf("failed to connect database: %v", err) + } + + query := `CREATE TABLE IF NOT EXISTS stockrequests ( + requestid SERIAL PRIMARY KEY, + tenantid INT NOT NULL, + locationid INT NOT NULL, + productid INT NOT NULL, + qty INT NOT NULL, + status VARCHAR(50) DEFAULT 'Pending', + created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP + );` + + if err := db.Exec(query).Error; err != nil { + log.Fatalf("failed to create table: %v", err) + } + fmt.Println("Table stockrequests created successfully") +} diff --git a/facade/container.go b/facade/container.go index 1c73ecc..77b3961 100644 --- a/facade/container.go +++ b/facade/container.go @@ -16,7 +16,8 @@ type Facade struct { UtilsController *controllers.UtilsController TenantController *controllers.TenantController PartnerController *controllers.PartnerController - CustomerController *controllers.CustomerController + CustomerController *controllers.CustomerController + StockRequestController *controllers.StockRequestController } func NewFacade(db *gorm.DB) *Facade { @@ -61,6 +62,11 @@ func NewFacade(db *gorm.DB) *Facade { customerService := services.NewCustomerService(customerRepo) customerController := controllers.NewCustomerController(customerService) + // Stock Request Module + stockRequestRepo := repositories.NewStockRequestRepository(db) + stockRequestService := services.NewStockRequestService(stockRequestRepo, productService) + stockRequestController := controllers.NewStockRequestController(stockRequestService) + return &Facade{ UserController: userController, ProductController: productController, @@ -68,7 +74,8 @@ func NewFacade(db *gorm.DB) *Facade { DeliveriesController: deliveriesController, UtilsController: utilsController, TenantController: tenantController, - PartnerController: partnerController, - CustomerController: customerController, + PartnerController: partnerController, + CustomerController: customerController, + StockRequestController: stockRequestController, } } diff --git a/main.go b/main.go index 0bf7695..0c6ecb6 100644 --- a/main.go +++ b/main.go @@ -62,9 +62,8 @@ func selectDBMiddleware(c *fiber.Ctx) error { } var currentDB *gorm.DB - if flavour == "dev" { - currentDB = db.DB - } else if flavour == "live" { + switch flavour { + case "dev", "live": currentDB = db.DB } diff --git a/models/order.go b/models/order.go index f54d803..5eaa2c6 100644 --- a/models/order.go +++ b/models/order.go @@ -452,3 +452,21 @@ type LocationRevenueDetails struct { Revenue float64 `json:"revenue"` } +type SalesSummaryChartData struct { + Date string `json:"date"` + Revenue float64 `json:"revenue"` + Orders int `json:"orders"` +} + +type SalesSummaryTopLocation struct { + Locationname string `json:"locationname"` + Revenue float64 `json:"revenue"` +} + +type SalesSummaryResponse struct { + TotalRevenue float64 `json:"totalRevenue"` + TotalOrders int `json:"totalOrders"` + AverageOrderValue float64 `json:"averageOrderValue"` + ChartData []SalesSummaryChartData `json:"chartData"` + TopLocations []SalesSummaryTopLocation `json:"topLocations"` +} diff --git a/models/partner.go b/models/partner.go index b3c5646..6da4340 100644 --- a/models/partner.go +++ b/models/partner.go @@ -86,6 +86,31 @@ type Locationconfigs struct { Status string `json:"status"` } +type FleetCounts struct { + Totalriders int `json:"totalriders"` + Activeriders int `json:"activeriders"` + Ridersworked int `json:"ridersworked"` + Totallogins int `json:"totallogins"` +} + +type FleetRiderSummary struct { + Userid int `json:"userid"` + Fullname string `json:"fullname"` + Contactno string `json:"contactno"` + Partnerid int `json:"partnerid"` + Status string `json:"status"` + Vehicleno string `json:"vehicleno"` + Vehiclename string `json:"vehiclename"` + Dayslogged int `json:"dayslogged"` + Totalworkhours float32 `json:"totalworkhours"` + Totalshorthours float32 `json:"totalshorthours"` + Totalbreakhours float32 `json:"totalbreakhours"` +} + +type FleetSummary struct { + Counts FleetCounts `json:"counts"` + Riders []FleetRiderSummary `json:"riders"` +} type RiderlogDetails struct { Logid int `json:"logid"` @@ -103,4 +128,4 @@ type RiderlogDetails struct { Shorthours float32 `json:"shorthours"` Breakhours float32 `json:"breakhours"` Logstatus int `json:"logstatus"` -} \ No newline at end of file +} diff --git a/models/stockrequest.go b/models/stockrequest.go new file mode 100644 index 0000000..4798236 --- /dev/null +++ b/models/stockrequest.go @@ -0,0 +1,20 @@ +package models + +import "time" + +type StockRequest struct { + Requestid int `json:"requestid" gorm:"primaryKey;autoIncrement;column:requestid"` + Tenantid int `json:"tenantid" gorm:"column:tenantid"` + Locationid int `json:"locationid" gorm:"column:locationid"` + Productid int `json:"productid" gorm:"column:productid"` + Productname string `json:"productname" gorm:"->"` + Productimage string `json:"productimage" gorm:"->"` + Qty int `json:"qty" gorm:"column:qty"` + Status string `json:"status" gorm:"column:status;default:Pending"` + Created time.Time `json:"created" gorm:"column:created;autoCreateTime"` + Updated time.Time `json:"updated" gorm:"column:updated;autoUpdateTime"` +} + +func (StockRequest) TableName() string { + return "stockrequests" +} diff --git a/repositories/orderRepository.go b/repositories/orderRepository.go index 2675ed8..33e1995 100644 --- a/repositories/orderRepository.go +++ b/repositories/orderRepository.go @@ -26,6 +26,7 @@ type OrderRepository interface { CreateOrder(order models.Orders) (models.Orders, error) GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error) + GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error) } type orderRepository struct { @@ -793,6 +794,100 @@ func (r *orderRepository) GetDistinctLocations() ([]models.OrderInsight, error) return locations, nil } +func (r *orderRepository) GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error) { + var summary models.SalesSummaryResponse + + whereClause := "tenantid = ? AND orderstatus IN ('delivered', 'completed') AND configid = 1" + var params []interface{} + params = append(params, tid) + + if lid != 0 { + whereClause += " AND locationid = ?" + params = append(params, lid) + } + + if fdate != "" && tdate != "" { + whereClause += " AND orderdate::date BETWEEN ? AND ?" + params = append(params, fdate, tdate) + } + + totalsQuery := fmt.Sprintf(` + SELECT + COALESCE(SUM(COALESCE(ordervalue, 0) + COALESCE(orderamount, 0) + COALESCE(deliveryamt, 0)), 0) AS total_revenue, + COUNT(orderheaderid) AS total_orders + FROM orders + WHERE %s`, whereClause) + + var result struct { + TotalRevenue float64 + TotalOrders int + } + + if err := r.db.Raw(totalsQuery, params...).Scan(&result).Error; err != nil { + return nil, err + } + + summary.TotalRevenue = result.TotalRevenue + summary.TotalOrders = result.TotalOrders + + if summary.TotalOrders > 0 { + summary.AverageOrderValue = summary.TotalRevenue / float64(summary.TotalOrders) + } + + chartQuery := fmt.Sprintf(` + SELECT + CAST(orderdate AS DATE) AS date, + COALESCE(SUM(COALESCE(ordervalue, 0) + COALESCE(orderamount, 0) + COALESCE(deliveryamt, 0)), 0) AS revenue, + COUNT(orderheaderid) AS orders + FROM orders + WHERE %s + GROUP BY CAST(orderdate AS DATE) + ORDER BY date ASC`, whereClause) + + var chartData []models.SalesSummaryChartData + if err := r.db.Raw(chartQuery, params...).Scan(&chartData).Error; err != nil { + return nil, err + } + + if chartData == nil { + chartData = []models.SalesSummaryChartData{} + } + summary.ChartData = chartData + + var topLocations []models.SalesSummaryTopLocation + if lid == 0 { + topWhere := "o.tenantid = ? AND o.orderstatus IN ('delivered', 'completed') AND o.configid = 1" + var topParams []interface{} + topParams = append(topParams, tid) + if fdate != "" && tdate != "" { + topWhere += " AND o.orderdate::date BETWEEN ? AND ?" + topParams = append(topParams, fdate, tdate) + } + + cleanTopLocQuery := fmt.Sprintf(` + SELECT + COALESCE(l.locationname, 'Unknown') AS locationname, + COALESCE(SUM(COALESCE(o.ordervalue, 0) + COALESCE(o.orderamount, 0) + COALESCE(o.deliveryamt, 0)), 0) AS revenue + FROM orders o + LEFT JOIN tenantlocations l ON o.locationid = l.locationid + WHERE %s + GROUP BY l.locationid, l.locationname + ORDER BY revenue DESC + LIMIT 5`, topWhere) + + if err := r.db.Raw(cleanTopLocQuery, topParams...).Scan(&topLocations).Error; err != nil { + return nil, err + } + } + + if topLocations == nil { + topLocations = []models.SalesSummaryTopLocation{} + } + summary.TopLocations = topLocations + + return &summary, nil +} + func (r *orderRepository) GetMonthlyOrders(applocationid string) (*models.Ordermonths, error) { var orderMonths models.Ordermonths @@ -920,13 +1015,14 @@ func (r *orderRepository) getSequenceno(tid int, prefix string) string { var q1 string // Formats the ID as tenantid-subprefix+seqno (e.g., 908-20245189) - if prefix == "ORD" { + switch prefix { + case "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" { + case "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 @@ -947,11 +1043,12 @@ func (r *orderRepository) getSequenceno(tid int, prefix string) string { func (r *orderRepository) updateSeqno(tid int, prefix string) error { var field string - if prefix == "ORD" { + switch prefix { + case "ORD": field = "orderseqno" - } else if prefix == "INV" { + case "INV": field = "invoiceseqno" - } else { + default: return fmt.Errorf("invalid prefix: %s", prefix) } diff --git a/repositories/partnerRepository.go b/repositories/partnerRepository.go index fadd25d..80e1bf2 100644 --- a/repositories/partnerRepository.go +++ b/repositories/partnerRepository.go @@ -15,6 +15,7 @@ type PartnerRepository interface { GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) GetRiderInfo(userid int) (models.RiderInfo, error) + GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error) } type partnerRepository struct { @@ -197,3 +198,100 @@ func (r *partnerRepository) GetRiderInfo(userid int) (models.RiderInfo, error) { return data, nil } + +func (r *partnerRepository) GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error) { + var summary models.FleetSummary + + // --- Fleet membership filter (app_users aliased as a) --- + memberWhere := "a.configid = 6" + var memberArgs []interface{} + if aid != 0 { + memberWhere += " AND a.applocationid = ?" + memberArgs = append(memberArgs, aid) + } + if tid != 0 { + memberWhere += " AND a.tenantid = ?" + memberArgs = append(memberArgs, tid) + } + + countsQuery := ` + SELECT + COUNT(*) AS totalriders, + COUNT(*) FILTER (WHERE a.status = 'Active') AS activeriders + FROM app_users a + WHERE ` + memberWhere + + if err := r.db.Raw(countsQuery, memberArgs...).Scan(&summary.Counts).Error; err != nil { + return models.FleetSummary{}, err + } + + // --- Log-range filter (app_users aliased as b, riderlogs as a) --- + logWhere := "b.configid = 6" + var logArgs []interface{} + if aid != 0 { + logWhere += " AND b.applocationid = ?" + logArgs = append(logArgs, aid) + } + if tid != 0 { + logWhere += " AND b.tenantid = ?" + logArgs = append(logArgs, tid) + } + if fdate != "" && tdate != "" { + logWhere += " AND a.logdate::date BETWEEN ? AND ?" + logArgs = append(logArgs, fdate, tdate) + } else { + logWhere += " AND a.logdate::date = CURRENT_DATE" + } + + var worked struct { + Ridersworked int `json:"ridersworked"` + Totallogins int `json:"totallogins"` + } + + workedQuery := ` + SELECT + COUNT(DISTINCT a.userid) AS ridersworked, + COUNT(*) AS totallogins + FROM riderlogs a + INNER JOIN app_users b ON a.userid = b.userid + WHERE ` + logWhere + + if err := r.db.Raw(workedQuery, logArgs...).Scan(&worked).Error; err != nil { + return models.FleetSummary{}, err + } + summary.Counts.Ridersworked = worked.Ridersworked + summary.Counts.Totallogins = worked.Totallogins + + // --- Per-rider breakdown over the range --- + // Breaks are pre-aggregated per log so the join cannot inflate the work/short-hour sums. + ridersQuery := ` + SELECT + b.userid, + CONCAT(b.firstname, ' ', b.lastname) AS fullname, + b.contactno, + b.partnerid, + b.status, + rs.vehicleno, + rs.vehiclename, + COUNT(DISTINCT a.logdate::date) AS dayslogged, + COALESCE(SUM(a.workhours), 0) AS totalworkhours, + COALESCE(SUM(a.shorthours), 0) AS totalshorthours, + COALESCE(SUM(bk.breakhours), 0) AS totalbreakhours + FROM riderlogs a + INNER JOIN app_users b ON a.userid = b.userid + LEFT JOIN ridersettings rs ON b.userid = rs.userid + LEFT JOIN ( + SELECT logid, userid, SUM(breakhours) AS breakhours + FROM riderbreaks + GROUP BY logid, userid + ) bk ON a.logid = bk.logid AND a.userid = bk.userid + WHERE ` + logWhere + ` + GROUP BY b.userid, b.firstname, b.lastname, b.contactno, b.partnerid, b.status, rs.vehicleno, rs.vehiclename + ORDER BY fullname ASC` + + if err := r.db.Raw(ridersQuery, logArgs...).Find(&summary.Riders).Error; err != nil { + return models.FleetSummary{}, err + } + + return summary, nil +} diff --git a/repositories/stockrequest.go b/repositories/stockrequest.go new file mode 100644 index 0000000..2658e67 --- /dev/null +++ b/repositories/stockrequest.go @@ -0,0 +1,56 @@ +package repositories + +import ( + "nearle/models" + + "gorm.io/gorm" +) + +type StockRequestRepository interface { + CreateStockRequest(req *models.StockRequest) error + GetStockRequests(tenantID int, locationID int, status string, pageNo int, pageSize int) ([]models.StockRequest, error) + GetStockRequestByID(requestID int) (*models.StockRequest, error) + UpdateStockRequest(requestID int, status string) error +} + +type stockRequestRepository struct { + db *gorm.DB +} + +func NewStockRequestRepository(db *gorm.DB) StockRequestRepository { + return &stockRequestRepository{db: db} +} + +func (r *stockRequestRepository) CreateStockRequest(req *models.StockRequest) error { + return r.db.Create(req).Error +} + +func (r *stockRequestRepository) GetStockRequests(tenantID int, locationID int, status string, pageNo int, pageSize int) ([]models.StockRequest, error) { + var requests []models.StockRequest + query := r.db.Table("stockrequests"). + Select("stockrequests.*, products.productname, products.productimage"). + Joins("left join products on products.productid = stockrequests.productid"). + Where("stockrequests.tenantid = ?", tenantID) + + if locationID > 0 { + query = query.Where("stockrequests.locationid = ?", locationID) + } + + if status != "" { + query = query.Where("stockrequests.status = ?", status) + } + + offset := (pageNo - 1) * pageSize + err := query.Order("stockrequests.created DESC").Offset(offset).Limit(pageSize).Find(&requests).Error + return requests, err +} + +func (r *stockRequestRepository) GetStockRequestByID(requestID int) (*models.StockRequest, error) { + var req models.StockRequest + err := r.db.Table("stockrequests").Where("requestid = ?", requestID).First(&req).Error + return &req, err +} + +func (r *stockRequestRepository) UpdateStockRequest(requestID int, status string) error { + return r.db.Model(&models.StockRequest{}).Where("requestid = ?", requestID).Update("status", status).Error +} diff --git a/repositories/tenantRepository.go b/repositories/tenantRepository.go index 2f6a86a..2f4cc38 100644 --- a/repositories/tenantRepository.go +++ b/repositories/tenantRepository.go @@ -21,6 +21,7 @@ type TenantRepository interface { GetTenantPricing(tid, aid int) (*models.Tenantpricing, error) UpdateLocation(input models.Tenantlocations) error CreateLocation(data models.Tenantlocations) error + DeleteLocation(locationid int, tenantid int) error GetStaffs(tid int) ([]models.StaffInfo, error) CreateStaff(user models.User) error UpdateStaff(user models.User) error @@ -290,52 +291,10 @@ func (r *tenantRepository) UpdateLocation(input models.Tenantlocations) error { return nil } -func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error { - var cust models.Customers - var tcust models.Tenantcustomers - var custloc models.Customerlocations - +func (r *tenantRepository) DeleteLocation(locationid int, tenantid int) error { 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 { + if err := tx.Where("locationid=? AND tenantid=?", locationid, tenantid).Delete(&models.Tenantlocations{}).Error; err != nil { tx.Rollback() return err } @@ -347,6 +306,13 @@ func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error { return nil } +func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error { + if err := r.db.Create(&data).Error; err != nil { + return err + } + return nil +} + func (r *tenantRepository) GetStaffs(tid int) ([]models.StaffInfo, error) { var data []models.StaffInfo diff --git a/repositories/userRepository.go b/repositories/userRepository.go index 6ae513a..7c62ac6 100644 --- a/repositories/userRepository.go +++ b/repositories/userRepository.go @@ -24,6 +24,7 @@ type UserRepository interface { GetUserById(uid int) (models.UserInfo, error) GetUserLogin(field, value string, configid int) (int, string, string, int) UpdateUserFcmToken(uid int, token string) error + GetLocationStatus(locationid int) string } type userRepository struct { @@ -277,3 +278,12 @@ 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 } + +func (r *userRepository) GetLocationStatus(locationid int) string { + var status string + query := `SELECT status FROM tenantlocations WHERE locationid = ?` + r.db.Raw(query, locationid).Row().Scan(&status) + return status +} + + diff --git a/routes/orderroutes.go b/routes/orderroutes.go index 9a06f2e..9cd0ca0 100644 --- a/routes/orderroutes.go +++ b/routes/orderroutes.go @@ -24,6 +24,9 @@ func RegisterOrderRoutes(api fiber.Router, f *facade.Facade) { orders.Put("/updateorder", f.OrderController.UpdateOrder) orders.Post("/createorder", f.OrderController.CreateOrderv3) + reports := api.Group("/v1/web/reports") + reports.Get("/sales-summary", f.OrderController.GetSalesSummary) + orders = api.Group("/v1/mob/orders") orders.Post("/createorder", f.OrderController.CreateOrderv3) diff --git a/routes/partnerroutes.go b/routes/partnerroutes.go index 1c7530d..cfb9c6e 100644 --- a/routes/partnerroutes.go +++ b/routes/partnerroutes.go @@ -15,6 +15,7 @@ func RegisterPartnerRoutes(api fiber.Router, f *facade.Facade) { partner.Get("/getridershifts", f.PartnerController.GetRiderShifts) partner.Get("/getlocations", f.PartnerController.GetLocationConfig) partner.Get("/getriderlogs", f.PartnerController.GetRiderLogs) + partner.Get("/getfleetsummary", f.PartnerController.GetFleetSummary) partner = api.Group("/v1/mob/partners") diff --git a/routes/productroutes.go b/routes/productroutes.go index cbeddea..524c135 100644 --- a/routes/productroutes.go +++ b/routes/productroutes.go @@ -27,6 +27,10 @@ func RegisterProductRoutes(api fiber.Router, f *facade.Facade) { products.Put("/updateproductlocation", f.ProductController.UpdateProductLocation) products.Post("/createproductlocation", f.ProductController.CreateProductLocation) products.Post("/createproductvariant", f.ProductController.CreateProductVariant) + + products.Post("/createstockrequest", f.StockRequestController.CreateStockRequest) + products.Get("/getstockrequests", f.StockRequestController.GetStockRequests) + products.Put("/updatestockrequest", f.StockRequestController.UpdateStockRequest) products = api.Group("/v1/mob/products") diff --git a/routes/tenantroutes.go b/routes/tenantroutes.go index 32b22fb..4ee43b6 100644 --- a/routes/tenantroutes.go +++ b/routes/tenantroutes.go @@ -17,6 +17,7 @@ func RegisterTenantRoutes(api fiber.Router, f *facade.Facade) { tenant.Post("/createtenantcustomer", f.TenantController.CreateTenantCustomer) tenant.Put("/updatelocation", f.TenantController.UpdateLocation) tenant.Post("/createlocation", f.TenantController.CreateLocation) + tenant.Delete("/deletelocation", f.TenantController.DeleteLocation) tenant.Post("/createtenantlocation", f.TenantController.CreateTenantLocation) tenant.Put("/updatetenantlocation", f.TenantController.UpdateTenantLocation) diff --git a/services/orderService.go b/services/orderService.go index 7ea3e38..18b5569 100644 --- a/services/orderService.go +++ b/services/orderService.go @@ -21,6 +21,7 @@ type OrderService interface { GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error) + GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error) } type orderService struct { @@ -91,3 +92,7 @@ func (s *orderService) GetRevenueSummary(tid, lid int, fdate, tdate string) (*mo return s.repo.GetRevenueSummary(tid, lid, fdate, tdate) } +func (s *orderService) GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error) { + return s.repo.GetSalesSummary(tid, lid, fdate, tdate) +} + diff --git a/services/partnerService.go b/services/partnerService.go index 0387e79..da7c4f2 100644 --- a/services/partnerService.go +++ b/services/partnerService.go @@ -12,6 +12,7 @@ type PartnerService interface { GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) GetRiderInfo(userid int) (models.RiderInfo, error) + GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error) } type partnerService struct { @@ -53,3 +54,7 @@ func (s *partnerService) GetRiderLogs(pid, aid int, fdate, tdate string) ([]mode func (s *partnerService) GetRiderInfo(userid int) (models.RiderInfo, error) { return s.repo.GetRiderInfo(userid) } + +func (s *partnerService) GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error) { + return s.repo.GetFleetSummary(aid, tid, fdate, tdate) +} diff --git a/services/stockrequestService.go b/services/stockrequestService.go new file mode 100644 index 0000000..969b916 --- /dev/null +++ b/services/stockrequestService.go @@ -0,0 +1,58 @@ +package services + +import ( + "nearle/models" + "nearle/repositories" + "time" +) + +type StockRequestService interface { + CreateStockRequest(req *models.StockRequest) error + GetStockRequests(tenantID int, locationID int, status string, pageNo int, pageSize int) ([]models.StockRequest, error) + UpdateStockRequest(requestID int, status string) error +} + +type stockRequestService struct { + repo repositories.StockRequestRepository + productService ProductService +} + +func NewStockRequestService(repo repositories.StockRequestRepository, productService ProductService) StockRequestService { + return &stockRequestService{repo: repo, productService: productService} +} + +func (s *stockRequestService) CreateStockRequest(req *models.StockRequest) error { + return s.repo.CreateStockRequest(req) +} + +func (s *stockRequestService) GetStockRequests(tenantID int, locationID int, status string, pageNo int, pageSize int) ([]models.StockRequest, error) { + return s.repo.GetStockRequests(tenantID, locationID, status, pageNo, pageSize) +} + +func (s *stockRequestService) UpdateStockRequest(requestID int, status string) error { + // If the request is being marked as Received, we need to update the actual store inventory + if status == "Received" { + req, err := s.repo.GetStockRequestByID(requestID) + if err != nil { + return err + } + + if req.Status != "Received" { // prevent double receiving + stk := models.Productstock{ + Tenantid: req.Tenantid, + Locationid: req.Locationid, + Productid: req.Productid, + Quantity: req.Qty, + Stocktype: "Credit", + Stockdate: time.Now(), + } + + err = s.productService.CreateProductStock([]models.Productstock{stk}) + if err != nil { + return err + } + } + } + + return s.repo.UpdateStockRequest(requestID, status) +} diff --git a/services/tenantService.go b/services/tenantService.go index dd24fb4..b1c9609 100644 --- a/services/tenantService.go +++ b/services/tenantService.go @@ -17,6 +17,7 @@ type TenantService interface { GetTenantPricing(tid, aid int) (models.Tenantpricing, error) UpdateLocation(input models.Tenantlocations) error CreateLocation(data models.Tenantlocations) error + DeleteLocation(locationid int, tenantid int) error GetStaffs(tid int) ([]models.StaffInfo, error) CreateStaff(user models.User) error UpdateStaff(user models.User) error @@ -89,6 +90,10 @@ func (s *tenantService) CreateLocation(data models.Tenantlocations) error { return s.repo.CreateLocation(data) } +func (s *tenantService) DeleteLocation(locationid int, tenantid int) error { + return s.repo.DeleteLocation(locationid, tenantid) +} + func (s *tenantService) GetStaffs(tid int) ([]models.StaffInfo, error) { return s.repo.GetStaffs(tid) } diff --git a/services/userService.go b/services/userService.go index 9ab58db..932de38 100644 --- a/services/userService.go +++ b/services/userService.go @@ -143,6 +143,19 @@ func (s *userService) AppLogin(user models.User) (models.TenantUserInfo, fiber.M // ✅ Fetch tenant user info info := s.repo.GetTenantUserById(uid) + // ✅ Check if assigned store is inactive + if info.Locationid > 0 { + storeStatus := s.repo.GetLocationStatus(info.Locationid) + if storeStatus == "InActive" { + resp := fiber.Map{ + "status": false, + "code": 403, + "message": "Assigned store is inactive. Contact admin.", + } + return models.TenantUserInfo{}, resp, errors.New("inactive store") + } + } + // ✅ Return success response resp := fiber.Map{ "status": true, @@ -247,8 +260,6 @@ func (s *userService) TenantWebLogin(user models.User) (models.TenantUserInfo, m } } - user.Userid = uid - // Step 4: Update FCM if provided if user.Userfcmtoken != "" { _ = s.repo.UpdateUserFcmToken(uid, user.Userfcmtoken) @@ -257,6 +268,18 @@ func (s *userService) TenantWebLogin(user models.User) (models.TenantUserInfo, m // Step 5: Get full tenant info info := s.repo.GetTenantUserById(uid) + // Step 6: Check if assigned store is inactive + if info.Locationid > 0 { + storeStatus := s.repo.GetLocationStatus(info.Locationid) + if storeStatus == "InActive" { + return models.TenantUserInfo{}, map[string]interface{}{ + "status": false, + "code": 403, + "message": "Assigned store is inactive. Contact admin.", + } + } + } + return info, map[string]interface{}{ "status": true, "code": 200,