NAM-APJATEL-BACKEND/usecase/device_inspection.go

414 lines
13 KiB
Go

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
}