add product ingredients
This commit is contained in:
parent
efe09c21e4
commit
3a04990ec8
@ -92,6 +92,8 @@ func (a *App) Initialize(cfg *config.Config) error {
|
|||||||
validators.chartOfAccountValidator,
|
validators.chartOfAccountValidator,
|
||||||
services.accountService,
|
services.accountService,
|
||||||
validators.accountValidator,
|
validators.accountValidator,
|
||||||
|
*services.orderIngredientTransactionService,
|
||||||
|
validators.orderIngredientTransactionValidator,
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -137,95 +139,103 @@ func (a *App) Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type repositories struct {
|
type repositories struct {
|
||||||
userRepo *repository.UserRepositoryImpl
|
userRepo *repository.UserRepositoryImpl
|
||||||
organizationRepo *repository.OrganizationRepositoryImpl
|
organizationRepo *repository.OrganizationRepositoryImpl
|
||||||
outletRepo *repository.OutletRepositoryImpl
|
outletRepo *repository.OutletRepositoryImpl
|
||||||
outletSettingRepo *repository.OutletSettingRepositoryImpl
|
outletSettingRepo *repository.OutletSettingRepositoryImpl
|
||||||
categoryRepo *repository.CategoryRepositoryImpl
|
categoryRepo *repository.CategoryRepositoryImpl
|
||||||
productRepo *repository.ProductRepositoryImpl
|
productRepo *repository.ProductRepositoryImpl
|
||||||
productVariantRepo *repository.ProductVariantRepositoryImpl
|
productVariantRepo *repository.ProductVariantRepositoryImpl
|
||||||
inventoryRepo *repository.InventoryRepositoryImpl
|
inventoryRepo *repository.InventoryRepositoryImpl
|
||||||
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
|
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
|
||||||
orderRepo *repository.OrderRepositoryImpl
|
orderRepo *repository.OrderRepositoryImpl
|
||||||
orderItemRepo *repository.OrderItemRepositoryImpl
|
orderItemRepo *repository.OrderItemRepositoryImpl
|
||||||
paymentRepo *repository.PaymentRepositoryImpl
|
paymentRepo *repository.PaymentRepositoryImpl
|
||||||
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
|
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
|
||||||
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
|
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
|
||||||
fileRepo *repository.FileRepositoryImpl
|
fileRepo *repository.FileRepositoryImpl
|
||||||
customerRepo *repository.CustomerRepository
|
customerRepo *repository.CustomerRepository
|
||||||
analyticsRepo *repository.AnalyticsRepositoryImpl
|
analyticsRepo *repository.AnalyticsRepositoryImpl
|
||||||
tableRepo *repository.TableRepository
|
tableRepo *repository.TableRepository
|
||||||
unitRepo *repository.UnitRepository
|
unitRepo *repository.UnitRepository
|
||||||
ingredientRepo *repository.IngredientRepository
|
ingredientRepo *repository.IngredientRepository
|
||||||
productRecipeRepo *repository.ProductRecipeRepository
|
productRecipeRepo *repository.ProductRecipeRepository
|
||||||
vendorRepo *repository.VendorRepositoryImpl
|
vendorRepo *repository.VendorRepositoryImpl
|
||||||
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
|
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
|
||||||
unitConverterRepo *repository.IngredientUnitConverterRepositoryImpl
|
unitConverterRepo *repository.IngredientUnitConverterRepositoryImpl
|
||||||
chartOfAccountTypeRepo *repository.ChartOfAccountTypeRepositoryImpl
|
chartOfAccountTypeRepo *repository.ChartOfAccountTypeRepositoryImpl
|
||||||
chartOfAccountRepo *repository.ChartOfAccountRepositoryImpl
|
chartOfAccountRepo *repository.ChartOfAccountRepositoryImpl
|
||||||
accountRepo *repository.AccountRepositoryImpl
|
accountRepo *repository.AccountRepositoryImpl
|
||||||
txManager *repository.TxManager
|
orderIngredientTransactionRepo *repository.OrderIngredientTransactionRepositoryImpl
|
||||||
|
productIngredientRepo *repository.ProductIngredientRepository
|
||||||
|
txManager *repository.TxManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initRepositories() *repositories {
|
func (a *App) initRepositories() *repositories {
|
||||||
return &repositories{
|
return &repositories{
|
||||||
userRepo: repository.NewUserRepository(a.db),
|
userRepo: repository.NewUserRepository(a.db),
|
||||||
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
|
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
|
||||||
outletRepo: repository.NewOutletRepositoryImpl(a.db),
|
outletRepo: repository.NewOutletRepositoryImpl(a.db),
|
||||||
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
|
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
|
||||||
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
|
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
|
||||||
productRepo: repository.NewProductRepositoryImpl(a.db),
|
productRepo: repository.NewProductRepositoryImpl(a.db),
|
||||||
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
|
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
|
||||||
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
|
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
|
||||||
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
|
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
|
||||||
orderRepo: repository.NewOrderRepositoryImpl(a.db),
|
orderRepo: repository.NewOrderRepositoryImpl(a.db),
|
||||||
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
|
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
|
||||||
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
|
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
|
||||||
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
|
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
|
||||||
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
|
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
|
||||||
fileRepo: repository.NewFileRepositoryImpl(a.db),
|
fileRepo: repository.NewFileRepositoryImpl(a.db),
|
||||||
customerRepo: repository.NewCustomerRepository(a.db),
|
customerRepo: repository.NewCustomerRepository(a.db),
|
||||||
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
|
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
|
||||||
tableRepo: repository.NewTableRepository(a.db),
|
tableRepo: repository.NewTableRepository(a.db),
|
||||||
unitRepo: repository.NewUnitRepository(a.db),
|
unitRepo: repository.NewUnitRepository(a.db),
|
||||||
ingredientRepo: repository.NewIngredientRepository(a.db),
|
ingredientRepo: repository.NewIngredientRepository(a.db),
|
||||||
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
|
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
|
||||||
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
|
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
|
||||||
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db),
|
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db),
|
||||||
unitConverterRepo: repository.NewIngredientUnitConverterRepositoryImpl(a.db).(*repository.IngredientUnitConverterRepositoryImpl),
|
unitConverterRepo: repository.NewIngredientUnitConverterRepositoryImpl(a.db).(*repository.IngredientUnitConverterRepositoryImpl),
|
||||||
chartOfAccountTypeRepo: repository.NewChartOfAccountTypeRepositoryImpl(a.db),
|
chartOfAccountTypeRepo: repository.NewChartOfAccountTypeRepositoryImpl(a.db),
|
||||||
chartOfAccountRepo: repository.NewChartOfAccountRepositoryImpl(a.db),
|
chartOfAccountRepo: repository.NewChartOfAccountRepositoryImpl(a.db),
|
||||||
accountRepo: repository.NewAccountRepositoryImpl(a.db),
|
accountRepo: repository.NewAccountRepositoryImpl(a.db),
|
||||||
txManager: repository.NewTxManager(a.db),
|
orderIngredientTransactionRepo: repository.NewOrderIngredientTransactionRepositoryImpl(a.db).(*repository.OrderIngredientTransactionRepositoryImpl),
|
||||||
|
productIngredientRepo: func() *repository.ProductIngredientRepository {
|
||||||
|
db, _ := a.db.DB()
|
||||||
|
return repository.NewProductIngredientRepository(db)
|
||||||
|
}(),
|
||||||
|
txManager: repository.NewTxManager(a.db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type processors struct {
|
type processors struct {
|
||||||
userProcessor *processor.UserProcessorImpl
|
userProcessor *processor.UserProcessorImpl
|
||||||
organizationProcessor processor.OrganizationProcessor
|
organizationProcessor processor.OrganizationProcessor
|
||||||
outletProcessor processor.OutletProcessor
|
outletProcessor processor.OutletProcessor
|
||||||
outletSettingProcessor *processor.OutletSettingProcessorImpl
|
outletSettingProcessor *processor.OutletSettingProcessorImpl
|
||||||
categoryProcessor processor.CategoryProcessor
|
categoryProcessor processor.CategoryProcessor
|
||||||
productProcessor processor.ProductProcessor
|
productProcessor processor.ProductProcessor
|
||||||
productVariantProcessor processor.ProductVariantProcessor
|
productVariantProcessor processor.ProductVariantProcessor
|
||||||
inventoryProcessor processor.InventoryProcessor
|
inventoryProcessor processor.InventoryProcessor
|
||||||
orderProcessor processor.OrderProcessor
|
orderProcessor processor.OrderProcessor
|
||||||
paymentMethodProcessor processor.PaymentMethodProcessor
|
paymentMethodProcessor processor.PaymentMethodProcessor
|
||||||
fileProcessor processor.FileProcessor
|
fileProcessor processor.FileProcessor
|
||||||
customerProcessor *processor.CustomerProcessor
|
customerProcessor *processor.CustomerProcessor
|
||||||
analyticsProcessor *processor.AnalyticsProcessorImpl
|
analyticsProcessor *processor.AnalyticsProcessorImpl
|
||||||
tableProcessor *processor.TableProcessor
|
tableProcessor *processor.TableProcessor
|
||||||
unitProcessor *processor.UnitProcessorImpl
|
unitProcessor *processor.UnitProcessorImpl
|
||||||
ingredientProcessor *processor.IngredientProcessorImpl
|
ingredientProcessor *processor.IngredientProcessorImpl
|
||||||
productRecipeProcessor *processor.ProductRecipeProcessorImpl
|
productRecipeProcessor *processor.ProductRecipeProcessorImpl
|
||||||
vendorProcessor *processor.VendorProcessorImpl
|
vendorProcessor *processor.VendorProcessorImpl
|
||||||
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
|
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
|
||||||
unitConverterProcessor *processor.IngredientUnitConverterProcessorImpl
|
unitConverterProcessor *processor.IngredientUnitConverterProcessorImpl
|
||||||
chartOfAccountTypeProcessor *processor.ChartOfAccountTypeProcessorImpl
|
chartOfAccountTypeProcessor *processor.ChartOfAccountTypeProcessorImpl
|
||||||
chartOfAccountProcessor *processor.ChartOfAccountProcessorImpl
|
chartOfAccountProcessor *processor.ChartOfAccountProcessorImpl
|
||||||
accountProcessor *processor.AccountProcessorImpl
|
accountProcessor *processor.AccountProcessorImpl
|
||||||
fileClient processor.FileClient
|
orderIngredientTransactionProcessor *processor.OrderIngredientTransactionProcessorImpl
|
||||||
inventoryMovementService service.InventoryMovementService
|
fileClient processor.FileClient
|
||||||
|
inventoryMovementService service.InventoryMovementService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
||||||
@ -233,60 +243,62 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
|
|||||||
inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo)
|
inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo)
|
||||||
|
|
||||||
return &processors{
|
return &processors{
|
||||||
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
|
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
|
||||||
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
|
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
|
||||||
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
|
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
|
||||||
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
|
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
|
||||||
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
|
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
|
||||||
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
|
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
|
||||||
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
|
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
|
||||||
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
|
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),
|
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService),
|
||||||
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
|
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
|
||||||
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
|
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
|
||||||
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
|
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
|
||||||
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo),
|
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo),
|
||||||
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
|
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
|
||||||
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
|
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
|
||||||
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo),
|
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo),
|
||||||
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
||||||
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
||||||
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
|
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),
|
unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo),
|
||||||
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
||||||
chartOfAccountProcessor: processor.NewChartOfAccountProcessorImpl(repos.chartOfAccountRepo, repos.chartOfAccountTypeRepo),
|
chartOfAccountProcessor: processor.NewChartOfAccountProcessorImpl(repos.chartOfAccountRepo, repos.chartOfAccountTypeRepo),
|
||||||
accountProcessor: processor.NewAccountProcessorImpl(repos.accountRepo, repos.chartOfAccountRepo),
|
accountProcessor: processor.NewAccountProcessorImpl(repos.accountRepo, repos.chartOfAccountRepo),
|
||||||
fileClient: fileClient,
|
orderIngredientTransactionProcessor: processor.NewOrderIngredientTransactionProcessorImpl(repos.orderIngredientTransactionRepo, repos.productIngredientRepo, repos.ingredientRepo, repos.unitRepo).(*processor.OrderIngredientTransactionProcessorImpl),
|
||||||
inventoryMovementService: inventoryMovementService,
|
fileClient: fileClient,
|
||||||
|
inventoryMovementService: inventoryMovementService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type services struct {
|
type services struct {
|
||||||
userService *service.UserServiceImpl
|
userService *service.UserServiceImpl
|
||||||
authService service.AuthService
|
authService service.AuthService
|
||||||
organizationService service.OrganizationService
|
organizationService service.OrganizationService
|
||||||
outletService service.OutletService
|
outletService service.OutletService
|
||||||
outletSettingService service.OutletSettingService
|
outletSettingService service.OutletSettingService
|
||||||
categoryService service.CategoryService
|
categoryService service.CategoryService
|
||||||
productService service.ProductService
|
productService service.ProductService
|
||||||
productVariantService service.ProductVariantService
|
productVariantService service.ProductVariantService
|
||||||
inventoryService service.InventoryService
|
inventoryService service.InventoryService
|
||||||
orderService service.OrderService
|
orderService service.OrderService
|
||||||
paymentMethodService service.PaymentMethodService
|
paymentMethodService service.PaymentMethodService
|
||||||
fileService service.FileService
|
fileService service.FileService
|
||||||
customerService service.CustomerService
|
customerService service.CustomerService
|
||||||
analyticsService *service.AnalyticsServiceImpl
|
analyticsService *service.AnalyticsServiceImpl
|
||||||
reportService service.ReportService
|
reportService service.ReportService
|
||||||
tableService *service.TableServiceImpl
|
tableService *service.TableServiceImpl
|
||||||
unitService *service.UnitServiceImpl
|
unitService *service.UnitServiceImpl
|
||||||
ingredientService *service.IngredientServiceImpl
|
ingredientService *service.IngredientServiceImpl
|
||||||
productRecipeService *service.ProductRecipeServiceImpl
|
productRecipeService *service.ProductRecipeServiceImpl
|
||||||
vendorService *service.VendorServiceImpl
|
vendorService *service.VendorServiceImpl
|
||||||
purchaseOrderService *service.PurchaseOrderServiceImpl
|
purchaseOrderService *service.PurchaseOrderServiceImpl
|
||||||
unitConverterService *service.IngredientUnitConverterServiceImpl
|
unitConverterService *service.IngredientUnitConverterServiceImpl
|
||||||
chartOfAccountTypeService service.ChartOfAccountTypeService
|
chartOfAccountTypeService service.ChartOfAccountTypeService
|
||||||
chartOfAccountService service.ChartOfAccountService
|
chartOfAccountService service.ChartOfAccountService
|
||||||
accountService service.AccountService
|
accountService service.AccountService
|
||||||
|
orderIngredientTransactionService *service.OrderIngredientTransactionService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
||||||
@ -300,7 +312,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
productService := service.NewProductService(processors.productProcessor)
|
productService := service.NewProductService(processors.productProcessor)
|
||||||
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
||||||
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
|
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
|
||||||
orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo)
|
orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productIngredientRepo, repos.txManager) // Will be updated after orderIngredientTransactionService is created
|
||||||
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
|
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
|
||||||
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
||||||
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
|
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
|
||||||
@ -316,33 +328,38 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
|||||||
chartOfAccountTypeService := service.NewChartOfAccountTypeService(processors.chartOfAccountTypeProcessor)
|
chartOfAccountTypeService := service.NewChartOfAccountTypeService(processors.chartOfAccountTypeProcessor)
|
||||||
chartOfAccountService := service.NewChartOfAccountService(processors.chartOfAccountProcessor)
|
chartOfAccountService := service.NewChartOfAccountService(processors.chartOfAccountProcessor)
|
||||||
accountService := service.NewAccountService(processors.accountProcessor)
|
accountService := service.NewAccountService(processors.accountProcessor)
|
||||||
|
orderIngredientTransactionService := service.NewOrderIngredientTransactionService(processors.orderIngredientTransactionProcessor, repos.txManager)
|
||||||
|
|
||||||
|
// Update order service with order ingredient transaction service
|
||||||
|
orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productIngredientRepo, repos.txManager)
|
||||||
|
|
||||||
return &services{
|
return &services{
|
||||||
userService: service.NewUserService(processors.userProcessor),
|
userService: service.NewUserService(processors.userProcessor),
|
||||||
authService: authService,
|
authService: authService,
|
||||||
organizationService: organizationService,
|
organizationService: organizationService,
|
||||||
outletService: outletService,
|
outletService: outletService,
|
||||||
outletSettingService: outletSettingService,
|
outletSettingService: outletSettingService,
|
||||||
categoryService: categoryService,
|
categoryService: categoryService,
|
||||||
productService: productService,
|
productService: productService,
|
||||||
productVariantService: productVariantService,
|
productVariantService: productVariantService,
|
||||||
inventoryService: inventoryService,
|
inventoryService: inventoryService,
|
||||||
orderService: orderService,
|
orderService: orderService,
|
||||||
paymentMethodService: paymentMethodService,
|
paymentMethodService: paymentMethodService,
|
||||||
fileService: fileService,
|
fileService: fileService,
|
||||||
customerService: customerService,
|
customerService: customerService,
|
||||||
analyticsService: analyticsService,
|
analyticsService: analyticsService,
|
||||||
reportService: reportService,
|
reportService: reportService,
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
unitService: unitService,
|
unitService: unitService,
|
||||||
ingredientService: ingredientService,
|
ingredientService: ingredientService,
|
||||||
productRecipeService: productRecipeService,
|
productRecipeService: productRecipeService,
|
||||||
vendorService: vendorService,
|
vendorService: vendorService,
|
||||||
purchaseOrderService: purchaseOrderService,
|
purchaseOrderService: purchaseOrderService,
|
||||||
unitConverterService: unitConverterService,
|
unitConverterService: unitConverterService,
|
||||||
chartOfAccountTypeService: chartOfAccountTypeService,
|
chartOfAccountTypeService: chartOfAccountTypeService,
|
||||||
chartOfAccountService: chartOfAccountService,
|
chartOfAccountService: chartOfAccountService,
|
||||||
accountService: accountService,
|
accountService: accountService,
|
||||||
|
orderIngredientTransactionService: orderIngredientTransactionService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,45 +374,47 @@ func (a *App) initMiddleware(services *services) *middlewares {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type validators struct {
|
type validators struct {
|
||||||
userValidator *validator.UserValidatorImpl
|
userValidator *validator.UserValidatorImpl
|
||||||
organizationValidator validator.OrganizationValidator
|
organizationValidator validator.OrganizationValidator
|
||||||
outletValidator validator.OutletValidator
|
outletValidator validator.OutletValidator
|
||||||
categoryValidator validator.CategoryValidator
|
categoryValidator validator.CategoryValidator
|
||||||
productValidator validator.ProductValidator
|
productValidator validator.ProductValidator
|
||||||
productVariantValidator validator.ProductVariantValidator
|
productVariantValidator validator.ProductVariantValidator
|
||||||
inventoryValidator validator.InventoryValidator
|
inventoryValidator validator.InventoryValidator
|
||||||
orderValidator validator.OrderValidator
|
orderValidator validator.OrderValidator
|
||||||
paymentMethodValidator validator.PaymentMethodValidator
|
paymentMethodValidator validator.PaymentMethodValidator
|
||||||
fileValidator validator.FileValidator
|
fileValidator validator.FileValidator
|
||||||
customerValidator validator.CustomerValidator
|
customerValidator validator.CustomerValidator
|
||||||
tableValidator *validator.TableValidator
|
tableValidator *validator.TableValidator
|
||||||
vendorValidator *validator.VendorValidatorImpl
|
vendorValidator *validator.VendorValidatorImpl
|
||||||
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
|
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
|
||||||
unitConverterValidator *validator.IngredientUnitConverterValidatorImpl
|
unitConverterValidator *validator.IngredientUnitConverterValidatorImpl
|
||||||
chartOfAccountTypeValidator *validator.ChartOfAccountTypeValidatorImpl
|
chartOfAccountTypeValidator *validator.ChartOfAccountTypeValidatorImpl
|
||||||
chartOfAccountValidator *validator.ChartOfAccountValidatorImpl
|
chartOfAccountValidator *validator.ChartOfAccountValidatorImpl
|
||||||
accountValidator *validator.AccountValidatorImpl
|
accountValidator *validator.AccountValidatorImpl
|
||||||
|
orderIngredientTransactionValidator *validator.OrderIngredientTransactionValidatorImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initValidators() *validators {
|
func (a *App) initValidators() *validators {
|
||||||
return &validators{
|
return &validators{
|
||||||
userValidator: validator.NewUserValidator(),
|
userValidator: validator.NewUserValidator(),
|
||||||
organizationValidator: validator.NewOrganizationValidator(),
|
organizationValidator: validator.NewOrganizationValidator(),
|
||||||
outletValidator: validator.NewOutletValidator(),
|
outletValidator: validator.NewOutletValidator(),
|
||||||
categoryValidator: validator.NewCategoryValidator(),
|
categoryValidator: validator.NewCategoryValidator(),
|
||||||
productValidator: validator.NewProductValidator(),
|
productValidator: validator.NewProductValidator(),
|
||||||
productVariantValidator: validator.NewProductVariantValidator(),
|
productVariantValidator: validator.NewProductVariantValidator(),
|
||||||
inventoryValidator: validator.NewInventoryValidator(),
|
inventoryValidator: validator.NewInventoryValidator(),
|
||||||
orderValidator: validator.NewOrderValidator(),
|
orderValidator: validator.NewOrderValidator(),
|
||||||
paymentMethodValidator: validator.NewPaymentMethodValidator(),
|
paymentMethodValidator: validator.NewPaymentMethodValidator(),
|
||||||
fileValidator: validator.NewFileValidatorImpl(),
|
fileValidator: validator.NewFileValidatorImpl(),
|
||||||
customerValidator: validator.NewCustomerValidator(),
|
customerValidator: validator.NewCustomerValidator(),
|
||||||
tableValidator: validator.NewTableValidator(),
|
tableValidator: validator.NewTableValidator(),
|
||||||
vendorValidator: validator.NewVendorValidator(),
|
vendorValidator: validator.NewVendorValidator(),
|
||||||
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
|
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
|
||||||
unitConverterValidator: validator.NewIngredientUnitConverterValidator().(*validator.IngredientUnitConverterValidatorImpl),
|
unitConverterValidator: validator.NewIngredientUnitConverterValidator().(*validator.IngredientUnitConverterValidatorImpl),
|
||||||
chartOfAccountTypeValidator: validator.NewChartOfAccountTypeValidator().(*validator.ChartOfAccountTypeValidatorImpl),
|
chartOfAccountTypeValidator: validator.NewChartOfAccountTypeValidator().(*validator.ChartOfAccountTypeValidatorImpl),
|
||||||
chartOfAccountValidator: validator.NewChartOfAccountValidator().(*validator.ChartOfAccountValidatorImpl),
|
chartOfAccountValidator: validator.NewChartOfAccountValidator().(*validator.ChartOfAccountValidatorImpl),
|
||||||
accountValidator: validator.NewAccountValidator().(*validator.AccountValidatorImpl),
|
accountValidator: validator.NewAccountValidator().(*validator.AccountValidatorImpl),
|
||||||
|
orderIngredientTransactionValidator: validator.NewOrderIngredientTransactionValidator().(*validator.OrderIngredientTransactionValidatorImpl),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,3 +74,11 @@ type ListIngredientUnitConvertersResponse struct {
|
|||||||
TotalPages int `json:"total_pages"`
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
20
internal/contract/order_ingredient_transaction_contract.go
Normal file
20
internal/contract/order_ingredient_transaction_contract.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
79
internal/contract/order_ingredient_transaction_request.go
Normal file
79
internal/contract/order_ingredient_transaction_request.go
Normal file
@ -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"`
|
||||||
|
}
|
||||||
48
internal/entities/order_ingredient_transaction.go
Normal file
48
internal/entities/order_ingredient_transaction.go
Normal file
@ -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"
|
||||||
|
}
|
||||||
@ -7,14 +7,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProductIngredient struct {
|
type ProductIngredient struct {
|
||||||
ID uuid.UUID `json:"id" db:"id"`
|
ID uuid.UUID `json:"id" db:"id"`
|
||||||
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
|
OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"`
|
||||||
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
|
OutletID *uuid.UUID `json:"outlet_id" db:"outlet_id"`
|
||||||
ProductID uuid.UUID `json:"product_id" db:"product_id"`
|
ProductID uuid.UUID `json:"product_id" db:"product_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id" db:"ingredient_id"`
|
||||||
Quantity float64 `json:"quantity" db:"quantity"`
|
Quantity float64 `json:"quantity" db:"quantity"`
|
||||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
WastePercentage float64 `json:"waste_percentage" db:"waste_percentage"`
|
||||||
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
Product *Product `json:"product,omitempty"`
|
Product *Product `json:"product,omitempty"`
|
||||||
|
|||||||
@ -254,3 +254,25 @@ func (h *IngredientUnitConverterHandler) ConvertUnit(c *gin.Context) {
|
|||||||
util.HandleResponse(c.Writer, c.Request, converterResponse, "IngredientUnitConverterHandler::ConvertUnit")
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
201
internal/handler/order_ingredient_transaction_handler.go
Normal file
201
internal/handler/order_ingredient_transaction_handler.go
Normal file
@ -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")
|
||||||
|
}
|
||||||
21
internal/handler/order_ingredient_transaction_interface.go
Normal file
21
internal/handler/order_ingredient_transaction_interface.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
@ -166,3 +166,110 @@ func ModelToContractAccountResponse(resp *models.AccountResponse) *contract.Acco
|
|||||||
|
|
||||||
return contractResp
|
return contractResp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Order Ingredient Transaction mappers
|
||||||
|
func ContractToModelCreateOrderIngredientTransactionRequest(req *contract.CreateOrderIngredientTransactionRequest) *models.CreateOrderIngredientTransactionRequest {
|
||||||
|
return &models.CreateOrderIngredientTransactionRequest{
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
OrderItemID: req.OrderItemID,
|
||||||
|
ProductID: req.ProductID,
|
||||||
|
ProductVariantID: req.ProductVariantID,
|
||||||
|
IngredientID: req.IngredientID,
|
||||||
|
GrossQty: req.GrossQty,
|
||||||
|
NetQty: req.NetQty,
|
||||||
|
WasteQty: req.WasteQty,
|
||||||
|
Unit: req.Unit,
|
||||||
|
TransactionDate: req.TransactionDate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContractToModelUpdateOrderIngredientTransactionRequest(req *contract.UpdateOrderIngredientTransactionRequest) *models.UpdateOrderIngredientTransactionRequest {
|
||||||
|
return &models.UpdateOrderIngredientTransactionRequest{
|
||||||
|
GrossQty: req.GrossQty,
|
||||||
|
NetQty: req.NetQty,
|
||||||
|
WasteQty: req.WasteQty,
|
||||||
|
Unit: req.Unit,
|
||||||
|
TransactionDate: req.TransactionDate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelToContractOrderIngredientTransactionResponse(resp *models.OrderIngredientTransactionResponse) *contract.OrderIngredientTransactionResponse {
|
||||||
|
return &contract.OrderIngredientTransactionResponse{
|
||||||
|
ID: resp.ID,
|
||||||
|
OrganizationID: resp.OrganizationID,
|
||||||
|
OutletID: resp.OutletID,
|
||||||
|
OrderID: resp.OrderID,
|
||||||
|
OrderItemID: resp.OrderItemID,
|
||||||
|
ProductID: resp.ProductID,
|
||||||
|
ProductVariantID: resp.ProductVariantID,
|
||||||
|
IngredientID: resp.IngredientID,
|
||||||
|
GrossQty: resp.GrossQty,
|
||||||
|
NetQty: resp.NetQty,
|
||||||
|
WasteQty: resp.WasteQty,
|
||||||
|
Unit: resp.Unit,
|
||||||
|
TransactionDate: resp.TransactionDate,
|
||||||
|
CreatedBy: resp.CreatedBy,
|
||||||
|
CreatedAt: resp.CreatedAt,
|
||||||
|
UpdatedAt: resp.UpdatedAt,
|
||||||
|
Organization: resp.Organization,
|
||||||
|
Outlet: resp.Outlet,
|
||||||
|
Order: resp.Order,
|
||||||
|
OrderItem: resp.OrderItem,
|
||||||
|
Product: resp.Product,
|
||||||
|
ProductVariant: resp.ProductVariant,
|
||||||
|
Ingredient: resp.Ingredient,
|
||||||
|
CreatedByUser: resp.CreatedByUser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelToContractOrderIngredientTransactionResponses(responses []*models.OrderIngredientTransactionResponse) []*contract.OrderIngredientTransactionResponse {
|
||||||
|
if responses == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contractResponses := make([]*contract.OrderIngredientTransactionResponse, len(responses))
|
||||||
|
for i, resp := range responses {
|
||||||
|
contractResponses[i] = ModelToContractOrderIngredientTransactionResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return contractResponses
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContractToModelListOrderIngredientTransactionsRequest(req *contract.ListOrderIngredientTransactionsRequest) *models.ListOrderIngredientTransactionsRequest {
|
||||||
|
return &models.ListOrderIngredientTransactionsRequest{
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
OrderItemID: req.OrderItemID,
|
||||||
|
ProductID: req.ProductID,
|
||||||
|
ProductVariantID: req.ProductVariantID,
|
||||||
|
IngredientID: req.IngredientID,
|
||||||
|
StartDate: req.StartDate,
|
||||||
|
EndDate: req.EndDate,
|
||||||
|
Page: req.Page,
|
||||||
|
Limit: req.Limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelToContractOrderIngredientTransactionSummary(resp *models.OrderIngredientTransactionSummary) *contract.OrderIngredientTransactionSummary {
|
||||||
|
return &contract.OrderIngredientTransactionSummary{
|
||||||
|
IngredientID: resp.IngredientID,
|
||||||
|
IngredientName: resp.IngredientName,
|
||||||
|
TotalGrossQty: resp.TotalGrossQty,
|
||||||
|
TotalNetQty: resp.TotalNetQty,
|
||||||
|
TotalWasteQty: resp.TotalWasteQty,
|
||||||
|
WastePercentage: resp.WastePercentage,
|
||||||
|
Unit: resp.Unit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ModelToContractOrderIngredientTransactionSummaries(responses []*models.OrderIngredientTransactionSummary) []*contract.OrderIngredientTransactionSummary {
|
||||||
|
if responses == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
contractResponses := make([]*contract.OrderIngredientTransactionSummary, len(responses))
|
||||||
|
for i, resp := range responses {
|
||||||
|
contractResponses[i] = ModelToContractOrderIngredientTransactionSummary(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return contractResponses
|
||||||
|
}
|
||||||
|
|||||||
185
internal/mappers/order_ingredient_transaction_mapper.go
Normal file
185
internal/mappers/order_ingredient_transaction_mapper.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package mappers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionEntityToModel(entity *entities.OrderIngredientTransaction) *models.OrderIngredientTransaction {
|
||||||
|
if entity == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.OrderIngredientTransaction{
|
||||||
|
ID: entity.ID,
|
||||||
|
OrganizationID: entity.OrganizationID,
|
||||||
|
OutletID: entity.OutletID,
|
||||||
|
OrderID: entity.OrderID,
|
||||||
|
OrderItemID: entity.OrderItemID,
|
||||||
|
ProductID: entity.ProductID,
|
||||||
|
ProductVariantID: entity.ProductVariantID,
|
||||||
|
IngredientID: entity.IngredientID,
|
||||||
|
GrossQty: entity.GrossQty,
|
||||||
|
NetQty: entity.NetQty,
|
||||||
|
WasteQty: entity.WasteQty,
|
||||||
|
Unit: entity.Unit,
|
||||||
|
TransactionDate: entity.TransactionDate,
|
||||||
|
CreatedBy: entity.CreatedBy,
|
||||||
|
CreatedAt: entity.CreatedAt,
|
||||||
|
UpdatedAt: entity.UpdatedAt,
|
||||||
|
Organization: nil,
|
||||||
|
Outlet: nil,
|
||||||
|
Order: nil,
|
||||||
|
OrderItem: nil,
|
||||||
|
Product: nil,
|
||||||
|
ProductVariant: nil,
|
||||||
|
Ingredient: nil,
|
||||||
|
CreatedByUser: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionModelToEntity(model *models.OrderIngredientTransaction) *entities.OrderIngredientTransaction {
|
||||||
|
if model == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &entities.OrderIngredientTransaction{
|
||||||
|
ID: model.ID,
|
||||||
|
OrganizationID: model.OrganizationID,
|
||||||
|
OutletID: model.OutletID,
|
||||||
|
OrderID: model.OrderID,
|
||||||
|
OrderItemID: model.OrderItemID,
|
||||||
|
ProductID: model.ProductID,
|
||||||
|
ProductVariantID: model.ProductVariantID,
|
||||||
|
IngredientID: model.IngredientID,
|
||||||
|
GrossQty: model.GrossQty,
|
||||||
|
NetQty: model.NetQty,
|
||||||
|
WasteQty: model.WasteQty,
|
||||||
|
Unit: model.Unit,
|
||||||
|
TransactionDate: model.TransactionDate,
|
||||||
|
CreatedBy: model.CreatedBy,
|
||||||
|
CreatedAt: model.CreatedAt,
|
||||||
|
UpdatedAt: model.UpdatedAt,
|
||||||
|
Organization: entities.Organization{},
|
||||||
|
Outlet: nil,
|
||||||
|
Order: entities.Order{},
|
||||||
|
OrderItem: nil,
|
||||||
|
Product: entities.Product{},
|
||||||
|
ProductVariant: nil,
|
||||||
|
Ingredient: entities.Ingredient{},
|
||||||
|
CreatedByUser: entities.User{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionEntitiesToModels(entities []*entities.OrderIngredientTransaction) []*models.OrderIngredientTransaction {
|
||||||
|
if entities == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
models := make([]*models.OrderIngredientTransaction, len(entities))
|
||||||
|
for i, entity := range entities {
|
||||||
|
models[i] = MapOrderIngredientTransactionEntityToModel(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionModelsToEntities(models []*models.OrderIngredientTransaction) []*entities.OrderIngredientTransaction {
|
||||||
|
if models == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entities := make([]*entities.OrderIngredientTransaction, len(models))
|
||||||
|
for i, model := range models {
|
||||||
|
entities[i] = MapOrderIngredientTransactionModelToEntity(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionEntityToResponse(entity *entities.OrderIngredientTransaction) *models.OrderIngredientTransactionResponse {
|
||||||
|
if entity == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.OrderIngredientTransactionResponse{
|
||||||
|
ID: entity.ID,
|
||||||
|
OrganizationID: entity.OrganizationID,
|
||||||
|
OutletID: entity.OutletID,
|
||||||
|
OrderID: entity.OrderID,
|
||||||
|
OrderItemID: entity.OrderItemID,
|
||||||
|
ProductID: entity.ProductID,
|
||||||
|
ProductVariantID: entity.ProductVariantID,
|
||||||
|
IngredientID: entity.IngredientID,
|
||||||
|
GrossQty: entity.GrossQty,
|
||||||
|
NetQty: entity.NetQty,
|
||||||
|
WasteQty: entity.WasteQty,
|
||||||
|
Unit: entity.Unit,
|
||||||
|
TransactionDate: entity.TransactionDate,
|
||||||
|
CreatedBy: entity.CreatedBy,
|
||||||
|
CreatedAt: entity.CreatedAt,
|
||||||
|
UpdatedAt: entity.UpdatedAt,
|
||||||
|
Organization: nil,
|
||||||
|
Outlet: nil,
|
||||||
|
Order: nil,
|
||||||
|
OrderItem: nil,
|
||||||
|
Product: nil,
|
||||||
|
ProductVariant: nil,
|
||||||
|
Ingredient: nil,
|
||||||
|
CreatedByUser: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionEntitiesToResponses(entities []*entities.OrderIngredientTransaction) []*models.OrderIngredientTransactionResponse {
|
||||||
|
if entities == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := make([]*models.OrderIngredientTransactionResponse, len(entities))
|
||||||
|
for i, entity := range entities {
|
||||||
|
responses[i] = MapOrderIngredientTransactionEntityToResponse(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapOrderIngredientTransactionSummary(transactions []*entities.OrderIngredientTransaction) []*models.OrderIngredientTransactionSummary {
|
||||||
|
if transactions == nil || len(transactions) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group by ingredient ID
|
||||||
|
ingredientMap := make(map[uuid.UUID]*models.OrderIngredientTransactionSummary)
|
||||||
|
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
ingredientID := transaction.IngredientID
|
||||||
|
|
||||||
|
if summary, exists := ingredientMap[ingredientID]; exists {
|
||||||
|
summary.TotalGrossQty += transaction.GrossQty
|
||||||
|
summary.TotalNetQty += transaction.NetQty
|
||||||
|
summary.TotalWasteQty += transaction.WasteQty
|
||||||
|
} else {
|
||||||
|
ingredientMap[ingredientID] = &models.OrderIngredientTransactionSummary{
|
||||||
|
IngredientID: ingredientID,
|
||||||
|
IngredientName: transaction.Ingredient.Name,
|
||||||
|
TotalGrossQty: transaction.GrossQty,
|
||||||
|
TotalNetQty: transaction.NetQty,
|
||||||
|
TotalWasteQty: transaction.WasteQty,
|
||||||
|
Unit: transaction.Unit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert map to slice and calculate waste percentage
|
||||||
|
var summaries []*models.OrderIngredientTransactionSummary
|
||||||
|
for _, summary := range ingredientMap {
|
||||||
|
if summary.TotalGrossQty > 0 {
|
||||||
|
summary.WastePercentage = (summary.TotalWasteQty / summary.TotalGrossQty) * 100
|
||||||
|
}
|
||||||
|
summaries = append(summaries, summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
return summaries
|
||||||
|
}
|
||||||
@ -11,16 +11,17 @@ func MapProductIngredientEntityToModel(entity *entities.ProductIngredient) *mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &models.ProductIngredient{
|
return &models.ProductIngredient{
|
||||||
ID: entity.ID,
|
ID: entity.ID,
|
||||||
OrganizationID: entity.OrganizationID,
|
OrganizationID: entity.OrganizationID,
|
||||||
OutletID: entity.OutletID,
|
OutletID: entity.OutletID,
|
||||||
ProductID: entity.ProductID,
|
ProductID: entity.ProductID,
|
||||||
IngredientID: entity.IngredientID,
|
IngredientID: entity.IngredientID,
|
||||||
Quantity: entity.Quantity,
|
Quantity: entity.Quantity,
|
||||||
CreatedAt: entity.CreatedAt,
|
WastePercentage: entity.WastePercentage,
|
||||||
UpdatedAt: entity.UpdatedAt,
|
CreatedAt: entity.CreatedAt,
|
||||||
Product: ProductEntityToModel(entity.Product),
|
UpdatedAt: entity.UpdatedAt,
|
||||||
Ingredient: MapIngredientEntityToModel(entity.Ingredient),
|
Product: ProductEntityToModel(entity.Product),
|
||||||
|
Ingredient: MapIngredientEntityToModel(entity.Ingredient),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,16 +31,17 @@ func MapProductIngredientModelToEntity(model *models.ProductIngredient) *entitie
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &entities.ProductIngredient{
|
return &entities.ProductIngredient{
|
||||||
ID: model.ID,
|
ID: model.ID,
|
||||||
OrganizationID: model.OrganizationID,
|
OrganizationID: model.OrganizationID,
|
||||||
OutletID: model.OutletID,
|
OutletID: model.OutletID,
|
||||||
ProductID: model.ProductID,
|
ProductID: model.ProductID,
|
||||||
IngredientID: model.IngredientID,
|
IngredientID: model.IngredientID,
|
||||||
Quantity: model.Quantity,
|
Quantity: model.Quantity,
|
||||||
CreatedAt: model.CreatedAt,
|
WastePercentage: model.WastePercentage,
|
||||||
UpdatedAt: model.UpdatedAt,
|
CreatedAt: model.CreatedAt,
|
||||||
Product: ProductModelToEntity(model.Product),
|
UpdatedAt: model.UpdatedAt,
|
||||||
Ingredient: MapIngredientModelToEntity(model.Ingredient),
|
Product: ProductModelToEntity(model.Product),
|
||||||
|
Ingredient: MapIngredientModelToEntity(model.Ingredient),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -94,3 +94,11 @@ type ListIngredientUnitConvertersResponse struct {
|
|||||||
TotalPages int `json:"total_pages"`
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
108
internal/models/order_ingredient_transaction.go
Normal file
108
internal/models/order_ingredient_transaction.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderIngredientTransaction struct {
|
||||||
|
ID uuid.UUID
|
||||||
|
OrganizationID uuid.UUID
|
||||||
|
OutletID *uuid.UUID
|
||||||
|
OrderID uuid.UUID
|
||||||
|
OrderItemID *uuid.UUID
|
||||||
|
ProductID uuid.UUID
|
||||||
|
ProductVariantID *uuid.UUID
|
||||||
|
IngredientID uuid.UUID
|
||||||
|
GrossQty float64
|
||||||
|
NetQty float64
|
||||||
|
WasteQty float64
|
||||||
|
Unit string
|
||||||
|
TransactionDate time.Time
|
||||||
|
CreatedBy uuid.UUID
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
|
Outlet *Outlet `json:"outlet,omitempty"`
|
||||||
|
Order *Order `json:"order,omitempty"`
|
||||||
|
OrderItem *OrderItem `json:"order_item,omitempty"`
|
||||||
|
Product *Product `json:"product,omitempty"`
|
||||||
|
ProductVariant *ProductVariant `json:"product_variant,omitempty"`
|
||||||
|
Ingredient *Ingredient `json:"ingredient,omitempty"`
|
||||||
|
CreatedByUser *User `json:"created_by_user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
OrganizationID uuid.UUID
|
||||||
|
OutletID *uuid.UUID
|
||||||
|
OrderID uuid.UUID
|
||||||
|
OrderItemID *uuid.UUID
|
||||||
|
ProductID uuid.UUID
|
||||||
|
ProductVariantID *uuid.UUID
|
||||||
|
IngredientID uuid.UUID
|
||||||
|
GrossQty float64
|
||||||
|
NetQty float64
|
||||||
|
WasteQty float64
|
||||||
|
Unit string
|
||||||
|
TransactionDate time.Time
|
||||||
|
CreatedBy uuid.UUID
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
|
||||||
|
// Relations
|
||||||
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
|
Outlet *Outlet `json:"outlet,omitempty"`
|
||||||
|
Order *Order `json:"order,omitempty"`
|
||||||
|
OrderItem *OrderItem `json:"order_item,omitempty"`
|
||||||
|
Product *Product `json:"product,omitempty"`
|
||||||
|
ProductVariant *ProductVariant `json:"product_variant,omitempty"`
|
||||||
|
Ingredient *Ingredient `json:"ingredient,omitempty"`
|
||||||
|
CreatedByUser *User `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"`
|
||||||
|
}
|
||||||
@ -7,14 +7,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProductIngredient struct {
|
type ProductIngredient struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
OrganizationID uuid.UUID `json:"organization_id"`
|
OrganizationID uuid.UUID `json:"organization_id"`
|
||||||
OutletID *uuid.UUID `json:"outlet_id"`
|
OutletID *uuid.UUID `json:"outlet_id"`
|
||||||
ProductID uuid.UUID `json:"product_id"`
|
ProductID uuid.UUID `json:"product_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Quantity float64 `json:"quantity"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
WastePercentage float64 `json:"waste_percentage"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
Product *Product `json:"product,omitempty"`
|
Product *Product `json:"product,omitempty"`
|
||||||
@ -22,26 +23,29 @@ type ProductIngredient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreateProductIngredientRequest struct {
|
type CreateProductIngredientRequest struct {
|
||||||
OutletID *uuid.UUID `json:"outlet_id"`
|
OutletID *uuid.UUID `json:"outlet_id"`
|
||||||
ProductID uuid.UUID `json:"product_id" validate:"required"`
|
ProductID uuid.UUID `json:"product_id" validate:"required"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
|
IngredientID uuid.UUID `json:"ingredient_id" validate:"required"`
|
||||||
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
||||||
|
WastePercentage float64 `json:"waste_percentage" validate:"min=0,max=100"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProductIngredientRequest struct {
|
type UpdateProductIngredientRequest struct {
|
||||||
OutletID *uuid.UUID `json:"outlet_id"`
|
OutletID *uuid.UUID `json:"outlet_id"`
|
||||||
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
Quantity float64 `json:"quantity" validate:"required,gt=0"`
|
||||||
|
WastePercentage float64 `json:"waste_percentage" validate:"min=0,max=100"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductIngredientResponse struct {
|
type ProductIngredientResponse struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
OrganizationID uuid.UUID `json:"organization_id"`
|
OrganizationID uuid.UUID `json:"organization_id"`
|
||||||
OutletID *uuid.UUID `json:"outlet_id"`
|
OutletID *uuid.UUID `json:"outlet_id"`
|
||||||
ProductID uuid.UUID `json:"product_id"`
|
ProductID uuid.UUID `json:"product_id"`
|
||||||
IngredientID uuid.UUID `json:"ingredient_id"`
|
IngredientID uuid.UUID `json:"ingredient_id"`
|
||||||
Quantity float64 `json:"quantity"`
|
Quantity float64 `json:"quantity"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
WastePercentage float64 `json:"waste_percentage"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
Product *Product `json:"product,omitempty"`
|
Product *Product `json:"product,omitempty"`
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type AccountProcessor interface {
|
type AccountProcessor interface {
|
||||||
CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error)
|
CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error)
|
||||||
GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error)
|
GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error)
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
type ChartOfAccountTypeProcessor interface {
|
type ChartOfAccountTypeProcessor interface {
|
||||||
CreateChartOfAccountType(ctx context.Context, req *models.CreateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error)
|
CreateChartOfAccountType(ctx context.Context, req *models.CreateChartOfAccountTypeRequest) (*models.ChartOfAccountTypeResponse, error)
|
||||||
GetChartOfAccountTypeByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountTypeResponse, error)
|
GetChartOfAccountTypeByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountTypeResponse, error)
|
||||||
|
|||||||
@ -1,17 +1 @@
|
|||||||
package processor
|
package processor
|
||||||
|
|
||||||
import (
|
|
||||||
"apskel-pos-be/internal/entities"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IngredientRepository interface {
|
|
||||||
Create(ctx context.Context, ingredient *entities.Ingredient) error
|
|
||||||
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.Ingredient, error)
|
|
||||||
GetAll(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string, isSemiFinished *bool) ([]*entities.Ingredient, int, error)
|
|
||||||
Update(ctx context.Context, ingredient *entities.Ingredient) error
|
|
||||||
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
|
||||||
UpdateStock(ctx context.Context, id uuid.UUID, newStock float64, organizationID uuid.UUID) error
|
|
||||||
}
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ type IngredientUnitConverterProcessor interface {
|
|||||||
ListIngredientUnitConverters(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.IngredientUnitConverterResponse, int, error)
|
ListIngredientUnitConverters(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*models.IngredientUnitConverterResponse, int, error)
|
||||||
GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.IngredientUnitConverterResponse, error)
|
GetConvertersForIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.IngredientUnitConverterResponse, error)
|
||||||
ConvertUnit(ctx context.Context, organizationID uuid.UUID, req *models.ConvertUnitRequest) (*models.ConvertUnitResponse, error)
|
ConvertUnit(ctx context.Context, organizationID uuid.UUID, req *models.ConvertUnitRequest) (*models.ConvertUnitResponse, error)
|
||||||
|
GetUnitsByIngredientID(ctx context.Context, organizationID, ingredientID uuid.UUID) (*models.IngredientUnitsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IngredientUnitConverterProcessorImpl struct {
|
type IngredientUnitConverterProcessorImpl struct {
|
||||||
@ -257,3 +258,64 @@ func (p *IngredientUnitConverterProcessorImpl) ConvertUnit(ctx context.Context,
|
|||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *IngredientUnitConverterProcessorImpl) GetUnitsByIngredientID(ctx context.Context, organizationID, ingredientID uuid.UUID) (*models.IngredientUnitsResponse, error) {
|
||||||
|
// Get the ingredient with its base unit
|
||||||
|
ingredient, err := p.ingredientRepo.GetByID(ctx, ingredientID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get ingredient: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the base unit details
|
||||||
|
baseUnit, err := p.unitRepo.GetByID(ctx, ingredient.UnitID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get base unit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start with the base unit
|
||||||
|
units := []*models.UnitResponse{
|
||||||
|
mappers.MapUnitEntityToResponse(baseUnit),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all converters for this ingredient
|
||||||
|
converters, err := p.converterRepo.GetConvertersForIngredient(ctx, ingredientID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get converters: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add unique units from converters
|
||||||
|
unitMap := make(map[uuid.UUID]bool)
|
||||||
|
unitMap[baseUnit.ID] = true
|
||||||
|
|
||||||
|
for _, converter := range converters {
|
||||||
|
if converter.IsActive {
|
||||||
|
// Add FromUnit if not already added
|
||||||
|
if !unitMap[converter.FromUnitID] {
|
||||||
|
fromUnit, err := p.unitRepo.GetByID(ctx, converter.FromUnitID, organizationID)
|
||||||
|
if err == nil {
|
||||||
|
units = append(units, mappers.MapUnitEntityToResponse(fromUnit))
|
||||||
|
unitMap[converter.FromUnitID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ToUnit if not already added
|
||||||
|
if !unitMap[converter.ToUnitID] {
|
||||||
|
toUnit, err := p.unitRepo.GetByID(ctx, converter.ToUnitID, organizationID)
|
||||||
|
if err == nil {
|
||||||
|
units = append(units, mappers.MapUnitEntityToResponse(toUnit))
|
||||||
|
unitMap[converter.ToUnitID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &models.IngredientUnitsResponse{
|
||||||
|
IngredientID: ingredientID,
|
||||||
|
IngredientName: ingredient.Name,
|
||||||
|
BaseUnitID: baseUnit.ID,
|
||||||
|
BaseUnitName: baseUnit.Name,
|
||||||
|
Units: units,
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|||||||
393
internal/processor/order_ingredient_transaction_processor.go
Normal file
393
internal/processor/order_ingredient_transaction_processor.go
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
|
"apskel-pos-be/internal/mappers"
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
"apskel-pos-be/internal/util"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderIngredientTransactionProcessor interface {
|
||||||
|
CreateOrderIngredientTransaction(ctx context.Context, req *models.CreateOrderIngredientTransactionRequest, organizationID, outletID, createdBy uuid.UUID) (*models.OrderIngredientTransactionResponse, error)
|
||||||
|
GetOrderIngredientTransactionByID(ctx context.Context, id, organizationID uuid.UUID) (*models.OrderIngredientTransactionResponse, error)
|
||||||
|
UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *models.UpdateOrderIngredientTransactionRequest, organizationID uuid.UUID) (*models.OrderIngredientTransactionResponse, error)
|
||||||
|
DeleteOrderIngredientTransaction(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
ListOrderIngredientTransactions(ctx context.Context, req *models.ListOrderIngredientTransactionsRequest, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, int64, error)
|
||||||
|
GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error)
|
||||||
|
GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error)
|
||||||
|
GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error)
|
||||||
|
GetOrderIngredientTransactionSummary(ctx context.Context, req *models.ListOrderIngredientTransactionsRequest, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionSummary, error)
|
||||||
|
BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*models.CreateOrderIngredientTransactionRequest, organizationID, outletID, createdBy uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error)
|
||||||
|
CalculateWasteQuantities(ctx context.Context, productID uuid.UUID, quantity float64, organizationID uuid.UUID) ([]*models.CreateOrderIngredientTransactionRequest, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderIngredientTransactionProcessorImpl struct {
|
||||||
|
orderIngredientTransactionRepo OrderIngredientTransactionRepository
|
||||||
|
productIngredientRepo ProductIngredientRepository
|
||||||
|
ingredientRepo IngredientRepository
|
||||||
|
unitRepo UnitRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderIngredientTransactionProcessorImpl(
|
||||||
|
orderIngredientTransactionRepo OrderIngredientTransactionRepository,
|
||||||
|
productIngredientRepo ProductIngredientRepository,
|
||||||
|
ingredientRepo IngredientRepository,
|
||||||
|
unitRepo UnitRepository,
|
||||||
|
) OrderIngredientTransactionProcessor {
|
||||||
|
return &OrderIngredientTransactionProcessorImpl{
|
||||||
|
orderIngredientTransactionRepo: orderIngredientTransactionRepo,
|
||||||
|
productIngredientRepo: productIngredientRepo,
|
||||||
|
ingredientRepo: ingredientRepo,
|
||||||
|
unitRepo: unitRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) CreateOrderIngredientTransaction(ctx context.Context, req *models.CreateOrderIngredientTransactionRequest, organizationID, outletID, createdBy uuid.UUID) (*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Validate that gross qty >= net qty
|
||||||
|
if req.GrossQty < req.NetQty {
|
||||||
|
return nil, fmt.Errorf("gross quantity must be greater than or equal to net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that waste qty = gross qty - net qty
|
||||||
|
expectedWasteQty := req.GrossQty - req.NetQty
|
||||||
|
if req.WasteQty != expectedWasteQty {
|
||||||
|
return nil, fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set transaction date if not provided
|
||||||
|
transactionDate := time.Now()
|
||||||
|
if req.TransactionDate != nil {
|
||||||
|
transactionDate = *req.TransactionDate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create entity
|
||||||
|
entity := &entities.OrderIngredientTransaction{
|
||||||
|
ID: uuid.New(),
|
||||||
|
OrganizationID: organizationID,
|
||||||
|
OutletID: &outletID,
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
OrderItemID: req.OrderItemID,
|
||||||
|
ProductID: req.ProductID,
|
||||||
|
ProductVariantID: req.ProductVariantID,
|
||||||
|
IngredientID: req.IngredientID,
|
||||||
|
GrossQty: req.GrossQty,
|
||||||
|
NetQty: req.NetQty,
|
||||||
|
WasteQty: req.WasteQty,
|
||||||
|
Unit: req.Unit,
|
||||||
|
TransactionDate: transactionDate,
|
||||||
|
CreatedBy: createdBy,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create in database
|
||||||
|
if err := p.orderIngredientTransactionRepo.Create(ctx, entity); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get created entity with relations
|
||||||
|
createdEntity, err := p.orderIngredientTransactionRepo.GetByID(ctx, entity.ID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get created order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to response
|
||||||
|
response := mappers.MapOrderIngredientTransactionEntityToResponse(createdEntity)
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) GetOrderIngredientTransactionByID(ctx context.Context, id, organizationID uuid.UUID) (*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
entity, err := p.orderIngredientTransactionRepo.GetByID(ctx, id, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := mappers.MapOrderIngredientTransactionEntityToResponse(entity)
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *models.UpdateOrderIngredientTransactionRequest, organizationID uuid.UUID) (*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get existing entity
|
||||||
|
entity, err := p.orderIngredientTransactionRepo.GetByID(ctx, id, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fields
|
||||||
|
if req.GrossQty != nil {
|
||||||
|
entity.GrossQty = *req.GrossQty
|
||||||
|
}
|
||||||
|
if req.NetQty != nil {
|
||||||
|
entity.NetQty = *req.NetQty
|
||||||
|
}
|
||||||
|
if req.WasteQty != nil {
|
||||||
|
entity.WasteQty = *req.WasteQty
|
||||||
|
}
|
||||||
|
if req.Unit != nil {
|
||||||
|
entity.Unit = *req.Unit
|
||||||
|
}
|
||||||
|
if req.TransactionDate != nil {
|
||||||
|
entity.TransactionDate = *req.TransactionDate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate quantities
|
||||||
|
if entity.GrossQty < entity.NetQty {
|
||||||
|
return nil, fmt.Errorf("gross quantity must be greater than or equal to net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedWasteQty := entity.GrossQty - entity.NetQty
|
||||||
|
if entity.WasteQty != expectedWasteQty {
|
||||||
|
return nil, fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
if err := p.orderIngredientTransactionRepo.Update(ctx, entity); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to update order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get updated entity with relations
|
||||||
|
updatedEntity, err := p.orderIngredientTransactionRepo.GetByID(ctx, id, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get updated order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := mappers.MapOrderIngredientTransactionEntityToResponse(updatedEntity)
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) DeleteOrderIngredientTransaction(ctx context.Context, id, organizationID uuid.UUID) error {
|
||||||
|
if err := p.orderIngredientTransactionRepo.Delete(ctx, id, organizationID); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) ListOrderIngredientTransactions(ctx context.Context, req *models.ListOrderIngredientTransactionsRequest, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, int64, error) {
|
||||||
|
// Convert filters
|
||||||
|
filters := make(map[string]interface{})
|
||||||
|
if req.OrderID != nil {
|
||||||
|
filters["order_id"] = *req.OrderID
|
||||||
|
}
|
||||||
|
if req.OrderItemID != nil {
|
||||||
|
filters["order_item_id"] = *req.OrderItemID
|
||||||
|
}
|
||||||
|
if req.ProductID != nil {
|
||||||
|
filters["product_id"] = *req.ProductID
|
||||||
|
}
|
||||||
|
if req.ProductVariantID != nil {
|
||||||
|
filters["product_variant_id"] = *req.ProductVariantID
|
||||||
|
}
|
||||||
|
if req.IngredientID != nil {
|
||||||
|
filters["ingredient_id"] = *req.IngredientID
|
||||||
|
}
|
||||||
|
if req.StartDate != nil {
|
||||||
|
filters["start_date"] = req.StartDate.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
if req.EndDate != nil {
|
||||||
|
filters["end_date"] = req.EndDate.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default pagination
|
||||||
|
page := req.Page
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
limit := req.Limit
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
entities, total, err := p.orderIngredientTransactionRepo.List(ctx, organizationID, filters, page, limit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to list order ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := mappers.MapOrderIngredientTransactionEntitiesToResponses(entities)
|
||||||
|
return responses, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
entities, err := p.orderIngredientTransactionRepo.GetByOrderID(ctx, orderID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by order: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := mappers.MapOrderIngredientTransactionEntitiesToResponses(entities)
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
entities, err := p.orderIngredientTransactionRepo.GetByOrderItemID(ctx, orderItemID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by order item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := mappers.MapOrderIngredientTransactionEntitiesToResponses(entities)
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
entities, err := p.orderIngredientTransactionRepo.GetByIngredientID(ctx, ingredientID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by ingredient: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := mappers.MapOrderIngredientTransactionEntitiesToResponses(entities)
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) GetOrderIngredientTransactionSummary(ctx context.Context, req *models.ListOrderIngredientTransactionsRequest, organizationID uuid.UUID) ([]*models.OrderIngredientTransactionSummary, error) {
|
||||||
|
// Convert filters
|
||||||
|
filters := make(map[string]interface{})
|
||||||
|
if req.OrderID != nil {
|
||||||
|
filters["order_id"] = *req.OrderID
|
||||||
|
}
|
||||||
|
if req.OrderItemID != nil {
|
||||||
|
filters["order_item_id"] = *req.OrderItemID
|
||||||
|
}
|
||||||
|
if req.ProductID != nil {
|
||||||
|
filters["product_id"] = *req.ProductID
|
||||||
|
}
|
||||||
|
if req.ProductVariantID != nil {
|
||||||
|
filters["product_variant_id"] = *req.ProductVariantID
|
||||||
|
}
|
||||||
|
if req.IngredientID != nil {
|
||||||
|
filters["ingredient_id"] = *req.IngredientID
|
||||||
|
}
|
||||||
|
if req.StartDate != nil {
|
||||||
|
filters["start_date"] = req.StartDate.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
if req.EndDate != nil {
|
||||||
|
filters["end_date"] = req.EndDate.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
entities, err := p.orderIngredientTransactionRepo.GetSummary(ctx, organizationID, filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transaction summary: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
summaries := mappers.MapOrderIngredientTransactionSummary(entities)
|
||||||
|
return summaries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*models.CreateOrderIngredientTransactionRequest, organizationID, outletID, createdBy uuid.UUID) ([]*models.OrderIngredientTransactionResponse, error) {
|
||||||
|
if len(transactions) == 0 {
|
||||||
|
return []*models.OrderIngredientTransactionResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to entities
|
||||||
|
transactionEntities := make([]*entities.OrderIngredientTransaction, len(transactions))
|
||||||
|
for i, req := range transactions {
|
||||||
|
// Validate quantities
|
||||||
|
if req.GrossQty < req.NetQty {
|
||||||
|
return nil, fmt.Errorf("gross quantity must be greater than or equal to net quantity for transaction %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedWasteQty := req.GrossQty - req.NetQty
|
||||||
|
if req.WasteQty != expectedWasteQty {
|
||||||
|
return nil, fmt.Errorf("waste quantity must equal gross quantity minus net quantity for transaction %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set transaction date if not provided
|
||||||
|
transactionDate := time.Now()
|
||||||
|
if req.TransactionDate != nil {
|
||||||
|
transactionDate = *req.TransactionDate
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionEntities[i] = &entities.OrderIngredientTransaction{
|
||||||
|
ID: uuid.New(),
|
||||||
|
OrganizationID: organizationID,
|
||||||
|
OutletID: &outletID,
|
||||||
|
OrderID: req.OrderID,
|
||||||
|
OrderItemID: req.OrderItemID,
|
||||||
|
ProductID: req.ProductID,
|
||||||
|
ProductVariantID: req.ProductVariantID,
|
||||||
|
IngredientID: req.IngredientID,
|
||||||
|
GrossQty: req.GrossQty,
|
||||||
|
NetQty: req.NetQty,
|
||||||
|
WasteQty: req.WasteQty,
|
||||||
|
Unit: req.Unit,
|
||||||
|
TransactionDate: transactionDate,
|
||||||
|
CreatedBy: createdBy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk create
|
||||||
|
if err := p.orderIngredientTransactionRepo.BulkCreate(ctx, transactionEntities); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to bulk create order ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get created entities with relations
|
||||||
|
responses := make([]*models.OrderIngredientTransactionResponse, len(transactionEntities))
|
||||||
|
for i, entity := range transactionEntities {
|
||||||
|
createdEntity, err := p.orderIngredientTransactionRepo.GetByID(ctx, entity.ID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get created order ingredient transaction %d: %w", i, err)
|
||||||
|
}
|
||||||
|
responses[i] = mappers.MapOrderIngredientTransactionEntityToResponse(createdEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *OrderIngredientTransactionProcessorImpl) CalculateWasteQuantities(ctx context.Context, productID uuid.UUID, quantity float64, organizationID uuid.UUID) ([]*models.CreateOrderIngredientTransactionRequest, error) {
|
||||||
|
// Get product ingredients
|
||||||
|
productIngredients, err := p.productIngredientRepo.GetByProductID(ctx, productID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get product ingredients: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(productIngredients) == 0 {
|
||||||
|
return []*models.CreateOrderIngredientTransactionRequest{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ingredient details for unit information
|
||||||
|
ingredientMap := make(map[uuid.UUID]*entities.Ingredient)
|
||||||
|
for _, pi := range productIngredients {
|
||||||
|
ingredient, err := p.ingredientRepo.GetByID(ctx, pi.IngredientID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get ingredient %s: %w", pi.IngredientID, err)
|
||||||
|
}
|
||||||
|
ingredientMap[pi.IngredientID] = ingredient
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate quantities for each ingredient
|
||||||
|
transactions := make([]*models.CreateOrderIngredientTransactionRequest, 0, len(productIngredients))
|
||||||
|
for _, pi := range productIngredients {
|
||||||
|
ingredient := ingredientMap[pi.IngredientID]
|
||||||
|
|
||||||
|
// Calculate net quantity (actual quantity needed for the product)
|
||||||
|
netQty := pi.Quantity * quantity
|
||||||
|
|
||||||
|
// Calculate gross quantity (including waste)
|
||||||
|
wasteMultiplier := 1 + (pi.WastePercentage / 100)
|
||||||
|
grossQty := netQty * wasteMultiplier
|
||||||
|
|
||||||
|
// Calculate waste quantity
|
||||||
|
wasteQty := grossQty - netQty
|
||||||
|
|
||||||
|
// Get unit name
|
||||||
|
unitName := "unit" // default
|
||||||
|
if ingredient.UnitID != uuid.Nil {
|
||||||
|
unit, err := p.unitRepo.GetByID(ctx, ingredient.UnitID, organizationID)
|
||||||
|
if err == nil {
|
||||||
|
unitName = unit.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction := &models.CreateOrderIngredientTransactionRequest{
|
||||||
|
IngredientID: pi.IngredientID,
|
||||||
|
GrossQty: util.RoundToDecimalPlaces(grossQty, 3),
|
||||||
|
NetQty: util.RoundToDecimalPlaces(netQty, 3),
|
||||||
|
WasteQty: util.RoundToDecimalPlaces(wasteQty, 3),
|
||||||
|
Unit: unitName,
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions = append(transactions, transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions, nil
|
||||||
|
}
|
||||||
@ -42,3 +42,43 @@ type AccountRepository interface {
|
|||||||
UpdateBalance(ctx context.Context, id uuid.UUID, amount float64) error
|
UpdateBalance(ctx context.Context, id uuid.UUID, amount float64) error
|
||||||
GetBalance(ctx context.Context, id uuid.UUID) (float64, error)
|
GetBalance(ctx context.Context, id uuid.UUID) (float64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrderIngredientTransactionRepository interface {
|
||||||
|
Create(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
|
||||||
|
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.OrderIngredientTransaction, error)
|
||||||
|
Update(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
|
||||||
|
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.OrderIngredientTransaction, int64, error)
|
||||||
|
GetByOrderID(ctx context.Context, orderID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetByOrderItemID(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetSummary(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
BulkCreate(ctx context.Context, transactions []*entities.OrderIngredientTransaction) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductIngredientRepository interface {
|
||||||
|
Create(ctx context.Context, productIngredient *entities.ProductIngredient) error
|
||||||
|
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.ProductIngredient, error)
|
||||||
|
GetByProductID(ctx context.Context, productID, organizationID uuid.UUID) ([]*entities.ProductIngredient, error)
|
||||||
|
GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.ProductIngredient, error)
|
||||||
|
Update(ctx context.Context, productIngredient *entities.ProductIngredient) error
|
||||||
|
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
DeleteByProductID(ctx context.Context, productID, organizationID uuid.UUID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type IngredientRepository interface {
|
||||||
|
Create(ctx context.Context, ingredient *entities.Ingredient) error
|
||||||
|
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.Ingredient, error)
|
||||||
|
GetAll(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string, isSemiFinished *bool) ([]*entities.Ingredient, int, error)
|
||||||
|
Update(ctx context.Context, ingredient *entities.Ingredient) error
|
||||||
|
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
UpdateStock(ctx context.Context, id uuid.UUID, newStock float64, organizationID uuid.UUID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitRepository interface {
|
||||||
|
Create(ctx context.Context, unit *entities.Unit) error
|
||||||
|
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.Unit, error)
|
||||||
|
GetAll(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) ([]*entities.Unit, int, error)
|
||||||
|
Update(ctx context.Context, unit *entities.Unit) error
|
||||||
|
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
}
|
||||||
|
|||||||
@ -1,16 +1 @@
|
|||||||
package processor
|
package processor
|
||||||
|
|
||||||
import (
|
|
||||||
"apskel-pos-be/internal/entities"
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UnitRepository interface {
|
|
||||||
Create(ctx context.Context, unit *entities.Unit) error
|
|
||||||
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.Unit, error)
|
|
||||||
GetAll(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID, page, limit int, search string) ([]*entities.Unit, int, error)
|
|
||||||
Update(ctx context.Context, unit *entities.Unit) error
|
|
||||||
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
|
||||||
}
|
|
||||||
|
|||||||
234
internal/repository/order_ingredient_transaction_repository.go
Normal file
234
internal/repository/order_ingredient_transaction_repository.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderIngredientTransactionRepository interface {
|
||||||
|
Create(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
|
||||||
|
GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.OrderIngredientTransaction, error)
|
||||||
|
Update(ctx context.Context, transaction *entities.OrderIngredientTransaction) error
|
||||||
|
Delete(ctx context.Context, id, organizationID uuid.UUID) error
|
||||||
|
List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.OrderIngredientTransaction, int64, error)
|
||||||
|
GetByOrderID(ctx context.Context, orderID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetByOrderItemID(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
GetSummary(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) ([]*entities.OrderIngredientTransaction, error)
|
||||||
|
BulkCreate(ctx context.Context, transactions []*entities.OrderIngredientTransaction) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderIngredientTransactionRepositoryImpl struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderIngredientTransactionRepositoryImpl(db *gorm.DB) OrderIngredientTransactionRepository {
|
||||||
|
return &OrderIngredientTransactionRepositoryImpl{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) Create(ctx context.Context, transaction *entities.OrderIngredientTransaction) error {
|
||||||
|
return r.db.WithContext(ctx).Create(transaction).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) GetByID(ctx context.Context, id, organizationID uuid.UUID) (*entities.OrderIngredientTransaction, error) {
|
||||||
|
var transaction entities.OrderIngredientTransaction
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("id = ? AND organization_id = ?", id, organizationID).
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("Order").
|
||||||
|
Preload("OrderItem").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("ProductVariant").
|
||||||
|
Preload("Ingredient").
|
||||||
|
Preload("CreatedByUser").
|
||||||
|
First(&transaction).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &transaction, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) Update(ctx context.Context, transaction *entities.OrderIngredientTransaction) error {
|
||||||
|
return r.db.WithContext(ctx).Save(transaction).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) Delete(ctx context.Context, id, organizationID uuid.UUID) error {
|
||||||
|
return r.db.WithContext(ctx).
|
||||||
|
Where("id = ? AND organization_id = ?", id, organizationID).
|
||||||
|
Delete(&entities.OrderIngredientTransaction{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) List(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}, page, limit int) ([]*entities.OrderIngredientTransaction, int64, error) {
|
||||||
|
var transactions []*entities.OrderIngredientTransaction
|
||||||
|
var total int64
|
||||||
|
|
||||||
|
query := r.db.WithContext(ctx).Model(&entities.OrderIngredientTransaction{}).
|
||||||
|
Where("organization_id = ?", organizationID)
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
for key, value := range filters {
|
||||||
|
switch key {
|
||||||
|
case "order_id":
|
||||||
|
if orderID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("order_id = ?", orderID)
|
||||||
|
}
|
||||||
|
case "order_item_id":
|
||||||
|
if orderItemID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("order_item_id = ?", orderItemID)
|
||||||
|
}
|
||||||
|
case "product_id":
|
||||||
|
if productID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("product_id = ?", productID)
|
||||||
|
}
|
||||||
|
case "product_variant_id":
|
||||||
|
if productVariantID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("product_variant_id = ?", productVariantID)
|
||||||
|
}
|
||||||
|
case "ingredient_id":
|
||||||
|
if ingredientID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("ingredient_id = ?", ingredientID)
|
||||||
|
}
|
||||||
|
case "start_date":
|
||||||
|
if startDate, ok := value.(string); ok {
|
||||||
|
query = query.Where("transaction_date >= ?", startDate)
|
||||||
|
}
|
||||||
|
case "end_date":
|
||||||
|
if endDate, ok := value.(string); ok {
|
||||||
|
query = query.Where("transaction_date <= ?", endDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get total count
|
||||||
|
if err := query.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply pagination and get results
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
err := query.
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("Order").
|
||||||
|
Preload("OrderItem").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("ProductVariant").
|
||||||
|
Preload("Ingredient").
|
||||||
|
Preload("CreatedByUser").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Offset(offset).
|
||||||
|
Limit(limit).
|
||||||
|
Find(&transactions).Error
|
||||||
|
|
||||||
|
return transactions, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) GetByOrderID(ctx context.Context, orderID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
|
||||||
|
var transactions []*entities.OrderIngredientTransaction
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("order_id = ? AND organization_id = ?", orderID, organizationID).
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("Order").
|
||||||
|
Preload("OrderItem").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("ProductVariant").
|
||||||
|
Preload("Ingredient").
|
||||||
|
Preload("CreatedByUser").
|
||||||
|
Order("created_at ASC").
|
||||||
|
Find(&transactions).Error
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) GetByOrderItemID(ctx context.Context, orderItemID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
|
||||||
|
var transactions []*entities.OrderIngredientTransaction
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("order_item_id = ? AND organization_id = ?", orderItemID, organizationID).
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("Order").
|
||||||
|
Preload("OrderItem").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("ProductVariant").
|
||||||
|
Preload("Ingredient").
|
||||||
|
Preload("CreatedByUser").
|
||||||
|
Order("created_at ASC").
|
||||||
|
Find(&transactions).Error
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) GetByIngredientID(ctx context.Context, ingredientID, organizationID uuid.UUID) ([]*entities.OrderIngredientTransaction, error) {
|
||||||
|
var transactions []*entities.OrderIngredientTransaction
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Where("ingredient_id = ? AND organization_id = ?", ingredientID, organizationID).
|
||||||
|
Preload("Organization").
|
||||||
|
Preload("Outlet").
|
||||||
|
Preload("Order").
|
||||||
|
Preload("OrderItem").
|
||||||
|
Preload("Product").
|
||||||
|
Preload("ProductVariant").
|
||||||
|
Preload("Ingredient").
|
||||||
|
Preload("CreatedByUser").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&transactions).Error
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) GetSummary(ctx context.Context, organizationID uuid.UUID, filters map[string]interface{}) ([]*entities.OrderIngredientTransaction, error) {
|
||||||
|
var transactions []*entities.OrderIngredientTransaction
|
||||||
|
|
||||||
|
query := r.db.WithContext(ctx).Model(&entities.OrderIngredientTransaction{}).
|
||||||
|
Where("organization_id = ?", organizationID)
|
||||||
|
|
||||||
|
// Apply filters
|
||||||
|
for key, value := range filters {
|
||||||
|
switch key {
|
||||||
|
case "order_id":
|
||||||
|
if orderID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("order_id = ?", orderID)
|
||||||
|
}
|
||||||
|
case "order_item_id":
|
||||||
|
if orderItemID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("order_item_id = ?", orderItemID)
|
||||||
|
}
|
||||||
|
case "product_id":
|
||||||
|
if productID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("product_id = ?", productID)
|
||||||
|
}
|
||||||
|
case "product_variant_id":
|
||||||
|
if productVariantID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("product_variant_id = ?", productVariantID)
|
||||||
|
}
|
||||||
|
case "ingredient_id":
|
||||||
|
if ingredientID, ok := value.(uuid.UUID); ok {
|
||||||
|
query = query.Where("ingredient_id = ?", ingredientID)
|
||||||
|
}
|
||||||
|
case "start_date":
|
||||||
|
if startDate, ok := value.(string); ok {
|
||||||
|
query = query.Where("transaction_date >= ?", startDate)
|
||||||
|
}
|
||||||
|
case "end_date":
|
||||||
|
if endDate, ok := value.(string); ok {
|
||||||
|
query = query.Where("transaction_date <= ?", endDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := query.
|
||||||
|
Preload("Ingredient").
|
||||||
|
Order("ingredient_id, created_at ASC").
|
||||||
|
Find(&transactions).Error
|
||||||
|
|
||||||
|
return transactions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *OrderIngredientTransactionRepositoryImpl) BulkCreate(ctx context.Context, transactions []*entities.OrderIngredientTransaction) error {
|
||||||
|
if len(transactions) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.db.WithContext(ctx).CreateInBatches(transactions, 100).Error
|
||||||
|
}
|
||||||
@ -12,34 +12,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Router struct {
|
type Router struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
healthHandler *handler.HealthHandler
|
healthHandler *handler.HealthHandler
|
||||||
authHandler *handler.AuthHandler
|
authHandler *handler.AuthHandler
|
||||||
userHandler *handler.UserHandler
|
userHandler *handler.UserHandler
|
||||||
organizationHandler *handler.OrganizationHandler
|
organizationHandler *handler.OrganizationHandler
|
||||||
outletHandler *handler.OutletHandler
|
outletHandler *handler.OutletHandler
|
||||||
outletSettingHandler *handler.OutletSettingHandlerImpl
|
outletSettingHandler *handler.OutletSettingHandlerImpl
|
||||||
categoryHandler *handler.CategoryHandler
|
categoryHandler *handler.CategoryHandler
|
||||||
productHandler *handler.ProductHandler
|
productHandler *handler.ProductHandler
|
||||||
productVariantHandler *handler.ProductVariantHandler
|
productVariantHandler *handler.ProductVariantHandler
|
||||||
inventoryHandler *handler.InventoryHandler
|
inventoryHandler *handler.InventoryHandler
|
||||||
orderHandler *handler.OrderHandler
|
orderHandler *handler.OrderHandler
|
||||||
fileHandler *handler.FileHandler
|
fileHandler *handler.FileHandler
|
||||||
customerHandler *handler.CustomerHandler
|
customerHandler *handler.CustomerHandler
|
||||||
paymentMethodHandler *handler.PaymentMethodHandler
|
paymentMethodHandler *handler.PaymentMethodHandler
|
||||||
analyticsHandler *handler.AnalyticsHandler
|
analyticsHandler *handler.AnalyticsHandler
|
||||||
reportHandler *handler.ReportHandler
|
reportHandler *handler.ReportHandler
|
||||||
tableHandler *handler.TableHandler
|
tableHandler *handler.TableHandler
|
||||||
unitHandler *handler.UnitHandler
|
unitHandler *handler.UnitHandler
|
||||||
ingredientHandler *handler.IngredientHandler
|
ingredientHandler *handler.IngredientHandler
|
||||||
productRecipeHandler *handler.ProductRecipeHandler
|
productRecipeHandler *handler.ProductRecipeHandler
|
||||||
vendorHandler *handler.VendorHandler
|
vendorHandler *handler.VendorHandler
|
||||||
purchaseOrderHandler *handler.PurchaseOrderHandler
|
purchaseOrderHandler *handler.PurchaseOrderHandler
|
||||||
unitConverterHandler *handler.IngredientUnitConverterHandler
|
unitConverterHandler *handler.IngredientUnitConverterHandler
|
||||||
chartOfAccountTypeHandler *handler.ChartOfAccountTypeHandler
|
chartOfAccountTypeHandler *handler.ChartOfAccountTypeHandler
|
||||||
chartOfAccountHandler *handler.ChartOfAccountHandler
|
chartOfAccountHandler *handler.ChartOfAccountHandler
|
||||||
accountHandler *handler.AccountHandler
|
accountHandler *handler.AccountHandler
|
||||||
authMiddleware *middleware.AuthMiddleware
|
orderIngredientTransactionHandler *handler.OrderIngredientTransactionHandler
|
||||||
|
authMiddleware *middleware.AuthMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(cfg *config.Config,
|
func NewRouter(cfg *config.Config,
|
||||||
@ -87,36 +88,39 @@ func NewRouter(cfg *config.Config,
|
|||||||
chartOfAccountService service.ChartOfAccountService,
|
chartOfAccountService service.ChartOfAccountService,
|
||||||
chartOfAccountValidator validator.ChartOfAccountValidator,
|
chartOfAccountValidator validator.ChartOfAccountValidator,
|
||||||
accountService service.AccountService,
|
accountService service.AccountService,
|
||||||
accountValidator validator.AccountValidator) *Router {
|
accountValidator validator.AccountValidator,
|
||||||
|
orderIngredientTransactionService service.OrderIngredientTransactionService,
|
||||||
|
orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator) *Router {
|
||||||
|
|
||||||
return &Router{
|
return &Router{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
healthHandler: healthHandler,
|
healthHandler: healthHandler,
|
||||||
authHandler: handler.NewAuthHandler(authService),
|
authHandler: handler.NewAuthHandler(authService),
|
||||||
userHandler: handler.NewUserHandler(userService, userValidator),
|
userHandler: handler.NewUserHandler(userService, userValidator),
|
||||||
organizationHandler: handler.NewOrganizationHandler(organizationService, organizationValidator),
|
organizationHandler: handler.NewOrganizationHandler(organizationService, organizationValidator),
|
||||||
outletHandler: handler.NewOutletHandler(outletService, outletValidator),
|
outletHandler: handler.NewOutletHandler(outletService, outletValidator),
|
||||||
outletSettingHandler: handler.NewOutletSettingHandlerImpl(outletSettingService),
|
outletSettingHandler: handler.NewOutletSettingHandlerImpl(outletSettingService),
|
||||||
categoryHandler: handler.NewCategoryHandler(categoryService, categoryValidator),
|
categoryHandler: handler.NewCategoryHandler(categoryService, categoryValidator),
|
||||||
productHandler: handler.NewProductHandler(productService, productValidator),
|
productHandler: handler.NewProductHandler(productService, productValidator),
|
||||||
inventoryHandler: handler.NewInventoryHandler(inventoryService, inventoryValidator),
|
inventoryHandler: handler.NewInventoryHandler(inventoryService, inventoryValidator),
|
||||||
orderHandler: handler.NewOrderHandler(orderService, orderValidator, transformer.NewTransformer()),
|
orderHandler: handler.NewOrderHandler(orderService, orderValidator, transformer.NewTransformer()),
|
||||||
fileHandler: handler.NewFileHandler(fileService, fileValidator, transformer.NewTransformer()),
|
fileHandler: handler.NewFileHandler(fileService, fileValidator, transformer.NewTransformer()),
|
||||||
customerHandler: handler.NewCustomerHandler(customerService, customerValidator),
|
customerHandler: handler.NewCustomerHandler(customerService, customerValidator),
|
||||||
paymentMethodHandler: handler.NewPaymentMethodHandler(paymentMethodService, paymentMethodValidator),
|
paymentMethodHandler: handler.NewPaymentMethodHandler(paymentMethodService, paymentMethodValidator),
|
||||||
analyticsHandler: handler.NewAnalyticsHandler(analyticsService, transformer.NewTransformer()),
|
analyticsHandler: handler.NewAnalyticsHandler(analyticsService, transformer.NewTransformer()),
|
||||||
reportHandler: handler.NewReportHandler(reportService, userService),
|
reportHandler: handler.NewReportHandler(reportService, userService),
|
||||||
tableHandler: handler.NewTableHandler(tableService, tableValidator),
|
tableHandler: handler.NewTableHandler(tableService, tableValidator),
|
||||||
unitHandler: handler.NewUnitHandler(unitService),
|
unitHandler: handler.NewUnitHandler(unitService),
|
||||||
ingredientHandler: handler.NewIngredientHandler(ingredientService),
|
ingredientHandler: handler.NewIngredientHandler(ingredientService),
|
||||||
productRecipeHandler: handler.NewProductRecipeHandler(productRecipeService),
|
productRecipeHandler: handler.NewProductRecipeHandler(productRecipeService),
|
||||||
vendorHandler: handler.NewVendorHandler(vendorService, vendorValidator),
|
vendorHandler: handler.NewVendorHandler(vendorService, vendorValidator),
|
||||||
purchaseOrderHandler: handler.NewPurchaseOrderHandler(purchaseOrderService, purchaseOrderValidator),
|
purchaseOrderHandler: handler.NewPurchaseOrderHandler(purchaseOrderService, purchaseOrderValidator),
|
||||||
unitConverterHandler: handler.NewIngredientUnitConverterHandler(unitConverterService, unitConverterValidator),
|
unitConverterHandler: handler.NewIngredientUnitConverterHandler(unitConverterService, unitConverterValidator),
|
||||||
chartOfAccountTypeHandler: handler.NewChartOfAccountTypeHandler(chartOfAccountTypeService, chartOfAccountTypeValidator),
|
chartOfAccountTypeHandler: handler.NewChartOfAccountTypeHandler(chartOfAccountTypeService, chartOfAccountTypeValidator),
|
||||||
chartOfAccountHandler: handler.NewChartOfAccountHandler(chartOfAccountService, chartOfAccountValidator),
|
chartOfAccountHandler: handler.NewChartOfAccountHandler(chartOfAccountService, chartOfAccountValidator),
|
||||||
accountHandler: handler.NewAccountHandler(accountService, accountValidator),
|
accountHandler: handler.NewAccountHandler(accountService, accountValidator),
|
||||||
authMiddleware: authMiddleware,
|
orderIngredientTransactionHandler: handler.NewOrderIngredientTransactionHandler(&orderIngredientTransactionService, orderIngredientTransactionValidator),
|
||||||
|
authMiddleware: authMiddleware,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,6 +355,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
unitConverters.POST("", r.unitConverterHandler.CreateIngredientUnitConverter)
|
unitConverters.POST("", r.unitConverterHandler.CreateIngredientUnitConverter)
|
||||||
unitConverters.GET("", r.unitConverterHandler.ListIngredientUnitConverters)
|
unitConverters.GET("", r.unitConverterHandler.ListIngredientUnitConverters)
|
||||||
unitConverters.GET("/ingredient/:ingredient_id", r.unitConverterHandler.GetConvertersForIngredient)
|
unitConverters.GET("/ingredient/:ingredient_id", r.unitConverterHandler.GetConvertersForIngredient)
|
||||||
|
unitConverters.GET("/ingredient/:ingredient_id/units", r.unitConverterHandler.GetUnitsByIngredientID)
|
||||||
unitConverters.POST("/convert", r.unitConverterHandler.ConvertUnit)
|
unitConverters.POST("/convert", r.unitConverterHandler.ConvertUnit)
|
||||||
unitConverters.GET("/:id", r.unitConverterHandler.GetIngredientUnitConverter)
|
unitConverters.GET("/:id", r.unitConverterHandler.GetIngredientUnitConverter)
|
||||||
unitConverters.PUT("/:id", r.unitConverterHandler.UpdateIngredientUnitConverter)
|
unitConverters.PUT("/:id", r.unitConverterHandler.UpdateIngredientUnitConverter)
|
||||||
@ -406,6 +411,21 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
|||||||
accounts.GET("/:id/balance", r.accountHandler.GetAccountBalance)
|
accounts.GET("/:id/balance", r.accountHandler.GetAccountBalance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderIngredientTransactions := protected.Group("/order-ingredient-transactions")
|
||||||
|
orderIngredientTransactions.Use(r.authMiddleware.RequireAdminOrManager())
|
||||||
|
{
|
||||||
|
orderIngredientTransactions.POST("", r.orderIngredientTransactionHandler.CreateOrderIngredientTransaction)
|
||||||
|
orderIngredientTransactions.GET("", r.orderIngredientTransactionHandler.ListOrderIngredientTransactions)
|
||||||
|
orderIngredientTransactions.GET("/:id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionByID)
|
||||||
|
orderIngredientTransactions.PUT("/:id", r.orderIngredientTransactionHandler.UpdateOrderIngredientTransaction)
|
||||||
|
orderIngredientTransactions.DELETE("/:id", r.orderIngredientTransactionHandler.DeleteOrderIngredientTransaction)
|
||||||
|
orderIngredientTransactions.GET("/order/:order_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByOrder)
|
||||||
|
orderIngredientTransactions.GET("/order-item/:order_item_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByOrderItem)
|
||||||
|
orderIngredientTransactions.GET("/ingredient/:ingredient_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByIngredient)
|
||||||
|
orderIngredientTransactions.GET("/summary", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionSummary)
|
||||||
|
orderIngredientTransactions.POST("/bulk", r.orderIngredientTransactionHandler.BulkCreateOrderIngredientTransactions)
|
||||||
|
}
|
||||||
|
|
||||||
outlets := protected.Group("/outlets")
|
outlets := protected.Group("/outlets")
|
||||||
outlets.Use(r.authMiddleware.RequireAdminOrManager())
|
outlets.Use(r.authMiddleware.RequireAdminOrManager())
|
||||||
{
|
{
|
||||||
|
|||||||
@ -19,6 +19,7 @@ type IngredientUnitConverterService interface {
|
|||||||
ListIngredientUnitConverters(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.ListIngredientUnitConvertersRequest) *contract.Response
|
ListIngredientUnitConverters(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.ListIngredientUnitConvertersRequest) *contract.Response
|
||||||
GetConvertersForIngredient(ctx context.Context, apctx *appcontext.ContextInfo, ingredientID uuid.UUID) *contract.Response
|
GetConvertersForIngredient(ctx context.Context, apctx *appcontext.ContextInfo, ingredientID uuid.UUID) *contract.Response
|
||||||
ConvertUnit(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.ConvertUnitRequest) *contract.Response
|
ConvertUnit(ctx context.Context, apctx *appcontext.ContextInfo, req *contract.ConvertUnitRequest) *contract.Response
|
||||||
|
GetUnitsByIngredientID(ctx context.Context, apctx *appcontext.ContextInfo, ingredientID uuid.UUID) *contract.Response
|
||||||
}
|
}
|
||||||
|
|
||||||
type IngredientUnitConverterServiceImpl struct {
|
type IngredientUnitConverterServiceImpl struct {
|
||||||
@ -149,3 +150,14 @@ func (s *IngredientUnitConverterServiceImpl) ConvertUnit(ctx context.Context, ap
|
|||||||
return contract.BuildSuccessResponse(contractResponse)
|
return contract.BuildSuccessResponse(contractResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IngredientUnitConverterServiceImpl) GetUnitsByIngredientID(ctx context.Context, apctx *appcontext.ContextInfo, ingredientID uuid.UUID) *contract.Response {
|
||||||
|
unitsResponse, err := s.converterProcessor.GetUnitsByIngredientID(ctx, apctx.OrganizationID, ingredientID)
|
||||||
|
if err != nil {
|
||||||
|
errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.IngredientUnitConverterServiceEntity, err.Error())
|
||||||
|
return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
|
||||||
|
}
|
||||||
|
|
||||||
|
contractResponse := transformer.IngredientUnitsModelResponseToResponse(unitsResponse)
|
||||||
|
return contract.BuildSuccessResponse(contractResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
205
internal/service/order_ingredient_transaction_service.go
Normal file
205
internal/service/order_ingredient_transaction_service.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/appcontext"
|
||||||
|
"apskel-pos-be/internal/contract"
|
||||||
|
"apskel-pos-be/internal/mappers"
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
"apskel-pos-be/internal/processor"
|
||||||
|
"apskel-pos-be/internal/repository"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderIngredientTransactionService struct {
|
||||||
|
processor processor.OrderIngredientTransactionProcessor
|
||||||
|
txManager *repository.TxManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderIngredientTransactionService(processor processor.OrderIngredientTransactionProcessor, txManager *repository.TxManager) *OrderIngredientTransactionService {
|
||||||
|
return &OrderIngredientTransactionService{
|
||||||
|
processor: processor,
|
||||||
|
txManager: txManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) CreateOrderIngredientTransaction(ctx context.Context, req *contract.CreateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization and outlet from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
outletID := appCtx.OutletID
|
||||||
|
createdBy := appCtx.UserID
|
||||||
|
|
||||||
|
// Convert contract to model
|
||||||
|
modelReq := mappers.ContractToModelCreateOrderIngredientTransactionRequest(req)
|
||||||
|
|
||||||
|
// Create transaction
|
||||||
|
response, err := s.processor.CreateOrderIngredientTransaction(ctx, modelReq, organizationID, outletID, createdBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model to contract
|
||||||
|
contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
|
||||||
|
return contractResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionByID(ctx context.Context, id uuid.UUID) (*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Get transaction
|
||||||
|
response, err := s.processor.GetOrderIngredientTransactionByID(ctx, id, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model to contract
|
||||||
|
contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
|
||||||
|
return contractResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) UpdateOrderIngredientTransaction(ctx context.Context, id uuid.UUID, req *contract.UpdateOrderIngredientTransactionRequest) (*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Convert contract to model
|
||||||
|
modelReq := mappers.ContractToModelUpdateOrderIngredientTransactionRequest(req)
|
||||||
|
|
||||||
|
// Update transaction
|
||||||
|
response, err := s.processor.UpdateOrderIngredientTransaction(ctx, id, modelReq, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to update order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert model to contract
|
||||||
|
contractResp := mappers.ModelToContractOrderIngredientTransactionResponse(response)
|
||||||
|
return contractResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) DeleteOrderIngredientTransaction(ctx context.Context, id uuid.UUID) error {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Delete transaction
|
||||||
|
if err := s.processor.DeleteOrderIngredientTransaction(ctx, id, organizationID); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete order ingredient transaction: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) ListOrderIngredientTransactions(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionResponse, int64, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Convert contract to model
|
||||||
|
modelReq := mappers.ContractToModelListOrderIngredientTransactionsRequest(req)
|
||||||
|
|
||||||
|
// List transactions
|
||||||
|
responses, total, err := s.processor.ListOrderIngredientTransactions(ctx, modelReq, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("failed to list order ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
|
||||||
|
return contractResponses, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByOrder(ctx context.Context, orderID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Get transactions by order
|
||||||
|
responses, err := s.processor.GetOrderIngredientTransactionsByOrder(ctx, orderID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by order: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
|
||||||
|
return contractResponses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByOrderItem(ctx context.Context, orderItemID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Get transactions by order item
|
||||||
|
responses, err := s.processor.GetOrderIngredientTransactionsByOrderItem(ctx, orderItemID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by order item: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
|
||||||
|
return contractResponses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionsByIngredient(ctx context.Context, ingredientID uuid.UUID) ([]*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Get transactions by ingredient
|
||||||
|
responses, err := s.processor.GetOrderIngredientTransactionsByIngredient(ctx, ingredientID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transactions by ingredient: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
|
||||||
|
return contractResponses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) GetOrderIngredientTransactionSummary(ctx context.Context, req *contract.ListOrderIngredientTransactionsRequest) ([]*contract.OrderIngredientTransactionSummary, error) {
|
||||||
|
// Get organization from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
|
||||||
|
// Convert contract to model
|
||||||
|
modelReq := mappers.ContractToModelListOrderIngredientTransactionsRequest(req)
|
||||||
|
|
||||||
|
// Get summary
|
||||||
|
summaries, err := s.processor.GetOrderIngredientTransactionSummary(ctx, modelReq, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get order ingredient transaction summary: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractSummaries := mappers.ModelToContractOrderIngredientTransactionSummaries(summaries)
|
||||||
|
return contractSummaries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderIngredientTransactionService) BulkCreateOrderIngredientTransactions(ctx context.Context, transactions []*contract.CreateOrderIngredientTransactionRequest) ([]*contract.OrderIngredientTransactionResponse, error) {
|
||||||
|
// Get organization and outlet from context
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
outletID := appCtx.OutletID
|
||||||
|
createdBy := appCtx.UserID
|
||||||
|
|
||||||
|
// Convert contracts to models
|
||||||
|
modelReqs := make([]*models.CreateOrderIngredientTransactionRequest, len(transactions))
|
||||||
|
for i, req := range transactions {
|
||||||
|
modelReqs[i] = mappers.ContractToModelCreateOrderIngredientTransactionRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk create transactions
|
||||||
|
responses, err := s.processor.BulkCreateOrderIngredientTransactions(ctx, modelReqs, organizationID, outletID, createdBy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to bulk create order ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert models to contracts
|
||||||
|
contractResponses := mappers.ModelToContractOrderIngredientTransactionResponses(responses)
|
||||||
|
return contractResponses, nil
|
||||||
|
}
|
||||||
@ -1,13 +1,17 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"apskel-pos-be/internal/appcontext"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"apskel-pos-be/internal/contract"
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
"apskel-pos-be/internal/models"
|
"apskel-pos-be/internal/models"
|
||||||
"apskel-pos-be/internal/processor"
|
"apskel-pos-be/internal/processor"
|
||||||
"apskel-pos-be/internal/repository"
|
"apskel-pos-be/internal/repository"
|
||||||
|
"apskel-pos-be/internal/util"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@ -27,14 +31,22 @@ type OrderService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OrderServiceImpl struct {
|
type OrderServiceImpl struct {
|
||||||
orderProcessor processor.OrderProcessor
|
orderProcessor processor.OrderProcessor
|
||||||
tableRepo repository.TableRepositoryInterface
|
tableRepo repository.TableRepositoryInterface
|
||||||
|
orderIngredientTransactionService *OrderIngredientTransactionService
|
||||||
|
orderIngredientTransactionProcessor processor.OrderIngredientTransactionProcessor
|
||||||
|
productIngredientRepo repository.ProductIngredientRepository
|
||||||
|
txManager *repository.TxManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repository.TableRepositoryInterface) *OrderServiceImpl {
|
func NewOrderServiceImpl(orderProcessor processor.OrderProcessor, tableRepo repository.TableRepositoryInterface, orderIngredientTransactionService *OrderIngredientTransactionService, orderIngredientTransactionProcessor processor.OrderIngredientTransactionProcessor, productIngredientRepo repository.ProductIngredientRepository, txManager *repository.TxManager) *OrderServiceImpl {
|
||||||
return &OrderServiceImpl{
|
return &OrderServiceImpl{
|
||||||
orderProcessor: orderProcessor,
|
orderProcessor: orderProcessor,
|
||||||
tableRepo: tableRepo,
|
tableRepo: tableRepo,
|
||||||
|
orderIngredientTransactionService: orderIngredientTransactionService,
|
||||||
|
orderIngredientTransactionProcessor: orderIngredientTransactionProcessor,
|
||||||
|
productIngredientRepo: productIngredientRepo,
|
||||||
|
txManager: txManager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,47 +61,133 @@ func (s *OrderServiceImpl) CreateOrder(ctx context.Context, req *models.CreateOr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.orderProcessor.CreateOrder(ctx, req, organizationID)
|
var response *models.OrderResponse
|
||||||
if err != nil {
|
var ingredientTransactions []*contract.CreateOrderIngredientTransactionRequest
|
||||||
return nil, fmt.Errorf("failed to create order: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.TableID != nil {
|
// Use transaction to ensure atomicity
|
||||||
if err := s.occupyTableWithOrder(ctx, *req.TableID, response.ID); err != nil {
|
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
fmt.Printf("Warning: failed to occupy table %s with order %s: %v\n", *req.TableID, response.ID, err)
|
// Create the order
|
||||||
|
orderResp, err := s.orderProcessor.CreateOrder(txCtx, req, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create order: %w", err)
|
||||||
}
|
}
|
||||||
|
response = orderResp
|
||||||
|
|
||||||
|
// Create ingredient transactions for each order item
|
||||||
|
ingredientTransactions, err = s.createIngredientTransactions(txCtx, response.ID, response.OrderItems)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk create ingredient transactions
|
||||||
|
if len(ingredientTransactions) > 0 {
|
||||||
|
_, err = s.orderIngredientTransactionService.BulkCreateOrderIngredientTransactions(txCtx, ingredientTransactions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to bulk create ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Occupy table if specified
|
||||||
|
if req.TableID != nil {
|
||||||
|
if err := s.occupyTableWithOrder(txCtx, *req.TableID, response.ID); err != nil {
|
||||||
|
// Log warning but don't fail the transaction
|
||||||
|
fmt.Printf("Warning: failed to occupy table %s with order %s: %v\n", *req.TableID, response.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createIngredientTransactions creates ingredient transactions for order items efficiently
|
||||||
|
func (s *OrderServiceImpl) createIngredientTransactions(ctx context.Context, orderID uuid.UUID, orderItems []models.OrderItemResponse) ([]*contract.CreateOrderIngredientTransactionRequest, error) {
|
||||||
|
appCtx := appcontext.FromGinContext(ctx)
|
||||||
|
organizationID := appCtx.OrganizationID
|
||||||
|
var allTransactions []*contract.CreateOrderIngredientTransactionRequest
|
||||||
|
|
||||||
|
for _, orderItem := range orderItems {
|
||||||
|
// Get product ingredients for this product
|
||||||
|
productIngredients, err := s.productIngredientRepo.GetByProductID(ctx, orderItem.ProductID, organizationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get product ingredients for product %s: %w", orderItem.ProductID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(productIngredients) == 0 {
|
||||||
|
continue // Skip if no ingredients
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate waste quantities
|
||||||
|
transactions, err := s.calculateWasteQuantities(productIngredients, float64(orderItem.Quantity))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to calculate waste quantities for product %s: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set common fields for all transactions
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
transaction.OrderID = orderID
|
||||||
|
transaction.OrderItemID = &orderItem.ID
|
||||||
|
transaction.ProductID = orderItem.ProductID
|
||||||
|
transaction.ProductVariantID = orderItem.ProductVariantID
|
||||||
|
}
|
||||||
|
|
||||||
|
allTransactions = append(allTransactions, transactions...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allTransactions, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *OrderServiceImpl) AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error) {
|
func (s *OrderServiceImpl) AddToOrder(ctx context.Context, orderID uuid.UUID, req *models.AddToOrderRequest) (*models.AddToOrderResponse, error) {
|
||||||
// Validate inputs
|
|
||||||
if orderID == uuid.Nil {
|
if orderID == uuid.Nil {
|
||||||
return nil, fmt.Errorf("invalid order ID")
|
return nil, fmt.Errorf("invalid order ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate request
|
|
||||||
if err := s.validateAddToOrderRequest(req); err != nil {
|
if err := s.validateAddToOrderRequest(req); err != nil {
|
||||||
return nil, fmt.Errorf("validation error: %w", err)
|
return nil, fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process adding items to order
|
var response *models.AddToOrderResponse
|
||||||
response, err := s.orderProcessor.AddToOrder(ctx, orderID, req)
|
var ingredientTransactions []*contract.CreateOrderIngredientTransactionRequest
|
||||||
|
|
||||||
|
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||||
|
addResp, err := s.orderProcessor.AddToOrder(txCtx, orderID, req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add items to order: %w", err)
|
||||||
|
}
|
||||||
|
response = addResp
|
||||||
|
|
||||||
|
ingredientTransactions, err = s.createIngredientTransactions(txCtx, orderID, response.AddedItems)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ingredientTransactions) > 0 {
|
||||||
|
_, err = s.orderIngredientTransactionService.BulkCreateOrderIngredientTransactions(txCtx, ingredientTransactions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to bulk create ingredient transactions: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add items to order: %w", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OrderServiceImpl) UpdateOrder(ctx context.Context, id uuid.UUID, req *models.UpdateOrderRequest) (*models.OrderResponse, error) {
|
func (s *OrderServiceImpl) UpdateOrder(ctx context.Context, id uuid.UUID, req *models.UpdateOrderRequest) (*models.OrderResponse, error) {
|
||||||
// Validate request
|
|
||||||
if err := s.validateUpdateOrderRequest(req); err != nil {
|
if err := s.validateUpdateOrderRequest(req); err != nil {
|
||||||
return nil, fmt.Errorf("validation error: %w", err)
|
return nil, fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process order update
|
|
||||||
response, err := s.orderProcessor.UpdateOrder(ctx, id, req)
|
response, err := s.orderProcessor.UpdateOrder(ctx, id, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to update order: %w", err)
|
return nil, fmt.Errorf("failed to update order: %w", err)
|
||||||
@ -137,9 +235,7 @@ func (s *OrderServiceImpl) VoidOrder(ctx context.Context, req *models.VoidOrderR
|
|||||||
return fmt.Errorf("failed to void order: %w", err)
|
return fmt.Errorf("failed to void order: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release table if order is voided
|
|
||||||
if err := s.handleTableReleaseOnVoid(ctx, req.OrderID); err != nil {
|
if err := s.handleTableReleaseOnVoid(ctx, req.OrderID); err != nil {
|
||||||
// Log the error but don't fail the void operation
|
|
||||||
fmt.Printf("Warning: failed to handle table release for voided order %s: %v\n", req.OrderID, err)
|
fmt.Printf("Warning: failed to handle table release for voided order %s: %v\n", req.OrderID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,3 +643,79 @@ func (s *OrderServiceImpl) handleTableReleaseOnVoid(ctx context.Context, orderID
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *OrderServiceImpl) createOrderIngredientTransactions(ctx context.Context, order *models.Order, orderItems []*models.OrderItem) error {
|
||||||
|
for _, orderItem := range orderItems {
|
||||||
|
productIngredients, err := s.productIngredientRepo.GetByProductID(ctx, orderItem.ProductID, order.OrganizationID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get product ingredients for product %s: %w", orderItem.ProductID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(productIngredients) == 0 {
|
||||||
|
continue // Skip if no ingredients
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate waste quantities using the utility function
|
||||||
|
transactions, err := s.calculateWasteQuantities(productIngredients, float64(orderItem.Quantity))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to calculate waste quantities for product %s: %w", orderItem.ProductID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set common fields for all transactions
|
||||||
|
for _, transaction := range transactions {
|
||||||
|
transaction.OrderID = order.ID
|
||||||
|
transaction.OrderItemID = &orderItem.ID
|
||||||
|
transaction.ProductID = orderItem.ProductID
|
||||||
|
transaction.ProductVariantID = orderItem.ProductVariantID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bulk create transactions
|
||||||
|
if len(transactions) > 0 {
|
||||||
|
_, err := s.orderIngredientTransactionService.BulkCreateOrderIngredientTransactions(ctx, transactions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create order ingredient transactions for product %s: %w", orderItem.ProductID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateWasteQuantities calculates gross, net, and waste quantities for product ingredients
|
||||||
|
func (s *OrderServiceImpl) calculateWasteQuantities(productIngredients []*entities.ProductIngredient, quantity float64) ([]*contract.CreateOrderIngredientTransactionRequest, error) {
|
||||||
|
if len(productIngredients) == 0 {
|
||||||
|
return []*contract.CreateOrderIngredientTransactionRequest{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions := make([]*contract.CreateOrderIngredientTransactionRequest, 0, len(productIngredients))
|
||||||
|
|
||||||
|
for _, pi := range productIngredients {
|
||||||
|
// Calculate net quantity (actual quantity needed for the product)
|
||||||
|
netQty := pi.Quantity * quantity
|
||||||
|
|
||||||
|
// Calculate gross quantity (including waste)
|
||||||
|
wasteMultiplier := 1 + (pi.WastePercentage / 100)
|
||||||
|
grossQty := netQty * wasteMultiplier
|
||||||
|
|
||||||
|
// Calculate waste quantity
|
||||||
|
wasteQty := grossQty - netQty
|
||||||
|
|
||||||
|
// Get unit name from ingredient
|
||||||
|
unitName := "unit" // default
|
||||||
|
if pi.Ingredient != nil && pi.Ingredient.Unit != nil {
|
||||||
|
unitName = pi.Ingredient.Unit.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction := &contract.CreateOrderIngredientTransactionRequest{
|
||||||
|
IngredientID: pi.IngredientID,
|
||||||
|
GrossQty: util.RoundToDecimalPlaces(grossQty, 3),
|
||||||
|
NetQty: util.RoundToDecimalPlaces(netQty, 3),
|
||||||
|
WasteQty: util.RoundToDecimalPlaces(wasteQty, 3),
|
||||||
|
Unit: unitName,
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions = append(transactions, transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -152,3 +152,27 @@ func UnitModelResponseToResponse(model *models.UnitResponse) contract.UnitRespon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IngredientUnitsModelResponseToResponse(model *models.IngredientUnitsResponse) contract.IngredientUnitsResponse {
|
||||||
|
if model == nil {
|
||||||
|
return contract.IngredientUnitsResponse{}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := contract.IngredientUnitsResponse{
|
||||||
|
IngredientID: model.IngredientID,
|
||||||
|
IngredientName: model.IngredientName,
|
||||||
|
BaseUnitID: model.BaseUnitID,
|
||||||
|
BaseUnitName: model.BaseUnitName,
|
||||||
|
Units: make([]*contract.UnitResponse, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map units
|
||||||
|
for _, unit := range model.Units {
|
||||||
|
if unit != nil {
|
||||||
|
unitResp := UnitModelResponseToResponse(unit)
|
||||||
|
response.Units = append(response.Units, &unitResp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"apskel-pos-be/internal/entities"
|
"apskel-pos-be/internal/entities"
|
||||||
"apskel-pos-be/internal/processor"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@ -35,8 +34,8 @@ type AccountTemplate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateDefaultChartOfAccounts(ctx context.Context,
|
func CreateDefaultChartOfAccounts(ctx context.Context,
|
||||||
chartOfAccountProcessor processor.ChartOfAccountProcessor,
|
chartOfAccountProcessor interface{}, // Use interface{} to avoid import cycle
|
||||||
accountProcessor processor.AccountProcessor,
|
accountProcessor interface{}, // Use interface{} to avoid import cycle
|
||||||
organizationID uuid.UUID,
|
organizationID uuid.UUID,
|
||||||
outletID *uuid.UUID) error {
|
outletID *uuid.UUID) error {
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ func loadDefaultChartOfAccountsTemplate() (*DefaultChartOfAccount, error) {
|
|||||||
return &template, nil
|
return &template, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChartOfAccountTypeMap(ctx context.Context, chartOfAccountProcessor processor.ChartOfAccountProcessor) (map[string]uuid.UUID, error) {
|
func getChartOfAccountTypeMap(ctx context.Context, chartOfAccountProcessor interface{}) (map[string]uuid.UUID, error) {
|
||||||
// This is a placeholder - in a real implementation, you would call the processor
|
// This is a placeholder - in a real implementation, you would call the processor
|
||||||
// to get all chart of account types and create a map of code -> ID
|
// to get all chart of account types and create a map of code -> ID
|
||||||
return map[string]uuid.UUID{
|
return map[string]uuid.UUID{
|
||||||
|
|||||||
87
internal/util/waste_util.go
Normal file
87
internal/util/waste_util.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/entities"
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CalculateWasteQuantities calculates gross, net, and waste quantities for product ingredients
|
||||||
|
func CalculateWasteQuantities(productIngredients []*entities.ProductIngredient, quantity float64) ([]*models.CreateOrderIngredientTransactionRequest, error) {
|
||||||
|
if len(productIngredients) == 0 {
|
||||||
|
return []*models.CreateOrderIngredientTransactionRequest{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions := make([]*models.CreateOrderIngredientTransactionRequest, 0, len(productIngredients))
|
||||||
|
|
||||||
|
for _, pi := range productIngredients {
|
||||||
|
// Calculate net quantity (actual quantity needed for the product)
|
||||||
|
netQty := pi.Quantity * quantity
|
||||||
|
|
||||||
|
// Calculate gross quantity (including waste)
|
||||||
|
wasteMultiplier := 1 + (pi.WastePercentage / 100)
|
||||||
|
grossQty := netQty * wasteMultiplier
|
||||||
|
|
||||||
|
// Calculate waste quantity
|
||||||
|
wasteQty := grossQty - netQty
|
||||||
|
|
||||||
|
// Get unit name from ingredient
|
||||||
|
unitName := "unit" // default
|
||||||
|
if pi.Ingredient != nil && pi.Ingredient.Unit != nil {
|
||||||
|
unitName = pi.Ingredient.Unit.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction := &models.CreateOrderIngredientTransactionRequest{
|
||||||
|
IngredientID: pi.IngredientID,
|
||||||
|
GrossQty: RoundToDecimalPlaces(grossQty, 3),
|
||||||
|
NetQty: RoundToDecimalPlaces(netQty, 3),
|
||||||
|
WasteQty: RoundToDecimalPlaces(wasteQty, 3),
|
||||||
|
Unit: unitName,
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions = append(transactions, transaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrderIngredientTransactionsFromProduct creates order ingredient transactions for a product
|
||||||
|
func CreateOrderIngredientTransactionsFromProduct(
|
||||||
|
productID uuid.UUID,
|
||||||
|
productVariantID *uuid.UUID,
|
||||||
|
orderID uuid.UUID,
|
||||||
|
orderItemID *uuid.UUID,
|
||||||
|
quantity float64,
|
||||||
|
productIngredients []*entities.ProductIngredient,
|
||||||
|
organizationID, outletID, createdBy uuid.UUID,
|
||||||
|
) ([]*models.CreateOrderIngredientTransactionRequest, error) {
|
||||||
|
// Calculate waste quantities
|
||||||
|
wasteTransactions, err := CalculateWasteQuantities(productIngredients, quantity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to calculate waste quantities: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set common fields for all transactions
|
||||||
|
for _, transaction := range wasteTransactions {
|
||||||
|
transaction.OrderID = orderID
|
||||||
|
transaction.OrderItemID = orderItemID
|
||||||
|
transaction.ProductID = productID
|
||||||
|
transaction.ProductVariantID = productVariantID
|
||||||
|
transaction.TransactionDate = &time.Time{}
|
||||||
|
*transaction.TransactionDate = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
return wasteTransactions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundToDecimalPlaces rounds a float64 to the specified number of decimal places
|
||||||
|
func RoundToDecimalPlaces(value float64, places int) float64 {
|
||||||
|
multiplier := 1.0
|
||||||
|
for i := 0; i < places; i++ {
|
||||||
|
multiplier *= 10
|
||||||
|
}
|
||||||
|
return float64(int(value*multiplier+0.5)) / multiplier
|
||||||
|
}
|
||||||
120
internal/validator/order_ingredient_transaction_validator.go
Normal file
120
internal/validator/order_ingredient_transaction_validator.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"apskel-pos-be/internal/models"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OrderIngredientTransactionValidator interface {
|
||||||
|
ValidateCreateOrderIngredientTransactionRequest(req *models.CreateOrderIngredientTransactionRequest) error
|
||||||
|
ValidateUpdateOrderIngredientTransactionRequest(req *models.UpdateOrderIngredientTransactionRequest) error
|
||||||
|
ValidateListOrderIngredientTransactionsRequest(req *models.ListOrderIngredientTransactionsRequest) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderIngredientTransactionValidatorImpl struct {
|
||||||
|
validator *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderIngredientTransactionValidator() OrderIngredientTransactionValidator {
|
||||||
|
v := validator.New()
|
||||||
|
return &OrderIngredientTransactionValidatorImpl{
|
||||||
|
validator: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *OrderIngredientTransactionValidatorImpl) ValidateCreateOrderIngredientTransactionRequest(req *models.CreateOrderIngredientTransactionRequest) error {
|
||||||
|
if err := v.validator.Struct(req); err != nil {
|
||||||
|
var validationErrors []string
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom validations
|
||||||
|
if req.GrossQty < req.NetQty {
|
||||||
|
return fmt.Errorf("gross quantity must be greater than or equal to net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedWasteQty := req.GrossQty - req.NetQty
|
||||||
|
if req.WasteQty != expectedWasteQty {
|
||||||
|
return fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *OrderIngredientTransactionValidatorImpl) ValidateUpdateOrderIngredientTransactionRequest(req *models.UpdateOrderIngredientTransactionRequest) error {
|
||||||
|
if err := v.validator.Struct(req); err != nil {
|
||||||
|
var validationErrors []string
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom validations for partial updates
|
||||||
|
if req.GrossQty != nil && req.NetQty != nil {
|
||||||
|
if *req.GrossQty < *req.NetQty {
|
||||||
|
return fmt.Errorf("gross quantity must be greater than or equal to net quantity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.GrossQty != nil && req.NetQty != nil && req.WasteQty != nil {
|
||||||
|
expectedWasteQty := *req.GrossQty - *req.NetQty
|
||||||
|
if *req.WasteQty != expectedWasteQty {
|
||||||
|
return fmt.Errorf("waste quantity must equal gross quantity minus net quantity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *OrderIngredientTransactionValidatorImpl) ValidateListOrderIngredientTransactionsRequest(req *models.ListOrderIngredientTransactionsRequest) error {
|
||||||
|
if err := v.validator.Struct(req); err != nil {
|
||||||
|
var validationErrors []string
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
validationErrors = append(validationErrors, fmt.Sprintf("%s: %s", err.Field(), getValidationMessage(err)))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("validation failed: %s", strings.Join(validationErrors, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom validations
|
||||||
|
if req.StartDate != nil && req.EndDate != nil {
|
||||||
|
if req.StartDate.After(*req.EndDate) {
|
||||||
|
return fmt.Errorf("start date must be before end date")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getValidationMessage(err validator.FieldError) string {
|
||||||
|
switch err.Tag() {
|
||||||
|
case "required":
|
||||||
|
return "is required"
|
||||||
|
case "min":
|
||||||
|
return fmt.Sprintf("must be at least %s", err.Param())
|
||||||
|
case "max":
|
||||||
|
return fmt.Sprintf("must be at most %s", err.Param())
|
||||||
|
case "gt":
|
||||||
|
return fmt.Sprintf("must be greater than %s", err.Param())
|
||||||
|
case "gte":
|
||||||
|
return fmt.Sprintf("must be greater than or equal to %s", err.Param())
|
||||||
|
case "lt":
|
||||||
|
return fmt.Sprintf("must be less than %s", err.Param())
|
||||||
|
case "lte":
|
||||||
|
return fmt.Sprintf("must be less than or equal to %s", err.Param())
|
||||||
|
case "email":
|
||||||
|
return "must be a valid email address"
|
||||||
|
case "uuid":
|
||||||
|
return "must be a valid UUID"
|
||||||
|
case "len":
|
||||||
|
return fmt.Sprintf("must be exactly %s characters long", err.Param())
|
||||||
|
default:
|
||||||
|
return "is invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- Remove waste_percentage column from product_ingredients table
|
||||||
|
ALTER TABLE product_ingredients DROP COLUMN waste_percentage;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
-- Add waste_percentage column to product_ingredients table
|
||||||
|
ALTER TABLE product_ingredients
|
||||||
|
ADD COLUMN waste_percentage DECIMAL(5,2) DEFAULT 0.00 CHECK (waste_percentage >= 0 AND waste_percentage <= 100);
|
||||||
|
|
||||||
|
-- Add comment to explain the column
|
||||||
|
COMMENT ON COLUMN product_ingredients.waste_percentage IS 'Waste percentage for this ingredient (0-100). Used to calculate gross quantity needed including waste.';
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- Drop order ingredients transactions table
|
||||||
|
DROP TABLE IF EXISTS order_ingredients_transactions;
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
-- Order ingredients transactions table
|
||||||
|
CREATE TABLE order_ingredients_transactions (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
||||||
|
outlet_id UUID REFERENCES outlets(id) ON DELETE CASCADE,
|
||||||
|
order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
||||||
|
order_item_id UUID REFERENCES order_items(id) ON DELETE CASCADE,
|
||||||
|
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
|
||||||
|
product_variant_id UUID REFERENCES product_variants(id) ON DELETE CASCADE,
|
||||||
|
ingredient_id UUID NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
|
||||||
|
gross_qty DECIMAL(12,3) NOT NULL CHECK (gross_qty > 0),
|
||||||
|
net_qty DECIMAL(12,3) NOT NULL CHECK (net_qty > 0),
|
||||||
|
waste_qty DECIMAL(12,3) NOT NULL CHECK (waste_qty >= 0),
|
||||||
|
unit VARCHAR(50) NOT NULL,
|
||||||
|
transaction_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_organization_id ON order_ingredients_transactions(organization_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_outlet_id ON order_ingredients_transactions(outlet_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_order_id ON order_ingredients_transactions(order_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_order_item_id ON order_ingredients_transactions(order_item_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_product_id ON order_ingredients_transactions(product_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_product_variant_id ON order_ingredients_transactions(product_variant_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_ingredient_id ON order_ingredients_transactions(ingredient_id);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_transaction_date ON order_ingredients_transactions(transaction_date);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_created_by ON order_ingredients_transactions(created_by);
|
||||||
|
CREATE INDEX idx_order_ingredients_transactions_created_at ON order_ingredients_transactions(created_at);
|
||||||
|
|
||||||
|
-- Add comment to explain the table
|
||||||
|
COMMENT ON TABLE order_ingredients_transactions IS 'Tracks ingredient usage for orders including gross, net, and waste quantities';
|
||||||
|
COMMENT ON COLUMN order_ingredients_transactions.gross_qty IS 'Total quantity needed including waste';
|
||||||
|
COMMENT ON COLUMN order_ingredients_transactions.net_qty IS 'Actual quantity used in the product';
|
||||||
|
COMMENT ON COLUMN order_ingredients_transactions.waste_qty IS 'Waste quantity (gross_qty - net_qty)';
|
||||||
Loading…
x
Reference in New Issue
Block a user