init
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
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
|
||||
}
|
||||
@@ -4,9 +4,9 @@ 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"
|
||||
)
|
||||
@@ -22,35 +22,14 @@ type InventoryProcessor interface {
|
||||
GetZeroStockItems(ctx context.Context, outletID uuid.UUID) ([]models.InventoryResponse, error)
|
||||
}
|
||||
|
||||
type InventoryRepository interface {
|
||||
Create(ctx context.Context, inventory *entities.Inventory) error
|
||||
GetByID(ctx context.Context, id uuid.UUID) (*entities.Inventory, error)
|
||||
GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.Inventory, error)
|
||||
GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID) (*entities.Inventory, error)
|
||||
GetByOutlet(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
|
||||
GetByProduct(ctx context.Context, productID uuid.UUID) ([]*entities.Inventory, error)
|
||||
GetLowStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
|
||||
GetZeroStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
|
||||
Update(ctx context.Context, inventory *entities.Inventory) error
|
||||
Delete(ctx context.Context, id uuid.UUID) error
|
||||
List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Inventory, int64, error)
|
||||
Count(ctx context.Context, filters map[string]interface{}) (int64, error)
|
||||
AdjustQuantity(ctx context.Context, productID, outletID uuid.UUID, delta int) (*entities.Inventory, error)
|
||||
SetQuantity(ctx context.Context, productID, outletID uuid.UUID, quantity int) (*entities.Inventory, error)
|
||||
UpdateReorderLevel(ctx context.Context, id uuid.UUID, reorderLevel int) error
|
||||
BulkCreate(ctx context.Context, inventoryItems []*entities.Inventory) error
|
||||
BulkAdjustQuantity(ctx context.Context, adjustments map[uuid.UUID]int, outletID uuid.UUID) error
|
||||
GetTotalValueByOutlet(ctx context.Context, outletID uuid.UUID) (float64, error)
|
||||
}
|
||||
|
||||
type InventoryProcessorImpl struct {
|
||||
inventoryRepo InventoryRepository
|
||||
inventoryRepo repository.InventoryRepository
|
||||
productRepo ProductRepository
|
||||
outletRepo OutletRepository
|
||||
}
|
||||
|
||||
func NewInventoryProcessorImpl(
|
||||
inventoryRepo InventoryRepository,
|
||||
inventoryRepo repository.InventoryRepository,
|
||||
productRepo ProductRepository,
|
||||
outletRepo OutletRepository,
|
||||
) *InventoryProcessorImpl {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/mappers"
|
||||
"apskel-pos-be/internal/models"
|
||||
"apskel-pos-be/internal/repository"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -62,6 +63,8 @@ type PaymentRepository interface {
|
||||
RefundPayment(ctx context.Context, id uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error
|
||||
UpdateStatus(ctx context.Context, id uuid.UUID, status entities.PaymentTransactionStatus) error
|
||||
GetTotalPaidByOrderID(ctx context.Context, orderID uuid.UUID) (float64, error)
|
||||
CreatePaymentWithInventoryMovement(ctx context.Context, req *models.CreatePaymentRequest, order *entities.Order, totalPaid float64) (*entities.Payment, error)
|
||||
RefundPaymentWithInventoryMovement(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID, order *entities.Payment) error
|
||||
}
|
||||
|
||||
type PaymentMethodRepository interface {
|
||||
@@ -89,15 +92,16 @@ func (r *SimplePaymentMethodRepository) GetByID(ctx context.Context, id uuid.UUI
|
||||
}
|
||||
|
||||
type OrderProcessorImpl struct {
|
||||
orderRepo OrderRepository
|
||||
orderItemRepo OrderItemRepository
|
||||
paymentRepo PaymentRepository
|
||||
productRepo ProductRepository
|
||||
paymentMethodRepo PaymentMethodRepository
|
||||
inventoryRepo InventoryRepository
|
||||
productVariantRepo ProductVariantRepository
|
||||
outletRepo OutletRepository
|
||||
customerRepo CustomerRepository
|
||||
orderRepo OrderRepository
|
||||
orderItemRepo OrderItemRepository
|
||||
paymentRepo PaymentRepository
|
||||
productRepo ProductRepository
|
||||
paymentMethodRepo PaymentMethodRepository
|
||||
inventoryRepo repository.InventoryRepository
|
||||
inventoryMovementRepo repository.InventoryMovementRepository
|
||||
productVariantRepo ProductVariantRepository
|
||||
outletRepo OutletRepository
|
||||
customerRepo CustomerRepository
|
||||
}
|
||||
|
||||
func NewOrderProcessorImpl(
|
||||
@@ -106,21 +110,23 @@ func NewOrderProcessorImpl(
|
||||
paymentRepo PaymentRepository,
|
||||
productRepo ProductRepository,
|
||||
paymentMethodRepo PaymentMethodRepository,
|
||||
inventoryRepo InventoryRepository,
|
||||
inventoryRepo repository.InventoryRepository,
|
||||
inventoryMovementRepo repository.InventoryMovementRepository,
|
||||
productVariantRepo ProductVariantRepository,
|
||||
outletRepo OutletRepository,
|
||||
customerRepo CustomerRepository,
|
||||
) *OrderProcessorImpl {
|
||||
return &OrderProcessorImpl{
|
||||
orderRepo: orderRepo,
|
||||
orderItemRepo: orderItemRepo,
|
||||
paymentRepo: paymentRepo,
|
||||
productRepo: productRepo,
|
||||
paymentMethodRepo: paymentMethodRepo,
|
||||
inventoryRepo: inventoryRepo,
|
||||
productVariantRepo: productVariantRepo,
|
||||
outletRepo: outletRepo,
|
||||
customerRepo: customerRepo,
|
||||
orderRepo: orderRepo,
|
||||
orderItemRepo: orderItemRepo,
|
||||
paymentRepo: paymentRepo,
|
||||
productRepo: productRepo,
|
||||
paymentMethodRepo: paymentMethodRepo,
|
||||
inventoryRepo: inventoryRepo,
|
||||
inventoryMovementRepo: inventoryMovementRepo,
|
||||
productVariantRepo: productVariantRepo,
|
||||
outletRepo: outletRepo,
|
||||
customerRepo: customerRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,7 +625,6 @@ func (p *OrderProcessorImpl) RefundOrder(ctx context.Context, id uuid.UUID, req
|
||||
return fmt.Errorf("order not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if order can be refunded
|
||||
if order.IsRefund {
|
||||
return fmt.Errorf("order is already refunded")
|
||||
}
|
||||
@@ -738,69 +743,11 @@ func (p *OrderProcessorImpl) CreatePayment(ctx context.Context, req *models.Crea
|
||||
return nil, fmt.Errorf("payment amount exceeds remaining balance")
|
||||
}
|
||||
|
||||
payment := &entities.Payment{
|
||||
OrderID: req.OrderID,
|
||||
PaymentMethodID: req.PaymentMethodID,
|
||||
Amount: req.Amount,
|
||||
Status: entities.PaymentTransactionStatusCompleted,
|
||||
TransactionID: req.TransactionID,
|
||||
SplitNumber: req.SplitNumber,
|
||||
SplitTotal: req.SplitTotal,
|
||||
SplitDescription: req.SplitDescription,
|
||||
Metadata: entities.Metadata(req.Metadata),
|
||||
payment, err := p.paymentRepo.CreatePaymentWithInventoryMovement(ctx, req, order, totalPaid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := p.paymentRepo.Create(ctx, payment); err != nil {
|
||||
return nil, fmt.Errorf("failed to create payment: %w", err)
|
||||
}
|
||||
|
||||
if len(req.PaymentOrderItems) > 0 {
|
||||
for _, itemPayment := range req.PaymentOrderItems {
|
||||
paymentOrderItem := &entities.PaymentOrderItem{
|
||||
PaymentID: payment.ID,
|
||||
OrderItemID: itemPayment.OrderItemID,
|
||||
Amount: itemPayment.Amount,
|
||||
}
|
||||
|
||||
fmt.Println(paymentOrderItem)
|
||||
// TODO: Create payment order item in database
|
||||
// This would require a PaymentOrderItemRepository
|
||||
}
|
||||
}
|
||||
|
||||
// Update order payment status if fully paid
|
||||
newTotalPaid := totalPaid + req.Amount
|
||||
orderJustCompleted := false
|
||||
if newTotalPaid >= order.TotalAmount {
|
||||
if order.PaymentStatus != entities.PaymentStatusCompleted {
|
||||
orderJustCompleted = true
|
||||
}
|
||||
if err := p.orderRepo.UpdatePaymentStatus(ctx, req.OrderID, entities.PaymentStatusCompleted); err != nil {
|
||||
return nil, fmt.Errorf("failed to update order payment status: %w", err)
|
||||
}
|
||||
// Set order status to completed when fully paid
|
||||
if err := p.orderRepo.UpdateStatus(ctx, req.OrderID, entities.OrderStatusCompleted); err != nil {
|
||||
return nil, fmt.Errorf("failed to update order status: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := p.orderRepo.UpdatePaymentStatus(ctx, req.OrderID, entities.PaymentStatusPartiallyRefunded); err != nil {
|
||||
return nil, fmt.Errorf("failed to update order payment status: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if orderJustCompleted {
|
||||
orderItems, err := p.orderItemRepo.GetByOrderID(ctx, req.OrderID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get order items for inventory adjustment: %w", err)
|
||||
}
|
||||
for _, item := range orderItems {
|
||||
if _, err := p.inventoryRepo.AdjustQuantity(ctx, item.ProductID, order.OutletID, -item.Quantity); err != nil {
|
||||
return nil, fmt.Errorf("failed to adjust inventory for product %s: %w", item.ProductID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get payment with relations for response
|
||||
paymentWithRelations, err := p.paymentRepo.GetByID(ctx, payment.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve created payment: %w", err)
|
||||
@@ -810,14 +757,16 @@ func (p *OrderProcessorImpl) CreatePayment(ctx context.Context, req *models.Crea
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func stringPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func (p *OrderProcessorImpl) RefundPayment(ctx context.Context, paymentID uuid.UUID, refundAmount float64, reason string, refundedBy uuid.UUID) error {
|
||||
// Get payment
|
||||
payment, err := p.paymentRepo.GetByID(ctx, paymentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("payment not found: %w", err)
|
||||
}
|
||||
|
||||
// Check if payment can be refunded
|
||||
if payment.Status != entities.PaymentTransactionStatusCompleted {
|
||||
return fmt.Errorf("payment is not completed, cannot refund")
|
||||
}
|
||||
@@ -826,23 +775,7 @@ func (p *OrderProcessorImpl) RefundPayment(ctx context.Context, paymentID uuid.U
|
||||
return fmt.Errorf("refund amount cannot exceed payment amount")
|
||||
}
|
||||
|
||||
// Process refund
|
||||
if err := p.paymentRepo.RefundPayment(ctx, paymentID, refundAmount, reason, refundedBy); err != nil {
|
||||
return fmt.Errorf("failed to refund payment: %w", err)
|
||||
}
|
||||
|
||||
// Update order refund amount
|
||||
order, err := p.orderRepo.GetByID(ctx, payment.OrderID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get order: %w", err)
|
||||
}
|
||||
|
||||
order.RefundAmount += refundAmount
|
||||
if err := p.orderRepo.Update(ctx, order); err != nil {
|
||||
return fmt.Errorf("failed to update order refund amount: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return p.paymentRepo.RefundPaymentWithInventoryMovement(ctx, paymentID, refundAmount, reason, refundedBy, payment)
|
||||
}
|
||||
|
||||
func (p *OrderProcessorImpl) SetOrderCustomer(ctx context.Context, orderID uuid.UUID, req *models.SetOrderCustomerRequest, organizationID uuid.UUID) (*models.SetOrderCustomerResponse, error) {
|
||||
|
||||
@@ -45,11 +45,11 @@ type ProductProcessorImpl struct {
|
||||
productRepo ProductRepository
|
||||
categoryRepo CategoryRepository
|
||||
productVariantRepo repository.ProductVariantRepository
|
||||
inventoryRepo InventoryRepository
|
||||
inventoryRepo repository.InventoryRepository
|
||||
outletRepo OutletRepository
|
||||
}
|
||||
|
||||
func NewProductProcessorImpl(productRepo ProductRepository, categoryRepo CategoryRepository, productVariantRepo repository.ProductVariantRepository, inventoryRepo InventoryRepository, outletRepo OutletRepository) *ProductProcessorImpl {
|
||||
func NewProductProcessorImpl(productRepo ProductRepository, categoryRepo CategoryRepository, productVariantRepo repository.ProductVariantRepository, inventoryRepo repository.InventoryRepository, outletRepo OutletRepository) *ProductProcessorImpl {
|
||||
return &ProductProcessorImpl{
|
||||
productRepo: productRepo,
|
||||
categoryRepo: categoryRepo,
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package processor
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"apskel-pos-be/internal/entities"
|
||||
"apskel-pos-be/internal/models"
|
||||
"apskel-pos-be/internal/repository"
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type TableProcessor struct {
|
||||
tableRepo *repository.TableRepository
|
||||
orderRepo *repository.OrderRepository
|
||||
}
|
||||
|
||||
func NewTableProcessor(tableRepo *repository.TableRepository, orderRepo *repository.OrderRepository) *TableProcessor {
|
||||
return &TableProcessor{
|
||||
tableRepo: tableRepo,
|
||||
orderRepo: orderRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TableProcessor) Create(ctx context.Context, req models.CreateTableRequest, organizationID uuid.UUID) (*models.TableResponse, error) {
|
||||
table := &entities.Table{
|
||||
OrganizationID: organizationID,
|
||||
OutletID: req.OutletID,
|
||||
TableName: req.TableName,
|
||||
PositionX: req.PositionX,
|
||||
PositionY: req.PositionY,
|
||||
Capacity: req.Capacity,
|
||||
Status: string(constants.TableStatusAvailable),
|
||||
IsActive: true,
|
||||
Metadata: req.Metadata,
|
||||
}
|
||||
|
||||
err := p.tableRepo.Create(ctx, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapTableToResponse(table), nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) GetByID(ctx context.Context, id uuid.UUID) (*models.TableResponse, error) {
|
||||
table, err := p.tableRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapTableToResponse(table), nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) Update(ctx context.Context, id uuid.UUID, req models.UpdateTableRequest) (*models.TableResponse, error) {
|
||||
table, err := p.tableRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.TableName != nil {
|
||||
table.TableName = *req.TableName
|
||||
}
|
||||
if req.Status != nil {
|
||||
table.Status = string(*req.Status)
|
||||
}
|
||||
if req.PositionX != nil {
|
||||
table.PositionX = *req.PositionX
|
||||
}
|
||||
if req.PositionY != nil {
|
||||
table.PositionY = *req.PositionY
|
||||
}
|
||||
if req.Capacity != nil {
|
||||
table.Capacity = *req.Capacity
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
table.IsActive = *req.IsActive
|
||||
}
|
||||
if req.Metadata != nil {
|
||||
table.Metadata = req.Metadata
|
||||
}
|
||||
|
||||
err = p.tableRepo.Update(ctx, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapTableToResponse(table), nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
table, err := p.tableRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if table.IsOccupied() {
|
||||
return errors.New("cannot delete occupied table")
|
||||
}
|
||||
|
||||
return p.tableRepo.Delete(ctx, id)
|
||||
}
|
||||
|
||||
func (p *TableProcessor) List(ctx context.Context, req models.ListTablesRequest) (*models.ListTablesResponse, error) {
|
||||
tables, total, err := p.tableRepo.List(ctx, req.OrganizationID, req.OutletID, (*string)(req.Status), req.IsActive, req.Search, req.Page, req.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := make([]models.TableResponse, len(tables))
|
||||
for i, table := range tables {
|
||||
responses[i] = *p.mapTableToResponse(&table)
|
||||
}
|
||||
|
||||
totalPages := int(math.Ceil(float64(total) / float64(req.Limit)))
|
||||
|
||||
return &models.ListTablesResponse{
|
||||
Tables: responses,
|
||||
TotalCount: int(total),
|
||||
Page: req.Page,
|
||||
Limit: req.Limit,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) OccupyTable(ctx context.Context, tableID uuid.UUID, req models.OccupyTableRequest) (*models.TableResponse, error) {
|
||||
table, err := p.tableRepo.GetByID(ctx, tableID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !table.CanBeOccupied() {
|
||||
return nil, errors.New("table is not available for occupation")
|
||||
}
|
||||
|
||||
// Verify order exists
|
||||
order, err := p.orderRepo.GetByID(ctx, req.OrderID)
|
||||
if err != nil {
|
||||
return nil, errors.New("order not found")
|
||||
}
|
||||
|
||||
err = p.tableRepo.OccupyTable(ctx, tableID, req.OrderID, &req.StartTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get updated table
|
||||
updatedTable, err := p.tableRepo.GetByID(ctx, tableID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapTableToResponse(updatedTable), nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) ReleaseTable(ctx context.Context, tableID uuid.UUID, req models.ReleaseTableRequest) (*models.TableResponse, error) {
|
||||
table, err := p.tableRepo.GetByID(ctx, tableID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !table.IsOccupied() {
|
||||
return nil, errors.New("table is not occupied")
|
||||
}
|
||||
|
||||
err = p.tableRepo.ReleaseTable(ctx, tableID, req.PaymentAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get updated table
|
||||
updatedTable, err := p.tableRepo.GetByID(ctx, tableID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.mapTableToResponse(updatedTable), nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) GetAvailableTables(ctx context.Context, outletID uuid.UUID) ([]models.TableResponse, error) {
|
||||
tables, err := p.tableRepo.GetAvailableTables(ctx, outletID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := make([]models.TableResponse, len(tables))
|
||||
for i, table := range tables {
|
||||
responses[i] = *p.mapTableToResponse(&table)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) GetOccupiedTables(ctx context.Context, outletID uuid.UUID) ([]models.TableResponse, error) {
|
||||
tables, err := p.tableRepo.GetOccupiedTables(ctx, outletID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responses := make([]models.TableResponse, len(tables))
|
||||
for i, table := range tables {
|
||||
responses[i] = *p.mapTableToResponse(&table)
|
||||
}
|
||||
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (p *TableProcessor) mapTableToResponse(table *entities.Table) *models.TableResponse {
|
||||
response := &models.TableResponse{
|
||||
ID: table.ID,
|
||||
OrganizationID: table.OrganizationID,
|
||||
OutletID: table.OutletID,
|
||||
TableName: table.TableName,
|
||||
StartTime: table.StartTime,
|
||||
Status: constants.TableStatus(table.Status),
|
||||
OrderID: table.OrderID,
|
||||
PaymentAmount: table.PaymentAmount,
|
||||
PositionX: table.PositionX,
|
||||
PositionY: table.PositionY,
|
||||
Capacity: table.Capacity,
|
||||
IsActive: table.IsActive,
|
||||
Metadata: table.Metadata,
|
||||
CreatedAt: table.CreatedAt,
|
||||
UpdatedAt: table.UpdatedAt,
|
||||
}
|
||||
|
||||
if table.Order != nil {
|
||||
response.Order = &models.OrderResponse{
|
||||
ID: table.Order.ID,
|
||||
OrganizationID: table.Order.OrganizationID,
|
||||
OutletID: table.Order.OutletID,
|
||||
UserID: table.Order.UserID,
|
||||
CustomerID: table.Order.CustomerID,
|
||||
OrderNumber: table.Order.OrderNumber,
|
||||
TableNumber: table.Order.TableNumber,
|
||||
OrderType: constants.OrderType(table.Order.OrderType),
|
||||
Status: constants.OrderStatus(table.Order.Status),
|
||||
Subtotal: table.Order.Subtotal,
|
||||
TaxAmount: table.Order.TaxAmount,
|
||||
DiscountAmount: table.Order.DiscountAmount,
|
||||
TotalAmount: table.Order.TotalAmount,
|
||||
TotalCost: table.Order.TotalCost,
|
||||
PaymentStatus: constants.PaymentStatus(table.Order.PaymentStatus),
|
||||
RefundAmount: table.Order.RefundAmount,
|
||||
IsVoid: table.Order.IsVoid,
|
||||
IsRefund: table.Order.IsRefund,
|
||||
VoidReason: table.Order.VoidReason,
|
||||
VoidedAt: table.Order.VoidedAt,
|
||||
VoidedBy: table.Order.VoidedBy,
|
||||
RefundReason: table.Order.RefundReason,
|
||||
RefundedAt: table.Order.RefundedAt,
|
||||
RefundedBy: table.Order.RefundedBy,
|
||||
Metadata: table.Order.Metadata,
|
||||
CreatedAt: table.Order.CreatedAt,
|
||||
UpdatedAt: table.Order.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
@@ -224,3 +224,32 @@ func (p *UserProcessorImpl) DeactivateUser(ctx context.Context, userID uuid.UUID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) UpdateUserOutlet(ctx context.Context, userID uuid.UUID, req *models.UpdateUserOutletRequest) (*models.UserResponse, error) {
|
||||
// Get user first to validate existence and get organization_id
|
||||
existingUser, err := p.userRepo.GetByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
// Validate outlet exists
|
||||
outlet, err := p.outletRepo.GetByID(ctx, req.OutletID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("outlet not found: %w", err)
|
||||
}
|
||||
|
||||
// Validate outlet belongs to user's organization
|
||||
if outlet.OrganizationID != existingUser.OrganizationID {
|
||||
return nil, fmt.Errorf("outlet does not belong to user's organization")
|
||||
}
|
||||
|
||||
// Update user's outlet_id
|
||||
existingUser.OutletID = &req.OutletID
|
||||
|
||||
err = p.userRepo.Update(ctx, existingUser)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update user outlet: %w", err)
|
||||
}
|
||||
|
||||
return mappers.UserEntityToResponse(existingUser), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user