fix: port assignment bulk update

This commit is contained in:
HasanMu 2025-07-16 08:58:19 +07:00
parent 689505fa6a
commit 0c25a3a941
7 changed files with 1465 additions and 1351 deletions

View File

@ -64,8 +64,6 @@ func (s *Server) setupController() {
publicRegistration.POST("/register", controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), rg).RegisterUser) publicRegistration.POST("/register", controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), rg).RegisterUser)
} }
// Add user registration controller (public endpoints) // Add user registration controller (public endpoints)
// controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), rg).Route() // controller.NewUserRegistrationController(s.ucManager.NewUserUsecase(), rg).Route()
@ -96,8 +94,6 @@ func (s *Server) setupController() {
} }
func (s *Server) Run() { func (s *Server) Run() {
s.setupController() s.setupController()
if err := s.engine.Run(s.host); err != nil { if err := s.engine.Run(s.host); err != nil {

View File

@ -22,7 +22,11 @@ type infraManager struct {
func (im *infraManager) openConn() error { func (im *infraManager) openConn() error {
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s TimeZone=Asia/Shanghai", dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s TimeZone=Asia/Shanghai",
im.cfg.DBHost, im.cfg.DBUser, im.cfg.DBPass, im.cfg.DBName, im.cfg.DBPort) im.cfg.DBHost, im.cfg.DBUser, im.cfg.DBPass, im.cfg.DBName, im.cfg.DBPort)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
// Configure GORM to disable foreign key constraint during migration
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil { if err != nil {
return err return err
} }
@ -41,13 +45,13 @@ func (im *infraManager) autoMigrate(db *gorm.DB) error {
&entity.Role{}, &entity.Role{},
&entity.User{}, &entity.User{},
&entity.Device{}, &entity.Device{},
&entity.Tower{},
&entity.OLT{},
&entity.DevicePort{},
&entity.Backbone{}, &entity.Backbone{},
&entity.Fishbone{}, &entity.Fishbone{},
&entity.Tower{},
&entity.DevicePort{},
&entity.CountAssets{}, &entity.CountAssets{},
&entity.ActivityLog{}, &entity.ActivityLog{},
&entity.OLT{},
) )
} }

View File

@ -3,6 +3,7 @@ package middleware
import ( import (
"net/http" "net/http"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -14,13 +15,13 @@ func CORSMiddleware() gin.HandlerFunc {
allowedOrigins := []string{ allowedOrigins := []string{
"http://localhost:3000", "http://localhost:3000",
"http://localhost:3001", "http://localhost:3001",
"http://localhost:5173", // Vite development server
"http://127.0.0.1:3000", "http://127.0.0.1:3000",
"http://127.0.0.1:5173", "http://127.0.0.1:5173",
"http://103.110.8.103:80",// Add production URL "http://103.110.8.103:80", // Add production URL
"http://103.110.8.103", "http://103.110.8.103",
"http://nam.winteraccess.id", "http://nam.winteraccess.id",
"https://nam.winteraccess.id",
} }
// Check if origin is in allowed list // Check if origin is in allowed list

View File

@ -39,6 +39,7 @@ type UpdateDeviceDetailsDTO struct {
type AssignMultipleCustomersDTO struct { type AssignMultipleCustomersDTO struct {
CustomerName string `json:"customer_name" binding:"required"` CustomerName string `json:"customer_name" binding:"required"`
PortNumber *int `json:"port_number,omitempty"` PortNumber *int `json:"port_number,omitempty"`
IsOccupied *bool `json:"is_occupied,omitempty"`
Status *entity.PortStatus `json:"status,omitempty"` // Add status field Status *entity.PortStatus `json:"status,omitempty"` // Add status field
Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field
} }
@ -46,6 +47,7 @@ type AssignMultipleCustomersDTO struct {
type UpdateCustomerByPortDTO struct { type UpdateCustomerByPortDTO struct {
PortNumber int `json:"port_number,omitempty"` PortNumber int `json:"port_number,omitempty"`
NewCustomerName *string `json:"new_customer_name,omitempty"` NewCustomerName *string `json:"new_customer_name,omitempty"`
IsOccupied *bool `json:"is_occupied,omitempty"`
Status *entity.PortStatus `json:"status,omitempty"` // Add status field Status *entity.PortStatus `json:"status,omitempty"` // Add status field
Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field
} }

View File

@ -16,7 +16,6 @@ type UpdateDevicePort struct {
PortNumber *int `json:"port_number,omitempty" validate:"omitempty,min=1"` PortNumber *int `json:"port_number,omitempty" validate:"omitempty,min=1"`
} }
type AssignCustomerToPortDTO struct { type AssignCustomerToPortDTO struct {
CustomerName string `json:"customer_name" binding:"required"` CustomerName string `json:"customer_name" binding:"required"`
PortNumber *int `json:"port_number,omitempty"` // Optional, will auto-assign if not provided PortNumber *int `json:"port_number,omitempty"` // Optional, will auto-assign if not provided
@ -31,6 +30,7 @@ type UpdatePortUsageDTO struct {
type PortAssignmentDTO struct { type PortAssignmentDTO struct {
PortNumber int `json:"port_number" binding:"required,min=1"` PortNumber int `json:"port_number" binding:"required,min=1"`
CustomerName *string `json:"customer_name,omitempty"` CustomerName *string `json:"customer_name,omitempty"`
IsOccupied *bool `json:"is_occupied,omitempty"`
Status *entity.PortStatus `json:"status,omitempty" validate:"omitempty,portstatus"` Status *entity.PortStatus `json:"status,omitempty" validate:"omitempty,portstatus"`
Bandwidth *string `json:"bandwidth,omitempty" validate:"omitempty,min=1"` Bandwidth *string `json:"bandwidth,omitempty" validate:"omitempty,min=1"`
} }

View File

@ -56,6 +56,7 @@ func (s *StringSlice) Scan(value interface{}) error {
type PortAssignment struct { type PortAssignment struct {
PortNumber int `json:"port_number"` PortNumber int `json:"port_number"`
CustomerName *string `json:"customer_name"` // Nullable for empty ports CustomerName *string `json:"customer_name"` // Nullable for empty ports
IsOccupied bool `json:"is_occupied"` // Explicitly store occupation status
Status PortStatus `json:"status"` // Add status field Status PortStatus `json:"status"` // Add status field
Bandwidth *string `json:"bandwidth"` // Add bandwidth field (nullable) Bandwidth *string `json:"bandwidth"` // Add bandwidth field (nullable)
} }
@ -222,23 +223,39 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
// Sort to get them in order // Sort to get them in order
sort.Ints(portsWithCustomers) sort.Ints(portsWithCustomers)
// Step 3: Determine occupied ports - USE EXPLICIT IsOccupied FIELD
// Step 3: Determine occupied ports
occupiedPorts := make(map[int]bool) occupiedPorts := make(map[int]bool)
// All ports with customers are occupied // For modern approach: use the explicitly stored IsOccupied field
for _, portNum := range portsWithCustomers { for portNum, assignment := range portMap {
occupiedPorts[portNum] = true // Use the explicitly stored IsOccupied value
// If not set, fallback to checking customer name presence
isOccupied := assignment.IsOccupied
if !isOccupied && assignment.CustomerName != nil && *assignment.CustomerName != "" {
isOccupied = true
} }
// Fill remaining slots to reach port_used occupiedPorts[portNum] = isOccupied
occupiedCount := len(portsWithCustomers) }
if occupiedCount < dp.PortUsed {
// Need to occupy more ports (ports without customer names but still occupied) // Legacy fallback: if we don't have enough explicit port assignments,
for i := 1; i <= maxPort && occupiedCount < dp.PortUsed; i++ { // use the old logic only for ports that don't have explicit assignment data
currentOccupiedCount := 0
for _, isOccupied := range occupiedPorts {
if isOccupied {
currentOccupiedCount++
}
}
// Only apply legacy logic if we have fewer occupied ports than port_used
// and only for ports that don't have explicit assignment data
if currentOccupiedCount < dp.PortUsed {
for i := 1; i <= maxPort && currentOccupiedCount < dp.PortUsed; i++ {
if _, hasExplicitData := portMap[i]; !hasExplicitData {
if !occupiedPorts[i] { if !occupiedPorts[i] {
occupiedPorts[i] = true occupiedPorts[i] = true
occupiedCount++ currentOccupiedCount++
}
} }
} }
} }
@ -253,8 +270,10 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
customerName = assignment.CustomerName customerName = assignment.CustomerName
status = assignment.Status status = assignment.Status
bandwidth = assignment.Bandwidth bandwidth = assignment.Bandwidth
} else if occupiedPorts[i] { }
// Port is occupied but no assignment data - use defaults
// Override status for legacy occupied ports (those without explicit assignment)
if occupiedPorts[i] && portMap[i] == nil {
status = PortStatusOn status = PortStatusOn
} }

View File

@ -41,8 +41,6 @@ type DeviceDetailsRepo interface {
GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error) GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error)
ValidateTowerExists(towerID uuid.UUID) (bool, error) ValidateTowerExists(towerID uuid.UUID) (bool, error)
SyncTowerLocationWithDevice(deviceID uuid.UUID) error SyncTowerLocationWithDevice(deviceID uuid.UUID) error
} }
type deviceDetailsRepo struct { type deviceDetailsRepo struct {
@ -152,6 +150,7 @@ func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.
devicePort.PortAssignments[i] = entity.PortAssignment{ devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1, PortNumber: i + 1,
CustomerName: nil, CustomerName: nil,
IsOccupied: false,
Status: entity.PortStatusOff, Status: entity.PortStatusOff,
Bandwidth: nil, Bandwidth: nil,
} }
@ -165,7 +164,16 @@ func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.
portIndex := update.PortNumber - 1 portIndex := update.PortNumber - 1
// If assigning a new customer name // Determine if port should be occupied
isOccupied := false
if update.IsOccupied != nil {
isOccupied = *update.IsOccupied
} else if update.NewCustomerName != nil {
// If not specified, determine based on customer name
isOccupied = *update.NewCustomerName != ""
}
// If assigning a new customer name and the customer name is provided
if update.NewCustomerName != nil && *update.NewCustomerName != "" { if update.NewCustomerName != nil && *update.NewCustomerName != "" {
// Check if customer name already exists on another port // Check if customer name already exists on another port
for i, assignment := range devicePort.PortAssignments { for i, assignment := range devicePort.PortAssignments {
@ -175,26 +183,35 @@ func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.
} }
} }
if update.NewCustomerName == nil { // Update customer name
// Remove customer from port if update.NewCustomerName != nil {
devicePort.PortAssignments[portIndex].CustomerName = nil
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
devicePort.PortAssignments[portIndex].Bandwidth = nil
} else {
// Assign/update customer on port
devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName
}
// Update status if provided, otherwise default to "on" // Explicitly set the IsOccupied field
devicePort.PortAssignments[portIndex].IsOccupied = isOccupied
// Update status (independent of occupation)
if update.Status != nil { if update.Status != nil {
devicePort.PortAssignments[portIndex].Status = *update.Status devicePort.PortAssignments[portIndex].Status = *update.Status
} else { } else if isOccupied {
// Only set default status if not provided and port is occupied
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn
} else {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
} }
// Update bandwidth if provided // Update bandwidth
if update.Bandwidth != nil { if update.Bandwidth != nil {
devicePort.PortAssignments[portIndex].Bandwidth = update.Bandwidth devicePort.PortAssignments[portIndex].Bandwidth = update.Bandwidth
} else if !isOccupied {
// Clear bandwidth when port is not occupied
devicePort.PortAssignments[portIndex].Bandwidth = nil
} }
// If explicitly marked as not occupied, clear customer name
if update.IsOccupied != nil && !*update.IsOccupied {
devicePort.PortAssignments[portIndex].CustomerName = nil
} }
// Update counters and backward compatibility fields // Update counters and backward compatibility fields
@ -269,57 +286,64 @@ func (r *deviceDetailsRepo) BulkUpdateCustomersByPort(deviceID uuid.UUID, update
} }
} }
// Apply all updates - FIX: Include Status and Bandwidth updates // Apply all updates - Include Status, Bandwidth and IsOccupied updates
highestOccupiedPort := 0
for _, update := range updates { for _, update := range updates {
portIndex := update.PortNumber - 1 portIndex := update.PortNumber - 1
// Update customer name // Update customer name
devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName
// Update status if provided // Determine if port should be occupied
isOccupied := false
if update.IsOccupied != nil {
isOccupied = *update.IsOccupied
} else {
// If not specified, determine based on customer name
isOccupied = update.NewCustomerName != nil && *update.NewCustomerName != ""
}
// Explicitly set the IsOccupied field
devicePort.PortAssignments[portIndex].IsOccupied = isOccupied
// Update status (independent of occupation)
if update.Status != nil { if update.Status != nil {
devicePort.PortAssignments[portIndex].Status = *update.Status devicePort.PortAssignments[portIndex].Status = *update.Status
} else { } else if isOccupied {
// Set default status based on customer assignment // Only set default status if not provided and port is occupied
if update.NewCustomerName != nil && *update.NewCustomerName != "" {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn
} else { } else {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
} }
}
// Update bandwidth if provided // Update bandwidth if provided
if update.Bandwidth != nil { if update.Bandwidth != nil {
devicePort.PortAssignments[portIndex].Bandwidth = update.Bandwidth devicePort.PortAssignments[portIndex].Bandwidth = update.Bandwidth
} else if update.NewCustomerName == nil || *update.NewCustomerName == "" { } else if !isOccupied {
// Clear bandwidth when removing customer // Clear bandwidth when port is not occupied
devicePort.PortAssignments[portIndex].Bandwidth = nil devicePort.PortAssignments[portIndex].Bandwidth = nil
} }
// Track highest occupied port
if isOccupied && update.PortNumber > highestOccupiedPort {
highestOccupiedPort = update.PortNumber
}
} }
// Recalculate port_used based on actual assignments // Recalculate port_used based on occupied ports (not just customer count)
highestUsedPort := 0 if highestOccupiedPort == 0 {
customerCount := 0 // No explicitly occupied ports from updates, check existing assignments
for _, assignment := range devicePort.PortAssignments { for _, assignment := range devicePort.PortAssignments {
if assignment.CustomerName != nil && *assignment.CustomerName != "" { if assignment.IsOccupied {
customerCount++ if assignment.PortNumber > highestOccupiedPort {
if assignment.PortNumber > highestUsedPort { highestOccupiedPort = assignment.PortNumber
highestUsedPort = assignment.PortNumber }
} }
} }
} }
// Update port_used to reflect actual usage // Update port_used to reflect highest occupied port
if customerCount == 0 { devicePort.PortUsed = highestOccupiedPort
devicePort.PortUsed = 0
} else {
// Set port_used to the highest port with a customer
// or keep current port_used if it's higher (for reserved ports)
if highestUsedPort > devicePort.PortUsed {
devicePort.PortUsed = highestUsedPort
}
}
// Recalculate port_available // Recalculate port_available
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
@ -367,7 +391,6 @@ func (r *deviceDetailsRepo) RemoveCustomerByPort(deviceID uuid.UUID, portNumber
r.updateDevicePortCounters(&devicePort) r.updateDevicePortCounters(&devicePort)
devicePort.UpdatedAt = time.Now() devicePort.UpdatedAt = time.Now()
return tx.Save(&devicePort).Error return tx.Save(&devicePort).Error
}) })
} }
@ -424,6 +447,9 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
} }
} }
// Track highest occupied port for calculating port_used
highestOccupiedPort := 0
// Update port assignments // Update port assignments
for _, assignment := range assignments { for _, assignment := range assignments {
portIndex := assignment.PortNumber - 1 portIndex := assignment.PortNumber - 1
@ -431,10 +457,23 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
devicePort.PortAssignments[portIndex].PortNumber = assignment.PortNumber devicePort.PortAssignments[portIndex].PortNumber = assignment.PortNumber
devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName
// Update status if provided, otherwise default based on customer presence // Handle is_occupied field
isOccupied := false
if assignment.IsOccupied != nil {
isOccupied = *assignment.IsOccupied
} else {
// If not specified, determine based on customer name
isOccupied = assignment.CustomerName != nil && *assignment.CustomerName != ""
}
// Explicitly set the IsOccupied field
devicePort.PortAssignments[portIndex].IsOccupied = isOccupied
// Update status (independent of occupation)
if assignment.Status != nil { if assignment.Status != nil {
devicePort.PortAssignments[portIndex].Status = *assignment.Status devicePort.PortAssignments[portIndex].Status = *assignment.Status
} else if assignment.CustomerName != nil && *assignment.CustomerName != "" { } else if isOccupied {
// Only set default status if not provided and port is occupied
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn
} else { } else {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
@ -444,29 +483,37 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
if assignment.Bandwidth != nil { if assignment.Bandwidth != nil {
devicePort.PortAssignments[portIndex].Bandwidth = assignment.Bandwidth devicePort.PortAssignments[portIndex].Bandwidth = assignment.Bandwidth
} }
// Track highest occupied port
if isOccupied && assignment.PortNumber > highestOccupiedPort {
highestOccupiedPort = assignment.PortNumber
} }
} }
}
// Calculate port usage based on highest occupied port (not just customer count)
if highestOccupiedPort == 0 {
// No explicitly occupied ports, calculate from existing assignments
for _, assignment := range devicePort.PortAssignments {
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
if assignment.PortNumber > highestOccupiedPort {
highestOccupiedPort = assignment.PortNumber
}
}
}
}
devicePort.PortUsed = highestOccupiedPort
devicePort.PortAvailable = device.PortAmount - highestOccupiedPort
// Update counters and backward compatibility fields // Update counters and backward compatibility fields
r.updateDevicePortCounters(&devicePort) r.updateDevicePortCounters(&devicePort)
// Calculate port usage based on assignments
portUsed := 0
for _, assignment := range devicePort.PortAssignments {
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
portUsed++
}
}
devicePort.PortUsed = portUsed
devicePort.PortAvailable = device.PortAmount - portUsed
devicePort.UpdatedAt = time.Now() devicePort.UpdatedAt = time.Now()
return tx.Save(&devicePort).Error return tx.Save(&devicePort).Error
}) })
} }
func (r *deviceDetailsRepo) Create(device entity.Device) error { func (r *deviceDetailsRepo) Create(device entity.Device) error {
return r.db.Transaction(func(tx *gorm.DB) error { return r.db.Transaction(func(tx *gorm.DB) error {
// Validate tower exists if TowerID is provided // Validate tower exists if TowerID is provided
@ -800,8 +847,6 @@ func (r *deviceDetailsRepo) GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.To
return towers, err return towers, err
} }
func (r *deviceDetailsRepo) GetPortUsageByDevice(deviceID uuid.UUID) (portUsed, portAvailable int, err error) { func (r *deviceDetailsRepo) GetPortUsageByDevice(deviceID uuid.UUID) (portUsed, portAvailable int, err error) {
var devicePort entity.DevicePort var devicePort entity.DevicePort
err = r.db.Where("device_id = ?", deviceID).First(&devicePort).Error err = r.db.Where("device_id = ?", deviceID).First(&devicePort).Error
@ -962,29 +1007,70 @@ func (r *deviceDetailsRepo) updateDevicePortCounters(devicePort *entity.DevicePo
device = devicePort.Device device = devicePort.Device
} }
// Count actual customers and find highest used port // Count actual customers and find highest occupied port
customerCount := 0 customerCount := 0
customerNames := make([]string, 0) customerNames := make([]string, 0)
highestCustomerPort := 0 highestOccupiedPort := 0
// Get ports with customers and check IsOccupied field
portsWithCustomers := make([]int, 0)
for _, assignment := range devicePort.PortAssignments { for _, assignment := range devicePort.PortAssignments {
// Count customers
if assignment.CustomerName != nil && *assignment.CustomerName != "" { if assignment.CustomerName != nil && *assignment.CustomerName != "" {
customerCount++ customerCount++
customerNames = append(customerNames, *assignment.CustomerName) // Just customer names, not with port numbers customerNames = append(customerNames, *assignment.CustomerName)
if assignment.PortNumber > highestCustomerPort { portsWithCustomers = append(portsWithCustomers, assignment.PortNumber)
highestCustomerPort = assignment.PortNumber }
// Check occupation (either by IsOccupied field or customer presence)
if assignment.IsOccupied || (assignment.CustomerName != nil && *assignment.CustomerName != "") {
if assignment.PortNumber > highestOccupiedPort {
highestOccupiedPort = assignment.PortNumber
} }
} }
} }
// Set port_used to the highest port number that has a customer // For ODP devices, we need to maintain the concept of "used ports" even without customer names
// This ensures we don't have gaps in port usage // This handles the case where port_used was set manually or ports are occupied without customer names
if customerCount == 0 { if device.DeviceType == "ODP" {
devicePort.PortUsed = 0 // If current port_used is higher than what we calculated from customer names,
} else { // it means there are occupied ports without customer names
devicePort.PortUsed = highestCustomerPort if devicePort.PortUsed > highestOccupiedPort {
highestOccupiedPort = devicePort.PortUsed
} }
// Ensure we have assignments for all ports up to the highest occupied port
if len(devicePort.PortAssignments) == 0 || len(devicePort.PortAssignments) < device.PortAmount {
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
for i := 0; i < device.PortAmount; i++ {
devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1,
CustomerName: nil,
IsOccupied: false,
Status: entity.PortStatusOff,
Bandwidth: nil,
}
}
// Restore customer assignments
for _, portNum := range portsWithCustomers {
portIndex := portNum - 1
for _, assignment := range devicePort.PortAssignments {
if assignment.PortNumber == portNum && assignment.CustomerName != nil {
devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName
devicePort.PortAssignments[portIndex].IsOccupied = assignment.IsOccupied
devicePort.PortAssignments[portIndex].Status = assignment.Status
devicePort.PortAssignments[portIndex].Bandwidth = assignment.Bandwidth
break
}
}
}
}
}
// Set port_used to the highest occupied port (with or without customer)
devicePort.PortUsed = highestOccupiedPort
// Calculate port_available // Calculate port_available
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
@ -1098,24 +1184,30 @@ func (r *deviceDetailsRepo) RecalculatePortUsageAfterBulkUpdate(deviceID uuid.UU
return err return err
} }
// Find the highest port number with a customer // Find the highest port number that is occupied (using IsOccupied field)
highestUsedPort := 0 highestOccupiedPort := 0
customerCount := 0 customerCount := 0
// Check all assignments for occupation and customers
for _, assignment := range devicePort.PortAssignments { for _, assignment := range devicePort.PortAssignments {
// Count customers
if assignment.CustomerName != nil && *assignment.CustomerName != "" { if assignment.CustomerName != nil && *assignment.CustomerName != "" {
customerCount++ customerCount++
if assignment.PortNumber > highestUsedPort { }
highestUsedPort = assignment.PortNumber
// Check occupation using IsOccupied field or customer presence
if assignment.IsOccupied || (assignment.CustomerName != nil && *assignment.CustomerName != "") {
if assignment.PortNumber > highestOccupiedPort {
highestOccupiedPort = assignment.PortNumber
} }
} }
} }
// Update port usage // Update port usage - this represents the highest occupied port number
devicePort.PortUsed = highestUsedPort devicePort.PortUsed = highestOccupiedPort
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
// Update other counters // Update other counters using the updated logic
r.updateDevicePortCounters(&devicePort) r.updateDevicePortCounters(&devicePort)
devicePort.UpdatedAt = time.Now() devicePort.UpdatedAt = time.Now()