Skip to content

Commit 38a4c42

Browse files
Merge branch 'inventory' into dev
2 parents e644582 + 5357bf8 commit 38a4c42

File tree

13 files changed

+177
-84
lines changed

13 files changed

+177
-84
lines changed

backend/src/main/java/com/smalltrend/controller/inventory/DisposalVoucherController.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import jakarta.validation.Valid;
66
import lombok.RequiredArgsConstructor;
77
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.access.prepost.PreAuthorize;
89
import org.springframework.web.bind.annotation.*;
910

1011
import java.util.List;
@@ -45,12 +46,20 @@ public ResponseEntity<DisposalVoucherResponse> saveDraft(
4546
return ResponseEntity.ok(disposalVoucherService.saveDraft(request, userId));
4647
}
4748

49+
@PostMapping("/confirm")
50+
public ResponseEntity<DisposalVoucherResponse> createAndApprove(
51+
@Valid @RequestBody DisposalVoucherRequest request,
52+
@RequestParam("userId") Long userId) {
53+
return ResponseEntity.ok(disposalVoucherService.createAndApprove(request, userId));
54+
}
55+
4856
@PutMapping("/{id}/submit")
4957
public ResponseEntity<DisposalVoucherResponse> submitForApproval(@PathVariable("id") Long id) {
5058
return ResponseEntity.ok(disposalVoucherService.submitForApproval(id));
5159
}
5260

5361
@PutMapping("/{id}/approve")
62+
@PreAuthorize("hasAnyAuthority('ADMIN', 'MANAGER', 'INVENTORY_STAFF', 'ROLE_ADMIN', 'ROLE_MANAGER', 'ROLE_INVENTORY_STAFF')")
5463
public ResponseEntity<DisposalVoucherResponse> approveVoucher(
5564
@PathVariable("id") Long id,
5665
@RequestParam("userId") Long userId) {

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,30 @@ public DisposalVoucherResponse submitForApproval(Long id) {
176176
return toResponse(saved);
177177
}
178178

179+
@Transactional
180+
public DisposalVoucherResponse createAndApprove(DisposalVoucherRequest request, Long userId) {
181+
DisposalVoucherResponse draft = saveDraft(request, userId);
182+
return approveVoucher(draft.getId(), userId);
183+
}
184+
179185
// Approve voucher (deduct stock)
180186
@Transactional
181187
public DisposalVoucherResponse approveVoucher(Long id, Long userId) {
182188
DisposalVoucher voucher = disposalVoucherRepository.findById(id)
183189
.orElseThrow(() -> new RuntimeException("Disposal voucher not found"));
184190

185-
if (voucher.getStatus() != DisposalStatus.PENDING) {
186-
throw new RuntimeException("Only PENDING vouchers can be approved");
191+
if (voucher.getStatus() == DisposalStatus.CONFIRMED) {
192+
return toResponse(voucher);
193+
}
194+
195+
if (voucher.getStatus() != DisposalStatus.DRAFT
196+
&& voucher.getStatus() != DisposalStatus.REJECTED
197+
&& voucher.getStatus() != DisposalStatus.PENDING) {
198+
throw new RuntimeException("Only DRAFT, REJECTED, or PENDING vouchers can be approved");
199+
}
200+
201+
if (voucher.getItems().isEmpty()) {
202+
throw new RuntimeException("Cannot approve voucher without items");
187203
}
188204

189205
User user = userRepository.findById(userId.intValue())

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import org.springframework.scheduling.annotation.Scheduled;
1616
import org.springframework.stereotype.Service;
1717

18+
import java.util.concurrent.CompletableFuture;
19+
1820
import java.time.LocalDate;
1921
import java.time.LocalDateTime;
2022
import java.time.temporal.ChronoUnit;
@@ -179,19 +181,20 @@ private boolean sendRealtimeOutOfStockAlert(InventoryStock stock, String source)
179181
+ "</ul>"
180182
+ "</div>";
181183

182-
boolean deliveredAny = false;
183184
for (String recipient : recipients) {
184-
try {
185-
sendEmail(recipient, subject, html);
186-
deliveredAny = true;
187-
} catch (Exception ex) {
188-
log.error("Failed to send realtime out-of-stock alert to {}", recipient, ex);
189-
}
185+
CompletableFuture.runAsync(() -> {
186+
try {
187+
sendEmail(recipient, subject, html);
188+
} catch (Exception ex) {
189+
log.error("Failed to send realtime out-of-stock alert to {}", recipient, ex);
190+
}
191+
});
190192
}
191193

192-
return deliveredAny;
194+
return true; // dispatch async send to avoid blocking stock update request
193195
}
194196

197+
195198
private String buildDailySummaryHtml(List<InventoryStock> outOfStockItems) {
196199
String rows = outOfStockItems.stream()
197200
.map(item -> {

backend/src/main/resources/data.sql

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ INSERT INTO `locations` VALUES (1,'Kho chính, Dãy A, Hàng 1',5000,'2026-03-18
128128

129129

130130
-- 11. PRODUCT BATCHES
131-
INSERT INTO `product_batches` VALUES (1,'VM2026001',20000.00,'2026-04-15','2026-01-15',1),(2,'DV2026001',12000.00,'2027-02-01','2026-02-01',2),(3,'NC2026001',35000.00,'2027-01-20','2026-01-20',3),(4,'CC2026001',8000.00,'2026-08-10','2026-02-10',4),(5,'OI2026001',6000.00,'2026-06-01','2026-02-01',5),(6,'CH2026001',10000.00,'2026-10-15','2026-01-15',6),(7,'CP2026001',45000.00,'2026-04-01','2026-02-01',7),(8,'VS2026001',18000.00,'2027-01-20','2026-01-20',8),(9,'OR2026001',32000.00,'2026-12-10','2026-02-10',9),(10,'CU2026001',1000.00,'2027-06-01','2026-02-01',10),(11,'VF2026001',6000.00,'2026-07-15','2026-01-15',11),(12,'HH2026001',3000.00,'2026-08-01','2026-02-01',12),(13,'OM2026001',7000.00,'2026-07-20','2026-01-20',13),(14,'CS2026001',11000.00,'2027-02-10','2026-02-10',14),(15,'THY2026001',4000.00,'2026-04-01','2026-03-01',15),(16,'THM2026001',25000.00,'2026-09-01','2026-03-01',16),(17,'LA2026001',8000.00,'2026-11-10','2026-02-10',17),(18,'TP2026001',7000.00,'2026-10-15','2026-02-15',18),(19,'KN2026001',24000.00,'2027-01-20','2026-01-20',19),(20,'MG2026001',20000.00,'2027-02-10','2026-02-10',20),(21,'HEI2026001',16000.00,'2027-02-15','2026-02-15',21),(22,'TIG2026001',14000.00,'2027-02-15','2026-02-15',22),(23,'NN2026001',28000.00,'2027-02-15','2026-02-15',23),(24,'OT2026001',20000.00,'2027-02-15','2026-02-15',24),(25,'TA2026001',40000.00,'2027-02-15','2026-02-15',25),(26,'OMO2026001',32000.00,'2027-02-15','2026-02-15',26),(27,'DA2026027',20000.00,'2026-10-31','2026-03-18',34),(28,'CC2026001',8000.00,'2026-08-10','2026-02-10',35),(29,'BE2026029',12000.00,'2026-11-30','2026-03-18',35),(30,'DA2026030',25000.00,'2026-11-30','2026-03-18',27),(31,'DA2026031',23000.00,'2027-03-18','2026-03-18',27),(32,'PE2026032',10000.00,'2027-12-31','2026-03-18',28),(33,'SN2026033',10000.00,'2027-03-13','2026-03-18',30),(34,'DAIRYV-000034',500000.00,'2027-03-19','2026-03-19',27),(38,'DAIRYV-000035',520000.00,'2026-12-30','2026-03-19',27),(39,'DAIRYV-000036',580000.00,'2026-12-30','2026-03-19',27);
131+
INSERT INTO `product_batches` VALUES (1,'VM2026001',20000.00,'2026-04-15','2026-01-15',1),(2,'DV2026001',12000.00,'2027-02-01','2026-02-01',2),(3,'NC2026001',35000.00,'2027-01-20','2026-01-20',3),(4,'CC2026001',8000.00,'2026-08-10','2026-02-10',4),(5,'OI2026001',6000.00,'2026-06-01','2026-02-01',5),(6,'CH2026001',10000.00,'2026-10-15','2026-01-15',6),(7,'CP2026001',45000.00,'2026-04-01','2026-02-01',7),(8,'VS2026001',18000.00,'2027-01-20','2026-01-20',8),(9,'OR2026001',32000.00,'2026-12-10','2026-02-10',9),(10,'CU2026001',1000.00,'2027-06-01','2026-02-01',10),(11,'VF2026001',6000.00,'2026-07-15','2026-01-15',11),(12,'HH2026001',3000.00,'2026-08-01','2026-02-01',12),(13,'OM2026001',7000.00,'2026-07-20','2026-01-20',13),(14,'CS2026001',11000.00,'2027-02-10','2026-02-10',14),(15,'THY2026001',4000.00,'2026-04-01','2026-03-01',15),(16,'THM2026001',25000.00,'2026-09-01','2026-03-01',16),(17,'LA2026001',8000.00,'2026-11-10','2026-02-10',17),(18,'TP2026001',7000.00,'2026-10-15','2026-02-15',18),(19,'KN2026001',24000.00,'2027-01-20','2026-01-20',19),(20,'MG2026001',20000.00,'2027-02-10','2026-02-10',20),(21,'HEI2026001',16000.00,'2027-02-15','2026-02-15',21),(22,'TIG2026001',14000.00,'2027-02-15','2026-02-15',22),(23,'NN2026001',28000.00,'2027-02-15','2026-02-15',23),(24,'OT2026001',20000.00,'2027-02-15','2026-02-15',24),(25,'TA2026001',40000.00,'2027-02-15','2026-02-15',25),(26,'OMO2026001',32000.00,'2027-02-15','2026-02-15',26),(27,'DA2026027',20000.00,'2026-10-31','2026-03-18',34),(28,'CC2026001',8000.00,'2026-08-10','2026-02-10',35),(29,'BE2026029',12000.00,'2026-11-30','2026-03-18',35),(30,'DA2026030',25000.00,'2026-11-30','2026-03-18',27),(31,'DA2026031',23000.00,'2027-03-18','2026-03-18',27),(32,'PE2026032',10000.00,'2027-12-31','2026-03-18',28),(33,'SN2026033',10000.00,'2027-03-13','2026-03-18',30),(34,'DAIRYV-000034',500000.00,'2027-03-19','2026-03-19',27),(38,'DAIRYV-000035',520000.00,'2026-12-30','2026-03-19',27),(39,'DAIRYV-000036',580000.00,'2026-12-30','2026-03-19',27),(40,'EX2026040',12000.00,'2026-02-10','2026-01-10',4),(41,'EX2026041',16000.00,'2026-01-25','2025-12-25',21),(42,'EX2026042',7000.00,'2026-03-05','2026-01-05',18);
132132

133133
-- 11.1 INVENTORY STOCK
134-
INSERT INTO `inventory_stock` VALUES (1,214,1,1,1),(2,178,2,2,2),(3,260,3,3,3),(4,502,4,4,4),(5,383,5,5,5),(6,120,6,1,6),(7,85,7,2,7),(8,150,8,3,8),(9,200,9,4,9),(10,973,10,5,10),(11,300,11,1,11),(12,500,12,2,12),(13,400,13,3,13),(14,250,14,4,14),(15,180,15,5,15),(16,210,16,1,16),(17,318,17,2,17),(18,280,18,3,18),(19,140,19,4,19),(20,190,20,5,20),(21,300,21,1,21),(22,250,22,2,22),(23,100,23,3,23),(24,148,24,4,24),(25,200,25,5,25),(26,80,26,1,26),(27,480,27,1,34),(28,16,28,4,35),(29,300,29,1,35),(30,480,30,1,27),(31,480,31,2,27),(32,1,32,1,28),(33,10,33,3,30),(34,96,34,1,27),(38,384,38,1,27),(39,240,39,1,27);
134+
INSERT INTO `inventory_stock` VALUES (1,214,1,1,1),(2,178,2,2,2),(3,260,3,3,3),(4,502,4,4,4),(5,383,5,5,5),(6,120,6,1,6),(7,85,7,2,7),(8,150,8,3,8),(9,200,9,4,9),(10,973,10,5,10),(11,300,11,1,11),(12,500,12,2,12),(13,400,13,3,13),(14,250,14,4,14),(15,180,15,5,15),(16,210,16,1,16),(17,318,17,2,17),(18,280,18,3,18),(19,140,19,4,19),(20,190,20,5,20),(21,300,21,1,21),(22,250,22,2,22),(23,100,23,3,23),(24,148,24,4,24),(25,200,25,5,25),(26,80,26,1,26),(27,480,27,1,34),(28,16,28,4,35),(29,300,29,1,35),(30,480,30,1,27),(31,480,31,2,27),(32,1,32,1,28),(33,10,33,3,30),(34,96,34,1,27),(38,384,38,1,27),(39,240,39,1,27),(40,24,40,2,4),(41,15,41,3,21),(42,30,42,4,18);
135135

136136
-- Điều chỉnh số lượng tồn kho để phản ánh trạng thái sau khi đã xác nhận phiếu kiểm kho
137137
-- và các giao dịch bán hàng đã ghi nhận trong stock_movements
@@ -276,17 +276,17 @@ VALUES
276276
(4,'PO-2024-005',4,NULL,3,NULL,'2024-02-10',NULL,NULL,'CONFIRMED',2200000.00,0.00,NULL,0.00,NULL,NULL,2200000.00,'Migrated from legacy purchase_orders',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 01:40:09.000000','2026-03-18 01:40:09.000000'),
277277
(5,'PO-2026-001',2,NULL,1,NULL,'2026-02-10',NULL,NULL,'RECEIVED',47000000.00,4700000.00,NULL,500000.00,NULL,NULL,51200000.00,'Đơn nhập sữa Vinamilk tháng 2',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 01:40:09.000000','2026-03-18 01:40:09.000000'),
278278
(6,'PO-2026-002',2,NULL,2,NULL,'2026-02-12',NULL,NULL,'CONFIRMED',18000000.00,1800000.00,NULL,0.00,NULL,NULL,19800000.00,'Đơn nhập đồ gia dụng Unilever',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 01:40:09.000000','2026-03-18 01:40:09.000000'),
279-
(7,'PO-2026-003',NULL,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',9600000.00,0.00,0.00,0.00,0.00,0.00,9600000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:09:23.200903','2026-03-17 20:10:11.864228'),
280-
(8,'PO-2026-004',NULL,NULL,4,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',3600000.00,0.00,0.00,0.00,0.00,0.00,3600000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:15:53.075633','2026-03-17 20:16:54.397422'),
281-
(9,'PO-2026-005',NULL,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',12000000.00,0.00,0.00,0.00,0.00,0.00,12000000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:58:16.495149','2026-03-17 20:59:00.566714'),
282-
(10,'PO-2026-006',NULL,NULL,1,2,'2026-03-18',NULL,'2026-03-18','RECEIVED',11040000.00,0.00,0.00,0.00,0.00,0.00,11040000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 21:00:20.775031','2026-03-17 21:03:37.948102'),
283-
(11,'PO-2026-007',NULL,NULL,NULL,NULL,'2026-03-18',NULL,NULL,'CHECKING',0.00,0.00,0.00,0.00,0.00,0.00,0.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 21:04:06.399199','2026-03-18 07:57:41.708769'),
284-
(12,'PO-2026-008',NULL,NULL,NULL,NULL,'2026-03-18',NULL,NULL,'CHECKING',0.00,0.00,0.00,0.00,0.00,0.00,0.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 07:35:21.349491','2026-03-18 07:36:02.684663'),
285-
(13,'PO-2026-009',NULL,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',10000.00,100.00,1.00,0.00,1000.00,0.00,11100.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 07:49:07.100376','2026-03-18 08:00:40.487019'),
286-
(14,'PO-2026-010',NULL,NULL,1,3,'2026-03-18',NULL,'2026-03-18','RECEIVED',100000.00,1000.00,1.00,0.00,1000.00,0.00,102000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 08:09:02.120031','2026-03-18 08:10:33.189713'),
287-
(15,'PO-2026-011',NULL,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',1000000.00,100000.00,10.00,0.00,100000.00,0.00,1200000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 06:52:38.117005','2026-03-19 06:54:18.353439'),
288-
(16,'PO-2026-012',NULL,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',4160000.00,416000.00,10.00,0.00,100000.00,0.00,4676000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 11:37:47.963521','2026-03-19 11:39:07.458262'),
289-
(17,'PO-2026-013',NULL,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',2900000.00,290000.00,10.00,0.00,100000.00,0.00,3290000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 11:43:22.501573','2026-03-19 11:44:28.297791');
279+
(7,'PO-2026-003',1,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',9600000.00,0.00,0.00,0.00,0.00,0.00,9600000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:09:23.200903','2026-03-17 20:10:11.864228'),
280+
(8,'PO-2026-004',4,NULL,4,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',3600000.00,0.00,0.00,0.00,0.00,0.00,3600000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:15:53.075633','2026-03-17 20:16:54.397422'),
281+
(9,'PO-2026-005',2,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',12000000.00,0.00,0.00,0.00,0.00,0.00,12000000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 20:58:16.495149','2026-03-17 20:59:00.566714'),
282+
(10,'PO-2026-006',2,NULL,1,2,'2026-03-18',NULL,'2026-03-18','RECEIVED',11040000.00,0.00,0.00,0.00,0.00,0.00,11040000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 21:00:20.775031','2026-03-17 21:03:37.948102'),
283+
(11,'PO-2026-007',3,NULL,NULL,NULL,'2026-03-18',NULL,NULL,'CHECKING',0.00,0.00,0.00,0.00,0.00,0.00,0.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-17 21:04:06.399199','2026-03-18 07:57:41.708769'),
284+
(12,'PO-2026-008',3,NULL,NULL,NULL,'2026-03-18',NULL,NULL,'CHECKING',0.00,0.00,0.00,0.00,0.00,0.00,0.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 07:35:21.349491','2026-03-18 07:36:02.684663'),
285+
(13,'PO-2026-009',2,NULL,1,1,'2026-03-18',NULL,'2026-03-18','RECEIVED',10000.00,100.00,1.00,0.00,1000.00,0.00,11100.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 07:49:07.100376','2026-03-18 08:00:40.487019'),
286+
(14,'PO-2026-010',4,NULL,1,3,'2026-03-18',NULL,'2026-03-18','RECEIVED',100000.00,1000.00,1.00,0.00,1000.00,0.00,102000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-18 08:09:02.120031','2026-03-18 08:10:33.189713'),
287+
(15,'PO-2026-011',1,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',1000000.00,100000.00,10.00,0.00,100000.00,0.00,1200000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 06:52:38.117005','2026-03-19 06:54:18.353439'),
288+
(16,'PO-2026-012',1,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',4160000.00,416000.00,10.00,0.00,100000.00,0.00,4676000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 11:37:47.963521','2026-03-19 11:39:07.458262'),
289+
(17,'PO-2026-013',1,NULL,1,1,'2026-03-19',NULL,'2026-03-19','RECEIVED',2900000.00,290000.00,10.00,0.00,100000.00,0.00,3290000.00,'',NULL,NULL,NULL,NULL,NULL,NULL,'2026-03-19 11:43:22.501573','2026-03-19 11:44:28.297791');
290290

291291
-- 26. PURCHASE ORDER ITEMS (Matching JPA PurchaseOrderItem schema)
292292
INSERT INTO `purchase_order_items` VALUES (1,NULL,'Migrated from legacy purchase_order_items',250,250,NULL,20000.00,1,1),(2,NULL,'Migrated from legacy purchase_order_items',160,160,NULL,22000.00,2,2),(3,NULL,'Migrated from legacy purchase_order_items',200,200,NULL,9500.00,3,4),(4,NULL,'Migrated from legacy purchase_order_items',180,0,NULL,12000.00,4,5),(5,NULL,'Đã nhận đủ 2000 hộp',2000,2000,NULL,20000.00,5,1),(6,NULL,'Đã nhận đủ 200 gói',200,200,NULL,35000.00,5,3),(7,NULL,'Chưa nhận hàng',1500,0,NULL,12000.00,6,2),(8,'2026-10-31','',10,480,9600000.00,20000.00,7,34),(9,'2026-11-30','',10,300,3600000.00,12000.00,8,35),(10,'2026-11-30','',480,480,12000000.00,25000.00,9,27),(11,NULL,'',480,480,11040000.00,23000.00,10,27),(12,NULL,'',48,0,0.00,0.00,11,27),(13,NULL,'',1,0,0.00,0.00,12,28),(14,'2027-12-31','',1,1,10000.00,10000.00,13,28),(15,'2027-03-13','',10,10,100000.00,10000.00,14,30),(16,'2027-03-19','',2,96,1000000.00,500000.00,15,34),(17,'2026-12-30','',8,384,4160000.00,520000.00,16,34),(18,'2026-12-30','',5,240,2900000.00,580000.00,17,34);
@@ -320,6 +320,16 @@ INSERT INTO `disposal_vouchers` VALUES (1,'DV-202602-001','2026-02-18 14:00:00.0
320320
-- 34. DISPOSAL VOUCHER ITEMS
321321
INSERT INTO `disposal_voucher_items` VALUES (1,'DV2026001','2027-02-01',12,180000.00,15000.00,2,1,2),(2,'NC2026001','2026-02-01',8,96000.00,12000.00,3,2,3);
322322

323+
-- 34.1 CLEANUP LEGACY PENDING DISPOSAL VOUCHERS
324+
-- Rule: auto-confirm all historical PENDING vouchers to align with no-approval flow
325+
UPDATE disposal_vouchers
326+
SET status = 'CONFIRMED',
327+
confirmed_at = COALESCE(confirmed_at, created_at, NOW()),
328+
confirmed_by = COALESCE(confirmed_by, created_by),
329+
rejection_reason = NULL
330+
WHERE id > 0
331+
AND status = 'PENDING';
332+
323333
-- 35. LOYALTY GIFTS (Quà tặng gift/rewards từ loyalty program)
324334
INSERT INTO `loyalty_gifts` VALUES (1,'2026-03-18 01:40:09.000000',_binary '','Nước uống đặc biệt 500ml',50,150,4),(2,'2026-03-18 01:40:09.000000',_binary '','Bộ cà phê hòa tan 3in1',150,80,3),(3,'2026-03-18 01:40:09.000000',_binary '','Xà phòng Dove 90g',75,200,2),(4,'2026-03-18 01:40:09.000000',_binary '','Sữa tươi Vinamilk 1L',200,50,1),(5,'2026-03-18 01:40:09.000000',_binary '','Combo Snack Oishi',100,100,5),(6,'2026-03-19 15:38:12.253398',_binary '','Nước ngọt Coca Cola (Đổi điểm)',5,10,4);
325335

frontend/src/components/inventory/count/InventoryCountTable.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ const InventoryCountRow = memo(function InventoryCountRow({
1919
const inputRef = useRef(null);
2020
const status = classifyCountItem(item);
2121
const colors = COUNT_ITEM_COLORS[status];
22-
const hasDifference =
23-
item.actual_quantity !== null && item.difference_quantity !== 0;
22+
const hasActualCount =
23+
item.actual_quantity !== null && item.actual_quantity !== undefined;
24+
const hasDifference = hasActualCount && (item.difference_quantity ?? 0) !== 0;
2425
const needsReason = hasDifference && !item.reason;
2526

2627
const handleKeyDown = useCallback(
@@ -124,18 +125,17 @@ const InventoryCountRow = memo(function InventoryCountRow({
124125
{/* Difference */}
125126
<td className="px-3 py-2.5 w-24 text-right">
126127
<span className={`text-sm font-bold font-mono ${colors.text}`}>
127-
{item.actual_quantity !== null
128-
? (item.difference_quantity > 0 ? "+" : "") +
129-
item.difference_quantity
128+
{hasActualCount
129+
? `${(item.difference_quantity ?? 0) > 0 ? "+" : ""}${item.difference_quantity ?? 0}`
130130
: "—"}
131131
</span>
132132
</td>
133133

134134
{/* Difference Value */}
135135
<td className="px-3 py-2.5 w-32 text-right">
136136
<span className={`text-xs font-medium ${colors.text}`}>
137-
{item.actual_quantity !== null
138-
? formatVNDCount(item.difference_value)
137+
{hasActualCount
138+
? formatVNDCount(item.difference_value ?? 0)
139139
: "—"}
140140
</span>
141141
</td>

frontend/src/hooks/useDisposalList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useEffect, useMemo, useCallback } from "react";
2-
import { getDisposalVouchers, submitDisposalVoucher, approveDisposalVoucher, rejectDisposalVoucher } from "../services/disposalService";
2+
import { getDisposalVouchers } from "../services/disposalService";
33
import { getLocations } from "../services/inventoryService";
44

55
export function useDisposalList() {

0 commit comments

Comments
 (0)