stock request table created
This commit is contained in:
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
|
||||
69
controllers/stockrequestController.go
Normal file
69
controllers/stockrequestController.go
Normal 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})
|
||||
}
|
||||
@@ -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"))
|
||||
|
||||
|
||||
32
create_table.go
Normal file
32
create_table.go
Normal 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")
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type Facade struct {
|
||||
TenantController *controllers.TenantController
|
||||
PartnerController *controllers.PartnerController
|
||||
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,
|
||||
@@ -70,5 +76,6 @@ func NewFacade(db *gorm.DB) *Facade {
|
||||
TenantController: tenantController,
|
||||
PartnerController: partnerController,
|
||||
CustomerController: customerController,
|
||||
StockRequestController: stockRequestController,
|
||||
}
|
||||
}
|
||||
|
||||
5
main.go
5
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
|
||||
20
models/stockrequest.go
Normal file
20
models/stockrequest.go
Normal 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"
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
56
repositories/stockrequest.go
Normal file
56
repositories/stockrequest.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ func RegisterProductRoutes(api fiber.Router, f *facade.Facade) {
|
||||
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")
|
||||
|
||||
products.Get("/getproductbyvariant", f.ProductController.GetProductByVariant)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
58
services/stockrequestService.go
Normal file
58
services/stockrequestService.go
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user