1
.gitignore
vendored
1
.gitignore
vendored
@@ -40,3 +40,4 @@ build/
|
||||
|
||||
## Database related
|
||||
connectionpetstore.properties
|
||||
.idea/workspace.xml
|
||||
|
||||
394
Petstoredata.sql
394
Petstoredata.sql
@@ -1,394 +0,0 @@
|
||||
DROP DATABASE IF EXISTS Petstoredb;
|
||||
CREATE DATABASE Petstoredb;
|
||||
USE Petstoredb;
|
||||
|
||||
-- Create Tables
|
||||
|
||||
CREATE TABLE storeLocation (
|
||||
storeId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
storeName VARCHAR(100) NOT NULL,
|
||||
address VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE employee (
|
||||
employeeId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
firstName VARCHAR(50) NOT NULL,
|
||||
lastName VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
isActive BOOLEAN DEFAULT TRUE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE employeeStore (
|
||||
employeeId INT NOT NULL,
|
||||
storeId INT NOT NULL,
|
||||
PRIMARY KEY (employeeId, storeId),
|
||||
FOREIGN KEY (employeeId) REFERENCES employee(employeeId),
|
||||
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId)
|
||||
);
|
||||
|
||||
CREATE TABLE customer (
|
||||
customerId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
firstName VARCHAR(50) NOT NULL,
|
||||
lastName VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(100) NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE pet (
|
||||
petId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
petName VARCHAR(50) NOT NULL,
|
||||
petSpecies VARCHAR(50) NOT NULL,
|
||||
petBreed VARCHAR(50) NOT NULL,
|
||||
petAge INT NOT NULL,
|
||||
petStatus VARCHAR(20) NOT NULL,
|
||||
petPrice DECIMAL(10, 2) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE adoption (
|
||||
adoptionId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
petId INT NOT NULL,
|
||||
customerId INT NOT NULL,
|
||||
adoptionDate DATE NOT NULL,
|
||||
adoptionStatus VARCHAR(20) NOT NULL,
|
||||
FOREIGN KEY (petId) REFERENCES pet(petId),
|
||||
FOREIGN KEY (customerId) REFERENCES customer(customerId)
|
||||
);
|
||||
|
||||
CREATE TABLE supplier (
|
||||
supId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
supCompany VARCHAR(100) NOT NULL,
|
||||
supContactFirstName VARCHAR(50) NOT NULL,
|
||||
supContactLastName VARCHAR(50) NOT NULL,
|
||||
supEmail VARCHAR(100) NOT NULL,
|
||||
supPhone VARCHAR(20) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE category (
|
||||
categoryId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
categoryName VARCHAR(100) NOT NULL,
|
||||
categoryType VARCHAR(50) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE product (
|
||||
prodId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
prodName VARCHAR(100) NOT NULL,
|
||||
prodPrice DECIMAL(10, 2) NOT NULL,
|
||||
categoryId INT NOT NULL,
|
||||
prodDesc TEXT,
|
||||
FOREIGN KEY (categoryId) REFERENCES category(categoryId)
|
||||
);
|
||||
|
||||
CREATE TABLE productSupplier (
|
||||
supId INT NOT NULL,
|
||||
prodId INT NOT NULL,
|
||||
cost DECIMAL(10, 2) NOT NULL,
|
||||
PRIMARY KEY (supId, prodId),
|
||||
FOREIGN KEY (supId) REFERENCES supplier(supId),
|
||||
FOREIGN KEY (prodId) REFERENCES product(prodId)
|
||||
);
|
||||
|
||||
CREATE TABLE inventory (
|
||||
inventoryId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
prodId INT NOT NULL,
|
||||
quantity INT DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY (prodId) REFERENCES product(prodId)
|
||||
);
|
||||
|
||||
CREATE TABLE service (
|
||||
serviceId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
serviceName VARCHAR(100) NOT NULL,
|
||||
serviceDesc TEXT,
|
||||
serviceDuration INT NOT NULL,
|
||||
servicePrice DECIMAL(10, 2) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE appointment (
|
||||
appointmentId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
serviceId INT NOT NULL,
|
||||
customerId INT NOT NULL,
|
||||
appointmentDate DATE NOT NULL,
|
||||
appointmentTime TIME NOT NULL,
|
||||
appointmentStatus VARCHAR(20) NOT NULL,
|
||||
FOREIGN KEY (serviceId) REFERENCES service(serviceId),
|
||||
FOREIGN KEY (customerId) REFERENCES customer(customerId)
|
||||
);
|
||||
|
||||
CREATE TABLE appointmentPet (
|
||||
appointmentId INT NOT NULL,
|
||||
petId INT NOT NULL,
|
||||
PRIMARY KEY (appointmentId, petId),
|
||||
FOREIGN KEY (appointmentId) REFERENCES appointment(appointmentId),
|
||||
FOREIGN KEY (petId) REFERENCES pet(petId)
|
||||
);
|
||||
|
||||
CREATE TABLE sale (
|
||||
saleId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
saleDate DATETIME NOT NULL,
|
||||
totalAmount DECIMAL(10, 2) NOT NULL,
|
||||
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 (originalSaleId) REFERENCES sale(saleId)
|
||||
);
|
||||
|
||||
CREATE TABLE saleItem (
|
||||
saleItemId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
saleId INT NOT NULL,
|
||||
prodId INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
unitPrice DECIMAL(10, 2) NOT NULL,
|
||||
FOREIGN KEY (saleId) REFERENCES sale(saleId),
|
||||
FOREIGN KEY (prodId) REFERENCES product(prodId)
|
||||
);
|
||||
|
||||
CREATE TABLE purchaseOrder (
|
||||
purchaseOrderId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
supId INT NOT NULL,
|
||||
orderDate DATE NOT NULL,
|
||||
status VARCHAR(50) NOT NULL,
|
||||
FOREIGN KEY (supId) REFERENCES supplier(supId)
|
||||
);
|
||||
|
||||
CREATE TABLE activityLog (
|
||||
logId INT AUTO_INCREMENT PRIMARY KEY,
|
||||
employeeId INT NOT NULL,
|
||||
activity TEXT NOT NULL,
|
||||
logTimestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (employeeId) REFERENCES employee(employeeId)
|
||||
);
|
||||
|
||||
-- Insert Sample Data
|
||||
|
||||
INSERT INTO storeLocation (storeName, address, phone, email)
|
||||
VALUES
|
||||
('Downtown Branch', '123 Main St', '123-456-7890', 'downtown@petshop.com'),
|
||||
('North Branch', '456 North Ave', '987-654-3210', 'north@petshop.com'),
|
||||
('West Side Store', '789 West Blvd', '555-123-4567', 'westside@petshop.com'),
|
||||
('East End Shop', '321 East Road', '555-987-6543', 'eastend@petshop.com'),
|
||||
('South Mall Location', '654 South Plaza', '555-246-8135', 'southmall@petshop.com');
|
||||
|
||||
INSERT INTO employee (firstName, lastName, email, phone, role, isActive)
|
||||
VALUES
|
||||
('John', 'Doe', 'john@petshop.com', '111-222-3333', 'Manager', TRUE),
|
||||
('Sara', 'Smith', 'sara@petshop.com', '444-555-6666', 'Staff', TRUE),
|
||||
('Michael', 'Johnson', 'michael@petshop.com', '222-333-4444', 'Groomer', TRUE),
|
||||
('Lisa', 'Williams', 'lisa@petshop.com', '333-444-5555', 'Staff', TRUE),
|
||||
('David', 'Brown', 'david@petshop.com', '555-666-7777', 'Veterinarian', TRUE),
|
||||
('Emma', 'Davis', 'emma@petshop.com', '666-777-8888', 'Manager', FALSE);
|
||||
|
||||
INSERT INTO employeeStore (employeeId, storeId)
|
||||
VALUES
|
||||
(1, 1),
|
||||
(2, 1),
|
||||
(2, 2),
|
||||
(3, 2),
|
||||
(4, 3),
|
||||
(5, 1),
|
||||
(5, 4),
|
||||
(6, 5);
|
||||
|
||||
INSERT INTO customer (firstName, lastName, email, phone)
|
||||
VALUES
|
||||
('Alex', 'Brown', 'alex@gmail.com', '777-888-9999'),
|
||||
('Emily', 'Clark', 'emily@gmail.com', '666-555-4444'),
|
||||
('James', 'Wilson', 'james@gmail.com', '888-999-0000'),
|
||||
('Olivia', 'Martinez', 'olivia@gmail.com', '999-000-1111'),
|
||||
('William', 'Anderson', 'william@gmail.com', '000-111-2222'),
|
||||
('Sophia', 'Taylor', 'sophia@gmail.com', '111-222-3333');
|
||||
|
||||
INSERT INTO pet (petName, petSpecies, petBreed, petAge, petStatus, petPrice)
|
||||
VALUES
|
||||
('Buddy', 'Dog', 'Labrador', 2, 'Available', 500.00),
|
||||
('Milo', 'Cat', 'Persian', 1, 'Available', 300.00),
|
||||
('Charlie', 'Dog', 'Golden Retriever', 3, 'Available', 550.00),
|
||||
('Luna', 'Cat', 'Siamese', 2, 'Adopted', 350.00),
|
||||
('Max', 'Dog', 'Beagle', 1, 'Available', 450.00),
|
||||
('Bella', 'Cat', 'Maine Coon', 4, 'Available', 400.00);
|
||||
|
||||
INSERT INTO adoption (petId, customerId, adoptionDate, adoptionStatus)
|
||||
VALUES
|
||||
(1, 1, '2026-01-15', 'Completed'),
|
||||
(4, 3, '2026-01-20', 'Completed'),
|
||||
(2, 2, '2026-01-25', 'Pending'),
|
||||
(5, 4, '2026-02-01', 'Completed'),
|
||||
(6, 5, '2026-02-02', 'Pending');
|
||||
|
||||
INSERT INTO supplier (supCompany, supContactFirstName, supContactLastName, supEmail, supPhone)
|
||||
VALUES
|
||||
('PetFood Inc', 'Robert', 'King', 'contact@petfood.com', '888-111-2222'),
|
||||
('Toy World', 'Jennifer', 'Lee', 'sales@toyworld.com', '888-222-3333'),
|
||||
('Pet Supplies Co', 'Kevin', 'White', 'info@petsupplies.com', '888-333-4444'),
|
||||
('Animal Care Products', 'Nancy', 'Green', 'orders@animalcare.com', '888-444-5555'),
|
||||
('Premium Pet Goods', 'Tom', 'Black', 'support@premiumpet.com', '888-555-6666');
|
||||
|
||||
INSERT INTO category (categoryName, categoryType)
|
||||
VALUES
|
||||
('Dog Food', 'Product'),
|
||||
('Cat Toys', 'Product'),
|
||||
('Bird Supplies', 'Product'),
|
||||
('Aquarium', 'Product'),
|
||||
('Small Animals', 'Product');
|
||||
|
||||
INSERT INTO product (prodName, prodPrice, categoryId, prodDesc)
|
||||
VALUES
|
||||
('Premium Dog Food', 50.00, 1, 'High quality dog food'),
|
||||
('Cat Toy Ball', 10.00, 2, 'Colorful toy for cats'),
|
||||
('Bird Cage Large', 120.00, 3, 'Spacious bird cage'),
|
||||
('Fish Tank 20 Gallon', 80.00, 4, 'Complete aquarium kit'),
|
||||
('Hamster Wheel', 15.00, 5, 'Exercise wheel for small pets'),
|
||||
('Organic Dog Treats', 25.00, 1, 'Natural dog treats');
|
||||
|
||||
INSERT INTO productSupplier (supId, prodId, cost)
|
||||
VALUES
|
||||
(1, 1, 35.00),
|
||||
(1, 2, 6.50),
|
||||
(2, 2, 7.00),
|
||||
(3, 3, 90.00),
|
||||
(3, 4, 60.00),
|
||||
(4, 5, 10.00),
|
||||
(5, 6, 18.00),
|
||||
(1, 6, 17.50);
|
||||
|
||||
INSERT INTO inventory (prodId, quantity)
|
||||
VALUES
|
||||
(1, 100),
|
||||
(2, 200),
|
||||
(3, 50),
|
||||
(4, 30),
|
||||
(5, 150),
|
||||
(6, 75);
|
||||
|
||||
INSERT INTO service (serviceName, serviceDesc, serviceDuration, servicePrice)
|
||||
VALUES
|
||||
('Pet Grooming', 'Full grooming service', 60, 40.00),
|
||||
('Nail Trimming', 'Quick nail trim', 15, 10.00),
|
||||
('Bath and Brush', 'Bathing and brushing service', 45, 30.00),
|
||||
('Veterinary Checkup', 'Complete health examination', 30, 75.00),
|
||||
('Teeth Cleaning', 'Professional dental cleaning', 90, 100.00);
|
||||
|
||||
INSERT INTO appointment (serviceId, customerId, appointmentDate, appointmentTime, appointmentStatus)
|
||||
VALUES
|
||||
(1, 2, '2026-02-01', '10:30:00', 'Booked'),
|
||||
(2, 1, '2026-02-03', '14:00:00', 'Booked'),
|
||||
(3, 3, '2026-02-05', '09:00:00', 'Completed'),
|
||||
(4, 4, '2026-02-07', '11:30:00', 'Booked'),
|
||||
(5, 5, '2026-02-10', '15:00:00', 'Cancelled');
|
||||
|
||||
INSERT INTO appointmentPet (appointmentId, petId)
|
||||
VALUES
|
||||
(1, 2),
|
||||
(2, 1),
|
||||
(3, 3),
|
||||
(4, 5),
|
||||
(5, 6);
|
||||
|
||||
INSERT INTO sale (saleDate, totalAmount, paymentMethod, employeeId, storeId)
|
||||
VALUES
|
||||
-- January Sales
|
||||
('2026-01-05 09:15:00', 125.00, 'Card', 1, 1), -- Sale 1: Dog food + treats
|
||||
('2026-01-08 11:30:00', 200.00, 'Card', 2, 1), -- Sale 2: Bird cage + fish tank
|
||||
('2026-01-12 14:20:00', 60.00, 'Cash', 3, 2), -- Sale 3: Cat toys + hamster wheel
|
||||
('2026-01-15 10:45:00', 150.00, 'Debit', 1, 1), -- Sale 4: Dog food (bulk purchase)
|
||||
('2026-01-18 16:30:00', 80.00, 'Card', 4, 3), -- Sale 5: Fish tank
|
||||
('2026-01-22 13:15:00', 95.00, 'Cash', 2, 2), -- Sale 6: Mixed items
|
||||
('2026-01-25 15:40:00', 240.00, 'Card', 5, 4), -- Sale 7: Two bird cages
|
||||
('2026-01-28 10:30:00', 80.00, 'Cash', 1, 1), -- Sale 8: Dog food + cat toys
|
||||
-- February Sales
|
||||
('2026-02-01 09:00:00', 175.00, 'Card', 3, 3), -- Sale 9: Dog food + treats (bulk)
|
||||
('2026-02-03 11:20:00', 120.00, 'Card', 2, 1), -- Sale 10: Bird cage
|
||||
('2026-02-05 14:50:00', 45.00, 'Cash', 4, 2), -- Sale 11: Hamster wheel + cat toys
|
||||
('2026-02-08 16:15:00', 160.00, 'Debit', 1, 1), -- Sale 12: Fish tank + accessories
|
||||
('2026-02-10 10:25:00', 100.00, 'Card', 5, 4), -- Sale 13: Dog treats (bulk)
|
||||
('2026-02-12 13:45:00', 50.00, 'Cash', 2, 2), -- Sale 14: Dog food
|
||||
('2026-02-15 15:30:00', 85.00, 'Card', 3, 3), -- Sale 15: Mixed pet supplies
|
||||
('2026-02-18 11:10:00', 200.00, 'Card', 1, 1), -- Sale 16: Bird cage + hamster wheel
|
||||
('2026-02-20 14:35:00', 155.00, 'Debit', 4, 3), -- Sale 17: Fish tank + cat toys
|
||||
('2026-02-22 16:50:00', 75.00, 'Cash', 2, 1), -- Sale 18: Dog treats + toys
|
||||
('2026-02-24 10:15:00', 140.00, 'Card', 5, 4), -- Sale 19: Dog food + treats
|
||||
(NOW(), 95.00, 'Card', 1, 1); -- Sale 20: Recent sale (current timestamp)
|
||||
|
||||
INSERT INTO saleItem (saleId, prodId, quantity, unitPrice)
|
||||
VALUES
|
||||
-- Sale 1 items (Dog food + treats)
|
||||
(1, 1, 2, 50.00), -- 2x Premium Dog Food
|
||||
(1, 6, 1, 25.00), -- 1x Organic Dog Treats
|
||||
-- Sale 2 items (Bird cage + fish tank)
|
||||
(2, 3, 1, 120.00), -- 1x Bird Cage Large
|
||||
(2, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
|
||||
-- Sale 3 items (Cat toys + hamster wheel)
|
||||
(3, 2, 3, 10.00), -- 3x Cat Toy Ball
|
||||
(3, 5, 2, 15.00), -- 2x Hamster Wheel
|
||||
-- Sale 4 items (Dog food bulk)
|
||||
(4, 1, 3, 50.00), -- 3x Premium Dog Food
|
||||
-- Sale 5 items (Fish tank)
|
||||
(5, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
|
||||
-- Sale 6 items (Mixed)
|
||||
(6, 2, 4, 10.00), -- 4x Cat Toy Ball
|
||||
(6, 5, 1, 15.00), -- 1x Hamster Wheel
|
||||
(6, 6, 1, 25.00), -- 1x Organic Dog Treats
|
||||
(6, 1, 1, 50.00), -- 1x Premium Dog Food (partial - discount applied)
|
||||
-- Sale 7 items (Two bird cages)
|
||||
(7, 3, 2, 120.00), -- 2x Bird Cage Large
|
||||
-- Sale 8 items (Dog food + cat toys)
|
||||
(8, 1, 1, 50.00), -- 1x Premium Dog Food
|
||||
(8, 2, 3, 10.00), -- 3x Cat Toy Ball
|
||||
-- Sale 9 items (Dog food + treats bulk)
|
||||
(9, 1, 3, 50.00), -- 3x Premium Dog Food
|
||||
(9, 6, 1, 25.00), -- 1x Organic Dog Treats
|
||||
-- Sale 10 items (Bird cage)
|
||||
(10, 3, 1, 120.00), -- 1x Bird Cage Large
|
||||
-- Sale 11 items (Hamster wheel + cat toys)
|
||||
(11, 5, 1, 15.00), -- 1x Hamster Wheel
|
||||
(11, 2, 3, 10.00), -- 3x Cat Toy Ball
|
||||
-- Sale 12 items (Fish tank + accessories)
|
||||
(12, 4, 2, 80.00), -- 2x Fish Tank 20 Gallon
|
||||
-- Sale 13 items (Dog treats bulk)
|
||||
(13, 6, 4, 25.00), -- 4x Organic Dog Treats
|
||||
-- Sale 14 items (Dog food)
|
||||
(14, 1, 1, 50.00), -- 1x Premium Dog Food
|
||||
-- Sale 15 items (Mixed supplies)
|
||||
(15, 2, 2, 10.00), -- 2x Cat Toy Ball
|
||||
(15, 5, 1, 15.00), -- 1x Hamster Wheel
|
||||
(15, 6, 2, 25.00), -- 2x Organic Dog Treats
|
||||
-- Sale 16 items (Bird cage + hamster wheel)
|
||||
(16, 3, 1, 120.00), -- 1x Bird Cage Large
|
||||
(16, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
|
||||
-- Sale 17 items (Fish tank + cat toys)
|
||||
(17, 4, 1, 80.00), -- 1x Fish Tank 20 Gallon
|
||||
(17, 1, 1, 50.00), -- 1x Premium Dog Food
|
||||
(17, 6, 1, 25.00), -- 1x Organic Dog Treats
|
||||
-- Sale 18 items (Dog treats + toys)
|
||||
(18, 6, 2, 25.00), -- 2x Organic Dog Treats
|
||||
(18, 2, 2, 10.00), -- 2x Cat Toy Ball
|
||||
(18, 5, 1, 15.00), -- 1x Hamster Wheel
|
||||
-- Sale 19 items (Dog food + treats)
|
||||
(19, 1, 2, 50.00), -- 2x Premium Dog Food
|
||||
(19, 6, 2, 25.00), -- 2x Organic Dog Treats (discount applied)
|
||||
-- Sale 20 items (Recent sale)
|
||||
(20, 2, 5, 10.00), -- 5x Cat Toy Ball
|
||||
(20, 5, 3, 15.00); -- 3x Hamster Wheel
|
||||
|
||||
INSERT INTO purchaseOrder (supId, orderDate, status)
|
||||
VALUES
|
||||
(1, '2025-01-15', 'Delivered'),
|
||||
(2, '2025-01-20', 'Pending'),
|
||||
(3, '2025-02-01', 'Delivered'),
|
||||
(4, '2025-02-10', 'In Transit'),
|
||||
(1, '2025-02-15', 'Pending');
|
||||
|
||||
INSERT INTO activityLog (employeeId, activity)
|
||||
VALUES
|
||||
(1, 'Created new sale'),
|
||||
(2, 'Booked appointment'),
|
||||
(3, 'Completed grooming service'),
|
||||
(4, 'Processed inventory order'),
|
||||
(5, 'Conducted health checkup'),
|
||||
(1, 'Updated customer information');
|
||||
@@ -1,3 +0,0 @@
|
||||
url=jdbc:mysql://127.0.0.1:3306/Petstoredb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
|
||||
user=petapp
|
||||
password=petapppass
|
||||
@@ -1,18 +0,0 @@
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.4
|
||||
container_name: petstore-mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
MYSQL_DATABASE: Petstoredb
|
||||
MYSQL_USER: petapp
|
||||
MYSQL_PASSWORD: petapppass
|
||||
volumes:
|
||||
- ./Petstoredata.sql:/docker-entrypoint-initdb.d/01-Petstoredata.sql:ro
|
||||
- petstore_mysql_data:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
petstore_mysql_data:
|
||||
|
||||
71
log.txt
71
log.txt
@@ -45,3 +45,74 @@ The last packet sent successfully to the server was 0 milliseconds ago. The driv
|
||||
|
||||
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
|
||||
[2026-03-07 17:50:34] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 17:50:34] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 17:55:02] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 17:55:16] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 18:11:05] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: ConnectException | Message: null | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 18:11:42] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: RuntimeException | Message: Authentication failed. Please log in again. | Context: Authentication attempt for username: staff
|
||||
[2026-03-07 18:11:48] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:11:52] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.product.ProductResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Loading products
|
||||
[2026-03-07 18:11:52] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.sale.SaleResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Loading sales
|
||||
[2026-03-07 18:11:53] [ERROR] EXCEPTION | Location: AppointmentController.loadAppointments | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.appointment.AppointmentResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Loading appointments for table display
|
||||
[2026-03-07 18:11:53] [ERROR] EXCEPTION | Location: ServiceController.displayServices | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.service.ServiceResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching service data for table display
|
||||
[2026-03-07 18:11:54] [ERROR] EXCEPTION | Location: ServiceController.displayServices | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.service.ServiceResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching service data for table display
|
||||
[2026-03-07 18:11:56] [ERROR] EXCEPTION | Location: PetController.displayPets | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.pet.PetResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching pet data for table display
|
||||
[2026-03-07 18:11:56] [ERROR] EXCEPTION | Location: PetController.displayPets | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.pet.PetResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching pet data for table display
|
||||
[2026-03-07 18:11:56] [ERROR] EXCEPTION | Location: AdoptionController.displayAdoptions | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.adoption.AdoptionResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching adoption data for table display
|
||||
[2026-03-07 18:11:57] [ERROR] EXCEPTION | Location: ProductController.displayProduct | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.util.ArrayList<org.example.petshopdesktop.api.dto.product.ProductResponse>` from Object value (token `JsonToken.START_OBJECT`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 1] | Context: Fetching product data for table display
|
||||
[2026-03-07 18:11:58] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:47:48] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:48:01] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:48:05] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:48:05] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:51:14] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:51:14] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:51:28] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-07 18:51:30] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-08 10:03:43] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Loading products
|
||||
[2026-03-08 10:03:43] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:03:44] [ERROR] EXCEPTION | Location: AppointmentController.loadAppointments | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 219] (through reference chain: org.example.petshopdesktop.api.dto.common.PageResponse["content"]->java.util.ArrayList[0]->org.example.petshopdesktop.api.dto.appointment.AppointmentResponse["petNames"]) | Context: Loading appointments for table display
|
||||
[2026-03-08 10:03:44] [ERROR] EXCEPTION | Location: ServiceController.displayServices | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.service.ServiceResponse.getId()" is null | Context: Fetching service data for table display
|
||||
[2026-03-08 10:03:45] [ERROR] EXCEPTION | Location: ServiceController.displayServices | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.service.ServiceResponse.getId()" is null | Context: Fetching service data for table display
|
||||
[2026-03-08 10:03:46] [ERROR] EXCEPTION | Location: PetController.displayPets | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.pet.PetResponse.getId()" is null | Context: Fetching pet data for table display
|
||||
[2026-03-08 10:03:46] [ERROR] EXCEPTION | Location: AdoptionController.displayAdoptions | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.adoption.AdoptionResponse.getId()" is null | Context: Fetching adoption data for table display
|
||||
[2026-03-08 10:03:47] [ERROR] EXCEPTION | Location: AdoptionController.displayAdoptions | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.adoption.AdoptionResponse.getId()" is null | Context: Fetching adoption data for table display
|
||||
[2026-03-08 10:03:47] [ERROR] EXCEPTION | Location: ProductController.displayProduct | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Fetching product data for table display
|
||||
[2026-03-08 10:03:48] [ERROR] EXCEPTION | Location: InventoryController.displayInventory | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.inventory.InventoryResponse.getId()" is null | Context: Fetching inventory data for table display
|
||||
[2026-03-08 10:03:48] [ERROR] EXCEPTION | Location: ProductSupplierController.displayProductSupplier | Type: NullPointerException | Message: Cannot invoke "java.math.BigDecimal.doubleValue()" because the return value of "org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse.getSupplierPrice()" is null | Context: Fetching product-supplier data for table display
|
||||
[2026-03-08 10:03:49] [ERROR] EXCEPTION | Location: SupplierController.displaySupplier | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.supplier.SupplierResponse.getId()" is null | Context: Fetching supplier data for table display
|
||||
[2026-03-08 10:03:50] [ERROR] EXCEPTION | Location: PurchaseOrderController.loadPurchaseOrders | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.longValue()" because the return value of "org.example.petshopdesktop.api.dto.purchaseorder.PurchaseOrderResponse.getId()" is null | Context: Loading purchase orders for table display
|
||||
[2026-03-08 10:04:02] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Loading products
|
||||
[2026-03-08 10:04:02] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:04:03] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:05:41] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-08 10:05:44] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-08 10:05:46] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Loading products
|
||||
[2026-03-08 10:05:46] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:05:47] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Loading products
|
||||
[2026-03-08 10:05:47] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:05:47] [ERROR] EXCEPTION | Location: AppointmentController.loadAppointments | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 219] (through reference chain: org.example.petshopdesktop.api.dto.common.PageResponse["content"]->java.util.ArrayList[0]->org.example.petshopdesktop.api.dto.appointment.AppointmentResponse["petNames"]) | Context: Loading appointments for table display
|
||||
[2026-03-08 10:05:48] [ERROR] EXCEPTION | Location: AppointmentController.loadAppointments | Type: MismatchedInputException | Message: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
|
||||
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 219] (through reference chain: org.example.petshopdesktop.api.dto.common.PageResponse["content"]->java.util.ArrayList[0]->org.example.petshopdesktop.api.dto.appointment.AppointmentResponse["petNames"]) | Context: Loading appointments for table display
|
||||
[2026-03-08 10:12:33] [ERROR] EXCEPTION | Location: LoginController.btnLoginClicked | Type: ConnectException | Message: null | Context: Authentication attempt for username: admin
|
||||
[2026-03-08 10:55:59] [ERROR] EXCEPTION | Location: AnalyticsController.loadAnalyticsData | Type: RuntimeException | Message: Access restricted. You don't have permission to perform this action. | Context: Loading analytics data
|
||||
[2026-03-08 10:56:02] [ERROR] EXCEPTION | Location: SaleController.setupCreateSale | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.product.ProductResponse.getId()" is null | Context: Loading products
|
||||
[2026-03-08 10:56:02] [ERROR] EXCEPTION | Location: SaleController.refreshSales | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.sale.SaleResponse.getId()" is null | Context: Loading sales
|
||||
[2026-03-08 10:56:04] [ERROR] EXCEPTION | Location: ServiceController.displayServices | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.service.ServiceResponse.getId()" is null | Context: Fetching service data for table display
|
||||
[2026-03-08 10:56:04] [ERROR] EXCEPTION | Location: PetController.displayPets | Type: NullPointerException | Message: Cannot invoke "java.lang.Long.intValue()" because the return value of "org.example.petshopdesktop.api.dto.pet.PetResponse.getId()" is null | Context: Fetching pet data for table display
|
||||
|
||||
21
pom.xml
21
pom.xml
@@ -40,9 +40,24 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>9.3.0</version>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.18.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@ module org.example.petshopdesktop {
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires java.sql;
|
||||
requires java.net.http;
|
||||
requires com.fasterxml.jackson.databind;
|
||||
requires com.fasterxml.jackson.core;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires com.fasterxml.jackson.datatype.jsr310;
|
||||
|
||||
opens org.example.petshopdesktop.DTOs to javafx.base;
|
||||
opens org.example.petshopdesktop.models to javafx.base;
|
||||
@@ -10,6 +15,21 @@ module org.example.petshopdesktop {
|
||||
opens org.example.petshopdesktop.controllers to javafx.fxml;
|
||||
opens org.example.petshopdesktop.auth to javafx.fxml;
|
||||
|
||||
opens org.example.petshopdesktop.api.dto.common to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.auth to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.product to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.pet to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.service to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.supplier to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.productsupplier to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.inventory to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.appointment to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.adoption to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.sale to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.user to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.analytics to com.fasterxml.jackson.databind;
|
||||
opens org.example.petshopdesktop.api.dto.purchaseorder to com.fasterxml.jackson.databind;
|
||||
|
||||
exports org.example.petshopdesktop;
|
||||
exports org.example.petshopdesktop.controllers;
|
||||
exports org.example.petshopdesktop.auth;
|
||||
|
||||
@@ -4,21 +4,21 @@ import javafx.beans.property.*;
|
||||
|
||||
public class PurchaseOrderDTO {
|
||||
|
||||
private IntegerProperty purchaseOrderId;
|
||||
private LongProperty purchaseOrderId;
|
||||
private StringProperty supplierName;
|
||||
private StringProperty orderDate;
|
||||
private StringProperty status;
|
||||
|
||||
public PurchaseOrderDTO(int id, String supplierName,
|
||||
public PurchaseOrderDTO(long id, String supplierName,
|
||||
String orderDate, String status) {
|
||||
|
||||
this.purchaseOrderId = new SimpleIntegerProperty(id);
|
||||
this.purchaseOrderId = new SimpleLongProperty(id);
|
||||
this.supplierName = new SimpleStringProperty(supplierName);
|
||||
this.orderDate = new SimpleStringProperty(orderDate);
|
||||
this.status = new SimpleStringProperty(status);
|
||||
}
|
||||
|
||||
public int getPurchaseOrderId() { return purchaseOrderId.get(); }
|
||||
public long getPurchaseOrderId() { return purchaseOrderId.get(); }
|
||||
public String getSupplierName() { return supplierName.get(); }
|
||||
public String getOrderDate() { return orderDate.get(); }
|
||||
public String getStatus() { return status.get(); }
|
||||
|
||||
@@ -4,18 +4,12 @@ import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.database.UserDB;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PetShopApplication extends Application {
|
||||
@Override
|
||||
public void start(Stage stage) throws IOException {
|
||||
try {
|
||||
UserDB.initializeTable();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Warning: could not initialize users table: " + e.getMessage());
|
||||
}
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(PetShopApplication.class.getResource("login-view.fxml"));
|
||||
Scene scene = new Scene(fxmlLoader.load());
|
||||
stage.setTitle("Pet Shop Manager - Login");
|
||||
|
||||
188
src/main/java/org/example/petshopdesktop/api/ApiClient.java
Normal file
188
src/main/java/org/example/petshopdesktop/api/ApiClient.java
Normal file
@@ -0,0 +1,188 @@
|
||||
package org.example.petshopdesktop.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
|
||||
public class ApiClient {
|
||||
private static final ApiClient INSTANCE = new ApiClient();
|
||||
private final HttpClient httpClient;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final String baseUrl;
|
||||
|
||||
private ApiClient() {
|
||||
this.httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(10))
|
||||
.build();
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.objectMapper.registerModule(new JavaTimeModule());
|
||||
this.objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
this.baseUrl = ApiConfig.getInstance().getBaseUrl();
|
||||
}
|
||||
|
||||
public static ApiClient getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public <T> T get(String path, Class<T> responseClass) throws Exception {
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.GET()
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
return handleResponse(response, responseClass);
|
||||
}
|
||||
|
||||
public String getRawResponse(String path) throws Exception {
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.GET()
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200 || response.statusCode() == 201) {
|
||||
return response.body();
|
||||
} else if (response.statusCode() == 401) {
|
||||
throw new RuntimeException("Authentication failed. Please log in again.");
|
||||
} else if (response.statusCode() == 403) {
|
||||
throw new RuntimeException("Access restricted. You don't have permission to perform this action.");
|
||||
} else {
|
||||
throw new RuntimeException(parseErrorMessage(response));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T post(String path, Object requestBody, Class<T> responseClass) throws Exception {
|
||||
String jsonBody = objectMapper.writeValueAsString(requestBody);
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
return handleResponse(response, responseClass);
|
||||
}
|
||||
|
||||
public <T> T put(String path, Object requestBody, Class<T> responseClass) throws Exception {
|
||||
String jsonBody = objectMapper.writeValueAsString(requestBody);
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.header("Content-Type", "application/json")
|
||||
.PUT(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
return handleResponse(response, responseClass);
|
||||
}
|
||||
|
||||
public void delete(String path) throws Exception {
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.DELETE()
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 204 && response.statusCode() != 200) {
|
||||
throw new RuntimeException(parseErrorMessage(response));
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteWithBody(String path, Object requestBody) throws Exception {
|
||||
String jsonBody = objectMapper.writeValueAsString(requestBody);
|
||||
|
||||
HttpRequest.Builder builder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(baseUrl + path))
|
||||
.header("Content-Type", "application/json")
|
||||
.method("DELETE", HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||
.timeout(Duration.ofSeconds(30));
|
||||
|
||||
addAuthHeader(builder);
|
||||
|
||||
HttpRequest request = builder.build();
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 204 && response.statusCode() != 200) {
|
||||
throw new RuntimeException(parseErrorMessage(response));
|
||||
}
|
||||
}
|
||||
|
||||
private void addAuthHeader(HttpRequest.Builder builder) {
|
||||
String token = UserSession.getInstance().getJwtToken();
|
||||
if (token != null && !token.isEmpty()) {
|
||||
builder.header("Authorization", "Bearer " + token);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T handleResponse(HttpResponse<String> response, Class<T> responseClass) throws Exception {
|
||||
int statusCode = response.statusCode();
|
||||
|
||||
if (statusCode == 200 || statusCode == 201) {
|
||||
if (response.body() == null || response.body().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.readValue(response.body(), responseClass);
|
||||
} else if (statusCode == 204) {
|
||||
return null;
|
||||
} else if (statusCode == 401) {
|
||||
throw new RuntimeException("Authentication failed. Please log in again.");
|
||||
} else if (statusCode == 403) {
|
||||
throw new RuntimeException("Access restricted. You don't have permission to perform this action.");
|
||||
} else {
|
||||
throw new RuntimeException(parseErrorMessage(response));
|
||||
}
|
||||
}
|
||||
|
||||
private String parseErrorMessage(HttpResponse<String> response) {
|
||||
try {
|
||||
if (response.body() != null && !response.body().isEmpty()) {
|
||||
var errorNode = objectMapper.readTree(response.body());
|
||||
if (errorNode.has("message")) {
|
||||
return errorNode.get("message").asText();
|
||||
}
|
||||
if (errorNode.has("errors")) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
errorNode.get("errors").fields().forEachRemaining(entry -> {
|
||||
sb.append(entry.getValue().asText()).append("\n");
|
||||
});
|
||||
return sb.toString().trim();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error parsing error message: " + e.getMessage());
|
||||
}
|
||||
return "Request failed with status " + response.statusCode();
|
||||
}
|
||||
|
||||
public ObjectMapper getObjectMapper() {
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
34
src/main/java/org/example/petshopdesktop/api/ApiConfig.java
Normal file
34
src/main/java/org/example/petshopdesktop/api/ApiConfig.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.example.petshopdesktop.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ApiConfig {
|
||||
private static final ApiConfig INSTANCE = new ApiConfig();
|
||||
private final String baseUrl;
|
||||
|
||||
private ApiConfig() {
|
||||
Properties props = new Properties();
|
||||
String url = "http://localhost:8080";
|
||||
|
||||
try (InputStream input = getClass().getClassLoader().getResourceAsStream("connectionpetstore.properties")) {
|
||||
if (input != null) {
|
||||
props.load(input);
|
||||
url = props.getProperty("api.baseUrl", "http://localhost:8080");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Failed to load api.baseUrl from properties: " + e.getMessage());
|
||||
}
|
||||
|
||||
this.baseUrl = url;
|
||||
}
|
||||
|
||||
public static ApiConfig getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.example.petshopdesktop.api.dto.adoption;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class AdoptionRequest {
|
||||
private Long petId;
|
||||
private Long customerId;
|
||||
private LocalDate adoptionDate;
|
||||
private String adoptionStatus;
|
||||
|
||||
public AdoptionRequest() {
|
||||
}
|
||||
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public void setPetId(Long petId) {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public LocalDate getAdoptionDate() {
|
||||
return adoptionDate;
|
||||
}
|
||||
|
||||
public void setAdoptionDate(LocalDate adoptionDate) {
|
||||
this.adoptionDate = adoptionDate;
|
||||
}
|
||||
|
||||
public String getAdoptionStatus() {
|
||||
return adoptionStatus;
|
||||
}
|
||||
|
||||
public void setAdoptionStatus(String adoptionStatus) {
|
||||
this.adoptionStatus = adoptionStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.adoption;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class AdoptionResponse {
|
||||
private Long adoptionId;
|
||||
private String petName;
|
||||
private String customerName;
|
||||
private LocalDate adoptionDate;
|
||||
private String adoptionStatus;
|
||||
|
||||
public AdoptionResponse() {
|
||||
}
|
||||
|
||||
public Long getAdoptionId() {
|
||||
return adoptionId;
|
||||
}
|
||||
|
||||
public void setAdoptionId(Long adoptionId) {
|
||||
this.adoptionId = adoptionId;
|
||||
}
|
||||
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
|
||||
public void setPetName(String petName) {
|
||||
this.petName = petName;
|
||||
}
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
public void setCustomerName(String customerName) {
|
||||
this.customerName = customerName;
|
||||
}
|
||||
|
||||
public LocalDate getAdoptionDate() {
|
||||
return adoptionDate;
|
||||
}
|
||||
|
||||
public void setAdoptionDate(LocalDate adoptionDate) {
|
||||
this.adoptionDate = adoptionDate;
|
||||
}
|
||||
|
||||
public String getAdoptionStatus() {
|
||||
return adoptionStatus;
|
||||
}
|
||||
|
||||
public void setAdoptionStatus(String adoptionStatus) {
|
||||
this.adoptionStatus = adoptionStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.example.petshopdesktop.api.dto.analytics;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class DailySales {
|
||||
private String date;
|
||||
private BigDecimal revenue;
|
||||
private Long salesCount;
|
||||
|
||||
public DailySales() {
|
||||
}
|
||||
|
||||
public String getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(String date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public BigDecimal getRevenue() {
|
||||
return revenue;
|
||||
}
|
||||
|
||||
public void setRevenue(BigDecimal revenue) {
|
||||
this.revenue = revenue;
|
||||
}
|
||||
|
||||
public Long getSalesCount() {
|
||||
return salesCount;
|
||||
}
|
||||
|
||||
public void setSalesCount(Long salesCount) {
|
||||
this.salesCount = salesCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.example.petshopdesktop.api.dto.analytics;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class DashboardResponse {
|
||||
private SalesSummary salesSummary;
|
||||
private InventorySummary inventorySummary;
|
||||
private List<TopProduct> topProducts;
|
||||
private List<DailySales> dailySales;
|
||||
|
||||
public DashboardResponse() {
|
||||
}
|
||||
|
||||
public SalesSummary getSalesSummary() {
|
||||
return salesSummary;
|
||||
}
|
||||
|
||||
public void setSalesSummary(SalesSummary salesSummary) {
|
||||
this.salesSummary = salesSummary;
|
||||
}
|
||||
|
||||
public InventorySummary getInventorySummary() {
|
||||
return inventorySummary;
|
||||
}
|
||||
|
||||
public void setInventorySummary(InventorySummary inventorySummary) {
|
||||
this.inventorySummary = inventorySummary;
|
||||
}
|
||||
|
||||
public List<TopProduct> getTopProducts() {
|
||||
return topProducts;
|
||||
}
|
||||
|
||||
public void setTopProducts(List<TopProduct> topProducts) {
|
||||
this.topProducts = topProducts;
|
||||
}
|
||||
|
||||
public List<DailySales> getDailySales() {
|
||||
return dailySales;
|
||||
}
|
||||
|
||||
public void setDailySales(List<DailySales> dailySales) {
|
||||
this.dailySales = dailySales;
|
||||
}
|
||||
|
||||
public static class SalesSummary {
|
||||
private BigDecimal totalRevenue;
|
||||
private Long totalSales;
|
||||
private BigDecimal totalRefunds;
|
||||
private Long totalRefundCount;
|
||||
|
||||
public SalesSummary() {
|
||||
}
|
||||
|
||||
public BigDecimal getTotalRevenue() {
|
||||
return totalRevenue;
|
||||
}
|
||||
|
||||
public void setTotalRevenue(BigDecimal totalRevenue) {
|
||||
this.totalRevenue = totalRevenue;
|
||||
}
|
||||
|
||||
public Long getTotalSales() {
|
||||
return totalSales;
|
||||
}
|
||||
|
||||
public void setTotalSales(Long totalSales) {
|
||||
this.totalSales = totalSales;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalRefunds() {
|
||||
return totalRefunds;
|
||||
}
|
||||
|
||||
public void setTotalRefunds(BigDecimal totalRefunds) {
|
||||
this.totalRefunds = totalRefunds;
|
||||
}
|
||||
|
||||
public Long getTotalRefundCount() {
|
||||
return totalRefundCount;
|
||||
}
|
||||
|
||||
public void setTotalRefundCount(Long totalRefundCount) {
|
||||
this.totalRefundCount = totalRefundCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InventorySummary {
|
||||
private Long totalProducts;
|
||||
private Long lowStockProducts;
|
||||
private Long outOfStockProducts;
|
||||
|
||||
public InventorySummary() {
|
||||
}
|
||||
|
||||
public Long getTotalProducts() {
|
||||
return totalProducts;
|
||||
}
|
||||
|
||||
public void setTotalProducts(Long totalProducts) {
|
||||
this.totalProducts = totalProducts;
|
||||
}
|
||||
|
||||
public Long getLowStockProducts() {
|
||||
return lowStockProducts;
|
||||
}
|
||||
|
||||
public void setLowStockProducts(Long lowStockProducts) {
|
||||
this.lowStockProducts = lowStockProducts;
|
||||
}
|
||||
|
||||
public Long getOutOfStockProducts() {
|
||||
return outOfStockProducts;
|
||||
}
|
||||
|
||||
public void setOutOfStockProducts(Long outOfStockProducts) {
|
||||
this.outOfStockProducts = outOfStockProducts;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.example.petshopdesktop.api.dto.analytics;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class TopProduct {
|
||||
private Long productId;
|
||||
private String productName;
|
||||
private Long quantitySold;
|
||||
private BigDecimal revenue;
|
||||
|
||||
public TopProduct() {
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(Long productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public Long getQuantitySold() {
|
||||
return quantitySold;
|
||||
}
|
||||
|
||||
public void setQuantitySold(Long quantitySold) {
|
||||
this.quantitySold = quantitySold;
|
||||
}
|
||||
|
||||
public BigDecimal getRevenue() {
|
||||
return revenue;
|
||||
}
|
||||
|
||||
public void setRevenue(BigDecimal revenue) {
|
||||
this.revenue = revenue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.example.petshopdesktop.api.dto.appointment;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.List;
|
||||
|
||||
public class AppointmentRequest {
|
||||
private List<Long> petIds;
|
||||
private Long customerId;
|
||||
private Long serviceId;
|
||||
private LocalDate appointmentDate;
|
||||
private LocalTime appointmentTime;
|
||||
private String appointmentStatus;
|
||||
|
||||
public AppointmentRequest() {
|
||||
}
|
||||
|
||||
public List<Long> getPetIds() {
|
||||
return petIds;
|
||||
}
|
||||
|
||||
public void setPetIds(List<Long> petIds) {
|
||||
this.petIds = petIds;
|
||||
}
|
||||
|
||||
public Long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(Long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public Long getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public void setServiceId(Long serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public LocalDate getAppointmentDate() {
|
||||
return appointmentDate;
|
||||
}
|
||||
|
||||
public void setAppointmentDate(LocalDate appointmentDate) {
|
||||
this.appointmentDate = appointmentDate;
|
||||
}
|
||||
|
||||
public LocalTime getAppointmentTime() {
|
||||
return appointmentTime;
|
||||
}
|
||||
|
||||
public void setAppointmentTime(LocalTime appointmentTime) {
|
||||
this.appointmentTime = appointmentTime;
|
||||
}
|
||||
|
||||
public String getAppointmentStatus() {
|
||||
return appointmentStatus;
|
||||
}
|
||||
|
||||
public void setAppointmentStatus(String appointmentStatus) {
|
||||
this.appointmentStatus = appointmentStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.example.petshopdesktop.api.dto.appointment;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
|
||||
public class AppointmentResponse {
|
||||
private Long appointmentId;
|
||||
private String customerName;
|
||||
private java.util.List<String> petNames;
|
||||
private String serviceName;
|
||||
private LocalDate appointmentDate;
|
||||
private LocalTime appointmentTime;
|
||||
private String appointmentStatus;
|
||||
|
||||
public AppointmentResponse() {
|
||||
}
|
||||
|
||||
public Long getAppointmentId() {
|
||||
return appointmentId;
|
||||
}
|
||||
|
||||
public void setAppointmentId(Long appointmentId) {
|
||||
this.appointmentId = appointmentId;
|
||||
}
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
public void setCustomerName(String customerName) {
|
||||
this.customerName = customerName;
|
||||
}
|
||||
|
||||
public java.util.List<String> getPetNames() {
|
||||
return petNames;
|
||||
}
|
||||
|
||||
public void setPetNames(java.util.List<String> petNames) {
|
||||
this.petNames = petNames;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public LocalDate getAppointmentDate() {
|
||||
return appointmentDate;
|
||||
}
|
||||
|
||||
public void setAppointmentDate(LocalDate appointmentDate) {
|
||||
this.appointmentDate = appointmentDate;
|
||||
}
|
||||
|
||||
public LocalTime getAppointmentTime() {
|
||||
return appointmentTime;
|
||||
}
|
||||
|
||||
public void setAppointmentTime(LocalTime appointmentTime) {
|
||||
this.appointmentTime = appointmentTime;
|
||||
}
|
||||
|
||||
public String getAppointmentStatus() {
|
||||
return appointmentStatus;
|
||||
}
|
||||
|
||||
public void setAppointmentStatus(String appointmentStatus) {
|
||||
this.appointmentStatus = appointmentStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.example.petshopdesktop.api.dto.auth;
|
||||
|
||||
public class LoginRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public LoginRequest() {
|
||||
}
|
||||
|
||||
public LoginRequest(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.example.petshopdesktop.api.dto.auth;
|
||||
|
||||
public class LoginResponse {
|
||||
private String token;
|
||||
private String username;
|
||||
private String role;
|
||||
|
||||
public LoginResponse() {
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.example.petshopdesktop.api.dto.auth;
|
||||
|
||||
public class UserInfoResponse {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String role;
|
||||
|
||||
public UserInfoResponse() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.example.petshopdesktop.api.dto.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BulkDeleteRequest {
|
||||
private List<Long> ids;
|
||||
|
||||
public BulkDeleteRequest() {
|
||||
}
|
||||
|
||||
public BulkDeleteRequest(List<Long> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void setIds(List<Long> ids) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.example.petshopdesktop.api.dto.common;
|
||||
|
||||
public class DropdownOption {
|
||||
private Long id;
|
||||
private String label;
|
||||
|
||||
public DropdownOption() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.example.petshopdesktop.api.dto.common;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class PageResponse<T> {
|
||||
private List<T> content;
|
||||
|
||||
@JsonProperty("number")
|
||||
private int pageNumber;
|
||||
|
||||
@JsonProperty("size")
|
||||
private int pageSize;
|
||||
|
||||
private long totalElements;
|
||||
private int totalPages;
|
||||
private boolean last;
|
||||
|
||||
public PageResponse() {
|
||||
}
|
||||
|
||||
public List<T> getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(List<T> content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public int getPageNumber() {
|
||||
return pageNumber;
|
||||
}
|
||||
|
||||
public void setPageNumber(int pageNumber) {
|
||||
this.pageNumber = pageNumber;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public long getTotalElements() {
|
||||
return totalElements;
|
||||
}
|
||||
|
||||
public void setTotalElements(long totalElements) {
|
||||
this.totalElements = totalElements;
|
||||
}
|
||||
|
||||
public int getTotalPages() {
|
||||
return totalPages;
|
||||
}
|
||||
|
||||
public void setTotalPages(int totalPages) {
|
||||
this.totalPages = totalPages;
|
||||
}
|
||||
|
||||
public boolean isLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
public void setLast(boolean last) {
|
||||
this.last = last;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.example.petshopdesktop.api.dto.inventory;
|
||||
|
||||
public class InventoryRequest {
|
||||
private Long prodId;
|
||||
private Integer quantity;
|
||||
|
||||
public InventoryRequest() {
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.example.petshopdesktop.api.dto.inventory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class InventoryResponse {
|
||||
private Long inventoryId;
|
||||
private Long prodId;
|
||||
private String productName;
|
||||
private String categoryName;
|
||||
private Integer quantity;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public InventoryResponse() {
|
||||
}
|
||||
|
||||
public Long getInventoryId() {
|
||||
return inventoryId;
|
||||
}
|
||||
|
||||
public void setInventoryId(Long inventoryId) {
|
||||
this.inventoryId = inventoryId;
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void setCategoryName(String categoryName) {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.example.petshopdesktop.api.dto.pet;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class PetRequest {
|
||||
private String petName;
|
||||
private String petSpecies;
|
||||
private String petBreed;
|
||||
private Integer petAge;
|
||||
private String petStatus;
|
||||
private BigDecimal petPrice;
|
||||
|
||||
public PetRequest() {
|
||||
}
|
||||
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
|
||||
public void setPetName(String petName) {
|
||||
this.petName = petName;
|
||||
}
|
||||
|
||||
public String getPetSpecies() {
|
||||
return petSpecies;
|
||||
}
|
||||
|
||||
public void setPetSpecies(String petSpecies) {
|
||||
this.petSpecies = petSpecies;
|
||||
}
|
||||
|
||||
public String getPetBreed() {
|
||||
return petBreed;
|
||||
}
|
||||
|
||||
public void setPetBreed(String petBreed) {
|
||||
this.petBreed = petBreed;
|
||||
}
|
||||
|
||||
public Integer getPetAge() {
|
||||
return petAge;
|
||||
}
|
||||
|
||||
public void setPetAge(Integer petAge) {
|
||||
this.petAge = petAge;
|
||||
}
|
||||
|
||||
public String getPetStatus() {
|
||||
return petStatus;
|
||||
}
|
||||
|
||||
public void setPetStatus(String petStatus) {
|
||||
this.petStatus = petStatus;
|
||||
}
|
||||
|
||||
public BigDecimal getPetPrice() {
|
||||
return petPrice;
|
||||
}
|
||||
|
||||
public void setPetPrice(BigDecimal petPrice) {
|
||||
this.petPrice = petPrice;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.example.petshopdesktop.api.dto.pet;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class PetResponse {
|
||||
private Long petId;
|
||||
private String petName;
|
||||
private String petSpecies;
|
||||
private String petBreed;
|
||||
private Integer petAge;
|
||||
private String petStatus;
|
||||
private BigDecimal petPrice;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public PetResponse() {
|
||||
}
|
||||
|
||||
public Long getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public void setPetId(Long petId) {
|
||||
this.petId = petId;
|
||||
}
|
||||
|
||||
public String getPetName() {
|
||||
return petName;
|
||||
}
|
||||
|
||||
public void setPetName(String petName) {
|
||||
this.petName = petName;
|
||||
}
|
||||
|
||||
public String getPetSpecies() {
|
||||
return petSpecies;
|
||||
}
|
||||
|
||||
public void setPetSpecies(String petSpecies) {
|
||||
this.petSpecies = petSpecies;
|
||||
}
|
||||
|
||||
public String getPetBreed() {
|
||||
return petBreed;
|
||||
}
|
||||
|
||||
public void setPetBreed(String petBreed) {
|
||||
this.petBreed = petBreed;
|
||||
}
|
||||
|
||||
public Integer getPetAge() {
|
||||
return petAge;
|
||||
}
|
||||
|
||||
public void setPetAge(Integer petAge) {
|
||||
this.petAge = petAge;
|
||||
}
|
||||
|
||||
public String getPetStatus() {
|
||||
return petStatus;
|
||||
}
|
||||
|
||||
public void setPetStatus(String petStatus) {
|
||||
this.petStatus = petStatus;
|
||||
}
|
||||
|
||||
public BigDecimal getPetPrice() {
|
||||
return petPrice;
|
||||
}
|
||||
|
||||
public void setPetPrice(BigDecimal petPrice) {
|
||||
this.petPrice = petPrice;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.example.petshopdesktop.api.dto.product;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductRequest {
|
||||
private String prodName;
|
||||
private Long categoryId;
|
||||
private BigDecimal prodPrice;
|
||||
private String prodDesc;
|
||||
|
||||
public ProductRequest() {
|
||||
}
|
||||
|
||||
public String getProdName() {
|
||||
return prodName;
|
||||
}
|
||||
|
||||
public void setProdName(String prodName) {
|
||||
this.prodName = prodName;
|
||||
}
|
||||
|
||||
public Long getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(Long categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public BigDecimal getProdPrice() {
|
||||
return prodPrice;
|
||||
}
|
||||
|
||||
public void setProdPrice(BigDecimal prodPrice) {
|
||||
this.prodPrice = prodPrice;
|
||||
}
|
||||
|
||||
public String getProdDesc() {
|
||||
return prodDesc;
|
||||
}
|
||||
|
||||
public void setProdDesc(String prodDesc) {
|
||||
this.prodDesc = prodDesc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.product;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductResponse {
|
||||
private Long prodId;
|
||||
private String prodName;
|
||||
private String categoryName;
|
||||
private BigDecimal prodPrice;
|
||||
private String prodDesc;
|
||||
|
||||
public ProductResponse() {
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public String getProdName() {
|
||||
return prodName;
|
||||
}
|
||||
|
||||
public void setProdName(String prodName) {
|
||||
this.prodName = prodName;
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void setCategoryName(String categoryName) {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public BigDecimal getProdPrice() {
|
||||
return prodPrice;
|
||||
}
|
||||
|
||||
public void setProdPrice(BigDecimal prodPrice) {
|
||||
this.prodPrice = prodPrice;
|
||||
}
|
||||
|
||||
public String getProdDesc() {
|
||||
return prodDesc;
|
||||
}
|
||||
|
||||
public void setProdDesc(String prodDesc) {
|
||||
this.prodDesc = prodDesc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.example.petshopdesktop.api.dto.productsupplier;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductSupplierRequest {
|
||||
private Long productId;
|
||||
private Long supplierId;
|
||||
private BigDecimal cost;
|
||||
|
||||
public ProductSupplierRequest() {
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(Long productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public Long getSupplierId() {
|
||||
return supplierId;
|
||||
}
|
||||
|
||||
public void setSupplierId(Long supplierId) {
|
||||
this.supplierId = supplierId;
|
||||
}
|
||||
|
||||
public BigDecimal getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(BigDecimal cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.productsupplier;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductSupplierResponse {
|
||||
private Long productId;
|
||||
private Long supplierId;
|
||||
private String productName;
|
||||
private String supplierName;
|
||||
private BigDecimal cost;
|
||||
|
||||
public ProductSupplierResponse() {
|
||||
}
|
||||
|
||||
public Long getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(Long productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public Long getSupplierId() {
|
||||
return supplierId;
|
||||
}
|
||||
|
||||
public void setSupplierId(Long supplierId) {
|
||||
this.supplierId = supplierId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public String getSupplierName() {
|
||||
return supplierName;
|
||||
}
|
||||
|
||||
public void setSupplierName(String supplierName) {
|
||||
this.supplierName = supplierName;
|
||||
}
|
||||
|
||||
public BigDecimal getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(BigDecimal cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.example.petshopdesktop.api.dto.purchaseorder;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class PurchaseOrderResponse {
|
||||
private Long purchaseOrderId;
|
||||
private String supplierName;
|
||||
private LocalDate orderDate;
|
||||
private LocalDate expectedDeliveryDate;
|
||||
private String orderStatus;
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
public PurchaseOrderResponse() {
|
||||
}
|
||||
|
||||
public Long getPurchaseOrderId() {
|
||||
return purchaseOrderId;
|
||||
}
|
||||
|
||||
public void setPurchaseOrderId(Long purchaseOrderId) {
|
||||
this.purchaseOrderId = purchaseOrderId;
|
||||
}
|
||||
|
||||
public String getSupplierName() {
|
||||
return supplierName;
|
||||
}
|
||||
|
||||
public void setSupplierName(String supplierName) {
|
||||
this.supplierName = supplierName;
|
||||
}
|
||||
|
||||
public LocalDate getOrderDate() {
|
||||
return orderDate;
|
||||
}
|
||||
|
||||
public void setOrderDate(LocalDate orderDate) {
|
||||
this.orderDate = orderDate;
|
||||
}
|
||||
|
||||
public LocalDate getExpectedDeliveryDate() {
|
||||
return expectedDeliveryDate;
|
||||
}
|
||||
|
||||
public void setExpectedDeliveryDate(LocalDate expectedDeliveryDate) {
|
||||
this.expectedDeliveryDate = expectedDeliveryDate;
|
||||
}
|
||||
|
||||
public String getOrderStatus() {
|
||||
return orderStatus;
|
||||
}
|
||||
|
||||
public void setOrderStatus(String orderStatus) {
|
||||
this.orderStatus = orderStatus;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(BigDecimal totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.example.petshopdesktop.api.dto.sale;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class SaleItemRequest {
|
||||
private Long prodId;
|
||||
private Integer quantity;
|
||||
|
||||
public SaleItemRequest() {
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.sale;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class SaleItemResponse {
|
||||
private Long saleItemId;
|
||||
private Long prodId;
|
||||
private String productName;
|
||||
private Integer quantity;
|
||||
private BigDecimal unitPrice;
|
||||
|
||||
public SaleItemResponse() {
|
||||
}
|
||||
|
||||
public Long getSaleItemId() {
|
||||
return saleItemId;
|
||||
}
|
||||
|
||||
public void setSaleItemId(Long saleItemId) {
|
||||
this.saleItemId = saleItemId;
|
||||
}
|
||||
|
||||
public Long getProdId() {
|
||||
return prodId;
|
||||
}
|
||||
|
||||
public void setProdId(Long prodId) {
|
||||
this.prodId = prodId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public BigDecimal getUnitPrice() {
|
||||
return unitPrice;
|
||||
}
|
||||
|
||||
public void setUnitPrice(BigDecimal unitPrice) {
|
||||
this.unitPrice = unitPrice;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.sale;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SaleRequest {
|
||||
private Long storeId;
|
||||
private String paymentMethod;
|
||||
private List<SaleItemRequest> items;
|
||||
private Boolean isRefund;
|
||||
private Long originalSaleId;
|
||||
|
||||
public SaleRequest() {
|
||||
}
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public void setStoreId(Long storeId) {
|
||||
this.storeId = storeId;
|
||||
}
|
||||
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public void setPaymentMethod(String paymentMethod) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
}
|
||||
|
||||
public List<SaleItemRequest> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<SaleItemRequest> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Boolean getIsRefund() {
|
||||
return isRefund;
|
||||
}
|
||||
|
||||
public void setIsRefund(Boolean isRefund) {
|
||||
this.isRefund = isRefund;
|
||||
}
|
||||
|
||||
public Long getOriginalSaleId() {
|
||||
return originalSaleId;
|
||||
}
|
||||
|
||||
public void setOriginalSaleId(Long originalSaleId) {
|
||||
this.originalSaleId = originalSaleId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.example.petshopdesktop.api.dto.sale;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class SaleResponse {
|
||||
private Long saleId;
|
||||
private String employeeName;
|
||||
private String storeName;
|
||||
private LocalDateTime saleDate;
|
||||
private BigDecimal totalAmount;
|
||||
private String paymentMethod;
|
||||
private Boolean isRefund;
|
||||
private Long originalSaleId;
|
||||
private List<SaleItemResponse> items;
|
||||
|
||||
public SaleResponse() {
|
||||
}
|
||||
|
||||
public Long getSaleId() {
|
||||
return saleId;
|
||||
}
|
||||
|
||||
public void setSaleId(Long saleId) {
|
||||
this.saleId = saleId;
|
||||
}
|
||||
|
||||
public String getEmployeeName() {
|
||||
return employeeName;
|
||||
}
|
||||
|
||||
public void setEmployeeName(String employeeName) {
|
||||
this.employeeName = employeeName;
|
||||
}
|
||||
|
||||
public String getStoreName() {
|
||||
return storeName;
|
||||
}
|
||||
|
||||
public void setStoreName(String storeName) {
|
||||
this.storeName = storeName;
|
||||
}
|
||||
|
||||
public LocalDateTime getSaleDate() {
|
||||
return saleDate;
|
||||
}
|
||||
|
||||
public void setSaleDate(LocalDateTime saleDate) {
|
||||
this.saleDate = saleDate;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(BigDecimal totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public String getPaymentMethod() {
|
||||
return paymentMethod;
|
||||
}
|
||||
|
||||
public void setPaymentMethod(String paymentMethod) {
|
||||
this.paymentMethod = paymentMethod;
|
||||
}
|
||||
|
||||
public Boolean getIsRefund() {
|
||||
return isRefund;
|
||||
}
|
||||
|
||||
public void setIsRefund(Boolean isRefund) {
|
||||
this.isRefund = isRefund;
|
||||
}
|
||||
|
||||
public Long getOriginalSaleId() {
|
||||
return originalSaleId;
|
||||
}
|
||||
|
||||
public void setOriginalSaleId(Long originalSaleId) {
|
||||
this.originalSaleId = originalSaleId;
|
||||
}
|
||||
|
||||
public List<SaleItemResponse> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<SaleItemResponse> items) {
|
||||
this.items = items;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.example.petshopdesktop.api.dto.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ServiceRequest {
|
||||
private String serviceName;
|
||||
private BigDecimal servicePrice;
|
||||
private String serviceDesc;
|
||||
private Integer serviceDuration;
|
||||
|
||||
public ServiceRequest() {
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public BigDecimal getServicePrice() {
|
||||
return servicePrice;
|
||||
}
|
||||
|
||||
public void setServicePrice(BigDecimal servicePrice) {
|
||||
this.servicePrice = servicePrice;
|
||||
}
|
||||
|
||||
public String getServiceDesc() {
|
||||
return serviceDesc;
|
||||
}
|
||||
|
||||
public void setServiceDesc(String serviceDesc) {
|
||||
this.serviceDesc = serviceDesc;
|
||||
}
|
||||
|
||||
public Integer getServiceDuration() {
|
||||
return serviceDuration;
|
||||
}
|
||||
|
||||
public void setServiceDuration(Integer serviceDuration) {
|
||||
this.serviceDuration = serviceDuration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.example.petshopdesktop.api.dto.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ServiceResponse {
|
||||
private Long serviceId;
|
||||
private String serviceName;
|
||||
private BigDecimal servicePrice;
|
||||
private String serviceDesc;
|
||||
private Integer serviceDuration;
|
||||
|
||||
public ServiceResponse() {
|
||||
}
|
||||
|
||||
public Long getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public void setServiceId(Long serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public String getServiceName() {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public void setServiceName(String serviceName) {
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
public BigDecimal getServicePrice() {
|
||||
return servicePrice;
|
||||
}
|
||||
|
||||
public void setServicePrice(BigDecimal servicePrice) {
|
||||
this.servicePrice = servicePrice;
|
||||
}
|
||||
|
||||
public String getServiceDesc() {
|
||||
return serviceDesc;
|
||||
}
|
||||
|
||||
public void setServiceDesc(String serviceDesc) {
|
||||
this.serviceDesc = serviceDesc;
|
||||
}
|
||||
|
||||
public Integer getServiceDuration() {
|
||||
return serviceDuration;
|
||||
}
|
||||
|
||||
public void setServiceDuration(Integer serviceDuration) {
|
||||
this.serviceDuration = serviceDuration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.example.petshopdesktop.api.dto.supplier;
|
||||
|
||||
public class SupplierRequest {
|
||||
private String supCompany;
|
||||
private String supContactFirstName;
|
||||
private String supContactLastName;
|
||||
private String supPhone;
|
||||
private String supEmail;
|
||||
private String address;
|
||||
|
||||
public SupplierRequest() {
|
||||
}
|
||||
|
||||
public String getSupCompany() {
|
||||
return supCompany;
|
||||
}
|
||||
|
||||
public void setSupCompany(String supCompany) {
|
||||
this.supCompany = supCompany;
|
||||
}
|
||||
|
||||
public String getSupContactFirstName() {
|
||||
return supContactFirstName;
|
||||
}
|
||||
|
||||
public void setSupContactFirstName(String supContactFirstName) {
|
||||
this.supContactFirstName = supContactFirstName;
|
||||
}
|
||||
|
||||
public String getSupContactLastName() {
|
||||
return supContactLastName;
|
||||
}
|
||||
|
||||
public void setSupContactLastName(String supContactLastName) {
|
||||
this.supContactLastName = supContactLastName;
|
||||
}
|
||||
|
||||
public String getSupPhone() {
|
||||
return supPhone;
|
||||
}
|
||||
|
||||
public void setSupPhone(String supPhone) {
|
||||
this.supPhone = supPhone;
|
||||
}
|
||||
|
||||
public String getSupEmail() {
|
||||
return supEmail;
|
||||
}
|
||||
|
||||
public void setSupEmail(String supEmail) {
|
||||
this.supEmail = supEmail;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.example.petshopdesktop.api.dto.supplier;
|
||||
|
||||
public class SupplierResponse {
|
||||
private Long supId;
|
||||
private String supCompany;
|
||||
private String supContactFirstName;
|
||||
private String supContactLastName;
|
||||
private String supPhone;
|
||||
private String supEmail;
|
||||
private String address;
|
||||
|
||||
public SupplierResponse() {
|
||||
}
|
||||
|
||||
public Long getSupId() {
|
||||
return supId;
|
||||
}
|
||||
|
||||
public void setSupId(Long supId) {
|
||||
this.supId = supId;
|
||||
}
|
||||
|
||||
public String getSupCompany() {
|
||||
return supCompany;
|
||||
}
|
||||
|
||||
public void setSupCompany(String supCompany) {
|
||||
this.supCompany = supCompany;
|
||||
}
|
||||
|
||||
public String getSupContactFirstName() {
|
||||
return supContactFirstName;
|
||||
}
|
||||
|
||||
public void setSupContactFirstName(String supContactFirstName) {
|
||||
this.supContactFirstName = supContactFirstName;
|
||||
}
|
||||
|
||||
public String getSupContactLastName() {
|
||||
return supContactLastName;
|
||||
}
|
||||
|
||||
public void setSupContactLastName(String supContactLastName) {
|
||||
this.supContactLastName = supContactLastName;
|
||||
}
|
||||
|
||||
public String getSupPhone() {
|
||||
return supPhone;
|
||||
}
|
||||
|
||||
public void setSupPhone(String supPhone) {
|
||||
this.supPhone = supPhone;
|
||||
}
|
||||
|
||||
public String getSupEmail() {
|
||||
return supEmail;
|
||||
}
|
||||
|
||||
public void setSupEmail(String supEmail) {
|
||||
this.supEmail = supEmail;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.example.petshopdesktop.api.dto.user;
|
||||
|
||||
public class UserRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String role;
|
||||
private Boolean active;
|
||||
|
||||
public UserRequest() {
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Boolean getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(Boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.example.petshopdesktop.api.dto.user;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class UserResponse {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String role;
|
||||
private Boolean active;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public UserResponse() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Boolean getActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(Boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.adoption.AdoptionRequest;
|
||||
import org.example.petshopdesktop.api.dto.adoption.AdoptionResponse;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class AdoptionApi {
|
||||
private static final AdoptionApi INSTANCE = new AdoptionApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private AdoptionApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static AdoptionApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<AdoptionResponse> listAdoptions(String query) throws Exception {
|
||||
String path = "/api/v1/adoptions?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<AdoptionResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<AdoptionResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from adoptions endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public AdoptionResponse createAdoption(AdoptionRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/adoptions", request, AdoptionResponse.class);
|
||||
}
|
||||
|
||||
public AdoptionResponse updateAdoption(Long id, AdoptionRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/adoptions/" + id, request, AdoptionResponse.class);
|
||||
}
|
||||
|
||||
public void deleteAdoptions(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/adoptions", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.analytics.DashboardResponse;
|
||||
|
||||
public class AnalyticsApi {
|
||||
private static final AnalyticsApi INSTANCE = new AnalyticsApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private AnalyticsApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static AnalyticsApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public DashboardResponse getDashboard(int days, int top) throws Exception {
|
||||
String path = "/api/v1/analytics/dashboard?days=" + days + "&top=" + top;
|
||||
return apiClient.get(path, DashboardResponse.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentRequest;
|
||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentResponse;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class AppointmentApi {
|
||||
private static final AppointmentApi INSTANCE = new AppointmentApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private AppointmentApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static AppointmentApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<AppointmentResponse> listAppointments(String query) throws Exception {
|
||||
String path = "/api/v1/appointments?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<AppointmentResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<AppointmentResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from appointments endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public AppointmentResponse createAppointment(AppointmentRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/appointments", request, AppointmentResponse.class);
|
||||
}
|
||||
|
||||
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/appointments/" + id, request, AppointmentResponse.class);
|
||||
}
|
||||
|
||||
public void deleteAppointments(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/appointments", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DropdownApi {
|
||||
private static final DropdownApi INSTANCE = new DropdownApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private DropdownApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static DropdownApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<DropdownOption> getCategories() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/categories");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from categories endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getProducts() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/products");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from products endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getSuppliers() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/suppliers");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from suppliers endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getServices() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/services");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from services endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getCustomers() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/customers");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from customers endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getPets() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/pets");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from pets endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
|
||||
public List<DropdownOption> getStores() throws Exception {
|
||||
String response = apiClient.getRawResponse("/api/v1/dropdowns/stores");
|
||||
if (response == null || response.isEmpty()) {
|
||||
throw new IllegalStateException("Empty response from stores endpoint");
|
||||
}
|
||||
return apiClient.getObjectMapper().readValue(response, new TypeReference<List<DropdownOption>>() {});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.inventory.InventoryRequest;
|
||||
import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class InventoryApi {
|
||||
private static final InventoryApi INSTANCE = new InventoryApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private InventoryApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static InventoryApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<InventoryResponse> listInventory(String query) throws Exception {
|
||||
String path = "/api/v1/inventory?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<InventoryResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<InventoryResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from inventory endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public InventoryResponse createInventory(InventoryRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/inventory", request, InventoryResponse.class);
|
||||
}
|
||||
|
||||
public InventoryResponse updateInventory(Long id, InventoryRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/inventory/" + id, request, InventoryResponse.class);
|
||||
}
|
||||
|
||||
public void deleteInventory(Long id) throws Exception {
|
||||
apiClient.delete("/api/v1/inventory/" + id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.pet.PetRequest;
|
||||
import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class PetApi {
|
||||
private static final PetApi INSTANCE = new PetApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private PetApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static PetApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<PetResponse> listPets(String query) throws Exception {
|
||||
String path = "/api/v1/pets?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<PetResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<PetResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from pets endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public PetResponse createPet(PetRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/pets", request, PetResponse.class);
|
||||
}
|
||||
|
||||
public PetResponse updatePet(Long id, PetRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/pets/" + id, request, PetResponse.class);
|
||||
}
|
||||
|
||||
public void deletePets(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/pets", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductRequest;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class ProductApi {
|
||||
private static final ProductApi INSTANCE = new ProductApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private ProductApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static ProductApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<ProductResponse> listProducts(String query) throws Exception {
|
||||
String path = "/api/v1/products?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<ProductResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<ProductResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from products endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public ProductResponse createProduct(ProductRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/products", request, ProductResponse.class);
|
||||
}
|
||||
|
||||
public ProductResponse updateProduct(Long id, ProductRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/products/" + id, request, ProductResponse.class);
|
||||
}
|
||||
|
||||
public void deleteProducts(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/products", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierRequest;
|
||||
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class ProductSupplierApi {
|
||||
private static final ProductSupplierApi INSTANCE = new ProductSupplierApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private ProductSupplierApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static ProductSupplierApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<ProductSupplierResponse> listProductSuppliers(String query) throws Exception {
|
||||
String path = "/api/v1/product-suppliers?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<ProductSupplierResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<ProductSupplierResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from product-suppliers endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public ProductSupplierResponse createProductSupplier(ProductSupplierRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/product-suppliers", request, ProductSupplierResponse.class);
|
||||
}
|
||||
|
||||
public ProductSupplierResponse updateProductSupplier(Long productId, Long supplierId, ProductSupplierRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/product-suppliers/" + productId + "/" + supplierId, request, ProductSupplierResponse.class);
|
||||
}
|
||||
|
||||
public void deleteProductSupplier(Long productId, Long supplierId) throws Exception {
|
||||
apiClient.delete("/api/v1/product-suppliers/" + productId + "/" + supplierId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.purchaseorder.PurchaseOrderResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class PurchaseOrderApi {
|
||||
private static final PurchaseOrderApi INSTANCE = new PurchaseOrderApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private PurchaseOrderApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static PurchaseOrderApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<PurchaseOrderResponse> listPurchaseOrders(String query) throws Exception {
|
||||
String path = "/api/v1/purchase-orders?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<PurchaseOrderResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<PurchaseOrderResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from purchase-orders endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleRequest;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class SaleApi {
|
||||
private static final SaleApi INSTANCE = new SaleApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private SaleApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static SaleApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<SaleResponse> listSales(int page, int size, String query) throws Exception {
|
||||
String path = "/api/v1/sales?page=" + page + "&size=" + size;
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<SaleResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<SaleResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from sales endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public SaleResponse getSale(Long id) throws Exception {
|
||||
return apiClient.get("/api/v1/sales/" + id, SaleResponse.class);
|
||||
}
|
||||
|
||||
public SaleResponse createSale(SaleRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/sales", request, SaleResponse.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.service.ServiceRequest;
|
||||
import org.example.petshopdesktop.api.dto.service.ServiceResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceApi {
|
||||
private static final ServiceApi INSTANCE = new ServiceApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private ServiceApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static ServiceApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<ServiceResponse> listServices(String query) throws Exception {
|
||||
String path = "/api/v1/services?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<ServiceResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<ServiceResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from services endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public ServiceResponse createService(ServiceRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/services", request, ServiceResponse.class);
|
||||
}
|
||||
|
||||
public ServiceResponse updateService(Long id, ServiceRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/services/" + id, request, ServiceResponse.class);
|
||||
}
|
||||
|
||||
public void deleteServices(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/services", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.BulkDeleteRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.supplier.SupplierRequest;
|
||||
import org.example.petshopdesktop.api.dto.supplier.SupplierResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class SupplierApi {
|
||||
private static final SupplierApi INSTANCE = new SupplierApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private SupplierApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static SupplierApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<SupplierResponse> listSuppliers(String query) throws Exception {
|
||||
String path = "/api/v1/suppliers?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<SupplierResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<SupplierResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from suppliers endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public SupplierResponse createSupplier(SupplierRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/suppliers", request, SupplierResponse.class);
|
||||
}
|
||||
|
||||
public SupplierResponse updateSupplier(Long id, SupplierRequest request) throws Exception {
|
||||
return apiClient.put("/api/v1/suppliers/" + id, request, SupplierResponse.class);
|
||||
}
|
||||
|
||||
public void deleteSuppliers(List<Long> ids) throws Exception {
|
||||
apiClient.deleteWithBody("/api/v1/suppliers", new BulkDeleteRequest(ids));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.common.PageResponse;
|
||||
import org.example.petshopdesktop.api.dto.user.UserRequest;
|
||||
import org.example.petshopdesktop.api.dto.user.UserResponse;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class UserApi {
|
||||
private static final UserApi INSTANCE = new UserApi();
|
||||
private final ApiClient apiClient;
|
||||
|
||||
private UserApi() {
|
||||
this.apiClient = ApiClient.getInstance();
|
||||
}
|
||||
|
||||
public static UserApi getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<UserResponse> listUsers(String query) throws Exception {
|
||||
String path = "/api/v1/users?page=0&size=1000";
|
||||
if (query != null && !query.isEmpty()) {
|
||||
path += "&q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
|
||||
}
|
||||
String response = apiClient.getRawResponse(path);
|
||||
PageResponse<UserResponse> pageResponse = apiClient.getObjectMapper().readValue(
|
||||
response,
|
||||
new TypeReference<PageResponse<UserResponse>>() {}
|
||||
);
|
||||
if (pageResponse == null) {
|
||||
throw new IllegalStateException("Null response from users endpoint");
|
||||
}
|
||||
return pageResponse.getContent();
|
||||
}
|
||||
|
||||
public UserResponse createUser(UserRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/users", request, UserResponse.class);
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package org.example.petshopdesktop.auth;
|
||||
public class UserSession {
|
||||
private static UserSession instance;
|
||||
|
||||
private Integer userId;
|
||||
private Integer employeeId;
|
||||
private Long userId;
|
||||
private Long employeeId;
|
||||
private String username;
|
||||
private String employeeName;
|
||||
private Role role;
|
||||
private String jwtToken;
|
||||
private Long storeId;
|
||||
|
||||
private UserSession() {}
|
||||
|
||||
@@ -18,12 +20,13 @@ public class UserSession {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void login(int userId, int employeeId, String username, String employeeName, Role role) {
|
||||
public void login(Long userId, String username, Role role, String jwtToken) {
|
||||
this.userId = userId;
|
||||
this.employeeId = employeeId;
|
||||
this.employeeId = userId;
|
||||
this.username = username;
|
||||
this.employeeName = employeeName;
|
||||
this.employeeName = username;
|
||||
this.role = role;
|
||||
this.jwtToken = jwtToken;
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
@@ -32,13 +35,15 @@ public class UserSession {
|
||||
this.username = null;
|
||||
this.employeeName = null;
|
||||
this.role = null;
|
||||
this.jwtToken = null;
|
||||
this.storeId = null;
|
||||
}
|
||||
|
||||
public Integer getUserId() {
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Integer getEmployeeId() {
|
||||
public Long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
|
||||
@@ -54,6 +59,18 @@ public class UserSession {
|
||||
return role;
|
||||
}
|
||||
|
||||
public String getJwtToken() {
|
||||
return jwtToken;
|
||||
}
|
||||
|
||||
public Long getStoreId() {
|
||||
return storeId;
|
||||
}
|
||||
|
||||
public void setStoreId(Long storeId) {
|
||||
this.storeId = storeId;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return username != null && role != null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -10,15 +11,16 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.adoption.AdoptionResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController;
|
||||
import org.example.petshopdesktop.database.AdoptionDB;
|
||||
import org.example.petshopdesktop.models.Adoption;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AdoptionController {
|
||||
|
||||
@@ -35,7 +37,7 @@ public class AdoptionController {
|
||||
private TableColumn<Adoption, Integer> colAdoptionId;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Adoption, Integer> colPetId;
|
||||
private TableColumn<Adoption, String> colPetId;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Adoption, String> colCustomerName;
|
||||
@@ -66,7 +68,7 @@ public class AdoptionController {
|
||||
tvAdoptions.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
||||
|
||||
colAdoptionId.setCellValueFactory(new PropertyValueFactory<>("adoptionId"));
|
||||
colPetId.setCellValueFactory(new PropertyValueFactory<>("petId"));
|
||||
colPetId.setCellValueFactory(new PropertyValueFactory<>("petName"));
|
||||
colCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName"));
|
||||
colAdoptionDate.setCellValueFactory(new PropertyValueFactory<>("adoptionDate"));
|
||||
colAdoptionFee.setCellValueFactory(new PropertyValueFactory<>("adoptionFee"));
|
||||
@@ -118,47 +120,24 @@ public class AdoptionController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedAdoptions.stream()
|
||||
.map(a -> (long) a.getAdoptionId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Adoption adoption : selectedAdoptions) {
|
||||
try {
|
||||
int numRows = AdoptionDB.deleteAdoption(adoption.getAdoptionId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete adoption ID %d - foreign key constraint", adoption.getAdoptionId()));
|
||||
failCount++;
|
||||
errors.append("Adoption ID ").append(adoption.getAdoptionId()).append(" is referenced in another table\n");
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete adoption ID %d", adoption.getAdoptionId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete adoption ID ").append(adoption.getAdoptionId()).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
AdoptionApi.getInstance().deleteAdoptions(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " adoption record(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " adoption record(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting adoptions");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -181,35 +160,55 @@ public class AdoptionController {
|
||||
}
|
||||
|
||||
private void displayFilteredAdoptions(String filter) {
|
||||
data.clear();
|
||||
try {
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
|
||||
displayAdoptions();
|
||||
} else {
|
||||
data = AdoptionDB.getFilteredAdoptions(filter);
|
||||
tvAdoptions.setItems(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.displayFilteredAdoptions",
|
||||
e,
|
||||
"Filtering adoptions with filter: " + filter);
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
|
||||
displayAdoptions();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(filter);
|
||||
List<Adoption> adoptionList = adoptions.stream()
|
||||
.map(this::mapToAdoption)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(adoptionList);
|
||||
tvAdoptions.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.displayFilteredAdoptions",
|
||||
e,
|
||||
"Filtering adoptions with filter: " + filter);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayAdoptions() {
|
||||
data.clear();
|
||||
try {
|
||||
data = AdoptionDB.getAdoptions();
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.displayAdoptions",
|
||||
e,
|
||||
"Fetching adoption data for table display");
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
}
|
||||
tvAdoptions.setItems(data);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(null);
|
||||
List<Adoption> adoptionList = adoptions.stream()
|
||||
.map(this::mapToAdoption)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(adoptionList);
|
||||
tvAdoptions.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionController.displayAdoptions",
|
||||
e,
|
||||
"Fetching adoption data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void openDialog(Adoption adoption, String mode) {
|
||||
@@ -244,4 +243,17 @@ public class AdoptionController {
|
||||
btnEdit.setDisable(true);
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private Adoption mapToAdoption(AdoptionResponse response) {
|
||||
return new Adoption(
|
||||
response.getAdoptionId().intValue(),
|
||||
0,
|
||||
0,
|
||||
response.getPetName(),
|
||||
response.getCustomerName(),
|
||||
response.getAdoptionDate() != null ? response.getAdoptionDate().toString() : "",
|
||||
0.0,
|
||||
response.getAdoptionStatus()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.chart.*;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.database.SaleDB;
|
||||
import org.example.petshopdesktop.models.analytics.*;
|
||||
import org.example.petshopdesktop.api.dto.analytics.DailySales;
|
||||
import org.example.petshopdesktop.api.dto.analytics.DashboardResponse;
|
||||
import org.example.petshopdesktop.api.dto.analytics.TopProduct;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.AnalyticsApi;
|
||||
import org.example.petshopdesktop.api.endpoints.SaleApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.NumberFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AnalyticsController {
|
||||
|
||||
@@ -78,79 +84,119 @@ public class AnalyticsController {
|
||||
|
||||
private void loadAnalyticsData() {
|
||||
lblError.setVisible(false);
|
||||
try {
|
||||
loadSummaryData();
|
||||
loadSalesOverTime();
|
||||
loadTopProductsByRevenue();
|
||||
loadTopProductsByQuantity();
|
||||
loadPaymentMethodDistribution();
|
||||
loadEmployeePerformance();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
|
||||
lblError.setText("Error loading analytics data. Please try again.");
|
||||
lblError.setVisible(true);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
DashboardResponse dashboard = AnalyticsApi.getInstance().getDashboard(30, 10);
|
||||
List<SaleResponse> sales = SaleApi.getInstance().listSales(0, Integer.MAX_VALUE, null);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
loadSummaryData(dashboard);
|
||||
loadSalesOverTime(dashboard);
|
||||
loadTopProductsByRevenue(dashboard);
|
||||
loadTopProductsByQuantity(dashboard);
|
||||
loadPaymentMethodDistribution(sales);
|
||||
loadEmployeePerformance(sales);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
|
||||
lblError.setText("Error loading analytics data. Please try again.");
|
||||
lblError.setVisible(true);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException("AnalyticsController.loadAnalyticsData", e, "Loading analytics data");
|
||||
lblError.setText("Error loading analytics data. Please try again.");
|
||||
lblError.setVisible(true);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void loadSummaryData(DashboardResponse dashboard) throws Exception {
|
||||
if (dashboard != null) {
|
||||
BigDecimal totalRevenue = BigDecimal.ZERO;
|
||||
Long totalSales = 0L;
|
||||
Long totalProducts = 0L;
|
||||
|
||||
if (dashboard.getSalesSummary() != null) {
|
||||
totalRevenue = dashboard.getSalesSummary().getTotalRevenue() != null ? dashboard.getSalesSummary().getTotalRevenue() : BigDecimal.ZERO;
|
||||
totalSales = dashboard.getSalesSummary().getTotalSales() != null ? dashboard.getSalesSummary().getTotalSales() : 0L;
|
||||
}
|
||||
|
||||
if (dashboard.getInventorySummary() != null) {
|
||||
totalProducts = dashboard.getInventorySummary().getTotalProducts() != null ? dashboard.getInventorySummary().getTotalProducts() : 0L;
|
||||
}
|
||||
|
||||
lblTotalRevenue.setText(currency.format(totalRevenue));
|
||||
lblTotalTransactions.setText(wholeNumber.format(totalSales));
|
||||
|
||||
BigDecimal avgTransaction = BigDecimal.ZERO;
|
||||
if (totalSales > 0) {
|
||||
avgTransaction = totalRevenue.divide(BigDecimal.valueOf(totalSales), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
lblAvgTransaction.setText(currency.format(avgTransaction));
|
||||
lblTotalItems.setText(wholeNumber.format(totalProducts));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSummaryData() throws Exception {
|
||||
SalesSummary summary = SaleDB.getSalesSummary();
|
||||
if (summary != null) {
|
||||
lblTotalRevenue.setText(currency.format(summary.getTotalRevenue()));
|
||||
lblTotalTransactions.setText(wholeNumber.format(summary.getTotalTransactions()));
|
||||
lblAvgTransaction.setText(currency.format(summary.getAvgTransactionValue()));
|
||||
lblTotalItems.setText(wholeNumber.format(summary.getTotalItemsSold()));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSalesOverTime() throws Exception {
|
||||
ObservableList<DailySalesData> data = SaleDB.getDailySalesRevenue();
|
||||
private void loadSalesOverTime(DashboardResponse dashboard) throws Exception {
|
||||
List<DailySales> dailySales = dashboard.getDailySales() != null ? dashboard.getDailySales() : new ArrayList<>();
|
||||
XYChart.Series<String, Number> series = new XYChart.Series<>();
|
||||
series.setName("Daily Revenue");
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd");
|
||||
for (DailySalesData dailySale : data) {
|
||||
String dateStr = dailySale.getDate().format(formatter);
|
||||
series.getData().add(new XYChart.Data<>(dateStr, dailySale.getRevenue()));
|
||||
for (DailySales dailySale : dailySales) {
|
||||
String dateStr = dailySale.getDate();
|
||||
BigDecimal revenue = dailySale.getRevenue() != null ? dailySale.getRevenue() : BigDecimal.ZERO;
|
||||
series.getData().add(new XYChart.Data<>(dateStr, revenue));
|
||||
}
|
||||
|
||||
chartSalesOverTime.getData().clear();
|
||||
chartSalesOverTime.getData().add(series);
|
||||
}
|
||||
|
||||
private void loadTopProductsByRevenue() throws Exception {
|
||||
ObservableList<ProductSalesData> data = SaleDB.getTopProductsByRevenue(10);
|
||||
private void loadTopProductsByRevenue(DashboardResponse dashboard) throws Exception {
|
||||
List<TopProduct> topProducts = dashboard.getTopProducts() != null ? dashboard.getTopProducts() : new ArrayList<>();
|
||||
XYChart.Series<Number, String> series = new XYChart.Series<>();
|
||||
series.setName("Revenue");
|
||||
|
||||
for (ProductSalesData product : data) {
|
||||
series.getData().add(new XYChart.Data<>(product.getTotalRevenue(), product.getProductName()));
|
||||
for (TopProduct product : topProducts) {
|
||||
BigDecimal revenue = product.getRevenue() != null ? product.getRevenue() : BigDecimal.ZERO;
|
||||
series.getData().add(new XYChart.Data<>(revenue, product.getProductName()));
|
||||
}
|
||||
|
||||
chartTopRevenue.getData().clear();
|
||||
chartTopRevenue.getData().add(series);
|
||||
}
|
||||
|
||||
private void loadTopProductsByQuantity() throws Exception {
|
||||
ObservableList<ProductSalesData> data = SaleDB.getTopProductsByQuantity(10);
|
||||
private void loadTopProductsByQuantity(DashboardResponse dashboard) throws Exception {
|
||||
List<TopProduct> topProducts = dashboard.getTopProducts() != null ? dashboard.getTopProducts() : new ArrayList<>();
|
||||
XYChart.Series<Number, String> series = new XYChart.Series<>();
|
||||
series.setName("Quantity");
|
||||
|
||||
for (ProductSalesData product : data) {
|
||||
series.getData().add(new XYChart.Data<>(product.getTotalQuantity(), product.getProductName()));
|
||||
for (TopProduct product : topProducts) {
|
||||
Long quantitySold = product.getQuantitySold() != null ? product.getQuantitySold() : 0L;
|
||||
series.getData().add(new XYChart.Data<>(quantitySold, product.getProductName()));
|
||||
}
|
||||
|
||||
chartTopQuantity.getData().clear();
|
||||
chartTopQuantity.getData().add(series);
|
||||
}
|
||||
|
||||
private void loadPaymentMethodDistribution() throws Exception {
|
||||
ObservableList<PaymentMethodData> data = SaleDB.getPaymentMethodDistribution();
|
||||
private void loadPaymentMethodDistribution(List<SaleResponse> sales) throws Exception {
|
||||
Map<String, Long> paymentMethodCount = sales.stream()
|
||||
.filter(sale -> sale.getIsRefund() == null || !sale.getIsRefund())
|
||||
.collect(Collectors.groupingBy(
|
||||
sale -> sale.getPaymentMethod() != null ? sale.getPaymentMethod() : "Unknown",
|
||||
Collectors.counting()
|
||||
));
|
||||
|
||||
chartPaymentMethods.getData().clear();
|
||||
|
||||
for (PaymentMethodData payment : data) {
|
||||
for (Map.Entry<String, Long> entry : paymentMethodCount.entrySet()) {
|
||||
PieChart.Data slice = new PieChart.Data(
|
||||
payment.getPaymentMethod() + " (" + payment.getTransactionCount() + ")",
|
||||
payment.getTransactionCount()
|
||||
entry.getKey() + " (" + entry.getValue() + ")",
|
||||
entry.getValue()
|
||||
);
|
||||
chartPaymentMethods.getData().add(slice);
|
||||
}
|
||||
@@ -158,13 +204,20 @@ public class AnalyticsController {
|
||||
chartPaymentMethods.setLabelsVisible(false);
|
||||
}
|
||||
|
||||
private void loadEmployeePerformance() throws Exception {
|
||||
ObservableList<EmployeeSalesData> data = SaleDB.getEmployeeSalesPerformance();
|
||||
private void loadEmployeePerformance(List<SaleResponse> sales) throws Exception {
|
||||
Map<String, Double> employeeRevenue = sales.stream()
|
||||
.filter(sale -> sale.getIsRefund() == null || !sale.getIsRefund())
|
||||
.filter(sale -> sale.getEmployeeName() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
SaleResponse::getEmployeeName,
|
||||
Collectors.summingDouble(sale -> sale.getTotalAmount() != null ? sale.getTotalAmount().doubleValue() : 0.0)
|
||||
));
|
||||
|
||||
XYChart.Series<String, Number> series = new XYChart.Series<>();
|
||||
series.setName("Revenue");
|
||||
|
||||
for (EmployeeSalesData employee : data) {
|
||||
series.getData().add(new XYChart.Data<>(employee.getEmployeeName(), employee.getTotalRevenue()));
|
||||
for (Map.Entry<String, Double> entry : employeeRevenue.entrySet()) {
|
||||
series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
|
||||
chartEmployeePerformance.getData().clear();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
@@ -13,10 +14,14 @@ import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.example.petshopdesktop.DTOs.AppointmentDTO;
|
||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.AppointmentApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.AppointmentDialogController;
|
||||
import org.example.petshopdesktop.database.AppointmentDB;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AppointmentController {
|
||||
|
||||
@FXML private TableView<AppointmentDTO> tvAppointments;
|
||||
@@ -71,41 +76,50 @@ public class AppointmentController {
|
||||
}
|
||||
|
||||
private void loadAppointments(){
|
||||
try{
|
||||
appointments.setAll(AppointmentDB.getAppointmentDTOs());
|
||||
}catch(Exception e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentController.loadAppointments",
|
||||
e,
|
||||
"Loading appointments for table display");
|
||||
e.printStackTrace();
|
||||
}
|
||||
new Thread(() -> {
|
||||
try{
|
||||
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(null);
|
||||
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
||||
.map(this::mapToAppointmentDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
appointments.setAll(appointmentDTOs);
|
||||
});
|
||||
}catch(Exception e){
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentController.loadAppointments",
|
||||
e,
|
||||
"Loading appointments for table display");
|
||||
e.printStackTrace();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void applyFilter(String text) {
|
||||
if (filtered == null) {
|
||||
return;
|
||||
}
|
||||
String query = text == null || text.trim().isEmpty() ? null : text.trim();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<AppointmentResponse> responses = AppointmentApi.getInstance().listAppointments(query);
|
||||
List<AppointmentDTO> appointmentDTOs = responses.stream()
|
||||
.map(this::mapToAppointmentDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
String q = text == null ? "" : text.trim().toLowerCase();
|
||||
if (q.isEmpty()) {
|
||||
filtered.setPredicate(a -> true);
|
||||
return;
|
||||
}
|
||||
|
||||
filtered.setPredicate(a ->
|
||||
String.valueOf(a.getAppointmentId()).contains(q)
|
||||
|| safe(a.getPetName()).contains(q)
|
||||
|| safe(a.getServiceName()).contains(q)
|
||||
|| safe(a.getAppointmentDate()).contains(q)
|
||||
|| safe(a.getAppointmentTime()).contains(q)
|
||||
|| safe(a.getCustomerName()).contains(q)
|
||||
|| safe(a.getAppointmentStatus()).contains(q)
|
||||
);
|
||||
}
|
||||
|
||||
private static String safe(String v) {
|
||||
return v == null ? "" : v.toLowerCase();
|
||||
Platform.runLater(() -> {
|
||||
appointments.setAll(appointmentDTOs);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentController.applyFilter",
|
||||
e,
|
||||
String.format("Filtering appointments with query: %s", query));
|
||||
e.printStackTrace();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -145,35 +159,24 @@ public class AppointmentController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedAppointments.stream()
|
||||
.map(a -> (long) a.getAppointmentId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (AppointmentDTO appointment : selectedAppointments) {
|
||||
try{
|
||||
AppointmentDB.deleteAppointment(appointment.getAppointmentId());
|
||||
successCount++;
|
||||
}catch(Exception e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete appointment ID %d", appointment.getAppointmentId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete appointment ID ").append(appointment.getAppointmentId()).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
AppointmentApi.getInstance().deleteAppointments(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " appointment(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " appointment(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting appointments");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -225,4 +228,19 @@ public class AppointmentController {
|
||||
alert.setContentText(msg);
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
private AppointmentDTO mapToAppointmentDTO(AppointmentResponse response) {
|
||||
return new AppointmentDTO(
|
||||
response.getAppointmentId().intValue(),
|
||||
0,
|
||||
response.getCustomerName(),
|
||||
0,
|
||||
String.join(", ", response.getPetNames()),
|
||||
0,
|
||||
response.getServiceName(),
|
||||
response.getAppointmentDate().toString(),
|
||||
response.getAppointmentTime().toString(),
|
||||
response.getAppointmentStatus()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -10,15 +11,16 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.InventoryDialogController;
|
||||
import org.example.petshopdesktop.database.InventoryDB;
|
||||
import org.example.petshopdesktop.models.Inventory;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InventoryController {
|
||||
|
||||
@@ -58,11 +60,9 @@ public class InventoryController {
|
||||
//Loads upon view bootup
|
||||
@FXML
|
||||
void initialize() {
|
||||
//Buttons disabled until row is selected
|
||||
btnEdit.setDisable(true);
|
||||
btnDelete.setDisable(true);
|
||||
//Enable multiple selection
|
||||
tvInventory.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
||||
tvInventory.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.SINGLE);
|
||||
|
||||
colInventoryId.setCellValueFactory(new PropertyValueFactory<>("inventoryId"));
|
||||
colProductId.setCellValueFactory(new PropertyValueFactory<>("prodId"));
|
||||
@@ -71,19 +71,16 @@ public class InventoryController {
|
||||
|
||||
displayInventory();
|
||||
|
||||
//Enables buttons when row is selected
|
||||
tvInventory.getSelectionModel().selectedItemProperty().addListener(
|
||||
(observable, oldValue, newValue) -> {
|
||||
btnEdit.setDisable(false);
|
||||
btnDelete.setDisable(false);
|
||||
});
|
||||
|
||||
//Filter as user types
|
||||
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
displayFilteredInventory(newValue);
|
||||
});
|
||||
|
||||
//EventListener for DELETE key
|
||||
tvInventory.setOnKeyPressed(event -> {
|
||||
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
||||
if (tvInventory.getSelectionModel().getSelectedItem() != null) {
|
||||
@@ -100,71 +97,35 @@ public class InventoryController {
|
||||
openDialog(null, mode);
|
||||
}
|
||||
|
||||
//Prompts user for confirmation prior to deletion
|
||||
@FXML
|
||||
void btnDeleteClicked(ActionEvent event) {
|
||||
//get selected inventory records
|
||||
var selectedInventory = tvInventory.getSelectionModel().getSelectedItems();
|
||||
if (selectedInventory.isEmpty()) return;
|
||||
Inventory selectedInventory = tvInventory.getSelectionModel().getSelectedItem();
|
||||
if (selectedInventory == null) return;
|
||||
|
||||
//ask user to confirm
|
||||
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
question.setHeaderText("Please confirm delete");
|
||||
String message = selectedInventory.size() == 1
|
||||
? "Are you sure you want to delete this inventory record?"
|
||||
: "Are you sure you want to delete " + selectedInventory.size() + " inventory records?";
|
||||
question.setContentText(message);
|
||||
question.setContentText("Are you sure you want to delete this inventory record?");
|
||||
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
|
||||
Optional<ButtonType> result = question.showAndWait();
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
|
||||
for (Inventory inventory : selectedInventory) {
|
||||
try {
|
||||
int numRows = InventoryDB.deleteInventory(inventory.getInventoryId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete inventory ID %d - foreign key constraint", inventory.getInventoryId()));
|
||||
failCount++;
|
||||
errors.append("Inventory record '").append(inventory.getProdName()).append("' is referenced in another table\n");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete inventory ID %d", inventory.getInventoryId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete '").append(inventory.getProdName()).append("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
InventoryApi.getInstance().deleteInventory((long) selectedInventory.getInventoryId());
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " inventory record(s)");
|
||||
alert.setContentText("Successfully deleted inventory record");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting inventory");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
//refresh display and reset inputs
|
||||
displayInventory();
|
||||
btnDelete.setDisable(true);
|
||||
btnEdit.setDisable(true);
|
||||
@@ -183,66 +144,72 @@ public class InventoryController {
|
||||
}
|
||||
}
|
||||
|
||||
//Search filter
|
||||
private void displayFilteredInventory(String filter) {
|
||||
data.clear();
|
||||
try {
|
||||
//If search box is empty, display all inventory
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
|
||||
displayInventory();
|
||||
}
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
|
||||
displayInventory();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(filter);
|
||||
List<Inventory> inventoryList = inventories.stream()
|
||||
.map(this::mapToInventory)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
else {
|
||||
data = InventoryDB.getFilteredInventory(filter);
|
||||
tvInventory.setItems(data);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.displayFilteredInventory",
|
||||
e,
|
||||
"Filtering inventory with filter: " + filter);
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(inventoryList);
|
||||
tvInventory.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.displayFilteredInventory",
|
||||
e,
|
||||
String.format("Filtering inventory with keyword: %s", filter));
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
//Displays all records from DB
|
||||
private void displayInventory() {
|
||||
data.clear();
|
||||
try {
|
||||
data = InventoryDB.getInventory();
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<InventoryResponse> inventories = InventoryApi.getInstance().listInventory(null);
|
||||
List<Inventory> inventoryList = inventories.stream()
|
||||
.map(this::mapToInventory)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.displayInventory",
|
||||
e,
|
||||
"Fetching inventory data for table display");
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
}
|
||||
tvInventory.setItems(data);
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(inventoryList);
|
||||
tvInventory.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.displayInventory",
|
||||
e,
|
||||
"Fetching inventory data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
//Opens inventory-dialog-view
|
||||
private void openDialog(Inventory inventory, String mode) {
|
||||
//Opens FXML
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/inventory-dialog-view.fxml"));
|
||||
Scene scene = null;
|
||||
|
||||
try {
|
||||
scene = new Scene(fxmlLoader.load());
|
||||
}
|
||||
|
||||
catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryController.openDialog",
|
||||
e,
|
||||
"Loading inventory dialog in " + mode + " mode");
|
||||
String.format("Loading inventory dialog view in %s mode", mode));
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
//Passes data and mode to the view
|
||||
InventoryDialogController dialogController = fxmlLoader.getController();
|
||||
dialogController.setMode(mode);
|
||||
|
||||
@@ -256,10 +223,22 @@ public class InventoryController {
|
||||
dialogStage.setScene(scene);
|
||||
dialogStage.showAndWait();
|
||||
|
||||
//Refresh inventory
|
||||
displayInventory();
|
||||
btnDelete.setDisable(true);
|
||||
btnEdit.setDisable(true);
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private Inventory mapToInventory(InventoryResponse response) {
|
||||
return new Inventory(
|
||||
response.getInventoryId().intValue(),
|
||||
0,
|
||||
response.getProductName(),
|
||||
response.getCategoryName() != null ? response.getCategoryName() : "",
|
||||
0,
|
||||
"N/A",
|
||||
response.getQuantity() != null ? response.getQuantity() : 0,
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,17 @@ import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.auth.LoginRequest;
|
||||
import org.example.petshopdesktop.api.dto.auth.LoginResponse;
|
||||
import org.example.petshopdesktop.api.dto.auth.UserInfoResponse;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.auth.Role;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.database.ConnectionDB;
|
||||
import org.example.petshopdesktop.database.UserDB;
|
||||
import org.example.petshopdesktop.models.User;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public class LoginController {
|
||||
|
||||
@@ -32,15 +36,6 @@ public class LoginController {
|
||||
@FXML
|
||||
public void initialize() {
|
||||
lblError.setText("");
|
||||
try {
|
||||
ConnectionDB.getConnection().close();
|
||||
try {
|
||||
UserDB.initializeTable();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
lblError.setText("Database is not connected. Check Docker and connectionpetstore.properties.");
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -54,39 +49,60 @@ public class LoginController {
|
||||
}
|
||||
|
||||
try {
|
||||
User user = UserDB.authenticate(username, password);
|
||||
if (user == null) {
|
||||
lblError.setText("Invalid username or password.");
|
||||
ApiClient apiClient = ApiClient.getInstance();
|
||||
|
||||
LoginRequest loginRequest = new LoginRequest(username, password);
|
||||
LoginResponse loginResponse = apiClient.post("/api/v1/auth/login", loginRequest, LoginResponse.class);
|
||||
|
||||
if (loginResponse == null) {
|
||||
throw new IllegalStateException("Login response is null");
|
||||
}
|
||||
|
||||
String token = loginResponse.getToken();
|
||||
String roleStr = loginResponse.getRole();
|
||||
if (token == null || roleStr == null) {
|
||||
throw new IllegalStateException("Token or role is null");
|
||||
}
|
||||
|
||||
if ("CUSTOMER".equalsIgnoreCase(roleStr)) {
|
||||
lblError.setText("Access Denied: Customer accounts cannot access the desktop application.");
|
||||
txtPassword.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
UserSession.getInstance().login(
|
||||
user.getUserId(),
|
||||
user.getEmployeeId(),
|
||||
user.getUsername(),
|
||||
user.getEmployeeFullName(),
|
||||
user.getRole()
|
||||
);
|
||||
Role role = Role.valueOf(roleStr.toUpperCase());
|
||||
|
||||
UserSession.getInstance().login(null, username, role, token);
|
||||
|
||||
UserInfoResponse userInfo = apiClient.get("/api/v1/auth/me", UserInfoResponse.class);
|
||||
if (userInfo == null) {
|
||||
throw new IllegalStateException("User info is null");
|
||||
}
|
||||
UserSession.getInstance().login(userInfo.getId(), username, role, token);
|
||||
|
||||
List<DropdownOption> stores = DropdownApi.getInstance().getStores();
|
||||
if (stores != null && !stores.isEmpty()) {
|
||||
UserSession.getInstance().setStoreId(stores.get(0).getId());
|
||||
}
|
||||
|
||||
openMainLayout();
|
||||
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"LoginController.btnLoginClicked",
|
||||
e,
|
||||
"Authentication attempt for username: " + username);
|
||||
String msg = e.getMessage() == null ? "" : e.getMessage().toLowerCase();
|
||||
if (msg.contains("doesn't exist") || msg.contains("unknown database") || msg.contains("access denied")) {
|
||||
lblError.setText("Database error. Check Docker and connectionpetstore.properties.");
|
||||
|
||||
String errorMsg = e.getMessage();
|
||||
if (errorMsg != null && errorMsg.contains("Authentication failed")) {
|
||||
lblError.setText("Invalid username or password.");
|
||||
txtPassword.clear();
|
||||
} else if (e.getCause() instanceof java.net.ConnectException ||
|
||||
e instanceof java.net.http.HttpConnectTimeoutException) {
|
||||
lblError.setText("Backend is not reachable, check backend docker compose and port 8080.");
|
||||
} else {
|
||||
lblError.setText("Login failed. Check username and password.");
|
||||
lblError.setText(errorMsg != null ? errorMsg : "Login failed. Please try again.");
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"LoginController.btnLoginClicked",
|
||||
e,
|
||||
"Database connection");
|
||||
lblError.setText("Database is not connected. Check Docker and connectionpetstore.properties.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,8 +173,14 @@ public class MainLayoutController {
|
||||
|
||||
@FXML
|
||||
void logoClicked(MouseEvent event) {
|
||||
loadView("analytics-view.fxml");
|
||||
updateButtons(btnAnalytics);
|
||||
UserSession session = UserSession.getInstance();
|
||||
if (session.isAdmin()) {
|
||||
loadView("analytics-view.fxml");
|
||||
updateButtons(btnAnalytics);
|
||||
} else {
|
||||
loadView("sale-view.fxml");
|
||||
updateButtons(btnSalesHistory);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -201,8 +207,14 @@ public class MainLayoutController {
|
||||
public void initialize() {
|
||||
applyRBAC();
|
||||
|
||||
loadView("analytics-view.fxml");
|
||||
updateButtons(btnAnalytics);
|
||||
UserSession session = UserSession.getInstance();
|
||||
if (session.isAdmin()) {
|
||||
loadView("analytics-view.fxml");
|
||||
updateButtons(btnAnalytics);
|
||||
} else {
|
||||
loadView("sale-view.fxml");
|
||||
updateButtons(btnSalesHistory);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyRBAC() {
|
||||
@@ -241,6 +253,11 @@ public class MainLayoutController {
|
||||
separatorAdmin.setManaged(isAdmin);
|
||||
}
|
||||
|
||||
if (btnAnalytics != null) {
|
||||
btnAnalytics.setVisible(isAdmin);
|
||||
btnAnalytics.setManaged(isAdmin);
|
||||
}
|
||||
|
||||
btnSalesHistory.setText(isAdmin ? "Sales History" : "Sales");
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -10,16 +11,18 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.PetApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.PetDialogController;
|
||||
import org.example.petshopdesktop.database.PetDB;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.models.Pet;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Period;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PetController {
|
||||
|
||||
@@ -83,48 +86,24 @@ public class PetController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedPets.stream()
|
||||
.map(p -> (long) p.getPetId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Pet pet : selectedPets) {
|
||||
try{
|
||||
int numRows = PetDB.deletePet(pet.getPetId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete pet ID %d - foreign key constraint", pet.getPetId()));
|
||||
failCount++;
|
||||
errors.append("Pet '").append(pet.getPetName()).append("' is referenced in another table\n");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete pet ID %d", pet.getPetId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete '").append(pet.getPetName()).append("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
PetApi.getInstance().deletePets(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " pet(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " pet(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting pets");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -187,39 +166,55 @@ public class PetController {
|
||||
}
|
||||
|
||||
private void displayFilteredPet(String filter) {
|
||||
data.clear();
|
||||
try{
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayPets();
|
||||
}
|
||||
else {
|
||||
data = PetDB.getFilteredPets(filter);
|
||||
tvPets.setItems(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.displayFilteredPet",
|
||||
e,
|
||||
"Filtering pets with filter: " + filter);
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayPets();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<PetResponse> pets = PetApi.getInstance().listPets(filter);
|
||||
List<Pet> petList = pets.stream()
|
||||
.map(this::mapToPet)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(petList);
|
||||
tvPets.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.displayFilteredPet",
|
||||
e,
|
||||
String.format("Filtering pets with keyword: %s", filter));
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void displayPets() {
|
||||
data.clear();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<PetResponse> pets = PetApi.getInstance().listPets(null);
|
||||
List<Pet> petList = pets.stream()
|
||||
.map(this::mapToPet)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try{
|
||||
data = PetDB.getPets();
|
||||
}
|
||||
catch(SQLException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.displayPets",
|
||||
e,
|
||||
"Fetching pet data for table display");
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
}
|
||||
|
||||
tvPets.setItems(data);
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(petList);
|
||||
tvPets.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetController.displayPets",
|
||||
e,
|
||||
"Fetching pet data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void openDialog(Pet pet, String mode){
|
||||
@@ -261,4 +256,20 @@ public class PetController {
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private Pet mapToPet(PetResponse response) {
|
||||
int age = 0;
|
||||
if (null != null) {
|
||||
age = Period.between(null, LocalDate.now()).getYears();
|
||||
}
|
||||
return new Pet(
|
||||
response.getPetId().intValue(),
|
||||
response.getPetName(),
|
||||
response.getPetSpecies(),
|
||||
response.getPetBreed(),
|
||||
age,
|
||||
response.getPetStatus(),
|
||||
response.getPetPrice().doubleValue()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -11,18 +12,16 @@ import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.SupplierDialogController;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.database.SupplierDB;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The controller for any operations in the products view
|
||||
@@ -110,22 +109,27 @@ public class ProductController {
|
||||
* Display the productDTO to table view
|
||||
*/
|
||||
private void displayProduct(){
|
||||
//Erase old content
|
||||
data.clear();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ProductResponse> products = ProductApi.getInstance().listProducts(null);
|
||||
List<ProductDTO> productDTOs = products.stream()
|
||||
.map(this::mapToProductDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//get Products from database
|
||||
try{
|
||||
data = ProductDB.getProductDTO();
|
||||
} catch (SQLException e) {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.displayProduct",
|
||||
e,
|
||||
"Fetching product data for table display");
|
||||
}
|
||||
|
||||
//put data in the table
|
||||
tvProducts.setItems(data);
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(productDTOs);
|
||||
tvProducts.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.displayProduct",
|
||||
e,
|
||||
"Fetching product data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,48 +164,24 @@ public class ProductController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedProducts.stream()
|
||||
.map(p -> (long) p.getProdId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ProductDTO product : selectedProducts) {
|
||||
try{
|
||||
int numRows = ProductDB.deleteProduct(product.getProdId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete product ID %d - foreign key constraint", product.getProdId()));
|
||||
failCount++;
|
||||
errors.append("Product '").append(product.getProdName()).append("' is referenced in another table\n");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete product ID %d", product.getProdId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete '").append(product.getProdName()).append("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
ProductApi.getInstance().deleteProducts(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " product(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " product(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting products");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -233,22 +213,30 @@ public class ProductController {
|
||||
* @param filter word to filter table
|
||||
*/
|
||||
private void displayFilteredProduct(String filter){
|
||||
data.clear();
|
||||
try{
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayProduct(); //If search bar is empty just display everything
|
||||
}
|
||||
else{
|
||||
//Filter the using the keyword
|
||||
data = ProductDB.getFilteredProductDTOs(filter);
|
||||
tvProducts.setItems(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.displayFilteredProduct",
|
||||
e,
|
||||
String.format("Filtering products with keyword: %s", filter));
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayProduct();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ProductResponse> products = ProductApi.getInstance().listProducts(filter);
|
||||
List<ProductDTO> productDTOs = products.stream()
|
||||
.map(this::mapToProductDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(productDTOs);
|
||||
tvProducts.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductController.displayFilteredProduct",
|
||||
e,
|
||||
String.format("Filtering products with keyword: %s", filter));
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,4 +285,15 @@ public class ProductController {
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private ProductDTO mapToProductDTO(ProductResponse response) {
|
||||
return new ProductDTO(
|
||||
response.getProdId().intValue(),
|
||||
response.getProdName(),
|
||||
response.getProdPrice().doubleValue(),
|
||||
0,
|
||||
response.getCategoryName(),
|
||||
response.getProdDesc()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -10,19 +11,16 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.DTOs.ProductSupplierDTO;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductDialogController;
|
||||
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductSupplierApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductSupplierDialogController;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.database.ProductSupplierDB;
|
||||
import org.example.petshopdesktop.models.ProductSupplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ProductSupplierController {
|
||||
|
||||
@@ -107,22 +105,27 @@ public class ProductSupplierController {
|
||||
* Display the ProductSupplierDTO to table view
|
||||
*/
|
||||
private void displayProductSupplier() {
|
||||
//Erase old content
|
||||
data.clear();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ProductSupplierResponse> productSuppliers = ProductSupplierApi.getInstance().listProductSuppliers(null);
|
||||
List<ProductSupplierDTO> productSupplierDTOs = productSuppliers.stream()
|
||||
.map(this::mapToProductSupplierDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//get ProductSupplier from database
|
||||
try{
|
||||
data = ProductSupplierDB.getProductSupplierDTO();
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.displayProductSupplier",
|
||||
e,
|
||||
"Fetching product-supplier data for table display");
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
}
|
||||
|
||||
//put data in the table
|
||||
tvProductSuppliers.setItems(data);
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(productSupplierDTOs);
|
||||
tvProductSuppliers.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.displayProductSupplier",
|
||||
e,
|
||||
"Fetching product-supplier data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,22 +133,30 @@ public class ProductSupplierController {
|
||||
* @param filter word to filter table
|
||||
*/
|
||||
private void displayFilteredProductSupplier(String filter){
|
||||
data.clear();
|
||||
try{
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayProductSupplier(); //If search bar is empty just display everything
|
||||
}
|
||||
else{
|
||||
//Filter the using the keyword
|
||||
data = ProductSupplierDB.getFilteredProductSupplierDTO(filter);
|
||||
tvProductSuppliers.setItems(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.displayFilteredProductSupplier",
|
||||
e,
|
||||
"Filtering product-supplier data with filter: " + filter);
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displayProductSupplier();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ProductSupplierResponse> productSuppliers = ProductSupplierApi.getInstance().listProductSuppliers(filter);
|
||||
List<ProductSupplierDTO> productSupplierDTOs = productSuppliers.stream()
|
||||
.map(this::mapToProductSupplierDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(productSupplierDTOs);
|
||||
tvProductSuppliers.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.displayFilteredProductSupplier",
|
||||
e,
|
||||
"Filtering product-supplier data with filter: " + filter);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,52 +192,36 @@ public class ProductSupplierController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
int deleteCount = 0;
|
||||
Exception lastException = null;
|
||||
|
||||
for (ProductSupplierDTO productSupplier : selectedProductSuppliers) {
|
||||
try{
|
||||
int numRows = ProductSupplierDB.deleteProductSupplier(productSupplier.getSupId(), productSupplier.getProdId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e){
|
||||
for (ProductSupplierDTO ps : selectedProductSuppliers) {
|
||||
try {
|
||||
ProductSupplierApi.getInstance().deleteProductSupplier(
|
||||
(long) ps.getProdId(),
|
||||
(long) ps.getSupId()
|
||||
);
|
||||
deleteCount++;
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete product-supplier - SupID: %d, ProdID: %d - foreign key constraint",
|
||||
productSupplier.getSupId(), productSupplier.getProdId()));
|
||||
failCount++;
|
||||
errors.append(String.format("Product-Supplier '%s - %s' is referenced in another table\n",
|
||||
productSupplier.getProdName(), productSupplier.getSupCompany()));
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete product-supplier - SupID: %d, ProdID: %d",
|
||||
productSupplier.getSupId(), productSupplier.getProdId()));
|
||||
failCount++;
|
||||
errors.append(String.format("Failed to delete '%s - %s'\n",
|
||||
productSupplier.getProdName(), productSupplier.getSupCompany()));
|
||||
"Deleting product-supplier with productId=" + ps.getProdId() + ", supplierId=" + ps.getSupId());
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
if (deleteCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " product-supplier(s)");
|
||||
alert.setContentText("Successfully deleted " + deleteCount + " product-supplier(s)");
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
if (lastException != null && deleteCount < selectedProductSuppliers.size()) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Partially Failed");
|
||||
alert.setContentText("Deleted " + deleteCount + " of " + selectedProductSuppliers.size() + " product-supplier(s). Last error: " + lastException.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -297,4 +292,14 @@ public class ProductSupplierController {
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private ProductSupplierDTO mapToProductSupplierDTO(ProductSupplierResponse response) {
|
||||
return new ProductSupplierDTO(
|
||||
response.getSupplierId().intValue(),
|
||||
response.getProductId().intValue(),
|
||||
response.getSupplierName(),
|
||||
response.getProductName(),
|
||||
response.getCost().doubleValue()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
@@ -7,9 +8,13 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import org.example.petshopdesktop.DTOs.PurchaseOrderDTO;
|
||||
import org.example.petshopdesktop.database.PurchaseOrderDB;
|
||||
import org.example.petshopdesktop.api.dto.purchaseorder.PurchaseOrderResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.PurchaseOrderApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PurchaseOrderController {
|
||||
|
||||
@FXML private Button btnRefresh;
|
||||
@@ -19,7 +24,7 @@ public class PurchaseOrderController {
|
||||
|
||||
@FXML private TableView<PurchaseOrderDTO> tvPurchaseOrders;
|
||||
|
||||
@FXML private TableColumn<PurchaseOrderDTO,Integer> colOrderId;
|
||||
@FXML private TableColumn<PurchaseOrderDTO,Long> colOrderId;
|
||||
@FXML private TableColumn<PurchaseOrderDTO,String> colSupplier;
|
||||
@FXML private TableColumn<PurchaseOrderDTO,String> colOrderDate;
|
||||
@FXML private TableColumn<PurchaseOrderDTO,String> colStatus;
|
||||
@@ -53,17 +58,28 @@ public class PurchaseOrderController {
|
||||
}
|
||||
|
||||
private void loadPurchaseOrders() {
|
||||
try {
|
||||
purchaseOrders.setAll(PurchaseOrderDB.getPurchaseOrders());
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PurchaseOrderController.loadPurchaseOrders",
|
||||
e,
|
||||
"Loading purchase orders for table display");
|
||||
e.printStackTrace();
|
||||
new Alert(Alert.AlertType.ERROR,
|
||||
"Unable to load purchase orders").showAndWait();
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<PurchaseOrderResponse> responses = PurchaseOrderApi.getInstance().listPurchaseOrders(null);
|
||||
List<PurchaseOrderDTO> dtos = responses.stream()
|
||||
.map(this::mapToPurchaseOrderDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
purchaseOrders.setAll(dtos);
|
||||
tvPurchaseOrders.setItems(filtered);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PurchaseOrderController.loadPurchaseOrders",
|
||||
e,
|
||||
"Loading purchase orders for table display");
|
||||
new Alert(Alert.AlertType.ERROR,
|
||||
"Unable to load purchase orders").showAndWait();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void applyFilter(String text) {
|
||||
@@ -93,4 +109,13 @@ public class PurchaseOrderController {
|
||||
void btnRefresh() {
|
||||
loadPurchaseOrders();
|
||||
}
|
||||
|
||||
private PurchaseOrderDTO mapToPurchaseOrderDTO(PurchaseOrderResponse response) {
|
||||
return new PurchaseOrderDTO(
|
||||
response.getPurchaseOrderId(),
|
||||
response.getSupplierName(),
|
||||
response.getOrderDate() != null ? response.getOrderDate().toString() : "",
|
||||
response.getOrderStatus()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -22,20 +22,25 @@ 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;
|
||||
import org.example.petshopdesktop.database.SaleDB;
|
||||
import org.example.petshopdesktop.models.Inventory;
|
||||
import javafx.concurrent.Task;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||
import org.example.petshopdesktop.api.endpoints.SaleApi;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleItemRequest;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleItemResponse;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleRequest;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleResponse;
|
||||
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.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.HashMap;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class SaleController {
|
||||
|
||||
@@ -124,8 +129,8 @@ public class SaleController {
|
||||
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);
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
@@ -133,7 +138,6 @@ public class SaleController {
|
||||
setupCreateSale();
|
||||
applyRoleMode();
|
||||
|
||||
refreshInventory();
|
||||
refreshSales();
|
||||
}
|
||||
|
||||
@@ -170,11 +174,20 @@ public class SaleController {
|
||||
updateCartTotal();
|
||||
|
||||
try {
|
||||
cbProduct.setItems(ProductDB.getProducts());
|
||||
} catch (SQLException e) {
|
||||
List<ProductResponse> productResponses = ProductApi.getInstance().listProducts(null);
|
||||
ObservableList<Product> products = FXCollections.observableArrayList();
|
||||
for (ProductResponse pr : productResponses) {
|
||||
products.add(new Product(
|
||||
pr.getProdId().intValue(),
|
||||
pr.getProdName(),
|
||||
pr.getProdPrice().doubleValue(),
|
||||
0,
|
||||
pr.getProdDesc()
|
||||
));
|
||||
}
|
||||
cbProduct.setItems(products);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("SaleController.setupCreateSale", e, "Loading products");
|
||||
} catch (RuntimeException e) {
|
||||
ActivityLogger.getInstance().logException("SaleController.setupCreateSale", e, "Database connection");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,42 +198,61 @@ public class SaleController {
|
||||
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.");
|
||||
Task<List<SaleLineItem>> task = new Task<List<SaleLineItem>>() {
|
||||
@Override
|
||||
protected List<SaleLineItem> call() throws Exception {
|
||||
List<SaleResponse> sales = SaleApi.getInstance().listSales(0, 1000, null);
|
||||
List<SaleLineItem> lineItems = new ArrayList<>();
|
||||
|
||||
for (SaleResponse sale : sales) {
|
||||
String saleDate = sale.getSaleDate() != null
|
||||
? sale.getSaleDate().format(DATE_FORMATTER)
|
||||
: "";
|
||||
|
||||
if (sale.getItems() != null && !sale.getItems().isEmpty()) {
|
||||
for (SaleItemResponse item : sale.getItems()) {
|
||||
double unitPrice = item.getUnitPrice() != null ? item.getUnitPrice().doubleValue() : 0.0;
|
||||
double lineTotal = unitPrice * item.getQuantity();
|
||||
lineItems.add(new SaleLineItem(
|
||||
sale.getSaleId().intValue(),
|
||||
saleDate,
|
||||
sale.getEmployeeName(),
|
||||
item.getProductName(),
|
||||
item.getQuantity(),
|
||||
unitPrice,
|
||||
lineTotal,
|
||||
sale.getPaymentMethod(),
|
||||
sale.getIsRefund() != null && sale.getIsRefund()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
return lineItems;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
ActivityLogger.getInstance().logException("SaleController.refreshSales", e, "Database connection");
|
||||
};
|
||||
|
||||
task.setOnSucceeded(event -> {
|
||||
saleItems.setAll(task.getValue());
|
||||
});
|
||||
|
||||
task.setOnFailed(event -> {
|
||||
Throwable e = task.getException();
|
||||
ActivityLogger.getInstance().logException("SaleController.refreshSales", (Exception) e, "Loading sales");
|
||||
if (showErrorDialog) {
|
||||
showError("Sales", "Database is not connected.");
|
||||
showError("Sales", "Could not load sales: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new Thread(task).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnRefresh(ActionEvent event) {
|
||||
refreshInventory();
|
||||
refreshSales(true);
|
||||
}
|
||||
|
||||
@@ -244,18 +276,6 @@ public class SaleController {
|
||||
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);
|
||||
@@ -291,9 +311,9 @@ public class SaleController {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer employeeId = UserSession.getInstance().getEmployeeId();
|
||||
if (employeeId == null || employeeId <= 0) {
|
||||
showError("Create Sale", "Employee is not set for this account.");
|
||||
Long storeId = UserSession.getInstance().getStoreId();
|
||||
if (storeId == null || storeId <= 0) {
|
||||
showError("Create Sale", "Store is not set for this account.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,20 +329,34 @@ public class SaleController {
|
||||
}
|
||||
|
||||
try {
|
||||
int saleId = SaleDB.createSale(employeeId, payment, cartItems);
|
||||
showInfo("Sale saved", "Sale ID " + saleId + " was created.");
|
||||
SaleRequest request = new SaleRequest();
|
||||
request.setStoreId(storeId);
|
||||
request.setPaymentMethod(payment);
|
||||
|
||||
List<SaleItemRequest> itemRequests = new ArrayList<>();
|
||||
for (SaleCartItem cartItem : cartItems) {
|
||||
SaleItemRequest itemRequest = new SaleItemRequest();
|
||||
itemRequest.setProdId((long) cartItem.getProdId());
|
||||
itemRequest.setQuantity(cartItem.getQuantity());
|
||||
itemRequests.add(itemRequest);
|
||||
}
|
||||
request.setItems(itemRequests);
|
||||
|
||||
SaleResponse response = SaleApi.getInstance().createSale(request);
|
||||
showInfo("Sale saved", "Sale ID " + response.getSaleId() + " was created.");
|
||||
|
||||
cartItems.clear();
|
||||
updateCartTotal();
|
||||
|
||||
refreshInventory();
|
||||
refreshSales(true);
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception 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.");
|
||||
String errorMsg = e.getMessage();
|
||||
if (errorMsg != null && errorMsg.contains("Insufficient inventory")) {
|
||||
showError("Create Sale", "Insufficient stock for one or more items.");
|
||||
} else {
|
||||
showError("Create Sale", errorMsg != null ? errorMsg : "Could not save the sale.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +377,6 @@ public class SaleController {
|
||||
dialog.setResizable(false);
|
||||
dialog.showAndWait();
|
||||
|
||||
refreshInventory();
|
||||
refreshSales(true);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("SaleController.openRefundDialog", e, "Opening refund dialog");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
@@ -10,12 +10,17 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.database.ServiceDB;
|
||||
import org.example.petshopdesktop.models.Service;
|
||||
import org.example.petshopdesktop.DTOs.ServiceDTO;
|
||||
import org.example.petshopdesktop.api.dto.service.ServiceResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.ServiceApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.ServiceDialogController;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
import javafx.stage.Modality;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class ServiceController {
|
||||
|
||||
@@ -23,22 +28,23 @@ public class ServiceController {
|
||||
@FXML private Button btnDelete;
|
||||
@FXML private Button btnEdit;
|
||||
|
||||
@FXML private TableColumn<Service, Integer> colServiceId;
|
||||
@FXML private TableColumn<Service, String> colServiceName;
|
||||
@FXML private TableColumn<Service, String> colServiceDesc;
|
||||
@FXML private TableColumn<Service, Integer> colServiceDuration;
|
||||
@FXML private TableColumn<Service, Double> colServicePrice;
|
||||
@FXML private TableColumn<ServiceDTO, Integer> colServiceId;
|
||||
@FXML private TableColumn<ServiceDTO, String> colServiceName;
|
||||
@FXML private TableColumn<ServiceDTO, String> colServiceDesc;
|
||||
@FXML private TableColumn<ServiceDTO, Integer> colServiceDuration;
|
||||
@FXML private TableColumn<ServiceDTO, Double> colServicePrice;
|
||||
|
||||
@FXML private TableView<Service> tvServices;
|
||||
@FXML private TableView<ServiceDTO> tvServices;
|
||||
|
||||
@FXML private TextField txtSearch;
|
||||
|
||||
private final ObservableList<Service> services = FXCollections.observableArrayList();
|
||||
private FilteredList<Service> filtered;
|
||||
private ObservableList<ServiceDTO> data = FXCollections.observableArrayList();
|
||||
private String mode = null;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
//Enable multiple selection
|
||||
btnEdit.setDisable(true);
|
||||
btnDelete.setDisable(true);
|
||||
tvServices.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
|
||||
|
||||
colServiceId.setCellValueFactory(new PropertyValueFactory<>("serviceId"));
|
||||
@@ -47,14 +53,19 @@ public class ServiceController {
|
||||
colServiceDuration.setCellValueFactory(new PropertyValueFactory<>("serviceDuration"));
|
||||
colServicePrice.setCellValueFactory(new PropertyValueFactory<>("servicePrice"));
|
||||
|
||||
filtered = new FilteredList<>(services, s -> true);
|
||||
tvServices.setItems(filtered);
|
||||
displayServices();
|
||||
|
||||
if (txtSearch != null) {
|
||||
txtSearch.textProperty().addListener((obs, o, n) -> applyFilter(n));
|
||||
}
|
||||
tvServices.getSelectionModel().selectedItemProperty().addListener(
|
||||
(observable, oldValue, newValue) -> {
|
||||
btnEdit.setDisable(false);
|
||||
btnDelete.setDisable(false);
|
||||
}
|
||||
);
|
||||
|
||||
txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
displayFilteredServices(newValue);
|
||||
});
|
||||
|
||||
//EventListener for DELETE key
|
||||
tvServices.setOnKeyPressed(event -> {
|
||||
if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
|
||||
if (tvServices.getSelectionModel().getSelectedItem() != null) {
|
||||
@@ -62,75 +73,82 @@ public class ServiceController {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loadServices();
|
||||
}
|
||||
|
||||
private void loadServices() {
|
||||
try {
|
||||
services.setAll(ServiceDB.getServices());
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.loadServices",
|
||||
e,
|
||||
"Loading services for table display");
|
||||
showAlert("Database Error", "Unable to load services.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
private void displayServices() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ServiceResponse> services = ServiceApi.getInstance().listServices(null);
|
||||
List<ServiceDTO> serviceDTOs = services.stream()
|
||||
.map(this::mapToServiceDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(serviceDTOs);
|
||||
tvServices.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.displayServices",
|
||||
e,
|
||||
"Fetching service data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void applyFilter(String text) {
|
||||
if (filtered == null) {
|
||||
return;
|
||||
private void displayFilteredServices(String filter) {
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
|
||||
displayServices();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<ServiceResponse> services = ServiceApi.getInstance().listServices(filter);
|
||||
List<ServiceDTO> serviceDTOs = services.stream()
|
||||
.map(this::mapToServiceDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(serviceDTOs);
|
||||
tvServices.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.displayFilteredServices",
|
||||
e,
|
||||
String.format("Filtering services with keyword: %s", filter));
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
String q = text == null ? "" : text.trim().toLowerCase();
|
||||
if (q.isEmpty()) {
|
||||
filtered.setPredicate(s -> true);
|
||||
return;
|
||||
}
|
||||
|
||||
filtered.setPredicate(s ->
|
||||
String.valueOf(s.getServiceId()).contains(q)
|
||||
|| safe(s.getServiceName()).contains(q)
|
||||
|| safe(s.getServiceDesc()).contains(q)
|
||||
|| String.valueOf(s.getServiceDuration()).contains(q)
|
||||
|| String.valueOf(s.getServicePrice()).contains(q)
|
||||
);
|
||||
}
|
||||
|
||||
private static String safe(String v) {
|
||||
return v == null ? "" : v.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
@FXML
|
||||
void btnAddClicked(ActionEvent event) {
|
||||
openDialog(null, "Add");
|
||||
loadServices();
|
||||
mode = "Add";
|
||||
openDialog(null, mode);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnEditClicked(ActionEvent event) {
|
||||
ServiceDTO selected = tvServices.getSelectionModel().getSelectedItem();
|
||||
|
||||
Service selected = tvServices.getSelectionModel().getSelectedItem();
|
||||
|
||||
if (selected == null) {
|
||||
showAlert("Select Service", "Please select a service to edit.");
|
||||
return;
|
||||
if (selected != null) {
|
||||
mode = "Edit";
|
||||
openDialog(selected, mode);
|
||||
}
|
||||
|
||||
openDialog(selected, "Edit");
|
||||
loadServices();
|
||||
}
|
||||
|
||||
@FXML
|
||||
void btnDeleteClicked(ActionEvent e) {
|
||||
//get selected services
|
||||
void btnDeleteClicked(ActionEvent event) {
|
||||
var selectedServices = tvServices.getSelectionModel().getSelectedItems();
|
||||
if (selectedServices.isEmpty()) return;
|
||||
|
||||
//ask user to confirm
|
||||
Alert question = new Alert(Alert.AlertType.CONFIRMATION);
|
||||
question.setHeaderText("Please confirm delete");
|
||||
String message = selectedServices.size() == 1
|
||||
@@ -138,82 +156,78 @@ public class ServiceController {
|
||||
: "Are you sure you want to delete " + selectedServices.size() + " services?";
|
||||
question.setContentText(message);
|
||||
question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
|
||||
java.util.Optional<ButtonType> result = question.showAndWait();
|
||||
Optional<ButtonType> result = question.showAndWait();
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedServices.stream()
|
||||
.map(s -> (long) s.getServiceId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Service service : selectedServices) {
|
||||
try {
|
||||
ServiceDB.deleteService(service.getServiceId());
|
||||
successCount++;
|
||||
} catch (Exception ex) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.btnDeleteClicked",
|
||||
ex,
|
||||
String.format("Attempting to delete service ID %d", service.getServiceId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete '").append(service.getServiceName()).append("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
ServiceApi.getInstance().deleteServices(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " service(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " service(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting services");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
//refresh display
|
||||
loadServices();
|
||||
displayServices();
|
||||
btnDelete.setDisable(true);
|
||||
btnEdit.setDisable(true);
|
||||
txtSearch.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
private void openDialog(Service service, String mode) {
|
||||
|
||||
private void openDialog(ServiceDTO service, String mode) {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml"));
|
||||
Scene scene = null;
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(
|
||||
getClass().getResource("/org/example/petshopdesktop/dialogviews/service-dialog-view.fxml")
|
||||
);
|
||||
|
||||
Stage stage = new Stage();
|
||||
stage.setScene(new Scene(loader.load()));
|
||||
|
||||
ServiceDialogController controller = loader.getController();
|
||||
controller.setMode(mode);
|
||||
|
||||
if (mode.equals("Edit")) {
|
||||
controller.setService(service);
|
||||
}
|
||||
|
||||
stage.initModality(Modality.APPLICATION_MODAL);
|
||||
stage.showAndWait();
|
||||
|
||||
loadServices();
|
||||
|
||||
scene = new Scene(fxmlLoader.load());
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ServiceController.openDialog",
|
||||
e,
|
||||
"Opening service dialog in " + mode + " mode");
|
||||
e.printStackTrace();
|
||||
String.format("Loading service dialog view in %s mode", mode));
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
ServiceDialogController dialogController = fxmlLoader.getController();
|
||||
dialogController.setMode(mode);
|
||||
|
||||
if (mode.equals("Edit")) {
|
||||
dialogController.setService(service);
|
||||
}
|
||||
Stage dialogStage = new Stage();
|
||||
dialogStage.initModality(Modality.APPLICATION_MODAL);
|
||||
if (mode.equals("Add")) {
|
||||
dialogStage.setTitle("Add Service");
|
||||
} else {
|
||||
dialogStage.setTitle("Edit Service");
|
||||
}
|
||||
dialogStage.setScene(scene);
|
||||
dialogStage.showAndWait();
|
||||
|
||||
displayServices();
|
||||
btnDelete.setDisable(true);
|
||||
btnEdit.setDisable(true);
|
||||
txtSearch.setText("");
|
||||
}
|
||||
private void showAlert(String title, String msg) {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(msg);
|
||||
alert.showAndWait();
|
||||
|
||||
private ServiceDTO mapToServiceDTO(ServiceResponse response) {
|
||||
return new ServiceDTO(
|
||||
response.getServiceId().intValue(),
|
||||
response.getServiceName(),
|
||||
response.getServiceDesc(),
|
||||
response.getServiceDuration() != null ? response.getServiceDuration() : 0,
|
||||
response.getServicePrice().doubleValue()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
@@ -15,12 +16,16 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.user.UserResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.UserApi;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.database.UserDB;
|
||||
import org.example.petshopdesktop.models.StaffAccount;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StaffAccountsController {
|
||||
|
||||
@@ -107,15 +112,54 @@ public class StaffAccountsController {
|
||||
|
||||
private void refresh() {
|
||||
lblError.setText("");
|
||||
try {
|
||||
staffAccounts.setAll(UserDB.getStaffAccounts());
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Loading staff accounts");
|
||||
lblError.setText("Could not load staff accounts.");
|
||||
} catch (RuntimeException e) {
|
||||
ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Database connection");
|
||||
lblError.setText("Database is not connected.");
|
||||
tvStaff.setDisable(true);
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<UserResponse> users = UserApi.getInstance().listUsers(null);
|
||||
List<StaffAccount> accounts = users.stream()
|
||||
.map(this::mapToStaffAccount)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
staffAccounts.setAll(accounts);
|
||||
tvStaff.setDisable(false);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("StaffAccountsController.refresh", e, "Loading staff accounts");
|
||||
Platform.runLater(() -> {
|
||||
lblError.setText("Could not load staff accounts.");
|
||||
tvStaff.setDisable(false);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private StaffAccount mapToStaffAccount(UserResponse user) {
|
||||
long id = user.getId() != null ? user.getId() : 0L;
|
||||
String username = user.getUsername();
|
||||
String fullName = user.getFullName() != null ? user.getFullName() : "";
|
||||
String[] names = splitFullName(fullName);
|
||||
String firstName = names[0];
|
||||
String lastName = names[1];
|
||||
String email = user.getEmail() != null ? user.getEmail() : "";
|
||||
String phone = "";
|
||||
boolean active = user.getActive() != null ? user.getActive() : false;
|
||||
Timestamp createdAt = user.getCreatedAt() != null
|
||||
? Timestamp.from(user.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant())
|
||||
: null;
|
||||
|
||||
return new StaffAccount(id, id, username, firstName, lastName, email, phone, active, createdAt);
|
||||
}
|
||||
|
||||
private String[] splitFullName(String fullName) {
|
||||
if (fullName == null || fullName.trim().isEmpty()) {
|
||||
return new String[]{"", ""};
|
||||
}
|
||||
String[] parts = fullName.trim().split("\\s+", 2);
|
||||
String firstName = parts.length > 0 ? parts[0] : "";
|
||||
String lastName = parts.length > 1 ? parts[1] : "";
|
||||
return new String[]{firstName, lastName};
|
||||
}
|
||||
|
||||
private void applyFilter(String text) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
@@ -10,15 +11,16 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.supplier.SupplierResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.SupplierApi;
|
||||
import org.example.petshopdesktop.controllers.dialogcontrollers.SupplierDialogController;
|
||||
import org.example.petshopdesktop.database.SupplierDB;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The controller for any operations in the supplier view
|
||||
@@ -105,19 +107,27 @@ public class SupplierController {
|
||||
* Display the suppliers to table view
|
||||
*/
|
||||
private void displaySupplier(){
|
||||
data.clear();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<SupplierResponse> suppliers = SupplierApi.getInstance().listSuppliers(null);
|
||||
List<Supplier> supplierList = suppliers.stream()
|
||||
.map(this::mapToSupplier)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try{
|
||||
data = SupplierDB.getSuppliers();
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.displaySupplier",
|
||||
e,
|
||||
"Fetching supplier data for table display");
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
}
|
||||
|
||||
tvSuppliers.setItems(data);
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(supplierList);
|
||||
tvSuppliers.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.displaySupplier",
|
||||
e,
|
||||
"Fetching supplier data for table display");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,22 +135,30 @@ public class SupplierController {
|
||||
* @param filter word to filter table
|
||||
*/
|
||||
private void displayFilteredSupplier(String filter){
|
||||
data.clear();
|
||||
try{
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displaySupplier(); //If search bar is empty just display everything
|
||||
}
|
||||
else{
|
||||
//Filter the using the keyword
|
||||
data = SupplierDB.getFilteredSuppliers(filter);
|
||||
tvSuppliers.setItems(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.displayFilteredSupplier",
|
||||
e,
|
||||
"Filtering suppliers with filter: " + filter);
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
|
||||
displaySupplier();
|
||||
} else {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<SupplierResponse> suppliers = SupplierApi.getInstance().listSuppliers(filter);
|
||||
List<Supplier> supplierList = suppliers.stream()
|
||||
.map(this::mapToSupplier)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
data.setAll(supplierList);
|
||||
tvSuppliers.setItems(data);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
System.out.println("Error while fetching table data: " + e.getMessage());
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.displayFilteredSupplier",
|
||||
e,
|
||||
"Filtering suppliers with filter: " + filter);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,48 +195,24 @@ public class SupplierController {
|
||||
|
||||
//if confirmed, start deletion
|
||||
if (result.isPresent() && result.get() == ButtonType.OK) {
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
StringBuilder errors = new StringBuilder();
|
||||
List<Long> ids = selectedSuppliers.stream()
|
||||
.map(s -> (long) s.getSupId())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Supplier supplier : selectedSuppliers) {
|
||||
try{
|
||||
int numRows = SupplierDB.deleteSupplier(supplier.getSupId());
|
||||
if (numRows > 0) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
catch (SQLIntegrityConstraintViolationException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete supplier ID %d - foreign key constraint", supplier.getSupId()));
|
||||
failCount++;
|
||||
errors.append("Supplier '").append(supplier.getSupCompany()).append("' is referenced in another table\n");
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.btnDeleteClicked",
|
||||
e,
|
||||
String.format("Attempting to delete supplier ID %d", supplier.getSupId()));
|
||||
failCount++;
|
||||
errors.append("Failed to delete '").append(supplier.getSupCompany()).append("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
//show results
|
||||
if (failCount > 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setHeaderText("Delete Operation Completed with Errors");
|
||||
alert.setContentText(String.format("Deleted: %d\nFailed: %d\n\n%s",
|
||||
successCount, failCount, errors.toString()));
|
||||
alert.showAndWait();
|
||||
} else if (successCount > 0) {
|
||||
try {
|
||||
SupplierApi.getInstance().deleteSuppliers(ids);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Database Operation Confirmed");
|
||||
alert.setContentText("Successfully deleted " + successCount + " supplier(s)");
|
||||
alert.setContentText("Successfully deleted " + ids.size() + " supplier(s)");
|
||||
alert.showAndWait();
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierController.btnDeleteClicked",
|
||||
e,
|
||||
"Deleting suppliers");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Delete Operation Failed");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
@@ -290,4 +284,20 @@ public class SupplierController {
|
||||
txtSearch.setText("");
|
||||
}
|
||||
|
||||
private Supplier mapToSupplier(SupplierResponse response) {
|
||||
String contactPerson = response.getSupContactFirstName() + " " + response.getSupContactLastName() != null ? response.getSupContactFirstName() + " " + response.getSupContactLastName() : "";
|
||||
String[] nameParts = contactPerson.split(" ", 2);
|
||||
String firstName = nameParts.length > 0 ? nameParts[0] : "";
|
||||
String lastName = nameParts.length > 1 ? nameParts[1] : "";
|
||||
|
||||
return new Supplier(
|
||||
response.getSupId().intValue(),
|
||||
response.getSupCompany(),
|
||||
firstName,
|
||||
lastName,
|
||||
response.getSupEmail(),
|
||||
response.getSupPhone()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
@@ -12,16 +13,15 @@ import javafx.scene.control.DatePicker;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
import org.example.petshopdesktop.database.AdoptionDB;
|
||||
import org.example.petshopdesktop.database.PetDB;
|
||||
import org.example.petshopdesktop.api.dto.adoption.AdoptionRequest;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
|
||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.models.Adoption;
|
||||
import org.example.petshopdesktop.models.Customer;
|
||||
import org.example.petshopdesktop.models.Pet;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public class AdoptionDialogController {
|
||||
|
||||
@@ -36,10 +36,10 @@ public class AdoptionDialogController {
|
||||
private ComboBox<String> cbAdoptionStatus;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Customer> cbCustomer;
|
||||
private ComboBox<DropdownOption> cbCustomer;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Pet> cbPet;
|
||||
private ComboBox<DropdownOption> cbPet;
|
||||
|
||||
@FXML
|
||||
private DatePicker dpAdoptionDate;
|
||||
@@ -58,52 +58,51 @@ public class AdoptionDialogController {
|
||||
"Pending", "Completed", "Cancelled"
|
||||
);
|
||||
|
||||
//Loads upon boot
|
||||
@FXML
|
||||
void initialize() {
|
||||
|
||||
//Loads statusList into combo box
|
||||
cbAdoptionStatus.setItems(statusList);
|
||||
|
||||
//Pet objects are converted into readable text for combobox (PetID + PetName)
|
||||
cbPet.setConverter(new StringConverter<Pet>() {
|
||||
@Override
|
||||
public String toString(Pet pet) {
|
||||
return pet == null ? "" : pet.getPetId() + ": " + pet.getPetName();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<DropdownOption> pets = DropdownApi.getInstance().getPets();
|
||||
Platform.runLater(() -> {
|
||||
if (pets != null) {
|
||||
ObservableList<DropdownOption> petsObs = FXCollections.observableArrayList(pets);
|
||||
cbPet.setItems(petsObs);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.initialize",
|
||||
e,
|
||||
"Loading pets for combo box");
|
||||
System.out.println("Error loading pets: " + e.getMessage());
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
|
||||
//Not used
|
||||
@Override
|
||||
public Pet fromString(String string) { return null; }
|
||||
});
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<DropdownOption> customers = DropdownApi.getInstance().getCustomers();
|
||||
Platform.runLater(() -> {
|
||||
if (customers != null) {
|
||||
ObservableList<DropdownOption> customersObs = FXCollections.observableArrayList(customers);
|
||||
cbCustomer.setItems(customersObs);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.initialize",
|
||||
e,
|
||||
"Loading customers for combo box");
|
||||
System.out.println("Error loading customers: " + e.getMessage());
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
|
||||
//Load pets from DB into pet combobox
|
||||
try {
|
||||
cbPet.setItems(PetDB.getPets());
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.initialize",
|
||||
e,
|
||||
"Loading pets for combo box");
|
||||
System.out.println("Error loading pets: " + e.getMessage());
|
||||
}
|
||||
|
||||
//Load customers from DB into customer combobox
|
||||
try {
|
||||
cbCustomer.setItems(AdoptionDB.getCustomers());
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.initialize",
|
||||
e,
|
||||
"Loading customers for combo box");
|
||||
System.out.println("Error loading customers: " + e.getMessage());
|
||||
}
|
||||
|
||||
//Save button handler
|
||||
btnSave.setOnMouseClicked(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent mouseEvent) {
|
||||
@@ -111,7 +110,6 @@ public class AdoptionDialogController {
|
||||
}
|
||||
});
|
||||
|
||||
//Cancel button handler, closes dialog view
|
||||
btnCancel.setOnMouseClicked(new EventHandler<MouseEvent>() {
|
||||
@Override
|
||||
public void handle(MouseEvent mouseEvent) {
|
||||
@@ -120,12 +118,9 @@ public class AdoptionDialogController {
|
||||
});
|
||||
}
|
||||
|
||||
//Handles logic when clicking Save
|
||||
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
||||
int numRow = 0;
|
||||
String errorMsg = "";
|
||||
|
||||
//Validation: checks if anything is missing
|
||||
if (cbPet.getSelectionModel().getSelectedItem() == null) {
|
||||
errorMsg += "Pet is required.\n";
|
||||
}
|
||||
@@ -142,60 +137,41 @@ public class AdoptionDialogController {
|
||||
errorMsg += "Status is required.\n";
|
||||
}
|
||||
|
||||
//If no errors, attempt DB operation
|
||||
if (errorMsg.isEmpty()) {
|
||||
Adoption adoption = collectAdoption();
|
||||
try {
|
||||
AdoptionRequest request = new AdoptionRequest();
|
||||
request.setPetId(cbPet.getSelectionModel().getSelectedItem().getId());
|
||||
request.setCustomerId(cbCustomer.getSelectionModel().getSelectedItem().getId());
|
||||
request.setAdoptionDate(dpAdoptionDate.getValue());
|
||||
request.setAdoptionStatus(cbAdoptionStatus.getValue());
|
||||
|
||||
//Try inserting into DB
|
||||
if (mode.equals("Add")) {
|
||||
try {
|
||||
numRow = AdoptionDB.insertAdoption(adoption);
|
||||
if (mode.equals("Add")) {
|
||||
AdoptionApi.getInstance().createAdoption(request);
|
||||
} else {
|
||||
String[] parts = lblAdoptionId.getText().split(": ");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalStateException("Invalid adoption ID format");
|
||||
}
|
||||
Long adoptionId = Long.parseLong(parts[1]);
|
||||
AdoptionApi.getInstance().updateAdoption(adoptionId, request);
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new adoption record");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Try updating adoption
|
||||
else {
|
||||
try {
|
||||
numRow = AdoptionDB.updateAdoption(adoption.getAdoptionId(), adoption);
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating adoption with ID: " + adoption.getAdoptionId());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//If no rows are affected, an issue has occurred
|
||||
if (numRow == 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
//DB operation worked!
|
||||
else {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode + " adoption");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
//If there are errors, display them
|
||||
else {
|
||||
} else {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Input Error");
|
||||
alert.setContentText(errorMsg);
|
||||
@@ -203,30 +179,6 @@ public class AdoptionDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
//Collects user input, builds an Adoption object
|
||||
private Adoption collectAdoption() {
|
||||
int adoptionId = 0;
|
||||
|
||||
//Only grab adoption ID if in edit mode
|
||||
if (lblAdoptionId.isVisible()) {
|
||||
adoptionId = Integer.parseInt(lblAdoptionId.getText().split(": ")[1]);
|
||||
}
|
||||
|
||||
Pet selectedPet = cbPet.getSelectionModel().getSelectedItem();
|
||||
Customer selectedCustomer = cbCustomer.getSelectionModel().getSelectedItem();
|
||||
String date = dpAdoptionDate.getValue().toString();
|
||||
String status = cbAdoptionStatus.getValue();
|
||||
|
||||
return new Adoption(
|
||||
adoptionId,
|
||||
selectedPet.getPetId(),
|
||||
selectedCustomer.getCustomerId(),
|
||||
selectedCustomer.toString(),
|
||||
date,
|
||||
selectedPet.getPetPrice(),
|
||||
status
|
||||
);
|
||||
}
|
||||
|
||||
private void closeStage(MouseEvent mouseEvent) {
|
||||
Node node = (Node) mouseEvent.getSource();
|
||||
@@ -234,35 +186,35 @@ public class AdoptionDialogController {
|
||||
stage.close();
|
||||
}
|
||||
|
||||
//Edit mode
|
||||
//Inserts data into fields
|
||||
public void displayAdoptionDetails(Adoption adoption) {
|
||||
if (adoption != null) {
|
||||
//Displays adoption ID
|
||||
lblAdoptionId.setText("ID: " + adoption.getAdoptionId());
|
||||
|
||||
//Select pet
|
||||
for (Pet pet : cbPet.getItems()) {
|
||||
if (pet.getPetId() == adoption.getPetId()) {
|
||||
for (DropdownOption pet : cbPet.getItems()) {
|
||||
if (pet.getLabel().equals(adoption.getPetName())) {
|
||||
cbPet.getSelectionModel().select(pet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Select customer
|
||||
for (Customer customer : cbCustomer.getItems()) {
|
||||
if (customer.getCustomerId() == adoption.getCustomerId()) {
|
||||
for (DropdownOption customer : cbCustomer.getItems()) {
|
||||
if (customer.getLabel().equals(adoption.getCustomerName())) {
|
||||
cbCustomer.getSelectionModel().select(customer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Select adoption date
|
||||
if (adoption.getAdoptionDate() != null && !adoption.getAdoptionDate().isEmpty()) {
|
||||
dpAdoptionDate.setValue(LocalDate.parse(adoption.getAdoptionDate()));
|
||||
try {
|
||||
dpAdoptionDate.setValue(LocalDate.parse(adoption.getAdoptionDate()));
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AdoptionDialogController.displayAdoptionDetails",
|
||||
e,
|
||||
"Parsing adoption date");
|
||||
}
|
||||
}
|
||||
|
||||
//Select adoption status
|
||||
for (String status : cbAdoptionStatus.getItems()) {
|
||||
if (status.equals(adoption.getAdoptionStatus())) {
|
||||
cbAdoptionStatus.getSelectionModel().select(status);
|
||||
@@ -272,8 +224,6 @@ public class AdoptionDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
//Sets dialog mode
|
||||
//Also updates label and adoption ID visibility
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
lblMode.setText(mode + " Adoption");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -10,11 +11,16 @@ import javafx.stage.Stage;
|
||||
import javafx.scene.control.ListCell;
|
||||
|
||||
import org.example.petshopdesktop.DTOs.AppointmentDTO;
|
||||
import org.example.petshopdesktop.database.*;
|
||||
import org.example.petshopdesktop.models.*;
|
||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentRequest;
|
||||
import org.example.petshopdesktop.api.dto.appointment.AppointmentResponse;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
import org.example.petshopdesktop.api.endpoints.AppointmentApi;
|
||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AppointmentDialogController {
|
||||
|
||||
@@ -25,9 +31,9 @@ public class AppointmentDialogController {
|
||||
@FXML private Button btnCancel;
|
||||
@FXML private Button btnSave;
|
||||
|
||||
@FXML private ComboBox<Service> cbService;
|
||||
@FXML private ComboBox<Customer> cbCustomer;
|
||||
@FXML private ComboBox<Pet> cbPet;
|
||||
@FXML private ComboBox<DropdownOption> cbService;
|
||||
@FXML private ComboBox<DropdownOption> cbCustomer;
|
||||
@FXML private ComboBox<DropdownOption> cbPet;
|
||||
|
||||
@FXML private ComboBox<Integer> cbHour;
|
||||
@FXML private ComboBox<Integer> cbMinute;
|
||||
@@ -67,17 +73,33 @@ public class AppointmentDialogController {
|
||||
@FXML
|
||||
public void initialize() {
|
||||
|
||||
try {
|
||||
cbService.setItems(ServiceDB.getServices());
|
||||
cbCustomer.setItems(CustomerDB.getCustomers());
|
||||
cbPet.setItems(PetDB.getPets());
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.initialize",
|
||||
e,
|
||||
"Loading combo box data for services, customers, and pets");
|
||||
e.printStackTrace();
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<DropdownOption> services = DropdownApi.getInstance().getServices();
|
||||
List<DropdownOption> customers = DropdownApi.getInstance().getCustomers();
|
||||
List<DropdownOption> pets = DropdownApi.getInstance().getPets();
|
||||
|
||||
Platform.runLater(() -> {
|
||||
if (services != null) {
|
||||
cbService.setItems(FXCollections.observableArrayList(services));
|
||||
}
|
||||
if (customers != null) {
|
||||
cbCustomer.setItems(FXCollections.observableArrayList(customers));
|
||||
}
|
||||
if (pets != null) {
|
||||
cbPet.setItems(FXCollections.observableArrayList(pets));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.initialize",
|
||||
e,
|
||||
"Loading combo box data for services, customers, and pets");
|
||||
e.printStackTrace();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
|
||||
cbAppointmentStatus.setItems(statusList);
|
||||
|
||||
@@ -88,20 +110,49 @@ public class AppointmentDialogController {
|
||||
|
||||
cbMinute.getItems().addAll(0, 15, 30, 45);
|
||||
|
||||
// Show pet name
|
||||
cbPet.setCellFactory(param -> new ListCell<>() {
|
||||
// Show dropdown labels
|
||||
cbService.setCellFactory(param -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Pet pet, boolean empty) {
|
||||
super.updateItem(pet, empty);
|
||||
setText(empty || pet == null ? null : pet.getPetName());
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
cbService.setButtonCell(new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
|
||||
cbCustomer.setCellFactory(param -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
cbCustomer.setButtonCell(new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
|
||||
cbPet.setCellFactory(param -> new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
cbPet.setButtonCell(new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Pet pet, boolean empty) {
|
||||
super.updateItem(pet, empty);
|
||||
setText(empty || pet == null ? null : pet.getPetName());
|
||||
protected void updateItem(DropdownOption option, boolean empty) {
|
||||
super.updateItem(option, empty);
|
||||
setText(empty || option == null ? null : option.getLabel());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,26 +169,40 @@ public class AppointmentDialogController {
|
||||
selectedAppointment = appt;
|
||||
lblAppointmentId.setText("ID: " + appt.getAppointmentId());
|
||||
|
||||
dpAppointmentDate.setValue(
|
||||
java.time.LocalDate.parse(appt.getAppointmentDate())
|
||||
);
|
||||
try {
|
||||
dpAppointmentDate.setValue(
|
||||
java.time.LocalDate.parse(appt.getAppointmentDate())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.displayAppointmentDetails",
|
||||
e,
|
||||
"Parsing appointment date");
|
||||
}
|
||||
|
||||
cbAppointmentStatus.setValue(appt.getAppointmentStatus());
|
||||
|
||||
Time time = Time.valueOf(appt.getAppointmentTime());
|
||||
cbHour.setValue(time.toLocalTime().getHour());
|
||||
cbMinute.setValue(time.toLocalTime().getMinute());
|
||||
try {
|
||||
LocalTime time = LocalTime.parse(appt.getAppointmentTime());
|
||||
cbHour.setValue(time.getHour());
|
||||
cbMinute.setValue(time.getMinute());
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.displayAppointmentDetails",
|
||||
e,
|
||||
"Parsing appointment time");
|
||||
}
|
||||
|
||||
cbService.getItems().forEach(s -> {
|
||||
if (s.getServiceId() == appt.getServiceId()) cbService.setValue(s);
|
||||
if (s.getId() == appt.getServiceId()) cbService.setValue(s);
|
||||
});
|
||||
|
||||
cbCustomer.getItems().forEach(c -> {
|
||||
if (c.getCustomerId() == appt.getCustomerId()) cbCustomer.setValue(c);
|
||||
if (c.getId() == appt.getCustomerId()) cbCustomer.setValue(c);
|
||||
});
|
||||
|
||||
cbPet.getItems().forEach(p -> {
|
||||
if (p.getPetId() == appt.getPetId()) cbPet.setValue(p);
|
||||
if (p.getId() == appt.getPetId()) cbPet.setValue(p);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -159,45 +224,40 @@ public class AppointmentDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
Time appointmentTime =
|
||||
Time.valueOf(String.format(
|
||||
"%02d:%02d:00",
|
||||
cbHour.getValue(),
|
||||
cbMinute.getValue()
|
||||
));
|
||||
LocalTime appointmentTime = LocalTime.of(cbHour.getValue(), cbMinute.getValue());
|
||||
|
||||
Appointment appt = new Appointment(
|
||||
selectedAppointment == null ? 0 : selectedAppointment.getAppointmentId(),
|
||||
cbService.getValue().getServiceId(),
|
||||
cbCustomer.getValue().getCustomerId(),
|
||||
dpAppointmentDate.getValue().toString(),
|
||||
appointmentTime.toString(),
|
||||
cbAppointmentStatus.getValue()
|
||||
);
|
||||
AppointmentRequest request = new AppointmentRequest();
|
||||
request.setPetIds(Collections.singletonList(cbPet.getValue().getId()));
|
||||
request.setCustomerId(cbCustomer.getValue().getId());
|
||||
request.setServiceId(cbService.getValue().getId());
|
||||
request.setAppointmentDate(dpAppointmentDate.getValue());
|
||||
request.setAppointmentTime(appointmentTime);
|
||||
request.setAppointmentStatus(cbAppointmentStatus.getValue());
|
||||
|
||||
try {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (mode.equals("Add")) {
|
||||
AppointmentApi.getInstance().createAppointment(request);
|
||||
} else {
|
||||
AppointmentApi.getInstance().updateAppointment(
|
||||
(long) selectedAppointment.getAppointmentId(),
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
if (mode.equals("Add")) {
|
||||
int newId = AppointmentDB.insertAppointment(appt);
|
||||
AppointmentDB.insertAppointmentPet(newId, cbPet.getValue().getPetId());
|
||||
} else {
|
||||
AppointmentDB.updateAppointment(
|
||||
selectedAppointment.getAppointmentId(),
|
||||
appt,
|
||||
cbPet.getValue().getPetId()
|
||||
);
|
||||
Platform.runLater(() -> closeStage(e));
|
||||
|
||||
} catch (Exception ex) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.buttonSaveClicked",
|
||||
ex,
|
||||
"Saving appointment in " + mode + " mode");
|
||||
ex.printStackTrace();
|
||||
showError("Error saving appointment: " + ex.getMessage());
|
||||
});
|
||||
}
|
||||
|
||||
closeStage(e);
|
||||
|
||||
} catch (Exception ex) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"AppointmentDialogController.buttonSaveClicked",
|
||||
ex,
|
||||
"Saving appointment in " + mode + " mode");
|
||||
ex.printStackTrace();
|
||||
showError("Error saving appointment");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
@@ -11,14 +13,19 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.StringConverter;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.database.InventoryDB;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.models.Inventory;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.api.dto.inventory.InventoryRequest;
|
||||
import org.example.petshopdesktop.api.dto.inventory.InventoryResponse;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.InventoryApi;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InventoryDialogController {
|
||||
|
||||
@@ -60,12 +67,23 @@ public class InventoryDialogController {
|
||||
public Product fromString(String string) { return null; }
|
||||
});
|
||||
|
||||
//Load product list from DB into combobox
|
||||
//Load product list from API into combobox
|
||||
try {
|
||||
cbProduct.setItems(ProductDB.getProducts());
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
List<ProductResponse> productResponses = ProductApi.getInstance().listProducts(null);
|
||||
if (productResponses != null) {
|
||||
ObservableList<Product> products = FXCollections.observableArrayList();
|
||||
for (ProductResponse pr : productResponses) {
|
||||
products.add(new Product(
|
||||
pr.getProdId().intValue(),
|
||||
pr.getProdName(),
|
||||
pr.getProdPrice().doubleValue(),
|
||||
0,
|
||||
pr.getProdDesc()
|
||||
));
|
||||
}
|
||||
cbProduct.setItems(products);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryDialogController.initialize",
|
||||
e,
|
||||
@@ -106,75 +124,43 @@ public class InventoryDialogController {
|
||||
|
||||
//Operation only occurs if there are no errors
|
||||
if (errorMsg.isEmpty()) {
|
||||
|
||||
//Ensures duplicate entries aren't possible
|
||||
if (mode.equals("Add")) {
|
||||
try {
|
||||
InventoryRequest request = new InventoryRequest();
|
||||
Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
|
||||
|
||||
request.setProdId((long) selectedProduct.getProdId());
|
||||
int quantity;
|
||||
try {
|
||||
quantity = Integer.parseInt(txtQuantity.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid quantity format");
|
||||
}
|
||||
request.setQuantity(quantity);
|
||||
|
||||
if (InventoryDB.productExistsInInventory(selectedProduct.getProdId())) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Duplicate Entry");
|
||||
alert.setContentText("An inventory record for \"" + selectedProduct.getProdName() + "\" already exists.");
|
||||
alert.showAndWait();
|
||||
return;
|
||||
if (mode.equals("Add")) {
|
||||
InventoryApi.getInstance().createInventory(request);
|
||||
} else {
|
||||
String[] parts = lblInventoryId.getText().split(": ");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalStateException("Invalid inventory ID format");
|
||||
}
|
||||
Long inventoryId = Long.parseLong(parts[1]);
|
||||
InventoryApi.getInstance().updateInventory(inventoryId, request);
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Checking if product exists in inventory");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Inventory inventory = collectInventory();
|
||||
|
||||
//Adding inventory
|
||||
if (mode.equals("Add")) {
|
||||
try {
|
||||
numRow = InventoryDB.insertInventory(inventory);
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new inventory record");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Updating inventory
|
||||
else {
|
||||
try {
|
||||
numRow = InventoryDB.updateInventory(inventory.getInventoryId(), inventory);
|
||||
}
|
||||
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating inventory with ID: " + inventory.getInventoryId());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Display database operation result
|
||||
if (numRow == 0) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
else {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"InventoryDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode + " inventory");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,27 +173,6 @@ public class InventoryDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
//Create Inventory object using values entered by user
|
||||
private Inventory collectInventory() {
|
||||
int inventoryId = 0;
|
||||
|
||||
//Grab inventory ID when editing pre-existing record
|
||||
if (lblInventoryId.isVisible()) {
|
||||
inventoryId = Integer.parseInt(lblInventoryId.getText().split(": ")[1]);
|
||||
}
|
||||
|
||||
//Get selected product
|
||||
Product selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
|
||||
|
||||
//Build and returns Inventory object
|
||||
return new Inventory(
|
||||
inventoryId,
|
||||
selectedProduct.getProdId(),
|
||||
selectedProduct.getProdName(),
|
||||
Integer.parseInt(txtQuantity.getText())
|
||||
);
|
||||
}
|
||||
|
||||
//Close dialog view
|
||||
private void closeStage(MouseEvent mouseEvent) {
|
||||
Node node = (Node) mouseEvent.getSource();
|
||||
|
||||
@@ -8,14 +8,15 @@ import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.database.PetDB;
|
||||
import org.example.petshopdesktop.models.Category;
|
||||
import org.example.petshopdesktop.api.dto.pet.PetRequest;
|
||||
import org.example.petshopdesktop.api.dto.pet.PetResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.PetApi;
|
||||
import org.example.petshopdesktop.models.Pet;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class PetDialogController {
|
||||
|
||||
@@ -77,7 +78,6 @@ public class PetDialogController {
|
||||
}
|
||||
|
||||
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
||||
int numRow = 0;
|
||||
String errorMsg = "";
|
||||
|
||||
//Check validation (input required)
|
||||
@@ -102,46 +102,34 @@ public class PetDialogController {
|
||||
errorMsg += Validator.isNonNegativeInteger(txtPetAge.getText(), "Age");
|
||||
|
||||
if(errorMsg.isEmpty()){
|
||||
Pet pet = collectPet();
|
||||
if(mode.equals("Add")) {
|
||||
try{
|
||||
numRow = PetDB.insertPet(pet);
|
||||
PetRequest request = buildPetRequest();
|
||||
try {
|
||||
if(mode.equals("Add")) {
|
||||
PetApi.getInstance().createPet(request);
|
||||
} else {
|
||||
String[] parts = lblPetId.getText().split(": ");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalStateException("Invalid pet ID format");
|
||||
}
|
||||
Long petId = Long.parseLong(parts[1]);
|
||||
PetApi.getInstance().updatePet(petId, request);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new pet record");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
numRow = PetDB.updatePet(pet.getPetId(), pet);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating pet with ID: " + pet.getPetId());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//if no rows were affected then there was an error (prompt user of error)
|
||||
if (numRow == 0){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
else {
|
||||
//tell the user operation was successful
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"PetDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode + " pet record");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Operation Error");
|
||||
alert.setContentText(mode + " failed: " + e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
else{
|
||||
@@ -152,23 +140,27 @@ public class PetDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
private Pet collectPet() {
|
||||
int petId =0;
|
||||
Pet pet = null;
|
||||
|
||||
if(lblPetId.isVisible()){
|
||||
petId = Integer.parseInt(lblPetId.getText().split(": ")[1]);
|
||||
private PetRequest buildPetRequest() {
|
||||
PetRequest request = new PetRequest();
|
||||
request.setPetName(txtPetName.getText());
|
||||
request.setPetSpecies(txtPetSpecies.getText());
|
||||
request.setPetBreed(txtPetBreed.getText());
|
||||
request.setPetStatus(cbPetStatus.getValue());
|
||||
try {
|
||||
request.setPetPrice(new BigDecimal(txtPetPrice.getText()));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid price format");
|
||||
}
|
||||
pet = new Pet(
|
||||
petId,
|
||||
txtPetName.getText(),
|
||||
txtPetSpecies.getText(),
|
||||
txtPetBreed.getText(),
|
||||
Integer.parseInt(txtPetAge.getText()),
|
||||
cbPetStatus.getValue(),
|
||||
Double.parseDouble(txtPetPrice.getText())
|
||||
);
|
||||
return pet;
|
||||
|
||||
int age;
|
||||
try {
|
||||
age = Integer.parseInt(txtPetAge.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid age format");
|
||||
}
|
||||
LocalDate dateOfBirth = LocalDate.now().minusYears(age);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private void closeStage(MouseEvent mouseEvent) {
|
||||
|
||||
@@ -10,15 +10,14 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.database.CategoryDB;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.models.Category;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
import org.example.petshopdesktop.api.dto.product.ProductRequest;
|
||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public class ProductDialogController {
|
||||
|
||||
@@ -29,7 +28,7 @@ public class ProductDialogController {
|
||||
private Button btnSave;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Category> cbProdCategory;
|
||||
private ComboBox<DropdownOption> cbProdCategory;
|
||||
|
||||
@FXML
|
||||
private Label lblMode;
|
||||
@@ -70,16 +69,17 @@ public class ProductDialogController {
|
||||
|
||||
//Set up combobox for selecting category
|
||||
try {
|
||||
//set up combobox
|
||||
ObservableList<Category> categories = FXCollections.observableArrayList(); //empty list
|
||||
categories = CategoryDB.getCategories();
|
||||
cbProdCategory.setItems(categories);
|
||||
} catch (SQLException e) {
|
||||
List<DropdownOption> categories = DropdownApi.getInstance().getCategories();
|
||||
if (categories != null) {
|
||||
ObservableList<DropdownOption> categoriesObs = FXCollections.observableArrayList(categories);
|
||||
cbProdCategory.setItems(categoriesObs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductDialogController.initialize",
|
||||
e,
|
||||
"Loading categories for combo box");
|
||||
throw new RuntimeException(e);
|
||||
System.out.println("Error loading categories: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -108,45 +108,45 @@ public class ProductDialogController {
|
||||
//Check Validation (format)
|
||||
errorMsg += Validator.isNonNegativeDouble(txtProdPrice.getText(), "Product Price");
|
||||
|
||||
if (errorMsg.isEmpty()) { //no validation errors detected
|
||||
Product product = collectProduct(); //get product info
|
||||
if (mode.equals("Add")){ //add mode
|
||||
try{
|
||||
numRow = ProductDB.insertProduct(product);
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new product record");
|
||||
throw new RuntimeException(e);
|
||||
if (errorMsg.isEmpty()) {
|
||||
try {
|
||||
ProductRequest request = new ProductRequest();
|
||||
request.setProdName(txtProdName.getText());
|
||||
BigDecimal price;
|
||||
try {
|
||||
price = new BigDecimal(txtProdPrice.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid price format");
|
||||
}
|
||||
request.setProdPrice(price);
|
||||
request.setCategoryId(cbProdCategory.getSelectionModel().getSelectedItem().getId());
|
||||
request.setProdDesc(txtProdDesc.getText());
|
||||
|
||||
if (mode.equals("Add")) {
|
||||
ProductApi.getInstance().createProduct(request);
|
||||
} else {
|
||||
String[] parts = lblProdId.getText().split(": ");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalStateException("Invalid product ID format");
|
||||
}
|
||||
Long productId = Long.parseLong(parts[1]);
|
||||
ProductApi.getInstance().updateProduct(productId, request);
|
||||
}
|
||||
}
|
||||
else { //edit
|
||||
try{
|
||||
numRow = ProductDB.updateProduct(product.getProdId(),product);
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating product with ID: " + product.getProdId());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//if no rows were affected then there was an error (prompt user of error)
|
||||
if (numRow == 0){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
else {
|
||||
//tell the user operation was successful
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode + " product");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
else{ //Display validation errors
|
||||
@@ -157,28 +157,6 @@ public class ProductDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the product info
|
||||
* @return product info with the id or the new product
|
||||
*/
|
||||
private Product collectProduct(){
|
||||
int prodId = 0;
|
||||
Product product = null;
|
||||
|
||||
if(lblProdId.isVisible()){ //Edit mode
|
||||
//get product id from lblId (split the string so we only get the int)
|
||||
prodId = Integer.parseInt(lblProdId.getText().split(": ")[1]);
|
||||
}
|
||||
product = new Product(
|
||||
prodId,
|
||||
txtProdName.getText(),
|
||||
Double.parseDouble(txtProdPrice.getText()),
|
||||
cbProdCategory.getSelectionModel().getSelectedItem().getCategoryId(),
|
||||
txtProdDesc.getText()
|
||||
);
|
||||
return product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the product data in text fields and combobox
|
||||
* @param product the product entity containing data to display
|
||||
@@ -190,10 +168,10 @@ public class ProductDialogController {
|
||||
txtProdDesc.setText(product.getProdDesc());
|
||||
txtProdPrice.setText(product.getProdPrice() + "");
|
||||
|
||||
//get the right combobox selection
|
||||
for (Category category : cbProdCategory.getItems()) {
|
||||
if(category.getCategoryId() == product.getCategoryId()){
|
||||
for (DropdownOption category : cbProdCategory.getItems()) {
|
||||
if(category.getLabel().equals(product.getCategoryName())){
|
||||
cbProdCategory.getSelectionModel().select(category);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.EventHandler;
|
||||
@@ -10,16 +11,14 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.DTOs.ProductSupplierDTO;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.database.ProductDB;
|
||||
import org.example.petshopdesktop.database.ProductSupplierDB;
|
||||
import org.example.petshopdesktop.database.SupplierDB;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.ProductSupplier;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.api.dto.common.DropdownOption;
|
||||
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierRequest;
|
||||
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.api.endpoints.ProductSupplierApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ProductSupplierDialogController {
|
||||
|
||||
@@ -30,10 +29,10 @@ public class ProductSupplierDialogController {
|
||||
private Button btnSave;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Product> cbProduct;
|
||||
private ComboBox<DropdownOption> cbProduct;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Supplier> cbSupplier;
|
||||
private ComboBox<DropdownOption> cbSupplier;
|
||||
|
||||
@FXML
|
||||
private Label lblMode;
|
||||
@@ -67,26 +66,78 @@ public class ProductSupplierDialogController {
|
||||
}
|
||||
});
|
||||
|
||||
//Set up combobox for selecting product and supplier
|
||||
try{
|
||||
ObservableList<Supplier> suppliers = FXCollections.observableArrayList(); //empty list
|
||||
ObservableList<Product> products = FXCollections.observableArrayList(); //empty list
|
||||
cbSupplier.setButtonCell(new ListCell<DropdownOption>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
} else {
|
||||
setText(item.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
cbSupplier.setCellFactory(lv -> new ListCell<DropdownOption>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
} else {
|
||||
setText(item.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//get suppliers and products from DB
|
||||
suppliers = SupplierDB.getSuppliers();
|
||||
products = ProductDB.getProducts();
|
||||
cbProduct.setButtonCell(new ListCell<DropdownOption>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
} else {
|
||||
setText(item.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
cbProduct.setCellFactory(lv -> new ListCell<DropdownOption>() {
|
||||
@Override
|
||||
protected void updateItem(DropdownOption item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
} else {
|
||||
setText(item.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Populate combobox
|
||||
cbSupplier.setItems(suppliers);
|
||||
cbProduct.setItems(products);
|
||||
}
|
||||
catch(SQLException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.initialize",
|
||||
e,
|
||||
"Loading suppliers and products for combo boxes");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
new Thread(() -> {
|
||||
try {
|
||||
var suppliers = DropdownApi.getInstance().getSuppliers();
|
||||
var products = DropdownApi.getInstance().getProducts();
|
||||
|
||||
Platform.runLater(() -> {
|
||||
if (suppliers != null) {
|
||||
cbSupplier.setItems(FXCollections.observableArrayList(suppliers));
|
||||
}
|
||||
if (products != null) {
|
||||
cbProduct.setItems(FXCollections.observableArrayList(products));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.initialize",
|
||||
e,
|
||||
"Loading suppliers and products for combo boxes");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Initialization Error");
|
||||
alert.setContentText("Failed to load dropdown data: " + e.getMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@@ -96,10 +147,8 @@ public class ProductSupplierDialogController {
|
||||
* @param mouseEvent click event for save button
|
||||
*/
|
||||
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
||||
int numRows = 0;
|
||||
String errorMsg = ""; //error message for validation
|
||||
String errorMsg = "";
|
||||
|
||||
//Check Validation (input required)
|
||||
errorMsg += Validator.isPresent(txtCost.getText(), "Cost");
|
||||
if (cbProduct.getSelectionModel().getSelectedItem() == null) {
|
||||
errorMsg += "Product is required \n";
|
||||
@@ -108,82 +157,41 @@ public class ProductSupplierDialogController {
|
||||
errorMsg += "Supplier is required \n";
|
||||
}
|
||||
|
||||
//Check validation (length size)
|
||||
errorMsg += Validator.isLessThanVarChars(txtCost.getText(), "Cost", 12);
|
||||
|
||||
//Check validation (format)
|
||||
errorMsg += Validator.isNonNegativeDouble(txtCost.getText(), "Cost");
|
||||
|
||||
if(errorMsg.isEmpty()){ //no validation errors
|
||||
ProductSupplier productSupplier = collectProductSupplier(); //get productSupplier info
|
||||
if (mode.equals("Add")) { //add mode
|
||||
try{
|
||||
numRows = ProductSupplierDB.insertProductSupplier(productSupplier);
|
||||
}
|
||||
catch(SQLIntegrityConstraintViolationException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting product-supplier (integrity constraint violation)");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText("Add failed \n" +
|
||||
"the product-supplier link is already in the database");
|
||||
alert.showAndWait();
|
||||
numRows = -1; //Update numRow so alert only shows once
|
||||
closeStage(mouseEvent);
|
||||
}
|
||||
catch(SQLException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new product-supplier record");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
else { //edit
|
||||
try{
|
||||
numRows = ProductSupplierDB.updateProductSupplier(selectedSupId, selectedProdId, productSupplier);
|
||||
}
|
||||
catch(SQLIntegrityConstraintViolationException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating product-supplier (integrity constraint violation) - SupID: " + selectedSupId + ", ProdID: " + selectedProdId);
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText("Edit failed \n" +
|
||||
"the product-supplier link is already in the database");
|
||||
alert.showAndWait();
|
||||
numRows = -1; //Update numRow so alert only shows once
|
||||
closeStage(mouseEvent);
|
||||
}
|
||||
catch(SQLException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating product-supplier - SupID: " + selectedSupId + ", ProdID: " + selectedProdId);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
if(errorMsg.isEmpty()){
|
||||
ProductSupplierRequest request = collectProductSupplierRequest();
|
||||
|
||||
//if no rows were affected then there was an error (prompt user of error)
|
||||
if (numRows == 0){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
else if (numRows > 0){
|
||||
//tell the user operation was successful
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
}
|
||||
}
|
||||
else { //Display validation errors
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (mode.equals("Add")) {
|
||||
ProductSupplierApi.getInstance().createProductSupplier(request);
|
||||
} else {
|
||||
ProductSupplierApi.getInstance().updateProductSupplier((long) selectedProdId, (long) selectedSupId, request);
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ProductSupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode + " product-supplier");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed: " + e.getMessage());
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
} else {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Input Error");
|
||||
alert.setContentText(errorMsg);
|
||||
@@ -193,18 +201,18 @@ public class ProductSupplierDialogController {
|
||||
|
||||
/**
|
||||
* collect the data for new/updated productSupplier
|
||||
* @return productSupplier entity with data
|
||||
* @return productSupplier request with data
|
||||
*/
|
||||
private ProductSupplier collectProductSupplier() {
|
||||
ProductSupplier productSupplier = null;
|
||||
|
||||
productSupplier = new ProductSupplier(
|
||||
cbSupplier.getSelectionModel().getSelectedItem().getSupId(),
|
||||
cbProduct.getSelectionModel().getSelectedItem().getProdId(),
|
||||
Double.parseDouble(txtCost.getText())
|
||||
);
|
||||
|
||||
return productSupplier;
|
||||
private ProductSupplierRequest collectProductSupplierRequest() {
|
||||
ProductSupplierRequest request = new ProductSupplierRequest();
|
||||
request.setSupplierId(cbSupplier.getSelectionModel().getSelectedItem().getId());
|
||||
request.setProductId(cbProduct.getSelectionModel().getSelectedItem().getId());
|
||||
try {
|
||||
request.setCost(new BigDecimal(txtCost.getText()));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid cost format");
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,20 +224,17 @@ public class ProductSupplierDialogController {
|
||||
txtCost.setText(productSupplier.getCost() + "");
|
||||
}
|
||||
|
||||
//Get the right combobox selection (product)
|
||||
for (Product product : cbProduct.getItems()) {
|
||||
if(product.getProdId() == productSupplier.getProdId()){
|
||||
for (DropdownOption product : cbProduct.getItems()) {
|
||||
if(product.getId() == productSupplier.getProdId()){
|
||||
cbProduct.getSelectionModel().select(product);
|
||||
}
|
||||
}
|
||||
|
||||
//Get the right combobox selection (supplier)
|
||||
for (Supplier supplier : cbSupplier.getItems()) {
|
||||
if (supplier.getSupId() == productSupplier.getSupId()) {
|
||||
for (DropdownOption supplier : cbSupplier.getItems()) {
|
||||
if (supplier.getId() == productSupplier.getSupId()) {
|
||||
cbSupplier.getSelectionModel().select(supplier);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,15 +7,19 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.PropertyValueFactory;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleItemRequest;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleItemResponse;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleRequest;
|
||||
import org.example.petshopdesktop.api.dto.sale.SaleResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.SaleApi;
|
||||
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.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -31,19 +35,19 @@ public class RefundDialogController {
|
||||
private Label lblSaleInfo;
|
||||
|
||||
@FXML
|
||||
private TableView<SaleDetail.SaleDetailItem> tvOriginalItems;
|
||||
private TableView<SaleItemResponse> tvOriginalItems;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, String> colOriginalProduct;
|
||||
private TableColumn<SaleItemResponse, String> colOriginalProduct;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Integer> colOriginalQuantity;
|
||||
private TableColumn<SaleItemResponse, Integer> colOriginalQuantity;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Double> colOriginalUnitPrice;
|
||||
private TableColumn<SaleItemResponse, BigDecimal> colOriginalUnitPrice;
|
||||
|
||||
@FXML
|
||||
private TableColumn<SaleDetail.SaleDetailItem, Double> colOriginalTotal;
|
||||
private TableColumn<SaleItemResponse, BigDecimal> colOriginalTotal;
|
||||
|
||||
@FXML
|
||||
private Button btnAddToRefund;
|
||||
@@ -78,7 +82,7 @@ public class RefundDialogController {
|
||||
@FXML
|
||||
private Button btnCancel;
|
||||
|
||||
private SaleDetail currentSale;
|
||||
private SaleResponse currentSale;
|
||||
private final ObservableList<RefundItem> refundItems = FXCollections.observableArrayList();
|
||||
private final NumberFormat currency = NumberFormat.getCurrencyInstance(Locale.CANADA);
|
||||
|
||||
@@ -94,7 +98,7 @@ public class RefundDialogController {
|
||||
colOriginalProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
|
||||
colOriginalQuantity.setCellValueFactory(new PropertyValueFactory<>("quantity"));
|
||||
colOriginalUnitPrice.setCellValueFactory(new PropertyValueFactory<>("unitPrice"));
|
||||
colOriginalTotal.setCellValueFactory(new PropertyValueFactory<>("total"));
|
||||
colOriginalTotal.setCellValueFactory(new PropertyValueFactory<>("lineTotal"));
|
||||
tvOriginalItems.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
|
||||
|
||||
colRefundProduct.setCellValueFactory(new PropertyValueFactory<>("productName"));
|
||||
@@ -113,21 +117,25 @@ public class RefundDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
int saleId;
|
||||
Long saleId;
|
||||
try {
|
||||
saleId = Integer.parseInt(saleIdText);
|
||||
saleId = Long.parseLong(saleIdText);
|
||||
} catch (NumberFormatException e) {
|
||||
showError("Load Sale", "Invalid transaction ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (SaleDB.isRefunded(saleId)) {
|
||||
List<SaleResponse> allSales = SaleApi.getInstance().listSales(0, 1000, null);
|
||||
boolean alreadyRefunded = allSales.stream()
|
||||
.anyMatch(s -> Boolean.TRUE.equals(s.getIsRefund()) && saleId.equals(s.getOriginalSaleId()));
|
||||
|
||||
if (alreadyRefunded) {
|
||||
showError("Load Sale", "This sale has already been refunded.");
|
||||
return;
|
||||
}
|
||||
|
||||
currentSale = SaleDB.getSaleById(saleId);
|
||||
currentSale = SaleApi.getInstance().getSale(saleId);
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
|
||||
String saleInfo = String.format("Sale Date: %s | Employee: %s | Original Total: %s | Payment: %s",
|
||||
@@ -137,13 +145,13 @@ public class RefundDialogController {
|
||||
currentSale.getPaymentMethod());
|
||||
lblSaleInfo.setText(saleInfo);
|
||||
|
||||
tvOriginalItems.setItems(currentSale.getItems());
|
||||
tvOriginalItems.setItems(FXCollections.observableArrayList(currentSale.getItems()));
|
||||
cbPaymentMethod.getSelectionModel().select(currentSale.getPaymentMethod());
|
||||
|
||||
refundItems.clear();
|
||||
updateRefundTotal();
|
||||
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("RefundDialogController.btnLoadSaleClicked", e, "Loading sale");
|
||||
showError("Load Sale", e.getMessage() != null ? e.getMessage() : "Could not load sale.");
|
||||
}
|
||||
@@ -156,14 +164,14 @@ public class RefundDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
SaleDetail.SaleDetailItem selected = tvOriginalItems.getSelectionModel().getSelectedItem();
|
||||
SaleItemResponse 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())
|
||||
.filter(r -> r.getProdId() == selected.getSaleItemId().intValue())
|
||||
.mapToInt(RefundItem::getQuantity)
|
||||
.sum();
|
||||
|
||||
@@ -192,10 +200,10 @@ public class RefundDialogController {
|
||||
}
|
||||
|
||||
refundItems.add(new RefundItem(
|
||||
selected.getProdId(),
|
||||
selected.getSaleItemId().intValue(),
|
||||
selected.getProductName(),
|
||||
quantity,
|
||||
selected.getUnitPrice()
|
||||
selected.getUnitPrice().doubleValue()
|
||||
));
|
||||
updateRefundTotal();
|
||||
|
||||
@@ -226,9 +234,9 @@ public class RefundDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer employeeId = UserSession.getInstance().getEmployeeId();
|
||||
if (employeeId == null || employeeId <= 0) {
|
||||
showError("Process Refund", "Employee is not set for this account.");
|
||||
Long storeId = UserSession.getInstance().getStoreId();
|
||||
if (storeId == null || storeId <= 0) {
|
||||
showError("Process Refund", "Store is not set for this account.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,22 +257,32 @@ public class RefundDialogController {
|
||||
}
|
||||
|
||||
try {
|
||||
ObservableList<SaleCartItem> cartItems = FXCollections.observableArrayList();
|
||||
for (RefundItem item : refundItems) {
|
||||
cartItems.add(new SaleCartItem(item.getProdId(), item.getProductName(), item.getQuantity(), item.getUnitPrice()));
|
||||
}
|
||||
SaleRequest request = new SaleRequest();
|
||||
request.setStoreId(storeId);
|
||||
request.setPaymentMethod(payment);
|
||||
request.setIsRefund(true);
|
||||
request.setOriginalSaleId(currentSale.getSaleId());
|
||||
|
||||
int refundId = SaleDB.createRefund(currentSale.getSaleId(), employeeId, payment, cartItems);
|
||||
List<SaleItemRequest> items = new ArrayList<>();
|
||||
for (RefundItem item : refundItems) {
|
||||
SaleItemRequest saleItem = new SaleItemRequest();
|
||||
saleItem.setProdId((long) item.getProdId());
|
||||
saleItem.setQuantity(-item.getQuantity());
|
||||
items.add(saleItem);
|
||||
}
|
||||
request.setItems(items);
|
||||
|
||||
SaleResponse refundResponse = SaleApi.getInstance().createSale(request);
|
||||
|
||||
Alert success = new Alert(Alert.AlertType.INFORMATION);
|
||||
success.setTitle("Refund Processed");
|
||||
success.setHeaderText(null);
|
||||
success.setContentText("Refund ID " + refundId + " was created successfully.");
|
||||
success.setContentText("Refund ID " + refundResponse.getSaleId() + " was created successfully.");
|
||||
success.showAndWait();
|
||||
|
||||
closeDialog();
|
||||
|
||||
} catch (SQLException e) {
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("RefundDialogController.btnProcessRefundClicked", e, "Processing refund");
|
||||
showError("Process Refund", e.getMessage() != null ? e.getMessage() : "Could not process refund.");
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.database.ServiceDB;
|
||||
import org.example.petshopdesktop.models.Service;
|
||||
import org.example.petshopdesktop.DTOs.ServiceDTO;
|
||||
import org.example.petshopdesktop.api.dto.service.ServiceRequest;
|
||||
import org.example.petshopdesktop.api.endpoints.ServiceApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ComboBox;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
public class ServiceDialogController {
|
||||
@@ -45,7 +46,7 @@ public class ServiceDialogController {
|
||||
private ComboBox<Integer> cbMinutes;
|
||||
|
||||
private String mode;
|
||||
private Service selectedService;
|
||||
private ServiceDTO selectedService;
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +69,7 @@ public class ServiceDialogController {
|
||||
}
|
||||
}
|
||||
|
||||
public void setService(Service service) {
|
||||
public void setService(ServiceDTO service) {
|
||||
this.selectedService = service;
|
||||
|
||||
lblServiceId.setText("ID: " + service.getServiceId());
|
||||
@@ -114,22 +115,20 @@ public class ServiceDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
int duration = (hours * 60) + minutes;
|
||||
|
||||
Service service = new Service(
|
||||
selectedService == null ? 0 : selectedService.getServiceId(),
|
||||
name,
|
||||
desc,
|
||||
duration,
|
||||
price
|
||||
);
|
||||
|
||||
try {
|
||||
int durationMinutes = (hours * 60) + minutes;
|
||||
|
||||
ServiceRequest request = new ServiceRequest();
|
||||
request.setServiceName(name);
|
||||
request.setServiceDesc(desc);
|
||||
request.setServicePrice(BigDecimal.valueOf(price));
|
||||
request.setServiceDuration(durationMinutes);
|
||||
|
||||
if (mode.equals("Add")) {
|
||||
ServiceDB.insertService(service);
|
||||
ServiceApi.getInstance().createService(request);
|
||||
} else {
|
||||
ServiceDB.updateService(selectedService.getServiceId(), service);
|
||||
Long serviceId = (long) selectedService.getServiceId();
|
||||
ServiceApi.getInstance().updateService(serviceId, request);
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.example.petshopdesktop.controllers.dialogcontrollers;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Alert;
|
||||
@@ -8,11 +9,10 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.database.UserDB;
|
||||
import org.example.petshopdesktop.api.dto.user.UserRequest;
|
||||
import org.example.petshopdesktop.api.endpoints.UserApi;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class StaffRegisterDialogController {
|
||||
|
||||
@FXML
|
||||
@@ -49,7 +49,6 @@ public class StaffRegisterDialogController {
|
||||
String firstName = value(txtFirstName);
|
||||
String lastName = value(txtLastName);
|
||||
String email = value(txtEmail);
|
||||
String phone = value(txtPhone);
|
||||
String username = value(txtUsername);
|
||||
String password = txtPassword.getText() == null ? "" : txtPassword.getText();
|
||||
String confirm = txtPasswordConfirm.getText() == null ? "" : txtPasswordConfirm.getText();
|
||||
@@ -62,10 +61,6 @@ public class StaffRegisterDialogController {
|
||||
lblError.setText("Email is required.");
|
||||
return;
|
||||
}
|
||||
if (phone.isBlank()) {
|
||||
lblError.setText("Phone is required.");
|
||||
return;
|
||||
}
|
||||
if (username.isBlank()) {
|
||||
lblError.setText("Username is required.");
|
||||
return;
|
||||
@@ -79,26 +74,41 @@ public class StaffRegisterDialogController {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
UserDB.createStaffAccount(firstName, lastName, email, phone, username, password);
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Staff Account");
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText("Staff account created. You can log in now.");
|
||||
alert.showAndWait();
|
||||
close();
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Creating staff account");
|
||||
String msg = e.getMessage() == null ? "Could not create staff account." : e.getMessage();
|
||||
if (msg.toLowerCase().contains("duplicate") || msg.toLowerCase().contains("unique")) {
|
||||
lblError.setText("Username already exists.");
|
||||
} else {
|
||||
lblError.setText(msg);
|
||||
btnCreate.setDisable(true);
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
UserRequest request = new UserRequest();
|
||||
request.setUsername(username);
|
||||
request.setPassword(password);
|
||||
request.setFullName(firstName + " " + lastName);
|
||||
request.setEmail(email);
|
||||
request.setRole("STAFF");
|
||||
request.setActive(true);
|
||||
|
||||
UserApi.getInstance().createUser(request);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Staff Account");
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText("Staff account created. You can log in now.");
|
||||
alert.showAndWait();
|
||||
close();
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Creating staff account");
|
||||
String msg = e.getMessage() == null ? "Could not create staff account." : e.getMessage();
|
||||
Platform.runLater(() -> {
|
||||
if (msg.toLowerCase().contains("duplicate") || msg.toLowerCase().contains("unique")) {
|
||||
lblError.setText("Username already exists.");
|
||||
} else {
|
||||
lblError.setText(msg);
|
||||
}
|
||||
btnCreate.setDisable(false);
|
||||
});
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
ActivityLogger.getInstance().logException("StaffRegisterDialogController.btnCreateClicked", e, "Database connection");
|
||||
lblError.setText("Database is not connected.");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -10,12 +10,12 @@ import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.stage.Stage;
|
||||
import org.example.petshopdesktop.Validator;
|
||||
import org.example.petshopdesktop.database.SupplierDB;
|
||||
import org.example.petshopdesktop.api.dto.supplier.SupplierRequest;
|
||||
import org.example.petshopdesktop.api.dto.supplier.SupplierResponse;
|
||||
import org.example.petshopdesktop.api.endpoints.SupplierApi;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class SupplierDialogController {
|
||||
|
||||
@FXML
|
||||
@@ -74,7 +74,6 @@ public class SupplierDialogController {
|
||||
* @param mouseEvent click event for save button
|
||||
*/
|
||||
private void buttonSaveClicked(MouseEvent mouseEvent) {
|
||||
int numRow = 0; //how many rows affected
|
||||
String errorMsg = ""; //error message for validation
|
||||
|
||||
//Check validation (input required)
|
||||
@@ -95,44 +94,33 @@ public class SupplierDialogController {
|
||||
errorMsg += Validator.isValidPhoneNumber(txtPhone.getText(), "Phone Number");
|
||||
|
||||
if(errorMsg.isEmpty()){ //no validation errors detected
|
||||
Supplier supplier = collectSupplier(); //get supplier info
|
||||
if (mode.equals("Add")) { //add mode
|
||||
try{
|
||||
numRow = SupplierDB.insertSupplier(supplier);
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Inserting new supplier record");
|
||||
throw new RuntimeException(e);
|
||||
SupplierRequest request = createSupplierRequest();
|
||||
try {
|
||||
if (mode.equals("Add")) {
|
||||
SupplierApi.getInstance().createSupplier(request);
|
||||
} else {
|
||||
String[] parts = lblSupId.getText().split(": ");
|
||||
if (parts.length < 2) {
|
||||
throw new IllegalStateException("Invalid supplier ID format");
|
||||
}
|
||||
Long supplierId = Long.parseLong(parts[1]);
|
||||
SupplierApi.getInstance().updateSupplier(supplierId, request);
|
||||
}
|
||||
}
|
||||
else{ //edit mode
|
||||
try{
|
||||
numRow = SupplierDB.updateSupplier(supplier.getSupId(),supplier);
|
||||
} catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
"Updating supplier with ID: " + supplier.getSupId());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//if no rows were affected then there was an error (prompt user of error)
|
||||
if (numRow == 0){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed");
|
||||
alert.showAndWait();
|
||||
}
|
||||
else {
|
||||
//tell the user operation was successful
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setHeaderText("Saved");
|
||||
alert.setContentText(mode + " succeeded");
|
||||
alert.showAndWait();
|
||||
closeStage(mouseEvent);
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"SupplierDialogController.buttonSaveClicked",
|
||||
e,
|
||||
mode.equals("Add") ? "Inserting new supplier record" : "Updating supplier record");
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setHeaderText("Database Operation Error");
|
||||
alert.setContentText(mode + " failed: " + e.getMessage());
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
else{ //Display validation errors
|
||||
@@ -154,26 +142,17 @@ public class SupplierDialogController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the supplier info
|
||||
* @return supplier info with the id or the new supplier
|
||||
* Create a supplier request from the form inputs
|
||||
* @return supplier request for API call
|
||||
*/
|
||||
private Supplier collectSupplier(){
|
||||
int supId = 0;
|
||||
Supplier supplier = null;
|
||||
|
||||
if(lblSupId.isVisible()){ //Edit mode
|
||||
//get supplier id from lblId (split the string so we only get the int)
|
||||
supId = Integer.parseInt(lblSupId.getText().split(": ")[1]);
|
||||
}
|
||||
supplier = new Supplier(
|
||||
supId,
|
||||
txtCompanyName.getText(),
|
||||
txtContactFirstName.getText(),
|
||||
txtContactLastName.getText(),
|
||||
txtEmail.getText(),
|
||||
txtPhone.getText()
|
||||
);
|
||||
return supplier;
|
||||
private SupplierRequest createSupplierRequest(){
|
||||
SupplierRequest request = new SupplierRequest();
|
||||
request.setSupCompany(txtCompanyName.getText());
|
||||
request.setSupContactFirstName(txtContactFirstName.getText());
|
||||
request.setSupContactLastName(txtContactLastName.getText());
|
||||
request.setSupEmail(txtEmail.getText());
|
||||
request.setSupPhone(txtPhone.getText());
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Adoption;
|
||||
import org.example.petshopdesktop.models.Customer;
|
||||
import org.example.petshopdesktop.models.Pet;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class AdoptionDB {
|
||||
|
||||
//Select query
|
||||
private static final String BASE_SELECT =
|
||||
"SELECT a.adoptionId, a.petId, a.customerId, " +
|
||||
"CONCAT(c.firstName, ' ', c.lastName) AS customerName, " +
|
||||
"a.adoptionDate, p.petPrice AS adoptionFee, a.adoptionStatus " +
|
||||
"FROM adoption a " +
|
||||
"JOIN customer c ON a.customerId = c.customerId " +
|
||||
"JOIN pet p ON a.petId = p.petId";
|
||||
|
||||
//Retrieve all adoption records from DB
|
||||
public static ObservableList<Adoption> getAdoptions() throws SQLException {
|
||||
ObservableList<Adoption> adoptions = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(BASE_SELECT);
|
||||
|
||||
//Map results
|
||||
while (rs.next()) {
|
||||
adoptions.add(mapRow(rs));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return adoptions;
|
||||
}
|
||||
|
||||
//Returns data depending on search query
|
||||
public static ObservableList<Adoption> getFilteredAdoptions(String filter) throws SQLException {
|
||||
ObservableList<Adoption> adoptions = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = BASE_SELECT +
|
||||
" WHERE a.adoptionId LIKE '%" + filter + "%' OR " +
|
||||
"a.petId LIKE '%" + filter + "%' OR " +
|
||||
"CONCAT(c.firstName, ' ', c.lastName) LIKE '%" + filter + "%' OR " +
|
||||
"a.adoptionDate LIKE '%" + filter + "%' OR " +
|
||||
"p.petPrice LIKE '%" + filter + "%' OR " +
|
||||
"a.adoptionStatus LIKE '%" + filter + "%'";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
//Map results
|
||||
while (rs.next()) {
|
||||
adoptions.add(mapRow(rs));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return adoptions;
|
||||
}
|
||||
|
||||
//Add new adoption
|
||||
public static int insertAdoption(Adoption adoption) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO adoption (petId, customerId, adoptionDate, adoptionStatus) VALUES (?, ?, ?, ?)";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
//Put data in Adoption object
|
||||
stmt.setInt(1, adoption.getPetId());
|
||||
stmt.setInt(2, adoption.getCustomerId());
|
||||
stmt.setString(3, adoption.getAdoptionDate());
|
||||
stmt.setString(4, adoption.getAdoptionStatus());
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("adoption",
|
||||
"N/A",
|
||||
String.format("Adoption record added for Pet ID %d, Customer ID %d", adoption.getPetId(), adoption.getCustomerId()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//Updating pre-existing adoption
|
||||
public static int updateAdoption(int adoptionId, Adoption adoption) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "UPDATE adoption SET petId = ?, customerId = ?, adoptionDate = ?, adoptionStatus = ? WHERE adoptionId = ?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, adoption.getPetId());
|
||||
stmt.setInt(2, adoption.getCustomerId());
|
||||
stmt.setString(3, adoption.getAdoptionDate());
|
||||
stmt.setString(4, adoption.getAdoptionStatus());
|
||||
stmt.setInt(5, adoptionId);
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("adoption",
|
||||
String.valueOf(adoptionId),
|
||||
String.format("Adoption ID %d updated", adoptionId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//Delete adoption
|
||||
public static int deleteAdoption(int adoptionId) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "DELETE FROM adoption WHERE adoptionId = ?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, adoptionId);
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("adoption",
|
||||
String.valueOf(adoptionId),
|
||||
String.format("Adoption ID %d deleted", adoptionId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//Grab list of customers from DB for comboboxes
|
||||
public static ObservableList<Customer> getCustomers() throws SQLException {
|
||||
ObservableList<Customer> customers = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT customerId, firstName, lastName, email, phone FROM customer");
|
||||
|
||||
while (rs.next()) {
|
||||
customers.add(new Customer(rs.getInt(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5)));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return customers;
|
||||
}
|
||||
|
||||
//DRY: converts DB data into usable Java object
|
||||
private static Adoption mapRow(ResultSet rs) throws SQLException {
|
||||
return new Adoption(
|
||||
rs.getInt("adoptionId"),
|
||||
rs.getInt("petId"),
|
||||
rs.getInt("customerId"),
|
||||
rs.getString("customerName"),
|
||||
rs.getString("adoptionDate"),
|
||||
rs.getDouble("adoptionFee"),
|
||||
rs.getString("adoptionStatus")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import org.example.petshopdesktop.DTOs.AppointmentDTO;
|
||||
import org.example.petshopdesktop.models.Appointment;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class AppointmentDB {
|
||||
|
||||
// ============================
|
||||
// GET ALL APPOINTMENTS
|
||||
// ============================
|
||||
public static ObservableList<AppointmentDTO> getAppointmentDTOs()
|
||||
throws SQLException {
|
||||
|
||||
ObservableList<AppointmentDTO> list =
|
||||
FXCollections.observableArrayList();
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT a.appointmentId,
|
||||
c.customerId,
|
||||
CONCAT(c.firstName,' ',c.lastName) AS customerName,
|
||||
p.petId,
|
||||
p.petName,
|
||||
s.serviceId,
|
||||
s.serviceName,
|
||||
a.appointmentDate,
|
||||
a.appointmentTime,
|
||||
a.appointmentStatus
|
||||
FROM appointment a
|
||||
JOIN customer c ON a.customerId = c.customerId
|
||||
JOIN appointmentPet ap ON a.appointmentId = ap.appointmentId
|
||||
JOIN pet p ON ap.petId = p.petId
|
||||
JOIN service s ON a.serviceId = s.serviceId
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
|
||||
AppointmentDTO dto = new AppointmentDTO(
|
||||
rs.getInt("appointmentId"),
|
||||
|
||||
rs.getInt("customerId"),
|
||||
rs.getString("customerName"),
|
||||
|
||||
rs.getInt("petId"),
|
||||
rs.getString("petName"),
|
||||
|
||||
rs.getInt("serviceId"),
|
||||
rs.getString("serviceName"),
|
||||
|
||||
rs.getString("appointmentDate"),
|
||||
rs.getString("appointmentTime"),
|
||||
rs.getString("appointmentStatus")
|
||||
);
|
||||
|
||||
list.add(dto);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
// ============================
|
||||
// INSERT APPOINTMENT
|
||||
// ============================
|
||||
public static int insertAppointment(Appointment appt)
|
||||
throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
INSERT INTO appointment
|
||||
(serviceId, customerId, appointmentDate,
|
||||
appointmentTime, appointmentStatus)
|
||||
VALUES (?,?,?,?,?)
|
||||
""";
|
||||
|
||||
PreparedStatement ps =
|
||||
conn.prepareStatement(sql,
|
||||
Statement.RETURN_GENERATED_KEYS);
|
||||
|
||||
ps.setInt(1, appt.getServiceId());
|
||||
ps.setInt(2, appt.getCustomerId());
|
||||
ps.setString(3, appt.getAppointmentDate());
|
||||
ps.setString(4, appt.getAppointmentTime());
|
||||
ps.setString(5, appt.getAppointmentStatus());
|
||||
|
||||
ps.executeUpdate();
|
||||
|
||||
ResultSet keys = ps.getGeneratedKeys();
|
||||
int newId = 0;
|
||||
|
||||
if (keys.next()) {
|
||||
newId = keys.getInt(1);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (newId > 0) {
|
||||
ActivityLogger.getInstance().logInsert("appointment",
|
||||
String.valueOf(newId),
|
||||
String.format("Appointment created for Customer ID %d, Service ID %d", appt.getCustomerId(), appt.getServiceId()));
|
||||
}
|
||||
|
||||
return newId;
|
||||
}
|
||||
|
||||
//
|
||||
// LINK PET TO APPOINTMENT
|
||||
//
|
||||
public static void insertAppointmentPet(int appointmentId,
|
||||
int petId)
|
||||
throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql =
|
||||
"INSERT INTO appointmentPet (appointmentId, petId) VALUES (?,?)";
|
||||
|
||||
PreparedStatement ps = conn.prepareStatement(sql);
|
||||
ps.setInt(1, appointmentId);
|
||||
ps.setInt(2, petId);
|
||||
int numRows = ps.executeUpdate();
|
||||
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("appointmentPet",
|
||||
String.valueOf(appointmentId),
|
||||
String.format("Pet ID %d linked to Appointment ID %d", petId, appointmentId));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// UPDATE APPOINTMENT
|
||||
//
|
||||
public static int updateAppointment(int id,
|
||||
Appointment appt,
|
||||
int petId)
|
||||
throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql =
|
||||
"UPDATE appointment SET serviceId=?, customerId=?, " +
|
||||
"appointmentDate=?, appointmentTime=?, appointmentStatus=? " +
|
||||
"WHERE appointmentId=?";
|
||||
|
||||
PreparedStatement ps = conn.prepareStatement(sql);
|
||||
|
||||
ps.setInt(1, appt.getServiceId());
|
||||
ps.setInt(2, appt.getCustomerId());
|
||||
ps.setString(3, appt.getAppointmentDate());
|
||||
ps.setString(4, appt.getAppointmentTime());
|
||||
ps.setString(5, appt.getAppointmentStatus());
|
||||
ps.setInt(6, id);
|
||||
|
||||
ps.executeUpdate();
|
||||
|
||||
String sql2 =
|
||||
"UPDATE appointmentPet SET petId=? WHERE appointmentId=?";
|
||||
|
||||
PreparedStatement ps2 = conn.prepareStatement(sql2);
|
||||
ps2.setInt(1, petId);
|
||||
ps2.setInt(2, id);
|
||||
ps2.executeUpdate();
|
||||
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
ActivityLogger.getInstance().logUpdate("appointment",
|
||||
String.valueOf(id),
|
||||
String.format("Appointment ID %d updated", id));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// DELETE APPOINTMENT
|
||||
//
|
||||
public static int deleteAppointment(int id)
|
||||
throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
PreparedStatement ps1 =
|
||||
conn.prepareStatement(
|
||||
"DELETE FROM appointmentPet WHERE appointmentId=?"
|
||||
);
|
||||
ps1.setInt(1, id);
|
||||
ps1.executeUpdate();
|
||||
|
||||
PreparedStatement ps2 =
|
||||
conn.prepareStatement(
|
||||
"DELETE FROM appointment WHERE appointmentId=?"
|
||||
);
|
||||
ps2.setInt(1, id);
|
||||
|
||||
int rows = ps2.executeUpdate();
|
||||
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (rows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("appointment",
|
||||
String.valueOf(id),
|
||||
String.format("Appointment ID %d deleted", id));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Category;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class CategoryDB {
|
||||
/**
|
||||
* gets all the category into an observable list
|
||||
* @return a list of all the category
|
||||
* @throws SQLException if failed to find categories in the database
|
||||
*/
|
||||
public static ObservableList<Category> getCategories() throws SQLException{
|
||||
//Connect to the database
|
||||
ObservableList<Category> categories = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM category");
|
||||
|
||||
//While there is still data add categories to the list
|
||||
while(rs.next()){
|
||||
Category category = new Category(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getString(3));
|
||||
categories.add(category);
|
||||
}
|
||||
|
||||
//close connection and return categories
|
||||
conn.close();
|
||||
return categories;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ConnectionDB {
|
||||
/**
|
||||
* Method to try and connect to the database using connectionpetstore.properties.
|
||||
* @return Connection to the database
|
||||
*/
|
||||
public static Connection getConnection(){
|
||||
String url = "";
|
||||
String user = "";
|
||||
String password = "";
|
||||
|
||||
Properties prop = new Properties();
|
||||
Path propsPath;
|
||||
|
||||
String explicitPath = System.getenv("PETSTORE_DB_PROPS");
|
||||
if (explicitPath != null && !explicitPath.isBlank()) {
|
||||
propsPath = Paths.get(explicitPath);
|
||||
} else {
|
||||
Path cwd = Paths.get(System.getProperty("user.dir"), "connectionpetstore.properties");
|
||||
Path xdg = Paths.get(System.getProperty("user.home"), ".config", "petstore", "connectionpetstore.properties");
|
||||
Path legacyWindows = Paths.get("./connectionpetstore.properties");
|
||||
|
||||
if (Files.exists(cwd)) propsPath = cwd;
|
||||
else if (Files.exists(xdg)) propsPath = xdg;
|
||||
else propsPath = legacyWindows;
|
||||
}
|
||||
|
||||
try (FileInputStream fis = new FileInputStream(propsPath.toString())) {
|
||||
prop.load(fis);
|
||||
url = prop.getProperty("url");
|
||||
user = prop.getProperty("user");
|
||||
password = prop.getProperty("password");
|
||||
}
|
||||
catch(IOException e){
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ConnectionDB.getConnection",
|
||||
e,
|
||||
"Reading connection properties file");
|
||||
throw new RuntimeException("Problem with reading connection info: "+e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
return DriverManager.getConnection(url,user,password);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ConnectionDB.getConnection",
|
||||
e,
|
||||
"Establishing database connection");
|
||||
throw new RuntimeException("Problem with database connection: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Customer;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class CustomerDB {
|
||||
|
||||
//
|
||||
// GET ALL CUSTOMERS
|
||||
//
|
||||
public static ObservableList<Customer> getCustomers()
|
||||
throws SQLException {
|
||||
|
||||
ObservableList<Customer> list =
|
||||
FXCollections.observableArrayList();
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "SELECT * FROM customer";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while(rs.next()) {
|
||||
|
||||
Customer c = new Customer(
|
||||
rs.getInt("customerId"),
|
||||
rs.getString("firstName"),
|
||||
rs.getString("lastName"),
|
||||
rs.getString("email"),
|
||||
rs.getString("phone")
|
||||
);
|
||||
|
||||
list.add(c);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class EmployeeDB {
|
||||
|
||||
public static int ensureDefaultEmployee(String firstName, String lastName, String email, String phone, String role, boolean isActive) throws SQLException {
|
||||
Integer existingId = findEmployeeIdByEmail(email);
|
||||
if (existingId != null) {
|
||||
return existingId;
|
||||
}
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection()) {
|
||||
conn.setAutoCommit(false);
|
||||
try {
|
||||
int storeId = getDefaultStoreId(conn);
|
||||
int employeeId = createEmployee(conn, firstName, lastName, email, phone, role, isActive);
|
||||
assignEmployeeToStore(conn, employeeId, storeId);
|
||||
conn.commit();
|
||||
return employeeId;
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Integer findEmployeeIdByEmail(String email) throws SQLException {
|
||||
String sql = "SELECT employeeId FROM employee WHERE email = ? LIMIT 1";
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setString(1, email);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt("employeeId");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int createEmployee(Connection conn, String firstName, String lastName, String email, String phone, String role, boolean isActive) throws SQLException {
|
||||
String sql = "INSERT INTO employee (firstName, lastName, email, phone, role, isActive) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
try (PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setString(1, firstName);
|
||||
ps.setString(2, lastName);
|
||||
ps.setString(3, email);
|
||||
ps.setString(4, phone);
|
||||
ps.setString(5, role);
|
||||
ps.setBoolean(6, isActive);
|
||||
ps.executeUpdate();
|
||||
|
||||
try (ResultSet keys = ps.getGeneratedKeys()) {
|
||||
if (keys.next()) {
|
||||
return keys.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new SQLException("Could not create employee.");
|
||||
}
|
||||
|
||||
public static void assignEmployeeToStore(Connection conn, int employeeId, int storeId) throws SQLException {
|
||||
String sql = "INSERT IGNORE INTO employeeStore (employeeId, storeId) VALUES (?, ?)";
|
||||
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, employeeId);
|
||||
ps.setInt(2, storeId);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDefaultStoreId() throws SQLException {
|
||||
try (Connection conn = ConnectionDB.getConnection()) {
|
||||
return getDefaultStoreId(conn);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDefaultStoreId(Connection conn) throws SQLException {
|
||||
Integer existing = firstStoreId(conn);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
String insert = "INSERT INTO storeLocation (storeName, address, phone, email) VALUES ('Main Store', 'N/A', '000-000-0000', 'main@petshop.com')";
|
||||
try (PreparedStatement ps = conn.prepareStatement(insert, Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.executeUpdate();
|
||||
try (ResultSet keys = ps.getGeneratedKeys()) {
|
||||
if (keys.next()) {
|
||||
return keys.getInt(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Integer after = firstStoreId(conn);
|
||||
if (after != null) {
|
||||
return after;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static Integer getPrimaryStoreId(int employeeId) throws SQLException {
|
||||
String sql = "SELECT storeId FROM employeeStore WHERE employeeId = ? ORDER BY storeId ASC LIMIT 1";
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, employeeId);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt("storeId");
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Integer firstStoreId(Connection conn) throws SQLException {
|
||||
String sql = "SELECT storeId FROM storeLocation ORDER BY storeId ASC LIMIT 1";
|
||||
try (PreparedStatement ps = conn.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt("storeId");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Inventory;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class InventoryDB {
|
||||
|
||||
//Base selection query
|
||||
//Returns invID, ProdID, Quantity from Inventory table
|
||||
//Returns ProdName from Product table
|
||||
private static final String BASE_SELECT =
|
||||
"SELECT i.inventoryId, i.prodId, p.prodName, i.quantity " +
|
||||
"FROM inventory i " +
|
||||
"JOIN product p ON i.prodId = p.prodId";
|
||||
|
||||
|
||||
//Retrieves inventory records from DB
|
||||
public static ObservableList<Inventory> getInventory() throws SQLException {
|
||||
ObservableList<Inventory> inventoryList = FXCollections.observableArrayList();
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(BASE_SELECT);
|
||||
|
||||
while (rs.next()) {
|
||||
inventoryList.add(mapRow(rs));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
|
||||
return inventoryList;
|
||||
}
|
||||
|
||||
//Returns records depending on search
|
||||
public static ObservableList<Inventory> getFilteredInventory(String filter) throws SQLException {
|
||||
ObservableList<Inventory> inventoryList = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = BASE_SELECT +
|
||||
" WHERE i.inventoryId LIKE ? OR " +
|
||||
"i.prodId LIKE ? OR " +
|
||||
"p.prodName LIKE ? OR " +
|
||||
"i.quantity LIKE ?";
|
||||
|
||||
String filteredString = "%" + filter + "%";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
stmt.setString(i, filteredString);
|
||||
}
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
inventoryList.add(mapRow(rs));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
|
||||
return inventoryList;
|
||||
}
|
||||
|
||||
//Checks if the product already has an inventory entry
|
||||
public static boolean productExistsInInventory(int prodId) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "SELECT COUNT(*) FROM inventory WHERE prodId = ?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, prodId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
boolean exists = rs.next() && rs.getInt(1) > 0;
|
||||
conn.close();
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
//Inserting new inventory record
|
||||
public static int insertInventory(Inventory inventory) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO inventory (prodId, quantity) VALUES (?, ?)";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, inventory.getProdId());
|
||||
stmt.setInt(2, inventory.getQuantity());
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("inventory",
|
||||
"N/A",
|
||||
String.format("Inventory added for Product ID %d, Quantity %d", inventory.getProdId(), inventory.getQuantity()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//Updating inventory record
|
||||
public static int updateInventory(int inventoryId, Inventory inventory) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "UPDATE inventory SET prodId = ?, quantity = ? WHERE inventoryId = ?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, inventory.getProdId());
|
||||
stmt.setInt(2, inventory.getQuantity());
|
||||
stmt.setInt(3, inventoryId);
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("inventory",
|
||||
String.valueOf(inventoryId),
|
||||
String.format("Inventory ID %d updated", inventoryId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//Deleting inventory record
|
||||
public static int deleteInventory(int inventoryId) throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "DELETE FROM inventory WHERE inventoryId = ?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, inventoryId);
|
||||
|
||||
int numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("inventory",
|
||||
String.valueOf(inventoryId),
|
||||
String.format("Inventory ID %d deleted", inventoryId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
//DRY: converts DB data into usable Java object
|
||||
private static Inventory mapRow(ResultSet rs) throws SQLException {
|
||||
return new Inventory(
|
||||
rs.getInt("inventoryId"),
|
||||
rs.getInt("prodId"),
|
||||
rs.getString("prodName"),
|
||||
rs.getInt("quantity")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Pet;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class PetDB {
|
||||
public static ObservableList<Pet> getPets() throws SQLException {
|
||||
//Connect to the database
|
||||
ObservableList<Pet> pets = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM pet");
|
||||
|
||||
//While there is still data add pets to the list
|
||||
while(rs.next()){
|
||||
Pet pet = new Pet(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getInt(5),
|
||||
rs.getString(6),
|
||||
rs.getDouble(7)
|
||||
);
|
||||
pets.add(pet);
|
||||
}
|
||||
|
||||
//close connection and return pets
|
||||
conn.close();
|
||||
return pets;
|
||||
}
|
||||
|
||||
public static ObservableList<Pet> getFilteredPets(String filter) throws SQLException {
|
||||
//Connect to the database
|
||||
ObservableList<Pet> pets = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Get SQL query for filtered word
|
||||
String sql = "SELECT * FROM pet " +
|
||||
" WHERE " +
|
||||
"petName LIKE ? OR " +
|
||||
"petSpecies LIKE ? OR " +
|
||||
"petBreed LIKE ? OR " +
|
||||
"petAge LIKE ? OR " +
|
||||
"petStatus LIKE ? OR " +
|
||||
"petPrice LIKE ?";
|
||||
|
||||
String filteredString = "%" + filter + "%";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, filteredString);
|
||||
stmt.setString(2, filteredString);
|
||||
stmt.setString(3, filteredString);
|
||||
stmt.setString(4, filteredString);
|
||||
stmt.setString(5, filteredString);
|
||||
stmt.setString(6, filteredString);
|
||||
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
while(rs.next()){
|
||||
Pet pet = new Pet(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getInt(5),
|
||||
rs.getString(6),
|
||||
rs.getDouble(7)
|
||||
);
|
||||
pets.add(pet);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return pets;
|
||||
}
|
||||
|
||||
public static int insertPet(Pet pet) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO pet (petId, petName, petSpecies, petBreed, petAge, petStatus, petPrice)" +
|
||||
" VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, pet.getPetId());
|
||||
stmt.setString(2, pet.getPetName());
|
||||
stmt.setString(3, pet.getPetSpecies());
|
||||
stmt.setString(4, pet.getPetBreed());
|
||||
stmt.setInt(5, pet.getPetAge());
|
||||
stmt.setString(6, pet.getPetStatus());
|
||||
stmt.setDouble(7, pet.getPetPrice());
|
||||
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("pet",
|
||||
String.valueOf(pet.getPetId()),
|
||||
String.format("Pet '%s' added", pet.getPetName()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
public static int updatePet(int petId, Pet pet) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "UPDATE pet SET " +
|
||||
" petName = ?, " +
|
||||
" petSpecies = ?, " +
|
||||
" petBreed = ?, " +
|
||||
" petAge = ?, " +
|
||||
" petStatus = ?, " +
|
||||
" petPrice = ? " +
|
||||
" WHERE petId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, pet.getPetName());
|
||||
stmt.setString(2, pet.getPetSpecies());
|
||||
stmt.setString(3, pet.getPetBreed());
|
||||
stmt.setInt(4, pet.getPetAge());
|
||||
stmt.setString(5, pet.getPetStatus());
|
||||
stmt.setDouble(6, pet.getPetPrice());
|
||||
stmt.setInt(7, petId);
|
||||
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("pet",
|
||||
String.valueOf(petId),
|
||||
String.format("Pet '%s' updated", pet.getPetName()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
public static int deletePet(int petId) throws SQLException {
|
||||
int numRows = 0;
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "DELETE FROM pet WHERE petId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
stmt.setInt(1, petId);
|
||||
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("pet",
|
||||
String.valueOf(petId),
|
||||
String.format("Pet ID %d deleted", petId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* A class containing all the methods relating to CRUD on Products table
|
||||
*/
|
||||
public class ProductDB {
|
||||
|
||||
/**
|
||||
* gets all the products into an observable list
|
||||
* @return a list of all the products
|
||||
* @throws SQLException if failed to find products in the database
|
||||
*/
|
||||
public static ObservableList<Product> getProducts() throws SQLException{
|
||||
//Connect to the database
|
||||
ObservableList<Product> products = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM product");
|
||||
|
||||
//While there is still data add products to the list
|
||||
while(rs.next()){
|
||||
Product product = new Product(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getDouble(3),
|
||||
rs.getInt(4),
|
||||
rs.getString(5));
|
||||
products.add(product);
|
||||
}
|
||||
|
||||
//close connection and return products
|
||||
conn.close();
|
||||
return products;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets all the ProductDTOs into an observable list for display (displays categoryName instead of categoryId)
|
||||
* @return the list of all the ProductDTOs
|
||||
* @throws SQLException if failed to find products in the database
|
||||
*/
|
||||
public static ObservableList<ProductDTO> getProductDTO() throws SQLException{
|
||||
//Connect to the database
|
||||
ObservableList<ProductDTO> products = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
String sql = "SELECT p.prodId, p.prodName, p.prodPrice, p.categoryId, c.categoryName, p.prodDesc " +
|
||||
"FROM product p " +
|
||||
"LEFT JOIN category c ON p.categoryId = c.categoryId";
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
//While there is still data add products to the list
|
||||
while(rs.next()){
|
||||
ProductDTO product = new ProductDTO(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getDouble(3),
|
||||
rs.getInt(4),
|
||||
rs.getString(5),
|
||||
rs.getString(6));
|
||||
products.add(product);
|
||||
}
|
||||
|
||||
//close connection and return products
|
||||
conn.close();
|
||||
return products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new product to the database
|
||||
* @param product product entity to be inserted
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if insertion failed
|
||||
*/
|
||||
public static int insertProduct(Product product) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO product (prodId, prodName, prodPrice, categoryId, prodDesc)" +
|
||||
"VALUES (?, ?, ?, ?, ?)";
|
||||
|
||||
//These are the values from product to put into the query above
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, product.getProdId());
|
||||
stmt.setString(2, product.getProdName());
|
||||
stmt.setDouble(3, product.getProdPrice());
|
||||
stmt.setInt(4, product.getCategoryId());
|
||||
stmt.setString(5, product.getProdDesc());
|
||||
|
||||
//update the number of rows affected, return and close connection
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("product",
|
||||
String.valueOf(product.getProdId()),
|
||||
String.format("Product '%s' added", product.getProdName()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing product to the database
|
||||
* @param prodId id of product
|
||||
* @param product new product data
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if update failed
|
||||
*/
|
||||
public static int updateProduct(int prodId, Product product) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "UPDATE product SET " +
|
||||
" prodName = ?, " +
|
||||
" prodPrice = ?, " +
|
||||
" categoryId = ?, " +
|
||||
" prodDesc = ? " +
|
||||
" WHERE prodId = ?";
|
||||
|
||||
//update values to query
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, product.getProdName());
|
||||
stmt.setDouble(2, product.getProdPrice());
|
||||
stmt.setInt(3, product.getCategoryId());
|
||||
stmt.setString(4, product.getProdDesc());
|
||||
stmt.setInt(5, prodId);
|
||||
|
||||
//Update rows and close connection
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("product",
|
||||
String.valueOf(prodId),
|
||||
String.format("Product '%s' updated", product.getProdName()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a product from the database
|
||||
* @param prodId product id to be deleted
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if delete failed
|
||||
*/
|
||||
public static int deleteProduct(int prodId) throws SQLException {
|
||||
int numRows = 0;
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "DELETE FROM product WHERE prodId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
stmt.setInt(1, prodId);
|
||||
|
||||
//close connection and update rows affected
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("product",
|
||||
String.valueOf(prodId),
|
||||
String.format("Product ID %d deleted", prodId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of productDTOs that is filtered by a given string
|
||||
* @param filter the word to filter table
|
||||
* @return ObservableList of ProductDTOs with the filtered data
|
||||
* @throws SQLException if getting products failed
|
||||
*/
|
||||
public static ObservableList<ProductDTO> getFilteredProductDTOs(String filter) throws SQLException {
|
||||
//Connect to the database
|
||||
ObservableList<ProductDTO> products = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Get SQL query for filtered word
|
||||
String sql =
|
||||
"SELECT p.prodId, p.prodName, p.prodPrice, p.categoryId, c.categoryName, p.prodDesc" +
|
||||
" FROM product p" +
|
||||
" LEFT JOIN category c ON p.categoryId = c.categoryId" +
|
||||
" WHERE " +
|
||||
"prodName LIKE ? OR " +
|
||||
"prodPrice LIKE ? OR " +
|
||||
"categoryName LIKE ? OR " +
|
||||
"prodDesc LIKE ?";
|
||||
|
||||
//add % wildcard so the query can use LIKE to filter data
|
||||
String filteredString = "%" + filter + "%";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, filteredString);
|
||||
stmt.setString(2, filteredString);
|
||||
stmt.setString(3, filteredString);
|
||||
stmt.setString(4, filteredString);
|
||||
|
||||
//execute query
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
//While there is still data add products to the list
|
||||
while(rs.next()){
|
||||
ProductDTO product = new ProductDTO(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getDouble(3),
|
||||
rs.getInt(4),
|
||||
rs.getString(5),
|
||||
rs.getString(6));
|
||||
products.add(product);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return products;
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.DTOs.ProductDTO;
|
||||
import org.example.petshopdesktop.DTOs.ProductSupplierDTO;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.ProductSupplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class ProductSupplierDB {
|
||||
/**
|
||||
* gets all the productSupplier into an observable list
|
||||
* @return a list of all the productSupplierDTOs
|
||||
* @throws SQLException if failed to find productSupplier in the database
|
||||
*/
|
||||
public static ObservableList<ProductSupplierDTO> getProductSupplierDTO() throws SQLException{
|
||||
//Connect to the database
|
||||
ObservableList<ProductSupplierDTO> productSuppliers = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
String sql = "SELECT ps.supId, ps.prodId, s.supCompany, p.prodName, ps.cost " +
|
||||
"FROM productSupplier ps " +
|
||||
"LEFT JOIN product p " +
|
||||
"ON p.prodId = ps.prodId " +
|
||||
"LEFT JOIN supplier s " +
|
||||
"ON s.supId = ps.supId";
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
//While there is still data add productSupplier to list
|
||||
while(rs.next()){
|
||||
ProductSupplierDTO productSupplier = new ProductSupplierDTO(
|
||||
rs.getInt(1),
|
||||
rs.getInt(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getDouble(5)
|
||||
);
|
||||
productSuppliers.add(productSupplier);
|
||||
}
|
||||
|
||||
//close connection and return products
|
||||
conn.close();
|
||||
return productSuppliers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of ProductSupplierDTOs that is filtered by a given string
|
||||
* @param filter the word to filter table
|
||||
* @return ObservableList of ProductSupplierDTOs with the filtered data
|
||||
* @throws SQLException if getting productSuppliers failed
|
||||
*/
|
||||
public static ObservableList<ProductSupplierDTO> getFilteredProductSupplierDTO (String filter) throws SQLException {
|
||||
//connect to the database
|
||||
ObservableList<ProductSupplierDTO> productSuppliers = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Get SQL query for filter word
|
||||
String sql =
|
||||
"SELECT ps.supId, ps.prodId, s.supCompany, p.prodName, ps.cost " +
|
||||
"FROM product p " +
|
||||
"LEFT JOIN productSupplier ps " +
|
||||
"ON p.prodId = ps.prodId " +
|
||||
"LEFT JOIN supplier s " +
|
||||
"ON s.supId = ps.supId " +
|
||||
"WHERE " +
|
||||
"prodName LIKE ? OR " +
|
||||
"supCompany LIKE ? OR " +
|
||||
"cost LIKE ?";
|
||||
|
||||
//add % wildcard so query can use LIKE to filter data
|
||||
String filteredString = "%" + filter + "%";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, filteredString);
|
||||
stmt.setString(2, filteredString);
|
||||
stmt.setString(3, filteredString);
|
||||
|
||||
//execute query
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
//While there is still data add productSupplier to the list
|
||||
while(rs.next()){
|
||||
ProductSupplierDTO productSupplier = new ProductSupplierDTO(
|
||||
rs.getInt(1),
|
||||
rs.getInt(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getDouble(5)
|
||||
);
|
||||
productSuppliers.add(productSupplier);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return productSuppliers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new productSupplier to the database
|
||||
* @param productSupplier productSupplier entity to be inserted
|
||||
* @return number of rows affected
|
||||
* @throws SQLException if insert failed
|
||||
*/
|
||||
public static int insertProductSupplier(ProductSupplier productSupplier) throws SQLException{
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO productSupplier (prodId, supId, cost) " +
|
||||
"VALUES (?, ?, ?)";
|
||||
|
||||
//These are the values from productSupplier to put into query above
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, productSupplier.getProdId());
|
||||
stmt.setInt(2, productSupplier.getSupId());
|
||||
stmt.setDouble(3, productSupplier.getCost());
|
||||
|
||||
//update number of rows affected, return and close connection
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("productSupplier",
|
||||
String.format("ProdID:%d-SupID:%d", productSupplier.getProdId(), productSupplier.getSupId()),
|
||||
String.format("Product-Supplier relation added for Product ID %d, Supplier ID %d", productSupplier.getProdId(), productSupplier.getSupId()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a productSupplier by deleting old productSupplier and inserting new one
|
||||
* @param oldProdId old product id (used to change primary compound key)
|
||||
* @param oldSupId old supplier id (used to change primary compound key)
|
||||
* @param productSupplier productSupplier entity with new info to update (including new primary compound key)
|
||||
* @return number of rows affected in database
|
||||
* @throws SQLException if update failed
|
||||
*/
|
||||
public static int updateProductSupplier(int oldSupId, int oldProdId, ProductSupplier productSupplier) throws SQLException{
|
||||
int numRows = 0;
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Make transaction so update can be rolled back if insert failed
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
try{
|
||||
//Delete old data first
|
||||
String sql = "DELETE FROM productSupplier WHERE supId = ? AND prodId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, oldSupId);
|
||||
stmt.setInt(2, oldProdId);
|
||||
numRows = stmt.executeUpdate();
|
||||
|
||||
//Then change the data by inserting a new relation with given keys (only if delete worked)
|
||||
if(numRows > 0){
|
||||
sql = "INSERT INTO productSupplier (prodId, supId, cost) " +
|
||||
"VALUES (?, ?, ?)";
|
||||
stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, productSupplier.getProdId());
|
||||
stmt.setInt(2, productSupplier.getSupId());
|
||||
stmt.setDouble(3, productSupplier.getCost());
|
||||
numRows = stmt.executeUpdate();
|
||||
}
|
||||
//Commit changes if both delete and insert worked
|
||||
conn.commit();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("productSupplier",
|
||||
String.format("ProdID:%d-SupID:%d", productSupplier.getProdId(), productSupplier.getSupId()),
|
||||
String.format("Product-Supplier relation updated from ProdID:%d-SupID:%d to ProdID:%d-SupID:%d", oldProdId, oldSupId, productSupplier.getProdId(), productSupplier.getSupId()));
|
||||
}
|
||||
}
|
||||
catch(SQLException e){
|
||||
//Rollback CRUD failed
|
||||
conn.rollback();
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
//Set auto commit back to true before closing connection
|
||||
conn.setAutoCommit(true);
|
||||
conn.close();
|
||||
}
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a productSupplier from the database
|
||||
* @param prodId id of the product
|
||||
* @param supId id of the supplier
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if delete failed
|
||||
*/
|
||||
public static int deleteProductSupplier(int supId, int prodId) throws SQLException{
|
||||
int numRows = 0;
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "DELETE FROM productSupplier WHERE supId = ? AND prodId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, supId);
|
||||
stmt.setInt(2, prodId);
|
||||
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("productSupplier",
|
||||
String.format("ProdID:%d-SupID:%d", prodId, supId),
|
||||
String.format("Product-Supplier relation deleted for Product ID %d, Supplier ID %d", prodId, supId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.DTOs.PurchaseOrderDTO;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class PurchaseOrderDB {
|
||||
|
||||
public static ObservableList<PurchaseOrderDTO> getPurchaseOrders()
|
||||
throws SQLException {
|
||||
|
||||
ObservableList<PurchaseOrderDTO> list =
|
||||
FXCollections.observableArrayList();
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT po.purchaseOrderId,
|
||||
s.supCompany,
|
||||
po.orderDate,
|
||||
po.status
|
||||
FROM purchaseOrder po
|
||||
JOIN supplier s ON po.supId = s.supId
|
||||
ORDER BY po.purchaseOrderId
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
|
||||
list.add(new PurchaseOrderDTO(
|
||||
rs.getInt("purchaseOrderId"),
|
||||
rs.getString("supCompany"),
|
||||
rs.getString("orderDate"),
|
||||
rs.getString("status")
|
||||
));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,572 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
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;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class SaleDB {
|
||||
|
||||
/**
|
||||
* Get all sale items with details
|
||||
* @return ObservableList of SaleDTOs
|
||||
* @throws SQLException if database operation fails
|
||||
*/
|
||||
public static ObservableList<SaleDTO> getSales() throws SQLException {
|
||||
ObservableList<SaleDTO> sales = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT
|
||||
s.saleId,
|
||||
DATE_FORMAT(s.saleDate, '%Y-%m-%d %H:%i') as saleDate,
|
||||
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
|
||||
p.prodName,
|
||||
si.quantity,
|
||||
si.unitPrice,
|
||||
(si.quantity * si.unitPrice) as lineTotal,
|
||||
s.paymentMethod
|
||||
FROM sale s
|
||||
JOIN saleItem si ON s.saleId = si.saleId
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
JOIN employee e ON s.employeeId = e.employeeId
|
||||
ORDER BY s.saleDate DESC, s.saleId, si.saleItemId
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
sales.add(new SaleDTO(
|
||||
rs.getInt("saleId"),
|
||||
rs.getString("saleDate"),
|
||||
rs.getString("employeeName"),
|
||||
rs.getString("prodName"),
|
||||
rs.getInt("quantity"),
|
||||
rs.getDouble("unitPrice"),
|
||||
rs.getDouble("lineTotal"),
|
||||
rs.getString("paymentMethod")
|
||||
));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return sales;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtered sale items
|
||||
* @param filter search term
|
||||
* @return ObservableList of SaleDTOs matching the filter
|
||||
* @throws SQLException if database operation fails
|
||||
*/
|
||||
public static ObservableList<SaleDTO> getFilteredSales(String filter) throws SQLException {
|
||||
ObservableList<SaleDTO> sales = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT
|
||||
s.saleId,
|
||||
DATE_FORMAT(s.saleDate, '%Y-%m-%d %H:%i') as saleDate,
|
||||
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
|
||||
p.prodName,
|
||||
si.quantity,
|
||||
si.unitPrice,
|
||||
(si.quantity * si.unitPrice) as lineTotal,
|
||||
s.paymentMethod
|
||||
FROM sale s
|
||||
JOIN saleItem si ON s.saleId = si.saleId
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
JOIN employee e ON s.employeeId = e.employeeId
|
||||
WHERE s.saleId LIKE ?
|
||||
OR p.prodName LIKE ?
|
||||
OR CONCAT(e.firstName, ' ', e.lastName) LIKE ?
|
||||
OR s.paymentMethod LIKE ?
|
||||
ORDER BY s.saleDate DESC, s.saleId, si.saleItemId
|
||||
""";
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||||
String searchPattern = "%" + filter + "%";
|
||||
pstmt.setString(1, searchPattern);
|
||||
pstmt.setString(2, searchPattern);
|
||||
pstmt.setString(3, searchPattern);
|
||||
pstmt.setString(4, searchPattern);
|
||||
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
sales.add(new SaleDTO(
|
||||
rs.getInt("saleId"),
|
||||
rs.getString("saleDate"),
|
||||
rs.getString("employeeName"),
|
||||
rs.getString("prodName"),
|
||||
rs.getInt("quantity"),
|
||||
rs.getDouble("unitPrice"),
|
||||
rs.getDouble("lineTotal"),
|
||||
rs.getString("paymentMethod")
|
||||
));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return sales;
|
||||
}
|
||||
|
||||
public static ObservableList<SaleLineItem> getSaleLineItems() throws SQLException {
|
||||
ObservableList<SaleLineItem> saleItems = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT
|
||||
s.saleId,
|
||||
DATE_FORMAT(s.saleDate, '%Y-%m-%d %H:%i') as saleDate,
|
||||
CONCAT(e.firstName, ' ', e.lastName) as employeeName,
|
||||
p.prodName,
|
||||
si.quantity,
|
||||
si.unitPrice,
|
||||
(si.quantity * si.unitPrice) as total,
|
||||
s.paymentMethod,
|
||||
s.isRefund
|
||||
FROM sale s
|
||||
JOIN saleItem si ON s.saleId = si.saleId
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
JOIN employee e ON s.employeeId = e.employeeId
|
||||
ORDER BY s.saleDate DESC, s.saleId, si.saleItemId
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
saleItems.add(new SaleLineItem(
|
||||
rs.getInt("saleId"),
|
||||
rs.getString("saleDate"),
|
||||
rs.getString("employeeName"),
|
||||
rs.getString("prodName"),
|
||||
rs.getInt("quantity"),
|
||||
rs.getDouble("unitPrice"),
|
||||
rs.getDouble("total"),
|
||||
rs.getString("paymentMethod"),
|
||||
rs.getBoolean("isRefund")
|
||||
));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return saleItems;
|
||||
}
|
||||
|
||||
public static int createSale(int employeeId, String paymentMethod, ObservableList<SaleCartItem> cartItems) throws SQLException {
|
||||
if (cartItems.isEmpty()) {
|
||||
throw new SQLException("Cannot create sale with empty cart");
|
||||
}
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
try {
|
||||
double totalAmount = cartItems.stream().mapToDouble(SaleCartItem::getTotal).sum();
|
||||
|
||||
String insertSale = """
|
||||
INSERT INTO sale (saleDate, totalAmount, paymentMethod, employeeId, storeId)
|
||||
VALUES (NOW(), ?, ?, ?, 1)
|
||||
""";
|
||||
|
||||
PreparedStatement saleStmt = conn.prepareStatement(insertSale, Statement.RETURN_GENERATED_KEYS);
|
||||
saleStmt.setDouble(1, totalAmount);
|
||||
saleStmt.setString(2, paymentMethod);
|
||||
saleStmt.setInt(3, employeeId);
|
||||
saleStmt.executeUpdate();
|
||||
|
||||
ResultSet rs = saleStmt.getGeneratedKeys();
|
||||
if (!rs.next()) {
|
||||
throw new SQLException("Failed to get generated sale ID");
|
||||
}
|
||||
int saleId = 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 : cartItems) {
|
||||
itemStmt.setInt(1, saleId);
|
||||
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("Sale ID: %d", saleId),
|
||||
String.format("Created sale with %d items, total: $%.2f", cartItems.size(), totalAmount));
|
||||
|
||||
return saleId;
|
||||
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
ActivityLogger.getInstance().logException("SaleDB.createSale", e, "Creating sale");
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(true);
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static ObservableList<DailySalesData> getDailySalesRevenue() throws SQLException {
|
||||
ObservableList<DailySalesData> dailySales = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT DATE(s.saleDate) as saleDate, SUM(s.totalAmount) as revenue
|
||||
FROM sale s
|
||||
GROUP BY DATE(s.saleDate)
|
||||
ORDER BY saleDate ASC
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
LocalDate date = rs.getDate("saleDate").toLocalDate();
|
||||
double revenue = rs.getDouble("revenue");
|
||||
dailySales.add(new DailySalesData(date, revenue));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return dailySales;
|
||||
}
|
||||
|
||||
public static ObservableList<ProductSalesData> getTopProductsByRevenue(int limit) throws SQLException {
|
||||
ObservableList<ProductSalesData> products = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT p.prodName, SUM(si.quantity * si.unitPrice) as totalRevenue
|
||||
FROM saleItem si
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
GROUP BY p.prodId, p.prodName
|
||||
ORDER BY totalRevenue DESC
|
||||
LIMIT ?
|
||||
""";
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||||
pstmt.setInt(1, limit);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
String productName = rs.getString("prodName");
|
||||
double totalRevenue = rs.getDouble("totalRevenue");
|
||||
products.add(new ProductSalesData(productName, 0, totalRevenue));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return products;
|
||||
}
|
||||
|
||||
public static ObservableList<ProductSalesData> getTopProductsByQuantity(int limit) throws SQLException {
|
||||
ObservableList<ProductSalesData> products = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT p.prodName, SUM(si.quantity) as totalQuantity,
|
||||
SUM(si.quantity * si.unitPrice) as totalRevenue
|
||||
FROM saleItem si
|
||||
JOIN product p ON si.prodId = p.prodId
|
||||
GROUP BY p.prodId, p.prodName
|
||||
ORDER BY totalQuantity DESC
|
||||
LIMIT ?
|
||||
""";
|
||||
|
||||
PreparedStatement pstmt = conn.prepareStatement(sql);
|
||||
pstmt.setInt(1, limit);
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
String productName = rs.getString("prodName");
|
||||
int totalQuantity = rs.getInt("totalQuantity");
|
||||
double totalRevenue = rs.getDouble("totalRevenue");
|
||||
products.add(new ProductSalesData(productName, totalQuantity, totalRevenue));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return products;
|
||||
}
|
||||
|
||||
public static ObservableList<PaymentMethodData> getPaymentMethodDistribution() throws SQLException {
|
||||
ObservableList<PaymentMethodData> paymentMethods = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT s.paymentMethod, COUNT(*) as transactionCount,
|
||||
SUM(s.totalAmount) as totalRevenue
|
||||
FROM sale s
|
||||
GROUP BY s.paymentMethod
|
||||
ORDER BY totalRevenue DESC
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
String paymentMethod = rs.getString("paymentMethod");
|
||||
int transactionCount = rs.getInt("transactionCount");
|
||||
double totalRevenue = rs.getDouble("totalRevenue");
|
||||
paymentMethods.add(new PaymentMethodData(paymentMethod, transactionCount, totalRevenue));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return paymentMethods;
|
||||
}
|
||||
|
||||
public static ObservableList<EmployeeSalesData> getEmployeeSalesPerformance() throws SQLException {
|
||||
ObservableList<EmployeeSalesData> employees = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT CONCAT(e.firstName, ' ', e.lastName) as employeeName,
|
||||
COUNT(DISTINCT s.saleId) as transactionCount,
|
||||
SUM(s.totalAmount) as totalRevenue,
|
||||
COALESCE(SUM(si.quantity), 0) as totalItemsSold
|
||||
FROM sale s
|
||||
JOIN employee e ON s.employeeId = e.employeeId
|
||||
LEFT JOIN saleItem si ON s.saleId = si.saleId
|
||||
GROUP BY e.employeeId, e.firstName, e.lastName
|
||||
ORDER BY totalRevenue DESC
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
String employeeName = rs.getString("employeeName");
|
||||
int transactionCount = rs.getInt("transactionCount");
|
||||
double totalRevenue = rs.getDouble("totalRevenue");
|
||||
int totalItemsSold = rs.getInt("totalItemsSold");
|
||||
employees.add(new EmployeeSalesData(employeeName, transactionCount, totalRevenue, totalItemsSold));
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return employees;
|
||||
}
|
||||
|
||||
public static SalesSummary getSalesSummary() throws SQLException {
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = """
|
||||
SELECT COUNT(DISTINCT s.saleId) as totalTransactions,
|
||||
COALESCE(SUM(s.totalAmount), 0) as totalRevenue,
|
||||
COALESCE(AVG(s.totalAmount), 0) as avgTransactionValue,
|
||||
COALESCE(SUM(si.quantity), 0) as totalItemsSold
|
||||
FROM sale s
|
||||
LEFT JOIN saleItem si ON s.saleId = si.saleId
|
||||
""";
|
||||
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
SalesSummary summary = null;
|
||||
if (rs.next()) {
|
||||
int totalTransactions = rs.getInt("totalTransactions");
|
||||
double totalRevenue = rs.getDouble("totalRevenue");
|
||||
double avgTransactionValue = rs.getDouble("avgTransactionValue");
|
||||
int totalItemsSold = rs.getInt("totalItemsSold");
|
||||
summary = new SalesSummary(totalTransactions, totalRevenue, avgTransactionValue, totalItemsSold);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Service;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
public class ServiceDB {
|
||||
|
||||
//
|
||||
// GET ALL SERVICES
|
||||
//
|
||||
public static ObservableList<Service> getServices() throws SQLException {
|
||||
|
||||
ObservableList<Service> list = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "SELECT * FROM service";
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
|
||||
while (rs.next()) {
|
||||
|
||||
Service service = new Service(
|
||||
rs.getInt("serviceId"),
|
||||
rs.getString("serviceName"),
|
||||
rs.getString("serviceDesc"),
|
||||
rs.getInt("serviceDuration"),
|
||||
rs.getDouble("servicePrice")
|
||||
);
|
||||
|
||||
list.add(service);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
//
|
||||
// INSERT SERVICE
|
||||
//
|
||||
public static int insertService(Service service) throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql =
|
||||
"INSERT INTO service (serviceName, serviceDesc, serviceDuration, servicePrice) " +
|
||||
"VALUES (?, ?, ?, ?)";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
stmt.setString(1, service.getServiceName());
|
||||
stmt.setString(2, service.getServiceDesc());
|
||||
stmt.setInt(3, service.getServiceDuration());
|
||||
stmt.setDouble(4, service.getServicePrice());
|
||||
|
||||
int rows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (rows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("service",
|
||||
"N/A",
|
||||
String.format("Service '%s' added", service.getServiceName()));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
//
|
||||
// UPDATE SERVICE
|
||||
//
|
||||
public static int updateService(int id, Service service) throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql =
|
||||
"UPDATE service SET " +
|
||||
"serviceName=?, serviceDesc=?, serviceDuration=?, servicePrice=? " +
|
||||
"WHERE serviceId=?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
|
||||
stmt.setString(1, service.getServiceName());
|
||||
stmt.setString(2, service.getServiceDesc());
|
||||
stmt.setInt(3, service.getServiceDuration());
|
||||
stmt.setDouble(4, service.getServicePrice());
|
||||
stmt.setInt(5, id);
|
||||
|
||||
int rows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (rows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("service",
|
||||
String.valueOf(id),
|
||||
String.format("Service '%s' updated", service.getServiceName()));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
//
|
||||
// DELETE SERVICE
|
||||
//
|
||||
public static int deleteService(int id) throws SQLException {
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "DELETE FROM service WHERE serviceId=?";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, id);
|
||||
|
||||
int rows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (rows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("service",
|
||||
String.valueOf(id),
|
||||
String.format("Service ID %d deleted", id));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.example.petshopdesktop.models.Product;
|
||||
import org.example.petshopdesktop.models.Supplier;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* A class containing all the methods relating to CRUD on Suppliers table
|
||||
*/
|
||||
public class SupplierDB {
|
||||
|
||||
/**
|
||||
* gets all the suppliers into an observable list
|
||||
* @return a list of all the suppliers
|
||||
* @throws SQLException if failed to find suppliers in the database
|
||||
*/
|
||||
public static ObservableList<Supplier> getSuppliers() throws SQLException {
|
||||
//Connect to the database
|
||||
ObservableList<Supplier> suppliers = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Execute Query
|
||||
Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM supplier");
|
||||
|
||||
//While there is still data add suppliers to the list
|
||||
while(rs.next()){
|
||||
Supplier supplier = new Supplier(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getString(5),
|
||||
rs.getString(6));
|
||||
suppliers.add(supplier);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return suppliers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new supplier to the database
|
||||
* @param supplier supplier entity to be inserted
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if insertion failed
|
||||
*/
|
||||
public static int insertSupplier(Supplier supplier) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "INSERT INTO supplier (supId, supCompany, supContactFirstName, supContactLastName, supEmail, supPhone)" +
|
||||
"VALUES (?, ?, ?, ?, ?, ?)";
|
||||
|
||||
//These are the values from supplier to put into the query above
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, supplier.getSupId());
|
||||
stmt.setString(2, supplier.getSupCompany());
|
||||
stmt.setString(3, supplier.getSupContactFirstName());
|
||||
stmt.setString(4, supplier.getSupContactLastName());
|
||||
stmt.setString(5, supplier.getSupEmail());
|
||||
stmt.setString(6, supplier.getSupPhone());
|
||||
|
||||
//update the number of rows affected, return and close connection
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("supplier",
|
||||
String.valueOf(supplier.getSupId()),
|
||||
String.format("Supplier '%s' added", supplier.getSupCompany()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing supplier to the database
|
||||
* @param supId id of supplier
|
||||
* @param supplier new supplier data
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if update failed
|
||||
*/
|
||||
public static int updateSupplier(int supId, Supplier supplier) throws SQLException {
|
||||
int numRows = 0;
|
||||
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
String sql = "UPDATE supplier SET " +
|
||||
" supCompany = ?, " +
|
||||
" supContactFirstName = ?, " +
|
||||
" supContactLastName = ?, " +
|
||||
" supEmail = ?, " +
|
||||
" supPhone = ? " +
|
||||
" WHERE supId = ?";
|
||||
|
||||
//updated values to update the supplier with the query above
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, supplier.getSupCompany());
|
||||
stmt.setString(2, supplier.getSupContactFirstName());
|
||||
stmt.setString(3, supplier.getSupContactLastName());
|
||||
stmt.setString(4, supplier.getSupEmail());
|
||||
stmt.setString(5, supplier.getSupPhone());
|
||||
stmt.setInt(6, supId);
|
||||
|
||||
//Update the rows and close connection
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logUpdate("supplier",
|
||||
String.valueOf(supId),
|
||||
String.format("Supplier '%s' updated", supplier.getSupCompany()));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a supplier form the database
|
||||
* @param supId supplier id to be deleted
|
||||
* @return number of rows affected in the database
|
||||
* @throws SQLException if delete failed
|
||||
*/
|
||||
public static int deleteSupplier(int supId) throws SQLException {
|
||||
int numRows = 0;
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
String sql = "DELETE FROM supplier WHERE supId = ?";
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
//The supplier id to be deleted for the query above
|
||||
stmt.setInt(1, supId);
|
||||
|
||||
//close connection and update rows affected
|
||||
numRows = stmt.executeUpdate();
|
||||
conn.close();
|
||||
|
||||
// Log the operation
|
||||
if (numRows > 0) {
|
||||
ActivityLogger.getInstance().logDelete("supplier",
|
||||
String.valueOf(supId),
|
||||
String.format("Supplier ID %d deleted", supId));
|
||||
}
|
||||
|
||||
return numRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of Suppliers that is filtered by a given string
|
||||
* @param filter the word to filter table
|
||||
* @return ObservableList of suppliers with the filtered data
|
||||
* @throws SQLException if getting suppliers failed
|
||||
*/
|
||||
public static ObservableList<Supplier> getFilteredSuppliers(String filter) throws SQLException {
|
||||
//Connect to the database
|
||||
ObservableList<Supplier> suppliers = FXCollections.observableArrayList();
|
||||
Connection conn = ConnectionDB.getConnection();
|
||||
|
||||
//Get SQL query for filtered word
|
||||
String sql =
|
||||
"SELECT * FROM supplier" +
|
||||
" WHERE " +
|
||||
"supCompany LIKE ? OR " +
|
||||
"supContactFirstName LIKE ? OR " +
|
||||
"supContactLastName LIKE ? OR " +
|
||||
"supEmail LIKE ? OR " +
|
||||
"supPhone LIKE ?";
|
||||
|
||||
//add % wildcard so the query can use LIKE to filter data
|
||||
String filteredString = "%" + filter + "%";
|
||||
|
||||
PreparedStatement stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, filteredString);
|
||||
stmt.setString(2, filteredString);
|
||||
stmt.setString(3, filteredString);
|
||||
stmt.setString(4, filteredString);
|
||||
stmt.setString(5, filteredString);
|
||||
|
||||
//execute query
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
//While there is still data add suppliers to the list
|
||||
while(rs.next()){
|
||||
Supplier supplier = new Supplier(
|
||||
rs.getInt(1),
|
||||
rs.getString(2),
|
||||
rs.getString(3),
|
||||
rs.getString(4),
|
||||
rs.getString(5),
|
||||
rs.getString(6));
|
||||
suppliers.add(supplier);
|
||||
}
|
||||
|
||||
conn.close();
|
||||
return suppliers;
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package org.example.petshopdesktop.database;
|
||||
|
||||
import org.example.petshopdesktop.auth.Role;
|
||||
import org.example.petshopdesktop.models.StaffAccount;
|
||||
import org.example.petshopdesktop.models.User;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UserDB {
|
||||
|
||||
public static User authenticate(String username, String password) throws SQLException {
|
||||
String sql = "SELECT u.user_id, u.employee_id, u.username, u.role, e.firstName, e.lastName "
|
||||
+ "FROM users u "
|
||||
+ "JOIN employee e ON u.employee_id = e.employeeId "
|
||||
+ "WHERE u.username = ? AND u.password_hash = SHA2(?, 256) AND e.isActive = TRUE";
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
|
||||
ps.setString(1, username);
|
||||
ps.setString(2, password);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
int userId = rs.getInt("user_id");
|
||||
int employeeId = rs.getInt("employee_id");
|
||||
String uname = rs.getString("username");
|
||||
Role role = Role.valueOf(rs.getString("role").toUpperCase());
|
||||
String firstName = rs.getString("firstName");
|
||||
String lastName = rs.getString("lastName");
|
||||
|
||||
return new User(userId, employeeId, uname, firstName, lastName, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void initializeTable() throws SQLException {
|
||||
String createTable = """
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
employee_id INT NOT NULL,
|
||||
username VARCHAR(100) NOT NULL UNIQUE,
|
||||
password_hash CHAR(64) NOT NULL,
|
||||
role ENUM('ADMIN','STAFF') NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY (employee_id) REFERENCES employee(employeeId)
|
||||
)
|
||||
""";
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
Statement st = conn.createStatement()) {
|
||||
st.executeUpdate(createTable);
|
||||
}
|
||||
|
||||
ensureCompatibleSchema();
|
||||
|
||||
int adminEmployeeId = EmployeeDB.ensureDefaultEmployee("John", "Doe", "john@petshop.com", "111-222-3333", "Manager", true);
|
||||
int staffEmployeeId = EmployeeDB.ensureDefaultEmployee("Sara", "Smith", "sara@petshop.com", "444-555-6666", "Staff", true);
|
||||
|
||||
ensureDefaultUser(adminEmployeeId, "admin", "admin123", Role.ADMIN);
|
||||
ensureDefaultUser(staffEmployeeId, "staff", "staff123", Role.STAFF);
|
||||
}
|
||||
|
||||
public static int createStaffAccount(String firstName, String lastName, String email, String phone, String username, String password) throws SQLException {
|
||||
if (username == null || username.isBlank()) {
|
||||
throw new SQLException("Username is required.");
|
||||
}
|
||||
if (password == null || password.isBlank()) {
|
||||
throw new SQLException("Password is required.");
|
||||
}
|
||||
|
||||
int storeId = EmployeeDB.getDefaultStoreId();
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection()) {
|
||||
conn.setAutoCommit(false);
|
||||
try {
|
||||
int employeeId = EmployeeDB.createEmployee(conn, firstName, lastName, email, phone, "Staff", true);
|
||||
EmployeeDB.assignEmployeeToStore(conn, employeeId, storeId);
|
||||
|
||||
String insertUser = "INSERT INTO users (employee_id, username, password_hash, role) VALUES (?, ?, SHA2(?, 256), 'STAFF')";
|
||||
try (PreparedStatement ps = conn.prepareStatement(insertUser, Statement.RETURN_GENERATED_KEYS)) {
|
||||
ps.setInt(1, employeeId);
|
||||
ps.setString(2, username);
|
||||
ps.setString(3, password);
|
||||
ps.executeUpdate();
|
||||
|
||||
try (ResultSet keys = ps.getGeneratedKeys()) {
|
||||
if (keys.next()) {
|
||||
int userId = keys.getInt(1);
|
||||
conn.commit();
|
||||
ActivityLogger.getInstance().logInsert("users", String.valueOf(userId), "Created staff account: " + username);
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conn.rollback();
|
||||
throw new SQLException("Could not create staff account.");
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
conn.rollback();
|
||||
throw e;
|
||||
} finally {
|
||||
conn.setAutoCommit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<StaffAccount> getStaffAccounts() throws SQLException {
|
||||
String sql = "SELECT u.user_id, u.username, u.created_at, e.employeeId, e.firstName, e.lastName, e.email, e.phone, e.isActive "
|
||||
+ "FROM users u "
|
||||
+ "JOIN employee e ON u.employee_id = e.employeeId "
|
||||
+ "WHERE u.role = 'STAFF' "
|
||||
+ "ORDER BY u.created_at DESC";
|
||||
|
||||
List<StaffAccount> accounts = new ArrayList<>();
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
|
||||
while (rs.next()) {
|
||||
accounts.add(new StaffAccount(
|
||||
rs.getInt("user_id"),
|
||||
rs.getInt("employeeId"),
|
||||
rs.getString("username"),
|
||||
rs.getString("firstName"),
|
||||
rs.getString("lastName"),
|
||||
rs.getString("email"),
|
||||
rs.getString("phone"),
|
||||
rs.getBoolean("isActive"),
|
||||
rs.getTimestamp("created_at")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
|
||||
private static void ensureCompatibleSchema() throws SQLException {
|
||||
try (Connection conn = ConnectionDB.getConnection();
|
||||
Statement st = conn.createStatement()) {
|
||||
|
||||
try {
|
||||
st.executeUpdate("ALTER TABLE users ADD COLUMN employee_id INT NULL");
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
st.executeUpdate("ALTER TABLE users ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL");
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
st.executeUpdate("ALTER TABLE users ADD CONSTRAINT fk_users_employee FOREIGN KEY (employee_id) REFERENCES employee(employeeId)");
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureDefaultUser(int employeeId, String username, String password, Role role) throws SQLException {
|
||||
String insert = "INSERT IGNORE INTO users (employee_id, username, password_hash, role) VALUES (?, ?, SHA2(?, 256), ?)";
|
||||
String updateIfMissingEmployeeId = "UPDATE users SET employee_id = ? WHERE username = ? AND (employee_id IS NULL OR employee_id = 0)";
|
||||
|
||||
try (Connection conn = ConnectionDB.getConnection()) {
|
||||
int rows;
|
||||
try (PreparedStatement ps = conn.prepareStatement(insert)) {
|
||||
ps.setInt(1, employeeId);
|
||||
ps.setString(2, username);
|
||||
ps.setString(3, password);
|
||||
ps.setString(4, role.name());
|
||||
rows = ps.executeUpdate();
|
||||
}
|
||||
|
||||
try (PreparedStatement ps = conn.prepareStatement(updateIfMissingEmployeeId)) {
|
||||
ps.setInt(1, employeeId);
|
||||
ps.setString(2, username);
|
||||
ps.executeUpdate();
|
||||
}
|
||||
|
||||
if (rows > 0) {
|
||||
ActivityLogger.getInstance().logInsert("users", username, "Default " + role.name().toLowerCase() + " user created");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,17 @@ public class Adoption {
|
||||
private SimpleIntegerProperty adoptionId;
|
||||
private SimpleIntegerProperty petId;
|
||||
private SimpleIntegerProperty customerId;
|
||||
private SimpleStringProperty petName;
|
||||
private SimpleStringProperty customerName;
|
||||
private SimpleStringProperty adoptionDate;
|
||||
private SimpleDoubleProperty adoptionFee;
|
||||
private SimpleStringProperty adoptionStatus;
|
||||
|
||||
//Constructor
|
||||
public Adoption(int adoptionId, int petId, int customerId, String customerName, String adoptionDate, double adoptionFee, String adoptionStatus) {
|
||||
public Adoption(int adoptionId, int petId, int customerId, String petName, String customerName, String adoptionDate, double adoptionFee, String adoptionStatus) {
|
||||
this.adoptionId = new SimpleIntegerProperty(adoptionId);
|
||||
this.petId = new SimpleIntegerProperty(petId);
|
||||
this.customerId = new SimpleIntegerProperty(customerId);
|
||||
this.petName = new SimpleStringProperty(petName);
|
||||
this.customerName = new SimpleStringProperty(customerName);
|
||||
this.adoptionDate = new SimpleStringProperty(adoptionDate);
|
||||
this.adoptionFee = new SimpleDoubleProperty(adoptionFee);
|
||||
@@ -42,6 +43,12 @@ public class Adoption {
|
||||
|
||||
public SimpleIntegerProperty customerIdProperty() { return customerId; }
|
||||
|
||||
public String getPetName() { return petName.get(); }
|
||||
|
||||
public void setPetName(String petName) { this.petName.set(petName); }
|
||||
|
||||
public SimpleStringProperty petNameProperty() { return petName; }
|
||||
|
||||
public String getCustomerName() { return customerName.get(); }
|
||||
|
||||
public void setCustomerName(String customerName) { this.customerName.set(customerName); }
|
||||
|
||||
@@ -7,14 +7,21 @@ public class Inventory {
|
||||
private SimpleIntegerProperty inventoryId;
|
||||
private SimpleIntegerProperty prodId;
|
||||
private SimpleStringProperty prodName;
|
||||
private SimpleStringProperty categoryName;
|
||||
private SimpleIntegerProperty storeId;
|
||||
private SimpleStringProperty storeName;
|
||||
private SimpleIntegerProperty quantity;
|
||||
private SimpleIntegerProperty reorderLevel;
|
||||
|
||||
//Constructor
|
||||
public Inventory(int inventoryId, int prodId, String prodName, int quantity) {
|
||||
public Inventory(int inventoryId, int prodId, String prodName, String categoryName, int storeId, String storeName, int quantity, int reorderLevel) {
|
||||
this.inventoryId = new SimpleIntegerProperty(inventoryId);
|
||||
this.prodId = new SimpleIntegerProperty(prodId);
|
||||
this.prodName = new SimpleStringProperty(prodName);
|
||||
this.categoryName = new SimpleStringProperty(categoryName);
|
||||
this.storeId = new SimpleIntegerProperty(storeId);
|
||||
this.storeName = new SimpleStringProperty(storeName);
|
||||
this.quantity = new SimpleIntegerProperty(quantity);
|
||||
this.reorderLevel = new SimpleIntegerProperty(reorderLevel);
|
||||
}
|
||||
|
||||
public int getInventoryId() { return inventoryId.get(); }
|
||||
@@ -35,9 +42,33 @@ public class Inventory {
|
||||
|
||||
public SimpleStringProperty prodNameProperty() { return prodName; }
|
||||
|
||||
public String getCategoryName() { return categoryName.get(); }
|
||||
|
||||
public void setCategoryName(String categoryName) { this.categoryName.set(categoryName); }
|
||||
|
||||
public SimpleStringProperty categoryNameProperty() { return categoryName; }
|
||||
|
||||
public int getStoreId() { return storeId.get(); }
|
||||
|
||||
public void setStoreId(int storeId) { this.storeId.set(storeId); }
|
||||
|
||||
public SimpleIntegerProperty storeIdProperty() { return storeId; }
|
||||
|
||||
public String getStoreName() { return storeName.get(); }
|
||||
|
||||
public void setStoreName(String storeName) { this.storeName.set(storeName); }
|
||||
|
||||
public SimpleStringProperty storeNameProperty() { return storeName; }
|
||||
|
||||
public int getQuantity() { return quantity.get(); }
|
||||
|
||||
public void setQuantity(int quantity) { this.quantity.set(quantity); }
|
||||
|
||||
public SimpleIntegerProperty quantityProperty() { return quantity; }
|
||||
|
||||
public int getReorderLevel() { return reorderLevel.get(); }
|
||||
|
||||
public void setReorderLevel(int reorderLevel) { this.reorderLevel.set(reorderLevel); }
|
||||
|
||||
public SimpleIntegerProperty reorderLevelProperty() { return reorderLevel; }
|
||||
}
|
||||
|
||||
@@ -1,35 +1,52 @@
|
||||
package org.example.petshopdesktop.models;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class PurchaseOrder {
|
||||
|
||||
private int purchaseOrderId;
|
||||
private int supId;
|
||||
private String orderDate;
|
||||
private long purchaseOrderId;
|
||||
private String supplierName;
|
||||
private LocalDate orderDate;
|
||||
private LocalDate expectedDeliveryDate;
|
||||
private String status;
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
public PurchaseOrder(int purchaseOrderId,
|
||||
int supId,
|
||||
String orderDate,
|
||||
String status) {
|
||||
public PurchaseOrder(long purchaseOrderId,
|
||||
String supplierName,
|
||||
LocalDate orderDate,
|
||||
LocalDate expectedDeliveryDate,
|
||||
String status,
|
||||
BigDecimal totalAmount) {
|
||||
this.purchaseOrderId = purchaseOrderId;
|
||||
this.supId = supId;
|
||||
this.supplierName = supplierName;
|
||||
this.orderDate = orderDate;
|
||||
this.expectedDeliveryDate = expectedDeliveryDate;
|
||||
this.status = status;
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public int getPurchaseOrderId() {
|
||||
public long getPurchaseOrderId() {
|
||||
return purchaseOrderId;
|
||||
}
|
||||
|
||||
public int getSupId() {
|
||||
return supId;
|
||||
public String getSupplierName() {
|
||||
return supplierName;
|
||||
}
|
||||
|
||||
public String getOrderDate() {
|
||||
public LocalDate getOrderDate() {
|
||||
return orderDate;
|
||||
}
|
||||
|
||||
public LocalDate getExpectedDeliveryDate() {
|
||||
return expectedDeliveryDate;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package org.example.petshopdesktop.models;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
public class StaffAccount {
|
||||
private final int userId;
|
||||
private final int employeeId;
|
||||
private final long userId;
|
||||
private final long employeeId;
|
||||
private final String username;
|
||||
private final String firstName;
|
||||
private final String lastName;
|
||||
@@ -13,7 +13,7 @@ public class StaffAccount {
|
||||
private final boolean active;
|
||||
private final Timestamp createdAt;
|
||||
|
||||
public StaffAccount(int userId, int employeeId, String username, String firstName, String lastName, String email, String phone, boolean active, Timestamp createdAt) {
|
||||
public StaffAccount(long userId, long employeeId, String username, String firstName, String lastName, String email, String phone, boolean active, Timestamp createdAt) {
|
||||
this.userId = userId;
|
||||
this.employeeId = employeeId;
|
||||
this.username = username;
|
||||
@@ -25,11 +25,11 @@ public class StaffAccount {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
public long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public int getEmployeeId() {
|
||||
public long getEmployeeId() {
|
||||
return employeeId;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user