Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// InventoryManagementService.swift
// Core
//
// High-level inventory management orchestration service
//

import Foundation

/// Service for managing inventory operations at a high level
@available(iOS 17.0, *)
public final class InventoryManagementService {
private let itemRepository: any ItemRepository
private let itemApplicationService: ItemApplicationService

public init(
itemRepository: any ItemRepository,
itemApplicationService: ItemApplicationService
) {
self.itemRepository = itemRepository
self.itemApplicationService = itemApplicationService
}

// MARK: - Inventory Overview

/// Gets complete inventory overview
public func getInventoryOverview() async throws -> InventoryOverview {
let items = try await itemRepository.fetchAll()
let statistics = try await itemApplicationService.getItemStatistics()

let recentItems = items
.filter { $0.createdAt > Date().addingTimeInterval(-30 * 24 * 60 * 60) }
.sorted { $0.createdAt > $1.createdAt }
.prefix(10)

let expiredWarranties = items.filter { item in
guard let warranty = item.warrantyInfo else { return false }
return warranty.isExpired
}

return InventoryOverview(
statistics: statistics,
recentItems: Array(recentItems),
expiredWarranties: expiredWarranties,
maintenanceDue: try await itemApplicationService.getItemsDueForMaintenance()
)
}

// MARK: - Bulk Operations

/// Performs bulk update on multiple items
public func bulkUpdateItems(_ items: [InventoryItem]) async throws {
for item in items {
try await itemApplicationService.updateItem(item)
}
}

/// Archives multiple items
public func bulkArchiveItems(_ itemIds: [UUID]) async throws {
for itemId in itemIds {
try await itemApplicationService.archiveItem(itemId)
}
}

/// Exports inventory data
public func exportInventoryData() async throws -> InventoryExportData {
let items = try await itemRepository.fetchAll()
let statistics = try await itemApplicationService.getItemStatistics()

return InventoryExportData(
items: items,
statistics: statistics,
exportDate: Date()
)
}
}

// MARK: - Supporting Types

public struct InventoryOverview {
public let statistics: ItemStatistics
public let recentItems: [InventoryItem]
public let expiredWarranties: [InventoryItem]
public let maintenanceDue: [InventoryItem]

public init(
statistics: ItemStatistics,
recentItems: [InventoryItem],
expiredWarranties: [InventoryItem],
maintenanceDue: [InventoryItem]
) {
self.statistics = statistics
self.recentItems = recentItems
self.expiredWarranties = expiredWarranties
self.maintenanceDue = maintenanceDue
}
}

public struct InventoryExportData {
public let items: [InventoryItem]
public let statistics: ItemStatistics
public let exportDate: Date

public init(
items: [InventoryItem],
statistics: ItemStatistics,
exportDate: Date
) {
self.items = items
self.statistics = statistics
self.exportDate = exportDate
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
//
// ItemApplicationService.swift
// Core
//
// Application Service layer for item operations
// Orchestrates domain services and handles business workflows
//

import Foundation

/// Application service for item-related operations
/// Coordinates between domain services and repositories
@available(iOS 17.0, *)
public final class ItemApplicationService {
private let itemRepository: any ItemRepository

public init(itemRepository: any ItemRepository) {
self.itemRepository = itemRepository
}

// MARK: - Item Management

/// Creates a new inventory item with validation
public func createItem(_ item: InventoryItem) async throws {
// Validate item data
try validateItem(item)

// Check for duplicates by barcode
if let barcode = item.barcode,
let _ = try await itemRepository.fetchByBarcode(barcode) {
throw ItemError.duplicateBarcode(barcode)
}

// Save the item
try await itemRepository.save(item)
}

/// Updates an existing inventory item
public func updateItem(_ item: InventoryItem) async throws {
// Validate item data
try validateItem(item)

// Ensure item exists
guard try await itemRepository.fetch(id: item.id) != nil else {
throw ItemError.itemNotFound(item.id)
}

// Update the item
try await itemRepository.save(item)
}

/// Deletes an inventory item
public func deleteItem(_ item: InventoryItem) async throws {
try await itemRepository.delete(item)
}

/// Archives an item instead of deleting
public func archiveItem(_ itemId: UUID) async throws {
guard var item = try await itemRepository.fetch(id: itemId) else {
throw ItemError.itemNotFound(itemId)
}

item = item.archived()
try await itemRepository.save(item)
}

// MARK: - Search and Query

/// Searches items with enhanced capabilities
public func searchItems(query: String) async throws -> [InventoryItem] {
if query.isEmpty {
return try await itemRepository.fetchAll()
}

return try await itemRepository.search(query: query)
}

/// Searches items with advanced criteria
public func searchItems(criteria: ItemSearchCriteria) async throws -> [InventoryItem] {
return try await itemRepository.searchWithCriteria(criteria)
}

/// Gets items by category with sorting
public func getItemsByCategory(_ category: ItemCategory) async throws -> [InventoryItem] {
let items = try await itemRepository.fetchByCategory(category)
return items.sorted { $0.name < $1.name }
}

/// Gets items by location
public func getItemsByLocation(_ locationId: UUID) async throws -> [InventoryItem] {
return try await itemRepository.fetchByLocation(locationId)
}

// MARK: - Maintenance Operations

/// Adds a maintenance record to an item
public func addMaintenanceRecord(_ record: MaintenanceRecord, to itemId: UUID) async throws {
guard var item = try await itemRepository.fetch(id: itemId) else {
throw ItemError.itemNotFound(itemId)
}

item = item.addingMaintenanceRecord(record)
try await itemRepository.save(item)
}

/// Gets items due for maintenance
public func getItemsDueForMaintenance() async throws -> [InventoryItem] {
let allItems = try await itemRepository.fetchAll()
let thirtyDaysFromNow = Date().addingTimeInterval(30 * 24 * 60 * 60)

return allItems.filter { item in
// Check if any maintenance is due soon
// This is a simplified check - in practice would use domain services
item.maintenanceRecords.isEmpty ||
item.maintenanceRecords.last?.date.addingTimeInterval(365 * 24 * 60 * 60) ?? Date() < thirtyDaysFromNow
}
}

// MARK: - Analytics Support

/// Gets item statistics
public func getItemStatistics() async throws -> ItemStatistics {
let items = try await itemRepository.fetchAll()

let totalValue = items.compactMap { $0.purchaseInfo?.price.amount }
.reduce(Decimal(0), +)

let categoryBreakdown = Dictionary(grouping: items, by: { $0.category })
.mapValues { $0.count }

return ItemStatistics(
totalItems: items.count,
totalValue: totalValue,
categoryBreakdown: categoryBreakdown,
archivedItems: items.filter { $0.isArchived }.count,
itemsWithPhotos: items.filter { !$0.photos.isEmpty }.count
)
}

// MARK: - Migration Support

/// Migrates legacy items to new domain model
public func migrateLegacyItems() async throws {
// Migration service not implemented yet
throw ItemError.migrationNotAvailable
}

// MARK: - Private Validation

private func validateItem(_ item: InventoryItem) throws {
// Use comprehensive security validation service
let securityValidator = SecurityValidationService()

// This will throw SecurityValidationError if validation fails
// The validated item is returned but we don't need to use it here
// since the validation is primarily for security checks
_ = try securityValidator.validateInventoryItem(item)

// Additional business logic validation
if let purchaseInfo = item.purchaseInfo {
if purchaseInfo.price.amount < 0 {
throw ItemError.invalidPrice
}
}

if let warrantyInfo = item.warrantyInfo {
if warrantyInfo.startDate > Date() {
throw ItemError.invalidWarrantyDate
}
}
}
}

// MARK: - Supporting Types

public struct ItemStatistics {
public let totalItems: Int
public let totalValue: Decimal
public let categoryBreakdown: [ItemCategory: Int]
public let archivedItems: Int
public let itemsWithPhotos: Int

public init(
totalItems: Int,
totalValue: Decimal,
categoryBreakdown: [ItemCategory: Int],
archivedItems: Int,
itemsWithPhotos: Int
) {
self.totalItems = totalItems
self.totalValue = totalValue
self.categoryBreakdown = categoryBreakdown
self.archivedItems = archivedItems
self.itemsWithPhotos = itemsWithPhotos
}
}

public enum ItemError: Error {
case itemNotFound(UUID)
case duplicateBarcode(String)
case invalidName
case invalidPrice
case invalidWarrantyDate
case migrationNotAvailable
}
Loading
Loading