Add void and printer type
This commit is contained in:
parent
11d814ab2f
commit
44dc3fa61c
4
go.mod
4
go.mod
@ -54,7 +54,7 @@ require (
|
||||
golang.org/x/arch v0.7.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
@ -68,5 +68,5 @@ require (
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.28.0
|
||||
gorm.io/driver/postgres v1.5.0
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11
|
||||
gorm.io/gorm v1.30.0
|
||||
)
|
||||
|
||||
7
go.sum
7
go.sum
@ -442,8 +442,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -607,8 +607,9 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U=
|
||||
gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 h1:9qNbmu21nNThCNnF5i2R3kw2aL27U8ZwbzccNjOmW0g=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@ -72,6 +72,7 @@ type OrderResponse struct {
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
OrderItems []OrderItemResponse `json:"order_items,omitempty"`
|
||||
IsRefund bool `json:"is_refund"`
|
||||
}
|
||||
|
||||
type OrderItemResponse struct {
|
||||
@ -90,6 +91,7 @@ type OrderItemResponse struct {
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
PrinterType string `json:"printer_type"`
|
||||
}
|
||||
|
||||
type ListOrdersQuery struct {
|
||||
|
||||
@ -91,14 +91,13 @@ func OrderItemEntityToResponse(item *entities.OrderItem) *models.OrderItemRespon
|
||||
Status: constants.OrderItemStatus(item.Status),
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
PrinterType: item.Product.PrinterType,
|
||||
}
|
||||
|
||||
// Set product name if product is preloaded
|
||||
if item.Product.ID != uuid.Nil {
|
||||
response.ProductName = item.Product.Name
|
||||
}
|
||||
|
||||
// Set product variant name if product variant is preloaded
|
||||
if item.ProductVariant != nil {
|
||||
response.ProductVariantName = &item.ProductVariant.Name
|
||||
}
|
||||
|
||||
@ -199,6 +199,7 @@ type OrderItemResponse struct {
|
||||
Status constants.OrderItemStatus
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
PrinterType string
|
||||
}
|
||||
|
||||
type PaymentOrderItemResponse struct {
|
||||
|
||||
@ -532,7 +532,6 @@ func (p *OrderProcessorImpl) VoidOrder(ctx context.Context, req *models.VoidOrde
|
||||
}
|
||||
|
||||
if req.Type == "ALL" {
|
||||
// Update order status to cancelled and mark as voided in a single transaction
|
||||
if err := p.orderRepo.VoidOrderWithStatus(ctx, req.OrderID, entities.OrderStatusCancelled, req.Reason, voidedBy); err != nil {
|
||||
return fmt.Errorf("failed to void order: %w", err)
|
||||
}
|
||||
@ -552,62 +551,53 @@ func (p *OrderProcessorImpl) VoidOrder(ctx context.Context, req *models.VoidOrde
|
||||
return fmt.Errorf("order item not found: %w", err)
|
||||
}
|
||||
|
||||
// Verify the order item belongs to this order
|
||||
if orderItem.OrderID != req.OrderID {
|
||||
return fmt.Errorf("order item does not belong to this order")
|
||||
}
|
||||
|
||||
// Validate void quantity
|
||||
if itemVoid.Quantity > orderItem.Quantity {
|
||||
return fmt.Errorf("void quantity cannot exceed original quantity for item %d", itemVoid.OrderItemID)
|
||||
}
|
||||
|
||||
// Calculate voided amounts
|
||||
voidedAmount := float64(itemVoid.Quantity) * orderItem.UnitPrice
|
||||
voidedCost := float64(itemVoid.Quantity) * orderItem.UnitCost
|
||||
|
||||
totalVoidedAmount += voidedAmount
|
||||
totalVoidedCost += voidedCost
|
||||
|
||||
// Void the order item
|
||||
if err := p.orderItemRepo.VoidOrderItem(ctx, orderItemID, itemVoid.Quantity, req.Reason, voidedBy); err != nil {
|
||||
return fmt.Errorf("failed to void order item %d: %w", itemVoid.OrderItemID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get outlet information for tax rate
|
||||
outlet, err := p.outletRepo.GetByID(ctx, order.OutletID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("outlet not found: %w", err)
|
||||
}
|
||||
|
||||
// Update order totals
|
||||
order.Subtotal -= totalVoidedAmount
|
||||
order.TotalCost -= totalVoidedCost
|
||||
order.TaxAmount = order.Subtotal * outlet.TaxRate // Recalculate tax using outlet's tax rate
|
||||
order.TotalAmount = order.Subtotal + order.TaxAmount - order.DiscountAmount
|
||||
|
||||
// Update the order
|
||||
if err := p.orderRepo.Update(ctx, order); err != nil {
|
||||
return fmt.Errorf("failed to update order totals: %w", err)
|
||||
}
|
||||
|
||||
// Check if all items are voided, then void the entire order
|
||||
remainingItems, err := p.orderItemRepo.GetByOrderID(ctx, req.OrderID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get remaining order items: %w", err)
|
||||
}
|
||||
|
||||
allItemsVoided := true
|
||||
hasActiveItems := false
|
||||
for _, item := range remainingItems {
|
||||
if item.Quantity > 0 {
|
||||
allItemsVoided = false
|
||||
if item.Status != entities.OrderItemStatusCancelled {
|
||||
hasActiveItems = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if allItemsVoided {
|
||||
// Update order status to cancelled and mark as voided when all items are voided
|
||||
if !hasActiveItems {
|
||||
if err := p.orderRepo.VoidOrderWithStatus(ctx, req.OrderID, entities.OrderStatusCancelled, req.Reason, voidedBy); err != nil {
|
||||
return fmt.Errorf("failed to void order after all items voided: %w", err)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"apskel-pos-be/internal/entities"
|
||||
@ -103,31 +104,65 @@ func (r *OrderItemRepositoryImpl) UpdateStatus(ctx context.Context, id uuid.UUID
|
||||
func (r *OrderItemRepositoryImpl) VoidOrderItem(ctx context.Context, id uuid.UUID, voidQuantity int, reason string, voidedBy uuid.UUID) error {
|
||||
now := time.Now()
|
||||
|
||||
// Get current order item
|
||||
var orderItem entities.OrderItem
|
||||
if err := r.db.WithContext(ctx).First(&orderItem, "id = ?", id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate new voided quantity
|
||||
newVoidedQuantity := orderItem.RefundQuantity + voidQuantity // Using refund_quantity field for voided quantity
|
||||
|
||||
// Determine if fully or partially voided
|
||||
isFullyVoided := newVoidedQuantity >= orderItem.Quantity
|
||||
isPartiallyVoided := newVoidedQuantity > 0 && newVoidedQuantity < orderItem.Quantity
|
||||
|
||||
// Calculate voided amount
|
||||
if voidQuantity >= orderItem.Quantity {
|
||||
voidedAmount := float64(voidQuantity) * orderItem.UnitPrice
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"refund_quantity": newVoidedQuantity, // Reusing refund_quantity field for voided quantity
|
||||
"refund_amount": orderItem.RefundAmount + voidedAmount,
|
||||
"is_partially_refunded": isPartiallyVoided, // Reusing refunded flags for voided status
|
||||
"is_fully_refunded": isFullyVoided,
|
||||
"refund_quantity": voidQuantity,
|
||||
"refund_amount": voidedAmount,
|
||||
"is_partially_refunded": false,
|
||||
"is_fully_refunded": true,
|
||||
"refund_reason": reason,
|
||||
"refunded_at": now,
|
||||
"refunded_by": voidedBy,
|
||||
"status": entities.OrderItemStatusCancelled, // Mark as cancelled when voided
|
||||
"status": entities.OrderItemStatusCancelled,
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
|
||||
Where("id = ?", id).
|
||||
Updates(updates).Error
|
||||
}
|
||||
|
||||
voidedOrderItem := entities.OrderItem{
|
||||
OrderID: orderItem.OrderID,
|
||||
ProductID: orderItem.ProductID,
|
||||
ProductVariantID: orderItem.ProductVariantID,
|
||||
Quantity: voidQuantity,
|
||||
UnitPrice: orderItem.UnitPrice,
|
||||
TotalPrice: float64(voidQuantity) * orderItem.UnitPrice,
|
||||
UnitCost: orderItem.UnitCost,
|
||||
TotalCost: float64(voidQuantity) * orderItem.UnitCost,
|
||||
RefundAmount: float64(voidQuantity) * orderItem.UnitPrice,
|
||||
RefundQuantity: voidQuantity,
|
||||
IsPartiallyRefunded: false,
|
||||
IsFullyRefunded: true,
|
||||
RefundReason: &reason,
|
||||
RefundedAt: &now,
|
||||
RefundedBy: &voidedBy,
|
||||
Modifiers: orderItem.Modifiers,
|
||||
Notes: orderItem.Notes,
|
||||
Metadata: orderItem.Metadata,
|
||||
Status: entities.OrderItemStatusCancelled,
|
||||
}
|
||||
|
||||
if err := r.db.WithContext(ctx).Create(&voidedOrderItem).Error; err != nil {
|
||||
return fmt.Errorf("failed to create voided order item: %w", err)
|
||||
}
|
||||
|
||||
remainingQuantity := orderItem.Quantity - voidQuantity
|
||||
remainingTotalPrice := float64(remainingQuantity) * orderItem.UnitPrice
|
||||
remainingTotalCost := float64(remainingQuantity) * orderItem.UnitCost
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"quantity": remainingQuantity,
|
||||
"total_price": remainingTotalPrice,
|
||||
"total_cost": remainingTotalCost,
|
||||
"updated_at": now,
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).Model(&entities.OrderItem{}).
|
||||
|
||||
@ -108,6 +108,7 @@ func OrderModelToContract(resp *models.OrderResponse) *contract.OrderResponse {
|
||||
Status: string(item.Status),
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
PrinterType: item.PrinterType,
|
||||
}
|
||||
}
|
||||
return &contract.OrderResponse{
|
||||
@ -127,6 +128,7 @@ func OrderModelToContract(resp *models.OrderResponse) *contract.OrderResponse {
|
||||
CreatedAt: resp.CreatedAt,
|
||||
UpdatedAt: resp.UpdatedAt,
|
||||
OrderItems: items,
|
||||
IsRefund: resp.IsRefund,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user