Fix refund flow

This commit is contained in:
2026-03-11 14:21:40 -06:00
parent 20db3e5344
commit 3bfdbf7584
2 changed files with 97 additions and 7 deletions

View File

@@ -372,6 +372,11 @@ public class SaleController {
private void openRefundDialog() {
try {
SaleLineItem selectedSale = tvSales.getSelectionModel().getSelectedItem();
if (selectedSale != null && selectedSale.isRefund()) {
showError("Refund", "Select an original sale, not an existing refund.");
return;
}
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"/org/example/petshopdesktop/dialogviews/refund-dialog-view.fxml"));
Stage dialog = new Stage();

View File

@@ -86,6 +86,8 @@ public class RefundDialogController {
private Button btnCancel;
private SaleResponse currentSale;
private final List<SaleItemResponse> baseOriginalItems = new ArrayList<>();
private final ObservableList<SaleItemResponse> originalItems = FXCollections.observableArrayList();
private final ObservableList<RefundItem> refundItems = FXCollections.observableArrayList();
private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA);
@@ -102,6 +104,7 @@ public class RefundDialogController {
colOriginalQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
colOriginalUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
colOriginalTotal.setCellValueFactory(new PropertyValueFactory<>("lineTotal"));
tvOriginalItems.setItems(originalItems);
tvOriginalItems.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
colRefundProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
@@ -143,6 +146,11 @@ public class RefundDialogController {
try {
List<SaleResponse> allSales = SaleApi.getInstance().listSales(0, 1000, null);
currentSale = SaleApi.getInstance().getSale(saleId);
if (Boolean.TRUE.equals(currentSale.getIsRefund())) {
clearLoadedSale();
showError("Load Sale", "Select an original sale, not a refund record.");
return;
}
List<SaleResponse> previousRefunds = allSales.stream()
.filter(s -> Boolean.TRUE.equals(s.getIsRefund()) && saleId.equals(s.getOriginalSaleId()))
.collect(Collectors.toList());
@@ -161,10 +169,13 @@ public class RefundDialogController {
return;
}
tvOriginalItems.setItems(FXCollections.observableArrayList(refundableItems));
baseOriginalItems.clear();
baseOriginalItems.addAll(copySaleItems(refundableItems));
originalItems.setAll(copySaleItems(refundableItems));
cbPaymentMethod.getSelectionModel().select(currentSale.getPaymentMethod());
refundItems.clear();
updateOriginalItemAvailability();
updateRefundTotal();
} catch (Exception e) {
@@ -215,12 +226,8 @@ public class RefundDialogController {
return;
}
refundItems.add(new RefundItem(
selected.getProdId().intValue(),
selected.getProductName(),
quantity,
selected.getUnitPrice().doubleValue()
));
addOrMergeRefundItem(selected, quantity);
updateOriginalItemAvailability();
updateRefundTotal();
} catch (NumberFormatException e) {
@@ -234,6 +241,7 @@ public class RefundDialogController {
RefundItem selected = tvRefundItems.getSelectionModel().getSelectedItem();
if (selected != null) {
refundItems.remove(selected);
updateOriginalItemAvailability();
updateRefundTotal();
}
}
@@ -309,6 +317,83 @@ public class RefundDialogController {
closeDialog();
}
private void clearLoadedSale() {
currentSale = null;
lblSaleInfo.setText("");
baseOriginalItems.clear();
originalItems.clear();
refundItems.clear();
updateRefundTotal();
}
private void addOrMergeRefundItem(SaleItemResponse selected, int quantity) {
for (int i = 0; i < refundItems.size(); i++) {
RefundItem existing = refundItems.get(i);
if (existing.getProdId() == selected.getProdId().intValue()) {
refundItems.set(i, new RefundItem(
existing.getProdId(),
existing.getProductName(),
existing.getQuantity() + quantity,
existing.getUnitPrice()
));
return;
}
}
refundItems.add(new RefundItem(
selected.getProdId().intValue(),
selected.getProductName(),
quantity,
selected.getUnitPrice().doubleValue()
));
}
private void updateOriginalItemAvailability() {
if (currentSale == null) {
baseOriginalItems.clear();
originalItems.clear();
return;
}
Map<Long, Integer> pendingRefunds = new HashMap<>();
for (RefundItem refundItem : refundItems) {
pendingRefunds.merge((long) refundItem.getProdId(), refundItem.getQuantity(), Integer::sum);
}
List<SaleItemResponse> refreshedItems = new ArrayList<>();
for (SaleItemResponse originalItem : baseOriginalItems) {
SaleItemResponse refreshedItem = copySaleItem(originalItem);
int pending = pendingRefunds.getOrDefault(refreshedItem.getProdId(), 0);
refreshedItem.setQuantity(Math.max(0, refreshedItem.getQuantity() - pending));
if (refreshedItem.getQuantity() > 0) {
refreshedItems.add(refreshedItem);
}
}
originalItems.setAll(refreshedItems);
tvOriginalItems.getSelectionModel().clearSelection();
tvOriginalItems.refresh();
tvRefundItems.refresh();
}
private List<SaleItemResponse> copySaleItems(List<SaleItemResponse> items) {
List<SaleItemResponse> copies = new ArrayList<>();
for (SaleItemResponse item : items) {
copies.add(copySaleItem(item));
}
return copies;
}
private SaleItemResponse copySaleItem(SaleItemResponse source) {
SaleItemResponse copy = new SaleItemResponse();
copy.setSaleItemId(source.getSaleItemId());
copy.setProdId(source.getProdId());
copy.setProductName(source.getProductName());
copy.setQuantity(source.getQuantity());
copy.setUnitPrice(source.getUnitPrice());
return copy;
}
private void updateRefundTotal() {
double total = refundItems.stream().mapToDouble(RefundItem::getTotal).sum();
lblRefundTotal.setText(currency.format(total));