Fix sale-view loading error by suppressing error dialogs during initialization

This commit is contained in:
2026-02-25 09:32:21 -07:00
parent ffa45044e4
commit 8077d7729a

View File

@@ -1,11 +1,37 @@
package org.example.petshopdesktop.controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.database.InventoryDB;
import org.example.petshopdesktop.database.ProductDB;
import org.example.petshopdesktop.database.SaleDB;
import org.example.petshopdesktop.models.Inventory;
import org.example.petshopdesktop.models.Product;
import org.example.petshopdesktop.models.SaleCartItem;
import org.example.petshopdesktop.models.SaleLineItem;
import org.example.petshopdesktop.util.ActivityLogger;
import java.sql.SQLException;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class SaleController {
@@ -13,38 +39,324 @@ public class SaleController {
private Button btnRefresh;
@FXML
private TableColumn<?, ?> colCustomerName;
private Label lblModeNote;
@FXML
private TableColumn<?, ?> colSaleDate;
private VBox vbCreateSale;
@FXML
private TableColumn<?, ?> colSaleId;
private ComboBox<Product> cbProduct;
@FXML
private TableColumn<?, ?> colSalePaymentType;
private Spinner<Integer> spQuantity;
@FXML
private TableColumn<?, ?> colSaleQuantity;
private Button btnAddToCart;
@FXML
private TableColumn<?, ?> colSaleTotal;
private Button btnRemoveSelected;
@FXML
private TableColumn<?, ?> colSaleUnitPrice;
private TableView<SaleCartItem> tvCart;
@FXML
private TableColumn<?, ?> colServiceProduct;
private TableColumn<SaleCartItem, String> colCartProduct;
@FXML
private TableView<?> tvSales;
private TableColumn<SaleCartItem, Integer> colCartQty;
@FXML
private TableColumn<SaleCartItem, Double> colCartUnitPrice;
@FXML
private TableColumn<SaleCartItem, Double> colCartTotal;
@FXML
private ComboBox<String> cbPaymentMethod;
@FXML
private Label lblCartTotal;
@FXML
private Button btnClearCart;
@FXML
private Button btnSaveSale;
@FXML
private TableColumn<SaleLineItem, Integer> colSaleId;
@FXML
private TableColumn<SaleLineItem, String> colSaleDate;
@FXML
private TableColumn<SaleLineItem, String> colEmployeeName;
@FXML
private TableColumn<SaleLineItem, String> colServiceProduct;
@FXML
private TableColumn<SaleLineItem, Integer> colSaleQuantity;
@FXML
private TableColumn<SaleLineItem, Double> colSaleUnitPrice;
@FXML
private TableColumn<SaleLineItem, Double> colSaleTotal;
@FXML
private TableColumn<SaleLineItem, String> colSalePaymentType;
@FXML
private TableView<SaleLineItem> tvSales;
@FXML
private TextField txtSearch;
@FXML
void btnRefresh(ActionEvent event) {
private final ObservableList<SaleCartItem> cartItems = FXCollections.observableArrayList();
private final ObservableList<SaleLineItem> saleItems = FXCollections.observableArrayList();
private FilteredList<SaleLineItem> filteredSales;
private final Map<Integer, Integer> inventoryByProdId = new HashMap<>();
private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA);
@FXML
public void initialize() {
setupTables();
setupCreateSale();
applyRoleMode();
refreshInventory();
refreshSales();
}
private void setupTables() {
colCartProduct.setCellValueFactory(new PropertyValueFactory<>("prodName"));
colCartQty.setCellValueFactory(new PropertyValueFactory<>("quantity"));
colCartUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
colCartTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
tvCart.setItems(cartItems);
tvCart.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
colSaleId.setCellValueFactory(new PropertyValueFactory<>("saleId"));
colSaleDate.setCellValueFactory(new PropertyValueFactory<>("saleDate"));
colEmployeeName.setCellValueFactory(new PropertyValueFactory<>("employeeName"));
colServiceProduct.setCellValueFactory(new PropertyValueFactory<>("itemName"));
colSaleQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
colSaleUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
colSaleTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
colSalePaymentType.setCellValueFactory(new PropertyValueFactory<>("paymentMethod"));
filteredSales = new FilteredList<>(saleItems, s -> true);
tvSales.setItems(filteredSales);
txtSearch.textProperty().addListener((obs, oldVal, newVal) -> applySalesFilter(newVal));
}
private void setupCreateSale() {
spQuantity.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 999, 1));
spQuantity.setEditable(true);
cbPaymentMethod.setItems(FXCollections.observableArrayList("Cash", "Card"));
cbPaymentMethod.getSelectionModel().selectFirst();
updateCartTotal();
try {
cbProduct.setItems(ProductDB.getProducts());
} catch (SQLException e) {
ActivityLogger.getInstance().logException("SaleController.setupCreateSale", e, "Loading products");
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("SaleController.setupCreateSale", e, "Database connection");
}
}
private void applyRoleMode() {
boolean isAdmin = UserSession.getInstance().isAdmin();
vbCreateSale.setVisible(!isAdmin);
vbCreateSale.setManaged(!isAdmin);
lblModeNote.setText(isAdmin ? "(View only)" : "(Staff can create sales)");
}
private void refreshInventory() {
inventoryByProdId.clear();
try {
for (Inventory inv : InventoryDB.getInventory()) {
inventoryByProdId.put(inv.getProdId(), inv.getQuantity());
}
} catch (SQLException e) {
ActivityLogger.getInstance().logException("SaleController.refreshInventory", e, "Loading inventory");
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("SaleController.refreshInventory", e, "Database connection");
}
}
private void refreshSales() {
refreshSales(false);
}
private void refreshSales(boolean showErrorDialog) {
try {
saleItems.setAll(SaleDB.getSaleLineItems());
} catch (SQLException e) {
ActivityLogger.getInstance().logException("SaleController.refreshSales", e, "Loading sales");
if (showErrorDialog) {
showError("Sales", "Could not load sales.");
}
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("SaleController.refreshSales", e, "Database connection");
if (showErrorDialog) {
showError("Sales", "Database is not connected.");
}
}
}
@FXML
void btnRefresh(ActionEvent event) {
refreshInventory();
refreshSales(true);
}
@FXML
void btnAddToCart(ActionEvent event) {
Product product = cbProduct.getSelectionModel().getSelectedItem();
if (product == null) {
showError("Create Sale", "Select a product.");
return;
}
int requestedQty;
try {
requestedQty = spQuantity.getValue();
} catch (Exception e) {
showError("Create Sale", "Enter a valid quantity.");
return;
}
if (requestedQty <= 0) {
showError("Create Sale", "Quantity must be at least 1.");
return;
}
int stock = inventoryByProdId.getOrDefault(product.getProdId(), 0);
int alreadyInCart = cartItems.stream()
.filter(i -> i.getProdId() == product.getProdId())
.mapToInt(SaleCartItem::getQuantity)
.sum();
int available = stock - alreadyInCart;
if (requestedQty > available) {
showError("Create Sale", "Not enough stock. Available: " + Math.max(0, available));
return;
}
for (SaleCartItem item : cartItems) {
if (item.getProdId() == product.getProdId()) {
item.setQuantity(item.getQuantity() + requestedQty);
tvCart.refresh();
updateCartTotal();
return;
}
}
cartItems.add(new SaleCartItem(product.getProdId(), product.getProdName(), requestedQty, product.getProdPrice()));
updateCartTotal();
}
@FXML
void btnRemoveSelected(ActionEvent event) {
SaleCartItem selected = tvCart.getSelectionModel().getSelectedItem();
if (selected != null) {
cartItems.remove(selected);
updateCartTotal();
}
}
@FXML
void btnClearCart(ActionEvent event) {
cartItems.clear();
updateCartTotal();
}
@FXML
void btnSaveSale(ActionEvent event) {
if (UserSession.getInstance().isAdmin()) {
showError("Create Sale", "This action is restricted to staff.");
return;
}
Integer employeeId = UserSession.getInstance().getEmployeeId();
if (employeeId == null || employeeId <= 0) {
showError("Create Sale", "Employee is not set for this account.");
return;
}
if (cartItems.isEmpty()) {
showError("Create Sale", "Add at least one item.");
return;
}
String payment = cbPaymentMethod.getSelectionModel().getSelectedItem();
if (payment == null || payment.isBlank()) {
showError("Create Sale", "Select a payment method.");
return;
}
try {
int saleId = SaleDB.createSale(employeeId, payment, cartItems);
showInfo("Sale saved", "Sale ID " + saleId + " was created.");
cartItems.clear();
updateCartTotal();
refreshInventory();
refreshSales(true);
} catch (SQLException e) {
ActivityLogger.getInstance().logException("SaleController.btnSaveSale", e, "Creating sale");
showError("Create Sale", e.getMessage() == null ? "Could not save the sale." : e.getMessage());
} catch (RuntimeException e) {
ActivityLogger.getInstance().logException("SaleController.btnSaveSale", e, "Database connection");
showError("Create Sale", "Database is not connected.");
}
}
private void updateCartTotal() {
double total = cartItems.stream().mapToDouble(SaleCartItem::getTotal).sum();
lblCartTotal.setText(currency.format(total));
}
private void applySalesFilter(String filter) {
String f = filter == null ? "" : filter.trim().toLowerCase();
if (f.isEmpty()) {
filteredSales.setPredicate(s -> true);
return;
}
filteredSales.setPredicate(s ->
String.valueOf(s.getSaleId()).contains(f)
|| safe(s.getSaleDate()).contains(f)
|| safe(s.getEmployeeName()).contains(f)
|| safe(s.getItemName()).contains(f)
|| safe(s.getPaymentMethod()).contains(f)
);
}
private static String safe(String v) {
return v == null ? "" : v.toLowerCase();
}
private void showError(String title, String message) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
private void showInfo(String title, String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
}