Compare commits

...
Author SHA1 Message Date
Efril a7022dd4c1 ingredient composition 2026-04-27 21:17:12 +07:00
Efril 3542104050 fix sales 2026-04-27 13:24:16 +07:00
Efril eb95459578 fix if order amount 0 2026-04-26 23:40:01 +07:00
Efril 1a5ddd2b34 update 2026-04-26 23:25:54 +07:00
Efril 0aa280462c fix cors 2026-04-26 23:14:11 +07:00
aefril 421475006b Merge pull request 'hpp' (#1) from hpp into main
Reviewed-on: #1
2026-04-26 16:08:02 +00:00
Efril 75fdb8e847 Merge branch 'hpp' of https://gits.altru.id/apksel-dev/apskel-pos-backend into hpp 2026-04-26 23:04:47 +07:00
Efril 8efa644680 payment amount fix 0 2026-04-26 23:04:31 +07:00
ryan ce99aef289 delete leftover 2026-04-24 18:48:40 +07:00
ryan ba970229a9 Fix HPP 2026-04-24 17:56:38 +07:00
ryan 9b606b4c8b Implementasi HPP std dan real 2026-04-23 11:28:51 +07:00
Efril d3dddea1c7 add category name at product response 2026-04-20 14:21:27 +07:00
Efril 80a78137a0 fix create outlet 2026-04-20 12:31:33 +07:00
efrilm 3826a6b7a9 create outlet route 2026-04-16 19:23:36 +07:00
efrilm d695bedc97 update product response 2026-04-16 17:14:18 +07:00
efrilm 3db4afbce6 fix update ingredients 2026-04-16 15:44:13 +07:00
efrilm 535e4c84f6 product variant route 2026-04-16 14:30:48 +07:00
Efril f25ec1c06f create outlet 2026-04-14 19:09:09 +07:00
efrilm b3359fa6ff update product analytic 2025-11-25 21:44:20 +07:00
efrilm 0c331dce6a update product analytic if exiest order 2025-11-25 13:23:59 +07:00
efrilm cd784624c9 update total sales in dashboard 2025-10-15 18:55:35 +07:00
efrilm 3e0d75a4d0 update products 2025-10-09 20:03:20 +07:00
efrilm 54dc8662d6 merge 2025-10-07 00:07:38 +07:00
efrilm 27a2535dde update product analytic 2025-10-07 00:02:15 +07:00
efrilm f55ea1ceb0 add order at categories 2025-10-06 22:46:01 +07:00
Aditya Siregar 670a283c7b update backend 2025-09-25 20:31:57 +07:00
Aditya Siregar b29677a192 Fix Product Analysitcs 2025-09-22 22:12:54 +07:00
Aditya Siregar 26ac7a2752 update refresh token 2025-09-20 17:24:39 +07:00
Aditya Siregar f85929c575 Add Refresh token 2025-09-20 17:17:00 +07:00
Aditya Siregar 9db3dcb472 Fix Commit 2025-09-18 17:21:58 +07:00
Aditya Siregar a520d0ed11 fix campaign rules 2025-09-18 13:39:37 +07:00
Aditya Siregar c7828a5cad push points 2025-09-18 12:42:07 +07:00
Aditya Siregar d3db08fd15 wheel 2025-09-18 12:29:27 +07:00
Aditya Siregar 67812a1d75 fix login issue 2025-09-18 12:27:03 +07:00
Aditya Siregar be92ec8b23 test wheels 2025-09-18 12:01:20 +07:00
Aditya Siregar f64fec1fe2 campaign 2025-09-18 02:01:50 +07:00
Aditya Siregar 259b8a11b5 Push 2025-09-18 01:38:03 +07:00
Aditya Siregar 65f61b65cf user auth register 2025-09-18 01:32:01 +07:00
Aditya Siregar c68b536480 Fixed tabled 2025-09-18 00:37:46 +07:00
Aditya Siregar 155016dec8 Add Campaign 2025-09-17 23:55:11 +07:00
Aditya Siregar 201e24041b Add Tiers and Game Prize 2025-09-17 19:30:17 +07:00
Aditya Siregar 12ee54390f Revert "voucher"
This reverts commit cfe690a40f.
2025-09-16 19:31:39 +07:00
Aditya Siregar 36c2352cb2 Revert "fix"
This reverts commit 691456af87.
2025-09-16 19:31:28 +07:00
Aditya Siregar b37d21d366 Revert "fix backend"
This reverts commit afa8782eb2.
2025-09-16 19:31:14 +07:00
Aditya Siregar afa8782eb2 fix backend 2025-09-13 17:34:34 +07:00
Aditya Siregar 691456af87 fix 2025-09-13 16:53:12 +07:00
Aditya Siregar cfe690a40f voucher 2025-09-13 15:37:26 +07:00
Aditya Siregar 4f6208e479 fix 2025-09-13 02:17:51 +07:00
Aditya Siregar 75ec5274d2 coa fix 2025-09-12 16:37:16 +07:00
Aditya Siregar 91f51d129e Fix 2025-09-12 15:41:08 +07:00
Aditya Siregar 3a04990ec8 add product ingredients 2025-09-12 15:37:19 +07:00
Aditya Siregar efe09c21e4 Add coa purchase and vendors 2025-09-12 01:12:11 +07:00
Aditya Siregar c107733add Update 2025-09-01 16:20:27 +07:00
Aditya Siregar 4c6dc5c8b4 deployment 2025-09-01 16:18:34 +07:00
Aditya Siregar f91f85202e add cors check and update docker file 2025-09-01 16:14:15 +07:00
Aditya Siregar 13d8c75be7 add total out 2025-08-14 01:35:19 +07:00
Aditya Siregar 07b3eda263 addd inventory movement 2025-08-14 01:00:36 +07:00
Aditya Siregar 3a0c262c77 Update 2025-08-14 00:45:14 +07:00
Aditya Siregar 7adba2c8f5 Add Inventory Report 2025-08-14 00:38:26 +07:00
Aditya Siregar bb9a81c7c1 Updarte 2025-08-13 23:51:58 +07:00
Aditya Siregar 4a720f439b Update 2025-08-13 23:49:35 +07:00
Aditya Siregar ee7d0e529b update order status 2025-08-13 23:36:31 +07:00
Aditya Siregar ccb0458189 Update payment 2025-08-13 23:14:26 +07:00
Aditya Siregar 451697b783 Add organizatin ID 2025-08-13 21:38:09 +07:00
Aditya Siregar 1c7f7feb2e Add list Paymenty method 2025-08-13 21:21:32 +07:00
Aditya Siregar 9e74d415b3 add product inventory 2025-08-13 21:02:26 +07:00
Aditya Siregar 58dc92c722 Add Outlet ID 2025-08-13 20:57:57 +07:00
306 changed files with 28200 additions and 1861 deletions
+7 -93
View File
@@ -1,106 +1,20 @@
# Build Stage # 1) Build stage
FROM golang:1.21-alpine AS build FROM golang:1.21-alpine AS build
# Install necessary packages including CA certificates
RUN apk --no-cache add ca-certificates tzdata git curl RUN apk --no-cache add ca-certificates tzdata git curl
WORKDIR /src WORKDIR /src
# Copy go mod files first for better caching
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy source code
COPY . .
# Build the application
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /app cmd/server/main.go
# Development Stage
FROM golang:1.21-alpine AS development
# Install air for live reload and other dev tools
RUN go install github.com/cosmtrek/air@latest
# Install necessary packages
RUN apk --no-cache add ca-certificates tzdata git curl
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
# Copy source code
COPY . . COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /out/app ./cmd/server
# Set timezone # 2) Production stage
ENV TZ=Asia/Jakarta
# Expose port
EXPOSE 3300
# Use air for live reload in development
CMD ["air", "-c", ".air.toml"]
# Migration Stage
FROM build AS migration
# Install migration tool
RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
WORKDIR /app
# Copy migration files
COPY migrations ./migrations
COPY infra ./infra
# Set the entrypoint for migrations
ENTRYPOINT ["migrate"]
# Production Stage
FROM debian:bullseye-slim AS production FROM debian:bullseye-slim AS production
RUN apt-get update && apt-get install -y ca-certificates tzdata curl && rm -rf /var/lib/apt/lists/*
# Install minimal runtime dependencies + Chrome, Chromium, and wkhtmltopdf for PDF generation
RUN apt-get update && apt-get install -y \
ca-certificates \
tzdata \
curl \
fontconfig \
wget \
gnupg \
&& wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \
&& apt-get install -y google-chrome-stable chromium wkhtmltopdf \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user for security
RUN groupadd -r appuser && useradd -r -g appuser appuser RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY --from=build /out/app /app
# Copy the binary
COPY --from=build /app /app
# Copy configuration files
COPY --from=build /src/infra /infra
# Change ownership to non-root user
RUN chown -R appuser:appuser /app /infra
# Set timezone
ENV TZ=Asia/Jakarta ENV TZ=Asia/Jakarta
EXPOSE 4000
# Expose port
EXPOSE 3300
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:3300/health || exit 1 CMD curl -fsS http://localhost:3300/health || exit 1
# Switch to non-root user
USER appuser USER appuser
# Set the entrypoint
ENTRYPOINT ["/app"] ENTRYPOINT ["/app"]
+17 -2
View File
@@ -29,6 +29,7 @@ type Config struct {
Jwt Jwt `mapstructure:"jwt"` Jwt Jwt `mapstructure:"jwt"`
Log Log `mapstructure:"log"` Log Log `mapstructure:"log"`
S3Config S3Config `mapstructure:"s3"` S3Config S3Config `mapstructure:"s3"`
Fonnte Fonnte `mapstructure:"fonnte"`
} }
var ( var (
@@ -63,11 +64,21 @@ func LoadConfig() *Config {
func (c *Config) Auth() *AuthConfig { func (c *Config) Auth() *AuthConfig {
return &AuthConfig{ return &AuthConfig{
jwtTokenSecret: c.Jwt.Token.Secret, jwtTokenSecret: c.Jwt.Token.Secret,
jwtTokenExpiresTTL: c.Jwt.Token.ExpiresTTL, jwtTokenExpiresTTL: c.Jwt.Token.ExpiresTTL,
refreshTokenSecret: c.Jwt.RefreshToken.Secret,
refreshTokenExpiresTTL: c.Jwt.RefreshToken.ExpiresTTL,
} }
} }
func (c *Config) GetCustomerJWTSecret() string {
return c.Jwt.Customer.Secret
}
func (c *Config) GetCustomerJWTExpiresTTL() int {
return c.Jwt.Customer.ExpiresTTL
}
func (c *Config) LogLevel() string { func (c *Config) LogLevel() string {
return c.Log.LogLevel return c.Log.LogLevel
} }
@@ -79,3 +90,7 @@ func (c *Config) Port() string {
func (c *Config) LogFormat() string { func (c *Config) LogFormat() string {
return c.Log.LogFormat return c.Log.LogFormat
} }
func (c *Config) GetFonnte() *Fonnte {
return &c.Fonnte
}
+21 -2
View File
@@ -3,8 +3,10 @@ package config
import "time" import "time"
type AuthConfig struct { type AuthConfig struct {
jwtTokenExpiresTTL int jwtTokenExpiresTTL int
jwtTokenSecret string jwtTokenSecret string
refreshTokenExpiresTTL int
refreshTokenSecret string
} }
type JWT struct { type JWT struct {
@@ -20,3 +22,20 @@ func (c *AuthConfig) AccessTokenExpiresDate() time.Time {
duration := time.Duration(c.jwtTokenExpiresTTL) duration := time.Duration(c.jwtTokenExpiresTTL)
return time.Now().UTC().Add(time.Minute * duration) return time.Now().UTC().Add(time.Minute * duration)
} }
func (c *AuthConfig) RefreshTokenSecret() string {
return c.refreshTokenSecret
}
func (c *AuthConfig) RefreshTokenExpiresDate() time.Time {
duration := time.Duration(c.refreshTokenExpiresTTL)
return time.Now().UTC().Add(time.Minute * duration)
}
func (c *AuthConfig) AccessTokenTTL() time.Duration {
return time.Duration(c.jwtTokenExpiresTTL) * time.Minute
}
func (c *AuthConfig) RefreshTokenTTL() time.Duration {
return time.Duration(c.refreshTokenExpiresTTL) * time.Minute
}
+19
View File
@@ -0,0 +1,19 @@
package config
type Fonnte struct {
ApiUrl string `mapstructure:"api_url"`
Token string `mapstructure:"token"`
Timeout int `mapstructure:"timeout"`
}
func (f *Fonnte) GetApiUrl() string {
return f.ApiUrl
}
func (f *Fonnte) GetToken() string {
return f.Token
}
func (f *Fonnte) GetTimeout() int {
return f.Timeout
}
+13 -1
View File
@@ -1,10 +1,22 @@
package config package config
type Jwt struct { type Jwt struct {
Token Token `mapstructure:"token"` Token Token `mapstructure:"token"`
RefreshToken RefreshToken `mapstructure:"refresh_token"`
Customer Customer `mapstructure:"customer"`
} }
type Token struct { type Token struct {
ExpiresTTL int `mapstructure:"expires-ttl"` ExpiresTTL int `mapstructure:"expires-ttl"`
Secret string `mapstructure:"secret"` Secret string `mapstructure:"secret"`
} }
type RefreshToken struct {
ExpiresTTL int `mapstructure:"expires-ttl"`
Secret string `mapstructure:"secret"`
}
type Customer struct {
ExpiresTTL int `mapstructure:"expires-ttl"`
Secret string `mapstructure:"secret"`
}
+5 -4
View File
@@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
set -euo pipefail
APP_NAME="apskel-pos" APP_NAME="apskel-pos"
PORT="4000" PORT="4000"
@@ -6,16 +7,16 @@ PORT="4000"
echo "🔄 Pulling latest code..." echo "🔄 Pulling latest code..."
git pull git pull
echo "🐳 Building Docker image..." echo "🐳 Building Docker image (production target)..."
docker build -t $APP_NAME . docker build --target production -t $APP_NAME:latest .
echo "🛑 Stopping and removing old container..." echo "🛑 Stopping and removing old container..."
docker stop $APP_NAME 2>/dev/null docker rm -f $APP_NAME 2>/dev/null || true
docker rm $APP_NAME 2>/dev/null
echo "🚀 Running new container..." echo "🚀 Running new container..."
docker run -d --name $APP_NAME \ docker run -d --name $APP_NAME \
-p $PORT:$PORT \ -p $PORT:$PORT \
-e TZ=Asia/Jakarta \
-v "$(pwd)/infra":/infra:ro \ -v "$(pwd)/infra":/infra:ro \
-v "$(pwd)/templates":/templates:ro \ -v "$(pwd)/templates":/templates:ro \
$APP_NAME:latest $APP_NAME:latest
+13 -2
View File
@@ -5,8 +5,14 @@ server:
jwt: jwt:
token: token:
expires-ttl: 1440 expires-ttl: 144000
secret: "5Lm25V3Qd7aut8dr4QUxm5PZUrSFs" secret: "5Lm25V3Qd7aut8dr4QUxm5PZUrSFs"
refresh_token:
expires-ttl: 7776000 # 3 months in minutes (90 days * 24 hours * 60 minutes)
secret: "R3fr3sh_T0k3n_S3cr3t_K3y_2024_P0S"
customer:
expires-ttl: 7776000
secret: "z8d5TlFCT58Q$i0%S^2M&3WtE$PMgd"
postgresql: postgresql:
host: 62.72.45.250 host: 62.72.45.250
@@ -31,4 +37,9 @@ s3:
log: log:
log_format: 'json' log_format: 'json'
log_level: 'debug' log_level: 'debug'
fonnte:
api_url: "https://api.fonnte.com/send"
token: "bADQrf9NTXfLZQCK2wGg"
timeout: 30
+322 -147
View File
@@ -42,7 +42,7 @@ func (a *App) Initialize(cfg *config.Config) error {
processors := a.initProcessors(cfg, repos) processors := a.initProcessors(cfg, repos)
services := a.initServices(processors, repos, cfg) services := a.initServices(processors, repos, cfg)
validators := a.initValidators() validators := a.initValidators()
middleware := a.initMiddleware(services) middleware := a.initMiddleware(services, cfg)
healthHandler := handler.NewHealthHandler() healthHandler := handler.NewHealthHandler()
a.router = router.NewRouter( a.router = router.NewRouter(
@@ -80,6 +80,31 @@ func (a *App) Initialize(cfg *config.Config) error {
services.unitService, services.unitService,
services.ingredientService, services.ingredientService,
services.productRecipeService, services.productRecipeService,
services.vendorService,
validators.vendorValidator,
services.purchaseOrderService,
validators.purchaseOrderValidator,
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,
) )
return nil return nil
@@ -125,127 +150,224 @@ func (a *App) Shutdown() {
} }
type repositories struct { type repositories struct {
userRepo *repository.UserRepositoryImpl userRepo *repository.UserRepositoryImpl
organizationRepo *repository.OrganizationRepositoryImpl organizationRepo *repository.OrganizationRepositoryImpl
outletRepo *repository.OutletRepositoryImpl outletRepo *repository.OutletRepositoryImpl
outletSettingRepo *repository.OutletSettingRepositoryImpl outletSettingRepo *repository.OutletSettingRepositoryImpl
categoryRepo *repository.CategoryRepositoryImpl categoryRepo *repository.CategoryRepositoryImpl
productRepo *repository.ProductRepositoryImpl productRepo *repository.ProductRepositoryImpl
productVariantRepo *repository.ProductVariantRepositoryImpl productVariantRepo *repository.ProductVariantRepositoryImpl
inventoryRepo *repository.InventoryRepositoryImpl inventoryRepo *repository.InventoryRepositoryImpl
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
orderRepo *repository.OrderRepositoryImpl orderRepo *repository.OrderRepositoryImpl
orderItemRepo *repository.OrderItemRepositoryImpl orderItemRepo *repository.OrderItemRepositoryImpl
paymentRepo *repository.PaymentRepositoryImpl paymentRepo *repository.PaymentRepositoryImpl
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
paymentMethodRepo *repository.PaymentMethodRepositoryImpl paymentMethodRepo *repository.PaymentMethodRepositoryImpl
fileRepo *repository.FileRepositoryImpl fileRepo *repository.FileRepositoryImpl
customerRepo *repository.CustomerRepository customerRepo *repository.CustomerRepository
analyticsRepo *repository.AnalyticsRepositoryImpl analyticsRepo *repository.AnalyticsRepositoryImpl
tableRepo repository.TableRepositoryInterface tableRepo *repository.TableRepository
unitRepo *repository.UnitRepository unitRepo *repository.UnitRepository
ingredientRepo *repository.IngredientRepository ingredientRepo *repository.IngredientRepository
productRecipeRepo *repository.ProductRecipeRepository ingredientCompositionRepo *repository.IngredientCompositionRepository
productRecipeRepo *repository.ProductRecipeRepository
vendorRepo *repository.VendorRepositoryImpl
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
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
txManager *repository.TxManager
} }
func (a *App) initRepositories() *repositories { func (a *App) initRepositories() *repositories {
return &repositories{ return &repositories{
userRepo: repository.NewUserRepository(a.db), userRepo: repository.NewUserRepository(a.db),
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db), organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
outletRepo: repository.NewOutletRepositoryImpl(a.db), outletRepo: repository.NewOutletRepositoryImpl(a.db),
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db), outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
categoryRepo: repository.NewCategoryRepositoryImpl(a.db), categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
productRepo: repository.NewProductRepositoryImpl(a.db), productRepo: repository.NewProductRepositoryImpl(a.db),
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db), productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db), inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db), inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
orderRepo: repository.NewOrderRepositoryImpl(a.db), orderRepo: repository.NewOrderRepositoryImpl(a.db),
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db), orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
paymentRepo: repository.NewPaymentRepositoryImpl(a.db), paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db), paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db), paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
fileRepo: repository.NewFileRepositoryImpl(a.db), fileRepo: repository.NewFileRepositoryImpl(a.db),
customerRepo: repository.NewCustomerRepository(a.db), customerRepo: repository.NewCustomerRepository(a.db),
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db), analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
tableRepo: repository.NewTableRepository(a.db), tableRepo: repository.NewTableRepository(a.db),
unitRepo: repository.NewUnitRepository(a.db), unitRepo: repository.NewUnitRepository(a.db),
ingredientRepo: repository.NewIngredientRepository(a.db), ingredientRepo: repository.NewIngredientRepository(a.db),
productRecipeRepo: repository.NewProductRecipeRepository(a.db), ingredientCompositionRepo: repository.NewIngredientCompositionRepository(a.db),
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(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),
txManager: repository.NewTxManager(a.db),
} }
} }
type processors struct { type processors struct {
userProcessor *processor.UserProcessorImpl userProcessor *processor.UserProcessorImpl
organizationProcessor processor.OrganizationProcessor organizationProcessor processor.OrganizationProcessor
outletProcessor processor.OutletProcessor outletProcessor processor.OutletProcessor
outletSettingProcessor *processor.OutletSettingProcessorImpl outletSettingProcessor *processor.OutletSettingProcessorImpl
categoryProcessor processor.CategoryProcessor categoryProcessor processor.CategoryProcessor
productProcessor processor.ProductProcessor productProcessor processor.ProductProcessor
productVariantProcessor processor.ProductVariantProcessor productVariantProcessor processor.ProductVariantProcessor
inventoryProcessor processor.InventoryProcessor inventoryProcessor processor.InventoryProcessor
orderProcessor processor.OrderProcessor orderProcessor processor.OrderProcessor
paymentMethodProcessor processor.PaymentMethodProcessor paymentMethodProcessor processor.PaymentMethodProcessor
fileProcessor processor.FileProcessor fileProcessor processor.FileProcessor
customerProcessor *processor.CustomerProcessor customerProcessor *processor.CustomerProcessor
analyticsProcessor *processor.AnalyticsProcessorImpl analyticsProcessor *processor.AnalyticsProcessorImpl
tableProcessor *processor.TableProcessor tableProcessor *processor.TableProcessor
unitProcessor *processor.UnitProcessorImpl unitProcessor *processor.UnitProcessorImpl
ingredientProcessor *processor.IngredientProcessorImpl ingredientProcessor *processor.IngredientProcessorImpl
productRecipeProcessor *processor.ProductRecipeProcessorImpl productRecipeProcessor *processor.ProductRecipeProcessorImpl
fileClient processor.FileClient vendorProcessor *processor.VendorProcessorImpl
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
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
} }
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors { func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
fileClient := client.NewFileClient(cfg.S3Config) 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{ return &processors{
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo), userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo), organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo), outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo), outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo), categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo), productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo), productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo), 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), 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),
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo), paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient), fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo), customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo), analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo),
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo), tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
unitProcessor: processor.NewUnitProcessor(repos.unitRepo), unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo), ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo), productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
fileClient: fileClient, vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
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(repos.customerPointsRepo, repos.gameRepo),
otpProcessor: otpProcessor,
fileClient: fileClient,
inventoryMovementService: inventoryMovementService,
} }
} }
type services struct { type services struct {
userService *service.UserServiceImpl userService *service.UserServiceImpl
authService service.AuthService authService service.AuthService
organizationService service.OrganizationService organizationService service.OrganizationService
outletService service.OutletService outletService service.OutletService
outletSettingService service.OutletSettingService outletSettingService service.OutletSettingService
categoryService service.CategoryService categoryService service.CategoryService
productService service.ProductService productService service.ProductService
productVariantService service.ProductVariantService productVariantService service.ProductVariantService
inventoryService service.InventoryService inventoryService service.InventoryService
orderService service.OrderService orderService service.OrderService
paymentMethodService service.PaymentMethodService paymentMethodService service.PaymentMethodService
fileService service.FileService fileService service.FileService
customerService service.CustomerService customerService service.CustomerService
analyticsService *service.AnalyticsServiceImpl analyticsService *service.AnalyticsServiceImpl
reportService service.ReportService reportService service.ReportService
tableService *service.TableServiceImpl tableService *service.TableServiceImpl
unitService *service.UnitServiceImpl unitService *service.UnitServiceImpl
ingredientService *service.IngredientServiceImpl ingredientService *service.IngredientServiceImpl
productRecipeService *service.ProductRecipeServiceImpl productRecipeService *service.ProductRecipeServiceImpl
vendorService *service.VendorServiceImpl
purchaseOrderService *service.PurchaseOrderServiceImpl
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
} }
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services { func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
authConfig := cfg.Auth() authConfig := cfg.Auth()
jwtSecret := authConfig.AccessTokenSecret() authService := service.NewAuthService(processors.userProcessor, authConfig)
authService := service.NewAuthService(processors.userProcessor, jwtSecret)
organizationService := service.NewOrganizationService(processors.organizationProcessor) organizationService := service.NewOrganizationService(processors.organizationProcessor)
outletService := service.NewOutletService(processors.outletProcessor) outletService := service.NewOutletService(processors.outletProcessor)
outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor) outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor)
@@ -253,7 +375,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
productService := service.NewProductService(processors.productProcessor) productService := service.NewProductService(processors.productProcessor)
productVariantService := service.NewProductVariantService(processors.productVariantProcessor) productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
inventoryService := service.NewInventoryService(processors.inventoryProcessor) inventoryService := service.NewInventoryService(processors.inventoryProcessor)
orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo) orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager) // Will be updated after orderIngredientTransactionService is created
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor) paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
fileService := service.NewFileServiceImpl(processors.fileProcessor) fileService := service.NewFileServiceImpl(processors.fileProcessor)
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor) var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
@@ -263,68 +385,121 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
unitService := service.NewUnitService(processors.unitProcessor) unitService := service.NewUnitService(processors.unitProcessor)
ingredientService := service.NewIngredientService(processors.ingredientProcessor) ingredientService := service.NewIngredientService(processors.ingredientProcessor)
productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor) productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor)
vendorService := service.NewVendorService(processors.vendorProcessor)
purchaseOrderService := service.NewPurchaseOrderService(processors.purchaseOrderProcessor)
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)
// Update order service with order ingredient transaction service
orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager)
return &services{ return &services{
userService: service.NewUserService(processors.userProcessor), userService: service.NewUserService(processors.userProcessor),
authService: authService, authService: authService,
organizationService: organizationService, organizationService: organizationService,
outletService: outletService, outletService: outletService,
outletSettingService: outletSettingService, outletSettingService: outletSettingService,
categoryService: categoryService, categoryService: categoryService,
productService: productService, productService: productService,
productVariantService: productVariantService, productVariantService: productVariantService,
inventoryService: inventoryService, inventoryService: inventoryService,
orderService: orderService, orderService: orderService,
paymentMethodService: paymentMethodService, paymentMethodService: paymentMethodService,
fileService: fileService, fileService: fileService,
customerService: customerService, customerService: customerService,
analyticsService: analyticsService, analyticsService: analyticsService,
reportService: reportService, reportService: reportService,
tableService: tableService, tableService: tableService,
unitService: unitService, unitService: unitService,
ingredientService: ingredientService, ingredientService: ingredientService,
productRecipeService: productRecipeService, productRecipeService: productRecipeService,
vendorService: vendorService,
purchaseOrderService: purchaseOrderService,
unitConverterService: unitConverterService,
chartOfAccountTypeService: chartOfAccountTypeService,
chartOfAccountService: chartOfAccountService,
accountService: accountService,
orderIngredientTransactionService: orderIngredientTransactionService,
gamificationService: gamificationService,
rewardService: rewardService,
campaignService: campaignService,
customerAuthService: customerAuthService,
customerPointsService: customerPointsService,
spinGameService: spinGameService,
} }
} }
type middlewares struct { type middlewares struct {
authMiddleware *middleware.AuthMiddleware authMiddleware *middleware.AuthMiddleware
customerAuthMiddleware *middleware.CustomerAuthMiddleware
} }
func (a *App) initMiddleware(services *services) *middlewares { func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
return &middlewares{ return &middlewares{
authMiddleware: middleware.NewAuthMiddleware(services.authService), authMiddleware: middleware.NewAuthMiddleware(services.authService),
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
} }
} }
type validators struct { type validators struct {
userValidator *validator.UserValidatorImpl userValidator *validator.UserValidatorImpl
organizationValidator validator.OrganizationValidator organizationValidator validator.OrganizationValidator
outletValidator validator.OutletValidator outletValidator validator.OutletValidator
categoryValidator validator.CategoryValidator categoryValidator validator.CategoryValidator
productValidator validator.ProductValidator productValidator validator.ProductValidator
productVariantValidator validator.ProductVariantValidator productVariantValidator validator.ProductVariantValidator
inventoryValidator validator.InventoryValidator inventoryValidator validator.InventoryValidator
orderValidator validator.OrderValidator orderValidator validator.OrderValidator
paymentMethodValidator validator.PaymentMethodValidator paymentMethodValidator validator.PaymentMethodValidator
fileValidator validator.FileValidator fileValidator validator.FileValidator
customerValidator validator.CustomerValidator customerValidator validator.CustomerValidator
tableValidator *validator.TableValidator tableValidator *validator.TableValidator
vendorValidator *validator.VendorValidatorImpl
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
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
} }
func (a *App) initValidators() *validators { func (a *App) initValidators() *validators {
return &validators{ return &validators{
userValidator: validator.NewUserValidator(), userValidator: validator.NewUserValidator(),
organizationValidator: validator.NewOrganizationValidator(), organizationValidator: validator.NewOrganizationValidator(),
outletValidator: validator.NewOutletValidator(), outletValidator: validator.NewOutletValidator(),
categoryValidator: validator.NewCategoryValidator(), categoryValidator: validator.NewCategoryValidator(),
productValidator: validator.NewProductValidator(), productValidator: validator.NewProductValidator(),
productVariantValidator: validator.NewProductVariantValidator(), productVariantValidator: validator.NewProductVariantValidator(),
inventoryValidator: validator.NewInventoryValidator(), inventoryValidator: validator.NewInventoryValidator(),
orderValidator: validator.NewOrderValidator(), orderValidator: validator.NewOrderValidator(),
paymentMethodValidator: validator.NewPaymentMethodValidator(), paymentMethodValidator: validator.NewPaymentMethodValidator(),
fileValidator: validator.NewFileValidatorImpl(), fileValidator: validator.NewFileValidatorImpl(),
customerValidator: validator.NewCustomerValidator(), customerValidator: validator.NewCustomerValidator(),
tableValidator: validator.NewTableValidator(), tableValidator: validator.NewTableValidator(),
vendorValidator: validator.NewVendorValidator(),
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
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(),
} }
} }
+15 -1
View File
@@ -2,6 +2,7 @@ package appcontext
import ( import (
"context" "context"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@@ -77,5 +78,18 @@ func FromContext(ctx context.Context) *ContextInfo {
if info, ok := ctx.Value(ctxKey).(*ContextInfo); ok { if info, ok := ctx.Value(ctxKey).(*ContextInfo); ok {
return info return info
} }
return nil // Fallback: construct ContextInfo from individual context values
return &ContextInfo{
CorrelationID: value(ctx, CorrelationIDKey),
UserID: uuidValue(ctx, UserIDKey),
OutletID: uuidValue(ctx, OutletIDKey),
OrganizationID: uuidValue(ctx, OrganizationIDKey),
AppVersion: value(ctx, AppVersionKey),
AppID: value(ctx, AppIDKey),
AppType: value(ctx, AppTypeKey),
Platform: value(ctx, PlatformKey),
DeviceOS: value(ctx, DeviceOSKey),
UserLocale: value(ctx, UserLocaleKey),
UserRole: value(ctx, UserRoleKey),
}
} }
+77
View File
@@ -0,0 +1,77 @@
package client
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"time"
"apskel-pos-be/config"
)
type FonnteClient interface {
SendWhatsAppMessage(target string, message string) error
}
type fonnteClient struct {
httpClient *http.Client
apiUrl string
token string
}
type FonnteResponse struct {
Status bool `json:"status"`
Message string `json:"message"`
}
func NewFonnteClient(cfg *config.Fonnte) FonnteClient {
return &fonnteClient{
httpClient: &http.Client{
Timeout: time.Duration(cfg.GetTimeout()) * time.Second,
},
apiUrl: cfg.GetApiUrl(),
token: cfg.GetToken(),
}
}
func (c *fonnteClient) SendWhatsAppMessage(target string, message string) error {
// Prepare form data
data := url.Values{}
data.Set("target", target)
data.Set("message", message)
// Create request
req, err := http.NewRequest("POST", c.apiUrl, bytes.NewBufferString(data.Encode()))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
// Set headers
req.Header.Set("Authorization", c.token)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// Send request
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
// Read response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}
// Check HTTP status
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("fonnte API returned status %d: %s", resp.StatusCode, string(body))
}
// Log the response for debugging
fmt.Printf("Fonnte API response: %s\n", string(body))
return nil
}
@@ -0,0 +1,272 @@
{
"chart_of_accounts": [
{
"name": "Current Assets",
"code": "1000",
"chart_of_account_type": "ASSET",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Cash on Hand",
"number": "1001",
"account_type": "cash",
"opening_balance": 0.00,
"description": "Physical cash available at the outlet"
},
{
"name": "Petty Cash",
"number": "1002",
"account_type": "cash",
"opening_balance": 0.00,
"description": "Small amount of cash for minor expenses"
},
{
"name": "Bank Account - Main",
"number": "1003",
"account_type": "bank",
"opening_balance": 0.00,
"description": "Primary business bank account"
},
{
"name": "Digital Wallet",
"number": "1004",
"account_type": "wallet",
"opening_balance": 0.00,
"description": "Digital payment wallet"
}
]
},
{
"name": "Inventory",
"code": "1100",
"chart_of_account_type": "ASSET",
"parent_code": "1000",
"is_system": true,
"accounts": [
{
"name": "Raw Materials",
"number": "1101",
"account_type": "asset",
"opening_balance": 0.00,
"description": "Raw materials and ingredients inventory"
},
{
"name": "Finished Goods",
"number": "1102",
"account_type": "asset",
"opening_balance": 0.00,
"description": "Finished products ready for sale"
},
{
"name": "Work in Progress",
"number": "1103",
"account_type": "asset",
"opening_balance": 0.00,
"description": "Products in production process"
}
]
},
{
"name": "Fixed Assets",
"code": "1500",
"chart_of_account_type": "ASSET",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Equipment",
"number": "1501",
"account_type": "asset",
"opening_balance": 0.00,
"description": "Business equipment and machinery"
},
{
"name": "Furniture & Fixtures",
"number": "1502",
"account_type": "asset",
"opening_balance": 0.00,
"description": "Furniture and fixtures"
}
]
},
{
"name": "Current Liabilities",
"code": "2000",
"chart_of_account_type": "LIABILITY",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Accounts Payable",
"number": "2001",
"account_type": "liability",
"opening_balance": 0.00,
"description": "Amounts owed to suppliers and vendors"
},
{
"name": "Accrued Expenses",
"number": "2002",
"account_type": "liability",
"opening_balance": 0.00,
"description": "Expenses incurred but not yet paid"
},
{
"name": "Sales Tax Payable",
"number": "2003",
"account_type": "liability",
"opening_balance": 0.00,
"description": "Sales tax collected but not yet remitted"
}
]
},
{
"name": "Owner's Equity",
"code": "3000",
"chart_of_account_type": "EQUITY",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Owner's Capital",
"number": "3001",
"account_type": "equity",
"opening_balance": 0.00,
"description": "Owner's initial investment in the business"
},
{
"name": "Retained Earnings",
"number": "3002",
"account_type": "equity",
"opening_balance": 0.00,
"description": "Accumulated profits retained in the business"
}
]
},
{
"name": "Revenue",
"code": "4000",
"chart_of_account_type": "REVENUE",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Sales Revenue",
"number": "4001",
"account_type": "revenue",
"opening_balance": 0.00,
"description": "Revenue from product sales"
},
{
"name": "Service Revenue",
"number": "4002",
"account_type": "revenue",
"opening_balance": 0.00,
"description": "Revenue from services provided"
},
{
"name": "Other Income",
"number": "4003",
"account_type": "revenue",
"opening_balance": 0.00,
"description": "Other sources of income"
}
]
},
{
"name": "Cost of Goods Sold",
"code": "5000",
"chart_of_account_type": "EXPENSE",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Raw Materials Cost",
"number": "5001",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Cost of raw materials used in production"
},
{
"name": "Direct Labor Cost",
"number": "5002",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Direct labor costs for production"
},
{
"name": "Manufacturing Overhead",
"number": "5003",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Manufacturing overhead costs"
}
]
},
{
"name": "Operating Expenses",
"code": "6000",
"chart_of_account_type": "EXPENSE",
"parent_code": null,
"is_system": true,
"accounts": [
{
"name": "Rent Expense",
"number": "6001",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Rent for business premises"
},
{
"name": "Utilities Expense",
"number": "6002",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Electricity, water, and other utilities"
},
{
"name": "Salaries & Wages",
"number": "6003",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Employee salaries and wages"
},
{
"name": "Marketing Expense",
"number": "6004",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Marketing and advertising expenses"
},
{
"name": "Office Supplies",
"number": "6005",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Office supplies and stationery"
},
{
"name": "Professional Services",
"number": "6006",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Legal, accounting, and consulting fees"
},
{
"name": "Insurance Expense",
"number": "6007",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Business insurance premiums"
},
{
"name": "Depreciation Expense",
"number": "6008",
"account_type": "expense",
"opening_balance": 0.00,
"description": "Depreciation of fixed assets"
}
]
}
]
}
+41 -24
View File
@@ -15,30 +15,47 @@ const (
) )
const ( const (
RequestEntity = "request" RequestEntity = "request"
UserServiceEntity = "user_service" UserServiceEntity = "user_service"
OrganizationServiceEntity = "organization_service" OrganizationServiceEntity = "organization_service"
CategoryServiceEntity = "category_service" CategoryServiceEntity = "category_service"
ProductServiceEntity = "product_service" ProductServiceEntity = "product_service"
ProductVariantServiceEntity = "product_variant_service" ProductVariantServiceEntity = "product_variant_service"
InventoryServiceEntity = "inventory_service" InventoryServiceEntity = "inventory_service"
OrderServiceEntity = "order_service" OrderServiceEntity = "order_service"
CustomerServiceEntity = "customer_service" CustomerServiceEntity = "customer_service"
UserValidatorEntity = "user_validator" UserValidatorEntity = "user_validator"
AuthHandlerEntity = "auth_handler" AuthHandlerEntity = "auth_handler"
UserHandlerEntity = "user_handler" UserHandlerEntity = "user_handler"
CategoryHandlerEntity = "category_handler" CategoryHandlerEntity = "category_handler"
ProductHandlerEntity = "product_handler" ProductHandlerEntity = "product_handler"
ProductVariantHandlerEntity = "product_variant_handler" ProductVariantHandlerEntity = "product_variant_handler"
InventoryHandlerEntity = "inventory_handler" InventoryHandlerEntity = "inventory_handler"
OrderValidatorEntity = "order_validator" OrderValidatorEntity = "order_validator"
OrderHandlerEntity = "order_handler" OrderHandlerEntity = "order_handler"
OrganizationValidatorEntity = "organization_validator" OrganizationValidatorEntity = "organization_validator"
OrgHandlerEntity = "organization_handler" OrgHandlerEntity = "organization_handler"
PaymentMethodValidatorEntity = "payment_method_validator" PaymentMethodValidatorEntity = "payment_method_validator"
PaymentMethodHandlerEntity = "payment_method_handler" PaymentMethodHandlerEntity = "payment_method_handler"
OutletServiceEntity = "outlet_service" OutletServiceEntity = "outlet_service"
TableEntity = "table" VendorServiceEntity = "vendor_service"
PurchaseOrderServiceEntity = "purchase_order_service"
IngredientUnitConverterServiceEntity = "ingredient_unit_converter_service"
IngredientCompositionServiceEntity = "ingredient_composition_service"
TableEntity = "table"
// Gamification entities
CustomerPointsEntity = "customer_points"
CustomerTokensEntity = "customer_tokens"
TierEntity = "tier"
GameEntity = "game"
GamePrizeEntity = "game_prize"
GamePlayEntity = "game_play"
OmsetTrackerEntity = "omset_tracker"
RewardEntity = "reward"
CampaignEntity = "campaign"
CampaignRuleEntity = "campaign_rule"
CustomerEntity = "customer"
SpinGameHandlerEntity = "spin_game_handler"
) )
var HttpErrorMap = map[string]int{ var HttpErrorMap = map[string]int{
+19
View File
@@ -0,0 +1,19 @@
package contract
import (
"context"
"github.com/google/uuid"
)
type AccountContract interface {
CreateAccount(ctx context.Context, req *CreateAccountRequest) (*AccountResponse, error)
GetAccountByID(ctx context.Context, id uuid.UUID) (*AccountResponse, error)
UpdateAccount(ctx context.Context, id uuid.UUID, req *UpdateAccountRequest) (*AccountResponse, error)
DeleteAccount(ctx context.Context, id uuid.UUID) error
ListAccounts(ctx context.Context, req *ListAccountsRequest) ([]AccountResponse, int, error)
GetAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]AccountResponse, error)
GetAccountsByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]AccountResponse, error)
UpdateAccountBalance(ctx context.Context, id uuid.UUID, req *UpdateAccountBalanceRequest) error
GetAccountBalance(ctx context.Context, id uuid.UUID) (float64, error)
}
+57
View File
@@ -0,0 +1,57 @@
package contract
import (
"github.com/google/uuid"
)
type CreateAccountRequest struct {
ChartOfAccountID uuid.UUID `json:"chart_of_account_id" validate:"required"`
Name string `json:"name" validate:"required,min=1,max=255"`
Number string `json:"number" validate:"required,min=1,max=50"`
AccountType string `json:"account_type" validate:"required,oneof=cash wallet bank credit debit asset liability equity revenue expense"`
OpeningBalance float64 `json:"opening_balance"`
Description *string `json:"description"`
}
type UpdateAccountRequest struct {
ChartOfAccountID *uuid.UUID `json:"chart_of_account_id"`
Name *string `json:"name" validate:"omitempty,min=1,max=255"`
Number *string `json:"number" validate:"omitempty,min=1,max=50"`
AccountType *string `json:"account_type" validate:"omitempty,oneof=cash wallet bank credit debit asset liability equity revenue expense"`
OpeningBalance *float64 `json:"opening_balance"`
Description *string `json:"description"`
IsActive *bool `json:"is_active"`
}
type AccountResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id"`
ChartOfAccountID uuid.UUID `json:"chart_of_account_id"`
Name string `json:"name"`
Number string `json:"number"`
AccountType string `json:"account_type"`
OpeningBalance float64 `json:"opening_balance"`
CurrentBalance float64 `json:"current_balance"`
Description *string `json:"description"`
IsActive bool `json:"is_active"`
IsSystem bool `json:"is_system"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ChartOfAccount *ChartOfAccountResponse `json:"chart_of_account,omitempty"`
}
type ListAccountsRequest struct {
OrganizationID *uuid.UUID `form:"organization_id"`
OutletID *uuid.UUID `form:"outlet_id"`
ChartOfAccountID *uuid.UUID `form:"chart_of_account_id"`
AccountType *string `form:"account_type"`
IsActive *bool `form:"is_active"`
IsSystem *bool `form:"is_system"`
Page int `form:"page,default=1"`
Limit int `form:"limit,default=10"`
}
type UpdateAccountBalanceRequest struct {
Amount float64 `json:"amount" binding:"required"`
}
+46 -10
View File
@@ -89,7 +89,7 @@ type ProductAnalyticsRequest struct {
OutletID *uuid.UUID `form:"outlet_id,omitempty"` OutletID *uuid.UUID `form:"outlet_id,omitempty"`
DateFrom string `form:"date_from" validate:"required"` DateFrom string `form:"date_from" validate:"required"`
DateTo string `form:"date_to" validate:"required"` DateTo string `form:"date_to" validate:"required"`
Limit int `form:"limit,default=10" validate:"min=1,max=100"` Limit int `form:"limit,default=1000" validate:"min=1,max=1000"`
} }
// ProductAnalyticsResponse represents the response for product analytics // ProductAnalyticsResponse represents the response for product analytics
@@ -101,16 +101,52 @@ type ProductAnalyticsResponse struct {
Data []ProductAnalyticsData `json:"data"` Data []ProductAnalyticsData `json:"data"`
} }
// ProductAnalyticsData represents individual product analytics data
type ProductAnalyticsData struct { type ProductAnalyticsData struct {
ProductID uuid.UUID `json:"product_id"` ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"` ProductSku string `json:"product_sku"`
CategoryName string `json:"category_name"` CategoryID uuid.UUID `json:"category_id"`
QuantitySold int64 `json:"quantity_sold"` CategoryName string `json:"category_name"`
Revenue float64 `json:"revenue"` CategoryOrder int `json:"category_order"`
AveragePrice float64 `json:"average_price"` QuantitySold int64 `json:"quantity_sold"`
OrderCount int64 `json:"order_count"` Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`
OrderCount int64 `json:"order_count"`
StandardHppPerUnit float64 `json:"standard_hpp_per_unit"`
StandardHppTotal float64 `json:"standard_hpp_total"`
FifoHppPerUnit float64 `json:"fifo_hpp_per_unit"`
FifoHppTotal float64 `json:"fifo_hpp_total"`
MovingAverageHppPerUnit float64 `json:"moving_average_hpp_per_unit"`
MovingAverageHppTotal float64 `json:"moving_average_hpp_total"`
}
// ProductAnalyticsPerCategoryRequest represents the request for product analytics per category
type ProductAnalyticsPerCategoryRequest struct {
OrganizationID uuid.UUID
OutletID *uuid.UUID `form:"outlet_id,omitempty"`
DateFrom string `form:"date_from" validate:"required"`
DateTo string `form:"date_to" validate:"required"`
}
// ProductAnalyticsPerCategoryResponse represents the response for product analytics per category
type ProductAnalyticsPerCategoryResponse struct {
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
DateFrom time.Time `json:"date_from"`
DateTo time.Time `json:"date_to"`
Data []ProductAnalyticsPerCategoryData `json:"data"`
}
type ProductAnalyticsPerCategoryData struct {
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
TotalRevenue float64 `json:"total_revenue"`
TotalQuantity int64 `json:"total_quantity"`
ProductCount int64 `json:"product_count"`
OrderCount int64 `json:"order_count"`
TotalStandardHpp float64 `json:"total_standard_hpp"`
TotalFifoHpp float64 `json:"total_fifo_hpp"`
TotalMovingAverageHpp float64 `json:"total_moving_average_hpp"`
} }
// DashboardAnalyticsRequest represents the request for dashboard analytics // DashboardAnalyticsRequest represents the request for dashboard analytics
+150
View File
@@ -0,0 +1,150 @@
package contract
import (
"time"
"apskel-pos-be/internal/entities"
"github.com/google/uuid"
)
// Request Contracts
type CreateCampaignRequest struct {
Name string `json:"name" binding:"required,min=1,max=150"`
Description *string `json:"description,omitempty"`
Type string `json:"type" binding:"required,oneof=REWARD POINTS TOKENS MIXED"`
StartDate time.Time `json:"start_date" binding:"required"`
EndDate time.Time `json:"end_date" binding:"required"`
IsActive bool `json:"is_active"`
ShowOnApp bool `json:"show_on_app"`
Position int `json:"position" binding:"min=0"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
}
type UpdateCampaignRequest struct {
ID uuid.UUID `json:"id" binding:"required"`
Name string `json:"name" binding:"required,min=1,max=150"`
Description *string `json:"description,omitempty"`
Type string `json:"type" binding:"required,oneof=REWARD POINTS TOKENS MIXED"`
StartDate time.Time `json:"start_date" binding:"required"`
EndDate time.Time `json:"end_date" binding:"required"`
IsActive bool `json:"is_active"`
ShowOnApp bool `json:"show_on_app"`
Position int `json:"position" binding:"min=0"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
}
type ListCampaignsRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
Search string `form:"search"`
Type string `form:"type"`
IsActive *bool `form:"is_active"`
ShowOnApp *bool `form:"show_on_app"`
StartDate *time.Time `form:"start_date"`
EndDate *time.Time `form:"end_date"`
}
type GetCampaignRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
type DeleteCampaignRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
// Campaign Rule Request Contracts
type CreateCampaignRuleRequest struct {
CampaignID uuid.UUID `json:"campaign_id" binding:"required"`
RuleType string `json:"rule_type" binding:"required,oneof=TIER SPEND PRODUCT CATEGORY DAY LOCATION"`
ConditionValue *string `json:"condition_value,omitempty"`
RewardType string `json:"reward_type" binding:"required,oneof=POINTS TOKENS REWARD"`
RewardValue *int64 `json:"reward_value,omitempty"`
RewardSubtype *string `json:"reward_subtype,omitempty"`
RewardRefID *uuid.UUID `json:"reward_ref_id,omitempty"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
}
type UpdateCampaignRuleRequest struct {
ID uuid.UUID `json:"id" binding:"required"`
CampaignID uuid.UUID `json:"campaign_id" binding:"required"`
RuleType string `json:"rule_type" binding:"required,oneof=TIER SPEND PRODUCT CATEGORY DAY LOCATION"`
ConditionValue *string `json:"condition_value,omitempty"`
RewardType string `json:"reward_type" binding:"required,oneof=POINTS TOKENS REWARD"`
RewardValue *int64 `json:"reward_value,omitempty"`
RewardSubtype *string `json:"reward_subtype,omitempty"`
RewardRefID *uuid.UUID `json:"reward_ref_id,omitempty"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
}
type ListCampaignRulesRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
CampaignID string `form:"campaign_id"`
RuleType string `form:"rule_type"`
RewardType string `form:"reward_type"`
}
type GetCampaignRuleRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
type DeleteCampaignRuleRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
// Response Contracts
type CampaignResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Type string `json:"type"`
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
IsActive bool `json:"is_active"`
ShowOnApp bool `json:"show_on_app"`
Position int `json:"position"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
Rules []CampaignRuleResponse `json:"rules,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CampaignRuleResponse struct {
ID uuid.UUID `json:"id"`
CampaignID uuid.UUID `json:"campaign_id"`
RuleType string `json:"rule_type"`
ConditionValue *string `json:"condition_value,omitempty"`
RewardType string `json:"reward_type"`
RewardValue *int64 `json:"reward_value,omitempty"`
RewardSubtype *string `json:"reward_subtype,omitempty"`
RewardRefID *uuid.UUID `json:"reward_ref_id,omitempty"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListCampaignsResponse struct {
Campaigns []CampaignResponse `json:"campaigns"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}
type ListCampaignRulesResponse struct {
Rules []CampaignRuleResponse `json:"rules"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}
// Helper structs
type CampaignRuleStruct struct {
RuleType string `json:"rule_type" binding:"required,oneof=TIER SPEND PRODUCT CATEGORY DAY LOCATION"`
ConditionValue *string `json:"condition_value,omitempty"`
RewardType string `json:"reward_type" binding:"required,oneof=POINTS TOKENS REWARD"`
RewardValue *int64 `json:"reward_value,omitempty"`
RewardSubtype *string `json:"reward_subtype,omitempty"`
RewardRefID *uuid.UUID `json:"reward_ref_id,omitempty"`
Metadata *entities.Metadata `json:"metadata,omitempty"`
}
+3
View File
@@ -10,6 +10,7 @@ type CreateCategoryRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"` Name string `json:"name" validate:"required,min=1,max=255"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"` BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"`
} }
@@ -17,6 +18,7 @@ type UpdateCategoryRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"` Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
Description *string `json:"description,omitempty"` Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"` BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"`
} }
@@ -35,6 +37,7 @@ type CategoryResponse struct {
Name string `json:"name"` Name string `json:"name"`
Description *string `json:"description"` Description *string `json:"description"`
BusinessType string `json:"business_type"` BusinessType string `json:"business_type"`
Order int `json:"order"`
Metadata map[string]interface{} `json:"metadata"` Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
@@ -0,0 +1,17 @@
package contract
import (
"context"
"github.com/google/uuid"
)
type ChartOfAccountContract interface {
CreateChartOfAccount(ctx context.Context, req *CreateChartOfAccountRequest) (*ChartOfAccountResponse, error)
GetChartOfAccountByID(ctx context.Context, id uuid.UUID) (*ChartOfAccountResponse, error)
UpdateChartOfAccount(ctx context.Context, id uuid.UUID, req *UpdateChartOfAccountRequest) (*ChartOfAccountResponse, error)
DeleteChartOfAccount(ctx context.Context, id uuid.UUID) error
ListChartOfAccounts(ctx context.Context, req *ListChartOfAccountsRequest) ([]ChartOfAccountResponse, int, error)
GetChartOfAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]ChartOfAccountResponse, error)
GetChartOfAccountsByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]ChartOfAccountResponse, error)
}
@@ -0,0 +1,49 @@
package contract
import (
"github.com/google/uuid"
)
type CreateChartOfAccountRequest struct {
ChartOfAccountTypeID uuid.UUID `json:"chart_of_account_type_id" validate:"required"`
ParentID *uuid.UUID `json:"parent_id"`
Name string `json:"name" validate:"required,min=1,max=255"`
Code string `json:"code" validate:"required,min=1,max=20"`
Description *string `json:"description"`
}
type UpdateChartOfAccountRequest struct {
ChartOfAccountTypeID *uuid.UUID `json:"chart_of_account_type_id"`
ParentID *uuid.UUID `json:"parent_id"`
Name *string `json:"name" validate:"omitempty,min=1,max=255"`
Code *string `json:"code" validate:"omitempty,min=1,max=20"`
Description *string `json:"description"`
IsActive *bool `json:"is_active"`
}
type ChartOfAccountResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id"`
ChartOfAccountTypeID uuid.UUID `json:"chart_of_account_type_id"`
ParentID *uuid.UUID `json:"parent_id"`
Name string `json:"name"`
Code string `json:"code"`
Description *string `json:"description"`
IsActive bool `json:"is_active"`
IsSystem bool `json:"is_system"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
ChartOfAccountType *ChartOfAccountTypeResponse `json:"chart_of_account_type,omitempty"`
Parent *ChartOfAccountResponse `json:"parent,omitempty"`
Children []ChartOfAccountResponse `json:"children,omitempty"`
}
type ListChartOfAccountsRequest struct {
ChartOfAccountTypeID *uuid.UUID `form:"chart_of_account_type_id"`
ParentID *uuid.UUID `form:"parent_id"`
IsActive *bool `form:"is_active"`
IsSystem *bool `form:"is_system"`
Page int `form:"page,default=1"`
Limit int `form:"limit,default=10"`
}
@@ -0,0 +1,15 @@
package contract
import (
"context"
"github.com/google/uuid"
)
type ChartOfAccountTypeContract interface {
CreateChartOfAccountType(ctx context.Context, req *CreateChartOfAccountTypeRequest) (*ChartOfAccountTypeResponse, error)
GetChartOfAccountTypeByID(ctx context.Context, id uuid.UUID) (*ChartOfAccountTypeResponse, error)
UpdateChartOfAccountType(ctx context.Context, id uuid.UUID, req *UpdateChartOfAccountTypeRequest) (*ChartOfAccountTypeResponse, error)
DeleteChartOfAccountType(ctx context.Context, id uuid.UUID) error
ListChartOfAccountTypes(ctx context.Context, filters map[string]interface{}, page, limit int) ([]ChartOfAccountTypeResponse, int, error)
}
@@ -0,0 +1,28 @@
package contract
import (
"github.com/google/uuid"
)
type CreateChartOfAccountTypeRequest struct {
Name string `json:"name" validate:"required,min=1,max=100"`
Code string `json:"code" validate:"required,min=1,max=10"`
Description *string `json:"description"`
}
type UpdateChartOfAccountTypeRequest struct {
Name *string `json:"name" validate:"omitempty,min=1,max=100"`
Code *string `json:"code" validate:"omitempty,min=1,max=10"`
Description *string `json:"description"`
IsActive *bool `json:"is_active"`
}
type ChartOfAccountTypeResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Description *string `json:"description"`
IsActive bool `json:"is_active"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
+144
View File
@@ -0,0 +1,144 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Request Contracts
type CheckPhoneRequest struct {
PhoneNumber string `json:"phone_number" binding:"required"`
Password string `json:"password,omitempty"` // Optional - only required if user exists
}
type RegisterStartRequest struct {
PhoneNumber string `json:"phone_number" binding:"required"`
Name string `json:"name" binding:"required"`
BirthDate string `json:"birth_date" binding:"required"`
}
type RegisterVerifyOtpRequest struct {
RegistrationToken string `json:"registration_token" binding:"required"`
OtpCode string `json:"otp_code" binding:"required"`
}
type RegisterSetPasswordRequest struct {
RegistrationToken string `json:"registration_token" binding:"required"`
Password string `json:"password" binding:"required,min=8"`
ConfirmPassword string `json:"confirm_password" binding:"required"`
}
type CustomerLoginRequest struct {
PhoneNumber string `json:"phone_number" binding:"required"`
Password string `json:"password" binding:"required"`
}
type ResendOtpRequest struct {
PhoneNumber string `json:"phone_number" binding:"required"`
Purpose string `json:"purpose" binding:"required,oneof=login registration"`
}
// Response Contracts
type CheckPhoneResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *CheckPhoneResponseData `json:"data,omitempty"`
}
type CheckPhoneResponseData struct {
// For NOT_REGISTERED status
PhoneNumber string `json:"phone_number,omitempty"`
// For PASSWORD_REQUIRED status
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
User *CustomerUserData `json:"user,omitempty"`
// For OTP_REQUIRED status (if password doesn't exist)
OtpToken string `json:"otp_token,omitempty"`
ExpiresIn int `json:"expires_in,omitempty"`
}
type RegisterStartResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *RegisterStartResponseData `json:"data,omitempty"`
}
type RegisterStartResponseData struct {
RegistrationToken string `json:"registration_token"`
OtpToken string `json:"otp_token"`
ExpiresIn int `json:"expires_in"`
}
type RegisterVerifyOtpResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *RegisterVerifyOtpResponseData `json:"data,omitempty"`
}
type RegisterVerifyOtpResponseData struct {
RegistrationToken string `json:"registration_token"`
}
type RegisterSetPasswordResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *RegisterSetPasswordResponseData `json:"data,omitempty"`
}
type RegisterSetPasswordResponseData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
User *CustomerUserData `json:"user"`
}
type CustomerUserData struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
PhoneNumber string `json:"phone_number"`
BirthDate string `json:"birth_date"`
}
type CustomerLoginResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *CustomerLoginResponseData `json:"data,omitempty"`
}
type CustomerLoginResponseData struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
User *CustomerUserData `json:"user"`
}
type ResendOtpResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *ResendOtpResponseData `json:"data,omitempty"`
}
type ResendOtpResponseData struct {
OtpToken string `json:"otp_token"`
ExpiresIn int `json:"expires_in"`
NextResendIn int `json:"next_resend_in"` // Seconds until next resend is allowed
}
// Internal structures for OTP and registration tokens
type OtpSession struct {
Token string `json:"token"`
Code string `json:"code"`
PhoneNumber string `json:"phone_number"`
ExpiresAt time.Time `json:"expires_at"`
Purpose string `json:"purpose"` // "login" or "registration"
}
type RegistrationSession struct {
Token string `json:"token"`
PhoneNumber string `json:"phone_number"`
Name string `json:"name"`
BirthDate string `json:"birth_date"`
ExpiresAt time.Time `json:"expires_at"`
Step string `json:"step"` // "otp_sent", "otp_verified", "password_set"
}
@@ -0,0 +1,60 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Request Contracts
type GetCustomerGamesRequest struct {
// No additional fields needed - customer ID comes from JWT token
}
// Response Contracts
type GetCustomerGamesResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *GetCustomerGamesResponseData `json:"data,omitempty"`
}
type GetCustomerGamesResponseData struct {
Games []CustomerGameResponse `json:"games"`
}
type CustomerGameResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsActive bool `json:"is_active"`
Metadata *map[string]interface{} `json:"metadata,omitempty"`
Prizes []CustomerGamePrizeResponse `json:"prizes,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CustomerGamePrizeResponse struct {
ID uuid.UUID `json:"id"`
GameID uuid.UUID `json:"game_id"`
Name string `json:"name"`
Image *string `json:"image,omitempty"`
Metadata *map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Ferris Wheel Game Contracts
type GetFerrisWheelGameRequest struct {
// No additional fields needed - customer ID comes from JWT token
}
type GetFerrisWheelGameResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *GetFerrisWheelGameResponseData `json:"data,omitempty"`
}
type GetFerrisWheelGameResponseData struct {
Game CustomerGameResponse `json:"game"`
Prizes []CustomerGamePrizeResponse `json:"prizes"`
}
@@ -0,0 +1,118 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Existing gamification contracts
type CreateCustomerPointsRequest struct {
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
Balance int64 `json:"balance" validate:"min=0"`
}
type UpdateCustomerPointsRequest struct {
Balance int64 `json:"balance" validate:"min=0"`
}
type AddCustomerPointsRequest struct {
Points int64 `json:"points" validate:"required,min=1"`
}
type DeductCustomerPointsRequest struct {
Points int64 `json:"points" validate:"required,min=1"`
}
type CustomerPointsResponse struct {
ID uuid.UUID `json:"id"`
CustomerID uuid.UUID `json:"customer_id"`
Balance int64 `json:"balance"`
Customer *CustomerResponse `json:"customer,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListCustomerPointsRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search"`
SortBy string `json:"sort_by" validate:"omitempty,oneof=balance created_at updated_at"`
SortOrder string `json:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedCustomerPointsResponse struct {
Data []CustomerPointsResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
// New customer API contracts
type GetCustomerPointsRequest struct {
// No additional fields needed - customer ID comes from JWT token
}
type GetCustomerTokensRequest struct {
// No additional fields needed - customer ID comes from JWT token
}
type GetCustomerWalletRequest struct {
// No additional fields needed - customer ID comes from JWT token
}
// Response Contracts
type GetCustomerPointsResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *GetCustomerPointsResponseData `json:"data,omitempty"`
}
type GetCustomerPointsResponseData struct {
TotalPoints int64 `json:"total_points"`
PointsHistory []PointsHistoryItem `json:"points_history,omitempty"`
LastUpdated time.Time `json:"last_updated"`
}
type PointsHistoryItem struct {
ID string `json:"id"`
Points int64 `json:"points"`
Type string `json:"type"` // EARNED, REDEEMED, EXPIRED
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
}
type GetCustomerTokensResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *GetCustomerTokensResponseData `json:"data,omitempty"`
}
type GetCustomerTokensResponseData struct {
TotalTokens int64 `json:"total_tokens"`
TokensHistory []TokensHistoryItem `json:"tokens_history,omitempty"`
LastUpdated time.Time `json:"last_updated"`
}
type TokensHistoryItem struct {
ID string `json:"id"`
Tokens int64 `json:"tokens"`
Type string `json:"type"` // EARNED, REDEEMED, EXPIRED
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
}
type GetCustomerWalletResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *GetCustomerWalletResponseData `json:"data,omitempty"`
}
type GetCustomerWalletResponseData struct {
TotalPoints int64 `json:"total_points"`
TotalTokens int64 `json:"total_tokens"`
PointsHistory []PointsHistoryItem `json:"points_history,omitempty"`
TokensHistory []TokensHistoryItem `json:"tokens_history,omitempty"`
LastUpdated time.Time `json:"last_updated"`
}
@@ -0,0 +1,52 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateCustomerTokensRequest struct {
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
TokenType string `json:"token_type" validate:"required,oneof=SPIN RAFFLE MINIGAME"`
Balance int64 `json:"balance" validate:"min=0"`
}
type UpdateCustomerTokensRequest struct {
Balance int64 `json:"balance" validate:"min=0"`
}
type AddCustomerTokensRequest struct {
Tokens int64 `json:"tokens" validate:"required,min=1"`
}
type DeductCustomerTokensRequest struct {
Tokens int64 `json:"tokens" validate:"required,min=1"`
}
type CustomerTokensResponse struct {
ID uuid.UUID `json:"id"`
CustomerID uuid.UUID `json:"customer_id"`
TokenType string `json:"token_type"`
Balance int64 `json:"balance"`
Customer *CustomerResponse `json:"customer,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListCustomerTokensRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search"`
TokenType string `json:"token_type" validate:"omitempty,oneof=SPIN RAFFLE MINIGAME"`
SortBy string `json:"sort_by" validate:"omitempty,oneof=balance token_type created_at updated_at"`
SortOrder string `json:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedCustomerTokensResponse struct {
Data []CustomerTokensResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
+49
View File
@@ -0,0 +1,49 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateGameRequest struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required,oneof=SPIN RAFFLE MINIGAME"`
IsActive bool `json:"is_active"`
Metadata map[string]interface{} `json:"metadata"`
}
type UpdateGameRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,required"`
Type *string `json:"type,omitempty" validate:"omitempty,oneof=SPIN RAFFLE MINIGAME"`
IsActive *bool `json:"is_active,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
type GameResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
IsActive bool `json:"is_active"`
Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListGamesRequest struct {
Page int `json:"page" form:"page" validate:"min=1"`
Limit int `json:"limit" form:"limit" validate:"min=1,max=100"`
Search string `json:"search" form:"search"`
Type string `json:"type" form:"type" validate:"omitempty,oneof=SPIN RAFFLE MINIGAME"`
IsActive *bool `json:"is_active" form:"is_active"`
SortBy string `json:"sort_by" form:"sort_by" validate:"omitempty,oneof=name type created_at updated_at"`
SortOrder string `json:"sort_order" form:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedGamesResponse struct {
Data []GameResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
+58
View File
@@ -0,0 +1,58 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateGamePlayRequest struct {
GameID uuid.UUID `json:"game_id" validate:"required"`
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
TokenUsed int `json:"token_used" validate:"min=0"`
RandomSeed *string `json:"random_seed,omitempty"`
}
type GamePlayResponse struct {
ID uuid.UUID `json:"id"`
GameID uuid.UUID `json:"game_id"`
CustomerID uuid.UUID `json:"customer_id"`
PrizeID *uuid.UUID `json:"prize_id,omitempty"`
TokenUsed int `json:"token_used"`
RandomSeed *string `json:"random_seed,omitempty"`
CreatedAt time.Time `json:"created_at"`
Game *GameResponse `json:"game,omitempty"`
Customer *CustomerResponse `json:"customer,omitempty"`
Prize *GamePrizeResponse `json:"prize,omitempty"`
}
type ListGamePlaysRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search"`
GameID *uuid.UUID `json:"game_id"`
CustomerID *uuid.UUID `json:"customer_id"`
PrizeID *uuid.UUID `json:"prize_id"`
SortBy string `json:"sort_by" validate:"omitempty,oneof=created_at token_used"`
SortOrder string `json:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedGamePlaysResponse struct {
Data []GamePlayResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
type PlayGameRequest struct {
GameID uuid.UUID `json:"game_id" validate:"required"`
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
TokenUsed int `json:"token_used" validate:"min=0"`
}
type PlayGameResponse struct {
GamePlay GamePlayResponse `json:"game_play"`
PrizeWon *GamePrizeResponse `json:"prize_won,omitempty"`
TokensRemaining int64 `json:"tokens_remaining"`
}
+64
View File
@@ -0,0 +1,64 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateGamePrizeRequest struct {
GameID uuid.UUID `json:"game_id" validate:"required"`
Name string `json:"name" validate:"required"`
Weight int `json:"weight" validate:"min=1"`
Stock int `json:"stock" validate:"min=0"`
MaxStock *int `json:"max_stock,omitempty"`
Threshold *int64 `json:"threshold,omitempty"`
FallbackPrizeID *uuid.UUID `json:"fallback_prize_id,omitempty"`
Image *string `json:"image,omitempty" validate:"omitempty,max=500"`
Metadata map[string]interface{} `json:"metadata"`
}
type UpdateGamePrizeRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,required"`
Weight *int `json:"weight,omitempty" validate:"omitempty,min=1"`
Stock *int `json:"stock,omitempty" validate:"omitempty,min=0"`
MaxStock *int `json:"max_stock,omitempty"`
Threshold *int64 `json:"threshold,omitempty"`
FallbackPrizeID *uuid.UUID `json:"fallback_prize_id,omitempty"`
Image *string `json:"image,omitempty" validate:"omitempty,max=500"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
type GamePrizeResponse struct {
ID uuid.UUID `json:"id"`
GameID uuid.UUID `json:"game_id"`
Name string `json:"name"`
Weight int `json:"weight"`
Stock int `json:"stock"`
MaxStock *int `json:"max_stock,omitempty"`
Threshold *int64 `json:"threshold,omitempty"`
FallbackPrizeID *uuid.UUID `json:"fallback_prize_id,omitempty"`
Image *string `json:"image,omitempty"`
Metadata map[string]interface{} `json:"metadata"`
Game *GameResponse `json:"game,omitempty"`
FallbackPrize *GamePrizeResponse `json:"fallback_prize,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListGamePrizesRequest struct {
Page int `json:"page" form:"page" validate:"min=1"`
Limit int `json:"limit" form:"limit" validate:"min=1,max=100"`
Search string `json:"search" form:"search"`
GameID *uuid.UUID `json:"game_id" form:"game_id"`
SortBy string `json:"sort_by" form:"sort_by" validate:"omitempty,oneof=name weight stock created_at updated_at"`
SortOrder string `json:"sort_order" form:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedGamePrizesResponse struct {
Data []GamePrizeResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
@@ -1,16 +0,0 @@
package contract
import (
"apskel-pos-be/internal/models"
"github.com/google/uuid"
)
type IngredientCompositionContract interface {
Create(request *models.CreateIngredientCompositionRequest, organizationID uuid.UUID) (*models.IngredientCompositionResponse, error)
GetByID(id uuid.UUID, organizationID uuid.UUID) (*models.IngredientCompositionResponse, error)
GetByParentIngredientID(parentIngredientID uuid.UUID, organizationID uuid.UUID) ([]*models.IngredientCompositionResponse, error)
GetByChildIngredientID(childIngredientID uuid.UUID, organizationID uuid.UUID) ([]*models.IngredientCompositionResponse, error)
Update(id uuid.UUID, request *models.UpdateIngredientCompositionRequest, organizationID uuid.UUID) (*models.IngredientCompositionResponse, error)
Delete(id uuid.UUID, organizationID uuid.UUID) error
}
@@ -0,0 +1,84 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Request DTOs
type CreateIngredientUnitConverterRequest struct {
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
FromUnitID uuid.UUID `json:"from_unit_id" validate:"required"`
ToUnitID uuid.UUID `json:"to_unit_id" validate:"required"`
ConversionFactor float64 `json:"conversion_factor" validate:"required,gt=0"`
IsActive *bool `json:"is_active,omitempty" validate:"omitempty"`
}
type UpdateIngredientUnitConverterRequest struct {
FromUnitID *uuid.UUID `json:"from_unit_id,omitempty" validate:"omitempty"`
ToUnitID *uuid.UUID `json:"to_unit_id,omitempty" validate:"omitempty"`
ConversionFactor *float64 `json:"conversion_factor,omitempty" validate:"omitempty,gt=0"`
IsActive *bool `json:"is_active,omitempty" validate:"omitempty"`
}
type ListIngredientUnitConvertersRequest struct {
IngredientID *uuid.UUID `json:"ingredient_id,omitempty"`
FromUnitID *uuid.UUID `json:"from_unit_id,omitempty"`
ToUnitID *uuid.UUID `json:"to_unit_id,omitempty"`
IsActive *bool `json:"is_active,omitempty"`
Search string `json:"search,omitempty"`
Page int `json:"page" validate:"required,min=1"`
Limit int `json:"limit" validate:"required,min=1,max=100"`
}
type ConvertUnitRequest struct {
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
FromUnitID uuid.UUID `json:"from_unit_id" validate:"required"`
ToUnitID uuid.UUID `json:"to_unit_id" validate:"required"`
Quantity float64 `json:"quantity" validate:"required,gt=0"`
}
// Response DTOs
type IngredientUnitConverterResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
IngredientID uuid.UUID `json:"ingredient_id"`
FromUnitID uuid.UUID `json:"from_unit_id"`
ToUnitID uuid.UUID `json:"to_unit_id"`
ConversionFactor float64 `json:"conversion_factor"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy uuid.UUID `json:"created_by"`
UpdatedBy uuid.UUID `json:"updated_by"`
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
FromUnit *UnitResponse `json:"from_unit,omitempty"`
ToUnit *UnitResponse `json:"to_unit,omitempty"`
}
type ConvertUnitResponse struct {
FromQuantity float64 `json:"from_quantity"`
FromUnit *UnitResponse `json:"from_unit"`
ToQuantity float64 `json:"to_quantity"`
ToUnit *UnitResponse `json:"to_unit"`
ConversionFactor float64 `json:"conversion_factor"`
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
}
type ListIngredientUnitConvertersResponse struct {
Converters []IngredientUnitConverterResponse `json:"converters"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
type IngredientUnitsResponse struct {
IngredientID uuid.UUID `json:"ingredient_id"`
IngredientName string `json:"ingredient_name"`
BaseUnitID uuid.UUID `json:"base_unit_id"`
BaseUnitName string `json:"base_unit_name"`
Units []*UnitResponse `json:"units"`
}
+86 -1
View File
@@ -1,8 +1,9 @@
package contract package contract
import ( import (
"github.com/google/uuid"
"time" "time"
"github.com/google/uuid"
) )
type CreateInventoryRequest struct { type CreateInventoryRequest struct {
@@ -24,6 +25,18 @@ type AdjustInventoryRequest struct {
Reason string `json:"reason" validate:"required,min=1,max=255"` Reason string `json:"reason" validate:"required,min=1,max=255"`
} }
type RestockInventoryRequest struct {
OutletID uuid.UUID `json:"outlet_id" validate:"required"`
Items []RestockItem `json:"items" validate:"required,min=1,dive"`
Reason string `json:"reason" validate:"required,min=1,max=255"`
}
type RestockItem struct {
ItemID uuid.UUID `json:"item_id" validate:"required"`
ItemType string `json:"item_type" validate:"required,oneof=PRODUCT INGREDIENT"`
Quantity int `json:"quantity" validate:"required,min=1"`
}
type ListInventoryRequest struct { type ListInventoryRequest struct {
OutletID *uuid.UUID `json:"outlet_id,omitempty"` OutletID *uuid.UUID `json:"outlet_id,omitempty"`
ProductID *uuid.UUID `json:"product_id,omitempty"` ProductID *uuid.UUID `json:"product_id,omitempty"`
@@ -67,3 +80,75 @@ type InventoryAdjustmentResponse struct {
Reason string `json:"reason"` Reason string `json:"reason"`
AdjustedAt time.Time `json:"adjusted_at"` AdjustedAt time.Time `json:"adjusted_at"`
} }
type RestockInventoryResponse struct {
OutletID uuid.UUID `json:"outlet_id"`
Items []RestockItemResult `json:"items"`
Reason string `json:"reason"`
RestockedAt time.Time `json:"restocked_at"`
}
type RestockItemResult struct {
ItemID uuid.UUID `json:"item_id"`
ItemType string `json:"item_type"`
ItemName string `json:"item_name"`
PreviousQty int `json:"previous_quantity"`
NewQty int `json:"new_quantity"`
AddedQty int `json:"added_quantity"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
// Inventory Report Contracts
type InventoryReportSummaryResponse struct {
TotalProducts int `json:"total_products"`
TotalIngredients int `json:"total_ingredients"`
TotalValue float64 `json:"total_value"`
LowStockProducts int `json:"low_stock_products"`
LowStockIngredients int `json:"low_stock_ingredients"`
ZeroStockProducts int `json:"zero_stock_products"`
ZeroStockIngredients int `json:"zero_stock_ingredients"`
TotalSoldProducts float64 `json:"total_sold_products"`
TotalSoldIngredients float64 `json:"total_sold_ingredients"`
OutletID string `json:"outlet_id"`
OutletName string `json:"outlet_name"`
GeneratedAt string `json:"generated_at"`
}
type InventoryReportDetailResponse struct {
Summary *InventoryReportSummaryResponse `json:"summary"`
Products []*InventoryProductDetailResponse `json:"products"`
Ingredients []*InventoryIngredientDetailResponse `json:"ingredients"`
}
type InventoryProductDetailResponse struct {
ID string `json:"id"`
ProductID string `json:"product_id"`
ProductName string `json:"product_name"`
CategoryName string `json:"category_name"`
Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt string `json:"updated_at"`
}
type InventoryIngredientDetailResponse struct {
ID string `json:"id"`
IngredientID string `json:"ingredient_id"`
IngredientName string `json:"ingredient_name"`
UnitName string `json:"unit_name"`
Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt string `json:"updated_at"`
}
@@ -0,0 +1,59 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateOmsetTrackerRequest struct {
PeriodType string `json:"period_type" validate:"required,oneof=DAILY WEEKLY MONTHLY TOTAL"`
PeriodStart time.Time `json:"period_start" validate:"required"`
PeriodEnd time.Time `json:"period_end" validate:"required"`
Total int64 `json:"total" validate:"min=0"`
GameID *uuid.UUID `json:"game_id,omitempty"`
}
type UpdateOmsetTrackerRequest struct {
PeriodType *string `json:"period_type,omitempty" validate:"omitempty,oneof=DAILY WEEKLY MONTHLY TOTAL"`
PeriodStart *time.Time `json:"period_start,omitempty"`
PeriodEnd *time.Time `json:"period_end,omitempty"`
Total *int64 `json:"total,omitempty" validate:"omitempty,min=0"`
GameID *uuid.UUID `json:"game_id,omitempty"`
}
type AddOmsetRequest struct {
Amount int64 `json:"amount" validate:"required,min=1"`
}
type OmsetTrackerResponse struct {
ID uuid.UUID `json:"id"`
PeriodType string `json:"period_type"`
PeriodStart time.Time `json:"period_start"`
PeriodEnd time.Time `json:"period_end"`
Total int64 `json:"total"`
GameID *uuid.UUID `json:"game_id,omitempty"`
Game *GameResponse `json:"game,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListOmsetTrackerRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search"`
PeriodType string `json:"period_type" validate:"omitempty,oneof=DAILY WEEKLY MONTHLY TOTAL"`
GameID *uuid.UUID `json:"game_id"`
From *time.Time `json:"from"`
To *time.Time `json:"to"`
SortBy string `json:"sort_by" validate:"omitempty,oneof=period_type period_start total created_at updated_at"`
SortOrder string `json:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedOmsetTrackerResponse struct {
Data []OmsetTrackerResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
+1 -1
View File
@@ -185,7 +185,7 @@ type CreatePaymentRequest struct {
type CreatePaymentOrderItemRequest struct { type CreatePaymentOrderItemRequest struct {
OrderItemID uuid.UUID `json:"order_item_id" validate:"required"` OrderItemID uuid.UUID `json:"order_item_id" validate:"required"`
Amount float64 `json:"amount" validate:"required,min=0"` Amount float64 `json:"amount" validate:"min=0"`
} }
type PaymentResponse struct { type PaymentResponse struct {
@@ -0,0 +1,20 @@
package contract
import (
"context"
"github.com/google/uuid"
)
type OrderIngredientTransactionContract interface {
CreateOrderIngredientTransaction(ctx context.Context, req *CreateOrderIngredientTransactionRequest) (*OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionByID(ctx context.Context, id uuid.UUID) (*OrderIngredientTransactionResponse, error)
UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *UpdateOrderIngredientTransactionRequest) (*OrderIngredientTransactionResponse, error)
DeleteOrderIngredientTransaction(ctx context.Context, id uuid.UUID) error
ListOrderIngredientTransactions(ctx context.Context, req *ListOrderIngredientTransactionsRequest) ([]*OrderIngredientTransactionResponse, int64, error)
GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID uuid.UUID) ([]*OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID uuid.UUID) ([]*OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID uuid.UUID) ([]*OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionSummary(ctx context.Context, req *ListOrderIngredientTransactionsRequest) ([]*OrderIngredientTransactionSummary, error)
BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*CreateOrderIngredientTransactionRequest) ([]*OrderIngredientTransactionResponse, error)
}
@@ -0,0 +1,79 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateOrderIngredientTransactionRequest struct {
OrderID uuid.UUID `json:"order_id" validate:"required"`
OrderItemID *uuid.UUID `json:"order_item_id,omitempty"`
ProductID uuid.UUID `json:"product_id" validate:"required"`
ProductVariantID *uuid.UUID `json:"product_variant_id,omitempty"`
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
GrossQty float64 `json:"gross_qty" validate:"required,gt=0"`
NetQty float64 `json:"net_qty" validate:"required,gt=0"`
WasteQty float64 `json:"waste_qty" validate:"min=0"`
Unit string `json:"unit" validate:"required,max=50"`
TransactionDate *time.Time `json:"transaction_date,omitempty"`
}
type UpdateOrderIngredientTransactionRequest struct {
GrossQty *float64 `json:"gross_qty,omitempty" validate:"omitempty,gt=0"`
NetQty *float64 `json:"net_qty,omitempty" validate:"omitempty,gt=0"`
WasteQty *float64 `json:"waste_qty,omitempty" validate:"min=0"`
Unit *string `json:"unit,omitempty" validate:"omitempty,max=50"`
TransactionDate *time.Time `json:"transaction_date,omitempty"`
}
type OrderIngredientTransactionResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id"`
OrderID uuid.UUID `json:"order_id"`
OrderItemID *uuid.UUID `json:"order_item_id"`
ProductID uuid.UUID `json:"product_id"`
ProductVariantID *uuid.UUID `json:"product_variant_id"`
IngredientID uuid.UUID `json:"ingredient_id"`
GrossQty float64 `json:"gross_qty"`
NetQty float64 `json:"net_qty"`
WasteQty float64 `json:"waste_qty"`
Unit string `json:"unit"`
TransactionDate time.Time `json:"transaction_date"`
CreatedBy uuid.UUID `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Relations - these would be populated by the service layer
Organization interface{} `json:"organization,omitempty"`
Outlet interface{} `json:"outlet,omitempty"`
Order interface{} `json:"order,omitempty"`
OrderItem interface{} `json:"order_item,omitempty"`
Product interface{} `json:"product,omitempty"`
ProductVariant interface{} `json:"product_variant,omitempty"`
Ingredient interface{} `json:"ingredient,omitempty"`
CreatedByUser interface{} `json:"created_by_user,omitempty"`
}
type ListOrderIngredientTransactionsRequest struct {
OrderID *uuid.UUID `json:"order_id,omitempty"`
OrderItemID *uuid.UUID `json:"order_item_id,omitempty"`
ProductID *uuid.UUID `json:"product_id,omitempty"`
ProductVariantID *uuid.UUID `json:"product_variant_id,omitempty"`
IngredientID *uuid.UUID `json:"ingredient_id,omitempty"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
}
type OrderIngredientTransactionSummary struct {
IngredientID uuid.UUID `json:"ingredient_id"`
IngredientName string `json:"ingredient_name"`
TotalGrossQty float64 `json:"total_gross_qty"`
TotalNetQty float64 `json:"total_net_qty"`
TotalWasteQty float64 `json:"total_waste_qty"`
WastePercentage float64 `json:"waste_percentage"`
Unit string `json:"unit"`
}
+1
View File
@@ -59,6 +59,7 @@ type ProductResponse struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"` OrganizationID uuid.UUID `json:"organization_id"`
CategoryID uuid.UUID `json:"category_id"` CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
SKU *string `json:"sku"` SKU *string `json:"sku"`
Name string `json:"name"` Name string `json:"name"`
Description *string `json:"description"` Description *string `json:"description"`
+66 -10
View File
@@ -1,18 +1,74 @@
package contract package contract
import ( import (
"apskel-pos-be/internal/models" "time"
"github.com/google/uuid" "github.com/google/uuid"
) )
type ProductRecipeContract interface { // Request structures
Create(request *models.CreateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) type CreateProductRecipeRequest struct {
GetByID(id uuid.UUID, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) OutletID *uuid.UUID `json:"outlet_id"`
GetByProductID(productID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) ProductID uuid.UUID `json:"product_id" validate:"required"`
GetByProductAndVariantID(productID uuid.UUID, variantID *uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) VariantID *uuid.UUID `json:"variant_id"`
GetByIngredientID(ingredientID uuid.UUID, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
Update(id uuid.UUID, request *models.UpdateProductRecipeRequest, organizationID uuid.UUID) (*models.ProductRecipeResponse, error) Quantity float64 `json:"quantity" validate:"required,gt=0"`
Delete(id uuid.UUID, organizationID uuid.UUID) error WastePercentage float64 `json:"waste_percentage" validate:"min=0,max=100"`
BulkCreate(recipes []models.CreateProductRecipeRequest, organizationID uuid.UUID) ([]*models.ProductRecipeResponse, error) }
type UpdateProductRecipeRequest struct {
OutletID *uuid.UUID `json:"outlet_id"`
VariantID *uuid.UUID `json:"variant_id"`
Quantity float64 `json:"quantity" validate:"required,gt=0"`
WastePercentage float64 `json:"waste_percentage" validate:"min=0,max=100"`
}
type GetProductRecipeByProductIDRequest struct {
ProductID uuid.UUID `json:"-"`
VariantID *uuid.UUID `json:"-"`
}
type BulkCreateProductRecipeRequest struct {
Recipes []CreateProductRecipeRequest `json:"recipes" validate:"required,min=1"`
}
// Response structures
type ProductRecipeResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id"`
ProductID uuid.UUID `json:"product_id"`
VariantID *uuid.UUID `json:"variant_id"`
IngredientID uuid.UUID `json:"ingredient_id"`
Quantity float64 `json:"quantity"`
WastePercentage float64 `json:"waste_percentage"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Product *ProductResponse `json:"product,omitempty"`
ProductVariant *ProductVariantResponse `json:"product_variant,omitempty"`
Ingredient *ProductRecipeIngredientResponse `json:"ingredient,omitempty"`
}
type ProductRecipeIngredientResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id"`
Name string `json:"name"`
UnitID uuid.UUID `json:"unit_id"`
Cost float64 `json:"cost"`
Stock float64 `json:"stock"`
IsSemiFinished bool `json:"is_semi_finished"`
IsActive bool `json:"is_active"`
Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Unit *ProductRecipeUnitResponse `json:"unit,omitempty"`
}
type ProductRecipeUnitResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} }
@@ -0,0 +1,117 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreatePurchaseOrderRequest struct {
VendorID uuid.UUID `json:"vendor_id" validate:"required"`
PONumber string `json:"po_number" validate:"required,min=1,max=50"`
TransactionDate string `json:"transaction_date" validate:"required"` // Format: YYYY-MM-DD
DueDate string `json:"due_date" validate:"required"` // Format: YYYY-MM-DD
Reference *string `json:"reference,omitempty" validate:"omitempty,max=100"`
Status *string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"`
Message *string `json:"message,omitempty" validate:"omitempty"`
Items []CreatePurchaseOrderItemRequest `json:"items" validate:"required,min=1,dive"`
AttachmentFileIDs []uuid.UUID `json:"attachment_file_ids,omitempty"`
}
type CreatePurchaseOrderItemRequest struct {
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
Description *string `json:"description,omitempty" validate:"omitempty"`
Quantity float64 `json:"quantity" validate:"required,gt=0"`
UnitID uuid.UUID `json:"unit_id" validate:"required"`
Amount float64 `json:"amount" validate:"required,gte=0"`
}
type UpdatePurchaseOrderRequest struct {
VendorID *uuid.UUID `json:"vendor_id,omitempty" validate:"omitempty"`
PONumber *string `json:"po_number,omitempty" validate:"omitempty,min=1,max=50"`
TransactionDate *string `json:"transaction_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD
DueDate *string `json:"due_date,omitempty" validate:"omitempty"` // Format: YYYY-MM-DD
Reference *string `json:"reference,omitempty" validate:"omitempty,max=100"`
Status *string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"`
Message *string `json:"message,omitempty" validate:"omitempty"`
Items []UpdatePurchaseOrderItemRequest `json:"items,omitempty" validate:"omitempty,dive"`
AttachmentFileIDs []uuid.UUID `json:"attachment_file_ids,omitempty"`
}
type UpdatePurchaseOrderItemRequest struct {
ID *uuid.UUID `json:"id,omitempty"` // For existing items
IngredientID *uuid.UUID `json:"ingredient_id,omitempty" validate:"omitempty"`
Description *string `json:"description,omitempty" validate:"omitempty"`
Quantity *float64 `json:"quantity,omitempty" validate:"omitempty,gt=0"`
UnitID *uuid.UUID `json:"unit_id,omitempty" validate:"omitempty"`
Amount *float64 `json:"amount,omitempty" validate:"omitempty,gte=0"`
}
type PurchaseOrderResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
VendorID uuid.UUID `json:"vendor_id"`
PONumber string `json:"po_number"`
TransactionDate time.Time `json:"transaction_date"`
DueDate time.Time `json:"due_date"`
Reference *string `json:"reference"`
Status string `json:"status"`
Message *string `json:"message"`
TotalAmount float64 `json:"total_amount"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Vendor *VendorResponse `json:"vendor,omitempty"`
Items []PurchaseOrderItemResponse `json:"items,omitempty"`
Attachments []PurchaseOrderAttachmentResponse `json:"attachments,omitempty"`
}
type PurchaseOrderItemResponse struct {
ID uuid.UUID `json:"id"`
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
IngredientID uuid.UUID `json:"ingredient_id"`
Description *string `json:"description"`
Quantity float64 `json:"quantity"`
UnitID uuid.UUID `json:"unit_id"`
Amount float64 `json:"amount"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Ingredient *IngredientResponse `json:"ingredient,omitempty"`
Unit *UnitResponse `json:"unit,omitempty"`
}
type PurchaseOrderAttachmentResponse struct {
ID uuid.UUID `json:"id"`
PurchaseOrderID uuid.UUID `json:"purchase_order_id"`
FileID uuid.UUID `json:"file_id"`
CreatedAt time.Time `json:"created_at"`
File *FileResponse `json:"file,omitempty"`
}
type ListPurchaseOrdersRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search,omitempty"`
Status string `json:"status,omitempty" validate:"omitempty,oneof=draft sent approved received cancelled"`
VendorID *uuid.UUID `json:"vendor_id,omitempty"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
}
type ListPurchaseOrdersResponse struct {
PurchaseOrders []PurchaseOrderResponse `json:"purchase_orders"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
// Helper types for ingredient and unit responses
type IngredientResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
}
type UnitResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
}
+82
View File
@@ -0,0 +1,82 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Request Contracts
type CreateRewardRequest struct {
Name string `json:"name" binding:"required,min=1,max=150"`
RewardType string `json:"reward_type" binding:"required,oneof=VOUCHER PHYSICAL DIGITAL"`
CostPoints int64 `json:"cost_points" binding:"required,min=1"`
Stock *int `json:"stock,omitempty"`
MaxPerCustomer int `json:"max_per_customer" binding:"min=1"`
Tnc *TermsAndConditionsStruct `json:"tnc,omitempty"`
Metadata *map[string]interface{} `json:"metadata,omitempty"`
Images *[]string `json:"images,omitempty"`
}
type UpdateRewardRequest struct {
ID uuid.UUID `json:"id" binding:"required"`
Name string `json:"name" binding:"required,min=1,max=150"`
RewardType string `json:"reward_type" binding:"required,oneof=VOUCHER PHYSICAL DIGITAL BALANCE"`
CostPoints int64 `json:"cost_points" binding:"required,min=1"`
Stock *int `json:"stock,omitempty"`
MaxPerCustomer int `json:"max_per_customer" binding:"min=1"`
Tnc *TermsAndConditionsStruct `json:"tnc,omitempty"`
Metadata *map[string]interface{} `json:"metadata,omitempty"`
Images *[]string `json:"images,omitempty"`
}
type ListRewardsRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
Search string `form:"search"`
RewardType string `form:"reward_type"`
MinPoints *int64 `form:"min_points"`
MaxPoints *int64 `form:"max_points"`
HasStock *bool `form:"has_stock"`
}
type GetRewardRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
type DeleteRewardRequest struct {
ID uuid.UUID `uri:"id" binding:"required"`
}
// Response Contracts
type RewardResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
RewardType string `json:"reward_type"`
CostPoints int64 `json:"cost_points"`
Stock *int `json:"stock,omitempty"`
MaxPerCustomer int `json:"max_per_customer"`
Tnc *TermsAndConditionsStruct `json:"tnc,omitempty"`
Metadata *map[string]interface{} `json:"metadata,omitempty"`
Images *[]string `json:"images,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListRewardsResponse struct {
Rewards []RewardResponse `json:"rewards"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}
// Helper structs
type TermsAndConditionsStruct struct {
Sections []TncSectionStruct `json:"sections"`
ExpiryDays int `json:"expiry_days"`
}
type TncSectionStruct struct {
Title string `json:"title"`
Rules []string `json:"rules"`
}
+20
View File
@@ -0,0 +1,20 @@
package contract
// SpinGameRequest represents the request to play a spin game
type SpinGameRequest struct {
SpinID string `json:"spin_id" validate:"required,uuid"`
}
// SpinGameResponse represents the response from playing a spin game
type SpinGameResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Data *SpinGameResponseData `json:"data,omitempty"`
}
// SpinGameResponseData contains the game play result
type SpinGameResponseData struct {
GamePlay GamePlayResponse `json:"game_play"`
PrizeWon *CustomerGamePrizeResponse `json:"prize_won,omitempty"`
TokensRemaining int64 `json:"tokens_remaining"`
}
+44
View File
@@ -0,0 +1,44 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateTierRequest struct {
Name string `json:"name" validate:"required"`
MinPoints int64 `json:"min_points" validate:"min=0"`
Benefits map[string]interface{} `json:"benefits"`
}
type UpdateTierRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,required"`
MinPoints *int64 `json:"min_points,omitempty" validate:"omitempty,min=0"`
Benefits map[string]interface{} `json:"benefits,omitempty"`
}
type TierResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
MinPoints int64 `json:"min_points"`
Benefits map[string]interface{} `json:"benefits"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListTiersRequest struct {
Page int `form:"page" validate:"min=1"`
Limit int `form:"limit" validate:"min=1,max=100"`
Search string `form:"search"`
SortBy string `form:"sort_by" validate:"omitempty,oneof=name min_points created_at updated_at"`
SortOrder string `form:"sort_order" validate:"omitempty,oneof=asc desc"`
}
type PaginatedTiersResponse struct {
Data []TierResponse `json:"data"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
+5 -3
View File
@@ -40,9 +40,11 @@ type LoginRequest struct {
} }
type LoginResponse struct { type LoginResponse struct {
Token string `json:"token"` Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"` RefreshToken string `json:"refresh_token"`
User UserResponse `json:"user"` ExpiresAt time.Time `json:"expires_at"`
RefreshExpiresAt time.Time `json:"refresh_expires_at"`
User UserResponse `json:"user"`
} }
type UserResponse struct { type UserResponse struct {
+62
View File
@@ -0,0 +1,62 @@
package contract
import (
"time"
"github.com/google/uuid"
)
type CreateVendorRequest struct {
Name string `json:"name" validate:"required,min=1,max=255"`
Email *string `json:"email,omitempty" validate:"omitempty,email"`
PhoneNumber *string `json:"phone_number,omitempty" validate:"omitempty"`
Address *string `json:"address,omitempty" validate:"omitempty"`
ContactPerson *string `json:"contact_person,omitempty" validate:"omitempty,max=255"`
TaxNumber *string `json:"tax_number,omitempty" validate:"omitempty,max=50"`
PaymentTerms *string `json:"payment_terms,omitempty" validate:"omitempty,max=100"`
Notes *string `json:"notes,omitempty" validate:"omitempty"`
IsActive *bool `json:"is_active,omitempty"`
}
type UpdateVendorRequest struct {
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=255"`
Email *string `json:"email,omitempty" validate:"omitempty,email"`
PhoneNumber *string `json:"phone_number,omitempty" validate:"omitempty"`
Address *string `json:"address,omitempty" validate:"omitempty"`
ContactPerson *string `json:"contact_person,omitempty" validate:"omitempty,max=255"`
TaxNumber *string `json:"tax_number,omitempty" validate:"omitempty,max=50"`
PaymentTerms *string `json:"payment_terms,omitempty" validate:"omitempty,max=100"`
Notes *string `json:"notes,omitempty" validate:"omitempty"`
IsActive *bool `json:"is_active,omitempty"`
}
type VendorResponse struct {
ID uuid.UUID `json:"id"`
OrganizationID uuid.UUID `json:"organization_id"`
Name string `json:"name"`
Email *string `json:"email"`
PhoneNumber *string `json:"phone_number"`
Address *string `json:"address"`
ContactPerson *string `json:"contact_person"`
TaxNumber *string `json:"tax_number"`
PaymentTerms *string `json:"payment_terms"`
Notes *string `json:"notes"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ListVendorsRequest struct {
Page int `json:"page" validate:"min=1"`
Limit int `json:"limit" validate:"min=1,max=100"`
Search string `json:"search,omitempty"`
IsActive *bool `json:"is_active,omitempty"`
}
type ListVendorsResponse struct {
Vendors []VendorResponse `json:"vendors"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
+55
View File
@@ -0,0 +1,55 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type AccountType string
const (
AccountTypeCash AccountType = "cash"
AccountTypeWallet AccountType = "wallet"
AccountTypeBank AccountType = "bank"
AccountTypeCredit AccountType = "credit"
AccountTypeDebit AccountType = "debit"
AccountTypeAsset AccountType = "asset"
AccountTypeLiability AccountType = "liability"
AccountTypeEquity AccountType = "equity"
AccountTypeRevenue AccountType = "revenue"
AccountTypeExpense AccountType = "expense"
)
type Account struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ChartOfAccountID uuid.UUID `gorm:"type:uuid;not null;index" json:"chart_of_account_id" validate:"required"`
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
Number string `gorm:"not null;size:50" json:"number" validate:"required,min=1,max=50"`
AccountType AccountType `gorm:"not null;size:20" json:"account_type" validate:"required,oneof=cash wallet bank credit debit asset liability equity revenue expense"`
OpeningBalance float64 `gorm:"type:decimal(15,2);default:0.00" json:"opening_balance"`
CurrentBalance float64 `gorm:"type:decimal(15,2);default:0.00" json:"current_balance"`
Description *string `gorm:"type:text" json:"description"`
IsActive bool `gorm:"default:true" json:"is_active"`
IsSystem bool `gorm:"default:false" json:"is_system"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Outlet *Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
ChartOfAccount ChartOfAccount `gorm:"foreignKey:ChartOfAccountID" json:"chart_of_account,omitempty"`
}
func (a *Account) BeforeCreate(tx *gorm.DB) error {
if a.ID == uuid.Nil {
a.ID = uuid.New()
}
return nil
}
func (Account) TableName() string {
return "accounts"
}
+28 -9
View File
@@ -27,16 +27,35 @@ type SalesAnalytics struct {
NetSales float64 `json:"net_sales"` NetSales float64 `json:"net_sales"`
} }
// ProductAnalytics represents product analytics data
type ProductAnalytics struct { type ProductAnalytics struct {
ProductID uuid.UUID `json:"product_id"` ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"` ProductSku string `json:"product_sku"`
CategoryName string `json:"category_name"` CategoryID uuid.UUID `json:"category_id"`
QuantitySold int64 `json:"quantity_sold"` CategoryName string `json:"category_name"`
Revenue float64 `json:"revenue"` CategoryOrder int `json:"category_order"`
AveragePrice float64 `json:"average_price"` QuantitySold int64 `json:"quantity_sold"`
OrderCount int64 `json:"order_count"` Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`
OrderCount int64 `json:"order_count"`
StandardHppPerUnit float64 `json:"standard_hpp_per_unit"`
StandardHppTotal float64 `json:"standard_hpp_total"`
FifoHppPerUnit float64 `json:"fifo_hpp_per_unit"`
FifoHppTotal float64 `json:"fifo_hpp_total"`
MovingAverageHppPerUnit float64 `json:"moving_average_hpp_per_unit"`
MovingAverageHppTotal float64 `json:"moving_average_hpp_total"`
}
type ProductAnalyticsPerCategory struct {
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
TotalRevenue float64 `json:"total_revenue"`
TotalQuantity int64 `json:"total_quantity"`
ProductCount int64 `json:"product_count"`
OrderCount int64 `json:"order_count"`
TotalStandardHpp float64 `json:"total_standard_hpp"`
TotalFifoHpp float64 `json:"total_fifo_hpp"`
TotalMovingAverageHpp float64 `json:"total_moving_average_hpp"`
} }
// DashboardOverview represents dashboard overview data // DashboardOverview represents dashboard overview data
+131
View File
@@ -0,0 +1,131 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type CampaignType string
const (
CampaignTypeReward CampaignType = "REWARD"
CampaignTypePoints CampaignType = "POINTS"
CampaignTypeTokens CampaignType = "TOKENS"
CampaignTypeMixed CampaignType = "MIXED"
)
type RuleType string
const (
RuleTypeTier RuleType = "TIER"
RuleTypeSpend RuleType = "SPEND"
RuleTypeProduct RuleType = "PRODUCT"
RuleTypeCategory RuleType = "CATEGORY"
RuleTypeDay RuleType = "DAY"
RuleTypeLocation RuleType = "LOCATION"
)
type CampaignRewardType string
const (
CampaignRewardTypePoints CampaignRewardType = "POINTS"
CampaignRewardTypeTokens CampaignRewardType = "TOKENS"
CampaignRewardTypeReward CampaignRewardType = "REWARD"
)
type RewardSubtype string
const (
RewardSubtypeMultiplier RewardSubtype = "MULTIPLIER"
RewardSubtypeSpin RewardSubtype = "SPIN"
RewardSubtypeBonus RewardSubtype = "BONUS"
RewardSubtypePhysical RewardSubtype = "PHYSICAL"
)
type Campaign struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Name string `gorm:"type:varchar(150);not null" json:"name"`
Description *string `gorm:"type:text" json:"description,omitempty"`
Type CampaignType `gorm:"type:varchar(50);not null" json:"type"`
StartDate time.Time `gorm:"type:timestamp;not null" json:"start_date"`
EndDate time.Time `gorm:"type:timestamp;not null" json:"end_date"`
IsActive bool `gorm:"type:boolean;default:true" json:"is_active"`
ShowOnApp bool `gorm:"type:boolean;default:true" json:"show_on_app"`
Position int `gorm:"type:int;default:0" json:"position"`
Metadata *Metadata `gorm:"type:jsonb" json:"metadata,omitempty"`
CreatedAt time.Time `gorm:"type:timestamp;default:now()" json:"created_at"`
UpdatedAt time.Time `gorm:"type:timestamp;default:now()" json:"updated_at"`
// Relations
Rules []CampaignRule `gorm:"foreignKey:CampaignID;constraint:OnDelete:CASCADE" json:"rules,omitempty"`
}
func (Campaign) TableName() string {
return "campaigns"
}
func (c *Campaign) BeforeCreate(tx *gorm.DB) error {
if c.ID == uuid.Nil {
c.ID = uuid.New()
}
return nil
}
func (c *Campaign) BeforeUpdate(tx *gorm.DB) error {
c.UpdatedAt = time.Now()
return nil
}
type CampaignRule struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
CampaignID uuid.UUID `gorm:"type:uuid;not null" json:"campaign_id"`
RuleType RuleType `gorm:"type:varchar(50);not null" json:"rule_type"`
ConditionValue *string `gorm:"type:varchar(255)" json:"condition_value,omitempty"`
RewardType CampaignRewardType `gorm:"type:varchar(50);not null" json:"reward_type"`
RewardValue *int64 `gorm:"type:bigint" json:"reward_value,omitempty"`
RewardSubtype *RewardSubtype `gorm:"type:varchar(50)" json:"reward_subtype,omitempty"`
RewardRefID *uuid.UUID `gorm:"type:uuid" json:"reward_ref_id,omitempty"`
Metadata *Metadata `gorm:"type:jsonb" json:"metadata,omitempty"`
CreatedAt time.Time `gorm:"type:timestamp;default:now()" json:"created_at"`
UpdatedAt time.Time `gorm:"type:timestamp;default:now()" json:"updated_at"`
// Relations
Campaign Campaign `gorm:"foreignKey:CampaignID" json:"campaign,omitempty"`
}
func (CampaignRule) TableName() string {
return "campaign_rules"
}
func (cr *CampaignRule) BeforeCreate(tx *gorm.DB) error {
if cr.ID == uuid.Nil {
cr.ID = uuid.New()
}
return nil
}
func (cr *CampaignRule) BeforeUpdate(tx *gorm.DB) error {
cr.UpdatedAt = time.Now()
return nil
}
type ListCampaignsRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
Search string `form:"search"`
Type string `form:"type"`
IsActive *bool `form:"is_active"`
ShowOnApp *bool `form:"show_on_app"`
StartDate *time.Time `form:"start_date"`
EndDate *time.Time `form:"end_date"`
}
type ListCampaignRulesRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
CampaignID string `form:"campaign_id"`
RuleType string `form:"rule_type"`
RewardType string `form:"reward_type"`
}
+1
View File
@@ -35,6 +35,7 @@ type Category struct {
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"` Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
Description *string `gorm:"type:text" json:"description"` Description *string `gorm:"type:text" json:"description"`
Order int `gorm:"default:0" json:"order"`
BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"` BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"` Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
+41
View File
@@ -0,0 +1,41 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type ChartOfAccount struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
OutletID uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ChartOfAccountTypeID uuid.UUID `gorm:"type:uuid;not null;index" json:"chart_of_account_type_id" validate:"required"`
ParentID *uuid.UUID `gorm:"type:uuid;index" json:"parent_id"`
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
Code string `gorm:"not null;size:20" json:"code" validate:"required,min=1,max=20"`
Description *string `gorm:"type:text" json:"description"`
IsActive bool `gorm:"default:true" json:"is_active"`
IsSystem bool `gorm:"default:false" json:"is_system"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Outlet *Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
ChartOfAccountType ChartOfAccountType `gorm:"foreignKey:ChartOfAccountTypeID" json:"chart_of_account_type,omitempty"`
Parent *ChartOfAccount `gorm:"foreignKey:ParentID" json:"parent,omitempty"`
Children []ChartOfAccount `gorm:"foreignKey:ParentID" json:"children,omitempty"`
Accounts []Account `gorm:"foreignKey:ChartOfAccountID" json:"accounts,omitempty"`
}
func (c *ChartOfAccount) BeforeCreate(tx *gorm.DB) error {
if c.ID == uuid.Nil {
c.ID = uuid.New()
}
return nil
}
func (ChartOfAccount) TableName() string {
return "chart_of_accounts"
}
@@ -0,0 +1,31 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type ChartOfAccountType struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Name string `gorm:"not null;size:100" json:"name" validate:"required,min=1,max=100"`
Code string `gorm:"not null;size:10;unique" json:"code" validate:"required,min=1,max=10"`
Description *string `gorm:"type:text" json:"description"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
ChartOfAccounts []ChartOfAccount `gorm:"foreignKey:ChartOfAccountTypeID" json:"chart_of_accounts,omitempty"`
}
func (c *ChartOfAccountType) BeforeCreate(tx *gorm.DB) error {
if c.ID == uuid.Nil {
c.ID = uuid.New()
}
return nil
}
func (ChartOfAccountType) TableName() string {
return "chart_of_account_types"
}
+14 -11
View File
@@ -8,17 +8,20 @@ import (
) )
type Customer struct { type Customer struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
Name string `gorm:"not null;size:255" json:"name" validate:"required"` Name string `gorm:"not null;size:255" json:"name" validate:"required"`
Email *string `gorm:"size:255;uniqueIndex" json:"email,omitempty"` Email *string `gorm:"size:255;uniqueIndex" json:"email,omitempty"`
Phone *string `gorm:"size:20" json:"phone,omitempty"` Phone *string `gorm:"size:20" json:"phone,omitempty"`
Address *string `gorm:"size:500" json:"address,omitempty"` PhoneNumber *string `gorm:"size:20;uniqueIndex" json:"phone_number,omitempty"`
IsDefault bool `gorm:"default:false" json:"is_default"` Address *string `gorm:"size:500" json:"address,omitempty"`
IsActive bool `gorm:"default:true" json:"is_active"` BirthDate *time.Time `gorm:"type:date" json:"birth_date,omitempty"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"` PasswordHash *string `gorm:"size:255" json:"-"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` IsDefault bool `gorm:"default:false" json:"is_default"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` IsActive bool `gorm:"default:true" json:"is_active"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"` Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Orders []Order `gorm:"foreignKey:CustomerID" json:"orders,omitempty"` Orders []Order `gorm:"foreignKey:CustomerID" json:"orders,omitempty"`
+29
View File
@@ -0,0 +1,29 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type CustomerPoints struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
CustomerID uuid.UUID `gorm:"type:uuid;not null;index" json:"customer_id" validate:"required"`
Balance int64 `gorm:"not null;default:0" json:"balance" validate:"min=0"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Customer Customer `gorm:"foreignKey:CustomerID" json:"customer,omitempty"`
}
func (cp *CustomerPoints) BeforeCreate(tx *gorm.DB) error {
if cp.ID == uuid.Nil {
cp.ID = uuid.New()
}
return nil
}
func (CustomerPoints) TableName() string {
return "customer_points"
}
+38
View File
@@ -0,0 +1,38 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type TokenType string
const (
TokenTypeSpin TokenType = "SPIN"
TokenTypeRaffle TokenType = "RAFFLE"
TokenTypeMinigame TokenType = "MINIGAME"
)
type CustomerTokens struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
CustomerID uuid.UUID `gorm:"type:uuid;not null;index" json:"customer_id" validate:"required"`
TokenType TokenType `gorm:"type:varchar(50);not null" json:"token_type" validate:"required,oneof=SPIN RAFFLE MINIGAME"`
Balance int64 `gorm:"not null;default:0" json:"balance" validate:"min=0"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Customer Customer `gorm:"foreignKey:CustomerID" json:"customer,omitempty"`
}
func (ct *CustomerTokens) BeforeCreate(tx *gorm.DB) error {
if ct.ID == uuid.Nil {
ct.ID = uuid.New()
}
return nil
}
func (CustomerTokens) TableName() string {
return "customer_tokens"
}
+17
View File
@@ -18,6 +18,23 @@ func GetAllEntities() []interface{} {
&Payment{}, &Payment{},
&Customer{}, &Customer{},
&Table{}, &Table{},
&Vendor{},
&PurchaseOrder{},
&PurchaseOrderItem{},
&PurchaseOrderAttachment{},
&IngredientUnitConverter{},
// Gamification entities
&CustomerPoints{},
&CustomerTokens{},
&Tier{},
&Game{},
&GamePrize{},
&GamePlay{},
&OmsetTracker{},
&Reward{},
&Campaign{},
&CampaignRule{},
&OtpSession{},
// Analytics entities are not database tables, they are query results // Analytics entities are not database tables, they are query results
} }
} }
+40
View File
@@ -0,0 +1,40 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type GameType string
const (
GameTypeSpin GameType = "SPIN"
GameTypeRaffle GameType = "RAFFLE"
GameTypeMinigame GameType = "MINIGAME"
)
type Game struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
Type GameType `gorm:"type:varchar(50);not null" json:"type" validate:"required,oneof=SPIN RAFFLE MINIGAME"`
IsActive bool `gorm:"default:true" json:"is_active"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Prizes []GamePrize `gorm:"foreignKey:GameID" json:"prizes,omitempty"`
Plays []GamePlay `gorm:"foreignKey:GameID" json:"plays,omitempty"`
}
func (g *Game) BeforeCreate(tx *gorm.DB) error {
if g.ID == uuid.Nil {
g.ID = uuid.New()
}
return nil
}
func (Game) TableName() string {
return "games"
}
+33
View File
@@ -0,0 +1,33 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type GamePlay struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
GameID uuid.UUID `gorm:"type:uuid;not null;index" json:"game_id" validate:"required"`
CustomerID uuid.UUID `gorm:"type:uuid;not null;index" json:"customer_id" validate:"required"`
PrizeID *uuid.UUID `gorm:"type:uuid" json:"prize_id,omitempty"`
TokenUsed int `gorm:"default:0" json:"token_used" validate:"min=0"`
RandomSeed *string `gorm:"type:varchar(255)" json:"random_seed,omitempty"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
Game Game `gorm:"foreignKey:GameID" json:"game,omitempty"`
Customer Customer `gorm:"foreignKey:CustomerID" json:"customer,omitempty"`
Prize *GamePrize `gorm:"foreignKey:PrizeID" json:"prize,omitempty"`
}
func (gp *GamePlay) BeforeCreate(tx *gorm.DB) error {
if gp.ID == uuid.Nil {
gp.ID = uuid.New()
}
return nil
}
func (GamePlay) TableName() string {
return "game_plays"
}
+38
View File
@@ -0,0 +1,38 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type GamePrize struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
GameID uuid.UUID `gorm:"type:uuid;not null;index" json:"game_id" validate:"required"`
Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
Weight int `gorm:"not null" json:"weight" validate:"min=1"`
Stock int `gorm:"default:0" json:"stock" validate:"min=0"`
MaxStock *int `gorm:"" json:"max_stock,omitempty"`
Threshold *int64 `gorm:"" json:"threshold,omitempty"`
FallbackPrizeID *uuid.UUID `gorm:"type:uuid" json:"fallback_prize_id,omitempty"`
Image *string `gorm:"type:varchar(500)" json:"image,omitempty"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Game Game `gorm:"foreignKey:GameID" json:"game,omitempty"`
FallbackPrize *GamePrize `gorm:"foreignKey:FallbackPrizeID" json:"fallback_prize,omitempty"`
Plays []GamePlay `gorm:"foreignKey:PrizeID" json:"plays,omitempty"`
}
func (gp *GamePrize) BeforeCreate(tx *gorm.DB) error {
if gp.ID == uuid.Nil {
gp.ID = uuid.New()
}
return nil
}
func (GamePrize) TableName() string {
return "game_prizes"
}
+14 -13
View File
@@ -7,17 +7,18 @@ import (
) )
type Ingredient struct { type Ingredient struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"` OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
Name string `gorm:"not null;size:255" json:"name"` Name string `gorm:"not null;size:255" json:"name"`
UnitID uuid.UUID `gorm:"type:uuid;not null;index" json:"unit_id"` UnitID uuid.UUID `gorm:"type:uuid;not null;index" json:"unit_id"`
Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost"` Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost"`
Stock float64 `gorm:"type:decimal(10,2);default:0.00" json:"stock"` Stock float64 `gorm:"type:decimal(10,2);default:0.00" json:"stock"`
IsSemiFinished bool `gorm:"default:false" json:"is_semi_finished"` IsSemiFinished bool `gorm:"default:false" json:"is_semi_finished"`
IsActive bool `gorm:"default:true" json:"is_active"` IsActive bool `gorm:"default:true" json:"is_active"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"` Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"` Unit *Unit `gorm:"foreignKey:UnitID;references:ID" json:"unit,omitempty"`
Compositions []IngredientComposition `gorm:"foreignKey:ParentIngredientID;references:ID" json:"compositions,omitempty"`
} }
+10 -10
View File
@@ -7,14 +7,14 @@ import (
) )
type IngredientComposition struct { type IngredientComposition struct {
ID uuid.UUID `json:"id" db:"id"` ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"` OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ParentIngredientID uuid.UUID `json:"parent_ingredient_id" db:"parent_ingredient_id"` ParentIngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"parent_ingredient_id"`
ChildIngredientID uuid.UUID `json:"child_ingredient_id" db:"child_ingredient_id"` ChildIngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"child_ingredient_id"`
Quantity float64 `json:"quantity" db:"quantity"` Quantity float64 `gorm:"type:decimal(10,4);not null" json:"quantity"`
CreatedAt time.Time `json:"created_at" db:"created_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
ParentIngredient *Ingredient `json:"parent_ingredient,omitempty"` ParentIngredient *Ingredient `gorm:"foreignKey:ParentIngredientID;references:ID" json:"parent_ingredient,omitempty"`
ChildIngredient *Ingredient `json:"child_ingredient,omitempty"` ChildIngredient *Ingredient `gorm:"foreignKey:ChildIngredientID;references:ID" json:"child_ingredient,omitempty"`
} }
@@ -0,0 +1,42 @@
package entities
import (
"time"
"github.com/google/uuid"
)
type IngredientUnitConverter struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
IngredientID uuid.UUID `gorm:"type:uuid;not null" json:"ingredient_id"`
FromUnitID uuid.UUID `gorm:"type:uuid;not null" json:"from_unit_id"`
ToUnitID uuid.UUID `gorm:"type:uuid;not null" json:"to_unit_id"`
ConversionFactor float64 `gorm:"type:decimal(15,6);not null" json:"conversion_factor"`
IsActive bool `gorm:"default:true" json:"is_active"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
CreatedBy uuid.UUID `gorm:"type:uuid;not null" json:"created_by"`
UpdatedBy uuid.UUID `gorm:"type:uuid;not null" json:"updated_by"`
// Relationships
Organization *Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Ingredient *Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
FromUnit *Unit `gorm:"foreignKey:FromUnitID" json:"from_unit,omitempty"`
ToUnit *Unit `gorm:"foreignKey:ToUnitID" json:"to_unit,omitempty"`
CreatedByUser *User `gorm:"foreignKey:CreatedBy" json:"created_by_user,omitempty"`
UpdatedByUser *User `gorm:"foreignKey:UpdatedBy" json:"updated_by_user,omitempty"`
}
func (IngredientUnitConverter) TableName() string {
return "ingredient_unit_converters"
}
// BeforeCreate hook to set default values
func (iuc *IngredientUnitConverter) BeforeCreate() error {
if iuc.ID == uuid.Nil {
iuc.ID = uuid.New()
}
return nil
}
+1
View File
@@ -20,6 +20,7 @@ const (
InventoryMovementTypeTransferOut InventoryMovementType = "transfer_out" InventoryMovementTypeTransferOut InventoryMovementType = "transfer_out"
InventoryMovementTypeDamage InventoryMovementType = "damage" InventoryMovementTypeDamage InventoryMovementType = "damage"
InventoryMovementTypeExpiry InventoryMovementType = "expiry" InventoryMovementTypeExpiry InventoryMovementType = "expiry"
InventoryMovementTypeIngredient InventoryMovementType = "ingredient"
) )
type InventoryMovementReferenceType string type InventoryMovementReferenceType string
+41
View File
@@ -0,0 +1,41 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type PeriodType string
const (
PeriodTypeDaily PeriodType = "DAILY"
PeriodTypeWeekly PeriodType = "WEEKLY"
PeriodTypeMonthly PeriodType = "MONTHLY"
PeriodTypeTotal PeriodType = "TOTAL"
)
type OmsetTracker struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
PeriodType PeriodType `gorm:"type:varchar(20);not null" json:"period_type" validate:"required,oneof=DAILY WEEKLY MONTHLY TOTAL"`
PeriodStart time.Time `gorm:"type:date;not null" json:"period_start" validate:"required"`
PeriodEnd time.Time `gorm:"type:date;not null" json:"period_end" validate:"required"`
Total int64 `gorm:"not null;default:0" json:"total" validate:"min=0"`
GameID *uuid.UUID `gorm:"type:uuid" json:"game_id,omitempty"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Game *Game `gorm:"foreignKey:GameID" json:"game,omitempty"`
}
func (ot *OmsetTracker) BeforeCreate(tx *gorm.DB) error {
if ot.ID == uuid.Nil {
ot.ID = uuid.New()
}
return nil
}
func (OmsetTracker) TableName() string {
return "omset_tracker"
}
@@ -0,0 +1,48 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type OrderIngredientTransaction struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
OrderID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_id" validate:"required"`
OrderItemID *uuid.UUID `gorm:"type:uuid;index" json:"order_item_id"`
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id" validate:"required"`
ProductVariantID *uuid.UUID `gorm:"type:uuid;index" json:"product_variant_id"`
IngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"ingredient_id" validate:"required"`
GrossQty float64 `gorm:"type:decimal(12,3);not null" json:"gross_qty" validate:"required,gt=0"`
NetQty float64 `gorm:"type:decimal(12,3);not null" json:"net_qty" validate:"required,gt=0"`
WasteQty float64 `gorm:"type:decimal(12,3);not null" json:"waste_qty" validate:"min=0"`
Unit string `gorm:"size:50;not null" json:"unit" validate:"required,max=50"`
TransactionDate time.Time `gorm:"not null;index" json:"transaction_date"`
CreatedBy uuid.UUID `gorm:"type:uuid;not null;index" json:"created_by" validate:"required"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// Relations
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Outlet *Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
Order Order `gorm:"foreignKey:OrderID" json:"order,omitempty"`
OrderItem *OrderItem `gorm:"foreignKey:OrderItemID" json:"order_item,omitempty"`
Product Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
ProductVariant *ProductVariant `gorm:"foreignKey:ProductVariantID" json:"product_variant,omitempty"`
Ingredient Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
CreatedByUser User `gorm:"foreignKey:CreatedBy" json:"created_by_user,omitempty"`
}
func (oit *OrderIngredientTransaction) BeforeCreate(tx *gorm.DB) error {
if oit.ID == uuid.Nil {
oit.ID = uuid.New()
}
return nil
}
func (OrderIngredientTransaction) TableName() string {
return "order_ingredients_transactions"
}
+54
View File
@@ -0,0 +1,54 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type OtpSession struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Token string `gorm:"type:varchar(255);uniqueIndex;not null" json:"token"`
Code string `gorm:"type:varchar(10);not null" json:"code"`
PhoneNumber string `gorm:"type:varchar(20);not null;index" json:"phone_number"`
Purpose string `gorm:"type:varchar(50);not null;index" json:"purpose"`
ExpiresAt time.Time `gorm:"not null;index" json:"expires_at"`
IsUsed bool `gorm:"default:false;index" json:"is_used"`
AttemptsCount int `gorm:"default:0" json:"attempts_count"`
MaxAttempts int `gorm:"default:3" json:"max_attempts"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
}
func (o *OtpSession) BeforeCreate(tx *gorm.DB) error {
if o.ID == uuid.Nil {
o.ID = uuid.New()
}
return nil
}
func (OtpSession) TableName() string {
return "otp_sessions"
}
func (o *OtpSession) IsExpired() bool {
return time.Now().After(o.ExpiresAt)
}
func (o *OtpSession) IsMaxAttemptsReached() bool {
return o.AttemptsCount >= o.MaxAttempts
}
func (o *OtpSession) CanBeUsed() bool {
return !o.IsUsed && !o.IsExpired() && !o.IsMaxAttemptsReached()
}
func (o *OtpSession) IncrementAttempts() {
o.AttemptsCount++
}
func (o *OtpSession) MarkAsUsed() {
o.IsUsed = true
}
+1 -1
View File
@@ -30,7 +30,7 @@ type Product struct {
Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"` Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"` Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"`
ProductVariants []ProductVariant `gorm:"foreignKey:ProductID" json:"variants,omitempty"` ProductVariants []ProductVariant `gorm:"foreignKey:ProductID" json:"variants,omitempty"`
ProductIngredients []ProductIngredient `gorm:"foreignKey:ProductID" json:"product_ingredients,omitempty"` ProductRecipes []ProductRecipe `gorm:"foreignKey:ProductID" json:"product_recipes,omitempty"`
Inventory []Inventory `gorm:"foreignKey:ProductID" json:"inventory,omitempty"` Inventory []Inventory `gorm:"foreignKey:ProductID" json:"inventory,omitempty"`
OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"` OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"`
} }
+9 -8
View File
@@ -7,14 +7,15 @@ import (
) )
type ProductIngredient struct { type ProductIngredient struct {
ID uuid.UUID `json:"id" db:"id"` ID uuid.UUID `json:"id" db:"id"`
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"` OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
ProductID uuid.UUID `json:"product_id" db:"product_id"` ProductID uuid.UUID `json:"product_id" db:"product_id"`
IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"` IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"`
Quantity float64 `json:"quantity" db:"quantity"` Quantity float64 `json:"quantity" db:"quantity"`
CreatedAt time.Time `json:"created_at" db:"created_at"` WastePercentage float64 `json:"waste_percentage" db:"waste_percentage"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"` CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
// Relations // Relations
Product *Product `json:"product,omitempty"` Product *Product `json:"product,omitempty"`
+10 -9
View File
@@ -8,15 +8,16 @@ import (
) )
type ProductRecipe struct { type ProductRecipe struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"` ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"` OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"` OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id"` ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id"`
VariantID *uuid.UUID `gorm:"type:uuid;index" json:"variant_id"` VariantID *uuid.UUID `gorm:"type:uuid;index" json:"variant_id"`
IngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"ingredient_id"` IngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"ingredient_id"`
Quantity float64 `gorm:"type:decimal(12,3);not null" json:"quantity"` Quantity float64 `gorm:"type:decimal(12,3);not null" json:"quantity"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` WastePercentage float64 `gorm:"type:decimal(5,2);default:0" json:"waste_percentage"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// Relations // Relations
Product *Product `gorm:"foreignKey:ProductID" json:"product,omitempty"` Product *Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
+91
View File
@@ -0,0 +1,91 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type PurchaseOrder struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id" validate:"required"`
VendorID uuid.UUID `gorm:"type:uuid;not null" json:"vendor_id" validate:"required"`
PONumber string `gorm:"not null;size:50" json:"po_number" validate:"required,min=1,max=50"`
TransactionDate time.Time `gorm:"type:date;not null" json:"transaction_date" validate:"required"`
DueDate time.Time `gorm:"type:date;not null" json:"due_date" validate:"required"`
Reference *string `gorm:"size:100" json:"reference" validate:"omitempty,max=100"`
Status string `gorm:"not null;size:20;default:'draft'" json:"status" validate:"required,oneof=draft sent approved received cancelled"`
Message *string `gorm:"type:text" json:"message" validate:"omitempty"`
TotalAmount float64 `gorm:"type:decimal(15,2);not null;default:0" json:"total_amount"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Organization *Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Vendor *Vendor `gorm:"foreignKey:VendorID" json:"vendor,omitempty"`
Items []PurchaseOrderItem `gorm:"foreignKey:PurchaseOrderID" json:"items,omitempty"`
Attachments []PurchaseOrderAttachment `gorm:"foreignKey:PurchaseOrderID" json:"attachments,omitempty"`
}
func (po *PurchaseOrder) BeforeCreate(tx *gorm.DB) error {
if po.ID == uuid.Nil {
id := uuid.New()
po.ID = id
}
return nil
}
func (PurchaseOrder) TableName() string {
return "purchase_orders"
}
type PurchaseOrderItem struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
PurchaseOrderID uuid.UUID `gorm:"type:uuid;not null" json:"purchase_order_id" validate:"required"`
IngredientID uuid.UUID `gorm:"type:uuid;not null" json:"ingredient_id" validate:"required"`
Description *string `gorm:"type:text" json:"description" validate:"omitempty"`
Quantity float64 `gorm:"type:decimal(10,3);not null" json:"quantity" validate:"required,gt=0"`
UnitID uuid.UUID `gorm:"type:uuid;not null" json:"unit_id" validate:"required"`
Amount float64 `gorm:"type:decimal(15,2);not null" json:"amount" validate:"required,gte=0"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
PurchaseOrder *PurchaseOrder `gorm:"foreignKey:PurchaseOrderID" json:"purchase_order,omitempty"`
Ingredient *Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"`
}
func (poi *PurchaseOrderItem) BeforeCreate(tx *gorm.DB) error {
if poi.ID == uuid.Nil {
id := uuid.New()
poi.ID = id
}
return nil
}
func (PurchaseOrderItem) TableName() string {
return "purchase_order_items"
}
type PurchaseOrderAttachment struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
PurchaseOrderID uuid.UUID `gorm:"type:uuid;not null" json:"purchase_order_id" validate:"required"`
FileID uuid.UUID `gorm:"type:uuid;not null" json:"file_id" validate:"required"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
PurchaseOrder *PurchaseOrder `gorm:"foreignKey:PurchaseOrderID" json:"purchase_order,omitempty"`
File *File `gorm:"foreignKey:FileID" json:"file,omitempty"`
}
func (poa *PurchaseOrderAttachment) BeforeCreate(tx *gorm.DB) error {
if poa.ID == uuid.Nil {
id := uuid.New()
poa.ID = id
}
return nil
}
func (PurchaseOrderAttachment) TableName() string {
return "purchase_order_attachments"
}
+126
View File
@@ -0,0 +1,126 @@
package entities
import (
"database/sql/driver"
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type RewardType string
const (
RewardTypeVoucher RewardType = "VOUCHER"
RewardTypePhysical RewardType = "PHYSICAL"
RewardTypeDigital RewardType = "DIGITAL"
RewardTypeBalance RewardType = "BALANCE"
)
// StringSlice is a custom type for []string that implements sql.Scanner and driver.Valuer
type StringSlice []string
// Scan implements the sql.Scanner interface for StringSlice
func (s *StringSlice) Scan(value interface{}) error {
if value == nil {
*s = StringSlice{}
return nil
}
var bytes []byte
switch v := value.(type) {
case []byte:
bytes = v
case string:
bytes = []byte(v)
default:
return fmt.Errorf("cannot scan %T into StringSlice", value)
}
return json.Unmarshal(bytes, s)
}
// Value implements the driver.Valuer interface for StringSlice
func (s StringSlice) Value() (driver.Value, error) {
if s == nil {
return nil, nil
}
return json.Marshal(s)
}
type TermsAndConditions struct {
Sections []TncSection `json:"sections"`
ExpiryDays int `json:"expiry_days"`
}
// Scan implements the sql.Scanner interface for TermsAndConditions
func (t *TermsAndConditions) Scan(value interface{}) error {
if value == nil {
*t = TermsAndConditions{}
return nil
}
var bytes []byte
switch v := value.(type) {
case []byte:
bytes = v
case string:
bytes = []byte(v)
default:
return fmt.Errorf("cannot scan %T into TermsAndConditions", value)
}
return json.Unmarshal(bytes, t)
}
// Value implements the driver.Valuer interface for TermsAndConditions
func (t TermsAndConditions) Value() (driver.Value, error) {
return json.Marshal(t)
}
type TncSection struct {
Title string `json:"title"`
Rules []string `json:"rules"`
}
type Reward struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Name string `gorm:"type:varchar(150);not null" json:"name"`
RewardType RewardType `gorm:"type:varchar(50);not null" json:"reward_type"`
CostPoints int64 `gorm:"type:bigint;not null" json:"cost_points"`
Stock *int `gorm:"type:int" json:"stock,omitempty"`
MaxPerCustomer int `gorm:"type:int;default:1" json:"max_per_customer"`
Tnc *TermsAndConditions `gorm:"type:jsonb" json:"tnc,omitempty"`
Metadata *map[string]interface{} `gorm:"type:jsonb" json:"metadata,omitempty"`
Images *StringSlice `gorm:"type:jsonb" json:"images,omitempty"`
CreatedAt time.Time `gorm:"type:timestamp;default:now()" json:"created_at"`
UpdatedAt time.Time `gorm:"type:timestamp;default:now()" json:"updated_at"`
}
func (Reward) TableName() string {
return "rewards"
}
func (r *Reward) BeforeCreate(tx *gorm.DB) error {
if r.ID == uuid.Nil {
r.ID = uuid.New()
}
return nil
}
func (r *Reward) BeforeUpdate(tx *gorm.DB) error {
r.UpdatedAt = time.Now()
return nil
}
type ListRewardsRequest struct {
Page int `form:"page" binding:"min=1"`
Limit int `form:"limit" binding:"min=1,max=100"`
Search string `form:"search"`
RewardType string `form:"reward_type"`
MinPoints *int64 `form:"min_points"`
MaxPoints *int64 `form:"max_points"`
HasStock *bool `form:"has_stock"`
}
+28
View File
@@ -0,0 +1,28 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Tier struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
Name string `gorm:"type:varchar(100);not null;unique" json:"name" validate:"required"`
MinPoints int64 `gorm:"not null" json:"min_points" validate:"min=0"`
Benefits Metadata `gorm:"type:jsonb;default:'{}'" json:"benefits"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
}
func (t *Tier) BeforeCreate(tx *gorm.DB) error {
if t.ID == uuid.Nil {
t.ID = uuid.New()
}
return nil
}
func (Tier) TableName() string {
return "tiers"
}
+39
View File
@@ -0,0 +1,39 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type Vendor struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id" validate:"required"`
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
Email *string `gorm:"size:255" json:"email" validate:"omitempty,email"`
PhoneNumber *string `gorm:"size:20" json:"phone_number" validate:"omitempty"`
Address *string `gorm:"type:text" json:"address" validate:"omitempty"`
ContactPerson *string `gorm:"size:255" json:"contact_person" validate:"omitempty,max=255"`
TaxNumber *string `gorm:"size:50" json:"tax_number" validate:"omitempty,max=50"`
PaymentTerms *string `gorm:"size:100" json:"payment_terms" validate:"omitempty,max=100"`
Notes *string `gorm:"type:text" json:"notes" validate:"omitempty"`
IsActive bool `gorm:"not null;default:true" json:"is_active"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Organization *Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
}
func (v *Vendor) BeforeCreate(tx *gorm.DB) error {
if v.ID == uuid.Nil {
id := uuid.New()
v.ID = id
}
return nil
}
func (Vendor) TableName() string {
return "vendors"
}
+197
View File
@@ -0,0 +1,197 @@
package handler
import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type AccountHandler struct {
service contract.AccountContract
validator validator.AccountValidator
}
func NewAccountHandler(service contract.AccountContract, validator validator.AccountValidator) *AccountHandler {
return &AccountHandler{
service: service,
validator: validator,
}
}
func (h *AccountHandler) CreateAccount(c *gin.Context) {
var req contract.CreateAccountRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
response, err := h.service.CreateAccount(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "AccountHandler")
}
func (h *AccountHandler) GetAccountByID(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "AccountHandler")
return
}
response, err := h.service.GetAccountByID(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "AccountHandler")
}
func (h *AccountHandler) UpdateAccount(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "AccountHandler")
return
}
var req contract.UpdateAccountRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
response, err := h.service.UpdateAccount(c, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "AccountHandler")
}
func (h *AccountHandler) DeleteAccount(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "AccountHandler")
return
}
err = h.service.DeleteAccount(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Account deleted successfully"}), "AccountHandler")
}
func (h *AccountHandler) ListAccounts(c *gin.Context) {
var req contract.ListAccountsRequest
if err := c.ShouldBindQuery(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
response, total, err := h.service.ListAccounts(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{
"data": response,
"total": total,
"page": req.Page,
"limit": req.Limit,
}), "AccountHandler")
}
func (h *AccountHandler) GetAccountsByOrganization(c *gin.Context) {
organizationIDStr := c.Param("organization_id")
organizationID, err := uuid.Parse(organizationIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid organization ID format"}}), "AccountHandler")
return
}
var outletID *uuid.UUID
if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
if parsedOutletID, err := uuid.Parse(outletIDStr); err == nil {
outletID = &parsedOutletID
}
}
response, err := h.service.GetAccountsByOrganization(c, organizationID, outletID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "AccountHandler")
}
func (h *AccountHandler) GetAccountsByChartOfAccount(c *gin.Context) {
chartOfAccountIDStr := c.Param("chart_of_account_id")
chartOfAccountID, err := uuid.Parse(chartOfAccountIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid chart of account ID format"}}), "AccountHandler")
return
}
response, err := h.service.GetAccountsByChartOfAccount(c, chartOfAccountID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "AccountHandler")
}
func (h *AccountHandler) UpdateAccountBalance(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "AccountHandler")
return
}
var req contract.UpdateAccountBalanceRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
err = h.service.UpdateAccountBalance(c, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Account balance updated successfully"}), "AccountHandler")
}
func (h *AccountHandler) GetAccountBalance(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "AccountHandler")
return
}
balance, err := h.service.GetAccountBalance(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "AccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"balance": balance}), "AccountHandler")
}
+24
View File
@@ -97,6 +97,30 @@ func (h *AnalyticsHandler) GetProductAnalytics(c *gin.Context) {
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "AnalyticsHandler::GetProductAnalytics") util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "AnalyticsHandler::GetProductAnalytics")
} }
func (h *AnalyticsHandler) GetProductAnalyticsPerCategory(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.ProductAnalyticsPerCategoryRequest
if err := c.ShouldBindQuery(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "AnalyticsHandler::GetProductAnalyticsPerCategory", err.Error())}), "AnalyticsHandler::GetProductAnalyticsPerCategory")
return
}
req.OrganizationID = contextInfo.OrganizationID
req.OutletID = &contextInfo.OutletID
modelReq := transformer.ProductAnalyticsPerCategoryContractToModel(&req)
response, err := h.analyticsService.GetProductAnalyticsPerCategory(ctx, modelReq)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "AnalyticsHandler::GetProductAnalyticsPerCategory", err.Error())}), "AnalyticsHandler::GetProductAnalyticsPerCategory")
return
}
contractResp := transformer.ProductAnalyticsPerCategoryModelToContract(response)
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "AnalyticsHandler::GetProductAnalyticsPerCategory")
}
func (h *AnalyticsHandler) GetDashboardAnalytics(c *gin.Context) { func (h *AnalyticsHandler) GetDashboardAnalytics(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
+307
View File
@@ -0,0 +1,307 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
)
type CampaignHandler struct {
campaignService service.CampaignService
campaignValidator validator.CampaignValidator
}
func NewCampaignHandler(campaignService service.CampaignService, campaignValidator validator.CampaignValidator) *CampaignHandler {
return &CampaignHandler{
campaignService: campaignService,
campaignValidator: campaignValidator,
}
}
func (h *CampaignHandler) CreateCampaign(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateCampaignRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::CreateCampaign -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::CreateCampaign")
return
}
validationError, validationErrorCode := h.campaignValidator.ValidateCreateCampaignRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CampaignHandler::CreateCampaign -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::CreateCampaign")
return
}
response, err := h.campaignService.CreateCampaign(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::CreateCampaign -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::CreateCampaign")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::CreateCampaign")
}
func (h *CampaignHandler) GetCampaign(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("CampaignHandler::GetCampaign -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.CampaignEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::GetCampaign")
return
}
response, err := h.campaignService.GetCampaign(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::GetCampaign -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::GetCampaign")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::GetCampaign")
}
func (h *CampaignHandler) ListCampaigns(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListCampaignsRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::ListCampaigns -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::ListCampaigns")
return
}
validationError, validationErrorCode := h.campaignValidator.ValidateListCampaignsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CampaignHandler::ListCampaigns -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::ListCampaigns")
return
}
response, err := h.campaignService.ListCampaigns(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::ListCampaigns -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::ListCampaigns")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::ListCampaigns")
}
func (h *CampaignHandler) UpdateCampaign(c *gin.Context) {
ctx := c.Request.Context()
var req contract.UpdateCampaignRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::UpdateCampaign -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::UpdateCampaign")
return
}
validationError, validationErrorCode := h.campaignValidator.ValidateUpdateCampaignRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CampaignHandler::UpdateCampaign -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::UpdateCampaign")
return
}
response, err := h.campaignService.UpdateCampaign(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::UpdateCampaign -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::UpdateCampaign")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::UpdateCampaign")
}
func (h *CampaignHandler) DeleteCampaign(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("CampaignHandler::DeleteCampaign -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.CampaignEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::DeleteCampaign")
return
}
err := h.campaignService.DeleteCampaign(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::DeleteCampaign -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::DeleteCampaign")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse("Campaign deleted successfully"), "CampaignHandler::DeleteCampaign")
}
func (h *CampaignHandler) GetActiveCampaigns(c *gin.Context) {
ctx := c.Request.Context()
response, err := h.campaignService.GetActiveCampaigns(ctx)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::GetActiveCampaigns -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::GetActiveCampaigns")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::GetActiveCampaigns")
}
func (h *CampaignHandler) GetCampaignsForApp(c *gin.Context) {
ctx := c.Request.Context()
response, err := h.campaignService.GetCampaignsForApp(ctx)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::GetCampaignsForApp -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignEntity, err.Error())}), "CampaignHandler::GetCampaignsForApp")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::GetCampaignsForApp")
}
// Campaign Rules Handlers
func (h *CampaignHandler) CreateCampaignRule(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateCampaignRuleRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::CreateCampaignRule -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::CreateCampaignRule")
return
}
response, err := h.campaignService.CreateCampaignRule(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::CreateCampaignRule -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::CreateCampaignRule")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::CreateCampaignRule")
}
func (h *CampaignHandler) GetCampaignRule(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("CampaignHandler::GetCampaignRule -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.CampaignRuleEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::GetCampaignRule")
return
}
response, err := h.campaignService.GetCampaignRule(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::GetCampaignRule -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::GetCampaignRule")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::GetCampaignRule")
}
func (h *CampaignHandler) ListCampaignRules(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListCampaignRulesRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::ListCampaignRules -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::ListCampaignRules")
return
}
response, err := h.campaignService.ListCampaignRules(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::ListCampaignRules -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::ListCampaignRules")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::ListCampaignRules")
}
func (h *CampaignHandler) UpdateCampaignRule(c *gin.Context) {
ctx := c.Request.Context()
var req contract.UpdateCampaignRuleRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::UpdateCampaignRule -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::UpdateCampaignRule")
return
}
response, err := h.campaignService.UpdateCampaignRule(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::UpdateCampaignRule -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::UpdateCampaignRule")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::UpdateCampaignRule")
}
func (h *CampaignHandler) DeleteCampaignRule(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("CampaignHandler::DeleteCampaignRule -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.CampaignRuleEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::DeleteCampaignRule")
return
}
err := h.campaignService.DeleteCampaignRule(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::DeleteCampaignRule -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::DeleteCampaignRule")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse("Campaign rule deleted successfully"), "CampaignHandler::DeleteCampaignRule")
}
func (h *CampaignHandler) GetCampaignRulesByCampaignID(c *gin.Context) {
ctx := c.Request.Context()
campaignIDStr := c.Param("campaign_id")
if campaignIDStr == "" {
logger.FromContext(c.Request.Context()).Error("CampaignHandler::GetCampaignRulesByCampaignID -> missing campaign_id parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.CampaignRuleEntity, "campaign_id parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CampaignHandler::GetCampaignRulesByCampaignID")
return
}
response, err := h.campaignService.GetCampaignRulesByCampaignID(ctx, campaignIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CampaignHandler::GetCampaignRulesByCampaignID -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CampaignRuleEntity, err.Error())}), "CampaignHandler::GetCampaignRulesByCampaignID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CampaignHandler::GetCampaignRulesByCampaignID")
}
+3
View File
@@ -1,6 +1,7 @@
package handler package handler
import ( import (
"fmt"
"strconv" "strconv"
"apskel-pos-be/internal/appcontext" "apskel-pos-be/internal/appcontext"
@@ -35,6 +36,7 @@ func (h *CategoryHandler) CreateCategory(c *gin.Context) {
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateCategoryRequest var req contract.CreateCategoryRequest
fmt.Printf("CategoryHandler::CreateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CategoryHandler::CreateCategory -> request binding failed") logger.FromContext(c.Request.Context()).WithError(err).Error("CategoryHandler::CreateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@@ -71,6 +73,7 @@ func (h *CategoryHandler) UpdateCategory(c *gin.Context) {
} }
var req contract.UpdateCategoryRequest var req contract.UpdateCategoryRequest
fmt.Printf("CategoryHandler::UpdateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("CategoryHandler::UpdateCategory -> request binding failed") logger.FromContext(ctx).WithError(err).Error("CategoryHandler::UpdateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body") validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
@@ -0,0 +1,171 @@
package handler
import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type ChartOfAccountHandler struct {
service contract.ChartOfAccountContract
validator validator.ChartOfAccountValidator
}
func NewChartOfAccountHandler(service contract.ChartOfAccountContract, validator validator.ChartOfAccountValidator) *ChartOfAccountHandler {
return &ChartOfAccountHandler{
service: service,
validator: validator,
}
}
func (h *ChartOfAccountHandler) CreateChartOfAccount(c *gin.Context) {
var req contract.CreateChartOfAccountRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
response, err := h.service.CreateChartOfAccount(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) GetChartOfAccountByID(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountHandler")
return
}
response, err := h.service.GetChartOfAccountByID(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) UpdateChartOfAccount(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountHandler")
return
}
var req contract.UpdateChartOfAccountRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
response, err := h.service.UpdateChartOfAccount(c, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) DeleteChartOfAccount(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountHandler")
return
}
err = h.service.DeleteChartOfAccount(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Chart of account deleted successfully"}), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) ListChartOfAccounts(c *gin.Context) {
var req contract.ListChartOfAccountsRequest
if err := c.ShouldBindQuery(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
response, total, err := h.service.ListChartOfAccounts(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{
"data": response,
"total": total,
"page": req.Page,
"limit": req.Limit,
}), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) GetChartOfAccountsByOrganization(c *gin.Context) {
organizationIDStr := c.Param("organization_id")
organizationID, err := uuid.Parse(organizationIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid organization ID format"}}), "ChartOfAccountHandler")
return
}
var outletID *uuid.UUID
if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
if parsedOutletID, err := uuid.Parse(outletIDStr); err == nil {
outletID = &parsedOutletID
}
}
response, err := h.service.GetChartOfAccountsByOrganization(c, organizationID, outletID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountHandler")
}
func (h *ChartOfAccountHandler) GetChartOfAccountsByType(c *gin.Context) {
organizationIDStr := c.Param("organization_id")
organizationID, err := uuid.Parse(organizationIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid organization ID format"}}), "ChartOfAccountHandler")
return
}
typeIDStr := c.Param("type_id")
typeID, err := uuid.Parse(typeIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid type ID format"}}), "ChartOfAccountHandler")
return
}
var outletID *uuid.UUID
if outletIDStr := c.Query("outlet_id"); outletIDStr != "" {
if parsedOutletID, err := uuid.Parse(outletIDStr); err == nil {
outletID = &parsedOutletID
}
}
response, err := h.service.GetChartOfAccountsByType(c, organizationID, typeID, outletID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountHandler")
}
@@ -0,0 +1,124 @@
package handler
import (
"strconv"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type ChartOfAccountTypeHandler struct {
service contract.ChartOfAccountTypeContract
validator validator.ChartOfAccountTypeValidator
}
func NewChartOfAccountTypeHandler(service contract.ChartOfAccountTypeContract, validator validator.ChartOfAccountTypeValidator) *ChartOfAccountTypeHandler {
return &ChartOfAccountTypeHandler{
service: service,
validator: validator,
}
}
func (h *ChartOfAccountTypeHandler) CreateChartOfAccountType(c *gin.Context) {
var req contract.CreateChartOfAccountTypeRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
response, err := h.service.CreateChartOfAccountType(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountTypeHandler")
}
func (h *ChartOfAccountTypeHandler) GetChartOfAccountTypeByID(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountTypeHandler")
return
}
response, err := h.service.GetChartOfAccountTypeByID(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountTypeHandler")
}
func (h *ChartOfAccountTypeHandler) UpdateChartOfAccountType(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountTypeHandler")
return
}
var req contract.UpdateChartOfAccountTypeRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
response, err := h.service.UpdateChartOfAccountType(c, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ChartOfAccountTypeHandler")
}
func (h *ChartOfAccountTypeHandler) DeleteChartOfAccountType(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "ChartOfAccountTypeHandler")
return
}
err = h.service.DeleteChartOfAccountType(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Chart of account type deleted successfully"}), "ChartOfAccountTypeHandler")
}
func (h *ChartOfAccountTypeHandler) ListChartOfAccountTypes(c *gin.Context) {
// Parse query parameters
filters := make(map[string]interface{})
if isActive := c.Query("is_active"); isActive != "" {
if isActiveBool, err := strconv.ParseBool(isActive); err == nil {
filters["is_active"] = isActiveBool
}
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
response, total, err := h.service.ListChartOfAccountTypes(c, filters, page, limit)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "ChartOfAccountTypeHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{
"data": response,
"total": total,
"page": page,
"limit": limit,
}), "ChartOfAccountTypeHandler")
}
+196
View File
@@ -0,0 +1,196 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
)
type CustomerAuthHandler struct {
customerAuthService service.CustomerAuthService
customerAuthValidator validator.CustomerAuthValidator
}
func NewCustomerAuthHandler(customerAuthService service.CustomerAuthService, customerAuthValidator validator.CustomerAuthValidator) *CustomerAuthHandler {
return &CustomerAuthHandler{
customerAuthService: customerAuthService,
customerAuthValidator: customerAuthValidator,
}
}
func (h *CustomerAuthHandler) CheckPhone(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CheckPhoneRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::CheckPhone -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::CheckPhone")
return
}
validationError, validationErrorCode := h.customerAuthValidator.ValidateCheckPhoneRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerAuthHandler::CheckPhone -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::CheckPhone")
return
}
response, err := h.customerAuthService.CheckPhoneNumber(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::CheckPhone -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::CheckPhone")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::CheckPhone")
}
func (h *CustomerAuthHandler) RegisterStart(c *gin.Context) {
ctx := c.Request.Context()
var req contract.RegisterStartRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterStart -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterStart")
return
}
validationError, validationErrorCode := h.customerAuthValidator.ValidateRegisterStartRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerAuthHandler::RegisterStart -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterStart")
return
}
response, err := h.customerAuthService.StartRegistration(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterStart -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::RegisterStart")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::RegisterStart")
}
func (h *CustomerAuthHandler) RegisterVerifyOtp(c *gin.Context) {
ctx := c.Request.Context()
var req contract.RegisterVerifyOtpRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterVerifyOtp -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterVerifyOtp")
return
}
validationError, validationErrorCode := h.customerAuthValidator.ValidateRegisterVerifyOtpRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerAuthHandler::RegisterVerifyOtp -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterVerifyOtp")
return
}
response, err := h.customerAuthService.VerifyOtp(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterVerifyOtp -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::RegisterVerifyOtp")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::RegisterVerifyOtp")
}
func (h *CustomerAuthHandler) RegisterSetPassword(c *gin.Context) {
ctx := c.Request.Context()
var req contract.RegisterSetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterSetPassword -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterSetPassword")
return
}
validationError, validationErrorCode := h.customerAuthValidator.ValidateRegisterSetPasswordRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerAuthHandler::RegisterSetPassword -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::RegisterSetPassword")
return
}
response, err := h.customerAuthService.SetPassword(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::RegisterSetPassword -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::RegisterSetPassword")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::RegisterSetPassword")
}
func (h *CustomerAuthHandler) Login(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CustomerLoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::Login -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::Login")
return
}
validationError, validationErrorCode := h.customerAuthValidator.ValidateCustomerLoginRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("CustomerAuthHandler::Login -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "CustomerAuthHandler::Login")
return
}
response, err := h.customerAuthService.Login(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CustomerAuthHandler::Login -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::Login")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::Login")
}
func (h *CustomerAuthHandler) ResendOtp(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ResendOtpRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerAuthHandler::ResendOtp -> binding request failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::ResendOtp")
return
}
// Validate request
if err, entity := h.customerAuthValidator.ValidateResendOtpRequest(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerAuthHandler::ResendOtp -> validation failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.MissingFieldErrorCode, entity, err.Error())}), "CustomerAuthHandler::ResendOtp")
return
}
response, err := h.customerAuthService.ResendOtp(ctx, &req)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerAuthHandler::ResendOtp -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())}), "CustomerAuthHandler::ResendOtp")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerAuthHandler::ResendOtp")
}
+153
View File
@@ -0,0 +1,153 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"github.com/gin-gonic/gin"
)
type CustomerPointsHandler struct {
customerPointsService service.CustomerPointsService
}
func NewCustomerPointsHandler(customerPointsService service.CustomerPointsService) *CustomerPointsHandler {
return &CustomerPointsHandler{
customerPointsService: customerPointsService,
}
}
func (h *CustomerPointsHandler) GetCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
// Get customer ID from context (set by middleware)
customerID, exists := c.Get("customer_id")
if !exists {
logger.FromContext(ctx).Error("Customer ID not found in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Customer ID not found"),
}), "CustomerPointsHandler::GetCustomerPoints")
return
}
customerIDStr, ok := customerID.(string)
if !ok {
logger.FromContext(ctx).Error("Invalid customer ID type in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Invalid customer ID"),
}), "CustomerPointsHandler::GetCustomerPoints")
return
}
response, err := h.customerPointsService.GetCustomerPoints(ctx, customerIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerPointsHandler::GetCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "CustomerPointsHandler::GetCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerPointsHandler::GetCustomerPoints")
}
func (h *CustomerPointsHandler) GetCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
// Get customer ID from context (set by middleware)
customerID, exists := c.Get("customer_id")
if !exists {
logger.FromContext(ctx).Error("Customer ID not found in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Customer ID not found"),
}), "CustomerPointsHandler::GetCustomerTokens")
return
}
customerIDStr, ok := customerID.(string)
if !ok {
logger.FromContext(ctx).Error("Invalid customer ID type in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Invalid customer ID"),
}), "CustomerPointsHandler::GetCustomerTokens")
return
}
response, err := h.customerPointsService.GetCustomerTokens(ctx, customerIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerPointsHandler::GetCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "CustomerPointsHandler::GetCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerPointsHandler::GetCustomerTokens")
}
func (h *CustomerPointsHandler) GetCustomerWallet(c *gin.Context) {
ctx := c.Request.Context()
// Get customer ID from context (set by middleware)
customerID, exists := c.Get("customer_id")
if !exists {
logger.FromContext(ctx).Error("Customer ID not found in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Customer ID not found"),
}), "CustomerPointsHandler::GetCustomerWallet")
return
}
customerIDStr, ok := customerID.(string)
if !ok {
logger.FromContext(ctx).Error("Invalid customer ID type in context")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.AuthHandlerEntity, "Invalid customer ID"),
}), "CustomerPointsHandler::GetCustomerWallet")
return
}
response, err := h.customerPointsService.GetCustomerWallet(ctx, customerIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerPointsHandler::GetCustomerWallet -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "CustomerPointsHandler::GetCustomerWallet")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerPointsHandler::GetCustomerWallet")
}
func (h *CustomerPointsHandler) GetCustomerGames(c *gin.Context) {
ctx := c.Request.Context()
response, err := h.customerPointsService.GetCustomerGames(ctx)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerPointsHandler::GetCustomerGames -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "CustomerPointsHandler::GetCustomerGames")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerPointsHandler::GetCustomerGames")
}
func (h *CustomerPointsHandler) GetFerrisWheelGame(c *gin.Context) {
ctx := c.Request.Context()
response, err := h.customerPointsService.GetFerrisWheelGame(ctx)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("CustomerPointsHandler::GetFerrisWheelGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "CustomerPointsHandler::GetFerrisWheelGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "CustomerPointsHandler::GetFerrisWheelGame")
}
+527
View File
@@ -0,0 +1,527 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type GamificationHandler struct {
gamificationService service.GamificationService
gamificationValidator validator.GamificationValidator
}
func NewGamificationHandler(
gamificationService service.GamificationService,
gamificationValidator validator.GamificationValidator,
) *GamificationHandler {
return &GamificationHandler{
gamificationService: gamificationService,
gamificationValidator: gamificationValidator,
}
}
// Customer Points Handlers
func (h *GamificationHandler) CreateCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateCustomerPointsRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateCustomerPoints -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateCustomerPoints")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateCustomerPointsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateCustomerPoints -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateCustomerPoints")
return
}
response, err := h.gamificationService.CreateCustomerPoints(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::CreateCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateCustomerPoints")
}
func (h *GamificationHandler) GetCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerPoints -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetCustomerPoints")
return
}
response, err := h.gamificationService.GetCustomerPoints(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::GetCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetCustomerPoints")
}
func (h *GamificationHandler) GetCustomerPointsByCustomerID(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerPointsByCustomerID -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetCustomerPointsByCustomerID")
return
}
response, err := h.gamificationService.GetCustomerPointsByCustomerID(ctx, customerID)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerPointsByCustomerID -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::GetCustomerPointsByCustomerID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetCustomerPointsByCustomerID")
}
func (h *GamificationHandler) ListCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListCustomerPointsRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListCustomerPoints -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListCustomerPoints")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListCustomerPointsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListCustomerPoints -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListCustomerPoints")
return
}
response, err := h.gamificationService.ListCustomerPoints(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::ListCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListCustomerPoints")
}
func (h *GamificationHandler) UpdateCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerPoints -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerPoints")
return
}
var req contract.UpdateCustomerPointsRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerPoints -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerPoints")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateCustomerPointsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateCustomerPoints -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerPoints")
return
}
response, err := h.gamificationService.UpdateCustomerPoints(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::UpdateCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateCustomerPoints")
}
func (h *GamificationHandler) DeleteCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteCustomerPoints -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteCustomerPoints")
return
}
err = h.gamificationService.DeleteCustomerPoints(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::DeleteCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteCustomerPoints")
}
func (h *GamificationHandler) AddCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerPoints -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerPoints")
return
}
var req contract.AddCustomerPointsRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerPoints -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerPoints")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateAddCustomerPointsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::AddCustomerPoints -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerPoints")
return
}
response, err := h.gamificationService.AddCustomerPoints(ctx, customerID, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::AddCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::AddCustomerPoints")
}
func (h *GamificationHandler) DeductCustomerPoints(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerPoints -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerPointsEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerPoints")
return
}
var req contract.DeductCustomerPointsRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerPoints -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerPoints")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateDeductCustomerPointsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::DeductCustomerPoints -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerPoints")
return
}
response, err := h.gamificationService.DeductCustomerPoints(ctx, customerID, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerPointsEntity, err.Error())}), "GamificationHandler::DeductCustomerPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::DeductCustomerPoints")
}
// Play Game Handler
func (h *GamificationHandler) PlayGame(c *gin.Context) {
ctx := c.Request.Context()
var req contract.PlayGameRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::PlayGame -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::PlayGame")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidatePlayGameRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::PlayGame -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::PlayGame")
return
}
response, err := h.gamificationService.PlayGame(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::PlayGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::PlayGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::PlayGame")
}
// Additional handler methods for other gamification features
func (h *GamificationHandler) CreateCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateCustomerTokensRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateCustomerTokens -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateCustomerTokens")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateCustomerTokensRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateCustomerTokens -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateCustomerTokens")
return
}
response, err := h.gamificationService.CreateCustomerTokens(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::CreateCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateCustomerTokens")
}
func (h *GamificationHandler) GetCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerTokens -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetCustomerTokens")
return
}
response, err := h.gamificationService.GetCustomerTokens(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::GetCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetCustomerTokens")
}
func (h *GamificationHandler) GetCustomerTokensByCustomerIDAndType(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerTokensByCustomerIDAndType -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetCustomerTokensByCustomerIDAndType")
return
}
tokenType := c.Param("token_type")
response, err := h.gamificationService.GetCustomerTokensByCustomerIDAndType(ctx, customerID, tokenType)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetCustomerTokensByCustomerIDAndType -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::GetCustomerTokensByCustomerIDAndType")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetCustomerTokensByCustomerIDAndType")
}
func (h *GamificationHandler) ListCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListCustomerTokensRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListCustomerTokens -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListCustomerTokens")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListCustomerTokensRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListCustomerTokens -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListCustomerTokens")
return
}
response, err := h.gamificationService.ListCustomerTokens(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::ListCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListCustomerTokens")
}
func (h *GamificationHandler) UpdateCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerTokens -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerTokens")
return
}
var req contract.UpdateCustomerTokensRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerTokens -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerTokens")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateCustomerTokensRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateCustomerTokens -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateCustomerTokens")
return
}
response, err := h.gamificationService.UpdateCustomerTokens(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::UpdateCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateCustomerTokens")
}
func (h *GamificationHandler) DeleteCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteCustomerTokens -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteCustomerTokens")
return
}
err = h.gamificationService.DeleteCustomerTokens(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::DeleteCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteCustomerTokens")
}
func (h *GamificationHandler) AddCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerTokens -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerTokens")
return
}
tokenType := c.Param("token_type")
var req contract.AddCustomerTokensRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerTokens -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerTokens")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateAddCustomerTokensRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::AddCustomerTokens -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::AddCustomerTokens")
return
}
response, err := h.gamificationService.AddCustomerTokens(ctx, customerID, tokenType, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::AddCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::AddCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::AddCustomerTokens")
}
func (h *GamificationHandler) DeductCustomerTokens(c *gin.Context) {
ctx := c.Request.Context()
customerIDStr := c.Param("customer_id")
customerID, err := uuid.Parse(customerIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerTokens -> invalid customer ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.CustomerTokensEntity, "Invalid customer ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerTokens")
return
}
tokenType := c.Param("token_type")
var req contract.DeductCustomerTokensRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerTokens -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerTokens")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateDeductCustomerTokensRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::DeductCustomerTokens -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeductCustomerTokens")
return
}
response, err := h.gamificationService.DeductCustomerTokens(ctx, customerID, tokenType, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeductCustomerTokens -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.CustomerTokensEntity, err.Error())}), "GamificationHandler::DeductCustomerTokens")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::DeductCustomerTokens")
}
@@ -0,0 +1,709 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/util"
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
// Tier Handlers
func (h *GamificationHandler) CreateTier(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateTierRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateTier -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateTier")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateTierRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateTier -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateTier")
return
}
response, err := h.gamificationService.CreateTier(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateTier -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::CreateTier")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateTier")
}
func (h *GamificationHandler) GetTier(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetTier -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.TierEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetTier")
return
}
response, err := h.gamificationService.GetTier(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetTier -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::GetTier")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetTier")
}
func (h *GamificationHandler) ListTiers(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListTiersRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListTiers -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListTiers")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListTiersRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListTiers -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListTiers")
return
}
response, err := h.gamificationService.ListTiers(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListTiers -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::ListTiers")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListTiers")
}
func (h *GamificationHandler) UpdateTier(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateTier -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.TierEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateTier")
return
}
var req contract.UpdateTierRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateTier -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateTier")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateTierRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateTier -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateTier")
return
}
response, err := h.gamificationService.UpdateTier(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateTier -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::UpdateTier")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateTier")
}
func (h *GamificationHandler) DeleteTier(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteTier -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.TierEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteTier")
return
}
err = h.gamificationService.DeleteTier(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteTier -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::DeleteTier")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteTier")
}
func (h *GamificationHandler) GetTierByPoints(c *gin.Context) {
ctx := c.Request.Context()
pointsStr := c.Param("points")
points, err := strconv.ParseInt(pointsStr, 10, 64)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetTierByPoints -> invalid points")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.TierEntity, "Invalid points format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetTierByPoints")
return
}
response, err := h.gamificationService.GetTierByPoints(ctx, points)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetTierByPoints -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.TierEntity, err.Error())}), "GamificationHandler::GetTierByPoints")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetTierByPoints")
}
// Game Handlers
func (h *GamificationHandler) CreateGame(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateGameRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGame -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGame")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateGameRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateGame -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGame")
return
}
response, err := h.gamificationService.CreateGame(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::CreateGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateGame")
}
func (h *GamificationHandler) GetGame(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGame -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GameEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetGame")
return
}
response, err := h.gamificationService.GetGame(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::GetGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetGame")
}
func (h *GamificationHandler) ListGames(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListGamesRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGames -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGames")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListGamesRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListGames -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGames")
return
}
response, err := h.gamificationService.ListGames(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGames -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::ListGames")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListGames")
}
func (h *GamificationHandler) GetActiveGames(c *gin.Context) {
ctx := c.Request.Context()
response, err := h.gamificationService.GetActiveGames(ctx)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetActiveGames -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::GetActiveGames")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetActiveGames")
}
func (h *GamificationHandler) UpdateGame(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGame -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GameEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGame")
return
}
var req contract.UpdateGameRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGame -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGame")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateGameRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateGame -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGame")
return
}
response, err := h.gamificationService.UpdateGame(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::UpdateGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateGame")
}
func (h *GamificationHandler) DeleteGame(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteGame -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GameEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteGame")
return
}
err = h.gamificationService.DeleteGame(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteGame -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GameEntity, err.Error())}), "GamificationHandler::DeleteGame")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteGame")
}
// Game Prize Handlers
func (h *GamificationHandler) CreateGamePrize(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateGamePrizeRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGamePrize -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGamePrize")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateGamePrizeRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateGamePrize -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGamePrize")
return
}
response, err := h.gamificationService.CreateGamePrize(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGamePrize -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::CreateGamePrize")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateGamePrize")
}
func (h *GamificationHandler) GetGamePrize(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePrize -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePrizeEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetGamePrize")
return
}
response, err := h.gamificationService.GetGamePrize(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePrize -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::GetGamePrize")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetGamePrize")
}
func (h *GamificationHandler) ListGamePrizes(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListGamePrizesRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGamePrizes -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGamePrizes")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListGamePrizesRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListGamePrizes -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGamePrizes")
return
}
response, err := h.gamificationService.ListGamePrizes(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGamePrizes -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::ListGamePrizes")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListGamePrizes")
}
func (h *GamificationHandler) UpdateGamePrize(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGamePrize -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePrizeEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGamePrize")
return
}
var req contract.UpdateGamePrizeRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGamePrize -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGamePrize")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateGamePrizeRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateGamePrize -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateGamePrize")
return
}
response, err := h.gamificationService.UpdateGamePrize(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateGamePrize -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::UpdateGamePrize")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateGamePrize")
}
func (h *GamificationHandler) DeleteGamePrize(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteGamePrize -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePrizeEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteGamePrize")
return
}
err = h.gamificationService.DeleteGamePrize(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteGamePrize -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::DeleteGamePrize")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteGamePrize")
}
func (h *GamificationHandler) GetGamePrizesByGameID(c *gin.Context) {
ctx := c.Request.Context()
gameIDStr := c.Param("game_id")
gameID, err := uuid.Parse(gameIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePrizesByGameID -> invalid game ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePrizeEntity, "Invalid game ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetGamePrizesByGameID")
return
}
response, err := h.gamificationService.GetGamePrizesByGameID(ctx, gameID)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePrizesByGameID -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::GetGamePrizesByGameID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetGamePrizesByGameID")
}
func (h *GamificationHandler) GetAvailablePrizes(c *gin.Context) {
ctx := c.Request.Context()
gameIDStr := c.Param("game_id")
gameID, err := uuid.Parse(gameIDStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetAvailablePrizes -> invalid game ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePrizeEntity, "Invalid game ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetAvailablePrizes")
return
}
response, err := h.gamificationService.GetAvailablePrizes(ctx, gameID)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetAvailablePrizes -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePrizeEntity, err.Error())}), "GamificationHandler::GetAvailablePrizes")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetAvailablePrizes")
}
// Game Play Handlers
func (h *GamificationHandler) CreateGamePlay(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateGamePlayRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGamePlay -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGamePlay")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateGamePlayRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateGamePlay -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateGamePlay")
return
}
response, err := h.gamificationService.CreateGamePlay(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateGamePlay -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePlayEntity, err.Error())}), "GamificationHandler::CreateGamePlay")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateGamePlay")
}
func (h *GamificationHandler) GetGamePlay(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePlay -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.GamePlayEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetGamePlay")
return
}
response, err := h.gamificationService.GetGamePlay(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetGamePlay -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePlayEntity, err.Error())}), "GamificationHandler::GetGamePlay")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetGamePlay")
}
func (h *GamificationHandler) ListGamePlays(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListGamePlaysRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGamePlays -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGamePlays")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListGamePlaysRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListGamePlays -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListGamePlays")
return
}
response, err := h.gamificationService.ListGamePlays(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListGamePlays -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.GamePlayEntity, err.Error())}), "GamificationHandler::ListGamePlays")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListGamePlays")
}
// Omset Tracker Handlers
func (h *GamificationHandler) CreateOmsetTracker(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateOmsetTrackerRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateOmsetTracker -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateOmsetTracker")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateCreateOmsetTrackerRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::CreateOmsetTracker -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::CreateOmsetTracker")
return
}
response, err := h.gamificationService.CreateOmsetTracker(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::CreateOmsetTracker -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.OmsetTrackerEntity, err.Error())}), "GamificationHandler::CreateOmsetTracker")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::CreateOmsetTracker")
}
func (h *GamificationHandler) GetOmsetTracker(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetOmsetTracker -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.OmsetTrackerEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::GetOmsetTracker")
return
}
response, err := h.gamificationService.GetOmsetTracker(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::GetOmsetTracker -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.OmsetTrackerEntity, err.Error())}), "GamificationHandler::GetOmsetTracker")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::GetOmsetTracker")
}
func (h *GamificationHandler) ListOmsetTrackers(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListOmsetTrackerRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListOmsetTrackers -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListOmsetTrackers")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateListOmsetTrackerRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::ListOmsetTrackers -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::ListOmsetTrackers")
return
}
response, err := h.gamificationService.ListOmsetTrackers(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::ListOmsetTrackers -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.OmsetTrackerEntity, err.Error())}), "GamificationHandler::ListOmsetTrackers")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::ListOmsetTrackers")
}
func (h *GamificationHandler) UpdateOmsetTracker(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateOmsetTracker -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.OmsetTrackerEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateOmsetTracker")
return
}
var req contract.UpdateOmsetTrackerRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateOmsetTracker -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateOmsetTracker")
return
}
validationError, validationErrorCode := h.gamificationValidator.ValidateUpdateOmsetTrackerRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("GamificationHandler::UpdateOmsetTracker -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::UpdateOmsetTracker")
return
}
response, err := h.gamificationService.UpdateOmsetTracker(ctx, id, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::UpdateOmsetTracker -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.OmsetTrackerEntity, err.Error())}), "GamificationHandler::UpdateOmsetTracker")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "GamificationHandler::UpdateOmsetTracker")
}
func (h *GamificationHandler) DeleteOmsetTracker(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteOmsetTracker -> invalid ID")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.OmsetTrackerEntity, "Invalid ID format")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "GamificationHandler::DeleteOmsetTracker")
return
}
err = h.gamificationService.DeleteOmsetTracker(ctx, id)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("GamificationHandler::DeleteOmsetTracker -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.OmsetTrackerEntity, err.Error())}), "GamificationHandler::DeleteOmsetTracker")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(nil), "GamificationHandler::DeleteOmsetTracker")
}
+142 -62
View File
@@ -18,9 +18,7 @@ type IngredientHandler struct {
} }
func NewIngredientHandler(ingredientService IngredientService) *IngredientHandler { func NewIngredientHandler(ingredientService IngredientService) *IngredientHandler {
return &IngredientHandler{ return &IngredientHandler{ingredientService: ingredientService}
ingredientService: ingredientService,
}
} }
func (h *IngredientHandler) Create(c *gin.Context) { func (h *IngredientHandler) Create(c *gin.Context) {
@@ -29,53 +27,53 @@ func (h *IngredientHandler) Create(c *gin.Context) {
var request models.CreateIngredientRequest var request models.CreateIngredientRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("IngredientHandler::Create -> request binding failed") logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()) util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Create") contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Create")
return return
} }
request.OrganizationID = contextInfo.OrganizationID request.OrganizationID = contextInfo.OrganizationID
ingredientResponse, err := h.ingredientService.CreateIngredient(ctx, &request) resp, err := h.ingredientService.CreateIngredient(ctx, &request)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> Failed to create ingredient from service") logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> failed")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()) util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Create") contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Create")
return return
} }
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(ingredientResponse), "IngredientHandler::Create") util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::Create")
} }
func (h *IngredientHandler) GetByID(c *gin.Context) { func (h *IngredientHandler) GetByID(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
idStr := c.Param("id") id, err := uuid.Parse(c.Param("id"))
id, err := uuid.Parse(idStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetByID -> Invalid ingredient ID") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetByID") }), "IngredientHandler::GetByID")
return return
} }
ingredientResponse, err := h.ingredientService.GetIngredientByID(ctx, id) resp, err := h.ingredientService.GetIngredientByID(ctx, id)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetByID -> Failed to get ingredient from service") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Ingredient not found") contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Ingredient not found"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetByID") }), "IngredientHandler::GetByID")
return return
} }
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(ingredientResponse), "IngredientHandler::GetByID") util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::GetByID")
} }
func (h *IngredientHandler) GetAll(c *gin.Context) { func (h *IngredientHandler) GetAll(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
// Get query parameters
pageStr := c.DefaultQuery("page", "1") pageStr := c.DefaultQuery("page", "1")
limitStr := c.DefaultQuery("limit", "10") limitStr := c.DefaultQuery("limit", "10")
search := c.Query("search") search := c.Query("search")
@@ -83,95 +81,177 @@ func (h *IngredientHandler) GetAll(c *gin.Context) {
page, err := strconv.Atoi(pageStr) page, err := strconv.Atoi(pageStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid page parameter") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid page parameter") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid page parameter"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll") }), "IngredientHandler::GetAll")
return return
} }
limit, err := strconv.Atoi(limitStr) limit, err := strconv.Atoi(limitStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid limit parameter") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid limit parameter") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid limit parameter"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll") }), "IngredientHandler::GetAll")
return return
} }
var outletID *uuid.UUID var outletID *uuid.UUID
if outletIDStr != "" { if outletIDStr != "" {
parsedOutletID, err := uuid.Parse(outletIDStr) parsed, err := uuid.Parse(outletIDStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid outlet ID") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll") }), "IngredientHandler::GetAll")
return return
} }
outletID = &parsedOutletID outletID = &parsed
} }
ingredientResponse, err := h.ingredientService.ListIngredients(ctx, contextInfo.OrganizationID, outletID, page, limit, search) resp, err := h.ingredientService.ListIngredients(ctx, contextInfo.OrganizationID, outletID, page, limit, search)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Failed to get ingredients from service") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, "Failed to get ingredients") contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, "Failed to get ingredients"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll") }), "IngredientHandler::GetAll")
return return
} }
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(ingredientResponse), "IngredientHandler::GetAll") util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::GetAll")
} }
func (h *IngredientHandler) Update(c *gin.Context) { func (h *IngredientHandler) Update(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
idStr := c.Param("id") id, err := uuid.Parse(c.Param("id"))
id, err := uuid.Parse(idStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> Invalid ingredient ID") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update") }), "IngredientHandler::Update")
return return
} }
var request models.UpdateIngredientRequest var request models.UpdateIngredientRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> request binding failed") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body") contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update") }), "IngredientHandler::Update")
return return
} }
ingredientResponse, err := h.ingredientService.UpdateIngredient(ctx, id, &request) resp, err := h.ingredientService.UpdateIngredient(ctx, id, &request)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> Failed to update ingredient from service") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()) contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update") }), "IngredientHandler::Update")
return return
} }
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(ingredientResponse), "IngredientHandler::Update") util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::Update")
} }
func (h *IngredientHandler) Delete(c *gin.Context) { func (h *IngredientHandler) Delete(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
idStr := c.Param("id") id, err := uuid.Parse(c.Param("id"))
id, err := uuid.Parse(idStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Delete -> Invalid ingredient ID") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID") contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Delete") }), "IngredientHandler::Delete")
return return
} }
err = h.ingredientService.DeleteIngredient(ctx, id) if err := h.ingredientService.DeleteIngredient(ctx, id); err != nil {
if err != nil { util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Delete -> Failed to delete ingredient from service") contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()) }), "IngredientHandler::Delete")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Delete")
return return
} }
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(map[string]interface{}{ util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(map[string]string{
"message": "Ingredient deleted successfully", "message": "Ingredient deleted successfully",
}), "IngredientHandler::Delete") }), "IngredientHandler::Delete")
} }
// AddCompositions adds multiple composition items to a semi-finished ingredient.
func (h *IngredientHandler) AddCompositions(c *gin.Context) {
ctx := c.Request.Context()
id, err := uuid.Parse(c.Param("id"))
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "invalid ingredient id"),
}), "IngredientHandler::AddCompositions")
return
}
var req models.AddIngredientCompositionsRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::AddCompositions")
return
}
resp, err := h.ingredientService.AddCompositions(ctx, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::AddCompositions")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::AddCompositions")
}
// UpdateComposition updates quantity/outlet of a single composition entry.
func (h *IngredientHandler) UpdateComposition(c *gin.Context) {
ctx := c.Request.Context()
id, err := uuid.Parse(c.Param("composition_id"))
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "invalid composition id"),
}), "IngredientHandler::UpdateComposition")
return
}
var req models.UpdateIngredientCompositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::UpdateComposition")
return
}
resp, err := h.ingredientService.UpdateComposition(ctx, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::UpdateComposition")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::UpdateComposition")
}
// DeleteComposition removes a single composition entry.
func (h *IngredientHandler) DeleteComposition(c *gin.Context) {
ctx := c.Request.Context()
id, err := uuid.Parse(c.Param("composition_id"))
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "invalid composition id"),
}), "IngredientHandler::DeleteComposition")
return
}
resp, err := h.ingredientService.DeleteComposition(ctx, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::DeleteComposition")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(resp), "IngredientHandler::DeleteComposition")
}
+3
View File
@@ -13,4 +13,7 @@ type IngredientService interface {
DeleteIngredient(ctx context.Context, id uuid.UUID) error DeleteIngredient(ctx context.Context, id uuid.UUID) error
GetIngredientByID(ctx context.Context, id uuid.UUID) (*models.IngredientResponse, error) GetIngredientByID(ctx context.Context, id uuid.UUID) (*models.IngredientResponse, error)
ListIngredients(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) (*models.PaginatedResponse[models.IngredientResponse], error) ListIngredients(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) (*models.PaginatedResponse[models.IngredientResponse], error)
UpdateComposition(ctx context.Context, id uuid.UUID, req *models.UpdateIngredientCompositionRequest) (*models.IngredientCompositionResponse, error)
DeleteComposition(ctx context.Context, id uuid.UUID) (*models.IngredientResponse, error)
AddCompositions(ctx context.Context, parentID uuid.UUID, req *models.AddIngredientCompositionsRequest) (*models.AddIngredientCompositionsResponse, error)
} }
@@ -0,0 +1,278 @@
package handler
import (
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type IngredientUnitConverterHandler struct {
converterService service.IngredientUnitConverterService
converterValidator validator.IngredientUnitConverterValidator
}
func NewIngredientUnitConverterHandler(
converterService service.IngredientUnitConverterService,
converterValidator validator.IngredientUnitConverterValidator,
) *IngredientUnitConverterHandler {
return &IngredientUnitConverterHandler{
converterService: converterService,
converterValidator: converterValidator,
}
}
func (h *IngredientUnitConverterHandler) CreateIngredientUnitConverter(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateIngredientUnitConverterRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("IngredientUnitConverterHandler::CreateIngredientUnitConverter -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::CreateIngredientUnitConverter")
return
}
validationError, validationErrorCode := h.converterValidator.ValidateCreateIngredientUnitConverterRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::CreateIngredientUnitConverter")
return
}
converterResponse := h.converterService.CreateIngredientUnitConverter(ctx, contextInfo, &req)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::CreateIngredientUnitConverter -> Failed to create ingredient unit converter from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::CreateIngredientUnitConverter")
}
func (h *IngredientUnitConverterHandler) UpdateIngredientUnitConverter(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
converterIDStr := c.Param("id")
converterID, err := uuid.Parse(converterIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::UpdateIngredientUnitConverter -> Invalid converter ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid converter ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::UpdateIngredientUnitConverter")
return
}
var req contract.UpdateIngredientUnitConverterRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::UpdateIngredientUnitConverter -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::UpdateIngredientUnitConverter")
return
}
validationError, validationErrorCode := h.converterValidator.ValidateUpdateIngredientUnitConverterRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::UpdateIngredientUnitConverter")
return
}
converterResponse := h.converterService.UpdateIngredientUnitConverter(ctx, contextInfo, converterID, &req)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::UpdateIngredientUnitConverter -> Failed to update ingredient unit converter from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::UpdateIngredientUnitConverter")
}
func (h *IngredientUnitConverterHandler) DeleteIngredientUnitConverter(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
converterIDStr := c.Param("id")
converterID, err := uuid.Parse(converterIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::DeleteIngredientUnitConverter -> Invalid converter ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid converter ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::DeleteIngredientUnitConverter")
return
}
converterResponse := h.converterService.DeleteIngredientUnitConverter(ctx, contextInfo, converterID)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::DeleteIngredientUnitConverter -> Failed to delete ingredient unit converter from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::DeleteIngredientUnitConverter")
}
func (h *IngredientUnitConverterHandler) GetIngredientUnitConverter(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
converterIDStr := c.Param("id")
converterID, err := uuid.Parse(converterIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::GetIngredientUnitConverter -> Invalid converter ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid converter ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::GetIngredientUnitConverter")
return
}
converterResponse := h.converterService.GetIngredientUnitConverter(ctx, contextInfo, converterID)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::GetIngredientUnitConverter -> Failed to get ingredient unit converter from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::GetIngredientUnitConverter")
}
func (h *IngredientUnitConverterHandler) ListIngredientUnitConverters(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListIngredientUnitConvertersRequest{
Page: 1,
Limit: 10,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
}
}
if limitStr := c.Query("limit"); limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil {
req.Limit = limit
}
}
if search := c.Query("search"); search != "" {
req.Search = search
}
if ingredientIDStr := c.Query("ingredient_id"); ingredientIDStr != "" {
if ingredientID, err := uuid.Parse(ingredientIDStr); err == nil {
req.IngredientID = &ingredientID
}
}
if fromUnitIDStr := c.Query("from_unit_id"); fromUnitIDStr != "" {
if fromUnitID, err := uuid.Parse(fromUnitIDStr); err == nil {
req.FromUnitID = &fromUnitID
}
}
if toUnitIDStr := c.Query("to_unit_id"); toUnitIDStr != "" {
if toUnitID, err := uuid.Parse(toUnitIDStr); err == nil {
req.ToUnitID = &toUnitID
}
}
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
req.IsActive = &isActive
}
}
validationError, validationErrorCode := h.converterValidator.ValidateListIngredientUnitConvertersRequest(req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::ListIngredientUnitConverters")
return
}
converterResponse := h.converterService.ListIngredientUnitConverters(ctx, contextInfo, req)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::ListIngredientUnitConverters -> Failed to list ingredient unit converters from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::ListIngredientUnitConverters")
}
func (h *IngredientUnitConverterHandler) GetConvertersForIngredient(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
ingredientIDStr := c.Param("ingredient_id")
ingredientID, err := uuid.Parse(ingredientIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::GetConvertersForIngredient -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::GetConvertersForIngredient")
return
}
converterResponse := h.converterService.GetConvertersForIngredient(ctx, contextInfo, ingredientID)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::GetConvertersForIngredient -> Failed to get converters for ingredient from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::GetConvertersForIngredient")
}
func (h *IngredientUnitConverterHandler) ConvertUnit(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.ConvertUnitRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("IngredientUnitConverterHandler::ConvertUnit -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::ConvertUnit")
return
}
validationError, validationErrorCode := h.converterValidator.ValidateConvertUnitRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::ConvertUnit")
return
}
converterResponse := h.converterService.ConvertUnit(ctx, contextInfo, &req)
if converterResponse.HasErrors() {
errorResp := converterResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::ConvertUnit -> Failed to convert unit from service")
}
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::ConvertUnit")
}
func (h *IngredientUnitConverterHandler) GetUnitsByIngredientID(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
ingredientIDStr := c.Param("ingredient_id")
ingredientID, err := uuid.Parse(ingredientIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientUnitConverterHandler::GetUnitsByIngredientID -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientUnitConverterHandler::GetUnitsByIngredientID")
return
}
unitsResponse := h.converterService.GetUnitsByIngredientID(ctx, contextInfo, ingredientID)
if unitsResponse.HasErrors() {
errorResp := unitsResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("IngredientUnitConverterHandler::GetUnitsByIngredientID -> Failed to get units for ingredient from service")
}
util.HandleResponse(c.Writer, c.Request, unitsResponse, "IngredientUnitConverterHandler::GetUnitsByIngredientID")
}
+162 -3
View File
@@ -2,11 +2,13 @@ package handler
import ( import (
"strconv" "strconv"
"time"
"apskel-pos-be/internal/appcontext" "apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/constants" "apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract" "apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger" "apskel-pos-be/internal/logger"
"apskel-pos-be/internal/models"
"apskel-pos-be/internal/service" "apskel-pos-be/internal/service"
"apskel-pos-be/internal/util" "apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator" "apskel-pos-be/internal/validator"
@@ -138,13 +140,14 @@ func (h *InventoryHandler) GetInventory(c *gin.Context) {
func (h *InventoryHandler) ListInventory(c *gin.Context) { func (h *InventoryHandler) ListInventory(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListInventoryRequest{ req := &contract.ListInventoryRequest{
Page: 1, Page: 1,
Limit: 10, Limit: 10,
OutletID: &contextInfo.OutletID,
} }
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" { if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil { if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page req.Page = page
@@ -235,6 +238,34 @@ func (h *InventoryHandler) AdjustInventory(c *gin.Context) {
util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::AdjustInventory") util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::AdjustInventory")
} }
func (h *InventoryHandler) RestockInventory(c *gin.Context) {
ctx := c.Request.Context()
var req contract.RestockInventoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("InventoryHandler::RestockInventory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::RestockInventory")
return
}
// TODO: Add validation for restock request
// validationError, validationErrorCode := h.inventoryValidator.ValidateRestockInventoryRequest(&req)
// if validationError != nil {
// validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
// util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::RestockInventory")
// return
// }
inventoryResponse := h.inventoryService.RestockInventory(ctx, &req)
if inventoryResponse.HasErrors() {
errorResp := inventoryResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::RestockInventory -> Failed to restock inventory from service")
}
util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::RestockInventory")
}
func (h *InventoryHandler) GetLowStockItems(c *gin.Context) { func (h *InventoryHandler) GetLowStockItems(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
@@ -276,3 +307,131 @@ func (h *InventoryHandler) GetZeroStockItems(c *gin.Context) {
util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::GetZeroStockItems") util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::GetZeroStockItems")
} }
func (h *InventoryHandler) GetInventoryReportSummary(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
outletIDStr := c.Param("outlet_id")
outletID, err := uuid.Parse(outletIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportSummary -> Invalid outlet ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportSummary")
return
}
// Parse date range parameters for summary
var dateFrom, dateTo *time.Time
if dateFromStr := c.Query("date_from"); dateFromStr != "" {
if parsedDateFrom, err := time.Parse("2006-01-02", dateFromStr); err == nil {
dateFrom = &parsedDateFrom
}
}
if dateToStr := c.Query("date_to"); dateToStr != "" {
if parsedDateTo, err := time.Parse("2006-01-02", dateToStr); err == nil {
dateTo = &parsedDateTo
}
}
summary, err := h.inventoryService.GetInventoryReportSummary(ctx, outletID, contextInfo.OrganizationID, dateFrom, dateTo)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportSummary -> Failed to get inventory report summary from service")
responseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{responseError}), "InventoryHandler::GetInventoryReportSummary")
return
}
response := contract.BuildSuccessResponse(summary)
util.HandleResponse(c.Writer, c.Request, response, "InventoryHandler::GetInventoryReportSummary")
}
func (h *InventoryHandler) GetInventoryReportDetails(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
filter := &models.InventoryReportFilter{}
if outletIDStr := c.Param("outlet_id"); outletIDStr != "" {
outletID, err := uuid.Parse(outletIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Invalid outlet ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
return
}
filter.OutletID = &outletID
} else {
logger.FromContext(ctx).Error("InventoryHandler::GetInventoryReportDetails -> Missing outlet_id parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "outlet_id is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
return
}
if categoryIDStr := c.Query("category_id"); categoryIDStr != "" {
categoryID, err := uuid.Parse(categoryIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Invalid category ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid category ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::GetInventoryReportDetails")
return
}
filter.CategoryID = &categoryID
}
// Parse show_low_stock (optional)
if showLowStockStr := c.Query("show_low_stock"); showLowStockStr != "" {
if showLowStock, err := strconv.ParseBool(showLowStockStr); err == nil {
filter.ShowLowStock = &showLowStock
}
}
// Parse show_zero_stock (optional)
if showZeroStockStr := c.Query("show_zero_stock"); showZeroStockStr != "" {
if showZeroStock, err := strconv.ParseBool(showZeroStockStr); err == nil {
filter.ShowZeroStock = &showZeroStock
}
}
// Parse search (optional)
if search := c.Query("search"); search != "" {
filter.Search = &search
}
// Parse limit (optional)
if limitStr := c.Query("limit"); limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil && limit > 0 {
filter.Limit = &limit
}
}
// Parse offset (optional)
if offsetStr := c.Query("offset"); offsetStr != "" {
if offset, err := strconv.Atoi(offsetStr); err == nil && offset >= 0 {
filter.Offset = &offset
}
}
dateFromStr := c.Query("date_from")
dateToStr := c.Query("date_to")
if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(dateFromStr, dateToStr); err == nil {
if fromTime != nil {
filter.DateFrom = fromTime
}
if toTime != nil {
filter.DateTo = toTime
}
}
report, err := h.inventoryService.GetInventoryReportDetails(ctx, filter, contextInfo.OrganizationID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("InventoryHandler::GetInventoryReportDetails -> Failed to get inventory report details from service")
responseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{responseError}), "InventoryHandler::GetInventoryReportDetails")
return
}
response := contract.BuildSuccessResponse(report)
util.HandleResponse(c.Writer, c.Request, response, "InventoryHandler::GetInventoryReportDetails")
}
+4
View File
@@ -122,6 +122,9 @@ func (h *OrderHandler) AddToOrder(c *gin.Context) {
func (h *OrderHandler) ListOrders(c *gin.Context) { func (h *OrderHandler) ListOrders(c *gin.Context) {
var query contract.ListOrdersQuery var query contract.ListOrdersQuery
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
if err := c.ShouldBindQuery(&query); err != nil { if err := c.ShouldBindQuery(&query); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_query_parameters", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders") util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_query_parameters", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
return return
@@ -133,6 +136,7 @@ func (h *OrderHandler) ListOrders(c *gin.Context) {
return return
} }
modelReq.OrganizationID = &contextInfo.OrganizationID
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")
@@ -0,0 +1,201 @@
package handler
import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type OrderIngredientTransactionHandler struct {
service OrderIngredientTransactionService
validator validator.OrderIngredientTransactionValidator
}
func NewOrderIngredientTransactionHandler(service OrderIngredientTransactionService, validator validator.OrderIngredientTransactionValidator) *OrderIngredientTransactionHandler {
return &OrderIngredientTransactionHandler{
service: service,
validator: validator,
}
}
func (h *OrderIngredientTransactionHandler) CreateOrderIngredientTransaction(c *gin.Context) {
var req contract.CreateOrderIngredientTransactionRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.CreateOrderIngredientTransaction(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) GetOrderIngredientTransactionByID(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.GetOrderIngredientTransactionByID(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) UpdateOrderIngredientTransaction(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "OrderIngredientTransactionHandler")
return
}
var req contract.UpdateOrderIngredientTransactionRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.UpdateOrderIngredientTransaction(c, id, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) DeleteOrderIngredientTransaction(c *gin.Context) {
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ID format"}}), "OrderIngredientTransactionHandler")
return
}
err = h.service.DeleteOrderIngredientTransaction(c, id)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{"message": "Order ingredient transaction deleted successfully"}), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) ListOrderIngredientTransactions(c *gin.Context) {
var req contract.ListOrderIngredientTransactionsRequest
if err := c.ShouldBindQuery(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
response, total, err := h.service.ListOrderIngredientTransactions(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(gin.H{
"data": response,
"total": total,
"page": req.Page,
"limit": req.Limit,
}), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) GetOrderIngredientTransactionsByOrder(c *gin.Context) {
orderIDStr := c.Param("order_id")
orderID, err := uuid.Parse(orderIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid order ID format"}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.GetOrderIngredientTransactionsByOrder(c, orderID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) GetOrderIngredientTransactionsByOrderItem(c *gin.Context) {
orderItemIDStr := c.Param("order_item_id")
orderItemID, err := uuid.Parse(orderItemIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid order item ID format"}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.GetOrderIngredientTransactionsByOrderItem(c, orderItemID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) GetOrderIngredientTransactionsByIngredient(c *gin.Context) {
ingredientIDStr := c.Param("ingredient_id")
ingredientID, err := uuid.Parse(ingredientIDStr)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: "Invalid ingredient ID format"}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.GetOrderIngredientTransactionsByIngredient(c, ingredientID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) GetOrderIngredientTransactionSummary(c *gin.Context) {
var req contract.ListOrderIngredientTransactionsRequest
if err := c.ShouldBindQuery(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.GetOrderIngredientTransactionSummary(c, &req)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
func (h *OrderIngredientTransactionHandler) BulkCreateOrderIngredientTransactions(c *gin.Context) {
var req struct {
Transactions []*contract.CreateOrderIngredientTransactionRequest `json:"transactions" validate:"required,min=1"`
}
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
response, err := h.service.BulkCreateOrderIngredientTransactions(c, req.Transactions)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{{Cause: err.Error()}}), "OrderIngredientTransactionHandler")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "OrderIngredientTransactionHandler")
}
@@ -0,0 +1,21 @@
package handler
import (
"apskel-pos-be/internal/contract"
"context"
"github.com/google/uuid"
)
type OrderIngredientTransactionService interface {
CreateOrderIngredientTransaction(ctx context.Context, req *contract.CreateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionByID(ctx context.Context, id uuid.UUID) (*contract.OrderIngredientTransactionResponse, error)
UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *contract.UpdateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error)
DeleteOrderIngredientTransaction(ctx context.Context, id uuid.UUID) error
ListOrderIngredientTransactions(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionResponse, int64, error)
GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error)
GetOrderIngredientTransactionSummary(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionSummary, error)
BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*contract.CreateOrderIngredientTransactionRequest) ([]*contract.OrderIngredientTransactionResponse, error)
}
+31
View File
@@ -102,6 +102,37 @@ func (h *OutletHandler) GetOutlet(c *gin.Context) {
util.HandleResponse(c.Writer, c.Request, outletResponse, "OutletHandler::GetOutlet") util.HandleResponse(c.Writer, c.Request, outletResponse, "OutletHandler::GetOutlet")
} }
func (h *OutletHandler) CreateOutlet(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateOutletRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("OutletHandler::CreateOutlet -> Failed to bind JSON")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid request body")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "OutletHandler::CreateOutlet")
return
}
req.OrganizationID = contextInfo.OrganizationID
validationError, validationErrorCode := h.outletValidator.ValidateCreateOutletRequest(&req)
if validationError != nil {
logger.FromContext(ctx).WithError(validationError).Error("OutletHandler::CreateOutlet -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "OutletHandler::CreateOutlet")
return
}
outletResponse := h.outletService.CreateOutlet(ctx, &req)
if outletResponse.HasErrors() {
errorResp := outletResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("OutletHandler::CreateOutlet -> Failed to create outlet from service")
}
util.HandleResponse(c.Writer, c.Request, outletResponse, "OutletHandler::CreateOutlet")
}
func (h *OutletHandler) UpdateOutlet(c *gin.Context) { func (h *OutletHandler) UpdateOutlet(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
+3 -7
View File
@@ -51,7 +51,7 @@ func (h *PaymentMethodHandler) CreatePaymentMethod(c *gin.Context) {
req.OrganizationID = contextInfo.OrganizationID req.OrganizationID = contextInfo.OrganizationID
req.OutletID = contextInfo.OutletID req.OutletID = contextInfo.OutletID
paymentMethodResponse := h.paymentMethodService.CreatePaymentMethod(ctx, contextInfo, &req) paymentMethodResponse := h.paymentMethodService.CreatePaymentMethod(ctx, contextInfo, &req)
if paymentMethodResponse.HasErrors() { if paymentMethodResponse.HasErrors() {
errorResp := paymentMethodResponse.GetErrors()[0] errorResp := paymentMethodResponse.GetErrors()[0]
@@ -84,13 +84,13 @@ func (h *PaymentMethodHandler) GetPaymentMethod(c *gin.Context) {
func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) { func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListPaymentMethodsRequest{ req := &contract.ListPaymentMethodsRequest{
Page: 1, Page: 1,
Limit: 10, Limit: 10,
} }
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" { if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil { if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page req.Page = page
@@ -111,11 +111,7 @@ func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) {
req.Type = &paymentMethodType req.Type = &paymentMethodType
} }
if organizationIDStr := c.Query("organization_id"); organizationIDStr != "" { req.OrganizationID = &contextInfo.OrganizationID
if organizationID, err := uuid.Parse(organizationIDStr); err == nil {
req.OrganizationID = &organizationID
}
}
if isActiveStr := c.Query("is_active"); isActiveStr != "" { if isActiveStr := c.Query("is_active"); isActiveStr != "" {
if isActive, err := strconv.ParseBool(isActiveStr); err == nil { if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
+13 -18
View File
@@ -5,7 +5,6 @@ import (
"apskel-pos-be/internal/constants" "apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract" "apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger" "apskel-pos-be/internal/logger"
"apskel-pos-be/internal/models"
"apskel-pos-be/internal/service" "apskel-pos-be/internal/service"
"apskel-pos-be/internal/util" "apskel-pos-be/internal/util"
"net/http" "net/http"
@@ -28,7 +27,7 @@ func (h *ProductRecipeHandler) Create(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
var request models.CreateProductRecipeRequest var request contract.CreateProductRecipeRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> request binding failed") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@@ -75,6 +74,7 @@ func (h *ProductRecipeHandler) GetByProductID(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
// Parse product ID from URL parameter
productIDStr := c.Param("product_id") productIDStr := c.Param("product_id")
productID, err := uuid.Parse(productIDStr) productID, err := uuid.Parse(productIDStr)
if err != nil { if err != nil {
@@ -84,10 +84,9 @@ func (h *ProductRecipeHandler) GetByProductID(c *gin.Context) {
return return
} }
// Check if variant_id is provided // Parse optional variant ID from query parameter
variantIDStr := c.Query("variant_id")
var variantID *uuid.UUID var variantID *uuid.UUID
if variantIDStr != "" { if variantIDStr := c.Query("variant_id"); variantIDStr != "" {
parsed, err := uuid.Parse(variantIDStr) parsed, err := uuid.Parse(variantIDStr)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid variant ID") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid variant ID")
@@ -98,15 +97,14 @@ func (h *ProductRecipeHandler) GetByProductID(c *gin.Context) {
variantID = &parsed variantID = &parsed
} }
var recipes []*models.ProductRecipeResponse // Create request object
if variantIDStr != "" { request := &contract.GetProductRecipeByProductIDRequest{
// Get by product and variant ID ProductID: productID,
recipes, err = h.productRecipeService.GetByProductAndVariantID(ctx, productID, variantID, contextInfo.OrganizationID) VariantID: variantID,
} else {
// Get by product ID only
recipes, err = h.productRecipeService.GetByProductID(ctx, productID, contextInfo.OrganizationID)
} }
// Call service
recipes, err := h.productRecipeService.GetByProductID(ctx, request, contextInfo.OrganizationID)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Failed to get product recipes") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Failed to get product recipes")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
@@ -154,7 +152,7 @@ func (h *ProductRecipeHandler) Update(c *gin.Context) {
return return
} }
var request models.UpdateProductRecipeRequest var request contract.UpdateProductRecipeRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> request binding failed") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@@ -204,10 +202,7 @@ func (h *ProductRecipeHandler) BulkCreate(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx) contextInfo := appcontext.FromGinContext(ctx)
var request struct { var request contract.BulkCreateProductRecipeRequest
Recipes []models.CreateProductRecipeRequest `json:"recipes" validate:"required,min=1"`
}
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> request binding failed") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@@ -215,7 +210,7 @@ func (h *ProductRecipeHandler) BulkCreate(c *gin.Context) {
return return
} }
recipes, err := h.productRecipeService.BulkCreate(ctx, contextInfo.OrganizationID, request.Recipes) recipes, err := h.productRecipeService.BulkCreate(ctx, contextInfo.OrganizationID, &request)
if err != nil { if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> Failed to bulk create product recipes") logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> Failed to bulk create product recipes")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()) validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
+267
View File
@@ -0,0 +1,267 @@
package handler
import (
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/util"
"strconv"
"time"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type PurchaseOrderHandler struct {
purchaseOrderService service.PurchaseOrderService
purchaseOrderValidator validator.PurchaseOrderValidator
}
func NewPurchaseOrderHandler(
purchaseOrderService service.PurchaseOrderService,
purchaseOrderValidator validator.PurchaseOrderValidator,
) *PurchaseOrderHandler {
return &PurchaseOrderHandler{
purchaseOrderService: purchaseOrderService,
purchaseOrderValidator: purchaseOrderValidator,
}
}
func (h *PurchaseOrderHandler) CreatePurchaseOrder(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreatePurchaseOrderRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("PurchaseOrderHandler::CreatePurchaseOrder -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::CreatePurchaseOrder")
return
}
validationError, validationErrorCode := h.purchaseOrderValidator.ValidateCreatePurchaseOrderRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::CreatePurchaseOrder")
return
}
poResponse := h.purchaseOrderService.CreatePurchaseOrder(ctx, contextInfo, &req)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::CreatePurchaseOrder -> Failed to create purchase order from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::CreatePurchaseOrder")
}
func (h *PurchaseOrderHandler) UpdatePurchaseOrder(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
poIDStr := c.Param("id")
poID, err := uuid.Parse(poIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("PurchaseOrderHandler::UpdatePurchaseOrder -> Invalid purchase order ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid purchase order ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::UpdatePurchaseOrder")
return
}
var req contract.UpdatePurchaseOrderRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("PurchaseOrderHandler::UpdatePurchaseOrder -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::UpdatePurchaseOrder")
return
}
validationError, validationErrorCode := h.purchaseOrderValidator.ValidateUpdatePurchaseOrderRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::UpdatePurchaseOrder")
return
}
poResponse := h.purchaseOrderService.UpdatePurchaseOrder(ctx, contextInfo, poID, &req)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::UpdatePurchaseOrder -> Failed to update purchase order from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::UpdatePurchaseOrder")
}
func (h *PurchaseOrderHandler) DeletePurchaseOrder(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
poIDStr := c.Param("id")
poID, err := uuid.Parse(poIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("PurchaseOrderHandler::DeletePurchaseOrder -> Invalid purchase order ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid purchase order ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::DeletePurchaseOrder")
return
}
poResponse := h.purchaseOrderService.DeletePurchaseOrder(ctx, contextInfo, poID)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::DeletePurchaseOrder -> Failed to delete purchase order from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::DeletePurchaseOrder")
}
func (h *PurchaseOrderHandler) GetPurchaseOrder(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
poIDStr := c.Param("id")
poID, err := uuid.Parse(poIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("PurchaseOrderHandler::GetPurchaseOrder -> Invalid purchase order ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid purchase order ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::GetPurchaseOrder")
return
}
poResponse := h.purchaseOrderService.GetPurchaseOrderByID(ctx, contextInfo, poID)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::GetPurchaseOrder -> Failed to get purchase order from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::GetPurchaseOrder")
}
func (h *PurchaseOrderHandler) ListPurchaseOrders(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListPurchaseOrdersRequest{
Page: 1,
Limit: 10,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
}
}
if limitStr := c.Query("limit"); limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil {
req.Limit = limit
}
}
if search := c.Query("search"); search != "" {
req.Search = search
}
if status := c.Query("status"); status != "" {
req.Status = status
}
if vendorIDStr := c.Query("vendor_id"); vendorIDStr != "" {
if vendorID, err := uuid.Parse(vendorIDStr); err == nil {
req.VendorID = &vendorID
}
}
if startDateStr := c.Query("start_date"); startDateStr != "" {
if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil {
req.StartDate = &startDate
}
}
if endDateStr := c.Query("end_date"); endDateStr != "" {
if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil {
req.EndDate = &endDate
}
}
validationError, validationErrorCode := h.purchaseOrderValidator.ValidateListPurchaseOrdersRequest(req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::ListPurchaseOrders")
return
}
poResponse := h.purchaseOrderService.ListPurchaseOrders(ctx, contextInfo, req)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::ListPurchaseOrders -> Failed to list purchase orders from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::ListPurchaseOrders")
}
func (h *PurchaseOrderHandler) GetPurchaseOrdersByStatus(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
status := c.Param("status")
if status == "" {
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Status parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::GetPurchaseOrdersByStatus")
return
}
poResponse := h.purchaseOrderService.GetPurchaseOrdersByStatus(ctx, contextInfo, status)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::GetPurchaseOrdersByStatus -> Failed to get purchase orders by status from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::GetPurchaseOrdersByStatus")
}
func (h *PurchaseOrderHandler) GetOverduePurchaseOrders(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
poResponse := h.purchaseOrderService.GetOverduePurchaseOrders(ctx, contextInfo)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::GetOverduePurchaseOrders -> Failed to get overdue purchase orders from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::GetOverduePurchaseOrders")
}
func (h *PurchaseOrderHandler) UpdatePurchaseOrderStatus(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
poIDStr := c.Param("id")
poID, err := uuid.Parse(poIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("PurchaseOrderHandler::UpdatePurchaseOrderStatus -> Invalid purchase order ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid purchase order ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::UpdatePurchaseOrderStatus")
return
}
status := c.Param("status")
if status == "" {
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Status parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "PurchaseOrderHandler::UpdatePurchaseOrderStatus")
return
}
poResponse := h.purchaseOrderService.UpdatePurchaseOrderStatus(ctx, contextInfo, poID, status)
if poResponse.HasErrors() {
errorResp := poResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("PurchaseOrderHandler::UpdatePurchaseOrderStatus -> Failed to update purchase order status from service")
}
util.HandleResponse(c.Writer, c.Request, poResponse, "PurchaseOrderHandler::UpdatePurchaseOrderStatus")
}
+206
View File
@@ -0,0 +1,206 @@
package handler
import (
"strconv"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
)
type RewardHandler struct {
rewardService service.RewardService
rewardValidator validator.RewardValidator
}
func NewRewardHandler(rewardService service.RewardService, rewardValidator validator.RewardValidator) *RewardHandler {
return &RewardHandler{
rewardService: rewardService,
rewardValidator: rewardValidator,
}
}
func (h *RewardHandler) CreateReward(c *gin.Context) {
ctx := c.Request.Context()
var req contract.CreateRewardRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::CreateReward -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::CreateReward")
return
}
validationError, validationErrorCode := h.rewardValidator.ValidateCreateRewardRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("RewardHandler::CreateReward -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::CreateReward")
return
}
response, err := h.rewardService.CreateReward(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::CreateReward -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::CreateReward")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "RewardHandler::CreateReward")
}
func (h *RewardHandler) GetReward(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("RewardHandler::GetReward -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RewardEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::GetReward")
return
}
response, err := h.rewardService.GetReward(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::GetReward -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::GetReward")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "RewardHandler::GetReward")
}
func (h *RewardHandler) ListRewards(c *gin.Context) {
ctx := c.Request.Context()
var req contract.ListRewardsRequest
if err := c.ShouldBindQuery(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::ListRewards -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::ListRewards")
return
}
validationError, validationErrorCode := h.rewardValidator.ValidateListRewardsRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("RewardHandler::ListRewards -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::ListRewards")
return
}
response, err := h.rewardService.ListRewards(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::ListRewards -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::ListRewards")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "RewardHandler::ListRewards")
}
func (h *RewardHandler) UpdateReward(c *gin.Context) {
ctx := c.Request.Context()
var req contract.UpdateRewardRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::UpdateReward -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::UpdateReward")
return
}
validationError, validationErrorCode := h.rewardValidator.ValidateUpdateRewardRequest(&req)
if validationError != nil {
logger.FromContext(c.Request.Context()).WithError(validationError).Error("RewardHandler::UpdateReward -> request validation failed")
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::UpdateReward")
return
}
response, err := h.rewardService.UpdateReward(ctx, &req)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::UpdateReward -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::UpdateReward")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "RewardHandler::UpdateReward")
}
func (h *RewardHandler) DeleteReward(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("RewardHandler::DeleteReward -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RewardEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::DeleteReward")
return
}
err := h.rewardService.DeleteReward(ctx, idStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::DeleteReward -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::DeleteReward")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse("Reward deleted successfully"), "RewardHandler::DeleteReward")
}
func (h *RewardHandler) UpdateRewardStock(c *gin.Context) {
ctx := c.Request.Context()
idStr := c.Param("id")
if idStr == "" {
logger.FromContext(c.Request.Context()).Error("RewardHandler::UpdateRewardStock -> missing ID parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RewardEntity, "ID parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::UpdateRewardStock")
return
}
stockStr := c.Param("stock")
stock, err := strconv.Atoi(stockStr)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::UpdateRewardStock -> invalid stock parameter")
validationResponseError := contract.NewResponseError(constants.InvalidFieldErrorCode, constants.RewardEntity, "Invalid stock parameter")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::UpdateRewardStock")
return
}
err = h.rewardService.UpdateRewardStock(ctx, idStr, stock)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::UpdateRewardStock -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::UpdateRewardStock")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse("Reward stock updated successfully"), "RewardHandler::UpdateRewardStock")
}
func (h *RewardHandler) GetRewardsByType(c *gin.Context) {
ctx := c.Request.Context()
rewardType := c.Param("type")
if rewardType == "" {
logger.FromContext(c.Request.Context()).Error("RewardHandler::GetRewardsByType -> missing type parameter")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RewardEntity, "Type parameter is required")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "RewardHandler::GetRewardsByType")
return
}
response, err := h.rewardService.GetRewardsByType(ctx, rewardType)
if err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("RewardHandler::GetRewardsByType -> service call failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError(constants.InternalServerErrorCode, constants.RewardEntity, err.Error())}), "RewardHandler::GetRewardsByType")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "RewardHandler::GetRewardsByType")
}
+62
View File
@@ -0,0 +1,62 @@
package handler
import (
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/mappers"
"apskel-pos-be/internal/models"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"github.com/gin-gonic/gin"
)
type SpinGameHandler struct {
spinGameService service.SpinGameService
}
func NewSpinGameHandler(spinGameService service.SpinGameService) *SpinGameHandler {
return &SpinGameHandler{
spinGameService: spinGameService,
}
}
// PlaySpinGame handles the spin game play request
func (h *SpinGameHandler) PlaySpinGame(c *gin.Context) {
// Get customer ID from JWT token (set by middleware)
customerID, exists := c.Get("customer_id")
if !exists {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.SpinGameHandlerEntity, "Customer ID not found in context"),
}), "SpinGameHandler::PlaySpinGame")
return
}
// Parse spin ID from request body
var req contract.SpinGameRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.ValidationErrorCode, constants.SpinGameHandlerEntity, err.Error()),
}), "SpinGameHandler::PlaySpinGame")
return
}
// Convert to model
modelReq := &models.SpinGameRequest{
SpinID: req.SpinID,
}
// Play the spin game
response, err := h.spinGameService.PlaySpinGame(c.Request.Context(), customerID.(string), modelReq)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.SpinGameHandlerEntity, err.Error()),
}), "SpinGameHandler::PlaySpinGame")
return
}
// Convert model response to contract response
contractResponse := mappers.SpinGameResponseModelToContract(response)
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResponse.Data), "SpinGameHandler::PlaySpinGame")
}
+3 -2
View File
@@ -133,15 +133,16 @@ func (h *TableHandler) Delete(c *gin.Context) {
func (h *TableHandler) List(c *gin.Context) { func (h *TableHandler) List(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
query := contract.ListTablesQuery{ query := contract.ListTablesQuery{
OrganizationID: c.Query("organization_id"), OrganizationID: contextInfo.OrganizationID.String(),
OutletID: c.Query("outlet_id"), OutletID: c.Query("outlet_id"),
Status: c.Query("status"), Status: c.Query("status"),
IsActive: c.Query("is_active"), IsActive: c.Query("is_active"),
Search: c.Query("search"), Search: c.Query("search"),
Page: 1, Page: 1,
Limit: 10, Limit: 100,
} }
if pageStr := c.Query("page"); pageStr != "" { if pageStr := c.Query("page"); pageStr != "" {
+201
View File
@@ -0,0 +1,201 @@
package handler
import (
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/util"
"strconv"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/validator"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type VendorHandler struct {
vendorService service.VendorService
vendorValidator validator.VendorValidator
}
func NewVendorHandler(
vendorService service.VendorService,
vendorValidator validator.VendorValidator,
) *VendorHandler {
return &VendorHandler{
vendorService: vendorService,
vendorValidator: vendorValidator,
}
}
func (h *VendorHandler) CreateVendor(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateVendorRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("VendorHandler::CreateVendor -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::CreateVendor")
return
}
validationError, validationErrorCode := h.vendorValidator.ValidateCreateVendorRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::CreateVendor")
return
}
vendorResponse := h.vendorService.CreateVendor(ctx, contextInfo, &req)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::CreateVendor -> Failed to create vendor from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::CreateVendor")
}
func (h *VendorHandler) UpdateVendor(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
vendorIDStr := c.Param("id")
vendorID, err := uuid.Parse(vendorIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("VendorHandler::UpdateVendor -> Invalid vendor ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid vendor ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::UpdateVendor")
return
}
var req contract.UpdateVendorRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("VendorHandler::UpdateVendor -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::UpdateVendor")
return
}
validationError, validationErrorCode := h.vendorValidator.ValidateUpdateVendorRequest(&req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::UpdateVendor")
return
}
vendorResponse := h.vendorService.UpdateVendor(ctx, contextInfo, vendorID, &req)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::UpdateVendor -> Failed to update vendor from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::UpdateVendor")
}
func (h *VendorHandler) DeleteVendor(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
vendorIDStr := c.Param("id")
vendorID, err := uuid.Parse(vendorIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("VendorHandler::DeleteVendor -> Invalid vendor ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid vendor ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::DeleteVendor")
return
}
vendorResponse := h.vendorService.DeleteVendor(ctx, contextInfo, vendorID)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::DeleteVendor -> Failed to delete vendor from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::DeleteVendor")
}
func (h *VendorHandler) GetVendor(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
vendorIDStr := c.Param("id")
vendorID, err := uuid.Parse(vendorIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("VendorHandler::GetVendor -> Invalid vendor ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid vendor ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::GetVendor")
return
}
vendorResponse := h.vendorService.GetVendorByID(ctx, contextInfo, vendorID)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::GetVendor -> Failed to get vendor from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::GetVendor")
}
func (h *VendorHandler) ListVendors(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListVendorsRequest{
Page: 1,
Limit: 10,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
}
}
if limitStr := c.Query("limit"); limitStr != "" {
if limit, err := strconv.Atoi(limitStr); err == nil {
req.Limit = limit
}
}
if search := c.Query("search"); search != "" {
req.Search = search
}
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
req.IsActive = &isActive
}
}
validationError, validationErrorCode := h.vendorValidator.ValidateListVendorsRequest(req)
if validationError != nil {
validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "VendorHandler::ListVendors")
return
}
vendorResponse := h.vendorService.ListVendors(ctx, contextInfo, req)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::ListVendors -> Failed to list vendors from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::ListVendors")
}
func (h *VendorHandler) GetActiveVendors(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
vendorResponse := h.vendorService.GetActiveVendors(ctx, contextInfo)
if vendorResponse.HasErrors() {
errorResp := vendorResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("VendorHandler::GetActiveVendors -> Failed to get active vendors from service")
}
util.HandleResponse(c.Writer, c.Request, vendorResponse, "VendorHandler::GetActiveVendors")
}
+73
View File
@@ -0,0 +1,73 @@
package mappers
import (
"apskel-pos-be/internal/entities"
"apskel-pos-be/internal/models"
"github.com/google/uuid"
)
func AccountEntityToResponse(entity *entities.Account) *models.AccountResponse {
response := &models.AccountResponse{
ID: entity.ID,
OrganizationID: entity.OrganizationID,
OutletID: entity.OutletID,
ChartOfAccountID: entity.ChartOfAccountID,
Name: entity.Name,
Number: entity.Number,
AccountType: string(entity.AccountType),
OpeningBalance: entity.OpeningBalance,
CurrentBalance: entity.CurrentBalance,
Description: entity.Description,
IsActive: entity.IsActive,
IsSystem: entity.IsSystem,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
}
if entity.ChartOfAccount.ID != uuid.Nil {
response.ChartOfAccount = ChartOfAccountEntityToResponse(&entity.ChartOfAccount)
}
return response
}
func AccountCreateRequestToEntity(req *models.CreateAccountRequest, organizationID uuid.UUID, outletID *uuid.UUID) *entities.Account {
return &entities.Account{
OrganizationID: organizationID,
OutletID: outletID,
ChartOfAccountID: req.ChartOfAccountID,
Name: req.Name,
Number: req.Number,
AccountType: entities.AccountType(req.AccountType),
OpeningBalance: req.OpeningBalance,
CurrentBalance: req.OpeningBalance, // Initialize current balance with opening balance
Description: req.Description,
IsActive: true,
IsSystem: false,
}
}
func AccountUpdateRequestToEntity(entity *entities.Account, req *models.UpdateAccountRequest) {
if req.ChartOfAccountID != nil {
entity.ChartOfAccountID = *req.ChartOfAccountID
}
if req.Name != nil {
entity.Name = *req.Name
}
if req.Number != nil {
entity.Number = *req.Number
}
if req.AccountType != nil {
entity.AccountType = entities.AccountType(*req.AccountType)
}
if req.OpeningBalance != nil {
entity.OpeningBalance = *req.OpeningBalance
}
if req.Description != nil {
entity.Description = req.Description
}
if req.IsActive != nil {
entity.IsActive = *req.IsActive
}
}
+177
View File
@@ -0,0 +1,177 @@
package mappers
import (
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/entities"
"apskel-pos-be/internal/models"
"github.com/google/uuid"
)
func ToCampaignResponse(entity *entities.Campaign) *models.CampaignResponse {
if entity == nil {
return nil
}
var rules []models.CampaignRuleResponse
if entity.Rules != nil {
for _, rule := range entity.Rules {
rules = append(rules, *ToCampaignRuleResponse(&rule))
}
}
return &models.CampaignResponse{
ID: entity.ID,
Name: entity.Name,
Description: entity.Description,
Type: string(entity.Type),
StartDate: entity.StartDate,
EndDate: entity.EndDate,
IsActive: entity.IsActive,
ShowOnApp: entity.ShowOnApp,
Position: entity.Position,
Metadata: entity.Metadata,
Rules: rules,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
}
}
func ToCampaignRuleResponse(entity *entities.CampaignRule) *models.CampaignRuleResponse {
if entity == nil {
return nil
}
var rewardSubtype *string
if entity.RewardSubtype != nil {
subtype := string(*entity.RewardSubtype)
rewardSubtype = &subtype
}
return &models.CampaignRuleResponse{
ID: entity.ID,
CampaignID: entity.CampaignID,
RuleType: string(entity.RuleType),
ConditionValue: entity.ConditionValue,
RewardType: string(entity.RewardType),
RewardValue: entity.RewardValue,
RewardSubtype: rewardSubtype,
RewardRefID: entity.RewardRefID,
Metadata: entity.Metadata,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
}
}
func ToCampaignEntity(request *contract.CreateCampaignRequest) *entities.Campaign {
if request == nil {
return nil
}
return &entities.Campaign{
Name: request.Name,
Description: request.Description,
Type: entities.CampaignType(request.Type),
StartDate: request.StartDate,
EndDate: request.EndDate,
IsActive: request.IsActive,
ShowOnApp: request.ShowOnApp,
Position: request.Position,
Metadata: request.Metadata,
Rules: nil, // Rules will be managed separately
}
}
func ToCampaignEntityFromUpdate(request *contract.UpdateCampaignRequest) *entities.Campaign {
if request == nil {
return nil
}
return &entities.Campaign{
ID: request.ID,
Name: request.Name,
Description: request.Description,
Type: entities.CampaignType(request.Type),
StartDate: request.StartDate,
EndDate: request.EndDate,
IsActive: request.IsActive,
ShowOnApp: request.ShowOnApp,
Position: request.Position,
Metadata: request.Metadata,
Rules: nil, // Rules will be managed separately
}
}
func ToCampaignRuleEntity(request *contract.CampaignRuleStruct, campaignID uuid.UUID) *entities.CampaignRule {
if request == nil {
return nil
}
var rewardSubtype *entities.RewardSubtype
if request.RewardSubtype != nil {
subtype := entities.RewardSubtype(*request.RewardSubtype)
rewardSubtype = &subtype
}
return &entities.CampaignRule{
ID: uuid.Nil, // Let GORM's BeforeCreate hook generate the UUID
CampaignID: campaignID,
RuleType: entities.RuleType(request.RuleType),
ConditionValue: request.ConditionValue,
RewardType: entities.CampaignRewardType(request.RewardType),
RewardValue: request.RewardValue,
RewardSubtype: rewardSubtype,
RewardRefID: request.RewardRefID,
Metadata: request.Metadata,
}
}
func ToCampaignRuleEntityFromUpdate(request *contract.CampaignRuleStruct, campaignID uuid.UUID) *entities.CampaignRule {
if request == nil {
return nil
}
var rewardSubtype *entities.RewardSubtype
if request.RewardSubtype != nil {
subtype := entities.RewardSubtype(*request.RewardSubtype)
rewardSubtype = &subtype
}
return &entities.CampaignRule{
ID: uuid.Nil, // Let GORM's BeforeCreate hook generate the UUID
CampaignID: campaignID,
RuleType: entities.RuleType(request.RuleType),
ConditionValue: request.ConditionValue,
RewardType: entities.CampaignRewardType(request.RewardType),
RewardValue: request.RewardValue,
RewardSubtype: rewardSubtype,
RewardRefID: request.RewardRefID,
Metadata: request.Metadata,
}
}
func ToCampaignResponseSlice(entities []entities.Campaign) []models.CampaignResponse {
if entities == nil {
return nil
}
responses := make([]models.CampaignResponse, len(entities))
for i, entity := range entities {
responses[i] = *ToCampaignResponse(&entity)
}
return responses
}
func ToCampaignRuleResponseSlice(entities []entities.CampaignRule) []models.CampaignRuleResponse {
if entities == nil {
return nil
}
responses := make([]models.CampaignRuleResponse, len(entities))
for i, entity := range entities {
responses[i] = *ToCampaignRuleResponse(&entity)
}
return responses
}
+8 -14
View File
@@ -16,7 +16,7 @@ func CategoryEntityToModel(entity *entities.Category) *models.Category {
Name: entity.Name, Name: entity.Name,
Description: entity.Description, Description: entity.Description,
ImageURL: nil, // Entity doesn't have ImageURL, model does ImageURL: nil, // Entity doesn't have ImageURL, model does
SortOrder: 0, // Entity doesn't have SortOrder, model does Order: entity.Order, // Entity doesn't have SortOrder, model does
IsActive: true, // Entity doesn't have IsActive, default to true IsActive: true, // Entity doesn't have IsActive, default to true
CreatedAt: entity.CreatedAt, CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt, UpdatedAt: entity.UpdatedAt,
@@ -32,7 +32,7 @@ func CategoryModelToEntity(model *models.Category) *entities.Category {
if model.ImageURL != nil { if model.ImageURL != nil {
metadata["image_url"] = *model.ImageURL metadata["image_url"] = *model.ImageURL
} }
metadata["sort_order"] = model.SortOrder // metadata["sort_order"] = model.SortOrder
return &entities.Category{ return &entities.Category{
ID: model.ID, ID: model.ID,
@@ -40,6 +40,7 @@ func CategoryModelToEntity(model *models.Category) *entities.Category {
Name: model.Name, Name: model.Name,
Description: model.Description, Description: model.Description,
BusinessType: "restaurant", // Default business type BusinessType: "restaurant", // Default business type
Order: model.Order,
Metadata: metadata, Metadata: metadata,
CreatedAt: model.CreatedAt, CreatedAt: model.CreatedAt,
UpdatedAt: model.UpdatedAt, UpdatedAt: model.UpdatedAt,
@@ -55,12 +56,13 @@ func CreateCategoryRequestToEntity(req *models.CreateCategoryRequest) *entities.
if req.ImageURL != nil { if req.ImageURL != nil {
metadata["image_url"] = *req.ImageURL metadata["image_url"] = *req.ImageURL
} }
metadata["sort_order"] = req.SortOrder // metadata["sort_order"] = req.SortOrder
return &entities.Category{ return &entities.Category{
OrganizationID: req.OrganizationID, OrganizationID: req.OrganizationID,
Name: req.Name, Name: req.Name,
Description: req.Description, Description: req.Description,
Order: req.Order,
BusinessType: "restaurant", // Default business type BusinessType: "restaurant", // Default business type
Metadata: metadata, Metadata: metadata,
} }
@@ -73,7 +75,6 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
// Extract image URL and sort order from metadata // Extract image URL and sort order from metadata
var imageURL *string var imageURL *string
var sortOrder int
if entity.Metadata != nil { if entity.Metadata != nil {
if imgURL, exists := entity.Metadata["image_url"]; exists { if imgURL, exists := entity.Metadata["image_url"]; exists {
@@ -81,13 +82,6 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
imageURL = &imgURLStr imageURL = &imgURLStr
} }
} }
if sort, exists := entity.Metadata["sort_order"]; exists {
if sortInt, ok := sort.(int); ok {
sortOrder = sortInt
} else if sortFloat, ok := sort.(float64); ok {
sortOrder = int(sortFloat)
}
}
} }
return &models.CategoryResponse{ return &models.CategoryResponse{
@@ -96,7 +90,7 @@ func CategoryEntityToResponse(entity *entities.Category) *models.CategoryRespons
Name: entity.Name, Name: entity.Name,
Description: entity.Description, Description: entity.Description,
ImageURL: imageURL, ImageURL: imageURL,
SortOrder: sortOrder, Order: entity.Order,
IsActive: true, // Default to true since entity doesn't have this field IsActive: true, // Default to true since entity doesn't have this field
CreatedAt: entity.CreatedAt, CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt, UpdatedAt: entity.UpdatedAt,
@@ -124,8 +118,8 @@ func UpdateCategoryEntityFromRequest(entity *entities.Category, req *models.Upda
entity.Metadata["image_url"] = *req.ImageURL entity.Metadata["image_url"] = *req.ImageURL
} }
if req.SortOrder != nil { if req.Order != nil {
entity.Metadata["sort_order"] = *req.SortOrder entity.Order = *req.Order
} }
} }
@@ -0,0 +1,77 @@
package mappers
import (
"apskel-pos-be/internal/entities"
"apskel-pos-be/internal/models"
"github.com/google/uuid"
)
func ChartOfAccountEntityToResponse(entity *entities.ChartOfAccount) *models.ChartOfAccountResponse {
response := &models.ChartOfAccountResponse{
ID: entity.ID,
OrganizationID: entity.OrganizationID,
OutletID: &entity.OutletID,
ChartOfAccountTypeID: entity.ChartOfAccountTypeID,
ParentID: entity.ParentID,
Name: entity.Name,
Code: entity.Code,
Description: entity.Description,
IsActive: entity.IsActive,
IsSystem: entity.IsSystem,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
}
if entity.ChartOfAccountType.ID != uuid.Nil {
response.ChartOfAccountType = ChartOfAccountTypeEntityToResponse(&entity.ChartOfAccountType)
}
if entity.Parent != nil {
response.Parent = ChartOfAccountEntityToResponse(entity.Parent)
}
if len(entity.Children) > 0 {
response.Children = make([]models.ChartOfAccountResponse, len(entity.Children))
for i, child := range entity.Children {
response.Children[i] = *ChartOfAccountEntityToResponse(&child)
}
}
return response
}
func ChartOfAccountCreateRequestToEntity(req *models.CreateChartOfAccountRequest, organizationID uuid.UUID, outletID *uuid.UUID) *entities.ChartOfAccount {
return &entities.ChartOfAccount{
OrganizationID: organizationID,
OutletID: *outletID,
ChartOfAccountTypeID: req.ChartOfAccountTypeID,
ParentID: req.ParentID,
Name: req.Name,
Code: req.Code,
Description: req.Description,
IsActive: true,
IsSystem: false,
}
}
func ChartOfAccountUpdateRequestToEntity(entity *entities.ChartOfAccount, req *models.UpdateChartOfAccountRequest) {
if req.ChartOfAccountTypeID != nil {
entity.ChartOfAccountTypeID = *req.ChartOfAccountTypeID
}
if req.ParentID != nil {
entity.ParentID = req.ParentID
}
if req.Name != nil {
entity.Name = *req.Name
}
if req.Code != nil {
entity.Code = *req.Code
}
if req.Description != nil {
entity.Description = req.Description
}
if req.IsActive != nil {
entity.IsActive = *req.IsActive
}
}
@@ -0,0 +1,42 @@
package mappers
import (
"apskel-pos-be/internal/entities"
"apskel-pos-be/internal/models"
)
func ChartOfAccountTypeEntityToResponse(entity *entities.ChartOfAccountType) *models.ChartOfAccountTypeResponse {
return &models.ChartOfAccountTypeResponse{
ID: entity.ID,
Name: entity.Name,
Code: entity.Code,
Description: entity.Description,
IsActive: entity.IsActive,
CreatedAt: entity.CreatedAt,
UpdatedAt: entity.UpdatedAt,
}
}
func ChartOfAccountTypeCreateRequestToEntity(req *models.CreateChartOfAccountTypeRequest) *entities.ChartOfAccountType {
return &entities.ChartOfAccountType{
Name: req.Name,
Code: req.Code,
Description: req.Description,
IsActive: true,
}
}
func ChartOfAccountTypeUpdateRequestToEntity(entity *entities.ChartOfAccountType, req *models.UpdateChartOfAccountTypeRequest) {
if req.Name != nil {
entity.Name = *req.Name
}
if req.Code != nil {
entity.Code = *req.Code
}
if req.Description != nil {
entity.Description = req.Description
}
if req.IsActive != nil {
entity.IsActive = *req.IsActive
}
}

Some files were not shown because too many files have changed in this diff Show More