Merge pull request #32 from RecentRunner/refund-system
Refund system and logo
This commit is contained in:
@@ -132,8 +132,11 @@ CREATE TABLE sale (
|
||||
paymentMethod VARCHAR(50) NOT NULL,
|
||||
employeeId INT NOT NULL,
|
||||
storeId INT NOT NULL,
|
||||
isRefund BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
originalSaleId INT NULL,
|
||||
FOREIGN KEY (employeeId) REFERENCES employee(employeeId),
|
||||
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId)
|
||||
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
|
||||
FOREIGN KEY (originalSaleId) REFERENCES sale(saleId)
|
||||
);
|
||||
|
||||
CREATE TABLE saleItem (
|
||||
|
||||
33
log.txt
33
log.txt
@@ -12,3 +12,36 @@ The last packet sent successfully to the server was 0 milliseconds ago. The driv
|
||||
[2026-02-28 14:35:14] [ERROR] EXCEPTION | Location: MainLayoutController.loadView | Type: LoadException | Message:
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/modelviews/analytics-view.fxml:58
|
||||
| Context: Loading view: analytics-view.fxml
|
||||
[2026-02-28 15:54:59] [ERROR] EXCEPTION | Location: LoginController.openMainLayout | Type: LoadException | Message: Error resolving onAction='#btnChatClicked', either the event handler is not in the Namespace or there is an error in the script.
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/main-layout-view.fxml:177
|
||||
| Context: Loading main application layout after successful login
|
||||
[2026-02-28 15:55:01] [ERROR] EXCEPTION | Location: LoginController.openMainLayout | Type: LoadException | Message: Error resolving onAction='#btnChatClicked', either the event handler is not in the Namespace or there is an error in the script.
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/main-layout-view.fxml:177
|
||||
| Context: Loading main application layout after successful login
|
||||
[2026-02-28 15:59:05] [ERROR] EXCEPTION | Location: LoginController.openMainLayout | Type: LoadException | Message:
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/main-layout-view.fxml:48
|
||||
| Context: Loading main application layout after successful login
|
||||
[2026-02-28 15:59:24] [ERROR] EXCEPTION | Location: LoginController.openMainLayout | Type: LoadException | Message:
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/main-layout-view.fxml:48
|
||||
| Context: Loading main application layout after successful login
|
||||
[2026-02-28 16:01:12] [ERROR] EXCEPTION | Location: LoginController.openMainLayout | Type: LoadException | Message:
|
||||
/home/user/Documents/SAIT/Winter%202026/intellij/group-2-threaded-project-petshop-desktop/target/classes/org/example/petshopdesktop/main-layout-view.fxml:48
|
||||
| Context: Loading main application layout after successful login
|
||||
[2026-03-02 12:37:53] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure
|
||||
|
||||
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection
|
||||
[2026-03-02 12:37:53] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure
|
||||
|
||||
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection
|
||||
[2026-03-02 12:41:14] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure
|
||||
|
||||
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection
|
||||
[2026-03-02 12:41:29] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: SQLSyntaxErrorException | Message: Unknown column 's.isRefund' in 'field list' | Context: Loading sales
|
||||
[2026-03-02 12:41:43] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: SQLSyntaxErrorException | Message: Unknown column 's.isRefund' in 'field list' | Context: Loading sales
|
||||
[2026-03-02 12:41:46] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: SQLSyntaxErrorException | Message: Unknown column 's.isRefund' in 'field list' | Context: Loading sales
|
||||
[2026-03-02 12:41:50] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: SQLSyntaxErrorException | Message: Unknown column 's.isRefund' in 'field list' | Context: Loading sales
|
||||
[2026-03-02 12:42:11] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: SQLSyntaxErrorException | Message: Unknown column 's.isRefund' in 'field list' | Context: Loading sales
|
||||
[2026-03-02 13:00:16] [ERROR] EXCEPTION | Location: ConnectionDB.getConnection | Type: CommunicationsException | Message: Communications link failure
|
||||
|
||||
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. | Context: Establishing database connection
|
||||
[2026-03-02 13:02:48] [INSERT] DB_INSERT | Table: sale | ID: Refund ID: 24 | Details: Created refund for sale ID 23 with 1 items, total: $240.00
|
||||
|
||||
@@ -154,6 +154,8 @@ public class AnalyticsController {
|
||||
);
|
||||
chartPaymentMethods.getData().add(slice);
|
||||
}
|
||||
|
||||
chartPaymentMethods.setLabelsVisible(false);
|
||||
}
|
||||
|
||||
private void loadEmployeePerformance() throws Exception {
|
||||
|
||||
@@ -9,6 +9,8 @@ import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Separator;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
@@ -73,6 +75,9 @@ public class MainLayoutController {
|
||||
@FXML
|
||||
private Button btnChat;
|
||||
|
||||
@FXML
|
||||
private ImageView imgLogo;
|
||||
|
||||
@FXML
|
||||
private Label lblUsername;
|
||||
|
||||
@@ -166,6 +171,12 @@ public class MainLayoutController {
|
||||
updateButtons(btnChat);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void logoClicked(MouseEvent event) {
|
||||
loadView("analytics-view.fxml");
|
||||
updateButtons(btnAnalytics);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnLogoutClicked(ActionEvent event) {
|
||||
UserSession.getInstance().logout();
|
||||
|
||||
@@ -5,6 +5,8 @@ import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
@@ -17,6 +19,8 @@ import javafx.scene.control.TableView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.database.InventoryDB;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
@@ -38,6 +42,9 @@ public class SaleController {
|
||||
@FXML
|
||||
private Button btnRefresh;
|
||||
|
||||
@FXML
|
||||
private Button btnRefund;
|
||||
|
||||
@FXML
|
||||
private Label lblModeNote;
|
||||
|
||||
@@ -319,6 +326,30 @@ public class SaleController {
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnRefund(ActionEvent event) {
|
||||
openRefundDialog();
|
||||
}
|
||||
|
||||
private void openRefundDialog() {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||
"/org/example/petshopdesktop/dialogviews/refund-dialog-view.fxml"));
|
||||
Stage dialog = new Stage();
|
||||
dialog.initOwner(btnRefund.getScene().getWindow());
|
||||
dialog.initModality(Modality.APPLICATION_MODAL);
|
||||
dialog.setTitle("Process Refund");
|
||||
dialog.setScene(new Scene(loader.load()));
|
||||
dialog.setResizable(false);
|
||||
dialog.showAndWait();
|
||||
|
||||
refreshInventory();
|
||||
refreshSales(true);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("SaleController.openRefundDialog", e, "Opening refund dialog");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCartTotal() {
|
||||
double total = cartItems.stream().mapToDouble(SaleCartItem::getTotal).sum();
|
||||
lblCartTotal.setText(currency.format(total));
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.database.SaleDB;
|
||||
import org.example.petshopdesktop.models.SaleCartItem;
|
||||
import org.example.petshopdesktop.models.SaleDetail;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.text.NumberFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RefundDialogController {
|
||||
|
||||
@FXML
|
||||
private TextField txtSaleId;
|
||||
|
||||
@FXML
|
||||
private Button btnLoadSale;
|
||||
|
||||
@FXML
|
||||
private Label lblSaleInfo;
|
||||
|
||||
@FXML
|
||||
private TableView<SaleDetail.SaleDetailItem> tvOriginalItems;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, String> colOriginalProduct;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Integer> colOriginalQuantity;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Double> colOriginalUnitPrice;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Double> colOriginalTotal;
|
||||
|
||||
@FXML
|
||||
private Button btnAddToRefund;
|
||||
|
||||
@FXML
|
||||
private TableView<RefundItem> tvRefundItems;
|
||||
|
||||
@FXML
|
||||
private TableColumn<RefundItem, String> colRefundProduct;
|
||||
|
||||
@FXML
|
||||
private TableColumn<RefundItem, Integer> colRefundQuantity;
|
||||
|
||||
@FXML
|
||||
private TableColumn<RefundItem, Double> colRefundUnitPrice;
|
||||
|
||||
@FXML
|
||||
private TableColumn<RefundItem, Double> colRefundTotal;
|
||||
|
||||
@FXML
|
||||
private Button btnRemoveFromRefund;
|
||||
|
||||
@FXML
|
||||
private ComboBox<String> cbPaymentMethod;
|
||||
|
||||
@FXML
|
||||
private Label lblRefundTotal;
|
||||
|
||||
@FXML
|
||||
private Button btnProcessRefund;
|
||||
|
||||
@FXML
|
||||
private Button btnCancel;
|
||||
|
||||
private SaleDetail currentSale;
|
||||
private final ObservableList<RefundItem> refundItems = FXCollections.observableArrayList();
|
||||
private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA);
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
setupTables();
|
||||
cbPaymentMethod.setItems(FXCollections.observableArrayList("Cash", "Card", "Debit"));
|
||||
cbPaymentMethod.getSelectionModel().selectFirst();
|
||||
updateRefundTotal();
|
||||
}
|
||||
|
||||
private void setupTables() {
|
||||
colOriginalProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
|
||||
colOriginalQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
|
||||
colOriginalUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
|
||||
colOriginalTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
|
||||
tvOriginalItems.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
|
||||
|
||||
colRefundProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
|
||||
colRefundQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
|
||||
colRefundUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
|
||||
colRefundTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
|
||||
tvRefundItems.setItems(refundItems);
|
||||
tvRefundItems.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnLoadSaleClicked(ActionEvent event) {
|
||||
String saleIdText = txtSaleId.getText().trim();
|
||||
if (saleIdText.isEmpty()) {
|
||||
showError("Load Sale", "Enter a transaction ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
int saleId;
|
||||
try {
|
||||
saleId = Integer.parseInt(saleIdText);
|
||||
} catch (NumberFormatException e) {
|
||||
showError("Load Sale", "Invalid transaction ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (SaleDB.isRefunded(saleId)) {
|
||||
showError("Load Sale", "This sale has already been refunded.");
|
||||
return;
|
||||
}
|
||||
|
||||
currentSale = SaleDB.getSaleById(saleId);
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
String saleInfo = String.format("Sale Date: %s | Employee: %s | Original Total: %s | Payment: %s",
|
||||
currentSale.getSaleDate().format(formatter),
|
||||
currentSale.getEmployeeName(),
|
||||
currency.format(currentSale.getTotalAmount()),
|
||||
currentSale.getPaymentMethod());
|
||||
lblSaleInfo.setText(saleInfo);
|
||||
|
||||
tvOriginalItems.setItems(currentSale.getItems());
|
||||
cbPaymentMethod.getSelectionModel().select(currentSale.getPaymentMethod());
|
||||
|
||||
refundItems.clear();
|
||||
updateRefundTotal();
|
||||
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException("RefundDialogController.btnLoadSaleClicked", e, "Loading sale");
|
||||
showError("Load Sale", e.getMessage() != null ? e.getMessage() : "Could not load sale.");
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnAddToRefundClicked(ActionEvent event) {
|
||||
if (currentSale == null) {
|
||||
showError("Add to Refund", "Load a sale first.");
|
||||
return;
|
||||
}
|
||||
|
||||
SaleDetail.SaleDetailItem selected = tvOriginalItems.getSelectionModel().getSelectedItem();
|
||||
if (selected == null) {
|
||||
showError("Add to Refund", "Select an item from the original sale.");
|
||||
return;
|
||||
}
|
||||
|
||||
int alreadyRefunded = refundItems.stream()
|
||||
.filter(r -> r.getProdId() == selected.getProdId())
|
||||
.mapToInt(RefundItem::getQuantity)
|
||||
.sum();
|
||||
|
||||
int available = selected.getQuantity() - alreadyRefunded;
|
||||
if (available <= 0) {
|
||||
showError("Add to Refund", "All items of this product are already in the refund list.");
|
||||
return;
|
||||
}
|
||||
|
||||
TextInputDialog dialog = new TextInputDialog(String.valueOf(available));
|
||||
dialog.setTitle("Refund Quantity");
|
||||
dialog.setHeaderText("Product: " + selected.getProductName());
|
||||
dialog.setContentText("Enter quantity to refund (max " + available + "):");
|
||||
|
||||
Optional<String> result = dialog.showAndWait();
|
||||
if (result.isPresent()) {
|
||||
try {
|
||||
int quantity = Integer.parseInt(result.get().trim());
|
||||
if (quantity <= 0) {
|
||||
showError("Add to Refund", "Quantity must be at least 1.");
|
||||
return;
|
||||
}
|
||||
if (quantity > available) {
|
||||
showError("Add to Refund", "Cannot refund more than " + available + " items.");
|
||||
return;
|
||||
}
|
||||
|
||||
refundItems.add(new RefundItem(
|
||||
selected.getProdId(),
|
||||
selected.getProductName(),
|
||||
quantity,
|
||||
selected.getUnitPrice()
|
||||
));
|
||||
updateRefundTotal();
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
showError("Add to Refund", "Invalid quantity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnRemoveFromRefundClicked(ActionEvent event) {
|
||||
RefundItem selected = tvRefundItems.getSelectionModel().getSelectedItem();
|
||||
if (selected != null) {
|
||||
refundItems.remove(selected);
|
||||
updateRefundTotal();
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnProcessRefundClicked(ActionEvent event) {
|
||||
if (currentSale == null) {
|
||||
showError("Process Refund", "Load a sale first.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (refundItems.isEmpty()) {
|
||||
showError("Process Refund", "Add at least one item to refund.");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer employeeId = UserSession.getInstance().getEmployeeId();
|
||||
if (employeeId == null || employeeId <= 0) {
|
||||
showError("Process Refund", "Employee is not set for this account.");
|
||||
return;
|
||||
}
|
||||
|
||||
String payment = cbPaymentMethod.getSelectionModel().getSelectedItem();
|
||||
if (payment == null || payment.isBlank()) {
|
||||
showError("Process Refund", "Select a payment method.");
|
||||
return;
|
||||
}
|
||||
|
||||
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
confirm.setTitle("Confirm Refund");
|
||||
confirm.setHeaderText("Process refund for sale ID " + currentSale.getSaleId() + "?");
|
||||
confirm.setContentText("Refund amount: " + lblRefundTotal.getText());
|
||||
|
||||
Optional<ButtonType> confirmResult = confirm.showAndWait();
|
||||
if (confirmResult.isEmpty() || confirmResult.get() != ButtonType.OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ObservableList<SaleCartItem> cartItems = FXCollections.observableArrayList();
|
||||
for (RefundItem item : refundItems) {
|
||||
cartItems.add(new SaleCartItem(item.getProdId(), item.getProductName(), item.getQuantity(), item.getUnitPrice()));
|
||||
}
|
||||
|
||||
int refundId = SaleDB.createRefund(currentSale.getSaleId(), employeeId, payment, cartItems);
|
||||
|
||||
Alert success = new Alert(Alert.AlertType.INFORMATION);
|
||||
success.setTitle("Refund Processed");
|
||||
success.setHeaderText(null);
|
||||
success.setContentText("Refund ID " + refundId + " was created successfully.");
|
||||
success.showAndWait();
|
||||
|
||||
closeDialog();
|
||||
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException("RefundDialogController.btnProcessRefundClicked", e, "Processing refund");
|
||||
showError("Process Refund", e.getMessage() != null ? e.getMessage() : "Could not process refund.");
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnCancelClicked(ActionEvent event) {
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
private void updateRefundTotal() {
|
||||
double total = refundItems.stream().mapToDouble(RefundItem::getTotal).sum();
|
||||
lblRefundTotal.setText(currency.format(total));
|
||||
}
|
||||
|
||||
private void closeDialog() {
|
||||
Stage stage = (Stage) btnCancel.getScene().getWindow();
|
||||
stage.close();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static class RefundItem {
|
||||
private final int prodId;
|
||||
private final String productName;
|
||||
private final int quantity;
|
||||
private final double unitPrice;
|
||||
|
||||
public RefundItem(int prodId, String productName, int quantity, double unitPrice) {
|
||||
this.prodId = prodId;
|
||||
this.productName = productName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
|
||||
public int getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public double getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
|
||||
public double getTotal() {
|
||||
return quantity * unitPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.DTOs.SaleDTO;
|
||||
import org.example.petshopdesktop.models.SaleCartItem;
|
||||
import org.example.petshopdesktop.models.SaleDetail;
|
||||
import org.example.petshopdesktop.models.SaleLineItem;
|
||||
import org.example.petshopdesktop.models.analytics.*;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
@@ -129,7 +130,8 @@ public class SaleDB {
|
||||
si.quantity,
|
||||
si.unitPrice,
|
||||
(si.quantity * si.unitPrice) as total,
|
||||
s.paymentMethod
|
||||
s.paymentMethod,
|
||||
s.isRefund
|
||||
FROM sale s
|
||||
JOIN saleItem si ON s.saleId = si.saleId
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
@@ -149,7 +151,8 @@ public class SaleDB {
|
||||
rs.getInt("quantity"),
|
||||
rs.getDouble("unitPrice"),
|
||||
rs.getDouble("total"),
|
||||
rs.getString("paymentMethod")
|
||||
rs.getString("paymentMethod"),
|
||||
rs.getBoolean("isRefund")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -397,4 +400,173 @@ public class SaleDB {
|
||||
conn.close();
|
||||
return summary;
|
||||
}
|
||||
|
||||
public static SaleDetail getSaleById(int saleId) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String saleSql = """
|
||||
SELECT s.saleId, s.saleDate, s.totalAmount, s.paymentMethod,
|
||||
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
|
||||
s.isRefund
|
||||
FROM sale s
|
||||
JOIN employee e ON s.employeeId = e.employeeId
|
||||
WHERE s.saleId = ?
|
||||
""";
|
||||
|
||||
PreparedStatement saleStmt = conn.prepareStatement(saleSql);
|
||||
saleStmt.setInt(1, saleId);
|
||||
ResultSet saleRs = saleStmt.executeQuery();
|
||||
|
||||
if (!saleRs.next()) {
|
||||
conn.close();
|
||||
throw new SQLException("Sale not found with ID: " + saleId);
|
||||
}
|
||||
|
||||
boolean isRefund = saleRs.getBoolean("isRefund");
|
||||
if (isRefund) {
|
||||
conn.close();
|
||||
throw new SQLException("Cannot refund a refund transaction");
|
||||
}
|
||||
|
||||
SaleDetail detail = new SaleDetail(
|
||||
saleRs.getInt("saleId"),
|
||||
saleRs.getTimestamp("saleDate").toLocalDateTime(),
|
||||
saleRs.getDouble("totalAmount"),
|
||||
saleRs.getString("paymentMethod"),
|
||||
saleRs.getString("employeeName"),
|
||||
FXCollections.observableArrayList()
|
||||
);
|
||||
|
||||
String itemsSql = """
|
||||
SELECT si.prodId, p.prodName, si.quantity, si.unitPrice,
|
||||
(si.quantity * si.unitPrice) as total
|
||||
FROM saleItem si
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
WHERE si.saleId = ?
|
||||
""";
|
||||
|
||||
PreparedStatement itemsStmt = conn.prepareStatement(itemsSql);
|
||||
itemsStmt.setInt(1, saleId);
|
||||
ResultSet itemsRs = itemsStmt.executeQuery();
|
||||
|
||||
while (itemsRs.next()) {
|
||||
detail.getItems().add(new SaleDetail.SaleDetailItem(
|
||||
itemsRs.getInt("prodId"),
|
||||
itemsRs.getString("prodName"),
|
||||
itemsRs.getInt("quantity"),
|
||||
itemsRs.getDouble("unitPrice"),
|
||||
itemsRs.getDouble("total")
|
||||
));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return detail;
|
||||
}
|
||||
|
||||
public static boolean isRefunded(int saleId) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT COUNT(*) as refundCount
|
||||
FROM sale
|
||||
WHERE originalSaleId = ? AND isRefund = TRUE
|
||||
""";
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||||
pstmt.setInt(1, saleId);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
|
||||
boolean refunded = false;
|
||||
if (rs.next()) {
|
||||
refunded = rs.getInt("refundCount") > 0;
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return refunded;
|
||||
}
|
||||
|
||||
public static int createRefund(int originalSaleId, int employeeId, String paymentMethod, ObservableList<SaleCartItem> refundItems) throws SQLException {
|
||||
if (refundItems.isEmpty()) {
|
||||
throw new SQLException("Cannot create refund with empty items");
|
||||
}
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
SaleDetail originalSale = getSaleById(originalSaleId);
|
||||
conn = ConnectionDB.getConnection();
|
||||
|
||||
if (isRefunded(originalSaleId)) {
|
||||
throw new SQLException("This sale has already been refunded");
|
||||
}
|
||||
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
try {
|
||||
double totalAmount = -refundItems.stream().mapToDouble(SaleCartItem::getTotal).sum();
|
||||
|
||||
String insertSale = """
|
||||
INSERT INTO sale (saleDate, totalAmount, paymentMethod, employeeId, storeId, isRefund, originalSaleId)
|
||||
VALUES (NOW(), ?, ?, ?, 1, TRUE, ?)
|
||||
""";
|
||||
|
||||
PreparedStatement saleStmt = conn.prepareStatement(insertSale, Statement.RETURN_GENERATED_KEYS);
|
||||
saleStmt.setDouble(1, totalAmount);
|
||||
saleStmt.setString(2, paymentMethod);
|
||||
saleStmt.setInt(3, employeeId);
|
||||
saleStmt.setInt(4, originalSaleId);
|
||||
saleStmt.executeUpdate();
|
||||
|
||||
ResultSet rs = saleStmt.getGeneratedKeys();
|
||||
if (!rs.next()) {
|
||||
throw new SQLException("Failed to get generated refund ID");
|
||||
}
|
||||
int refundId = rs.getInt(1);
|
||||
|
||||
String insertItem = """
|
||||
INSERT INTO saleItem (saleId, prodId, quantity, unitPrice)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
String updateInventory = """
|
||||
UPDATE inventory
|
||||
SET quantity = quantity + ?
|
||||
WHERE prodId = ?
|
||||
""";
|
||||
|
||||
PreparedStatement itemStmt = conn.prepareStatement(insertItem);
|
||||
PreparedStatement invStmt = conn.prepareStatement(updateInventory);
|
||||
|
||||
for (SaleCartItem item : refundItems) {
|
||||
itemStmt.setInt(1, refundId);
|
||||
itemStmt.setInt(2, item.getProdId());
|
||||
itemStmt.setInt(3, -item.getQuantity());
|
||||
itemStmt.setDouble(4, item.getUnitPrice());
|
||||
itemStmt.executeUpdate();
|
||||
|
||||
invStmt.setInt(1, item.getQuantity());
|
||||
invStmt.setInt(2, item.getProdId());
|
||||
int updated = invStmt.executeUpdate();
|
||||
|
||||
if (updated == 0) {
|
||||
throw new SQLException("Failed to update inventory for product ID " + item.getProdId());
|
||||
}
|
||||
}
|
||||
|
||||
conn.commit();
|
||||
|
||||
ActivityLogger.getInstance().logInsert("sale",
|
||||
String.format("Refund ID: %d", refundId),
|
||||
String.format("Created refund for sale ID %d with %d items, total: $%.2f", originalSaleId, refundItems.size(), Math.abs(totalAmount)));
|
||||
|
||||
return refundId;
|
||||
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
ActivityLogger.getInstance().logException("SaleDB.createRefund", e, "Creating refund");
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(true);
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.example.petshopdesktop.models;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SaleDetail {
|
||||
private final int saleId;
|
||||
private final LocalDateTime saleDate;
|
||||
private final double totalAmount;
|
||||
private final String paymentMethod;
|
||||
private final String employeeName;
|
||||
private final ObservableList<SaleDetailItem> items;
|
||||
|
||||
public SaleDetail(int saleId, LocalDateTime saleDate, double totalAmount, String paymentMethod, String employeeName, ObservableList<SaleDetailItem> items) {
|
||||
this.saleId = saleId;
|
||||
this.saleDate = saleDate;
|
||||
this.totalAmount = totalAmount;
|
||||
this.paymentMethod = paymentMethod;
|
||||
this.employeeName = employeeName;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public int getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public LocalDateTime getSaleDate() {
|
||||
return saleDate;
|
||||
}
|
||||
|
||||
public double getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
|
||||
public ObservableList<SaleDetailItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public static class SaleDetailItem {
|
||||
private final int prodId;
|
||||
private final String productName;
|
||||
private final int quantity;
|
||||
private final double unitPrice;
|
||||
private final double total;
|
||||
|
||||
public SaleDetailItem(int prodId, String productName, int quantity, double unitPrice, double total) {
|
||||
this.prodId = prodId;
|
||||
this.productName = productName;
|
||||
this.quantity = quantity;
|
||||
this.unitPrice = unitPrice;
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public int getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public int getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public double getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
|
||||
public double getTotal() {
|
||||
return total;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@ public class SaleLineItem {
|
||||
private final double unitPrice;
|
||||
private final double total;
|
||||
private final String paymentMethod;
|
||||
private final boolean isRefund;
|
||||
|
||||
public SaleLineItem(int saleId, String saleDate, String employeeName, String itemName, int quantity, double unitPrice, double total, String paymentMethod) {
|
||||
public SaleLineItem(int saleId, String saleDate, String employeeName, String itemName, int quantity, double unitPrice, double total, String paymentMethod, boolean isRefund) {
|
||||
this.saleId = saleId;
|
||||
this.saleDate = saleDate;
|
||||
this.employeeName = employeeName;
|
||||
@@ -19,6 +20,7 @@ public class SaleLineItem {
|
||||
this.unitPrice = unitPrice;
|
||||
this.total = total;
|
||||
this.paymentMethod = paymentMethod;
|
||||
this.isRefund = isRefund;
|
||||
}
|
||||
|
||||
public int getSaleId() {
|
||||
@@ -52,4 +54,8 @@ public class SaleLineItem {
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public boolean isRefund() {
|
||||
return isRefund;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.scene.control.TableColumn?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="900.0" spacing="20.0" style="-fx-font-size: 14px;" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.petshopdesktop.controllers.dialogcontrollers.RefundDialogController">
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<HBox alignment="CENTER_LEFT" spacing="20.0" style="-fx-background-color: #FF6b6b; -fx-background-radius: 14;">
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<VBox alignment="CENTER_LEFT">
|
||||
<children>
|
||||
<Label text="Process Refund" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="Comic Sans MS Bold" size="30.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</children>
|
||||
</VBox>
|
||||
<Region HBox.hgrow="ALWAYS" />
|
||||
<Button fx:id="btnCancel" mnemonicParsing="false" onAction="#btnCancelClicked" style="-fx-background-color: #E74c3c; -fx-cursor: hand; -fx-background-radius: 8;" text="Cancel" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="12.0" left="24.0" right="24.0" top="12.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
|
||||
<VBox spacing="15.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #FF6b6b; -fx-border-radius: 14;">
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<Label text="Load Sale" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="18.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<HBox alignment="CENTER_LEFT" spacing="10.0">
|
||||
<children>
|
||||
<Label text="Transaction ID:" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<TextField fx:id="txtSaleId" prefWidth="150.0" promptText="Enter sale ID" style="-fx-border-color: #E8EBED; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-radius: 10;">
|
||||
<padding>
|
||||
<Insets bottom="7.0" left="10.0" right="10.0" top="7.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<Button fx:id="btnLoadSale" mnemonicParsing="false" onAction="#btnLoadSaleClicked" style="-fx-background-color: #4ECDC4; -fx-cursor: hand; -fx-background-radius: 8;" text="Load Sale" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="8.0" left="18.0" right="18.0" top="8.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<Label fx:id="lblSaleInfo" text="" textFill="#7f8c8d">
|
||||
<font>
|
||||
<Font size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="10.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #FF6b6b; -fx-border-radius: 14;" VBox.vgrow="ALWAYS">
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<HBox alignment="CENTER_LEFT" spacing="10.0">
|
||||
<children>
|
||||
<Label text="Original Items" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="16.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Region HBox.hgrow="ALWAYS" />
|
||||
<Button fx:id="btnAddToRefund" mnemonicParsing="false" onAction="#btnAddToRefundClicked" style="-fx-background-color: #FF6b6b; -fx-cursor: hand; -fx-background-radius: 8;" text="Add to Refund" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="8.0" left="18.0" right="18.0" top="8.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<TableView fx:id="tvOriginalItems" prefHeight="150.0" style="-fx-background-color: white; -fx-background-radius: 10;">
|
||||
<columns>
|
||||
<TableColumn fx:id="colOriginalProduct" prefWidth="350.0" text="Product Name" />
|
||||
<TableColumn fx:id="colOriginalQuantity" prefWidth="120.0" text="Quantity" />
|
||||
<TableColumn fx:id="colOriginalUnitPrice" prefWidth="150.0" text="Unit Price" />
|
||||
<TableColumn fx:id="colOriginalTotal" prefWidth="150.0" text="Total" />
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
<Separator />
|
||||
|
||||
<HBox alignment="CENTER_LEFT" spacing="10.0">
|
||||
<children>
|
||||
<Label text="Refund Items" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="16.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Region HBox.hgrow="ALWAYS" />
|
||||
<Button fx:id="btnRemoveFromRefund" mnemonicParsing="false" onAction="#btnRemoveFromRefundClicked" style="-fx-background-color: #34495E; -fx-cursor: hand; -fx-background-radius: 8;" text="Remove Selected" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="13.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="8.0" left="18.0" right="18.0" top="8.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<TableView fx:id="tvRefundItems" prefHeight="150.0" style="-fx-background-color: white; -fx-background-radius: 10;">
|
||||
<columns>
|
||||
<TableColumn fx:id="colRefundProduct" prefWidth="350.0" text="Product Name" />
|
||||
<TableColumn fx:id="colRefundQuantity" prefWidth="120.0" text="Quantity" />
|
||||
<TableColumn fx:id="colRefundUnitPrice" prefWidth="150.0" text="Unit Price" />
|
||||
<TableColumn fx:id="colRefundTotal" prefWidth="150.0" text="Total" />
|
||||
</columns>
|
||||
</TableView>
|
||||
</children>
|
||||
</VBox>
|
||||
|
||||
<VBox spacing="12.0" style="-fx-background-color: white; -fx-background-radius: 14; -fx-border-width: 2; -fx-border-color: #FF6b6b; -fx-border-radius: 14;">
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<HBox alignment="CENTER_LEFT" spacing="15.0">
|
||||
<children>
|
||||
<Label text="Payment Method:" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<ComboBox fx:id="cbPaymentMethod" prefWidth="160.0" style="-fx-border-color: #E8EBED; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-radius: 10; -fx-background-color: white;">
|
||||
<padding>
|
||||
<Insets bottom="3.0" left="10.0" right="10.0" top="3.0" />
|
||||
</padding>
|
||||
</ComboBox>
|
||||
<Region HBox.hgrow="ALWAYS" />
|
||||
<Label text="Refund Total:" textFill="#2c3e50">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Label fx:id="lblRefundTotal" text="\$0.00" textFill="#FF6b6b">
|
||||
<font>
|
||||
<Font name="System Bold" size="18.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Region prefWidth="20.0" />
|
||||
<Button fx:id="btnProcessRefund" mnemonicParsing="false" onAction="#btnProcessRefundClicked" style="-fx-background-color: #FF6b6b; -fx-cursor: hand; -fx-background-radius: 8;" text="Process Refund" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="12.0" left="24.0" right="24.0" top="12.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</VBox>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 538 KiB |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 512 512" role="img" aria-label="Leon's Pet Store logo">
|
||||
<defs>
|
||||
<style>
|
||||
.ring { fill: none; stroke: #A83B2B; stroke-width: 18; }
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<circle class="ring" cx="256" cy="256" r="236"/>
|
||||
|
||||
<g>
|
||||
<path d="M 174.96 419.66 C 166.94 420.40 157.23 419.87 150.27 417.76 C 143.30 415.66 137.45 410.43 133.17 407.00 C 128.90 403.57 127.42 402.52 124.63 397.19 C 121.83 391.86 117.34 384.53 116.40 375.03 C 115.45 365.53 117.03 349.39 118.93 340.21 C 120.83 331.03 123.36 327.65 127.79 319.95 C 132.22 312.24 132.49 307.76 145.52 293.99 C 158.55 280.22 190.84 249.93 205.98 237.32 C 221.13 224.71 224.98 222.13 236.37 218.33 C 247.77 214.53 264.76 214.32 274.36 214.53 C 283.97 214.74 287.76 217.06 293.99 219.60 C 300.21 222.13 297.00 218.38 311.72 229.73 C 326.44 241.07 368.43 275.36 382.31 287.66 C 396.19 299.95 391.17 297.36 394.97 303.48 C 398.77 309.61 402.57 317.20 405.10 324.38 C 407.63 331.55 409.64 338.52 410.17 346.54 C 410.69 354.56 410.59 364.79 408.27 372.50 C 405.94 380.20 401.78 387.01 396.24 392.76 C 390.70 398.50 381.41 403.99 375.03 407.00 C 368.65 410.01 363.32 410.48 357.93 410.80 C 352.55 411.12 352.44 412.07 342.74 408.90 C 333.03 405.74 313.93 395.29 299.69 391.81 C 285.44 388.32 268.03 387.69 257.27 388.01 C 246.50 388.32 244.92 389.48 235.11 393.71 C 225.29 397.93 208.41 409.01 198.39 413.33 C 188.36 417.66 182.98 418.92 174.96 419.66 Z" fill="#E75D40" fill-rule="evenodd"/>
|
||||
<path d="M 205.98 202.50 C 201.44 203.56 200.28 203.66 195.22 201.87 C 190.15 200.08 181.56 196.85 175.59 191.74 C 169.63 186.62 163.09 177.44 159.45 171.16 C 155.81 164.88 154.17 162.09 153.75 154.07 C 153.33 146.04 154.12 131.12 156.92 123.04 C 159.71 114.97 166.04 109.70 170.53 105.63 C 175.01 101.57 179.39 99.93 183.82 98.67 C 188.26 97.40 191.74 96.77 197.12 98.03 C 202.50 99.30 210.68 102.31 216.11 106.26 C 221.55 110.22 226.08 116.03 229.73 121.78 C 233.37 127.53 236.37 134.65 237.96 140.77 C 239.54 146.89 240.06 151.53 239.22 158.50 C 238.38 165.46 235.69 176.38 232.89 182.56 C 230.09 188.73 226.93 192.21 222.44 195.54 C 217.96 198.86 210.52 201.44 205.98 202.50 Z" fill="#E75D40" fill-rule="evenodd"/>
|
||||
<path d="M 314.25 195.54 C 307.92 197.44 301.11 196.85 295.25 194.90 C 289.40 192.95 283.28 187.89 279.11 183.82 C 274.94 179.76 272.35 177.18 270.25 170.53 C 268.14 163.88 266.45 151.32 266.45 143.94 C 266.45 136.55 267.92 132.43 270.25 126.21 C 272.57 119.98 276.21 111.70 280.38 106.58 C 284.54 101.47 290.45 97.88 295.25 95.50 C 300.05 93.13 303.80 92.23 309.18 92.34 C 314.56 92.44 322.59 94.03 327.54 96.13 C 332.50 98.24 335.19 99.25 338.94 105.00 C 342.69 110.75 348.28 122.36 350.02 130.64 C 351.76 138.92 350.86 147.63 349.39 154.70 C 347.91 161.77 343.85 168.26 341.16 173.06 C 338.47 177.86 337.72 179.76 333.24 183.51 C 328.76 187.25 320.58 193.64 314.25 195.54 Z" fill="#E75D40" fill-rule="evenodd"/>
|
||||
<path d="M 126.84 268.98 C 122.30 269.72 118.29 269.40 113.55 268.35 C 108.80 267.29 103.37 265.76 98.35 262.65 C 93.34 259.53 87.95 255.42 83.47 249.67 C 78.99 243.92 73.55 236.69 71.44 228.14 C 69.33 219.60 70.07 205.03 70.81 198.39 C 71.55 191.74 71.92 192.32 75.87 188.26 C 79.83 184.19 88.70 176.59 94.55 174.01 C 100.41 171.43 105.11 171.69 111.01 172.74 C 116.92 173.80 123.09 174.69 130.01 180.34 C 136.92 185.99 148.00 198.86 152.48 206.62 C 156.97 214.37 156.81 219.80 156.92 226.88 C 157.02 233.95 155.81 242.86 153.12 249.04 C 150.43 255.21 145.15 260.59 140.77 263.91 C 136.39 267.24 131.38 268.24 126.84 268.98 Z" fill="#E75D40" fill-rule="evenodd"/>
|
||||
<path d="M 396.55 256.32 C 389.06 257.79 380.88 255.63 375.66 253.78 C 370.44 251.94 368.33 249.51 365.21 245.24 C 362.10 240.96 358.36 234.68 356.98 228.14 C 355.61 221.60 355.83 212.74 356.98 205.98 C 358.14 199.23 360.42 193.59 363.95 187.62 C 367.48 181.66 372.65 174.80 378.19 170.21 C 383.73 165.62 391.17 161.77 397.19 160.08 C 403.20 158.39 407.90 157.60 414.28 160.08 C 420.66 162.56 430.79 169.42 435.49 174.96 C 440.19 180.50 441.51 186.99 442.46 193.32 C 443.41 199.65 443.20 205.98 441.19 212.95 C 439.18 219.91 433.86 229.78 430.43 235.11 C 427.00 240.44 426.26 241.39 420.61 244.92 C 414.97 248.45 404.04 254.84 396.55 256.32 Z" fill="#E75D40" fill-rule="evenodd"/>
|
||||
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -5,6 +5,8 @@
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
@@ -17,14 +19,14 @@
|
||||
<Insets bottom="40.0" left="50.0" right="50.0" top="40.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<Label text="Pet Shop Manager" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="22.0" />
|
||||
</font>
|
||||
<ImageView fitHeight="180.0" fitWidth="320.0" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@images/leons-pet-store-badge-text.png" />
|
||||
</image>
|
||||
<VBox.margin>
|
||||
<Insets bottom="10.0" />
|
||||
<Insets bottom="35.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
</ImageView>
|
||||
|
||||
<Label text="Username" textFill="#cccccc">
|
||||
<font>
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
@@ -19,6 +22,16 @@
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
<children>
|
||||
<HBox alignment="CENTER">
|
||||
<ImageView fx:id="imgLogo" fitHeight="90.0" fitWidth="90.0" preserveRatio="true" onMouseClicked="#logoClicked" style="-fx-cursor: hand;">
|
||||
<image>
|
||||
<Image url="@images/leons-pet-store-badge.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
<VBox.margin>
|
||||
<Insets bottom="15.0" />
|
||||
</VBox.margin>
|
||||
</HBox>
|
||||
<Label fx:id="lblUsername" text="Name" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="18.0" />
|
||||
|
||||
@@ -38,6 +38,14 @@
|
||||
</padding>
|
||||
</Label>
|
||||
<Region prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
|
||||
<Button fx:id="btnRefund" mnemonicParsing="false" onAction="#btnRefund" prefHeight="44.0" style="-fx-background-color: #FF6b6b; -fx-cursor: hand; -fx-background-radius: 8;" text="Process Refund" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="12.0" left="24.0" right="24.0" top="12.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
<Button fx:id="btnRefresh" mnemonicParsing="false" onAction="#btnRefresh" prefHeight="44.0" prefWidth="118.0" style="-fx-background-color: #4ECDC4; -fx-cursor: hand; -fx-background-radius: 8;" text="Refresh" textFill="WHITE">
|
||||
<font>
|
||||
<Font name="System Bold" size="14.0" />
|
||||
@@ -145,14 +153,14 @@
|
||||
</HBox>
|
||||
<TableView fx:id="tvSales" prefHeight="362.0" prefWidth="752.0" style="-fx-background-color: white; -fx-background-radius: 12;" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn fx:id="colSaleId" prefWidth="50.0" text="ID" />
|
||||
<TableColumn fx:id="colSaleDate" prefWidth="150.0" text="Date" />
|
||||
<TableColumn fx:id="colEmployeeName" prefWidth="180.0" text="Employee" />
|
||||
<TableColumn fx:id="colServiceProduct" prefWidth="170.0" text="Product" />
|
||||
<TableColumn fx:id="colSaleQuantity" prefWidth="80.0" text="Qty" />
|
||||
<TableColumn fx:id="colSaleUnitPrice" prefWidth="100.0" text="Unit Price" />
|
||||
<TableColumn fx:id="colSaleTotal" prefWidth="100.0" text="Total" />
|
||||
<TableColumn fx:id="colSalePaymentType" prefWidth="120.0" text="Payment" />
|
||||
<TableColumn fx:id="colSaleId" prefWidth="40.0" text="ID" />
|
||||
<TableColumn fx:id="colSaleDate" prefWidth="130.0" text="Date" />
|
||||
<TableColumn fx:id="colEmployeeName" prefWidth="140.0" text="Employee" />
|
||||
<TableColumn fx:id="colServiceProduct" prefWidth="150.0" text="Product" />
|
||||
<TableColumn fx:id="colSaleQuantity" prefWidth="50.0" text="Qty" />
|
||||
<TableColumn fx:id="colSaleUnitPrice" prefWidth="90.0" text="Unit Price" />
|
||||
<TableColumn fx:id="colSaleTotal" prefWidth="90.0" text="Total" />
|
||||
<TableColumn fx:id="colSalePaymentType" prefWidth="90.0" text="Payment" />
|
||||
</columns>
|
||||
</TableView>
|
||||
</children>
|
||||
|
||||
Reference in New Issue
Block a user