fix: port assignment bulk update
This commit is contained in:
parent
689505fa6a
commit
0c25a3a941
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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{},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue