Merge pull request 'fixing for device details and logic error on devices' (#18) from feature/responses-v2 into dev
Reviewed-on: winter-access/backend_nam#18
This commit is contained in:
commit
80b8a7da28
|
|
@ -42,4 +42,5 @@ node_modules/
|
||||||
|
|
||||||
# Coverage files
|
# Coverage files
|
||||||
coverage.out
|
coverage.out
|
||||||
uploads/towers
|
uploads/towers
|
||||||
|
uploads/*
|
||||||
|
|
@ -6,21 +6,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeviceDetails struct {
|
type DeviceDetails struct {
|
||||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||||
DeviceCode string `json:"device_code" gorm:"unique"`
|
DeviceCode string `json:"device_code" gorm:"unique"`
|
||||||
DeviceType DeviceType `json:"device_type"`
|
DeviceType DeviceType `json:"device_type"`
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
PortAmount int `json:"port_amount"`
|
PortAmount int `json:"port_amount"`
|
||||||
Status DeviceStatus `json:"status"`
|
Status DeviceStatus `json:"status"`
|
||||||
Region *string `json:"region,omitempty" gorm:"type:varchar(255)"`
|
Region *string `json:"region,omitempty" gorm:"type:varchar(255)"`
|
||||||
Province *string `json:"province,omitempty" gorm:"type:varchar(255)"`
|
Province *string `json:"province,omitempty" gorm:"type:varchar(255)"`
|
||||||
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
||||||
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
||||||
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
||||||
AdditionalImages StringSlice `gorm:"type:text[]"`
|
ImageURLs StringSlice `json:"additional_images" gorm:"type:jsonb"` // Multiple images stored as JSONB
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
// Fixed Relationships - Use direct foreign keys
|
// Fixed Relationships - Use direct foreign keys
|
||||||
DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"`
|
DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
|
|
@ -31,8 +31,47 @@ type DeviceDetails struct {
|
||||||
Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"`
|
Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (DeviceDetails) TableName() string {
|
|
||||||
return "devices" // Use same table as Device entity
|
|
||||||
|
func (d *DeviceDetails) GetAllImageURLs() []string {
|
||||||
|
var allImages []string
|
||||||
|
|
||||||
|
// Add main image URL if exists
|
||||||
|
if d.ImageURL != nil && *d.ImageURL != "" {
|
||||||
|
allImages = append(allImages, *d.ImageURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add additional images
|
||||||
|
for _, img := range d.ImageURLs {
|
||||||
|
if img != "" {
|
||||||
|
// Avoid duplicates
|
||||||
|
isDuplicate := false
|
||||||
|
for _, existingImg := range allImages {
|
||||||
|
if existingImg == img {
|
||||||
|
isDuplicate = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isDuplicate {
|
||||||
|
allImages = append(allImages, img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allImages
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DeviceDetails) SetMultipleImages(imageURLs []string) {
|
||||||
|
if len(imageURLs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set primary image
|
||||||
|
primaryImage := imageURLs[0]
|
||||||
|
d.ImageURL = &primaryImage
|
||||||
|
|
||||||
|
// Set all images
|
||||||
|
d.ImageURLs = StringSlice(imageURLs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to get all backbones connected to this device
|
// Helper method to get all backbones connected to this device
|
||||||
|
|
@ -51,3 +90,6 @@ func (d *DeviceDetails) GetAllFishbones() []Fishbone {
|
||||||
return allFishbones
|
return allFishbones
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (DeviceDetails) TableName() string {
|
||||||
|
return "devices" // Use same table as Device entity
|
||||||
|
}
|
||||||
|
|
@ -71,12 +71,10 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI
|
||||||
updates["latitude"] = *deviceDTO.Latitude
|
updates["latitude"] = *deviceDTO.Latitude
|
||||||
}
|
}
|
||||||
if deviceDTO.PortAmount != nil {
|
if deviceDTO.PortAmount != nil {
|
||||||
// Validate port amount change
|
|
||||||
if *deviceDTO.PortAmount < 0 {
|
if *deviceDTO.PortAmount < 0 {
|
||||||
return fmt.Errorf("port amount cannot be negative")
|
return fmt.Errorf("port amount cannot be negative")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not setting to 0, check current usage
|
|
||||||
if *deviceDTO.PortAmount > 0 {
|
if *deviceDTO.PortAmount > 0 {
|
||||||
currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id)
|
currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -124,27 +122,13 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI
|
||||||
|
|
||||||
if shouldReplace {
|
if shouldReplace {
|
||||||
// Replace all images - delete old ones
|
// Replace all images - delete old ones
|
||||||
if currentDevice.ImageURL != nil && *currentDevice.ImageURL != "" {
|
for _, oldImageURL := range currentDevice.GetAllImageURLs() {
|
||||||
helper.DeleteDeviceImage(*currentDevice.ImageURL)
|
helper.DeleteDeviceImage(oldImageURL)
|
||||||
}
|
|
||||||
// Delete other images if they exist
|
|
||||||
for _, imgURL := range currentDevice.AdditionalImages {
|
|
||||||
if imgURL != "" && (currentDevice.ImageURL == nil || imgURL != *currentDevice.ImageURL) {
|
|
||||||
helper.DeleteDeviceImage(imgURL)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finalImageURLs = newImageURLs
|
finalImageURLs = newImageURLs
|
||||||
} else {
|
} else {
|
||||||
// Append to existing images
|
// Append to existing images
|
||||||
existingImages := make([]string, 0)
|
existingImages := currentDevice.GetAllImageURLs()
|
||||||
if currentDevice.ImageURL != nil && *currentDevice.ImageURL != "" {
|
|
||||||
existingImages = append(existingImages, *currentDevice.ImageURL)
|
|
||||||
}
|
|
||||||
for _, imgURL := range currentDevice.AdditionalImages {
|
|
||||||
if imgURL != "" && (currentDevice.ImageURL == nil || imgURL != *currentDevice.ImageURL) {
|
|
||||||
existingImages = append(existingImages, imgURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalImageURLs = append(existingImages, newImageURLs...)
|
finalImageURLs = append(existingImages, newImageURLs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,8 +137,8 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI
|
||||||
updates["image_url"] = finalImageURLs[0]
|
updates["image_url"] = finalImageURLs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update all images
|
// Update all images using image_urls column (not additional_images)
|
||||||
updates["additional_images"] = entity.StringSlice(finalImageURLs)
|
updates["image_urls"] = entity.StringSlice(finalImageURLs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(updates) == 0 {
|
if len(updates) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate device types
|
// Validate device types
|
||||||
if startDevice.DeviceType != "closure" {
|
if startDevice.DeviceType != "CLOSURE" || startDevice.DeviceType != "closure" {
|
||||||
return fmt.Errorf("start device must be of type closure, got %s", startDevice.DeviceType)
|
return fmt.Errorf("start device must be of type closure, got %s", startDevice.DeviceType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,35 +82,37 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
fishboneInfos = append(fishboneInfos, info)
|
fishboneInfos = append(fishboneInfos, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert tower connections - safely handle nullable ExternalTower
|
// Convert tower connections
|
||||||
towerInfos := make([]res.TowerConnectionDetail, 0)
|
towerInfos := make([]res.TowerConnectionDetail, 0)
|
||||||
for _, tower := range device.Towers {
|
for _, tower := range device.Towers {
|
||||||
distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude)
|
distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude)
|
||||||
|
|
||||||
// Safely handle nullable ExternalTower field
|
|
||||||
var externalTower *bool
|
var externalTower *bool
|
||||||
if tower.ExternalTower != nil {
|
if tower.ExternalTower != nil {
|
||||||
externalTower = tower.ExternalTower
|
externalTower = tower.ExternalTower
|
||||||
} // If tower.ExternalTower is nil, externalTower remains nil
|
}
|
||||||
|
|
||||||
// Safely handle nullable ImageURL
|
// Get all tower image URLs
|
||||||
|
allTowerImageURLs := tower.GetAllImageURLs()
|
||||||
|
|
||||||
|
// Primary image URL
|
||||||
var imageURL *string
|
var imageURL *string
|
||||||
if tower.ImageURL != "" {
|
if tower.ImageURL != "" {
|
||||||
imageURL = &tower.ImageURL
|
imageURL = &tower.ImageURL
|
||||||
}
|
}
|
||||||
|
|
||||||
info := res.TowerConnectionDetail{
|
info := res.TowerConnectionDetail{
|
||||||
ID: tower.ID,
|
ID: tower.ID,
|
||||||
TowerCode: tower.TowerCode,
|
TowerCode: tower.TowerCode,
|
||||||
Distance: distance,
|
Distance: distance,
|
||||||
ExternalTower: externalTower, // This will be null if tower.ExternalTower is nil
|
ExternalTower: externalTower,
|
||||||
ImageURL: imageURL,
|
ImageURL: imageURL,
|
||||||
ImageURLs: tower.GetAllImageURLs(), // Get all images as a slice
|
ImageURLs: allTowerImageURLs,
|
||||||
}
|
}
|
||||||
towerInfos = append(towerInfos, info)
|
towerInfos = append(towerInfos, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert entity.PortAssignmentResponse to res.PortAssignmentResponse
|
// Convert port assignments
|
||||||
entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
|
entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
|
||||||
resPortAssignments := make([]res.PortAssignmentResponse, len(entityPortAssignments))
|
resPortAssignments := make([]res.PortAssignmentResponse, len(entityPortAssignments))
|
||||||
for i, pa := range entityPortAssignments {
|
for i, pa := range entityPortAssignments {
|
||||||
|
|
@ -120,6 +122,7 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
IsOccupied: pa.IsOccupied,
|
IsOccupied: pa.IsOccupied,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
portAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
|
portAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
|
||||||
|
|
||||||
// Ensure we show all ports up to PortAmount
|
// Ensure we show all ports up to PortAmount
|
||||||
|
|
@ -135,106 +138,51 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
// Fill in missing ports
|
// Fill in missing ports
|
||||||
finalPortAssignments := make([]res.PortAssignmentResponse, 0)
|
finalPortAssignments := make([]res.PortAssignmentResponse, 0)
|
||||||
for i := 1; i <= device.PortAmount; i++ {
|
for i := 1; i <= device.PortAmount; i++ {
|
||||||
if pa, exists := portAssignmentMap[i]; exists {
|
if assignment, exists := portAssignmentMap[i]; exists {
|
||||||
finalPortAssignments = append(finalPortAssignments, pa)
|
finalPortAssignments = append(finalPortAssignments, assignment)
|
||||||
} else {
|
} else {
|
||||||
// Determine if this port should be occupied
|
|
||||||
isOccupied := false
|
|
||||||
|
|
||||||
// Count occupied ports before this one
|
|
||||||
occupiedBefore := 0
|
|
||||||
for j := 1; j < i; j++ {
|
|
||||||
if existingPA, exists := portAssignmentMap[j]; exists && existingPA.IsOccupied {
|
|
||||||
occupiedBefore++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count total ports with customers
|
|
||||||
customersCount := 0
|
|
||||||
for _, pa := range portAssignments {
|
|
||||||
if pa.CustomerName != nil && *pa.CustomerName != "" {
|
|
||||||
customersCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This port should be occupied if we haven't reached port_used yet
|
|
||||||
if occupiedBefore + customersCount < device.DevicePort.PortUsed {
|
|
||||||
isOccupied = true
|
|
||||||
}
|
|
||||||
|
|
||||||
finalPortAssignments = append(finalPortAssignments, res.PortAssignmentResponse{
|
finalPortAssignments = append(finalPortAssignments, res.PortAssignmentResponse{
|
||||||
PortNumber: i,
|
PortNumber: i,
|
||||||
CustomerName: nil,
|
CustomerName: nil,
|
||||||
IsOccupied: isOccupied,
|
IsOccupied: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var customerNames []string
|
// Get customer names
|
||||||
|
customerNames := device.DevicePort.GetCustomerNamesWithPorts()
|
||||||
|
|
||||||
// If port_used is 0, return empty port assignments
|
// DIRECT FIX: Use device.GetAllImageURLs() method directly
|
||||||
if device.PortAmount == 0 {
|
allImageURLs := device.GetAllImageURLs()
|
||||||
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()
|
|
||||||
|
|
||||||
// Convert to response format
|
|
||||||
finalPortAssignments = make([]res.PortAssignmentResponse, len(portAssignments))
|
|
||||||
for i, pa := range portAssignments {
|
|
||||||
finalPortAssignments[i] = res.PortAssignmentResponse{
|
|
||||||
PortNumber: pa.PortNumber,
|
|
||||||
CustomerName: pa.CustomerName,
|
|
||||||
IsOccupied: pa.IsOccupied,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get customer names
|
|
||||||
customerNames = device.DevicePort.GetCustomerNamesOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
var deviceImageURLs []string
|
|
||||||
if device.ImageURL != nil && *device.ImageURL != "" {
|
|
||||||
deviceImageURLs = append(deviceImageURLs, *device.ImageURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use deviceImageURLs instead of trying to call a non-existent method
|
|
||||||
allImageURLs := deviceImageURLs
|
|
||||||
|
|
||||||
// Handle empty slice vs nil for JSON response
|
// Handle empty slice vs nil for JSON response
|
||||||
if len(allImageURLs) == 0 {
|
if len(allImageURLs) == 0 {
|
||||||
allImageURLs = []string{} // Return empty array instead of nil
|
allImageURLs = []string{} // Return empty array instead of nil
|
||||||
}
|
}
|
||||||
|
|
||||||
response := res.DeviceDetailsResponse{
|
response := res.DeviceDetailsResponse{
|
||||||
ID: device.ID,
|
ID: device.ID,
|
||||||
DeviceCode: device.DeviceCode,
|
DeviceCode: device.DeviceCode,
|
||||||
DeviceType: string(device.DeviceType),
|
DeviceType: string(device.DeviceType),
|
||||||
Address: address,
|
Address: address,
|
||||||
Longitude: device.Longitude,
|
Longitude: device.Longitude,
|
||||||
Latitude: device.Latitude,
|
Latitude: device.Latitude,
|
||||||
Status: string(device.Status),
|
Status: string(device.Status),
|
||||||
PortAmount: device.PortAmount,
|
PortAmount: device.PortAmount,
|
||||||
PortUsed: device.DevicePort.PortUsed,
|
PortUsed: device.DevicePort.PortUsed,
|
||||||
PortAvailable: device.DevicePort.PortAvailable,
|
PortAvailable: device.DevicePort.PortAvailable,
|
||||||
CustomerNames: customerNames, // Just customer names
|
CustomerNames: customerNames,
|
||||||
PortAssignments: finalPortAssignments, // Use finalPortAssignments instead of resPortAssignments
|
PortAssignments: finalPortAssignments,
|
||||||
Region: device.Region,
|
Province: device.Province,
|
||||||
Province: device.Province,
|
City: device.City,
|
||||||
City: device.City,
|
District: device.District,
|
||||||
District: device.District,
|
ImageURL: device.ImageURL, // Primary image
|
||||||
ImageURL: device.ImageURL,
|
ImageURLs: allImageURLs, // All images using device.GetAllImageURLs()
|
||||||
ImageURLs: allImageURLs, // Use allImageURLs to ensure empty array instead of nil
|
Backbones: backboneInfos,
|
||||||
Backbones: backboneInfos,
|
Fishbones: fishboneInfos,
|
||||||
Fishbones: fishboneInfos,
|
Towers: towerInfos,
|
||||||
Towers: towerInfos,
|
CreatedAt: device.CreatedAt,
|
||||||
CreatedAt: device.CreatedAt,
|
UpdatedAt: device.UpdatedAt,
|
||||||
UpdatedAt: device.UpdatedAt,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue