# Quote Weight & Volume Calculation Update

## Tổng quan
Cập nhật logic tính toán cân nặng và thể tích của khai báo để tự động tính từ tổng cân nặng và thể tích của các sản phẩm có trong danh sách khai báo.

## Thay đổi chính

### 1. Frontend (Quote detail.vue)

#### Cập nhật phương thức `calculateAmounts()`
- **Trước**: Sử dụng giá trị cân nặng và thể tích nhập tay từ form
- **Sau**: Tự động tính tổng cân nặng và thể tích từ `quoteProducts`

```javascript
// Tính tổng cân nặng và thể tích từ danh sách sản phẩm
let totalWeight = 0;
let totalVolume = 0;
if (this.quoteProducts && this.quoteProducts.length > 0) {
  totalWeight = this.quoteProducts.reduce((sum, product) => sum + parseFloat(product.Weight || 0), 0);
  totalVolume = this.quoteProducts.reduce((sum, product) => sum + parseFloat(product.Volume || 0), 0);
}

// Cập nhật cân nặng và thể tích tổng
this.currentItem.Weight = totalWeight;
this.currentItem.Volume = totalVolume;
```

#### Thêm watcher cho `quoteProducts`
```javascript
quoteProducts: {
  handler() {
    // Tự động tính lại khi danh sách sản phẩm thay đổi
    this.calculateAmounts();
  },
  deep: true
}
```

#### Cập nhật các phương thức thêm/xóa sản phẩm
- `updateProductsQuote()`: Gọi `calculateAmounts()` sau khi reload danh sách
- `removeProduct()`: Gọi `calculateAmounts()` sau khi reload danh sách

### 2. Backend (Quote model)

#### Cập nhật phương thức `calculate_quote_amounts()`
```python
# Tính tổng cân nặng và thể tích từ các sản phẩm trong khai báo
quote_id = request.data.get('ID')
total_weight, total_volume = self.get_total_weight_volume_from_products(quote_id)

# Nếu có dữ liệu từ sản phẩm thì sử dụng, không thì dùng giá trị nhập tay
weight = total_weight if total_weight > 0 else float(request.data.get('Weight', 0))
volume = total_volume if total_volume > 0 else float(request.data.get('Volume', 0))
```

#### Thêm phương thức `get_total_weight_volume_from_products()`
```python
def get_total_weight_volume_from_products(self, quote_id):
    """Lấy tổng cân nặng và thể tích từ các sản phẩm trong khai báo"""
    query_sql = """
        SELECT SUM(COALESCE(Weight, 0)) as TotalWeight, 
               SUM(COALESCE(Volume, 0)) as TotalVolume
        FROM tbl_product 
        WHERE QuoteID = :quote_id
    """
    # Trả về (total_weight, total_volume)
```

## Logic hoạt động

### Luồng tính toán mới
1. **Khi load khai báo**: Danh sách sản phẩm được load → watcher kích hoạt → tính toán tự động
2. **Khi thêm sản phẩm**: Thêm vào database → reload danh sách → tính toán tự động
3. **Khi xóa sản phẩm**: Xóa khỏi database → reload danh sách → tính toán tự động
4. **Khi thay đổi các field khác**: Manual trigger calculateAmounts() → tính toán lại

### Ưu tiên dữ liệu
- **Nếu có sản phẩm trong danh sách**: Sử dụng tổng cân nặng/thể tích từ sản phẩm
- **Nếu không có sản phẩm**: Sử dụng giá trị nhập tay từ form

## Tác động đến UI

### Form input Weight & Volume
- Các field cân nặng và thể tích vẫn hiển thị
- Giá trị sẽ được cập nhật tự động khi có sản phẩm
- Người dùng vẫn có thể nhập tay khi chưa có sản phẩm

### Tính toán phí vận chuyển
- Phí vận chuyển dự kiến được tính dựa trên tổng cân nặng thực tế
- Đảm bảo tính chính xác hơn trong khai báo

## Testing

### Test cases cần kiểm tra
1. **Báo giá không có sản phẩm**: Sử dụng giá trị nhập tay
2. **Báo giá có 1 sản phẩm**: Cân nặng/thể tích = giá trị của sản phẩm đó
3. **Báo giá có nhiều sản phẩm**: Cân nặng/thể tích = tổng của tất cả sản phẩm
4. **Thêm sản phẩm**: Cân nặng/thể tích tăng theo
5. **Xóa sản phẩm**: Cân nặng/thể tích giảm theo
6. **Sản phẩm có Weight/Volume = 0 hoặc null**: Xử lý đúng

### Manual testing
```javascript
// Test data
const products = [
  { Weight: 1.5, Volume: 0.01 },
  { Weight: 2.0, Volume: 0.015 },
  { Weight: 0.8, Volume: 0.005 }
];
// Expected: Weight = 4.3, Volume = 0.03
```

## Backward Compatibility

### Dữ liệu cũ
- Báo giá hiện tại không bị ảnh hưởng
- Giá trị cân nặng/thể tích cũ được giữ nguyên nếu không có sản phẩm

### API compatibility
- Không thay đổi cấu trúc response
- Thêm logic tính toán trong nội bộ

## Performance Considerations

### Frontend
- Watcher `deep: true` có thể ảnh hưởng performance nếu danh sách sản phẩm lớn
- Debounce có thể cần thiết nếu cần tối ưu

### Backend
- Query tổng cân nặng/thể tích được thực hiện mỗi lần save
- Có thể cache kết quả nếu cần tối ưu

## Files Modified

### Frontend
- `/src/views/Quote/detail.vue`
  - Cập nhật `calculateAmounts()` method
  - Thêm watcher cho `quoteProducts`
  - Cập nhật `updateProductsQuote()` và `removeProduct()`

### Backend
- `/DepositAPI/model/Quote.py`
  - Cập nhật `calculate_quote_amounts()` method
  - Thêm `get_total_weight_volume_from_products()` method

## Future Enhancements

### Possible improvements
1. **Real-time sync**: Sync ngay khi thay đổi weight/volume của sản phẩm
2. **Validation**: Cảnh báo khi tổng cân nặng/thể tích quá lớn
3. **History tracking**: Lưu lịch sử thay đổi cân nặng/thể tích
4. **Manual override**: Cho phép override giá trị tự động trong trường hợp đặc biệt

## Summary
Cập nhật này giúp đảm bảo tính chính xác của khai báo bằng cách tự động tính toán cân nặng và thể tích từ các sản phẩm thực tế, đồng thời vẫn duy trì tính linh hoạt cho trường hợp chưa có sản phẩm cụ thể.
