# Flutter Integration: Not Found Items in Transfer Requirements

## Overview

The backend now includes "not found" items in transfer requirements with special handling. These items have `is_not_found: true` and should not have quantity clamping applied, allowing full transfer flexibility.

## API Response Changes

### Enhanced Transfer Requirements Response

```json
{
  "status": true,
  "my_receive_order": { /* existing */ },
  "message": "Box processing has been queued successfully.",
  "transfer_requirements": {
    "ABC123": {
      "barcode": "ABC123",
      "description": "Regular Item",
      "counted_qty": 10,
      "is_not_found": false,
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A",
          "qty_required": 15,
          "qty_fulfilled": 5,
          "qty_remaining": 10,
          "max_allowed": 10,
          "default_allocation_qty": 10,
          "is_partial_allocation": false
        }
      ]
    },
    "NOTFOUND456": {
      "barcode": "NOTFOUND456",
      "description": "Not Found Item",
      "counted_qty": 3,
      "is_not_found": true,
      "destinations": [
        {
          "warehouse_id": 2,
          "warehouse_name": "Shop A",
          "qty_required": 8,
          "qty_fulfilled": 0,
          "qty_remaining": 8,
          "max_allowed": 8,  // Full qty_remaining - NO CLAMPING
          "default_allocation_qty": 8,
          "is_partial_allocation": false
        }
      ]
    }
  }
}
```

## Model Updates

### 1. Update Transfer Requirement Models

```dart
class TransferRequirement {
  final String barcode;
  final String description;
  final int? countedQty;
  final bool isNotFound;  // NEW FIELD
  final List<TransferDestination> destinations;

  TransferRequirement({
    required this.barcode,
    required this.description,
    this.countedQty,
    required this.isNotFound,  // Add this field
    required this.destinations,
  });

  factory TransferRequirement.fromJson(Map<String, dynamic> json) {
    return TransferRequirement(
      barcode: json['barcode'],
      description: json['description'],
      countedQty: json['counted_qty'],
      isNotFound: json['is_not_found'] ?? false,  // Default to false for backward compatibility
      destinations: (json['destinations'] as List)
          .map((d) => TransferDestination.fromJson(d))
          .toList(),
    );
  }
}

class TransferDestination {
  final int warehouseId;
  final String warehouseName;
  final int qtyRequired;
  final int qtyFulfilled;
  final int qtyRemaining;
  final int maxAllowed;
  final int defaultAllocationQty;
  final bool isPartialAllocation;

  TransferDestination({
    required this.warehouseId,
    required this.warehouseName,
    required this.qtyRequired,
    required this.qtyFulfilled,
    required this.qtyRemaining,
    required this.maxAllowed,
    required this.defaultAllocationQty,
    required this.isPartialAllocation,
  });

  factory TransferDestination.fromJson(Map<String, dynamic> json) {
    return TransferDestination(
      warehouseId: json['warehouse_id'],
      warehouseName: json['warehouse_name'],
      qtyRequired: json['qty_required'],
      qtyFulfilled: json['qty_fulfilled'],
      qtyRemaining: json['qty_remaining'],
      maxAllowed: json['max_allowed'],
      defaultAllocationQty: json['default_allocation_qty'],
      isPartialAllocation: json['is_partial_allocation'],
    );
  }
}
```

### 2. Update Transfer Allocation Models

```dart
class TransferAllocation {
  final String barcode;
  final String description;
  final String? codein;
  final String? style;
  final String? colour;
  final String? size;
  final bool isNotFound;  // NEW FIELD
  final List<WarehouseAllocation> allocations;

  TransferAllocation({
    required this.barcode,
    required this.description,
    this.codein,
    this.style,
    this.colour,
    this.size,
    required this.isNotFound,  // Add this field
    required this.allocations,
  });

  Map<String, dynamic> toJson() {
    return {
      'barcode': barcode,
      'description': description,
      'codein': codein,
      'style': style,
      'colour': colour,
      'size': size,
      'is_not_found': isNotFound,  // Include in API payload
      'allocations': allocations.map((a) => a.toJson()).toList(),
    };
  }
}
```

## UI Implementation

### 1. Transfer Allocation Screen Updates

```dart
class TransferAllocationScreen extends StatefulWidget {
  final Map<String, TransferRequirement> transferRequirements;

  const TransferAllocationScreen({
    Key? key,
    required this.transferRequirements,
  }) : super(key: key);

  @override
  _TransferAllocationScreenState createState() => _TransferAllocationScreenState();
}

class _TransferAllocationScreenState extends State<TransferAllocationScreen> {
  late Map<String, Map<int, int>> _allocations; // barcode -> warehouseId -> qty

  @override
  void initState() {
    super.initState();
    _initializeAllocations();
  }

  void _initializeAllocations() {
    _allocations = {};

    widget.transferRequirements.forEach((barcode, requirement) {
      _allocations[barcode] = {};

      for (final destination in requirement.destinations) {
        // For not_found items, use full default allocation
        // For regular items, use existing logic
        final defaultQty = requirement.isNotFound
            ? destination.defaultAllocationQty
            : min(destination.defaultAllocationQty, destination.maxAllowed);

        _allocations[barcode]![destination.warehouseId] = defaultQty;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Transfer Allocations'),
      ),
      body: ListView(
        children: widget.transferRequirements.entries.map((entry) {
          final barcode = entry.key;
          final requirement = entry.value;

          return _buildBarcodeCard(barcode, requirement);
        }).toList(),
      ),
      bottomNavigationBar: _buildSubmitButton(),
    );
  }

  Widget _buildBarcodeCard(String barcode, TransferRequirement requirement) {
    return Card(
      margin: EdgeInsets.all(8),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Expanded(
                  child: Text(
                    requirement.description,
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                if (requirement.isNotFound)
                  Container(
                    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                    decoration: BoxDecoration(
                      color: Colors.orange.shade100,
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Text(
                      'NOT FOUND',
                      style: TextStyle(
                        color: Colors.orange.shade800,
                        fontSize: 12,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
              ],
            ),
            SizedBox(height: 8),
            Text(
              'Barcode: $barcode',
              style: TextStyle(color: Colors.grey.shade600),
            ),
            if (requirement.countedQty != null)
              Text(
                'Counted: ${requirement.countedQty}',
                style: TextStyle(color: Colors.grey.shade600),
              ),
            SizedBox(height: 16),
            ...requirement.destinations.map((destination) {
              return _buildDestinationInput(barcode, requirement, destination);
            }),
          ],
        ),
      ),
    );
  }

  Widget _buildDestinationInput(
    String barcode,
    TransferRequirement requirement,
    TransferDestination destination,
  ) {
    final currentValue = _allocations[barcode]?[destination.warehouseId] ?? 0;

    return Padding(
      padding: EdgeInsets.only(bottom: 8),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(destination.warehouseName),
                Text(
                  'Required: ${destination.qtyRequired} | Remaining: ${destination.qtyRemaining}',
                  style: TextStyle(fontSize: 12, color: Colors.grey),
                ),
                if (requirement.isNotFound)
                  Text(
                    'Not Found Item - Full transfer allowed',
                    style: TextStyle(
                      fontSize: 12,
                      color: Colors.orange.shade700,
                      fontStyle: FontStyle.italic,
                    ),
                  ),
              ],
            ),
          ),
          SizedBox(
            width: 80,
            child: TextFormField(
              initialValue: currentValue.toString(),
              keyboardType: TextInputType.number,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              ),
              onChanged: (value) {
                final qty = int.tryParse(value) ?? 0;
                setState(() {
                  _allocations[barcode] ??= {};
                  _allocations[barcode]![destination.warehouseId] = qty;
                });
              },
              validator: (value) {
                final qty = int.tryParse(value ?? '0') ?? 0;

                // For not_found items, allow up to qty_remaining
                // For regular items, enforce max_allowed
                final maxAllowed = requirement.isNotFound
                    ? destination.qtyRemaining
                    : destination.maxAllowed;

                if (qty < 0) {
                  return 'Cannot be negative';
                }
                if (qty > maxAllowed) {
                  return 'Max: $maxAllowed';
                }
                return null;
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildSubmitButton() {
    return Padding(
      padding: EdgeInsets.all(16),
      child: ElevatedButton(
        onPressed: _submitAllocations,
        child: Text('Confirm Transfers'),
        style: ElevatedButton.styleFrom(
          minimumSize: Size(double.infinity, 48),
        ),
      ),
    );
  }

  void _submitAllocations() {
    // Validate allocations
    if (!_validateAllocations()) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Please fix allocation errors')),
      );
      return;
    }

    // Convert to API format
    final transferAllocations = _buildTransferAllocations();

    // Submit to API
    // TODO: Implement API call
  }

  bool _validateAllocations() {
    // Validate that allocations don't exceed limits
    for (final entry in widget.transferRequirements.entries) {
      final barcode = entry.key;
      final requirement = entry.value;

      final totalAllocated = _allocations[barcode]?.values.fold(0, (sum, qty) => sum + qty) ?? 0;

      if (requirement.countedQty != null && totalAllocated > requirement.countedQty!) {
        return false; // Regular items cannot exceed counted quantity
      }

      // Not found items can exceed their counted quantity if needed
    }

    return true;
  }

  List<Map<String, dynamic>> _buildTransferAllocations() {
    final allocations = <Map<String, dynamic>>[];

    for (final entry in widget.transferRequirements.entries) {
      final barcode = entry.key;
      final requirement = entry.value;

      final warehouseAllocations = <Map<String, dynamic>>[];

      for (final destination in requirement.destinations) {
        final qty = _allocations[barcode]?[destination.warehouseId] ?? 0;

        if (qty > 0) {
          warehouseAllocations.add({
            'destination_warehouse_id': destination.warehouseId,
            'qty': qty,
            'destination_box_name': 'BOX-${destination.warehouseId}-001', // TODO: Generate proper box names
          });
        }
      }

      if (warehouseAllocations.isNotEmpty) {
        allocations.add({
          'barcode': barcode,
          'description': requirement.description,
          'codein': null, // TODO: Add from item data
          'style': null,
          'colour': null,
          'size': null,
          'is_not_found': requirement.isNotFound,
          'allocations': warehouseAllocations,
        });
      }
    }

    return allocations;
  }
}
```

## Cubit/State Management Updates

### 1. Update Transfer State

```dart
class TransferState {
  final Map<String, TransferRequirement> transferRequirements;
  final Map<String, Map<int, int>> allocations;
  final bool isLoading;
  final String? error;

  TransferState({
    required this.transferRequirements,
    required this.allocations,
    this.isLoading = false,
    this.error,
  });

  TransferState copyWith({
    Map<String, TransferRequirement>? transferRequirements,
    Map<String, Map<int, int>>? allocations,
    bool? isLoading,
    String? error,
  }) {
    return TransferState(
      transferRequirements: transferRequirements ?? this.transferRequirements,
      allocations: allocations ?? this.allocations,
      isLoading: isLoading ?? this.isLoading,
      error: error ?? this.error,
    );
  }
}
```

### 2. Update Cubit Methods

```dart
class TransferCubit extends Cubit<TransferState> {
  final TransferRepository _repository;

  TransferCubit(this._repository) : super(TransferState(
    transferRequirements: {},
    allocations: {},
  ));

  void loadTransferRequirements(int receiveOrderId, List<Map<String, dynamic>> submittedLines, List<Map<String, dynamic>> notFoundItems) {
    emit(state.copyWith(isLoading: true, error: null));

    try {
      // API call to get transfer requirements
      final response = await _repository.getTransferRequirements(
        receiveOrderId,
        submittedLines,
        notFoundItems,
      );

      final requirements = _parseTransferRequirements(response['transfer_requirements']);
      final allocations = _initializeAllocations(requirements);

      emit(state.copyWith(
        transferRequirements: requirements,
        allocations: allocations,
        isLoading: false,
      ));
    } catch (e) {
      emit(state.copyWith(
        isLoading: false,
        error: e.toString(),
      ));
    }
  }

  Map<String, TransferRequirement> _parseTransferRequirements(Map<String, dynamic> json) {
    final requirements = <String, TransferRequirement>{};

    json.forEach((barcode, data) {
      requirements[barcode] = TransferRequirement.fromJson(data);
    });

    return requirements;
  }

  Map<String, Map<int, int>> _initializeAllocations(Map<String, TransferRequirement> requirements) {
    final allocations = <String, Map<int, int>>{};

    requirements.forEach((barcode, requirement) {
      allocations[barcode] = {};

      for (final destination in requirement.destinations) {
        // For not_found items, use full default allocation
        // For regular items, respect max_allowed
        final defaultQty = requirement.isNotFound
            ? destination.defaultAllocationQty
            : min(destination.defaultAllocationQty, destination.maxAllowed);

        allocations[barcode]![destination.warehouseId] = defaultQty;
      }
    });

    return allocations;
  }

  void updateAllocation(String barcode, int warehouseId, int qty) {
    final newAllocations = Map<String, Map<int, int>>.from(state.allocations);
    newAllocations[barcode] ??= {};
    newAllocations[barcode]![warehouseId] = qty;

    emit(state.copyWith(allocations: newAllocations));
  }

  Future<void> submitAllocations(int receiveOrderId, String sourceBoxName) async {
    emit(state.copyWith(isLoading: true, error: null));

    try {
      final transferAllocations = _buildTransferAllocations();

      await _repository.confirmTransfers(
        receiveOrderId: receiveOrderId,
        sourceBoxName: sourceBoxName,
        transferAllocations: transferAllocations,
      );

      emit(state.copyWith(isLoading: false));
    } catch (e) {
      emit(state.copyWith(
        isLoading: false,
        error: e.toString(),
      ));
    }
  }

  List<Map<String, dynamic>> _buildTransferAllocations() {
    final allocations = <Map<String, dynamic>>[];

    state.transferRequirements.forEach((barcode, requirement) {
      final warehouseAllocations = <Map<String, dynamic>>[];

      state.allocations[barcode]?.forEach((warehouseId, qty) {
        if (qty > 0) {
          warehouseAllocations.add({
            'destination_warehouse_id': warehouseId,
            'qty': qty,
            'destination_box_name': 'BOX-$warehouseId-001', // TODO: Proper box naming
          });
        }
      });

      if (warehouseAllocations.isNotEmpty) {
        allocations.add({
          'barcode': barcode,
          'description': requirement.description,
          'codein': null, // TODO: Add from item data
          'style': null,
          'colour': null,
          'size': null,
          'is_not_found': requirement.isNotFound,
          'allocations': warehouseAllocations,
        });
      }
    });

    return allocations;
  }

  bool validateAllocations() {
    for (final entry in state.transferRequirements.entries) {
      final barcode = entry.key;
      final requirement = entry.value;

      final totalAllocated = state.allocations[barcode]?.values.fold(0, (sum, qty) => sum + qty) ?? 0;

      // Regular items cannot exceed counted quantity
      if (!requirement.isNotFound && requirement.countedQty != null && totalAllocated > requirement.countedQty!) {
        return false;
      }

      // Not found items can exceed their counted quantity if needed for transfers
    }

    return true;
  }
}
```

## Key Behavioral Changes

### 1. **Not Found Item Identification**
- Items with `is_not_found: true` should be visually distinguished
- Use orange/warning colors or badges to indicate "NOT FOUND" status

### 2. **Quantity Validation**
- **Regular Items**: `total_allocated ≤ counted_qty`
- **Not Found Items**: No upper limit on allocation (can exceed `counted_qty`)

### 3. **Input Controls**
- **Regular Items**: `max_allowed` enforces clamping
- **Not Found Items**: Allow input up to `qty_remaining` (full requirement)

### 4. **Default Allocations**
- **Regular Items**: Respect `max_allowed` when setting defaults
- **Not Found Items**: Use full `default_allocation_qty`

## API Integration

### 1. Update Box Submission Call

```dart
// When submitting a box with not_found items
final response = await api.updateReceiveOrderBoxAsync(
  receiveOrderId: receiveOrderId,
  boxName: boxName,
  lines: countedLines,
  notFound: notFoundItems, // Include not_found items
);

// Check for transfer requirements
if (response.containsKey('transfer_requirements')) {
  // Navigate to transfer allocation screen
  navigator.push(TransferAllocationScreen(
    transferRequirements: response['transfer_requirements'],
  ));
}
```

### 2. Transfer Confirmation API Call

```dart
final transferAllocations = cubit.buildTransferAllocations();

final response = await api.confirmTransfers(
  receiveOrderId: receiveOrderId,
  sourceBoxName: sourceBoxName,
  transferAllocations: transferAllocations, // Includes is_not_found flag
);
```

## Testing Checklist

- [ ] Regular items respect quantity clamping
- [ ] Not found items allow full transfer quantities
- [ ] UI properly distinguishes not found items
- [ ] Validation works for both item types
- [ ] API calls include `is_not_found` flag
- [ ] Backward compatibility with existing responses

## Migration Notes

- The `is_not_found` field is optional in API responses for backward compatibility
- Default value should be `false` when not present
- Existing apps will continue to work without changes
- New functionality only activates when `is_not_found: true`
