Fix sale-view loading error by suppressing error dialogs during initialization
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user