fix minor issue

This commit is contained in:
areeqakbr 2025-06-14 17:32:17 +07:00
parent ece05cee18
commit 8afca3054a
7 changed files with 125 additions and 61 deletions

View File

@ -16,10 +16,10 @@ type DeviceDetailsDTO struct {
type UpdateDeviceDetailsDTO struct {
DeviceCode *string `json:"device_code,omitempty" validate:"omitempty,min=3"`
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,oneof=OTB ODP"`
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,oneof=OTB ODP CLOSURE"`
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=1,max=100"`
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=0,max=100"`
Status *string `json:"status,omitempty" validate:"omitempty,oneof=active inactive maintenance"`
Region *string `json:"region,omitempty" validate:"omitempty,min=3"`
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`

View File

@ -5,7 +5,7 @@ type DeviceDTO struct {
DeviceType string `json:"device_type" validate:"required"`
Longitude float64 `json:"longitude" validate:"required"`
Latitude float64 `json:"latitude" validate:"required"`
PortAmount int `json:"port_amount" validate:"required"`
PortAmount int `json:"port_amount"`
Status string `json:"status" validate:"required,oneof=active inactive maintenance"`
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
@ -17,7 +17,7 @@ type UpdateDeviceDTO struct {
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,min=3"`
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=1"`
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty"`
Status *string `json:"status,omitempty" validate:"omitempty,oneof=active inactive maintenance"`
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
City *string `json:"city,omitempty" validate:"omitempty,min=3"`

View File

@ -141,13 +141,7 @@ func (dp *DevicePort) GetCustomerNames() []string {
}
func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
// If both port_used and port_available are 0, return empty array
if dp.PortUsed == 0 && dp.PortAvailable == 0 {
return []PortAssignmentResponse{}
}
// If port_used is 0 but port_available > 0, still return empty array
// This means the device has capacity but no ports are currently used
// If port_used is 0, return empty array
if dp.PortUsed == 0 {
return []PortAssignmentResponse{}
}
@ -166,19 +160,20 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
}
}
} else {
// Fallback: use CustomerNames array
// Fallback: use CustomerNames array if PortAssignments is empty
for i, name := range dp.CustomerNames {
portNum := i + 1
if name != "" {
portMap[i+1] = &name
}
if i+1 > maxPort {
maxPort = i + 1
portMap[portNum] = &name
if portNum > maxPort {
maxPort = portNum
}
}
}
}
// If no ports have assignments and port_used > 0, create empty assignments up to port_used
if maxPort == 0 && dp.PortUsed > 0 {
// Ensure maxPort is at least equal to port_used
if maxPort < dp.PortUsed {
maxPort = dp.PortUsed
}
@ -212,30 +207,20 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
occupiedCount++
}
}
// If we still haven't reached port_used, extend maxPort
for i := maxPort + 1; occupiedCount < dp.PortUsed; i++ {
occupiedPorts[i] = true
occupiedCount++
maxPort = i
}
}
// Step 4: Create the result only for ports that are either occupied or have customers
// Step 4: Create the result for ALL ports up to maxPort
for i := 1; i <= maxPort; i++ {
// Only include ports that are occupied or have customers
if occupiedPorts[i] || (portMap[i] != nil && *portMap[i] != "") {
var customerName *string
if portMap[i] != nil {
customerName = portMap[i]
}
result = append(result, PortAssignmentResponse{
PortNumber: i,
CustomerName: customerName,
IsOccupied: occupiedPorts[i],
})
var customerName *string
if portMap[i] != nil {
customerName = portMap[i]
}
result = append(result, PortAssignmentResponse{
PortNumber: i,
CustomerName: customerName,
IsOccupied: occupiedPorts[i],
})
}
return result

View File

@ -1,7 +1,6 @@
package repository
import (
"errors"
"fmt"
"time"
"users_management/m/model/dto/req"
@ -33,6 +32,8 @@ type DeviceDetailsRepo interface {
RemoveCustomerFromPort(deviceID uuid.UUID, customerName string) error
UpdatePortUsage(deviceID uuid.UUID, portUsed int) error
UpdatePortAssignments(deviceID uuid.UUID, assignments []req.PortAssignmentDTO) error
MigrateCustomerNamesToPortAssignments(devicePort *entity.DevicePort, devicePortAmount int) error
}
@ -222,17 +223,45 @@ func (r *deviceDetailsRepo) updatePortAmountCascade(tx *gorm.DB, deviceID uuid.U
// Check if new port amount is sufficient for current usage
if newPortAmount < devicePort.PortUsed {
return errors.New("cannot reduce port amount below current usage")
return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", newPortAmount, devicePort.PortUsed)
}
// Update port available
newPortAvailable := newPortAmount - devicePort.PortUsed
return tx.Model(&entity.DevicePort{}).
Where("device_id = ?", deviceID).
Updates(map[string]interface{}{
"port_available": newPortAvailable,
"updated_at": gorm.Expr("NOW()"),
}).Error
// Special case: if port_amount is set to 0, clear all assignments
if newPortAmount == 0 {
devicePort.PortUsed = 0
devicePort.PortAvailable = 0
devicePort.CustomerCount = 0
devicePort.CustomerNames = []string{}
devicePort.PortAssignments = entity.PortAssignments{}
devicePort.UpdatedAt = time.Now()
} else {
// Calculate new port available
newPortAvailable := newPortAmount - devicePort.PortUsed
// Update port assignments to match new port amount
if len(devicePort.PortAssignments) > 0 {
// Resize port assignments array
newPortAssignments := make(entity.PortAssignments, newPortAmount)
// Copy existing assignments up to the new port amount
for i := 0; i < newPortAmount; i++ {
if i < len(devicePort.PortAssignments) {
newPortAssignments[i] = devicePort.PortAssignments[i]
} else {
newPortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1,
CustomerName: nil,
}
}
}
devicePort.PortAssignments = newPortAssignments
}
devicePort.PortAvailable = newPortAvailable
devicePort.UpdatedAt = time.Now()
}
return tx.Save(&devicePort).Error
}
func (r *deviceDetailsRepo) UpdatePortUsage(deviceID uuid.UUID, portUsed int) error {
@ -253,13 +282,19 @@ func (r *deviceDetailsRepo) UpdatePortUsage(deviceID uuid.UUID, portUsed int) er
return fmt.Errorf("device port record not found: %w", err)
}
// Initialize port assignments if empty
if len(devicePort.PortAssignments) == 0 {
// Initialize port assignments if empty and we have customers or port_used > 0
if len(devicePort.PortAssignments) == 0 && (len(devicePort.CustomerNames) > 0 || portUsed > 0) {
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
for i := 0; i < device.PortAmount; i++ {
var customerName *string
// Map existing customer names to ports
if i < len(devicePort.CustomerNames) && devicePort.CustomerNames[i] != "" {
customerName = &devicePort.CustomerNames[i]
}
devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1,
CustomerName: nil,
CustomerName: customerName,
}
}
}
@ -296,6 +331,26 @@ func (r *deviceDetailsRepo) UpdatePortUsage(deviceID uuid.UUID, portUsed int) er
})
}
func (r *deviceDetailsRepo) MigrateCustomerNamesToPortAssignments(devicePort *entity.DevicePort, devicePortAmount int) error {
// Only migrate if PortAssignments is empty but CustomerNames has data
if len(devicePort.PortAssignments) == 0 && len(devicePort.CustomerNames) > 0 {
devicePort.PortAssignments = make(entity.PortAssignments, devicePortAmount)
for i := 0; i < devicePortAmount; i++ {
var customerName *string
if i < len(devicePort.CustomerNames) && devicePort.CustomerNames[i] != "" {
customerName = &devicePort.CustomerNames[i]
}
devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1,
CustomerName: customerName,
}
}
}
return nil
}
// func (r *deviceDetailsRepo) ValidatePortAvailability(deviceID uuid.UUID, requiredPorts int) error {
// var devicePort entity.DevicePort
// if err := r.db.Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {

View File

@ -118,8 +118,6 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.U
return fmt.Errorf("validation error: %w", err)
}
// Check if device exists
updates := map[string]interface{}{}
if deviceDTO.DeviceCode != nil {
@ -136,13 +134,21 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.U
}
if deviceDTO.PortAmount != nil {
// Validate port amount change
currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id)
if err != nil {
return err
if *deviceDTO.PortAmount < 0 {
return fmt.Errorf("port amount cannot be negative")
}
if *deviceDTO.PortAmount < currentUsed {
return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", *deviceDTO.PortAmount, currentUsed)
// If not setting to 0, check current usage
if *deviceDTO.PortAmount > 0 {
currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id)
if err != nil {
return err
}
if *deviceDTO.PortAmount < currentUsed {
return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", *deviceDTO.PortAmount, currentUsed)
}
}
updates["port_amount"] = *deviceDTO.PortAmount
}
if deviceDTO.Status != nil {

View File

@ -43,6 +43,10 @@ func (u *deviceUseCase) CreateDevice(device req.DeviceDTO) error {
return fmt.Errorf("validation error: %w", err)
}
if device.DeviceType == "OTB" || device.DeviceType == "ODP" && device.PortAmount <= 0 {
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
}
newDevice := entity.Device{
ID: uuid.New(),
DeviceCode: device.DeviceCode,
@ -92,7 +96,6 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e
if err != nil {
return fmt.Errorf("validation error: %w", err)
}
updates := map[string]interface{}{}
if device.DeviceCode != nil {
@ -122,6 +125,9 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e
if device.District != nil {
updates["District"] = *device.District
}
if device.DeviceType != nil && (*device.DeviceType == "OTB" || *device.DeviceType == "ODP") && device.PortAmount != nil && *device.PortAmount <= 0 {
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
}
if len(updates) == 0 {
return fmt.Errorf("no update data")
@ -129,6 +135,8 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e
updates["UpdatedAt"] = time.Now()
return u.deviceRepo.Update(id, updates)
}

View File

@ -168,10 +168,17 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
})
}
}
var customerNames []string
// If port_used is 0, return empty port assignments
if device.DevicePort.PortUsed == 0 {
if device.PortAmount == 0 {
finalPortAssignments = []res.PortAssignmentResponse{}
customerNames = []string{}
} else if device.DevicePort.PortUsed == 0 {
// If port_used is 0, return empty port assignments but keep port_amount structure
finalPortAssignments = []res.PortAssignmentResponse{}
customerNames = []string{}
} else {
// Get port assignments with details
portAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
@ -185,6 +192,9 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
IsOccupied: pa.IsOccupied,
}
}
// Get customer names
customerNames = device.DevicePort.GetCustomerNamesOnly()
}
response := res.DeviceDetailsResponse{
@ -198,7 +208,7 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
PortAmount: device.PortAmount,
PortUsed: device.DevicePort.PortUsed,
PortAvailable: device.DevicePort.PortAvailable,
CustomerNames: device.DevicePort.GetCustomerNamesOnly(), // Just customer names
CustomerNames: customerNames, // Just customer names
PortAssignments: finalPortAssignments, // Use finalPortAssignments instead of resPortAssignments
Region: device.Region,
Province: device.Province,