package usecase import ( "errors" "fmt" "log" "strconv" "time" "users_management/m/model/dto/req" "users_management/m/model/dto/res" "users_management/m/model/entity" "users_management/m/repository" "users_management/m/utils/helper" "users_management/m/utils/service" "github.com/go-playground/validator/v10" "github.com/google/uuid" ) type DeviceInspectionUseCase interface { CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error) GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error) GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error) UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) // Add this line } type deviceInspectionUseCase struct { inspectionRepo repository.DeviceInspectionRepo activityLogUC ActivityLogUseCase geoService service.GeocodingService validate *validator.Validate } func NewDeviceInspectionUseCase(inspectionRepo repository.DeviceInspectionRepo, activityLogUC ActivityLogUseCase, geoService service.GeocodingService) DeviceInspectionUseCase { return &deviceInspectionUseCase{ inspectionRepo: inspectionRepo, activityLogUC: activityLogUC, geoService: geoService, validate: validator.New(), } } func (u *deviceInspectionUseCase) CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) { return u.inspectionRepo.CheckOwnership(inspectionID, userID) } func (u *deviceInspectionUseCase) CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error { err := u.validate.Struct(inspection) if err != nil { return fmt.Errorf("validation error: %w", err) } // Check if device exists deviceExists, err := u.inspectionRepo.CheckDeviceExists(inspection.DeviceID) if err != nil { return err } if !deviceExists { return errors.New("device not found") } // Get device details device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID) if err != nil { return err } // Validate device type constraints if device.DeviceType == "OTB" && inspection.BackboneID == nil { return errors.New("backbone is required for OTB device type") } if device.DeviceType == "ODP" && inspection.FishboneID == nil { return errors.New("fishbone is required for ODP device type") } // Validate backbone exists if provided if inspection.BackboneID != nil { backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID) if err != nil { return err } if !backboneExists { return errors.New("backbone not found") } } // Validate fishbone exists if provided if inspection.FishboneID != nil { fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID) if err != nil { return err } if !fishboneExists { return errors.New("fishbone not found") } } // Validate tower exists if provided if inspection.TowerID != nil { towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID) if err != nil { return err } if !towerExists { return errors.New("tower not found") } } // Check port availability portUsedInt, err := strconv.Atoi(inspection.PortUsed) if err != nil { return errors.New("invalid port_used value") } // Get current device port usage devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID) if err != nil { // If device port doesn't exist, create it return fmt.Errorf("device port not found for device %s: %w", inspection.DeviceID, err) } // Check if adding this inspection would exceed port capacity newPortUsed := devicePort.PortUsed + portUsedInt if newPortUsed > device.PortAmount { return fmt.Errorf("insufficient ports available. Device has %d ports, currently %d used, requested %d additional", device.PortAmount, devicePort.PortUsed, portUsedInt) } // Calculate cable amount based on device type cableAmount, err := u.inspectionRepo.CountCablesByDevice(inspection.DeviceID, string(device.DeviceType)) if err != nil { return err } // Calculate port available portAvailable := device.PortAmount - newPortUsed // Get address from coordinates using OpenStreetMap longitude := inspection.InspectionPlacement.Longitude latitude := inspection.InspectionPlacement.Latitude address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude) if err != nil { log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err) address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates } // Create inspection newInspection := entity.DeviceInspection{ ID: uuid.New(), DeviceID: inspection.DeviceID, BackboneID: inspection.BackboneID, FishboneID: inspection.FishboneID, TowerID: inspection.TowerID, UserID: userID, Status: inspection.Status, PortUsed: inspection.PortUsed, PortAvailable: strconv.Itoa(portAvailable), CableAmount: cableAmount, Description: inspection.Description, ImageURL: inspection.ImageURL, InspectionPlacement: address, // Store the full address from OpenStreetMap Longitude: longitude, // Store longitude Latitude: latitude, // Store latitude InspectionApproval: "pending", CreatedAt: time.Now(), UpdatedAt: time.Now(), } err = u.inspectionRepo.Create(newInspection) if err != nil { return err } // Log activity inspectionIDStr := newInspection.ID.String() u.activityLogUC.LogActivity( userID, "CREATE", "device_inspection", &inspectionIDStr, nil, fmt.Sprintf("Created device inspection for device %s at %s", device.DeviceCode, address), "", "", "", ) return nil } func (u *deviceInspectionUseCase) UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error { err := u.validate.Struct(inspection) if err != nil { return fmt.Errorf("validation error: %w", err) } // Get existing inspection existingInspection, err := u.inspectionRepo.GetByID(id) if err != nil { return errors.New("inspection not found") } // Check if user owns this inspection (for teknisi) if userRole == "Teknisi" && existingInspection.UserID != userID { return errors.New("unauthorized: you can only update your own inspections") } updates := make(map[string]interface{}) if inspection.BackboneID != nil { backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID) if err != nil { return err } if !backboneExists { return errors.New("backbone not found") } updates["backbone_id"] = *inspection.BackboneID } if inspection.FishboneID != nil { fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID) if err != nil { return err } if !fishboneExists { return errors.New("fishbone not found") } updates["fishbone_id"] = *inspection.FishboneID } if inspection.TowerID != nil { towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID) if err != nil { return err } if !towerExists { return errors.New("tower not found") } updates["tower_id"] = *inspection.TowerID } if inspection.Status != nil { updates["status"] = *inspection.Status } if inspection.PortUsed != nil { updates["port_used"] = *inspection.PortUsed } if inspection.Description != nil { updates["description"] = *inspection.Description } if inspection.ImageURL != nil { updates["image_url"] = *inspection.ImageURL } // Handle location update if inspection.InspectionPlacement != nil { longitude := inspection.InspectionPlacement.Longitude latitude := inspection.InspectionPlacement.Latitude // Get new address from coordinates address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude) if err != nil { log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err) address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates } updates["inspection_placement"] = address updates["longitude"] = longitude updates["latitude"] = latitude } if len(updates) == 0 { return errors.New("no fields to update") } updates["updated_at"] = time.Now() return u.inspectionRepo.Update(id, updates) } // Keep the rest of the methods unchanged... func (u *deviceInspectionUseCase) GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error) { offset := (page - 1) * limit inspections, err := u.inspectionRepo.GetAll() if err != nil { return nil, 0, err } total, err := u.inspectionRepo.CountAll() if err != nil { return nil, 0, err } // Apply pagination manually since we need all data for processing start := offset end := offset + limit if start > len(inspections) { start = len(inspections) } if end > len(inspections) { end = len(inspections) } paginatedInspections := inspections[start:end] responses := helper.ConvertToDeviceInspectionResponses(paginatedInspections) return responses, total, nil } func (u *deviceInspectionUseCase) GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error) { offset := (page - 1) * limit inspections, err := u.inspectionRepo.GetByUserID(userID, limit, offset) if err != nil { return nil, 0, err } total, err := u.inspectionRepo.CountByUserID(userID) if err != nil { return nil, 0, err } responses := helper.ConvertToDeviceInspectionResponses(inspections) return responses, total, nil } func (u *deviceInspectionUseCase) GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error) { inspection, err := u.inspectionRepo.GetByID(id) if err != nil { return res.DeviceInspectionDetailResponse{}, err } response := helper.ConvertToDeviceInspectionDetailResponse(inspection) return response, nil } func (u *deviceInspectionUseCase) ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error { err := u.validate.Struct(approval) if err != nil { return fmt.Errorf("validation error: %w", err) } // Get inspection details inspection, err := u.inspectionRepo.GetByID(id) if err != nil { return errors.New("inspection not found") } updates := map[string]interface{}{ "inspection_approval": approval.InspectionApproval, "updated_at": time.Now(), } // Update inspection err = u.inspectionRepo.Update(id, updates) if err != nil { return err } // If approved, update device port and device status if approval.InspectionApproval == "approved" { // Update device status err = u.inspectionRepo.UpdateDeviceStatus(inspection.DeviceID, inspection.Status) if err != nil { return err } // Update device port usage portUsedInt, _ := strconv.Atoi(inspection.PortUsed) devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID) if err != nil { return err } device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID) if err != nil { return err } newPortUsed := devicePort.PortUsed + portUsedInt newPortAvailable := device.PortAmount - newPortUsed portUpdates := map[string]interface{}{ "port_used": newPortUsed, "port_available": newPortAvailable, "updated_at": time.Now(), } // For OTB devices, also update backbone_id if device.DeviceType == "OTB" && inspection.BackboneID != nil { portUpdates["backbone_id"] = *inspection.BackboneID } err = u.inspectionRepo.UpdateDevicePort(inspection.DeviceID, portUpdates) if err != nil { return err } } return nil }