feat(loyalty): outlet loyalty settings API
Adds GET and PUT /outlets/:id/loyalty-settings (docs/prd-point-coin.md F1, PC-201) on top of the typed settings processor. The response shows every setting with its default when unset, the organization's point value, and the effective EnakPoint cashback (earn_value × point_value / earn_per_amount), so an owner cannot misread the scale. PUT applies the body on top of the current settings: fields left out keep their value, null clears an optional limit, and unknown fields are refused so a typo cannot be ignored silently. The read-only fields of the GET response are accepted and ignored, so a client can send back what it received. It returns the keys that changed. Values outside the F1 bounds answer 400, and an outlet of another organization 404. RequireAdminOrManager also lets the purchasing role through, so loyalty settings and the manual wallet adjustment from PC-107 now use a stricter RequireLoyaltyManager (superadmin, admin, manager, owner). Adds a test that registers every route, since gin panics at startup when two routes name the same path parameter differently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
39e47ff0e6
commit
2bd53ee4a4
@@ -55,12 +55,13 @@ type Router struct {
|
||||
expenseHandler *handler.ExpenseHandler
|
||||
cashAdvanceHandler *handler.CashAdvanceHandler
|
||||
walletAdminHandler *handler.WalletAdminHandler
|
||||
loyaltySettingsHandler *handler.LoyaltySettingsHandler
|
||||
authMiddleware *middleware.AuthMiddleware
|
||||
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
||||
redisClient *redis.Client
|
||||
}
|
||||
|
||||
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, purchaseCategoryService service.PurchaseCategoryService, purchaseCategoryValidator validator.PurchaseCategoryValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, userDeviceService service.UserDeviceService, userDeviceValidator validator.UserDeviceValidator, notificationService service.NotificationService, notificationValidator validator.NotificationValidator, productOutletPriceService service.ProductOutletPriceService, productOutletPriceValidator validator.ProductOutletPriceValidator, selfOrderHandler *handler.SelfOrderHandler, expenseService *service.ExpenseServiceImpl, expenseValidator *validator.ExpenseValidatorImpl, cashAdvanceService service.CashAdvanceService, cashAdvanceValidator validator.CashAdvanceValidator, walletAdminService service.WalletAdminService, walletValidator validator.WalletValidator, redisClient *redis.Client) *Router {
|
||||
func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authService service.AuthService, authMiddleware *middleware.AuthMiddleware, userService *service.UserServiceImpl, userValidator *validator.UserValidatorImpl, organizationService service.OrganizationService, organizationValidator validator.OrganizationValidator, outletService service.OutletService, outletValidator validator.OutletValidator, outletSettingService service.OutletSettingService, categoryService service.CategoryService, categoryValidator validator.CategoryValidator, productService service.ProductService, productValidator validator.ProductValidator, productVariantService service.ProductVariantService, productVariantValidator validator.ProductVariantValidator, inventoryService service.InventoryService, inventoryValidator validator.InventoryValidator, orderService service.OrderService, orderValidator validator.OrderValidator, fileService service.FileService, fileValidator validator.FileValidator, customerService service.CustomerService, customerValidator validator.CustomerValidator, paymentMethodService service.PaymentMethodService, paymentMethodValidator validator.PaymentMethodValidator, analyticsService *service.AnalyticsServiceImpl, reportService service.ReportService, tableService *service.TableServiceImpl, tableValidator *validator.TableValidator, unitService handler.UnitService, ingredientService handler.IngredientService, productRecipeService service.ProductRecipeService, vendorService service.VendorService, vendorValidator validator.VendorValidator, purchaseOrderService service.PurchaseOrderService, purchaseOrderValidator validator.PurchaseOrderValidator, purchaseCategoryService service.PurchaseCategoryService, purchaseCategoryValidator validator.PurchaseCategoryValidator, unitConverterService service.IngredientUnitConverterService, unitConverterValidator validator.IngredientUnitConverterValidator, chartOfAccountTypeService service.ChartOfAccountTypeService, chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator, chartOfAccountService service.ChartOfAccountService, chartOfAccountValidator validator.ChartOfAccountValidator, accountService service.AccountService, accountValidator validator.AccountValidator, orderIngredientTransactionService service.OrderIngredientTransactionService, orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator, gamificationService service.GamificationService, gamificationValidator validator.GamificationValidator, rewardService service.RewardService, rewardValidator validator.RewardValidator, campaignService service.CampaignService, campaignValidator validator.CampaignValidator, customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator, customerPointsService service.CustomerPointsService, spinGameService service.SpinGameService, customerAuthMiddleware *middleware.CustomerAuthMiddleware, userDeviceService service.UserDeviceService, userDeviceValidator validator.UserDeviceValidator, notificationService service.NotificationService, notificationValidator validator.NotificationValidator, productOutletPriceService service.ProductOutletPriceService, productOutletPriceValidator validator.ProductOutletPriceValidator, selfOrderHandler *handler.SelfOrderHandler, expenseService *service.ExpenseServiceImpl, expenseValidator *validator.ExpenseValidatorImpl, cashAdvanceService service.CashAdvanceService, cashAdvanceValidator validator.CashAdvanceValidator, walletAdminService service.WalletAdminService, walletValidator validator.WalletValidator, loyaltySettingsService service.LoyaltySettingsService, redisClient *redis.Client) *Router {
|
||||
|
||||
return &Router{
|
||||
config: cfg,
|
||||
@@ -107,6 +108,7 @@ func NewRouter(cfg *config.Config, healthHandler *handler.HealthHandler, authSer
|
||||
expenseHandler: handler.NewExpenseHandler(expenseService, expenseValidator),
|
||||
cashAdvanceHandler: handler.NewCashAdvanceHandler(cashAdvanceService, cashAdvanceValidator),
|
||||
walletAdminHandler: handler.NewWalletAdminHandler(walletAdminService, walletValidator),
|
||||
loyaltySettingsHandler: handler.NewLoyaltySettingsHandler(loyaltySettingsService),
|
||||
redisClient: redisClient,
|
||||
}
|
||||
}
|
||||
@@ -621,7 +623,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
marketingCustomers := gamification.Group("/customers")
|
||||
{
|
||||
marketingCustomers.GET("/:id/wallet", r.walletAdminHandler.GetCustomerWallet)
|
||||
marketingCustomers.POST("/:id/wallet/adjust", r.walletAdminHandler.AdjustCustomerWallet)
|
||||
marketingCustomers.POST("/:id/wallet/adjust", r.authMiddleware.RequireLoyaltyManager(), r.walletAdminHandler.AdjustCustomerWallet)
|
||||
}
|
||||
|
||||
campaignRules := gamification.Group("/campaign-rules")
|
||||
@@ -644,6 +646,9 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
outlets.PUT("/detail/:id", r.outletHandler.UpdateOutlet)
|
||||
outlets.GET("/printer-setting/:outlet_id", r.outletSettingHandler.GetPrinterSettings)
|
||||
outlets.PUT("/printer-setting/:outlet_id", r.outletSettingHandler.UpdatePrinterSettings)
|
||||
// EnakPoint & EnakCoin settings of the outlet (docs/prd-point-coin.md F1)
|
||||
outlets.GET("/:outlet_id/loyalty-settings", r.loyaltySettingsHandler.GetOutletSettings)
|
||||
outlets.PUT("/:outlet_id/loyalty-settings", r.authMiddleware.RequireLoyaltyManager(), r.loyaltySettingsHandler.UpdateOutletSettings)
|
||||
outlets.GET("/:outlet_id/tables/available", r.tableHandler.GetAvailableTables)
|
||||
outlets.GET("/:outlet_id/tables/occupied", r.tableHandler.GetOccupiedTables)
|
||||
// Reports
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"apskel-pos-be/config"
|
||||
"apskel-pos-be/internal/middleware"
|
||||
)
|
||||
|
||||
// Gin panics at startup when two routes disagree on a path parameter's name at the
|
||||
// same position (say /outlets/:id/x next to /outlets/:outlet_id/y). Registering every
|
||||
// route here catches that in a test instead of on deploy. Handlers are nil: nothing is
|
||||
// served, only registered.
|
||||
func TestAllRoutesRegister(t *testing.T) {
|
||||
r := &Router{
|
||||
config: &config.Config{},
|
||||
authMiddleware: middleware.NewAuthMiddleware(nil),
|
||||
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware("test"),
|
||||
}
|
||||
engine := r.Init()
|
||||
|
||||
registered := map[string]bool{}
|
||||
for _, route := range engine.Routes() {
|
||||
registered[route.Method+" "+route.Path] = true
|
||||
}
|
||||
for _, want := range []string{
|
||||
"GET /api/v1/customer/wallet",
|
||||
"GET /api/v1/customer/wallet/transactions",
|
||||
"GET /api/v1/marketing/customers/:id/wallet",
|
||||
"POST /api/v1/marketing/customers/:id/wallet/adjust",
|
||||
"GET /api/v1/outlets/:outlet_id/loyalty-settings",
|
||||
"PUT /api/v1/outlets/:outlet_id/loyalty-settings",
|
||||
} {
|
||||
assert.True(t, registered[want], want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user