Skip to content

Commit 1d51196

Browse files
feat: Refactor SummaryPanel and related hooks for improved supplier and location management
- Simplified supplier and location selection in SummaryPanel, allowing for direct editing of supplier and location fields. - Introduced checkingFinancials to calculate order financials dynamically based on receipt items. - Updated validation logic to ensure supplier, location, tax, and shipping fee are provided before submission. - Removed unnecessary state and props related to financial calculations in SummaryPanel. - Enhanced user experience by providing immediate feedback on financial summaries and capacity warnings.
1 parent bebbf25 commit 1d51196

File tree

9 files changed

+355
-692
lines changed

9 files changed

+355
-692
lines changed

backend/src/main/java/com/smalltrend/dto/inventory/purchaseorder/GoodsReceiptRequest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import lombok.Data;
66
import lombok.NoArgsConstructor;
77

8+
import java.math.BigDecimal;
89
import java.util.List;
910

1011
@Data
@@ -13,6 +14,13 @@
1314
@AllArgsConstructor
1415
public class GoodsReceiptRequest {
1516
private String notes;
17+
private Integer supplierId;
18+
private Integer locationId;
19+
private BigDecimal taxPercent;
20+
private BigDecimal shippingFee;
21+
private BigDecimal subtotal;
22+
private BigDecimal taxAmount;
23+
private BigDecimal totalAmount;
1624
private List<GoodsReceiptItemRequest> items;
1725

1826
@Data
@@ -23,6 +31,7 @@ public static class GoodsReceiptItemRequest {
2331
private Integer itemId; // PurchaseOrderItem id
2432
private Integer variantId;
2533
private Integer receivedQuantity;
34+
private java.math.BigDecimal unitCost;
2635
private String notes; // Ghi chú kiểm kê
2736
}
2837
}

backend/src/main/java/com/smalltrend/service/inventory/PurchaseOrderService.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,37 @@ public PurchaseOrderResponse receiveGoods(Integer orderId, GoodsReceiptRequest r
184184
throw new RuntimeException("Chỉ có thể nhập kho phiếu đang kiểm kê.");
185185
}
186186

187+
if (receiptRequest.getSupplierId() == null) {
188+
throw new RuntimeException("Nhà cung cấp là bắt buộc khi xác nhận nhập kho.");
189+
}
190+
if (receiptRequest.getLocationId() == null) {
191+
throw new RuntimeException("Vị trí nhập kho là bắt buộc khi xác nhận nhập kho.");
192+
}
193+
if (receiptRequest.getTaxPercent() == null) {
194+
throw new RuntimeException("Thuế VAT (%) là bắt buộc khi xác nhận nhập kho.");
195+
}
196+
if (receiptRequest.getShippingFee() == null) {
197+
throw new RuntimeException("Phí vận chuyển là bắt buộc khi xác nhận nhập kho.");
198+
}
199+
if (receiptRequest.getTaxPercent().compareTo(BigDecimal.ZERO) < 0) {
200+
throw new RuntimeException("Thuế VAT (%) không được âm.");
201+
}
202+
if (receiptRequest.getShippingFee().compareTo(BigDecimal.ZERO) < 0) {
203+
throw new RuntimeException("Phí vận chuyển không được âm.");
204+
}
205+
206+
// Cập nhật receivedQuantity cho từng item
207+
if (receiptRequest.getItems() != null) {
208+
for (GoodsReceiptRequest.GoodsReceiptItemRequest receiptItem : receiptRequest.getItems()) {
209+
if (receiptItem.getUnitCost() == null || receiptItem.getUnitCost().compareTo(BigDecimal.ZERO) <= 0) {
210+
throw new RuntimeException("Giá nhập từng sản phẩm là bắt buộc và phải lớn hơn 0.");
211+
}
212+
if (receiptItem.getReceivedQuantity() == null || receiptItem.getReceivedQuantity() < 0) {
213+
throw new RuntimeException("Số lượng thực nhận không hợp lệ.");
214+
}
215+
}
216+
}
217+
187218
// Cập nhật receivedQuantity cho từng item
188219
if (receiptRequest.getItems() != null) {
189220
for (GoodsReceiptRequest.GoodsReceiptItemRequest receiptItem : receiptRequest.getItems()) {
@@ -195,6 +226,11 @@ public PurchaseOrderResponse receiveGoods(Integer orderId, GoodsReceiptRequest r
195226
if (orderItem != null) {
196227
orderItem.setReceivedQuantity(receiptItem.getReceivedQuantity() != null
197228
? receiptItem.getReceivedQuantity() : 0);
229+
if (receiptItem.getUnitCost() != null) {
230+
orderItem.setUnitCost(receiptItem.getUnitCost());
231+
int receivedQty = orderItem.getReceivedQuantity() != null ? orderItem.getReceivedQuantity() : 0;
232+
orderItem.setTotalCost(receiptItem.getUnitCost().multiply(BigDecimal.valueOf(receivedQty)));
233+
}
198234
if (receiptItem.getNotes() != null) {
199235
orderItem.setNotes(receiptItem.getNotes());
200236
}
@@ -203,6 +239,29 @@ public PurchaseOrderResponse receiveGoods(Integer orderId, GoodsReceiptRequest r
203239
}
204240
}
205241

242+
Supplier supplier = supplierRepository.findById(receiptRequest.getSupplierId())
243+
.orElseThrow(() -> new RuntimeException("Nhà cung cấp không tồn tại."));
244+
order.setSupplier(supplier);
245+
order.setLocationId(receiptRequest.getLocationId());
246+
247+
BigDecimal subtotal = order.getItems().stream()
248+
.map(item -> {
249+
int receivedQty = item.getReceivedQuantity() != null ? item.getReceivedQuantity() : 0;
250+
BigDecimal unitCost = item.getUnitCost() != null ? item.getUnitCost() : BigDecimal.ZERO;
251+
return unitCost.multiply(BigDecimal.valueOf(receivedQty));
252+
})
253+
.reduce(BigDecimal.ZERO, BigDecimal::add);
254+
BigDecimal taxPercent = receiptRequest.getTaxPercent();
255+
BigDecimal taxAmount = subtotal.multiply(taxPercent).divide(BigDecimal.valueOf(100));
256+
BigDecimal shippingFee = receiptRequest.getShippingFee();
257+
BigDecimal totalAmount = subtotal.add(taxAmount).add(shippingFee);
258+
259+
order.setSubtotal(subtotal);
260+
order.setTaxPercent(taxPercent);
261+
order.setTaxAmount(taxAmount);
262+
order.setShippingFee(shippingFee);
263+
order.setTotalAmount(totalAmount);
264+
206265
order.setStatus(PurchaseOrderStatus.RECEIVED);
207266
order.setActualDeliveryDate(LocalDate.now());
208267
if (receiptRequest.getNotes() != null) {

frontend/src/components/inventory/purchase/GoodsReceiptTable.jsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export default function GoodsReceiptTable({
6060
<th className="px-4 py-3 text-center text-xs font-medium text-slate-500 uppercase w-32">
6161
SL thực nhận
6262
</th>
63+
<th className="px-4 py-3 text-center text-xs font-medium text-slate-500 uppercase w-32">
64+
Giá nhập
65+
</th>
6366
<th className="px-4 py-3 text-center text-xs font-medium text-slate-500 uppercase w-24">
6467
Chênh lệch
6568
</th>
@@ -129,12 +132,28 @@ export default function GoodsReceiptTable({
129132
onUpdateReceiptItem(
130133
item.id,
131134
"receivedQuantity",
132-
parseInt(e.target.value) || 0,
135+
Number.parseInt(e.target.value, 10) || 0,
133136
)
134137
}
135138
className="w-20 text-center px-2 py-1.5 text-sm font-semibold border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
136139
/>
137140
</td>
141+
<td className="px-4 py-3 text-center">
142+
<input
143+
type="number"
144+
min="0"
145+
step="any"
146+
value={ri.unitCost ?? item.unitCost ?? 0}
147+
onChange={(e) =>
148+
onUpdateReceiptItem(
149+
item.id,
150+
"unitCost",
151+
Number.parseFloat(e.target.value) || 0,
152+
)
153+
}
154+
className="w-24 text-center px-2 py-1.5 text-sm border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
155+
/>
156+
</td>
138157
<td className="px-4 py-3 text-center">
139158
<span
140159
className={`inline-flex items-center gap-1 px-2 py-1 rounded-lg text-xs font-semibold ${diffClass}`}

0 commit comments

Comments
 (0)