Skip to content

Commit de5d6ec

Browse files
committed
fix: Revise the base unit conversion section
1 parent 7fa4c40 commit de5d6ec

File tree

5 files changed

+66
-11
lines changed

5 files changed

+66
-11
lines changed

backend/src/main/java/com/smalltrend/dto/products/ProductVariantRespone.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class ProductVariantRespone {
2323
private String imageUrl;
2424
private Map<String, String> attributes;
2525
private Boolean isActive;
26+
private Boolean isBaseUnit;
2627
private Boolean productActive;
2728
private BigDecimal taxRate;
2829
private String taxName;

backend/src/main/java/com/smalltrend/service/products/ProductVariantService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,22 @@ private ProductVariantRespone mapToResponse(ProductVariant variant) {
458458
response.setImageUrl(variant.getImageUrl());
459459
response.setSellPrice(variant.getSellPrice());
460460
response.setIsActive(variant.isActive());
461+
462+
boolean isConversionDerivedVariant = false;
463+
if (product != null && product.getId() != null && variant.getUnit() != null && variant.getUnit().getId() != null) {
464+
List<UnitConversion> conversionsToThisUnit = unitConversionRepository.findByProductIdAndToUnitId(
465+
product.getId(),
466+
variant.getUnit().getId());
467+
468+
isConversionDerivedVariant = conversionsToThisUnit.stream()
469+
.filter(conversion -> conversion != null
470+
&& conversion.getVariant() != null
471+
&& conversion.getVariant().getId() != null
472+
&& !conversion.getVariant().getId().equals(variant.getId()))
473+
.anyMatch(conversion -> hasSameAttributes(conversion.getVariant(), variant));
474+
}
475+
476+
response.setIsBaseUnit(!isConversionDerivedVariant);
461477
response.setProductActive(product != null ? product.getIsActive() : null);
462478
response.setAttributes(variant.getAttributes());
463479
response.setCreatedAt(variant.getCreatedAt());

backend/src/main/java/com/smalltrend/service/products/UnitConversionService.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020

2121
import java.time.LocalDate;
2222
import java.math.RoundingMode;
23+
import java.util.Collections;
2324
import java.util.List;
25+
import java.util.Map;
2426
import java.util.stream.Collectors;
2527

2628
@Service
@@ -58,6 +60,24 @@ public UnitConversionResponse addConversion(Integer variantId, UnitConversionReq
5860
.orElseThrow(() -> new RuntimeException(
5961
"Không tìm thấy biến thể với ID: " + variantId));
6062

63+
Integer productId = baseVariant.getProduct() != null ? baseVariant.getProduct().getId() : null;
64+
Integer unitId = baseVariant.getUnit() != null ? baseVariant.getUnit().getId() : null;
65+
66+
if (productId != null && unitId != null) {
67+
List<UnitConversion> conversionsToThisUnit = unitConversionRepository.findByProductIdAndToUnitId(productId, unitId);
68+
boolean isConversionDerivedVariant = conversionsToThisUnit.stream()
69+
.filter(conversion -> conversion != null
70+
&& conversion.getVariant() != null
71+
&& conversion.getVariant().getId() != null
72+
&& !conversion.getVariant().getId().equals(baseVariant.getId()))
73+
.anyMatch(conversion -> hasSameAttributes(conversion.getVariant(), baseVariant));
74+
75+
if (isConversionDerivedVariant) {
76+
throw new RuntimeException("Chỉ biến thể đơn vị gốc mới được phép thêm quy đổi đơn vị.");
77+
}
78+
}
79+
80+
6181
Unit toUnit = unitRepository.findById(request.getToUnitId())
6282
.orElseThrow(() -> new RuntimeException(
6383
"Không tìm thấy đơn vị với ID: " + request.getToUnitId()));
@@ -94,7 +114,6 @@ public UnitConversionResponse addConversion(Integer variantId, UnitConversionReq
94114
.unit(toUnit)
95115
.sellPrice(request.getSellPrice())
96116
.isActive(baseVariant.isActive())
97-
.imageUrl(baseVariant.getImageUrl()) // Kế thừa ảnh từ variant gốc
98117
.attributes(baseVariant.getAttributes() != null
99118
? new java.util.HashMap<>(baseVariant.getAttributes())
100119
: new java.util.HashMap<>())
@@ -212,4 +231,10 @@ public UnitConversionResponse mapToResponse(UnitConversion entity) {
212231
response.setIsActive(entity.isActive());
213232
return response;
214233
}
234+
235+
private boolean hasSameAttributes(ProductVariant left, ProductVariant right) {
236+
Map<String, String> leftAttrs = left != null && left.getAttributes() != null ? left.getAttributes() : Collections.emptyMap();
237+
Map<String, String> rightAttrs = right != null && right.getAttributes() != null ? right.getAttributes() : Collections.emptyMap();
238+
return leftAttrs.equals(rightAttrs);
239+
}
215240
}

frontend/src/hooks/product_variants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const useFetchVariants = (productId) => {
2727
stock_quantity: v.stockQuantity,
2828
image_url: v.imageUrl,
2929
is_active: v.isActive,
30+
is_base_unit: v.isBaseUnit,
3031
created_at: v.createdAt,
3132
attributes: v.attributes,
3233
unit_conversions: v.unitConversions || [],

frontend/src/pages/Products/ProductManager/ProductDetail.jsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -680,15 +680,27 @@ function ProductDetail() {
680680
>
681681
<Edit className="w-[18px] h-[18px]" />
682682
</Button>
683-
<Button
684-
size="sm"
685-
variant="ghost"
686-
title={expandedVariantId === variant.id ? "Thu gọn quy đổi" : "Cấu hình quy đổi đơn vị"}
687-
onClick={() => setExpandedVariantId(expandedVariantId === variant.id ? null : variant.id)}
688-
className={`h-10 w-10 p-0 rounded-xl border border-gray-200 shadow-sm hover:shadow-md transition-all focus:ring-0 ${expandedVariantId === variant.id ? 'bg-indigo-100 text-indigo-700 border-indigo-200' : 'hover:bg-indigo-50 text-indigo-600'}`}
689-
>
690-
<Box className="w-[18px] h-[18px]" />
691-
</Button>
683+
{variant.is_base_unit ? (
684+
<Button
685+
size="sm"
686+
variant="ghost"
687+
title={expandedVariantId === variant.id ? "Thu gọn quy đổi" : "Cấu hình quy đổi đơn vị"}
688+
onClick={() => setExpandedVariantId(expandedVariantId === variant.id ? null : variant.id)}
689+
className={`h-10 w-10 p-0 rounded-xl border border-gray-200 shadow-sm hover:shadow-md transition-all focus:ring-0 ${expandedVariantId === variant.id ? 'bg-indigo-100 text-indigo-700 border-indigo-200' : 'hover:bg-indigo-50 text-indigo-600'}`}
690+
>
691+
<Box className="w-[18px] h-[18px]" />
692+
</Button>
693+
) : (
694+
<Button
695+
size="sm"
696+
variant="ghost"
697+
disabled
698+
title="Biến thể quy đổi không được phép tạo thêm quy đổi"
699+
className="h-10 w-10 p-0 rounded-xl border border-gray-200 text-gray-300 cursor-not-allowed bg-gray-50"
700+
>
701+
<Box className="w-[18px] h-[18px]" />
702+
</Button>
703+
)}
692704
{isWithin2Minutes(variant.created_at) && (
693705
<Button
694706
size="sm"
@@ -706,7 +718,7 @@ function ProductDetail() {
706718
</TableCell>
707719
</TableRow>
708720
{/* Dòng hiển thị quy đổi đơn vị khi expand */}
709-
{canManageProduct && expandedVariantId === variant.id && (
721+
{canManageProduct && variant.is_base_unit && expandedVariantId === variant.id && (
710722
<TableRow className="bg-gray-50/30">
711723
<TableCell colSpan={10} className="p-0">
712724
<div className="px-6 py-4 animate-in slide-in-from-top-2 duration-200 border-b border-gray-100">

0 commit comments

Comments
 (0)