215 lines
7.5 KiB
Go
215 lines
7.5 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"apskel-pos-be/internal/entities"
|
|
"apskel-pos-be/internal/mappers"
|
|
"apskel-pos-be/internal/models"
|
|
"apskel-pos-be/internal/repository"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type InventoryMovementProcessor interface {
|
|
CreateMovement(ctx context.Context, req *models.CreateInventoryMovementRequest) (*models.InventoryMovementResponse, error)
|
|
GetMovementByID(ctx context.Context, id uuid.UUID) (*models.InventoryMovementResponse, error)
|
|
ListMovements(ctx context.Context, req *models.ListInventoryMovementsRequest) (*models.ListInventoryMovementsResponse, error)
|
|
GetMovementsByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID, limit, offset int) (*models.ListInventoryMovementsResponse, error)
|
|
GetMovementsByOrderID(ctx context.Context, orderID uuid.UUID) ([]models.InventoryMovementResponse, error)
|
|
GetMovementsByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]models.InventoryMovementResponse, error)
|
|
}
|
|
|
|
type InventoryMovementRepository interface {
|
|
Create(ctx context.Context, movement *entities.InventoryMovement) error
|
|
GetByID(ctx context.Context, id uuid.UUID) (*entities.InventoryMovement, error)
|
|
GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.InventoryMovement, error)
|
|
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.InventoryMovement, int64, error)
|
|
GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID, limit, offset int) ([]*entities.InventoryMovement, int64, error)
|
|
GetByOrderID(ctx context.Context, orderID uuid.UUID) ([]*entities.InventoryMovement, error)
|
|
GetByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]*entities.InventoryMovement, error)
|
|
Count(ctx context.Context, filters map[string]interface{}) (int64, error)
|
|
}
|
|
|
|
type InventoryMovementProcessorImpl struct {
|
|
movementRepo InventoryMovementRepository
|
|
inventoryRepo repository.InventoryRepository
|
|
}
|
|
|
|
func NewInventoryMovementProcessorImpl(
|
|
movementRepo InventoryMovementRepository,
|
|
inventoryRepo repository.InventoryRepository,
|
|
) *InventoryMovementProcessorImpl {
|
|
return &InventoryMovementProcessorImpl{
|
|
movementRepo: movementRepo,
|
|
inventoryRepo: inventoryRepo,
|
|
}
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) CreateMovement(ctx context.Context, req *models.CreateInventoryMovementRequest) (*models.InventoryMovementResponse, error) {
|
|
currentInventory, err := p.inventoryRepo.GetByProductAndOutlet(ctx, req.ProductID, req.OutletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get current inventory: %w", err)
|
|
}
|
|
|
|
previousQuantity := currentInventory.Quantity
|
|
newQuantity := previousQuantity + req.Quantity
|
|
|
|
movement := &entities.InventoryMovement{
|
|
OrganizationID: req.OrganizationID,
|
|
OutletID: req.OutletID,
|
|
ProductID: req.ProductID,
|
|
MovementType: entities.InventoryMovementType(req.MovementType),
|
|
Quantity: req.Quantity,
|
|
PreviousQuantity: previousQuantity,
|
|
NewQuantity: newQuantity,
|
|
UnitCost: req.UnitCost,
|
|
TotalCost: float64(req.Quantity) * req.UnitCost,
|
|
ReferenceType: (*entities.InventoryMovementReferenceType)(req.ReferenceType),
|
|
ReferenceID: req.ReferenceID,
|
|
OrderID: req.OrderID,
|
|
PaymentID: req.PaymentID,
|
|
UserID: req.UserID,
|
|
Reason: req.Reason,
|
|
Notes: req.Notes,
|
|
Metadata: entities.Metadata(req.Metadata),
|
|
}
|
|
|
|
if err := p.movementRepo.Create(ctx, movement); err != nil {
|
|
return nil, fmt.Errorf("failed to create inventory movement: %w", err)
|
|
}
|
|
|
|
movementWithRelations, err := p.movementRepo.GetWithRelations(ctx, movement.ID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve created movement: %w", err)
|
|
}
|
|
|
|
response := mappers.InventoryMovementEntityToResponse(movementWithRelations)
|
|
return response, nil
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) GetMovementByID(ctx context.Context, id uuid.UUID) (*models.InventoryMovementResponse, error) {
|
|
movement, err := p.movementRepo.GetWithRelations(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("movement not found: %w", err)
|
|
}
|
|
|
|
response := mappers.InventoryMovementEntityToResponse(movement)
|
|
return response, nil
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) ListMovements(ctx context.Context, req *models.ListInventoryMovementsRequest) (*models.ListInventoryMovementsResponse, error) {
|
|
filters := make(map[string]interface{})
|
|
if req.OrganizationID != nil {
|
|
filters["organization_id"] = *req.OrganizationID
|
|
}
|
|
if req.OutletID != nil {
|
|
filters["outlet_id"] = *req.OutletID
|
|
}
|
|
if req.ProductID != nil {
|
|
filters["product_id"] = *req.ProductID
|
|
}
|
|
if req.MovementType != nil {
|
|
filters["movement_type"] = string(*req.MovementType)
|
|
}
|
|
if req.ReferenceType != nil {
|
|
filters["reference_type"] = string(*req.ReferenceType)
|
|
}
|
|
if req.ReferenceID != nil {
|
|
filters["reference_id"] = *req.ReferenceID
|
|
}
|
|
if req.OrderID != nil {
|
|
filters["order_id"] = *req.OrderID
|
|
}
|
|
if req.PaymentID != nil {
|
|
filters["payment_id"] = *req.PaymentID
|
|
}
|
|
if req.UserID != nil {
|
|
filters["user_id"] = *req.UserID
|
|
}
|
|
if req.DateFrom != nil {
|
|
filters["date_from"] = *req.DateFrom
|
|
}
|
|
if req.DateTo != nil {
|
|
filters["date_to"] = *req.DateTo
|
|
}
|
|
|
|
offset := (req.Page - 1) * req.Limit
|
|
movements, total, err := p.movementRepo.List(ctx, filters, req.Limit, offset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list movements: %w", err)
|
|
}
|
|
|
|
// Convert to responses
|
|
movementResponses := make([]models.InventoryMovementResponse, len(movements))
|
|
for i, movement := range movements {
|
|
response := mappers.InventoryMovementEntityToResponse(movement)
|
|
if response != nil {
|
|
movementResponses[i] = *response
|
|
}
|
|
}
|
|
|
|
// Calculate total pages
|
|
totalPages := int(total) / req.Limit
|
|
if int(total)%req.Limit > 0 {
|
|
totalPages++
|
|
}
|
|
|
|
return &models.ListInventoryMovementsResponse{
|
|
Movements: movementResponses,
|
|
TotalCount: int(total),
|
|
Page: req.Page,
|
|
Limit: req.Limit,
|
|
TotalPages: totalPages,
|
|
}, nil
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) GetMovementsByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID, limit, offset int) (*models.ListInventoryMovementsResponse, error) {
|
|
movements, total, err := p.movementRepo.GetByProductAndOutlet(ctx, productID, outletID, limit, offset)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get movements by product and outlet: %w", err)
|
|
}
|
|
|
|
movementResponses := make([]models.InventoryMovementResponse, len(movements))
|
|
for i, movement := range movements {
|
|
response := mappers.InventoryMovementEntityToResponse(movement)
|
|
if response != nil {
|
|
movementResponses[i] = *response
|
|
}
|
|
}
|
|
|
|
totalPages := int(total) / limit
|
|
if int(total)%limit > 0 {
|
|
totalPages++
|
|
}
|
|
|
|
return &models.ListInventoryMovementsResponse{
|
|
Movements: movementResponses,
|
|
TotalCount: int(total),
|
|
Page: 1,
|
|
Limit: limit,
|
|
TotalPages: totalPages,
|
|
}, nil
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) GetMovementsByOrderID(ctx context.Context, orderID uuid.UUID) ([]models.InventoryMovementResponse, error) {
|
|
movements, err := p.movementRepo.GetByOrderID(ctx, orderID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get movements by order ID: %w", err)
|
|
}
|
|
|
|
responses := mappers.InventoryMovementEntitiesToResponses(movements)
|
|
return responses, nil
|
|
}
|
|
|
|
func (p *InventoryMovementProcessorImpl) GetMovementsByPaymentID(ctx context.Context, paymentID uuid.UUID) ([]models.InventoryMovementResponse, error) {
|
|
movements, err := p.movementRepo.GetByPaymentID(ctx, paymentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get movements by payment ID: %w", err)
|
|
}
|
|
|
|
responses := mappers.InventoryMovementEntitiesToResponses(movements)
|
|
return responses, nil
|
|
}
|