stock request table created

This commit is contained in:
2026-07-04 17:49:54 +05:30
parent f220c24c17
commit abe2cb997b
24 changed files with 648 additions and 61 deletions

View File

@@ -377,7 +377,6 @@ func (ctl *OrderController) GetRevenueSummary(c *fiber.Ctx) error {
"status": false, "status": false,
}) })
} }
return c.Status(http.StatusOK).JSON(fiber.Map{ return c.Status(http.StatusOK).JSON(fiber.Map{
"code": http.StatusOK, "code": http.StatusOK,
"message": "Success", "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,
})
}

View File

@@ -20,7 +20,7 @@ func (ctl *PartnerController) GetActiveRiders(c *fiber.Ctx) error {
pid, _ := strconv.Atoi(c.Query("partnerid")) pid, _ := strconv.Atoi(c.Query("partnerid"))
aid, _ := strconv.Atoi(c.Query("applocationid")) aid, _ := strconv.Atoi(c.Query("applocationid"))
uid, _ := strconv.Atoi(c.Query("userid")) 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) result, err := ctl.partnerService.GetActiveRiders(pid, aid, uid, tid)
if err != nil { if err != nil {
@@ -109,7 +109,6 @@ func (ctl *PartnerController) GetLocationConfig(c *fiber.Ctx) error {
} }
func (ctl *PartnerController) GetRiderLogs(c *fiber.Ctx) error { func (ctl *PartnerController) GetRiderLogs(c *fiber.Ctx) error {
pid, _ := strconv.Atoi(c.Query("partnerid")) pid, _ := strconv.Atoi(c.Query("partnerid"))
aid, _ := strconv.Atoi(c.Query("applocationid")) 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 { func (ctl *PartnerController) GetRiderInfo(c *fiber.Ctx) error {
uid, _ := strconv.Atoi(c.Query("userid")) uid, _ := strconv.Atoi(c.Query("userid"))

View File

@@ -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})
}

View File

@@ -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 { func (ctl *TenantController) GetStaffs(c *fiber.Ctx) error {
tid, _ := strconv.Atoi(c.Query("tenantid")) tid, _ := strconv.Atoi(c.Query("tenantid"))

32
create_table.go Normal file
View File

@@ -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")
}

View File

@@ -16,7 +16,8 @@ type Facade struct {
UtilsController *controllers.UtilsController UtilsController *controllers.UtilsController
TenantController *controllers.TenantController TenantController *controllers.TenantController
PartnerController *controllers.PartnerController PartnerController *controllers.PartnerController
CustomerController *controllers.CustomerController CustomerController *controllers.CustomerController
StockRequestController *controllers.StockRequestController
} }
func NewFacade(db *gorm.DB) *Facade { func NewFacade(db *gorm.DB) *Facade {
@@ -61,6 +62,11 @@ func NewFacade(db *gorm.DB) *Facade {
customerService := services.NewCustomerService(customerRepo) customerService := services.NewCustomerService(customerRepo)
customerController := controllers.NewCustomerController(customerService) customerController := controllers.NewCustomerController(customerService)
// Stock Request Module
stockRequestRepo := repositories.NewStockRequestRepository(db)
stockRequestService := services.NewStockRequestService(stockRequestRepo, productService)
stockRequestController := controllers.NewStockRequestController(stockRequestService)
return &Facade{ return &Facade{
UserController: userController, UserController: userController,
ProductController: productController, ProductController: productController,
@@ -68,7 +74,8 @@ func NewFacade(db *gorm.DB) *Facade {
DeliveriesController: deliveriesController, DeliveriesController: deliveriesController,
UtilsController: utilsController, UtilsController: utilsController,
TenantController: tenantController, TenantController: tenantController,
PartnerController: partnerController, PartnerController: partnerController,
CustomerController: customerController, CustomerController: customerController,
StockRequestController: stockRequestController,
} }
} }

View File

@@ -62,9 +62,8 @@ func selectDBMiddleware(c *fiber.Ctx) error {
} }
var currentDB *gorm.DB var currentDB *gorm.DB
if flavour == "dev" { switch flavour {
currentDB = db.DB case "dev", "live":
} else if flavour == "live" {
currentDB = db.DB currentDB = db.DB
} }

View File

@@ -452,3 +452,21 @@ type LocationRevenueDetails struct {
Revenue float64 `json:"revenue"` 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"`
}

View File

@@ -86,6 +86,31 @@ type Locationconfigs struct {
Status string `json:"status"` 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 { type RiderlogDetails struct {
Logid int `json:"logid"` Logid int `json:"logid"`
@@ -103,4 +128,4 @@ type RiderlogDetails struct {
Shorthours float32 `json:"shorthours"` Shorthours float32 `json:"shorthours"`
Breakhours float32 `json:"breakhours"` Breakhours float32 `json:"breakhours"`
Logstatus int `json:"logstatus"` Logstatus int `json:"logstatus"`
} }

20
models/stockrequest.go Normal file
View File

@@ -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"
}

View File

@@ -26,6 +26,7 @@ type OrderRepository interface {
CreateOrder(order models.Orders) (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) 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) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error)
GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error)
} }
type orderRepository struct { type orderRepository struct {
@@ -793,6 +794,100 @@ func (r *orderRepository) GetDistinctLocations() ([]models.OrderInsight, error)
return locations, nil 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) { func (r *orderRepository) GetMonthlyOrders(applocationid string) (*models.Ordermonths, error) {
var orderMonths models.Ordermonths var orderMonths models.Ordermonths
@@ -920,13 +1015,14 @@ func (r *orderRepository) getSequenceno(tid int, prefix string) string {
var q1 string var q1 string
// Formats the ID as tenantid-subprefix+seqno (e.g., 908-20245189) // Formats the ID as tenantid-subprefix+seqno (e.g., 908-20245189)
if prefix == "ORD" { switch prefix {
case "ORD":
q1 = `SELECT CONCAT(tenantid, '-', 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, 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 COALESCE(MAX(orderseqno) + 1, 1)) AS orderseqno
FROM ordersequences WHERE tenantid = ? FROM ordersequences WHERE tenantid = ?
GROUP BY tenantid, subprefix` GROUP BY tenantid, subprefix`
} else if prefix == "INV" { case "INV":
q1 = `SELECT CONCAT(tenantid, '-', 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, 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 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 { func (r *orderRepository) updateSeqno(tid int, prefix string) error {
var field string var field string
if prefix == "ORD" { switch prefix {
case "ORD":
field = "orderseqno" field = "orderseqno"
} else if prefix == "INV" { case "INV":
field = "invoiceseqno" field = "invoiceseqno"
} else { default:
return fmt.Errorf("invalid prefix: %s", prefix) return fmt.Errorf("invalid prefix: %s", prefix)
} }

View File

@@ -15,6 +15,7 @@ type PartnerRepository interface {
GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error)
GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error)
GetRiderInfo(userid int) (models.RiderInfo, error) GetRiderInfo(userid int) (models.RiderInfo, error)
GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error)
} }
type partnerRepository struct { type partnerRepository struct {
@@ -197,3 +198,100 @@ func (r *partnerRepository) GetRiderInfo(userid int) (models.RiderInfo, error) {
return data, nil 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
}

View File

@@ -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
}

View File

@@ -21,6 +21,7 @@ type TenantRepository interface {
GetTenantPricing(tid, aid int) (*models.Tenantpricing, error) GetTenantPricing(tid, aid int) (*models.Tenantpricing, error)
UpdateLocation(input models.Tenantlocations) error UpdateLocation(input models.Tenantlocations) error
CreateLocation(data models.Tenantlocations) error CreateLocation(data models.Tenantlocations) error
DeleteLocation(locationid int, tenantid int) error
GetStaffs(tid int) ([]models.StaffInfo, error) GetStaffs(tid int) ([]models.StaffInfo, error)
CreateStaff(user models.User) error CreateStaff(user models.User) error
UpdateStaff(user models.User) error UpdateStaff(user models.User) error
@@ -290,52 +291,10 @@ func (r *tenantRepository) UpdateLocation(input models.Tenantlocations) error {
return nil return nil
} }
func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error { func (r *tenantRepository) DeleteLocation(locationid int, tenantid int) error {
var cust models.Customers
var tcust models.Tenantcustomers
var custloc models.Customerlocations
tx := r.db.Begin() tx := r.db.Begin()
if err := tx.Create(&data).Error; err != nil { if err := tx.Where("locationid=? AND tenantid=?", locationid, tenantid).Delete(&models.Tenantlocations{}).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() tx.Rollback()
return err return err
} }
@@ -347,6 +306,13 @@ func (r *tenantRepository) CreateLocation(data models.Tenantlocations) error {
return nil 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) { func (r *tenantRepository) GetStaffs(tid int) ([]models.StaffInfo, error) {
var data []models.StaffInfo var data []models.StaffInfo

View File

@@ -24,6 +24,7 @@ type UserRepository interface {
GetUserById(uid int) (models.UserInfo, error) GetUserById(uid int) (models.UserInfo, error)
GetUserLogin(field, value string, configid int) (int, string, string, int) GetUserLogin(field, value string, configid int) (int, string, string, int)
UpdateUserFcmToken(uid int, token string) error UpdateUserFcmToken(uid int, token string) error
GetLocationStatus(locationid int) string
} }
type userRepository struct { type userRepository struct {
@@ -277,3 +278,12 @@ func (r *userRepository) UpdateUserFcmToken(userid int, fcmToken string) error {
query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?` query := `UPDATE app_users SET userfcmtoken = ? WHERE userid = ?`
return r.db.Exec(query, fcmToken, userid).Error 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
}

View File

@@ -24,6 +24,9 @@ func RegisterOrderRoutes(api fiber.Router, f *facade.Facade) {
orders.Put("/updateorder", f.OrderController.UpdateOrder) orders.Put("/updateorder", f.OrderController.UpdateOrder)
orders.Post("/createorder", f.OrderController.CreateOrderv3) 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 = api.Group("/v1/mob/orders")
orders.Post("/createorder", f.OrderController.CreateOrderv3) orders.Post("/createorder", f.OrderController.CreateOrderv3)

View File

@@ -15,6 +15,7 @@ func RegisterPartnerRoutes(api fiber.Router, f *facade.Facade) {
partner.Get("/getridershifts", f.PartnerController.GetRiderShifts) partner.Get("/getridershifts", f.PartnerController.GetRiderShifts)
partner.Get("/getlocations", f.PartnerController.GetLocationConfig) partner.Get("/getlocations", f.PartnerController.GetLocationConfig)
partner.Get("/getriderlogs", f.PartnerController.GetRiderLogs) partner.Get("/getriderlogs", f.PartnerController.GetRiderLogs)
partner.Get("/getfleetsummary", f.PartnerController.GetFleetSummary)
partner = api.Group("/v1/mob/partners") partner = api.Group("/v1/mob/partners")

View File

@@ -27,6 +27,10 @@ func RegisterProductRoutes(api fiber.Router, f *facade.Facade) {
products.Put("/updateproductlocation", f.ProductController.UpdateProductLocation) products.Put("/updateproductlocation", f.ProductController.UpdateProductLocation)
products.Post("/createproductlocation", f.ProductController.CreateProductLocation) products.Post("/createproductlocation", f.ProductController.CreateProductLocation)
products.Post("/createproductvariant", f.ProductController.CreateProductVariant) 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") products = api.Group("/v1/mob/products")

View File

@@ -17,6 +17,7 @@ func RegisterTenantRoutes(api fiber.Router, f *facade.Facade) {
tenant.Post("/createtenantcustomer", f.TenantController.CreateTenantCustomer) tenant.Post("/createtenantcustomer", f.TenantController.CreateTenantCustomer)
tenant.Put("/updatelocation", f.TenantController.UpdateLocation) tenant.Put("/updatelocation", f.TenantController.UpdateLocation)
tenant.Post("/createlocation", f.TenantController.CreateLocation) tenant.Post("/createlocation", f.TenantController.CreateLocation)
tenant.Delete("/deletelocation", f.TenantController.DeleteLocation)
tenant.Post("/createtenantlocation", f.TenantController.CreateTenantLocation) tenant.Post("/createtenantlocation", f.TenantController.CreateTenantLocation)
tenant.Put("/updatetenantlocation", f.TenantController.UpdateTenantLocation) tenant.Put("/updatetenantlocation", f.TenantController.UpdateTenantLocation)

View File

@@ -21,6 +21,7 @@ type OrderService interface {
GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error) GetCustomerOrdersv3(customerID, tenantID, moduleID, fromDate, toDate, orderStatus, keyword string, pageSize, offset int) ([]models.CustomerOrder, error)
GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error) GetTenantLocationOrders(input models.DeliveryQuery) ([]models.OrderInfo, error)
GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error) GetRevenueSummary(tid, lid int, fdate, tdate string) (*models.TenantRevenueSummary, error)
GetSalesSummary(tid, lid int, fdate, tdate string) (*models.SalesSummaryResponse, error)
} }
type orderService struct { 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) 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)
}

View File

@@ -12,6 +12,7 @@ type PartnerService interface {
GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error) GetLocationConfig(uid, cid int) ([]models.Locationconfigs, error)
GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error) GetRiderLogs(pid, aid int, fdate, tdate string) ([]models.RiderlogDetails, error)
GetRiderInfo(userid int) (models.RiderInfo, error) GetRiderInfo(userid int) (models.RiderInfo, error)
GetFleetSummary(aid, tid int, fdate, tdate string) (models.FleetSummary, error)
} }
type partnerService struct { 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) { func (s *partnerService) GetRiderInfo(userid int) (models.RiderInfo, error) {
return s.repo.GetRiderInfo(userid) 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)
}

View File

@@ -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)
}

View File

@@ -17,6 +17,7 @@ type TenantService interface {
GetTenantPricing(tid, aid int) (models.Tenantpricing, error) GetTenantPricing(tid, aid int) (models.Tenantpricing, error)
UpdateLocation(input models.Tenantlocations) error UpdateLocation(input models.Tenantlocations) error
CreateLocation(data models.Tenantlocations) error CreateLocation(data models.Tenantlocations) error
DeleteLocation(locationid int, tenantid int) error
GetStaffs(tid int) ([]models.StaffInfo, error) GetStaffs(tid int) ([]models.StaffInfo, error)
CreateStaff(user models.User) error CreateStaff(user models.User) error
UpdateStaff(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) 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) { func (s *tenantService) GetStaffs(tid int) ([]models.StaffInfo, error) {
return s.repo.GetStaffs(tid) return s.repo.GetStaffs(tid)
} }

View File

@@ -143,6 +143,19 @@ func (s *userService) AppLogin(user models.User) (models.TenantUserInfo, fiber.M
// ✅ Fetch tenant user info // ✅ Fetch tenant user info
info := s.repo.GetTenantUserById(uid) 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 // ✅ Return success response
resp := fiber.Map{ resp := fiber.Map{
"status": true, "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 // Step 4: Update FCM if provided
if user.Userfcmtoken != "" { if user.Userfcmtoken != "" {
_ = s.repo.UpdateUserFcmToken(uid, 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 // Step 5: Get full tenant info
info := s.repo.GetTenantUserById(uid) 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{}{ return info, map[string]interface{}{
"status": true, "status": true,
"code": 200, "code": 200,