Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b51a7616 | ||
|
|
b27e40b531 | ||
|
|
44aca7641f | ||
|
|
a89ff00d94 | ||
|
|
227f11359c | ||
|
|
7a737d7f83 | ||
|
|
312ea94e62 | ||
|
|
cb8a830345 | ||
|
|
222cadd8df |
@@ -137,6 +137,10 @@ func (h *OrderHandler) ListOrders(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modelReq.OrganizationID = &contextInfo.OrganizationID
|
modelReq.OrganizationID = &contextInfo.OrganizationID
|
||||||
|
if modelReq.OutletID == nil && contextInfo.OutletID != uuid.Nil {
|
||||||
|
modelReq.OutletID = &contextInfo.OutletID
|
||||||
|
}
|
||||||
|
|
||||||
response, err := h.orderService.ListOrders(c.Request.Context(), modelReq)
|
response, err := h.orderService.ListOrders(c.Request.Context(), modelReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
|
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
|
||||||
|
|||||||
@@ -150,6 +150,11 @@ func (h *TableHandler) List(c *gin.Context) {
|
|||||||
Limit: 100,
|
Limit: 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback to context outlet ID if not provided in query
|
||||||
|
if query.OutletID == "" && contextInfo.OutletID != uuid.Nil {
|
||||||
|
query.OutletID = contextInfo.OutletID.String()
|
||||||
|
}
|
||||||
|
|
||||||
if pageStr := c.Query("page"); pageStr != "" {
|
if pageStr := c.Query("page"); pageStr != "" {
|
||||||
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
|
if page, err := strconv.Atoi(pageStr); err == nil && page > 0 {
|
||||||
query.Page = page
|
query.Page = page
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"apskel-pos-be/internal/service"
|
"apskel-pos-be/internal/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthMiddleware struct {
|
type AuthMiddleware struct {
|
||||||
@@ -45,9 +46,13 @@ func (m *AuthMiddleware) RequireAuth() gin.HandlerFunc {
|
|||||||
setKeyInContext(c, appcontext.OrganizationIDKey, userResponse.OrganizationID.String())
|
setKeyInContext(c, appcontext.OrganizationIDKey, userResponse.OrganizationID.String())
|
||||||
setKeyInContext(c, appcontext.UserIDKey, userResponse.ID.String())
|
setKeyInContext(c, appcontext.UserIDKey, userResponse.ID.String())
|
||||||
|
|
||||||
if userResponse.Role != "superadmin" {
|
// Always override OutletID from token to prevent header injection.
|
||||||
setKeyInContext(c, appcontext.OutletIDKey, userResponse.OutletID.String())
|
// Set empty string if user has no outlet, so PopulateContext header value is ignored.
|
||||||
|
outletIDStr := ""
|
||||||
|
if userResponse.OutletID != nil && *userResponse.OutletID != uuid.Nil {
|
||||||
|
outletIDStr = userResponse.OutletID.String()
|
||||||
}
|
}
|
||||||
|
setKeyInContext(c, appcontext.OutletIDKey, outletIDStr)
|
||||||
|
|
||||||
logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireAuth -> User authenticated: %s", userResponse.Email)
|
logger.FromContext(c.Request.Context()).Infof("AuthMiddleware::RequireAuth -> User authenticated: %s", userResponse.Email)
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|||||||
@@ -98,7 +98,42 @@ func (r *OrderRepositoryImpl) List(ctx context.Context, filters map[string]inter
|
|||||||
var orders []*entities.Order
|
var orders []*entities.Order
|
||||||
var total int64
|
var total int64
|
||||||
|
|
||||||
query := r.db.WithContext(ctx).Model(&entities.Order{}).
|
// organization_id is mandatory to prevent cross-org data leaks
|
||||||
|
organizationID, ok := filters["organization_id"]
|
||||||
|
if !ok {
|
||||||
|
return nil, 0, fmt.Errorf("organization_id is required for listing orders")
|
||||||
|
}
|
||||||
|
|
||||||
|
baseQuery := r.db.WithContext(ctx).Model(&entities.Order{}).
|
||||||
|
Where("organization_id = ?", organizationID)
|
||||||
|
|
||||||
|
// outlet_id is optional — if present, scope to that outlet; otherwise return all outlets in the org
|
||||||
|
if outletID, exists := filters["outlet_id"]; exists {
|
||||||
|
baseQuery = baseQuery.Where("outlet_id = ?", outletID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range filters {
|
||||||
|
switch key {
|
||||||
|
case "organization_id", "outlet_id":
|
||||||
|
// already handled above
|
||||||
|
case "search":
|
||||||
|
searchValue := "%" + value.(string) + "%"
|
||||||
|
baseQuery = baseQuery.Where("order_number ILIKE ?", searchValue)
|
||||||
|
case "date_from":
|
||||||
|
baseQuery = baseQuery.Where("created_at >= ?", value)
|
||||||
|
case "date_to":
|
||||||
|
baseQuery = baseQuery.Where("created_at <= ?", value)
|
||||||
|
default:
|
||||||
|
baseQuery = baseQuery.Where(key+" = ?", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use separate queries for count and find to avoid GORM state mutation issues
|
||||||
|
if err := baseQuery.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := baseQuery.
|
||||||
Preload("Organization").
|
Preload("Organization").
|
||||||
Preload("Outlet").
|
Preload("Outlet").
|
||||||
Preload("User").
|
Preload("User").
|
||||||
@@ -107,27 +142,8 @@ func (r *OrderRepositoryImpl) List(ctx context.Context, filters map[string]inter
|
|||||||
Preload("OrderItems.ProductVariant").
|
Preload("OrderItems.ProductVariant").
|
||||||
Preload("Payments").
|
Preload("Payments").
|
||||||
Preload("Payments.PaymentMethod").
|
Preload("Payments.PaymentMethod").
|
||||||
Preload("Payments.PaymentOrderItems")
|
Preload("Payments.PaymentOrderItems").
|
||||||
|
Limit(limit).Offset(offset).Order("created_at DESC").Find(&orders).Error
|
||||||
for key, value := range filters {
|
|
||||||
switch key {
|
|
||||||
case "search":
|
|
||||||
searchValue := "%" + value.(string) + "%"
|
|
||||||
query = query.Where("order_number ILIKE ?", searchValue)
|
|
||||||
case "date_from":
|
|
||||||
query = query.Where("created_at >= ?", value)
|
|
||||||
case "date_to":
|
|
||||||
query = query.Where("created_at <= ?", value)
|
|
||||||
default:
|
|
||||||
query = query.Where(key+" = ?", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := query.Count(&total).Error; err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err := query.Limit(limit).Offset(offset).Order("created_at DESC").Find(&orders).Error
|
|
||||||
return orders, total, err
|
return orders, total, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func (r *OrganizationRepositoryImpl) GetTotalOmset(ctx context.Context, organiza
|
|||||||
var total float64
|
var total float64
|
||||||
err := r.db.WithContext(ctx).
|
err := r.db.WithContext(ctx).
|
||||||
Table("orders").
|
Table("orders").
|
||||||
Where("organization_id = ? AND payment_status = ?", organizationID, "completed").
|
Where("organization_id = ? AND payment_status = ? AND is_void = ? AND is_refund = ?", organizationID, "completed", false, false).
|
||||||
Select("COALESCE(SUM(total_amount), 0)").
|
Select("COALESCE(SUM(total_amount), 0)").
|
||||||
Scan(&total).Error
|
Scan(&total).Error
|
||||||
return total, err
|
return total, err
|
||||||
|
|||||||
Reference in New Issue
Block a user