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
Aditya Siregar 4cade376b9 Add Category 2025-08-13 20:37:55 +07:00
Aditya Siregar 82f72fc3eb Push products 2025-08-13 20:30:39 +07:00
Aditya Siregar b72ab4ef3d Update product receipe 2025-08-10 21:46:44 +07:00
Aditya Siregar 265248ba49 update middlewared 2025-08-10 21:09:51 +07:00
Aditya Siregar db7c862fa8 fix unit 2025-08-10 20:58:32 +07:00
Aditya Siregar 835097d381 Update Ingredients 2025-08-10 20:57:26 +07:00
Aditya Siregar 5a42523f0f Update 2025-08-10 20:41:34 +07:00
Aditya Siregar dc23a318cd add deployment 2025-08-08 22:48:57 +07:00
Aditya Siregar 721235e6fe Update Order 2025-08-08 22:46:55 +07:00
Aditya Siregar 8e9c14b860 Add order items 2025-08-08 22:33:08 +07:00
Aditya Siregar c18e915d1e add users 2025-08-08 00:32:05 +07:00
Aditya Siregar fe4f17b34d Add ingredieints 2025-08-08 00:22:28 +07:00
Aditya Siregar 93a3b29ae9 Fix Split Bill 2025-08-07 22:45:02 +07:00
Aditya Siregar 3696451dc6 add orders 2025-08-06 00:42:57 +07:00
Aditya Siregar 0a44135fb6 Update Order Response Ingredient 2025-08-06 00:23:03 +07:00
350 changed files with 32293 additions and 2153 deletions
+7 -86
View File
@@ -1,99 +1,20 @@
# Build Stage
# 1) Build stage
FROM golang:1.21-alpine AS build
# Install necessary packages including CA certificates
RUN apk --no-cache add ca-certificates tzdata git curl
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 ./
RUN go mod download
# Copy source code
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /out/app ./cmd/server
# Set timezone
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
# 2) Production stage
FROM debian:bullseye-slim AS production
# Install minimal runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
tzdata \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user for security
RUN apt-get update && apt-get install -y ca-certificates tzdata curl && rm -rf /var/lib/apt/lists/*
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 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
COPY --from=build /out/app /app
ENV TZ=Asia/Jakarta
# Expose port
EXPOSE 3300
# Health check
EXPOSE 4000
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:3300/health || exit 1
# Switch to non-root user
CMD curl -fsS http://localhost:3300/health || exit 1
USER appuser
# Set the entrypoint
ENTRYPOINT ["/app"]
+17 -2
View File
@@ -29,6 +29,7 @@ type Config struct {
Jwt Jwt `mapstructure:"jwt"`
Log Log `mapstructure:"log"`
S3Config S3Config `mapstructure:"s3"`
Fonnte Fonnte `mapstructure:"fonnte"`
}
var (
@@ -63,11 +64,21 @@ func LoadConfig() *Config {
func (c *Config) Auth() *AuthConfig {
return &AuthConfig{
jwtTokenSecret: c.Jwt.Token.Secret,
jwtTokenExpiresTTL: c.Jwt.Token.ExpiresTTL,
jwtTokenSecret: c.Jwt.Token.Secret,
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 {
return c.Log.LogLevel
}
@@ -79,3 +90,7 @@ func (c *Config) Port() string {
func (c *Config) LogFormat() string {
return c.Log.LogFormat
}
func (c *Config) GetFonnte() *Fonnte {
return &c.Fonnte
}
+21 -2
View File
@@ -3,8 +3,10 @@ package config
import "time"
type AuthConfig struct {
jwtTokenExpiresTTL int
jwtTokenSecret string
jwtTokenExpiresTTL int
jwtTokenSecret string
refreshTokenExpiresTTL int
refreshTokenSecret string
}
type JWT struct {
@@ -20,3 +22,20 @@ func (c *AuthConfig) AccessTokenExpiresDate() time.Time {
duration := time.Duration(c.jwtTokenExpiresTTL)
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
}
+1 -1
View File
@@ -20,7 +20,7 @@ type Database struct {
}
func (c Database) DSN() string {
return fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=%s TimeZone=UTC", c.Host, c.Port, c.DB, c.Username, c.Password, c.SslMode)
return fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=%s TimeZone=Asia/Jakarta", c.Host, c.Port, c.DB, c.Username, c.Password, c.SslMode)
}
func (c Database) ConnectionMaxLifetime() time.Duration {
+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
type Jwt struct {
Token Token `mapstructure:"token"`
Token Token `mapstructure:"token"`
RefreshToken RefreshToken `mapstructure:"refresh_token"`
Customer Customer `mapstructure:"customer"`
}
type Token struct {
ExpiresTTL int `mapstructure:"expires-ttl"`
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"`
}
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
set -euo pipefail
APP_NAME="apskel-pos"
PORT="4000"
echo "🔄 Pulling latest code..."
git pull
echo "🐳 Building Docker image (production target)..."
docker build --target production -t $APP_NAME:latest .
echo "🛑 Stopping and removing old container..."
docker rm -f $APP_NAME 2>/dev/null || true
echo "🚀 Running new container..."
docker run -d --name $APP_NAME \
-p $PORT:$PORT \
-e TZ=Asia/Jakarta \
-v "$(pwd)/infra":/infra:ro \
-v "$(pwd)/templates":/templates:ro \
$APP_NAME:latest
echo "✅ Deployment complete."
+13 -2
View File
@@ -5,8 +5,14 @@ server:
jwt:
token:
expires-ttl: 1440
expires-ttl: 144000
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:
host: 62.72.45.250
@@ -31,4 +37,9 @@ s3:
log:
log_format: 'json'
log_level: 'debug'
log_level: 'debug'
fonnte:
api_url: "https://api.fonnte.com/send"
token: "bADQrf9NTXfLZQCK2wGg"
timeout: 30
+328 -137
View File
@@ -40,9 +40,9 @@ func NewApp(db *gorm.DB) *App {
func (a *App) Initialize(cfg *config.Config) error {
repos := a.initRepositories()
processors := a.initProcessors(cfg, repos)
services := a.initServices(processors, cfg)
services := a.initServices(processors, repos, cfg)
validators := a.initValidators()
middleware := a.initMiddleware(services)
middleware := a.initMiddleware(services, cfg)
healthHandler := handler.NewHealthHandler()
a.router = router.NewRouter(
@@ -74,10 +74,37 @@ func (a *App) Initialize(cfg *config.Config) error {
services.paymentMethodService,
validators.paymentMethodValidator,
services.analyticsService,
services.reportService,
services.tableService,
validators.tableValidator,
services.unitService,
services.ingredientService,
services.productRecipeService,
services.vendorService,
validators.vendorValidator,
services.purchaseOrderService,
validators.purchaseOrderValidator,
services.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
@@ -123,117 +150,224 @@ func (a *App) Shutdown() {
}
type repositories struct {
userRepo *repository.UserRepositoryImpl
organizationRepo *repository.OrganizationRepositoryImpl
outletRepo *repository.OutletRepositoryImpl
outletSettingRepo *repository.OutletSettingRepositoryImpl
categoryRepo *repository.CategoryRepositoryImpl
productRepo *repository.ProductRepositoryImpl
productVariantRepo *repository.ProductVariantRepositoryImpl
inventoryRepo *repository.InventoryRepositoryImpl
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
orderRepo *repository.OrderRepositoryImpl
orderItemRepo *repository.OrderItemRepositoryImpl
paymentRepo *repository.PaymentRepositoryImpl
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
fileRepo *repository.FileRepositoryImpl
customerRepo *repository.CustomerRepository
analyticsRepo *repository.AnalyticsRepositoryImpl
tableRepo *repository.TableRepository
unitRepo *repository.UnitRepository
ingredientRepo *repository.IngredientRepository
userRepo *repository.UserRepositoryImpl
organizationRepo *repository.OrganizationRepositoryImpl
outletRepo *repository.OutletRepositoryImpl
outletSettingRepo *repository.OutletSettingRepositoryImpl
categoryRepo *repository.CategoryRepositoryImpl
productRepo *repository.ProductRepositoryImpl
productVariantRepo *repository.ProductVariantRepositoryImpl
inventoryRepo *repository.InventoryRepositoryImpl
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
orderRepo *repository.OrderRepositoryImpl
orderItemRepo *repository.OrderItemRepositoryImpl
paymentRepo *repository.PaymentRepositoryImpl
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
fileRepo *repository.FileRepositoryImpl
customerRepo *repository.CustomerRepository
analyticsRepo *repository.AnalyticsRepositoryImpl
tableRepo *repository.TableRepository
unitRepo *repository.UnitRepository
ingredientRepo *repository.IngredientRepository
ingredientCompositionRepo *repository.IngredientCompositionRepository
productRecipeRepo *repository.ProductRecipeRepository
vendorRepo *repository.VendorRepositoryImpl
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
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 {
return &repositories{
userRepo: repository.NewUserRepository(a.db),
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
outletRepo: repository.NewOutletRepositoryImpl(a.db),
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
productRepo: repository.NewProductRepositoryImpl(a.db),
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
orderRepo: repository.NewOrderRepositoryImpl(a.db),
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
fileRepo: repository.NewFileRepositoryImpl(a.db),
customerRepo: repository.NewCustomerRepository(a.db),
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
tableRepo: repository.NewTableRepository(a.db),
unitRepo: repository.NewUnitRepository(a.db),
ingredientRepo: repository.NewIngredientRepository(a.db),
userRepo: repository.NewUserRepository(a.db),
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
outletRepo: repository.NewOutletRepositoryImpl(a.db),
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
productRepo: repository.NewProductRepositoryImpl(a.db),
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
orderRepo: repository.NewOrderRepositoryImpl(a.db),
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
fileRepo: repository.NewFileRepositoryImpl(a.db),
customerRepo: repository.NewCustomerRepository(a.db),
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
tableRepo: repository.NewTableRepository(a.db),
unitRepo: repository.NewUnitRepository(a.db),
ingredientRepo: repository.NewIngredientRepository(a.db),
ingredientCompositionRepo: repository.NewIngredientCompositionRepository(a.db),
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db),
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 {
userProcessor *processor.UserProcessorImpl
organizationProcessor processor.OrganizationProcessor
outletProcessor processor.OutletProcessor
outletSettingProcessor *processor.OutletSettingProcessorImpl
categoryProcessor processor.CategoryProcessor
productProcessor processor.ProductProcessor
productVariantProcessor processor.ProductVariantProcessor
inventoryProcessor processor.InventoryProcessor
orderProcessor processor.OrderProcessor
paymentMethodProcessor processor.PaymentMethodProcessor
fileProcessor processor.FileProcessor
customerProcessor *processor.CustomerProcessor
analyticsProcessor *processor.AnalyticsProcessorImpl
tableProcessor *processor.TableProcessor
unitProcessor *processor.UnitProcessorImpl
ingredientProcessor *processor.IngredientProcessorImpl
userProcessor *processor.UserProcessorImpl
organizationProcessor processor.OrganizationProcessor
outletProcessor processor.OutletProcessor
outletSettingProcessor *processor.OutletSettingProcessorImpl
categoryProcessor processor.CategoryProcessor
productProcessor processor.ProductProcessor
productVariantProcessor processor.ProductVariantProcessor
inventoryProcessor processor.InventoryProcessor
orderProcessor processor.OrderProcessor
paymentMethodProcessor processor.PaymentMethodProcessor
fileProcessor processor.FileProcessor
customerProcessor *processor.CustomerProcessor
analyticsProcessor *processor.AnalyticsProcessorImpl
tableProcessor *processor.TableProcessor
unitProcessor *processor.UnitProcessorImpl
ingredientProcessor *processor.IngredientProcessorImpl
productRecipeProcessor *processor.ProductRecipeProcessorImpl
vendorProcessor *processor.VendorProcessorImpl
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
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 {
fileClient := client.NewFileClient(cfg.S3Config)
fonnteClient := client.NewFonnteClient(cfg.GetFonnte())
otpProcessor := processor.NewOtpProcessor(fonnteClient, repos.otpRepo)
inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo)
return &processors{
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo),
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo),
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo),
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo),
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService),
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo),
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.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 {
userService *service.UserServiceImpl
authService service.AuthService
organizationService service.OrganizationService
outletService service.OutletService
outletSettingService service.OutletSettingService
categoryService service.CategoryService
productService service.ProductService
productVariantService service.ProductVariantService
inventoryService service.InventoryService
orderService service.OrderService
paymentMethodService service.PaymentMethodService
fileService service.FileService
customerService service.CustomerService
analyticsService *service.AnalyticsServiceImpl
tableService *service.TableServiceImpl
unitService *service.UnitServiceImpl
ingredientService *service.IngredientServiceImpl
userService *service.UserServiceImpl
authService service.AuthService
organizationService service.OrganizationService
outletService service.OutletService
outletSettingService service.OutletSettingService
categoryService service.CategoryService
productService service.ProductService
productVariantService service.ProductVariantService
inventoryService service.InventoryService
orderService service.OrderService
paymentMethodService service.PaymentMethodService
fileService service.FileService
customerService service.CustomerService
analyticsService *service.AnalyticsServiceImpl
reportService service.ReportService
tableService *service.TableServiceImpl
unitService *service.UnitServiceImpl
ingredientService *service.IngredientServiceImpl
productRecipeService *service.ProductRecipeServiceImpl
vendorService *service.VendorServiceImpl
purchaseOrderService *service.PurchaseOrderServiceImpl
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, cfg *config.Config) *services {
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
authConfig := cfg.Auth()
jwtSecret := authConfig.AccessTokenSecret()
authService := service.NewAuthService(processors.userProcessor, jwtSecret)
authService := service.NewAuthService(processors.userProcessor, authConfig)
organizationService := service.NewOrganizationService(processors.organizationProcessor)
outletService := service.NewOutletService(processors.outletProcessor)
outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor)
@@ -241,74 +375,131 @@ func (a *App) initServices(processors *processors, cfg *config.Config) *services
productService := service.NewProductService(processors.productProcessor)
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
orderService := service.NewOrderServiceImpl(processors.orderProcessor)
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)
fileService := service.NewFileServiceImpl(processors.fileProcessor)
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
analyticsService := service.NewAnalyticsServiceImpl(processors.analyticsProcessor)
reportService := service.NewReportService(analyticsService, repos.organizationRepo, repos.outletRepo, processors.fileClient)
tableService := service.NewTableService(processors.tableProcessor, transformer.NewTableTransformer())
unitService := service.NewUnitService(processors.unitProcessor)
ingredientService := service.NewIngredientService(processors.ingredientProcessor)
productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor)
vendorService := service.NewVendorService(processors.vendorProcessor)
purchaseOrderService := service.NewPurchaseOrderService(processors.purchaseOrderProcessor)
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{
userService: service.NewUserService(processors.userProcessor),
authService: authService,
organizationService: organizationService,
outletService: outletService,
outletSettingService: outletSettingService,
categoryService: categoryService,
productService: productService,
productVariantService: productVariantService,
inventoryService: inventoryService,
orderService: orderService,
paymentMethodService: paymentMethodService,
fileService: fileService,
customerService: customerService,
analyticsService: analyticsService,
tableService: tableService,
unitService: unitService,
ingredientService: ingredientService,
userService: service.NewUserService(processors.userProcessor),
authService: authService,
organizationService: organizationService,
outletService: outletService,
outletSettingService: outletSettingService,
categoryService: categoryService,
productService: productService,
productVariantService: productVariantService,
inventoryService: inventoryService,
orderService: orderService,
paymentMethodService: paymentMethodService,
fileService: fileService,
customerService: customerService,
analyticsService: analyticsService,
reportService: reportService,
tableService: tableService,
unitService: unitService,
ingredientService: ingredientService,
productRecipeService: productRecipeService,
vendorService: vendorService,
purchaseOrderService: purchaseOrderService,
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 {
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{
authMiddleware: middleware.NewAuthMiddleware(services.authService),
authMiddleware: middleware.NewAuthMiddleware(services.authService),
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
}
}
type validators struct {
userValidator *validator.UserValidatorImpl
organizationValidator validator.OrganizationValidator
outletValidator validator.OutletValidator
categoryValidator validator.CategoryValidator
productValidator validator.ProductValidator
productVariantValidator validator.ProductVariantValidator
inventoryValidator validator.InventoryValidator
orderValidator validator.OrderValidator
paymentMethodValidator validator.PaymentMethodValidator
fileValidator validator.FileValidator
customerValidator validator.CustomerValidator
tableValidator *validator.TableValidator
userValidator *validator.UserValidatorImpl
organizationValidator validator.OrganizationValidator
outletValidator validator.OutletValidator
categoryValidator validator.CategoryValidator
productValidator validator.ProductValidator
productVariantValidator validator.ProductVariantValidator
inventoryValidator validator.InventoryValidator
orderValidator validator.OrderValidator
paymentMethodValidator validator.PaymentMethodValidator
fileValidator validator.FileValidator
customerValidator validator.CustomerValidator
tableValidator *validator.TableValidator
vendorValidator *validator.VendorValidatorImpl
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
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 {
return &validators{
userValidator: validator.NewUserValidator(),
organizationValidator: validator.NewOrganizationValidator(),
outletValidator: validator.NewOutletValidator(),
categoryValidator: validator.NewCategoryValidator(),
productValidator: validator.NewProductValidator(),
productVariantValidator: validator.NewProductVariantValidator(),
inventoryValidator: validator.NewInventoryValidator(),
orderValidator: validator.NewOrderValidator(),
paymentMethodValidator: validator.NewPaymentMethodValidator(),
fileValidator: validator.NewFileValidatorImpl(),
customerValidator: validator.NewCustomerValidator(),
tableValidator: validator.NewTableValidator(),
userValidator: validator.NewUserValidator(),
organizationValidator: validator.NewOrganizationValidator(),
outletValidator: validator.NewOutletValidator(),
categoryValidator: validator.NewCategoryValidator(),
productValidator: validator.NewProductValidator(),
productVariantValidator: validator.NewProductVariantValidator(),
inventoryValidator: validator.NewInventoryValidator(),
orderValidator: validator.NewOrderValidator(),
paymentMethodValidator: validator.NewPaymentMethodValidator(),
fileValidator: validator.NewFileValidatorImpl(),
customerValidator: validator.NewCustomerValidator(),
tableValidator: validator.NewTableValidator(),
vendorValidator: validator.NewVendorValidator(),
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
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 (
"context"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
@@ -77,5 +78,18 @@ func FromContext(ctx context.Context) *ContextInfo {
if info, ok := ctx.Value(ctxKey).(*ContextInfo); ok {
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 (
RequestEntity = "request"
UserServiceEntity = "user_service"
OrganizationServiceEntity = "organization_service"
CategoryServiceEntity = "category_service"
ProductServiceEntity = "product_service"
ProductVariantServiceEntity = "product_variant_service"
InventoryServiceEntity = "inventory_service"
OrderServiceEntity = "order_service"
CustomerServiceEntity = "customer_service"
UserValidatorEntity = "user_validator"
AuthHandlerEntity = "auth_handler"
UserHandlerEntity = "user_handler"
CategoryHandlerEntity = "category_handler"
ProductHandlerEntity = "product_handler"
ProductVariantHandlerEntity = "product_variant_handler"
InventoryHandlerEntity = "inventory_handler"
OrderValidatorEntity = "order_validator"
OrderHandlerEntity = "order_handler"
OrganizationValidatorEntity = "organization_validator"
OrgHandlerEntity = "organization_handler"
PaymentMethodValidatorEntity = "payment_method_validator"
PaymentMethodHandlerEntity = "payment_method_handler"
OutletServiceEntity = "outlet_service"
TableEntity = "table"
RequestEntity = "request"
UserServiceEntity = "user_service"
OrganizationServiceEntity = "organization_service"
CategoryServiceEntity = "category_service"
ProductServiceEntity = "product_service"
ProductVariantServiceEntity = "product_variant_service"
InventoryServiceEntity = "inventory_service"
OrderServiceEntity = "order_service"
CustomerServiceEntity = "customer_service"
UserValidatorEntity = "user_validator"
AuthHandlerEntity = "auth_handler"
UserHandlerEntity = "user_handler"
CategoryHandlerEntity = "category_handler"
ProductHandlerEntity = "product_handler"
ProductVariantHandlerEntity = "product_variant_handler"
InventoryHandlerEntity = "inventory_handler"
OrderValidatorEntity = "order_validator"
OrderHandlerEntity = "order_handler"
OrganizationValidatorEntity = "organization_validator"
OrgHandlerEntity = "organization_handler"
PaymentMethodValidatorEntity = "payment_method_validator"
PaymentMethodHandlerEntity = "payment_method_handler"
OutletServiceEntity = "outlet_service"
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{
+1
View File
@@ -28,6 +28,7 @@ const (
OrderItemStatusServed OrderItemStatus = "served"
OrderItemStatusCancelled OrderItemStatus = "cancelled"
OrderItemStatusCompleted OrderItemStatus = "completed"
OrderItemStatusPaid OrderItemStatus = "paid"
)
func GetAllOrderTypes() []OrderType {
+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"`
DateFrom string `form:"date_from" 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
@@ -101,16 +101,52 @@ type ProductAnalyticsResponse struct {
Data []ProductAnalyticsData `json:"data"`
}
// ProductAnalyticsData represents individual product analytics data
type ProductAnalyticsData struct {
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
QuantitySold int64 `json:"quantity_sold"`
Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`
OrderCount int64 `json:"order_count"`
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
ProductSku string `json:"product_sku"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
CategoryOrder int `json:"category_order"`
QuantitySold int64 `json:"quantity_sold"`
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
+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"`
Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,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"`
Description *string `json:"description,omitempty"`
BusinessType *string `json:"business_type,omitempty"`
Order *int `json:"order,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
@@ -35,6 +37,7 @@ type CategoryResponse struct {
Name string `json:"name"`
Description *string `json:"description"`
BusinessType string `json:"business_type"`
Order int `json:"order"`
Metadata map[string]interface{} `json:"metadata"`
CreatedAt time.Time `json:"created_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
import (
"github.com/google/uuid"
"time"
"github.com/google/uuid"
)
type CreateInventoryRequest struct {
@@ -24,6 +25,18 @@ type AdjustInventoryRequest struct {
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 {
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
ProductID *uuid.UUID `json:"product_id,omitempty"`
@@ -67,3 +80,75 @@ type InventoryAdjustmentResponse struct {
Reason string `json:"reason"`
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"`
}
+75 -24
View File
@@ -10,6 +10,7 @@ type CreateOrderRequest struct {
OutletID uuid.UUID `json:"outlet_id" validate:"required"`
UserID uuid.UUID `json:"user_id" validate:"required"`
CustomerID *uuid.UUID `json:"customer_id"`
TableID *uuid.UUID `json:"table_id,omitempty" validate:"omitempty"`
TableNumber *string `json:"table_number,omitempty" validate:"omitempty,max=50"`
OrderType string `json:"order_type" validate:"required,oneof=dine_in takeaway delivery"`
Notes *string `json:"notes,omitempty" validate:"omitempty,max=1000"`
@@ -56,23 +57,38 @@ type UpdateOrderItemRequest struct {
}
type OrderResponse struct {
ID uuid.UUID `json:"id"`
OrderNumber string `json:"order_number"`
OutletID uuid.UUID `json:"outlet_id"`
UserID uuid.UUID `json:"user_id"`
TableNumber *string `json:"table_number"`
OrderType string `json:"order_type"`
Status string `json:"status"`
Subtotal float64 `json:"subtotal"`
TaxAmount float64 `json:"tax_amount"`
DiscountAmount float64 `json:"discount_amount"`
TotalAmount float64 `json:"total_amount"`
Notes *string `json:"notes"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OrderItems []OrderItemResponse `json:"order_items,omitempty"`
IsRefund bool `json:"is_refund"`
ID uuid.UUID `json:"id"`
OrderNumber string `json:"order_number"`
OutletID uuid.UUID `json:"outlet_id"`
UserID uuid.UUID `json:"user_id"`
TableNumber *string `json:"table_number"`
OrderType string `json:"order_type"`
Status string `json:"status"`
Subtotal float64 `json:"subtotal"`
TaxAmount float64 `json:"tax_amount"`
DiscountAmount float64 `json:"discount_amount"`
TotalAmount float64 `json:"total_amount"`
TotalCost float64 `json:"total_cost"`
RemainingAmount float64 `json:"remaining_amount"`
PaymentStatus string `json:"payment_status"`
RefundAmount float64 `json:"refund_amount"`
IsVoid bool `json:"is_void"`
IsRefund bool `json:"is_refund"`
VoidReason *string `json:"void_reason,omitempty"`
VoidedAt *time.Time `json:"voided_at,omitempty"`
VoidedBy *uuid.UUID `json:"voided_by,omitempty"`
RefundReason *string `json:"refund_reason,omitempty"`
RefundedAt *time.Time `json:"refunded_at,omitempty"`
RefundedBy *uuid.UUID `json:"refunded_by,omitempty"`
Notes *string `json:"notes"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
OrderItems []OrderItemResponse `json:"order_items,omitempty"`
Payments []PaymentResponse `json:"payments,omitempty"`
TotalPaid float64 `json:"total_paid"`
PaymentCount int `json:"payment_count"`
SplitType *string `json:"split_type,omitempty"`
}
type OrderItemResponse struct {
@@ -92,6 +108,7 @@ type OrderItemResponse struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PrinterType string `json:"printer_type"`
PaidQuantity int `json:"paid_quantity"`
}
type ListOrdersQuery struct {
@@ -123,11 +140,12 @@ type ListOrdersRequest struct {
}
type ListOrdersResponse struct {
Orders []OrderResponse `json:"orders"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
Orders []OrderResponse `json:"orders"`
Payments []PaymentResponse `json:"payments"`
TotalCount int `json:"total_count"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"total_pages"`
}
type VoidOrderRequest struct {
@@ -152,7 +170,6 @@ type SetOrderCustomerResponse struct {
Message string `json:"message"`
}
// Payment-related contracts
type CreatePaymentRequest struct {
OrderID uuid.UUID `json:"order_id" validate:"required"`
PaymentMethodID uuid.UUID `json:"payment_method_id" validate:"required"`
@@ -160,6 +177,7 @@ type CreatePaymentRequest struct {
TransactionID *string `json:"transaction_id,omitempty" validate:"omitempty"`
SplitNumber int `json:"split_number,omitempty" validate:"omitempty,min=1"`
SplitTotal int `json:"split_total,omitempty" validate:"omitempty,min=1"`
SplitType *string `json:"split_type,omitempty" validate:"omitempty,oneof=AMOUNT ITEM"`
SplitDescription *string `json:"split_description,omitempty" validate:"omitempty,max=255"`
PaymentOrderItems []CreatePaymentOrderItemRequest `json:"payment_order_items,omitempty" validate:"omitempty,dive"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
@@ -167,18 +185,21 @@ type CreatePaymentRequest struct {
type CreatePaymentOrderItemRequest struct {
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 {
ID uuid.UUID `json:"id"`
OrderID uuid.UUID `json:"order_id"`
PaymentMethodID uuid.UUID `json:"payment_method_id"`
PaymentMethodName string `json:"payment_method_name"`
PaymentMethodType string `json:"payment_method_type"`
Amount float64 `json:"amount"`
Status string `json:"status"`
TransactionID *string `json:"transaction_id,omitempty"`
SplitNumber int `json:"split_number"`
SplitTotal int `json:"split_total"`
SplitType *string `json:"split_type,omitempty"`
SplitDescription *string `json:"split_description,omitempty"`
RefundAmount float64 `json:"refund_amount"`
RefundReason *string `json:"refund_reason,omitempty"`
@@ -216,3 +237,33 @@ type RefundPaymentRequest struct {
RefundAmount float64 `json:"refund_amount" validate:"required,min=0"`
Reason string `json:"reason" validate:"omitempty,max=255"`
}
type SplitBillRequest struct {
OrderID uuid.UUID `json:"order_id" validate:"required"`
OrganizationID uuid.UUID `json:"organization_id"`
PaymentMethodID uuid.UUID `json:"payment_method_id" validate:"required"`
CustomerID uuid.UUID `json:"customer_id"`
Type string `json:"type" validate:"required,oneof=ITEM AMOUNT"`
Items []SplitBillItemRequest `json:"items,omitempty" validate:"required_if=Type ITEM,dive"`
Amount float64 `json:"amount,omitempty" validate:"required_if=Type AMOUNT,min=0"`
}
type SplitBillItemRequest struct {
OrderItemID uuid.UUID `json:"order_item_id" validate:"required"`
Quantity int `json:"quantity" validate:"required,min=0"`
}
type SplitBillResponse struct {
PaymentID uuid.UUID `json:"payment_id"`
OrderID uuid.UUID `json:"order_id"`
CustomerID uuid.UUID `json:"customer_id"`
Type string `json:"type"`
Amount float64 `json:"amount"`
Items []SplitBillItemResponse `json:"items,omitempty"`
Message string `json:"message"`
}
type SplitBillItemResponse struct {
OrderItemID uuid.UUID `json:"order_item_id"`
Amount float64 `json:"amount"`
}
@@ -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"`
OrganizationID uuid.UUID `json:"organization_id"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
SKU *string `json:"sku"`
Name string `json:"name"`
Description *string `json:"description"`
@@ -0,0 +1,74 @@
package contract
import (
"time"
"github.com/google/uuid"
)
// Request structures
type CreateProductRecipeRequest struct {
OutletID *uuid.UUID `json:"outlet_id"`
ProductID uuid.UUID `json:"product_id" validate:"required"`
VariantID *uuid.UUID `json:"variant_id"`
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
Quantity float64 `json:"quantity" validate:"required,gt=0"`
WastePercentage float64 `json:"waste_percentage" validate:"min=0,max=100"`
}
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"`
}
+6 -4
View File
@@ -7,7 +7,7 @@ import (
)
type CreateUserRequest struct {
OrganizationID uuid.UUID `json:"organization_id" validate:"required"`
OrganizationID uuid.UUID `json:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
Name string `json:"name" validate:"required,min=1,max=255"`
Email string `json:"email" validate:"required,email"`
@@ -40,9 +40,11 @@ type LoginRequest struct {
}
type LoginResponse struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expires_at"`
User UserResponse `json:"user"`
Token string `json:"token"`
RefreshToken string `json:"refresh_token"`
ExpiresAt time.Time `json:"expires_at"`
RefreshExpiresAt time.Time `json:"refresh_expires_at"`
User UserResponse `json:"user"`
}
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"`
}
+1
View File
@@ -3,6 +3,7 @@ package db
import (
"apskel-pos-be/config"
"fmt"
_ "github.com/lib/pq"
"go.uber.org/zap"
_ "gopkg.in/yaml.v3"
+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"`
}
// ProductAnalytics represents product analytics data
type ProductAnalytics struct {
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
QuantitySold int64 `json:"quantity_sold"`
Revenue float64 `json:"revenue"`
AveragePrice float64 `json:"average_price"`
OrderCount int64 `json:"order_count"`
ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"`
ProductSku string `json:"product_sku"`
CategoryID uuid.UUID `json:"category_id"`
CategoryName string `json:"category_name"`
CategoryOrder int `json:"category_order"`
QuantitySold int64 `json:"quantity_sold"`
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
+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"`
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
Description *string `gorm:"type:text" json:"description"`
Order int `gorm:"default:0" json:"order"`
BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"`
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
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 {
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"`
Name string `gorm:"not null;size:255" json:"name" validate:"required"`
Email *string `gorm:"size:255;uniqueIndex" json:"email,omitempty"`
Phone *string `gorm:"size:20" json:"phone,omitempty"`
Address *string `gorm:"size:500" json:"address,omitempty"`
IsDefault bool `gorm:"default:false" json:"is_default"`
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"`
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"`
Name string `gorm:"not null;size:255" json:"name" validate:"required"`
Email *string `gorm:"size:255;uniqueIndex" json:"email,omitempty"`
Phone *string `gorm:"size:20" json:"phone,omitempty"`
PhoneNumber *string `gorm:"size:20;uniqueIndex" json:"phone_number,omitempty"`
Address *string `gorm:"size:500" json:"address,omitempty"`
BirthDate *time.Time `gorm:"type:date" json:"birth_date,omitempty"`
PasswordHash *string `gorm:"size:255" json:"-"`
IsDefault bool `gorm:"default:false" json:"is_default"`
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"`
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{},
&Customer{},
&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
}
}
+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 {
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"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
Name string `gorm:"not null;size:255" json:"name"`
UnitID uuid.UUID `gorm:"type:uuid;not null;index" json:"unit_id"`
Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost"`
Stock float64 `gorm:"type:decimal(10,2);default:0.00" json:"stock"`
IsSemiFinished bool `gorm:"default:false" json:"is_semi_finished"`
IsActive bool `gorm:"default:true" json:"is_active"`
Metadata map[string]any `gorm:"type:jsonb;default:'{}'" json:"metadata"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,omitempty"`
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"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
Name string `gorm:"not null;size:255" json:"name"`
UnitID uuid.UUID `gorm:"type:uuid;not null;index" json:"unit_id"`
Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost"`
Stock float64 `gorm:"type:decimal(10,2);default:0.00" json:"stock"`
IsSemiFinished bool `gorm:"default:false" json:"is_semi_finished"`
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"`
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 {
ID uuid.UUID `json:"id" db:"id"`
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
ParentIngredientID uuid.UUID `json:"parent_ingredient_id" db:"parent_ingredient_id"`
ChildIngredientID uuid.UUID `json:"child_ingredient_id" db:"child_ingredient_id"`
Quantity float64 `json:"quantity" db:"quantity"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
ParentIngredient *Ingredient `json:"parent_ingredient,omitempty"`
ChildIngredient *Ingredient `json:"child_ingredient,omitempty"`
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"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ParentIngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"parent_ingredient_id"`
ChildIngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"child_ingredient_id"`
Quantity float64 `gorm:"type:decimal(10,4);not null" json:"quantity"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
ParentIngredient *Ingredient `gorm:"foreignKey:ParentIngredientID;references:ID" json:"parent_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"
InventoryMovementTypeDamage InventoryMovementType = "damage"
InventoryMovementTypeExpiry InventoryMovementType = "expiry"
InventoryMovementTypeIngredient InventoryMovementType = "ingredient"
)
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"
}
+29 -27
View File
@@ -28,6 +28,7 @@ const (
const (
PaymentStatusPending PaymentStatus = "pending"
PaymentStatusPartial PaymentStatus = "partial"
PaymentStatusCompleted PaymentStatus = "completed"
PaymentStatusFailed PaymentStatus = "failed"
PaymentStatusRefunded PaymentStatus = "refunded"
@@ -35,33 +36,34 @@ const (
)
type Order 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;not null;index" json:"outlet_id" validate:"required"`
UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id" validate:"required"`
CustomerID *uuid.UUID `gorm:"type:uuid;index" json:"customer_id"`
OrderNumber string `gorm:"uniqueIndex;not null;size:50" json:"order_number" validate:"required"`
TableNumber *string `gorm:"size:20" json:"table_number"`
OrderType OrderType `gorm:"not null;size:50" json:"order_type" validate:"required,oneof=dine_in takeout delivery"`
Status OrderStatus `gorm:"default:'pending';size:50" json:"status"`
Subtotal float64 `gorm:"type:decimal(10,2);not null" json:"subtotal" validate:"required,min=0"`
TaxAmount float64 `gorm:"type:decimal(10,2);not null" json:"tax_amount" validate:"required,min=0"`
DiscountAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"discount_amount" validate:"min=0"`
TotalAmount float64 `gorm:"type:decimal(10,2);not null" json:"total_amount" validate:"required,min=0"`
TotalCost float64 `gorm:"type:decimal(10,2);default:0.00" json:"total_cost"`
PaymentStatus PaymentStatus `gorm:"default:'pending';size:50" json:"payment_status"`
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
IsVoid bool `gorm:"default:false" json:"is_void"`
IsRefund bool `gorm:"default:false" json:"is_refund"`
VoidReason *string `gorm:"size:255" json:"void_reason,omitempty"`
VoidedAt *time.Time `gorm:"" json:"voided_at,omitempty"`
VoidedBy *uuid.UUID `gorm:"type:uuid" json:"voided_by,omitempty"`
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
RefundedAt *time.Time `gorm:"" json:"refunded_at,omitempty"`
RefundedBy *uuid.UUID `gorm:"type:uuid" json:"refunded_by,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"`
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;not null;index" json:"outlet_id" validate:"required"`
UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id" validate:"required"`
CustomerID *uuid.UUID `gorm:"type:uuid;index" json:"customer_id"`
OrderNumber string `gorm:"uniqueIndex;not null;size:50" json:"order_number" validate:"required"`
TableNumber *string `gorm:"size:20" json:"table_number"`
OrderType OrderType `gorm:"not null;size:50" json:"order_type" validate:"required,oneof=dine_in takeout delivery"`
Status OrderStatus `gorm:"default:'pending';size:50" json:"status"`
Subtotal float64 `gorm:"type:decimal(10,2);not null" json:"subtotal" validate:"required,min=0"`
TaxAmount float64 `gorm:"type:decimal(10,2);not null" json:"tax_amount" validate:"required,min=0"`
DiscountAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"discount_amount" validate:"min=0"`
TotalAmount float64 `gorm:"type:decimal(10,2);not null" json:"total_amount" validate:"required,min=0"`
TotalCost float64 `gorm:"type:decimal(10,2);default:0.00" json:"total_cost"`
RemainingAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"remaining_amount"`
PaymentStatus PaymentStatus `gorm:"default:'pending';size:50" json:"payment_status"`
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
IsVoid bool `gorm:"default:false" json:"is_void"`
IsRefund bool `gorm:"default:false" json:"is_refund"`
VoidReason *string `gorm:"size:255" json:"void_reason,omitempty"`
VoidedAt *time.Time `gorm:"" json:"voided_at,omitempty"`
VoidedBy *uuid.UUID `gorm:"type:uuid" json:"voided_by,omitempty"`
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
RefundedAt *time.Time `gorm:"" json:"refunded_at,omitempty"`
RefundedBy *uuid.UUID `gorm:"type:uuid" json:"refunded_by,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"`
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
@@ -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"
}
+1
View File
@@ -38,6 +38,7 @@ const (
OrderItemStatusReady OrderItemStatus = "ready"
OrderItemStatusServed OrderItemStatus = "served"
OrderItemStatusCancelled OrderItemStatus = "cancelled"
OrderItemStatusPaid OrderItemStatus = "paid"
)
type OrderItem struct {
+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
}
+8
View File
@@ -50,6 +50,13 @@ const (
PaymentTransactionStatusRefunded PaymentTransactionStatus = "refunded"
)
type SplitType string
const (
SplitTypeAmount SplitType = "AMOUNT"
SplitTypeItem SplitType = "ITEM"
)
type Payment struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
OrderID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_id" validate:"required"`
@@ -59,6 +66,7 @@ type Payment struct {
TransactionID *string `gorm:"size:255" json:"transaction_id"`
SplitNumber int `gorm:"default:1" json:"split_number"`
SplitTotal int `gorm:"default:1" json:"split_total"`
SplitType *SplitType `gorm:"size:20" json:"split_type,omitempty"`
SplitDescription *string `gorm:"size:255" json:"split_description,omitempty"`
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
+1
View File
@@ -11,6 +11,7 @@ type PaymentOrderItem struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
PaymentID uuid.UUID `gorm:"type:uuid;not null;index" json:"payment_id"`
OrderItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_item_id"`
Quantity int `gorm:"not null;default:0" json:"quantity"` // Quantity paid for this specific payment
Amount float64 `gorm:"type:decimal(10,2);not null" json:"amount"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
+1 -1
View File
@@ -30,7 +30,7 @@ type Product struct {
Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
Unit *Unit `gorm:"foreignKey:UnitID" json:"unit,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"`
OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"`
}
+9 -8
View File
@@ -7,14 +7,15 @@ import (
)
type ProductIngredient struct {
ID uuid.UUID `json:"id" db:"id"`
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
ProductID uuid.UUID `json:"product_id" db:"product_id"`
IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"`
Quantity float64 `json:"quantity" db:"quantity"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
ID uuid.UUID `json:"id" db:"id"`
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
ProductID uuid.UUID `json:"product_id" db:"product_id"`
IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"`
Quantity float64 `json:"quantity" db:"quantity"`
WastePercentage float64 `json:"waste_percentage" db:"waste_percentage"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
// Relations
Product *Product `json:"product,omitempty"`
+37
View File
@@ -0,0 +1,37 @@
package entities
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
type ProductRecipe 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"`
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id"`
VariantID *uuid.UUID `gorm:"type:uuid;index" json:"variant_id"`
IngredientID uuid.UUID `gorm:"type:uuid;not null;index" json:"ingredient_id"`
Quantity float64 `gorm:"type:decimal(12,3);not null" json:"quantity"`
WastePercentage float64 `gorm:"type:decimal(5,2);default:0" json:"waste_percentage"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
// Relations
Product *Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
ProductVariant *ProductVariant `gorm:"foreignKey:VariantID" json:"product_variant,omitempty"`
Ingredient *Ingredient `gorm:"foreignKey:IngredientID" json:"ingredient,omitempty"`
}
func (pr *ProductRecipe) BeforeCreate(tx *gorm.DB) error {
if pr.ID == uuid.Nil {
pr.ID = uuid.New()
}
return nil
}
func (ProductRecipe) TableName() string {
return "product_recipes"
}
+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"
}
+5
View File
@@ -13,6 +13,11 @@ type Unit struct {
Name string `gorm:"not null;size:255" json:"name"`
Abbreviation *string `gorm:"size:50" json:"abbreviation"`
IsActive bool `gorm:"default:true" json:"is_active"`
DeletedAt *time.Time `gorm:"index" json:"deleted_at,omitempty"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
}
func (Unit) TableName() string {
return "units"
}
+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")
}
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) {
ctx := c.Request.Context()
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")
}
+7 -2
View File
@@ -1,6 +1,7 @@
package handler
import (
"fmt"
"strconv"
"apskel-pos-be/internal/appcontext"
@@ -35,6 +36,7 @@ func (h *CategoryHandler) CreateCategory(c *gin.Context) {
contextInfo := appcontext.FromGinContext(ctx)
var req contract.CreateCategoryRequest
fmt.Printf("CategoryHandler::CreateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("CategoryHandler::CreateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
@@ -71,6 +73,7 @@ func (h *CategoryHandler) UpdateCategory(c *gin.Context) {
}
var req contract.UpdateCategoryRequest
fmt.Printf("CategoryHandler::UpdateCategory -> Request: %+v\n", req)
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(ctx).WithError(err).Error("CategoryHandler::UpdateCategory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
@@ -138,10 +141,12 @@ func (h *CategoryHandler) GetCategory(c *gin.Context) {
func (h *CategoryHandler) ListCategories(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListCategoriesRequest{
Page: 1,
Limit: 10,
Page: 1,
Limit: 10,
OrganizationID: &contextInfo.OrganizationID,
}
// Parse query parameters
@@ -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 {
return &IngredientHandler{
ingredientService: ingredientService,
}
return &IngredientHandler{ingredientService: ingredientService}
}
func (h *IngredientHandler) Create(c *gin.Context) {
@@ -29,53 +27,53 @@ func (h *IngredientHandler) Create(c *gin.Context) {
var request models.CreateIngredientRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(c.Request.Context()).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{validationResponseError}), "IngredientHandler::Create")
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> request binding failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Create")
return
}
request.OrganizationID = contextInfo.OrganizationID
ingredientResponse, err := h.ingredientService.CreateIngredient(ctx, &request)
resp, err := h.ingredientService.CreateIngredient(ctx, &request)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> Failed to create ingredient from service")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Create")
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Create -> failed")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Create")
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) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
id, err := uuid.Parse(c.Param("id"))
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetByID -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetByID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
}), "IngredientHandler::GetByID")
return
}
ingredientResponse, err := h.ingredientService.GetIngredientByID(ctx, id)
resp, err := h.ingredientService.GetIngredientByID(ctx, id)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetByID -> Failed to get ingredient from service")
validationResponseError := contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Ingredient not found")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetByID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Ingredient not found"),
}), "IngredientHandler::GetByID")
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) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
// Get query parameters
pageStr := c.DefaultQuery("page", "1")
limitStr := c.DefaultQuery("limit", "10")
search := c.Query("search")
@@ -83,95 +81,177 @@ func (h *IngredientHandler) GetAll(c *gin.Context) {
page, err := strconv.Atoi(pageStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid page parameter")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid page parameter")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid page parameter"),
}), "IngredientHandler::GetAll")
return
}
limit, err := strconv.Atoi(limitStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid limit parameter")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid limit parameter")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid limit parameter"),
}), "IngredientHandler::GetAll")
return
}
var outletID *uuid.UUID
if outletIDStr != "" {
parsedOutletID, err := uuid.Parse(outletIDStr)
parsed, err := uuid.Parse(outletIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Invalid outlet ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid outlet ID"),
}), "IngredientHandler::GetAll")
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 {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::GetAll -> Failed to get ingredients from service")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, "Failed to get ingredients")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::GetAll")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, "Failed to get ingredients"),
}), "IngredientHandler::GetAll")
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) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
id, err := uuid.Parse(c.Param("id"))
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
}), "IngredientHandler::Update")
return
}
var request models.UpdateIngredientRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, "Invalid request body"),
}), "IngredientHandler::Update")
return
}
ingredientResponse, err := h.ingredientService.UpdateIngredient(ctx, id, &request)
resp, err := h.ingredientService.UpdateIngredient(ctx, id, &request)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Update -> Failed to update ingredient from service")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Update")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Update")
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) {
ctx := c.Request.Context()
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
id, err := uuid.Parse(c.Param("id"))
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Delete -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Delete")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID"),
}), "IngredientHandler::Delete")
return
}
err = h.ingredientService.DeleteIngredient(ctx, id)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("IngredientHandler::Delete -> Failed to delete ingredient from service")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "IngredientHandler::Delete")
if err := h.ingredientService.DeleteIngredient(ctx, id); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{
contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error()),
}), "IngredientHandler::Delete")
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",
}), "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
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)
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 (
"strconv"
"time"
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
"apskel-pos-be/internal/logger"
"apskel-pos-be/internal/models"
"apskel-pos-be/internal/service"
"apskel-pos-be/internal/util"
"apskel-pos-be/internal/validator"
@@ -138,13 +140,14 @@ func (h *InventoryHandler) GetInventory(c *gin.Context) {
func (h *InventoryHandler) ListInventory(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListInventoryRequest{
Page: 1,
Limit: 10,
Page: 1,
Limit: 10,
OutletID: &contextInfo.OutletID,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
@@ -235,6 +238,34 @@ func (h *InventoryHandler) AdjustInventory(c *gin.Context) {
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) {
ctx := c.Request.Context()
@@ -276,3 +307,131 @@ func (h *InventoryHandler) GetZeroStockItems(c *gin.Context) {
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")
}
+31 -5
View File
@@ -122,6 +122,9 @@ func (h *OrderHandler) AddToOrder(c *gin.Context) {
func (h *OrderHandler) ListOrders(c *gin.Context) {
var query contract.ListOrdersQuery
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
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")
return
@@ -133,6 +136,7 @@ func (h *OrderHandler) ListOrders(c *gin.Context) {
return
}
modelReq.OrganizationID = &contextInfo.OrganizationID
response, err := h.orderService.ListOrders(c.Request.Context(), modelReq)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::ListOrders", err.Error())}), "OrderHandler::ListOrders")
@@ -265,7 +269,6 @@ func (h *OrderHandler) SetOrderCustomer(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
// Parse order ID from URL parameter
orderIDStr := c.Param("id")
orderID, err := uuid.Parse(orderIDStr)
if err != nil {
@@ -273,24 +276,47 @@ func (h *OrderHandler) SetOrderCustomer(c *gin.Context) {
return
}
// Parse request body
var req contract.SetOrderCustomerRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::SetOrderCustomer", err.Error())}), "OrderHandler::SetOrderCustomer")
return
}
// Transform contract to model
modelReq := transformer.SetOrderCustomerContractToModel(&req)
// Call service
response, err := h.orderService.SetOrderCustomer(ctx, orderID, modelReq, contextInfo.OrganizationID)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::SetOrderCustomer", err.Error())}), "OrderHandler::SetOrderCustomer")
return
}
// Transform model to contract
contractResp := transformer.SetOrderCustomerModelToContract(response)
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::SetOrderCustomer")
}
func (h *OrderHandler) SplitBill(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var req contract.SplitBillRequest
if err := c.ShouldBindJSON(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("invalid_request", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
return
}
if err := h.validator.Validate(&req); err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("validation_failed", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
return
}
req.OrganizationID = contextInfo.OrganizationID
modelReq := transformer.SplitBillContractToModel(&req)
response, err := h.orderService.SplitBill(c.Request.Context(), modelReq)
if err != nil {
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{contract.NewResponseError("internal_error", "OrderHandler::SplitBill", err.Error())}), "OrderHandler::SplitBill")
return
}
contractResp := transformer.SplitBillModelToContract(response)
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(contractResp), "OrderHandler::SplitBill")
}
@@ -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 -1
View File
@@ -36,7 +36,6 @@ func (h *OutletHandler) ListOutlets(c *gin.Context) {
OrganizationID: contextInfo.OrganizationID,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
@@ -103,6 +102,37 @@ func (h *OutletHandler) GetOutlet(c *gin.Context) {
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) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
+3 -7
View File
@@ -51,7 +51,7 @@ func (h *PaymentMethodHandler) CreatePaymentMethod(c *gin.Context) {
req.OrganizationID = contextInfo.OrganizationID
req.OutletID = contextInfo.OutletID
paymentMethodResponse := h.paymentMethodService.CreatePaymentMethod(ctx, contextInfo, &req)
if paymentMethodResponse.HasErrors() {
errorResp := paymentMethodResponse.GetErrors()[0]
@@ -84,13 +84,13 @@ func (h *PaymentMethodHandler) GetPaymentMethod(c *gin.Context) {
func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListPaymentMethodsRequest{
Page: 1,
Limit: 10,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
@@ -111,11 +111,7 @@ func (h *PaymentMethodHandler) ListPaymentMethods(c *gin.Context) {
req.Type = &paymentMethodType
}
if organizationIDStr := c.Query("organization_id"); organizationIDStr != "" {
if organizationID, err := uuid.Parse(organizationIDStr); err == nil {
req.OrganizationID = &organizationID
}
}
req.OrganizationID = &contextInfo.OrganizationID
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
+4 -3
View File
@@ -138,13 +138,14 @@ func (h *ProductHandler) GetProduct(c *gin.Context) {
func (h *ProductHandler) ListProducts(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
req := &contract.ListProductsRequest{
Page: 1,
Limit: 10,
Page: 1,
Limit: 10,
OrganizationID: &contextInfo.OrganizationID,
}
// Parse query parameters
if pageStr := c.Query("page"); pageStr != "" {
if page, err := strconv.Atoi(pageStr); err == nil {
req.Page = page
+222
View File
@@ -0,0 +1,222 @@
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"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type ProductRecipeHandler struct {
productRecipeService service.ProductRecipeService
}
func NewProductRecipeHandler(productRecipeService service.ProductRecipeService) *ProductRecipeHandler {
return &ProductRecipeHandler{
productRecipeService: productRecipeService,
}
}
func (h *ProductRecipeHandler) Create(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var request contract.CreateProductRecipeRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Create")
return
}
recipeResponse, err := h.productRecipeService.Create(ctx, contextInfo.OrganizationID, &request)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Create -> Failed to create product recipe")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Create")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::Create")
}
func (h *ProductRecipeHandler) GetByID(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByID -> Invalid recipe ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByID")
return
}
recipeResponse, err := h.productRecipeService.GetByID(ctx, id, contextInfo.OrganizationID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByID -> Failed to get product recipe")
validationResponseError := contract.NewResponseError(constants.NotFoundErrorCode, constants.RequestEntity, "Product recipe not found")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::GetByID")
}
func (h *ProductRecipeHandler) GetByProductID(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
// Parse product ID from URL parameter
productIDStr := c.Param("product_id")
productID, err := uuid.Parse(productIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid product ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid product ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
return
}
// Parse optional variant ID from query parameter
var variantID *uuid.UUID
if variantIDStr := c.Query("variant_id"); variantIDStr != "" {
parsed, err := uuid.Parse(variantIDStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Invalid variant ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid variant ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
return
}
variantID = &parsed
}
// Create request object
request := &contract.GetProductRecipeByProductIDRequest{
ProductID: productID,
VariantID: variantID,
}
// Call service
recipes, err := h.productRecipeService.GetByProductID(ctx, request, contextInfo.OrganizationID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByProductID -> Failed to get product recipes")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByProductID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipes), "ProductRecipeHandler::GetByProductID")
}
func (h *ProductRecipeHandler) GetByIngredientID(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("ProductRecipeHandler::GetByIngredientID -> Invalid ingredient ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid ingredient ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByIngredientID")
return
}
recipes, err := h.productRecipeService.GetByIngredientID(ctx, ingredientID, contextInfo.OrganizationID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::GetByIngredientID -> Failed to get product recipes")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::GetByIngredientID")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipes), "ProductRecipeHandler::GetByIngredientID")
}
func (h *ProductRecipeHandler) Update(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> Invalid recipe ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
return
}
var request contract.UpdateProductRecipeRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
return
}
recipeResponse, err := h.productRecipeService.Update(ctx, id, contextInfo.OrganizationID, &request)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Update -> Failed to update product recipe")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Update")
return
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(recipeResponse), "ProductRecipeHandler::Update")
}
func (h *ProductRecipeHandler) Delete(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
idStr := c.Param("id")
id, err := uuid.Parse(idStr)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Delete -> Invalid recipe ID")
validationResponseError := contract.NewResponseError(constants.MalformedFieldErrorCode, constants.RequestEntity, "Invalid recipe ID")
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Delete")
return
}
err = h.productRecipeService.Delete(ctx, id, contextInfo.OrganizationID)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::Delete -> Failed to delete product recipe")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::Delete")
return
}
response := map[string]interface{}{
"message": "Product recipe deleted successfully",
}
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(response), "ProductRecipeHandler::Delete")
}
func (h *ProductRecipeHandler) BulkCreate(c *gin.Context) {
ctx := c.Request.Context()
contextInfo := appcontext.FromGinContext(ctx)
var request contract.BulkCreateProductRecipeRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::BulkCreate")
return
}
recipes, err := h.productRecipeService.BulkCreate(ctx, contextInfo.OrganizationID, &request)
if err != nil {
logger.FromContext(ctx).WithError(err).Error("ProductRecipeHandler::BulkCreate -> Failed to bulk create product recipes")
validationResponseError := contract.NewResponseError(constants.InternalServerErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "ProductRecipeHandler::BulkCreate")
return
}
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(recipes))
}
+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")
}

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