add total out

This commit is contained in:
Aditya Siregar 2025-08-14 01:35:19 +07:00
parent 07b3eda263
commit 13d8c75be7
7 changed files with 320 additions and 77 deletions

View File

@ -210,7 +210,7 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo), categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo), productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo),
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo), productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo), inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
orderProcessor: processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, 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),

View File

@ -1,8 +1,9 @@
package contract package contract
import ( import (
"github.com/google/uuid"
"time" "time"
"github.com/google/uuid"
) )
type CreateInventoryRequest struct { type CreateInventoryRequest struct {
@ -24,6 +25,18 @@ type AdjustInventoryRequest struct {
Reason string `json:"reason" validate:"required,min=1,max=255"` Reason string `json:"reason" validate:"required,min=1,max=255"`
} }
type RestockInventoryRequest struct {
OutletID uuid.UUID `json:"outlet_id" validate:"required"`
Items []RestockItem `json:"items" validate:"required,min=1,dive"`
Reason string `json:"reason" validate:"required,min=1,max=255"`
}
type RestockItem struct {
ItemID uuid.UUID `json:"item_id" validate:"required"`
ItemType string `json:"item_type" validate:"required,oneof=PRODUCT INGREDIENT"`
Quantity int `json:"quantity" validate:"required,min=1"`
}
type ListInventoryRequest struct { type ListInventoryRequest struct {
OutletID *uuid.UUID `json:"outlet_id,omitempty"` OutletID *uuid.UUID `json:"outlet_id,omitempty"`
ProductID *uuid.UUID `json:"product_id,omitempty"` ProductID *uuid.UUID `json:"product_id,omitempty"`
@ -68,25 +81,43 @@ type InventoryAdjustmentResponse struct {
AdjustedAt time.Time `json:"adjusted_at"` AdjustedAt time.Time `json:"adjusted_at"`
} }
type RestockInventoryResponse struct {
OutletID uuid.UUID `json:"outlet_id"`
Items []RestockItemResult `json:"items"`
Reason string `json:"reason"`
RestockedAt time.Time `json:"restocked_at"`
}
type RestockItemResult struct {
ItemID uuid.UUID `json:"item_id"`
ItemType string `json:"item_type"`
ItemName string `json:"item_name"`
PreviousQty int `json:"previous_quantity"`
NewQty int `json:"new_quantity"`
AddedQty int `json:"added_quantity"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
// Inventory Report Contracts // Inventory Report Contracts
type InventoryReportSummaryResponse struct { type InventoryReportSummaryResponse struct {
TotalProducts int `json:"total_products"` TotalProducts int `json:"total_products"`
TotalIngredients int `json:"total_ingredients"` TotalIngredients int `json:"total_ingredients"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
LowStockProducts int `json:"low_stock_products"` LowStockProducts int `json:"low_stock_products"`
LowStockIngredients int `json:"low_stock_ingredients"` LowStockIngredients int `json:"low_stock_ingredients"`
ZeroStockProducts int `json:"zero_stock_products"` ZeroStockProducts int `json:"zero_stock_products"`
ZeroStockIngredients int `json:"zero_stock_ingredients"` ZeroStockIngredients int `json:"zero_stock_ingredients"`
TotalSoldProducts float64 `json:"total_sold_products"` TotalSoldProducts float64 `json:"total_sold_products"`
TotalSoldIngredients float64 `json:"total_sold_ingredients"` TotalSoldIngredients float64 `json:"total_sold_ingredients"`
OutletID string `json:"outlet_id"` OutletID string `json:"outlet_id"`
OutletName string `json:"outlet_name"` OutletName string `json:"outlet_name"`
GeneratedAt string `json:"generated_at"` GeneratedAt string `json:"generated_at"`
} }
type InventoryReportDetailResponse struct { type InventoryReportDetailResponse struct {
Summary *InventoryReportSummaryResponse `json:"summary"` Summary *InventoryReportSummaryResponse `json:"summary"`
Products []*InventoryProductDetailResponse `json:"products"` Products []*InventoryProductDetailResponse `json:"products"`
Ingredients []*InventoryIngredientDetailResponse `json:"ingredients"` Ingredients []*InventoryIngredientDetailResponse `json:"ingredients"`
} }
@ -95,29 +126,29 @@ type InventoryProductDetailResponse struct {
ProductID string `json:"product_id"` ProductID string `json:"product_id"`
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
CategoryName string `json:"category_name"` CategoryName string `json:"category_name"`
Quantity int `json:"quantity"` Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"` ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"` UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"` TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"` TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"` IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"` IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
} }
type InventoryIngredientDetailResponse struct { type InventoryIngredientDetailResponse struct {
ID string `json:"id"` ID string `json:"id"`
IngredientID string `json:"ingredient_id"` IngredientID string `json:"ingredient_id"`
IngredientName string `json:"ingredient_name"` IngredientName string `json:"ingredient_name"`
UnitName string `json:"unit_name"` UnitName string `json:"unit_name"`
Quantity int `json:"quantity"` Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"` ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"` UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"` TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"` TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"` IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"` IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
} }

View File

@ -238,6 +238,34 @@ func (h *InventoryHandler) AdjustInventory(c *gin.Context) {
util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::AdjustInventory") util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::AdjustInventory")
} }
func (h *InventoryHandler) RestockInventory(c *gin.Context) {
ctx := c.Request.Context()
var req contract.RestockInventoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.FromContext(c.Request.Context()).WithError(err).Error("InventoryHandler::RestockInventory -> request binding failed")
validationResponseError := contract.NewResponseError(constants.MissingFieldErrorCode, constants.RequestEntity, err.Error())
util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::RestockInventory")
return
}
// TODO: Add validation for restock request
// validationError, validationErrorCode := h.inventoryValidator.ValidateRestockInventoryRequest(&req)
// if validationError != nil {
// validationResponseError := contract.NewResponseError(validationErrorCode, constants.RequestEntity, validationError.Error())
// util.HandleResponse(c.Writer, c.Request, contract.BuildErrorResponse([]*contract.ResponseError{validationResponseError}), "InventoryHandler::RestockInventory")
// return
// }
inventoryResponse := h.inventoryService.RestockInventory(ctx, &req)
if inventoryResponse.HasErrors() {
errorResp := inventoryResponse.GetErrors()[0]
logger.FromContext(ctx).WithError(errorResp).Error("InventoryHandler::RestockInventory -> Failed to restock inventory from service")
}
util.HandleResponse(c.Writer, c.Request, inventoryResponse, "InventoryHandler::RestockInventory")
}
func (h *InventoryHandler) GetLowStockItems(c *gin.Context) { func (h *InventoryHandler) GetLowStockItems(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()

View File

@ -63,23 +63,23 @@ func (i *Inventory) AdjustQuantity(delta int) int {
// Inventory Report Models // Inventory Report Models
type InventoryReportSummary struct { type InventoryReportSummary struct {
TotalProducts int `json:"total_products"` TotalProducts int `json:"total_products"`
TotalIngredients int `json:"total_ingredients"` TotalIngredients int `json:"total_ingredients"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
LowStockProducts int `json:"low_stock_products"` LowStockProducts int `json:"low_stock_products"`
LowStockIngredients int `json:"low_stock_ingredients"` LowStockIngredients int `json:"low_stock_ingredients"`
ZeroStockProducts int `json:"zero_stock_products"` ZeroStockProducts int `json:"zero_stock_products"`
ZeroStockIngredients int `json:"zero_stock_ingredients"` ZeroStockIngredients int `json:"zero_stock_ingredients"`
TotalSoldProducts float64 `json:"total_sold_products"` TotalSoldProducts float64 `json:"total_sold_products"`
TotalSoldIngredients float64 `json:"total_sold_ingredients"` TotalSoldIngredients float64 `json:"total_sold_ingredients"`
OutletID uuid.UUID `json:"outlet_id"` OutletID uuid.UUID `json:"outlet_id"`
OutletName string `json:"outlet_name"` OutletName string `json:"outlet_name"`
GeneratedAt time.Time `json:"generated_at"` GeneratedAt time.Time `json:"generated_at"`
} }
type InventoryReportDetail struct { type InventoryReportDetail struct {
Summary *InventoryReportSummary `json:"summary"` Summary *InventoryReportSummary `json:"summary"`
Products []*InventoryProductDetail `json:"products"` Products []*InventoryProductDetail `json:"products"`
Ingredients []*InventoryIngredientDetail `json:"ingredients"` Ingredients []*InventoryIngredientDetail `json:"ingredients"`
} }
@ -88,31 +88,31 @@ type InventoryProductDetail struct {
ProductID uuid.UUID `json:"product_id"` ProductID uuid.UUID `json:"product_id"`
ProductName string `json:"product_name"` ProductName string `json:"product_name"`
CategoryName string `json:"category_name"` CategoryName string `json:"category_name"`
Quantity int `json:"quantity"` Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"` ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"` UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"` TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"` TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"` IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"` IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
type InventoryIngredientDetail struct { type InventoryIngredientDetail struct {
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
IngredientID uuid.UUID `json:"ingredient_id"` IngredientID uuid.UUID `json:"ingredient_id"`
IngredientName string `json:"ingredient_name"` IngredientName string `json:"ingredient_name"`
UnitName string `json:"unit_name"` UnitName string `json:"unit_name"`
Quantity int `json:"quantity"` Quantity int `json:"quantity"`
ReorderLevel int `json:"reorder_level"` ReorderLevel int `json:"reorder_level"`
UnitCost float64 `json:"unit_cost"` UnitCost float64 `json:"unit_cost"`
TotalValue float64 `json:"total_value"` TotalValue float64 `json:"total_value"`
TotalIn float64 `json:"total_in"` TotalIn float64 `json:"total_in"`
TotalOut float64 `json:"total_out"` TotalOut float64 `json:"total_out"`
IsLowStock bool `json:"is_low_stock"` IsLowStock bool `json:"is_low_stock"`
IsZeroStock bool `json:"is_zero_stock"` IsZeroStock bool `json:"is_zero_stock"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
type InventoryReportFilter struct { type InventoryReportFilter struct {

View File

@ -1,10 +1,13 @@
package processor package processor
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/mappers" "apskel-pos-be/internal/mappers"
"apskel-pos-be/internal/models" "apskel-pos-be/internal/models"
"apskel-pos-be/internal/repository" "apskel-pos-be/internal/repository"
@ -26,25 +29,32 @@ type InventoryProcessor interface {
AdjustQuantity(ctx context.Context, productID, outletID, organizationID uuid.UUID, delta int) (*models.InventoryResponse, error) AdjustQuantity(ctx context.Context, productID, outletID, organizationID uuid.UUID, delta int) (*models.InventoryResponse, error)
SetQuantity(ctx context.Context, productID, outletID, organizationID uuid.UUID, quantity int) (*models.InventoryResponse, error) SetQuantity(ctx context.Context, productID, outletID, organizationID uuid.UUID, quantity int) (*models.InventoryResponse, error)
UpdateReorderLevel(ctx context.Context, id uuid.UUID, reorderLevel int, organizationID uuid.UUID) error UpdateReorderLevel(ctx context.Context, id uuid.UUID, reorderLevel int, organizationID uuid.UUID) error
RestockInventory(ctx context.Context, outletID uuid.UUID, items []contract.RestockItem, reason string) (*contract.RestockInventoryResponse, error)
GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*models.InventoryReportSummary, error) GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*models.InventoryReportSummary, error)
GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter, organizationID uuid.UUID) (*models.InventoryReportDetail, error) GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter, organizationID uuid.UUID) (*models.InventoryReportDetail, error)
} }
type InventoryProcessorImpl struct { type InventoryProcessorImpl struct {
inventoryRepo repository.InventoryRepository inventoryRepo repository.InventoryRepository
productRepo ProductRepository productRepo ProductRepository
outletRepo OutletRepository outletRepo OutletRepository
ingredientRepo IngredientRepository
inventoryMovementRepo repository.InventoryMovementRepository
} }
func NewInventoryProcessorImpl( func NewInventoryProcessorImpl(
inventoryRepo repository.InventoryRepository, inventoryRepo repository.InventoryRepository,
productRepo ProductRepository, productRepo ProductRepository,
outletRepo OutletRepository, outletRepo OutletRepository,
ingredientRepo IngredientRepository,
inventoryMovementRepo repository.InventoryMovementRepository,
) *InventoryProcessorImpl { ) *InventoryProcessorImpl {
return &InventoryProcessorImpl{ return &InventoryProcessorImpl{
inventoryRepo: inventoryRepo, inventoryRepo: inventoryRepo,
productRepo: productRepo, productRepo: productRepo,
outletRepo: outletRepo, outletRepo: outletRepo,
ingredientRepo: ingredientRepo,
inventoryMovementRepo: inventoryMovementRepo,
} }
} }
@ -401,3 +411,165 @@ func (p *InventoryProcessorImpl) GetZeroStockItems(ctx context.Context, outletID
return responses, nil return responses, nil
} }
func (p *InventoryProcessorImpl) RestockInventory(ctx context.Context, outletID uuid.UUID, items []contract.RestockItem, reason string) (*contract.RestockInventoryResponse, error) {
outlet, err := p.outletRepo.GetByID(ctx, outletID)
if err != nil {
return nil, fmt.Errorf("invalid outlet: %w", err)
}
if len(items) == 0 {
return nil, fmt.Errorf("no items provided for restocking")
}
restockResults := make([]contract.RestockItemResult, 0, len(items))
restockedAt := time.Now()
for _, item := range items {
result := contract.RestockItemResult{
ItemID: item.ItemID,
ItemType: item.ItemType,
AddedQty: item.Quantity,
Success: false,
}
switch item.ItemType {
case "PRODUCT":
if err := p.restockProduct(ctx, outletID, item.ItemID, item.Quantity, reason, outlet.OrganizationID); err != nil {
result.Error = err.Error()
}
case "INGREDIENT":
if err := p.restockIngredient(ctx, outletID, item.ItemID, item.Quantity, reason, outlet.OrganizationID); err != nil {
result.Error = err.Error()
}
default:
result.Error = fmt.Sprintf("unsupported item type: %s", item.ItemType)
}
restockResults = append(restockResults, result)
}
return &contract.RestockInventoryResponse{
OutletID: outletID,
Items: restockResults,
Reason: reason,
RestockedAt: restockedAt,
}, nil
}
func (p *InventoryProcessorImpl) restockProduct(ctx context.Context, outletID, productID uuid.UUID, quantity int, reason string, organizationID uuid.UUID) error {
contextInfo := appcontext.FromGinContext(ctx)
var previousQuantity int
var newQuantity int
inventory, err := p.inventoryRepo.GetByProductAndOutlet(ctx, productID, outletID)
if err != nil {
createReq := &models.CreateInventoryRequest{
OutletID: outletID,
ProductID: productID,
Quantity: quantity,
ReorderLevel: 0,
}
inventoryEntity := mappers.CreateInventoryRequestToEntity(createReq)
if err := p.inventoryRepo.Create(ctx, inventoryEntity); err != nil {
return fmt.Errorf("failed to create inventory for product %s: %w", productID, err)
}
previousQuantity = 0
newQuantity = quantity
} else {
previousQuantity = inventory.Quantity
newQuantity = inventory.Quantity + quantity
updateReq := &models.UpdateInventoryRequest{
Quantity: &newQuantity,
}
mappers.UpdateInventoryEntityFromRequest(inventory, updateReq)
if err := p.inventoryRepo.Update(ctx, inventory); err != nil {
return fmt.Errorf("failed to update inventory for product %s: %w", productID, err)
}
}
referenceType := entities.InventoryMovementReferenceTypeManual
movement := &entities.InventoryMovement{
OrganizationID: organizationID,
OutletID: outletID,
ItemID: productID,
ItemType: "PRODUCT",
MovementType: entities.InventoryMovementTypePurchase,
Quantity: float64(quantity),
PreviousQuantity: float64(previousQuantity),
NewQuantity: float64(newQuantity),
ReferenceType: &referenceType,
Reason: &reason,
UserID: contextInfo.UserID,
}
if err := p.inventoryMovementRepo.Create(ctx, movement); err != nil {
return fmt.Errorf("failed to create inventory movement record: %w", err)
}
return nil
}
func (p *InventoryProcessorImpl) restockIngredient(ctx context.Context, outletID, ingredientID uuid.UUID, quantity int, reason string, organizationID uuid.UUID) error {
contextInfo := appcontext.FromGinContext(ctx)
ingredient, err := p.getIngredientByID(ctx, ingredientID, organizationID)
if err != nil {
return fmt.Errorf("failed to get ingredient %s: %w", ingredientID, err)
}
previousStock := ingredient.Stock
newStock := ingredient.Stock + float64(quantity)
if err := p.updateIngredientStock(ctx, ingredientID, float64(quantity), organizationID); err != nil {
return fmt.Errorf("failed to update ingredient stock: %w", err)
}
referenceType := entities.InventoryMovementReferenceTypeManual
movement := &entities.InventoryMovement{
OrganizationID: organizationID,
OutletID: outletID,
ItemID: ingredientID,
ItemType: "INGREDIENT",
MovementType: entities.InventoryMovementTypePurchase,
Quantity: float64(quantity),
PreviousQuantity: previousStock,
NewQuantity: newStock,
ReferenceType: &referenceType,
Reason: &reason,
UserID: contextInfo.UserID,
}
if err := p.inventoryMovementRepo.Create(ctx, movement); err != nil {
return fmt.Errorf("failed to create inventory movement record: %w", err)
}
return nil
}
func (p *InventoryProcessorImpl) getIngredientByID(ctx context.Context, ingredientID, organizationID uuid.UUID) (*models.Ingredient, error) {
ingredient, err := p.ingredientRepo.GetByID(ctx, ingredientID, organizationID)
if err != nil {
return nil, fmt.Errorf("failed to get ingredient %s: %w", ingredientID, err)
}
return &models.Ingredient{
ID: ingredient.ID,
Name: ingredient.Name,
Stock: ingredient.Stock,
UnitID: ingredient.UnitID,
OrganizationID: ingredient.OrganizationID,
OutletID: ingredient.OutletID,
IsActive: ingredient.IsActive,
CreatedAt: ingredient.CreatedAt,
UpdatedAt: ingredient.UpdatedAt,
}, nil
}
func (p *InventoryProcessorImpl) updateIngredientStock(ctx context.Context, ingredientID uuid.UUID, quantityToAdd float64, organizationID uuid.UUID) error {
if err := p.ingredientRepo.UpdateStock(ctx, ingredientID, quantityToAdd, organizationID); err != nil {
return fmt.Errorf("failed to update ingredient stock: %w", err)
}
return nil
}

View File

@ -203,6 +203,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
inventory.PUT("/:id", r.inventoryHandler.UpdateInventory) inventory.PUT("/:id", r.inventoryHandler.UpdateInventory)
inventory.DELETE("/:id", r.inventoryHandler.DeleteInventory) inventory.DELETE("/:id", r.inventoryHandler.DeleteInventory)
inventory.POST("/adjust", r.inventoryHandler.AdjustInventory) inventory.POST("/adjust", r.inventoryHandler.AdjustInventory)
inventory.POST("/restock", r.inventoryHandler.RestockInventory)
inventory.GET("/low-stock/:outlet_id", r.inventoryHandler.GetLowStockItems) inventory.GET("/low-stock/:outlet_id", r.inventoryHandler.GetLowStockItems)
inventory.GET("/zero-stock/:outlet_id", r.inventoryHandler.GetZeroStockItems) inventory.GET("/zero-stock/:outlet_id", r.inventoryHandler.GetZeroStockItems)
inventory.GET("/report/summary/:outlet_id", r.inventoryHandler.GetInventoryReportSummary) inventory.GET("/report/summary/:outlet_id", r.inventoryHandler.GetInventoryReportSummary)

View File

@ -21,6 +21,7 @@ type InventoryService interface {
GetInventoryByID(ctx context.Context, id uuid.UUID) *contract.Response GetInventoryByID(ctx context.Context, id uuid.UUID) *contract.Response
ListInventory(ctx context.Context, req *contract.ListInventoryRequest) *contract.Response ListInventory(ctx context.Context, req *contract.ListInventoryRequest) *contract.Response
AdjustInventory(ctx context.Context, req *contract.AdjustInventoryRequest) *contract.Response AdjustInventory(ctx context.Context, req *contract.AdjustInventoryRequest) *contract.Response
RestockInventory(ctx context.Context, req *contract.RestockInventoryRequest) *contract.Response
GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response
GetZeroStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response GetZeroStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response
GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*contract.InventoryReportSummaryResponse, error) GetInventoryReportSummary(ctx context.Context, outletID, organizationID uuid.UUID, dateFrom, dateTo *time.Time) (*contract.InventoryReportSummaryResponse, error)
@ -153,6 +154,16 @@ func (s *InventoryServiceImpl) AdjustInventory(ctx context.Context, req *contrac
return contract.BuildSuccessResponse(contractResponse) return contract.BuildSuccessResponse(contractResponse)
} }
func (s *InventoryServiceImpl) RestockInventory(ctx context.Context, req *contract.RestockInventoryRequest) *contract.Response {
restockResponse, err := s.inventoryProcessor.RestockInventory(ctx, req.OutletID, req.Items, req.Reason)
if err != nil {
errorResp := contract.NewResponseError(constants.InternalServerErrorCode, constants.InventoryServiceEntity, err.Error())
return contract.BuildErrorResponse([]*contract.ResponseError{errorResp})
}
return contract.BuildSuccessResponse(restockResponse)
}
func (s *InventoryServiceImpl) GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response { func (s *InventoryServiceImpl) GetLowStockItems(ctx context.Context, outletID uuid.UUID) *contract.Response {
inventory, err := s.inventoryProcessor.GetLowStock(ctx, outletID, uuid.Nil) // TODO: Get organizationID from context inventory, err := s.inventoryProcessor.GetLowStock(ctx, outletID, uuid.Nil) // TODO: Get organizationID from context
if err != nil { if err != nil {