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>
667 lines
38 KiB
Go
667 lines
38 KiB
Go
package app
|
|
|
|
import (
|
|
"apskel-pos-be/internal/client"
|
|
"apskel-pos-be/internal/transformer"
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"apskel-pos-be/config"
|
|
"apskel-pos-be/internal/handler"
|
|
"apskel-pos-be/internal/middleware"
|
|
"apskel-pos-be/internal/processor"
|
|
"apskel-pos-be/internal/repository"
|
|
"apskel-pos-be/internal/router"
|
|
"apskel-pos-be/internal/service"
|
|
"apskel-pos-be/internal/validator"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type App struct {
|
|
server *http.Server
|
|
db *gorm.DB
|
|
redisClient *redis.Client
|
|
router *router.Router
|
|
shutdown chan os.Signal
|
|
omsetScheduler *service.OmsetMilestoneScheduler
|
|
walletRecon *service.WalletReconciliationJob
|
|
}
|
|
|
|
func NewApp(db *gorm.DB, redisClient *redis.Client) *App {
|
|
return &App{
|
|
db: db,
|
|
redisClient: redisClient,
|
|
shutdown: make(chan os.Signal, 1),
|
|
}
|
|
}
|
|
|
|
func (a *App) Initialize(cfg *config.Config) error {
|
|
repos := a.initRepositories()
|
|
processors := a.initProcessors(cfg, repos)
|
|
|
|
// Initialize omset milestone scheduler
|
|
a.omsetScheduler = service.NewOmsetMilestoneScheduler(
|
|
repos.organizationRepo,
|
|
repos.outletRepo,
|
|
repos.userRepo,
|
|
processors.notificationProcessor,
|
|
)
|
|
|
|
// Checks that wallet balances, ledger and lots agree (docs/prd-point-coin.md §7.5)
|
|
a.walletRecon = service.NewWalletReconciliationJob(
|
|
repository.NewWalletReconciliationRepository(a.db),
|
|
repos.userRepo,
|
|
processors.notificationProcessor,
|
|
)
|
|
|
|
services := a.initServices(processors, repos, cfg)
|
|
validators := a.initValidators()
|
|
middleware := a.initMiddleware(services, cfg)
|
|
healthHandler := handler.NewHealthHandler()
|
|
selfOrderHandler := handler.NewSelfOrderHandler(
|
|
services.orderService,
|
|
services.categoryService,
|
|
services.productService,
|
|
repos.tableRepo,
|
|
repos.outletRepo,
|
|
repos.userRepo,
|
|
repos.sessionRepo,
|
|
repos.orderRepo,
|
|
services.productOutletPriceService,
|
|
)
|
|
|
|
a.router = router.NewRouter(
|
|
cfg,
|
|
healthHandler,
|
|
services.authService,
|
|
middleware.authMiddleware,
|
|
services.userService,
|
|
validators.userValidator,
|
|
services.organizationService,
|
|
validators.organizationValidator,
|
|
services.outletService,
|
|
validators.outletValidator,
|
|
services.outletSettingService,
|
|
services.categoryService,
|
|
validators.categoryValidator,
|
|
services.productService,
|
|
validators.productValidator,
|
|
services.productVariantService,
|
|
validators.productVariantValidator,
|
|
services.inventoryService,
|
|
validators.inventoryValidator,
|
|
services.orderService,
|
|
validators.orderValidator,
|
|
services.fileService,
|
|
validators.fileValidator,
|
|
services.customerService,
|
|
validators.customerValidator,
|
|
services.paymentMethodService,
|
|
validators.paymentMethodValidator,
|
|
services.analyticsService,
|
|
services.reportService,
|
|
services.tableService,
|
|
validators.tableValidator,
|
|
services.unitService,
|
|
services.ingredientService,
|
|
services.productRecipeService,
|
|
services.vendorService,
|
|
validators.vendorValidator,
|
|
services.purchaseOrderService,
|
|
validators.purchaseOrderValidator,
|
|
services.purchaseCategoryService,
|
|
validators.purchaseCategoryValidator,
|
|
services.unitConverterService,
|
|
validators.unitConverterValidator,
|
|
services.chartOfAccountTypeService,
|
|
validators.chartOfAccountTypeValidator,
|
|
services.chartOfAccountService,
|
|
validators.chartOfAccountValidator,
|
|
services.accountService,
|
|
validators.accountValidator,
|
|
*services.orderIngredientTransactionService,
|
|
validators.orderIngredientTransactionValidator,
|
|
services.gamificationService,
|
|
validators.gamificationValidator,
|
|
services.rewardService,
|
|
validators.rewardValidator,
|
|
services.campaignService,
|
|
validators.campaignValidator,
|
|
services.customerAuthService,
|
|
validators.customerAuthValidator,
|
|
services.customerPointsService,
|
|
services.spinGameService,
|
|
middleware.customerAuthMiddleware,
|
|
services.userDeviceService,
|
|
validators.userDeviceValidator,
|
|
services.notificationService,
|
|
validators.notificationValidator,
|
|
services.productOutletPriceService,
|
|
validators.productOutletPriceValidator,
|
|
selfOrderHandler,
|
|
services.expenseService,
|
|
validators.expenseValidator,
|
|
services.cashAdvanceService,
|
|
validators.cashAdvanceValidator,
|
|
services.walletAdminService,
|
|
validators.walletValidator,
|
|
services.loyaltySettingsService,
|
|
a.redisClient,
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *App) Start(port string) error {
|
|
// Start the omset milestone scheduler (checks every 5 minutes for daily omset milestones)
|
|
if a.omsetScheduler != nil {
|
|
a.omsetScheduler.Start(5 * time.Minute)
|
|
}
|
|
if a.walletRecon != nil {
|
|
a.walletRecon.Start(6 * time.Hour)
|
|
}
|
|
|
|
engine := a.router.Init()
|
|
|
|
a.server = &http.Server{
|
|
Addr: ":" + port,
|
|
Handler: engine,
|
|
ReadTimeout: 15 * time.Second,
|
|
WriteTimeout: 15 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
signal.Notify(a.shutdown, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
log.Printf("Server starting on port %s", port)
|
|
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}()
|
|
|
|
<-a.shutdown
|
|
log.Println("Shutting down server...")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if err := a.server.Shutdown(ctx); err != nil {
|
|
log.Printf("Server forced to shutdown: %v", err)
|
|
return err
|
|
}
|
|
|
|
log.Println("Server exited gracefully")
|
|
return nil
|
|
}
|
|
|
|
func (a *App) Shutdown() {
|
|
if a.omsetScheduler != nil {
|
|
a.omsetScheduler.Stop()
|
|
}
|
|
if a.walletRecon != nil {
|
|
a.walletRecon.Stop()
|
|
}
|
|
close(a.shutdown)
|
|
}
|
|
|
|
type repositories struct {
|
|
userRepo *repository.UserRepositoryImpl
|
|
organizationRepo *repository.OrganizationRepositoryImpl
|
|
outletRepo *repository.OutletRepositoryImpl
|
|
outletSettingRepo *repository.OutletSettingRepositoryImpl
|
|
categoryRepo *repository.CategoryRepositoryImpl
|
|
productRepo *repository.ProductRepositoryImpl
|
|
productVariantRepo *repository.ProductVariantRepositoryImpl
|
|
inventoryRepo *repository.InventoryRepositoryImpl
|
|
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
|
|
orderRepo *repository.OrderRepositoryImpl
|
|
orderItemRepo *repository.OrderItemRepositoryImpl
|
|
paymentRepo *repository.PaymentRepositoryImpl
|
|
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
|
|
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
|
|
fileRepo *repository.FileRepositoryImpl
|
|
customerRepo *repository.CustomerRepository
|
|
analyticsRepo *repository.AnalyticsRepositoryImpl
|
|
tableRepo *repository.TableRepository
|
|
unitRepo *repository.UnitRepository
|
|
ingredientRepo *repository.IngredientRepository
|
|
ingredientCompositionRepo *repository.IngredientCompositionRepository
|
|
productRecipeRepo *repository.ProductRecipeRepository
|
|
vendorRepo *repository.VendorRepositoryImpl
|
|
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
|
|
purchaseCategoryRepo *repository.PurchaseCategoryRepositoryImpl
|
|
unitConverterRepo *repository.IngredientUnitConverterRepositoryImpl
|
|
chartOfAccountTypeRepo *repository.ChartOfAccountTypeRepositoryImpl
|
|
chartOfAccountRepo *repository.ChartOfAccountRepositoryImpl
|
|
accountRepo *repository.AccountRepositoryImpl
|
|
orderIngredientTransactionRepo *repository.OrderIngredientTransactionRepositoryImpl
|
|
customerTokensRepo *repository.CustomerTokensRepository
|
|
tierRepo *repository.TierRepository
|
|
gameRepo *repository.GameRepository
|
|
gamePrizeRepo *repository.GamePrizeRepository
|
|
gamePlayRepo repository.GamePlayRepository
|
|
omsetTrackerRepo *repository.OmsetTrackerRepository
|
|
rewardRepo repository.RewardRepository
|
|
campaignRepo repository.CampaignRepository
|
|
campaignRuleRepo repository.CampaignRuleRepository
|
|
customerAuthRepo repository.CustomerAuthRepository
|
|
customerPointsRepo repository.CustomerPointsRepository
|
|
otpRepo repository.OtpRepository
|
|
sessionRepo repository.SessionRepository
|
|
txManager *repository.TxManager
|
|
userDeviceRepo *repository.UserDeviceRepositoryImpl
|
|
notificationRepo *repository.NotificationRepositoryImpl
|
|
notificationReceiverRepo *repository.NotificationReceiverRepositoryImpl
|
|
notificationDeliveryRepo *repository.NotificationDeliveryRepositoryImpl
|
|
productOutletPriceRepo *repository.ProductOutletPriceRepositoryImpl
|
|
expenseRepo *repository.ExpenseRepositoryImpl
|
|
cashAdvanceRepo *repository.CashAdvanceRepositoryImpl
|
|
walletRepo repository.WalletRepository
|
|
walletQueryRepo repository.WalletQueryRepository
|
|
loyaltySettingsRepo repository.LoyaltySettingsRepository
|
|
}
|
|
|
|
func (a *App) initRepositories() *repositories {
|
|
return &repositories{
|
|
userRepo: repository.NewUserRepository(a.db),
|
|
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
|
|
outletRepo: repository.NewOutletRepositoryImpl(a.db),
|
|
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
|
|
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
|
|
productRepo: repository.NewProductRepositoryImpl(a.db),
|
|
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
|
|
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
|
|
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
|
|
orderRepo: repository.NewOrderRepositoryImpl(a.db),
|
|
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
|
|
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
|
|
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
|
|
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
|
|
fileRepo: repository.NewFileRepositoryImpl(a.db),
|
|
customerRepo: repository.NewCustomerRepository(a.db),
|
|
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
|
|
tableRepo: repository.NewTableRepository(a.db),
|
|
unitRepo: repository.NewUnitRepository(a.db),
|
|
ingredientRepo: repository.NewIngredientRepository(a.db),
|
|
ingredientCompositionRepo: repository.NewIngredientCompositionRepository(a.db),
|
|
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
|
|
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
|
|
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db),
|
|
purchaseCategoryRepo: repository.NewPurchaseCategoryRepositoryImpl(a.db),
|
|
unitConverterRepo: repository.NewIngredientUnitConverterRepositoryImpl(a.db).(*repository.IngredientUnitConverterRepositoryImpl),
|
|
chartOfAccountTypeRepo: repository.NewChartOfAccountTypeRepositoryImpl(a.db),
|
|
chartOfAccountRepo: repository.NewChartOfAccountRepositoryImpl(a.db),
|
|
accountRepo: repository.NewAccountRepositoryImpl(a.db),
|
|
orderIngredientTransactionRepo: repository.NewOrderIngredientTransactionRepositoryImpl(a.db).(*repository.OrderIngredientTransactionRepositoryImpl),
|
|
customerTokensRepo: repository.NewCustomerTokensRepository(a.db),
|
|
tierRepo: repository.NewTierRepository(a.db),
|
|
gameRepo: repository.NewGameRepository(a.db),
|
|
gamePrizeRepo: repository.NewGamePrizeRepository(a.db),
|
|
gamePlayRepo: repository.NewGamePlayRepository(a.db),
|
|
omsetTrackerRepo: repository.NewOmsetTrackerRepository(a.db),
|
|
rewardRepo: repository.NewRewardRepository(a.db),
|
|
campaignRepo: repository.NewCampaignRepository(a.db),
|
|
campaignRuleRepo: repository.NewCampaignRuleRepository(a.db),
|
|
customerAuthRepo: repository.NewCustomerAuthRepository(a.db),
|
|
customerPointsRepo: repository.NewCustomerPointsRepository(a.db),
|
|
otpRepo: repository.NewOtpRepository(a.db),
|
|
sessionRepo: repository.NewSessionRepository(a.redisClient),
|
|
txManager: repository.NewTxManager(a.db),
|
|
userDeviceRepo: repository.NewUserDeviceRepositoryImpl(a.db),
|
|
notificationRepo: repository.NewNotificationRepository(a.db),
|
|
notificationReceiverRepo: repository.NewNotificationReceiverRepository(a.db),
|
|
notificationDeliveryRepo: repository.NewNotificationDeliveryRepository(a.db),
|
|
productOutletPriceRepo: repository.NewProductOutletPriceRepositoryImpl(a.db),
|
|
expenseRepo: repository.NewExpenseRepositoryImpl(a.db),
|
|
cashAdvanceRepo: repository.NewCashAdvanceRepositoryImpl(a.db),
|
|
walletRepo: repository.NewWalletRepository(a.db),
|
|
walletQueryRepo: repository.NewWalletQueryRepository(a.db),
|
|
loyaltySettingsRepo: repository.NewLoyaltySettingsRepository(a.db),
|
|
}
|
|
}
|
|
|
|
type processors struct {
|
|
userProcessor *processor.UserProcessorImpl
|
|
organizationProcessor processor.OrganizationProcessor
|
|
outletProcessor processor.OutletProcessor
|
|
outletSettingProcessor *processor.OutletSettingProcessorImpl
|
|
categoryProcessor processor.CategoryProcessor
|
|
productProcessor processor.ProductProcessor
|
|
productVariantProcessor processor.ProductVariantProcessor
|
|
inventoryProcessor processor.InventoryProcessor
|
|
orderProcessor processor.OrderProcessor
|
|
paymentMethodProcessor processor.PaymentMethodProcessor
|
|
fileProcessor processor.FileProcessor
|
|
customerProcessor *processor.CustomerProcessor
|
|
analyticsProcessor *processor.AnalyticsProcessorImpl
|
|
tableProcessor *processor.TableProcessor
|
|
unitProcessor *processor.UnitProcessorImpl
|
|
ingredientProcessor *processor.IngredientProcessorImpl
|
|
productRecipeProcessor *processor.ProductRecipeProcessorImpl
|
|
vendorProcessor *processor.VendorProcessorImpl
|
|
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
|
|
purchaseCategoryProcessor *processor.PurchaseCategoryProcessorImpl
|
|
unitConverterProcessor *processor.IngredientUnitConverterProcessorImpl
|
|
chartOfAccountTypeProcessor *processor.ChartOfAccountTypeProcessorImpl
|
|
chartOfAccountProcessor *processor.ChartOfAccountProcessorImpl
|
|
accountProcessor *processor.AccountProcessorImpl
|
|
orderIngredientTransactionProcessor *processor.OrderIngredientTransactionProcessorImpl
|
|
customerTokensProcessor *processor.CustomerTokensProcessor
|
|
tierProcessor *processor.TierProcessor
|
|
gameProcessor *processor.GameProcessor
|
|
gamePrizeProcessor *processor.GamePrizeProcessor
|
|
gamePlayProcessor *processor.GamePlayProcessor
|
|
omsetTrackerProcessor *processor.OmsetTrackerProcessor
|
|
rewardProcessor processor.RewardProcessor
|
|
campaignProcessor processor.CampaignProcessor
|
|
campaignRuleProcessor processor.CampaignRuleProcessor
|
|
customerAuthProcessor processor.CustomerAuthProcessor
|
|
customerPointsProcessor *processor.CustomerPointsProcessor
|
|
otpProcessor processor.OtpProcessor
|
|
fileClient processor.FileClient
|
|
inventoryMovementService service.InventoryMovementService
|
|
userDeviceProcessor *processor.UserDeviceProcessorImpl
|
|
notificationProcessor *processor.NotificationProcessorImpl
|
|
productOutletPriceProcessor processor.ProductOutletPriceProcessor
|
|
expenseProcessor *processor.ExpenseProcessorImpl
|
|
cashAdvanceProcessor *processor.CashAdvanceProcessorImpl
|
|
walletProcessor *processor.WalletProcessor
|
|
walletAdminProcessor *processor.WalletAdminProcessor
|
|
loyaltySettingsProcessor *processor.LoyaltySettingsProcessor
|
|
}
|
|
|
|
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
|
fileClient := client.NewFileClient(cfg.S3Config)
|
|
fonnteClient := client.NewFonnteClient(cfg.GetFonnte())
|
|
otpProcessor := processor.NewOtpProcessor(fonnteClient, repos.otpRepo)
|
|
inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo)
|
|
|
|
return &processors{
|
|
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
|
|
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
|
|
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
|
|
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
|
|
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
|
|
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo, repos.productOutletPriceRepo),
|
|
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
|
|
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
|
|
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService, repos.productOutletPriceRepo),
|
|
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
|
|
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
|
|
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
|
|
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo, repos.expenseRepo),
|
|
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
|
|
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
|
|
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
|
|
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
|
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
|
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.purchaseCategoryRepo, repos.categoryRepo, repos.cashAdvanceRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
|
|
purchaseCategoryProcessor: processor.NewPurchaseCategoryProcessorImpl(repos.purchaseCategoryRepo),
|
|
unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo),
|
|
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
|
chartOfAccountProcessor: processor.NewChartOfAccountProcessorImpl(repos.chartOfAccountRepo, repos.chartOfAccountTypeRepo),
|
|
accountProcessor: processor.NewAccountProcessorImpl(repos.accountRepo, repos.chartOfAccountRepo),
|
|
orderIngredientTransactionProcessor: processor.NewOrderIngredientTransactionProcessorImpl(repos.orderIngredientTransactionRepo, repos.productRecipeRepo, repos.ingredientRepo, repos.unitRepo).(*processor.OrderIngredientTransactionProcessorImpl),
|
|
customerTokensProcessor: processor.NewCustomerTokensProcessor(repos.customerTokensRepo),
|
|
tierProcessor: processor.NewTierProcessor(repos.tierRepo),
|
|
gameProcessor: processor.NewGameProcessor(repos.gameRepo),
|
|
gamePrizeProcessor: processor.NewGamePrizeProcessor(repos.gamePrizeRepo),
|
|
gamePlayProcessor: processor.NewGamePlayProcessor(repos.gamePlayRepo, repos.gameRepo, repos.gamePrizeRepo, repos.customerTokensRepo, repos.customerPointsRepo),
|
|
omsetTrackerProcessor: processor.NewOmsetTrackerProcessor(repos.omsetTrackerRepo),
|
|
rewardProcessor: processor.NewRewardProcessor(repos.rewardRepo),
|
|
campaignProcessor: processor.NewCampaignProcessor(repos.campaignRepo),
|
|
campaignRuleProcessor: processor.NewCampaignRuleProcessor(repos.campaignRuleRepo),
|
|
customerAuthProcessor: processor.NewCustomerAuthProcessor(repos.customerAuthRepo, otpProcessor, repos.otpRepo, cfg.GetCustomerJWTSecret(), cfg.GetCustomerJWTExpiresTTL()),
|
|
customerPointsProcessor: processor.NewCustomerPointsProcessor(processor.NewWalletQueryProcessor(repos.walletQueryRepo, processor.NewLoyaltySettingsProcessor(repos.loyaltySettingsRepo, repos.txManager)), repos.gameRepo),
|
|
otpProcessor: otpProcessor,
|
|
fileClient: fileClient,
|
|
inventoryMovementService: inventoryMovementService,
|
|
userDeviceProcessor: processor.NewUserDeviceProcessorImpl(repos.userDeviceRepo),
|
|
notificationProcessor: buildNotificationProcessor(cfg, repos),
|
|
productOutletPriceProcessor: processor.NewProductOutletPriceProcessorImpl(repos.productOutletPriceRepo, repos.productRepo, repos.outletRepo),
|
|
expenseProcessor: processor.NewExpenseProcessorImpl(repos.expenseRepo, repos.purchaseCategoryRepo, repos.cashAdvanceRepo),
|
|
cashAdvanceProcessor: processor.NewCashAdvanceProcessorImpl(repos.cashAdvanceRepo, repos.categoryRepo),
|
|
walletProcessor: processor.NewWalletProcessor(repos.walletRepo),
|
|
loyaltySettingsProcessor: processor.NewLoyaltySettingsProcessor(repos.loyaltySettingsRepo, repos.txManager),
|
|
walletAdminProcessor: processor.NewWalletAdminProcessor(repository.NewWalletAdminRepository(a.db), repos.walletQueryRepo, processor.NewWalletProcessor(repos.walletRepo), repos.txManager),
|
|
}
|
|
}
|
|
|
|
type services struct {
|
|
userService *service.UserServiceImpl
|
|
authService service.AuthService
|
|
organizationService service.OrganizationService
|
|
outletService service.OutletService
|
|
outletSettingService service.OutletSettingService
|
|
categoryService service.CategoryService
|
|
productService service.ProductService
|
|
productVariantService service.ProductVariantService
|
|
inventoryService service.InventoryService
|
|
orderService service.OrderService
|
|
paymentMethodService service.PaymentMethodService
|
|
fileService service.FileService
|
|
customerService service.CustomerService
|
|
analyticsService *service.AnalyticsServiceImpl
|
|
reportService service.ReportService
|
|
tableService *service.TableServiceImpl
|
|
unitService *service.UnitServiceImpl
|
|
ingredientService *service.IngredientServiceImpl
|
|
productRecipeService *service.ProductRecipeServiceImpl
|
|
vendorService *service.VendorServiceImpl
|
|
purchaseOrderService *service.PurchaseOrderServiceImpl
|
|
purchaseCategoryService service.PurchaseCategoryService
|
|
unitConverterService *service.IngredientUnitConverterServiceImpl
|
|
chartOfAccountTypeService service.ChartOfAccountTypeService
|
|
chartOfAccountService service.ChartOfAccountService
|
|
accountService service.AccountService
|
|
orderIngredientTransactionService *service.OrderIngredientTransactionService
|
|
gamificationService service.GamificationService
|
|
rewardService service.RewardService
|
|
campaignService service.CampaignService
|
|
customerAuthService service.CustomerAuthService
|
|
customerPointsService service.CustomerPointsService
|
|
spinGameService service.SpinGameService
|
|
userDeviceService service.UserDeviceService
|
|
notificationService service.NotificationService
|
|
productOutletPriceService service.ProductOutletPriceService
|
|
expenseService *service.ExpenseServiceImpl
|
|
cashAdvanceService *service.CashAdvanceServiceImpl
|
|
walletAdminService *service.WalletAdminServiceImpl
|
|
loyaltySettingsService *service.LoyaltySettingsServiceImpl
|
|
}
|
|
|
|
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
|
authConfig := cfg.Auth()
|
|
authService := service.NewAuthService(processors.userProcessor, processors.userDeviceProcessor, authConfig)
|
|
organizationService := service.NewOrganizationService(processors.organizationProcessor)
|
|
outletService := service.NewOutletService(processors.outletProcessor)
|
|
outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor)
|
|
categoryService := service.NewCategoryService(processors.categoryProcessor)
|
|
productService := service.NewProductService(processors.productProcessor)
|
|
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
|
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
|
|
orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo) // Will be updated after orderIngredientTransactionService is created
|
|
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
|
|
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
|
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
|
|
analyticsService := service.NewAnalyticsServiceImpl(processors.analyticsProcessor)
|
|
reportService := service.NewReportService(analyticsService, repos.organizationRepo, repos.outletRepo, processors.fileClient)
|
|
tableService := service.NewTableService(processors.tableProcessor, transformer.NewTableTransformer())
|
|
unitService := service.NewUnitService(processors.unitProcessor)
|
|
ingredientService := service.NewIngredientService(processors.ingredientProcessor)
|
|
productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor)
|
|
vendorService := service.NewVendorService(processors.vendorProcessor)
|
|
purchaseOrderService := service.NewPurchaseOrderService(processors.purchaseOrderProcessor)
|
|
purchaseCategoryService := service.NewPurchaseCategoryService(processors.purchaseCategoryProcessor)
|
|
unitConverterService := service.NewIngredientUnitConverterService(processors.unitConverterProcessor)
|
|
chartOfAccountTypeService := service.NewChartOfAccountTypeService(processors.chartOfAccountTypeProcessor)
|
|
chartOfAccountService := service.NewChartOfAccountService(processors.chartOfAccountProcessor)
|
|
accountService := service.NewAccountService(processors.accountProcessor)
|
|
orderIngredientTransactionService := service.NewOrderIngredientTransactionService(processors.orderIngredientTransactionProcessor, repos.txManager)
|
|
gamificationService := service.NewGamificationService(processors.customerPointsProcessor, processors.customerTokensProcessor, processors.tierProcessor, processors.gameProcessor, processors.gamePrizeProcessor, processors.gamePlayProcessor, processors.omsetTrackerProcessor)
|
|
rewardService := service.NewRewardService(processors.rewardProcessor)
|
|
campaignService := service.NewCampaignService(processors.campaignProcessor, processors.campaignRuleProcessor)
|
|
customerAuthService := service.NewCustomerAuthService(processors.customerAuthProcessor)
|
|
customerPointsService := service.NewCustomerPointsService(processors.customerPointsProcessor)
|
|
spinGameService := service.NewSpinGameService(processors.gamePlayProcessor, repos.txManager)
|
|
userDeviceService := service.NewUserDeviceService(processors.userDeviceProcessor)
|
|
notificationService := service.NewNotificationService(processors.notificationProcessor)
|
|
|
|
// Update order service with order ingredient transaction service
|
|
orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo)
|
|
|
|
return &services{
|
|
userService: service.NewUserService(processors.userProcessor),
|
|
authService: authService,
|
|
organizationService: organizationService,
|
|
outletService: outletService,
|
|
outletSettingService: outletSettingService,
|
|
categoryService: categoryService,
|
|
productService: productService,
|
|
productVariantService: productVariantService,
|
|
inventoryService: inventoryService,
|
|
orderService: orderService,
|
|
paymentMethodService: paymentMethodService,
|
|
fileService: fileService,
|
|
customerService: customerService,
|
|
analyticsService: analyticsService,
|
|
reportService: reportService,
|
|
tableService: tableService,
|
|
unitService: unitService,
|
|
ingredientService: ingredientService,
|
|
productRecipeService: productRecipeService,
|
|
vendorService: vendorService,
|
|
purchaseOrderService: purchaseOrderService,
|
|
purchaseCategoryService: purchaseCategoryService,
|
|
unitConverterService: unitConverterService,
|
|
chartOfAccountTypeService: chartOfAccountTypeService,
|
|
chartOfAccountService: chartOfAccountService,
|
|
accountService: accountService,
|
|
orderIngredientTransactionService: orderIngredientTransactionService,
|
|
gamificationService: gamificationService,
|
|
rewardService: rewardService,
|
|
campaignService: campaignService,
|
|
customerAuthService: customerAuthService,
|
|
customerPointsService: customerPointsService,
|
|
spinGameService: spinGameService,
|
|
userDeviceService: userDeviceService,
|
|
notificationService: notificationService,
|
|
productOutletPriceService: service.NewProductOutletPriceService(processors.productOutletPriceProcessor),
|
|
expenseService: service.NewExpenseService(processors.expenseProcessor),
|
|
cashAdvanceService: service.NewCashAdvanceService(processors.cashAdvanceProcessor),
|
|
walletAdminService: service.NewWalletAdminService(processors.walletAdminProcessor),
|
|
loyaltySettingsService: service.NewLoyaltySettingsService(processors.loyaltySettingsProcessor),
|
|
}
|
|
}
|
|
|
|
type middlewares struct {
|
|
authMiddleware *middleware.AuthMiddleware
|
|
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
|
}
|
|
|
|
func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
|
|
return &middlewares{
|
|
authMiddleware: middleware.NewAuthMiddleware(services.authService),
|
|
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
|
|
}
|
|
}
|
|
|
|
type validators struct {
|
|
userValidator *validator.UserValidatorImpl
|
|
organizationValidator validator.OrganizationValidator
|
|
outletValidator validator.OutletValidator
|
|
categoryValidator validator.CategoryValidator
|
|
productValidator validator.ProductValidator
|
|
productVariantValidator validator.ProductVariantValidator
|
|
inventoryValidator validator.InventoryValidator
|
|
orderValidator validator.OrderValidator
|
|
paymentMethodValidator validator.PaymentMethodValidator
|
|
fileValidator validator.FileValidator
|
|
customerValidator validator.CustomerValidator
|
|
tableValidator *validator.TableValidator
|
|
vendorValidator *validator.VendorValidatorImpl
|
|
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
|
|
purchaseCategoryValidator *validator.PurchaseCategoryValidatorImpl
|
|
unitConverterValidator *validator.IngredientUnitConverterValidatorImpl
|
|
chartOfAccountTypeValidator *validator.ChartOfAccountTypeValidatorImpl
|
|
chartOfAccountValidator *validator.ChartOfAccountValidatorImpl
|
|
accountValidator *validator.AccountValidatorImpl
|
|
orderIngredientTransactionValidator *validator.OrderIngredientTransactionValidatorImpl
|
|
gamificationValidator *validator.GamificationValidatorImpl
|
|
rewardValidator validator.RewardValidator
|
|
campaignValidator validator.CampaignValidator
|
|
customerAuthValidator validator.CustomerAuthValidator
|
|
userDeviceValidator *validator.UserDeviceValidatorImpl
|
|
notificationValidator *validator.NotificationValidatorImpl
|
|
productOutletPriceValidator *validator.ProductOutletPriceValidatorImpl
|
|
expenseValidator *validator.ExpenseValidatorImpl
|
|
cashAdvanceValidator *validator.CashAdvanceValidatorImpl
|
|
walletValidator *validator.WalletValidatorImpl
|
|
}
|
|
|
|
func (a *App) initValidators() *validators {
|
|
return &validators{
|
|
userValidator: validator.NewUserValidator(),
|
|
organizationValidator: validator.NewOrganizationValidator(),
|
|
outletValidator: validator.NewOutletValidator(),
|
|
categoryValidator: validator.NewCategoryValidator(),
|
|
productValidator: validator.NewProductValidator(),
|
|
productVariantValidator: validator.NewProductVariantValidator(),
|
|
inventoryValidator: validator.NewInventoryValidator(),
|
|
orderValidator: validator.NewOrderValidator(),
|
|
paymentMethodValidator: validator.NewPaymentMethodValidator(),
|
|
fileValidator: validator.NewFileValidatorImpl(),
|
|
customerValidator: validator.NewCustomerValidator(),
|
|
tableValidator: validator.NewTableValidator(),
|
|
vendorValidator: validator.NewVendorValidator(),
|
|
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
|
|
purchaseCategoryValidator: validator.NewPurchaseCategoryValidator(),
|
|
unitConverterValidator: validator.NewIngredientUnitConverterValidator().(*validator.IngredientUnitConverterValidatorImpl),
|
|
chartOfAccountTypeValidator: validator.NewChartOfAccountTypeValidator().(*validator.ChartOfAccountTypeValidatorImpl),
|
|
chartOfAccountValidator: validator.NewChartOfAccountValidator().(*validator.ChartOfAccountValidatorImpl),
|
|
accountValidator: validator.NewAccountValidator().(*validator.AccountValidatorImpl),
|
|
orderIngredientTransactionValidator: validator.NewOrderIngredientTransactionValidator().(*validator.OrderIngredientTransactionValidatorImpl),
|
|
gamificationValidator: validator.NewGamificationValidator(),
|
|
rewardValidator: validator.NewRewardValidator(),
|
|
campaignValidator: validator.NewCampaignValidator(),
|
|
customerAuthValidator: validator.NewCustomerAuthValidator(),
|
|
userDeviceValidator: validator.NewUserDeviceValidator(),
|
|
notificationValidator: validator.NewNotificationValidator(),
|
|
productOutletPriceValidator: validator.NewProductOutletPriceValidator(),
|
|
expenseValidator: validator.NewExpenseValidator(),
|
|
cashAdvanceValidator: validator.NewCashAdvanceValidator(),
|
|
walletValidator: validator.NewWalletValidator(),
|
|
}
|
|
}
|
|
|
|
// buildNotificationProcessor creates the notification processor with FCM integration.
|
|
// If FCM is not configured, it returns a processor with a nil FCM client (FCM dispatch will be skipped).
|
|
func buildNotificationProcessor(cfg *config.Config, repos *repositories) *processor.NotificationProcessorImpl {
|
|
var fcmClient client.FCMClient
|
|
if cfg.FCM.CredentialsFile != "" {
|
|
var err error
|
|
fcmClient, err = client.NewFCMClient(&cfg.FCM)
|
|
if err != nil {
|
|
// FCM init failure is non-fatal; notifications will still be persisted.
|
|
fcmClient = nil
|
|
}
|
|
}
|
|
|
|
return processor.NewNotificationProcessor(
|
|
repos.notificationRepo,
|
|
repos.notificationReceiverRepo,
|
|
repos.notificationDeliveryRepo,
|
|
repos.userDeviceRepo,
|
|
repos.userRepo,
|
|
fcmClient,
|
|
)
|
|
}
|