add total out
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/appcontext"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"apskel-pos-be/internal/contract"
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
"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)
|
||||
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
|
||||
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)
|
||||
GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter, organizationID uuid.UUID) (*models.InventoryReportDetail, error)
|
||||
}
|
||||
|
||||
type InventoryProcessorImpl struct {
|
||||
inventoryRepo repository.InventoryRepository
|
||||
productRepo ProductRepository
|
||||
outletRepo OutletRepository
|
||||
inventoryRepo repository.InventoryRepository
|
||||
productRepo ProductRepository
|
||||
outletRepo OutletRepository
|
||||
ingredientRepo IngredientRepository
|
||||
inventoryMovementRepo repository.InventoryMovementRepository
|
||||
}
|
||||
|
||||
func NewInventoryProcessorImpl(
|
||||
inventoryRepo repository.InventoryRepository,
|
||||
productRepo ProductRepository,
|
||||
outletRepo OutletRepository,
|
||||
ingredientRepo IngredientRepository,
|
||||
inventoryMovementRepo repository.InventoryMovementRepository,
|
||||
) *InventoryProcessorImpl {
|
||||
return &InventoryProcessorImpl{
|
||||
inventoryRepo: inventoryRepo,
|
||||
productRepo: productRepo,
|
||||
outletRepo: outletRepo,
|
||||
inventoryRepo: inventoryRepo,
|
||||
productRepo: productRepo,
|
||||
outletRepo: outletRepo,
|
||||
ingredientRepo: ingredientRepo,
|
||||
inventoryMovementRepo: inventoryMovementRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,3 +411,165 @@ func (p *InventoryProcessorImpl) GetZeroStockItems(ctx context.Context, outletID
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user