diff --git a/.idea/runConfigurations/BSK_Client.xml b/.idea/runConfigurations/BSK_Client.xml
new file mode 100644
index 0000000..70202fb
--- /dev/null
+++ b/.idea/runConfigurations/BSK_Client.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/BSK_Server.xml b/.idea/runConfigurations/BSK_Server.xml
new file mode 100644
index 0000000..dcad2e6
--- /dev/null
+++ b/.idea/runConfigurations/BSK_Server.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..55e4357
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,72 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "Client",
+ "request": "launch",
+ "mainClass": "BsK.client.Client",
+ "projectName": "ldS_BsK"
+ },
+ {
+ "type": "java",
+ "name": "Server",
+ "request": "launch",
+ "mainClass": "BsK.server.Server",
+ "projectName": "ldS_BsK"
+ },
+ {
+ "type": "java",
+ "name": "BSK Server",
+ "request": "launch",
+ "mainClass": "BsK.server.Server",
+ "projectName": "BSK-All-in-One-Clinic-Management-System",
+ "cwd": "${workspaceFolder}",
+ "console": "internalConsole",
+ "internalConsoleOptions": "openOnSessionStart",
+ "args": [],
+ "vmArgs": [],
+ "env": {},
+ "preLaunchTask": "${command:java.clean.workspace}"
+ },
+ {
+ "type": "java",
+ "name": "BSK Client",
+ "request": "launch",
+ "mainClass": "BsK.client.Client",
+ "projectName": "BSK-All-in-One-Clinic-Management-System",
+ "cwd": "${workspaceFolder}",
+ "console": "internalConsole",
+ "internalConsoleOptions": "openOnSessionStart",
+ "args": [],
+ "vmArgs": [],
+ "env": {}
+ },
+ {
+ "type": "java",
+ "name": "BSK Client (Instance 2)",
+ "request": "launch",
+ "mainClass": "BsK.client.Client",
+ "projectName": "BSK-All-in-One-Clinic-Management-System",
+ "cwd": "${workspaceFolder}",
+ "console": "internalConsole",
+ "internalConsoleOptions": "openOnSessionStart",
+ "args": [],
+ "vmArgs": [],
+ "env": {}
+ },
+ {
+ "type": "java",
+ "name": "BSK Client (Instance 3)",
+ "request": "launch",
+ "mainClass": "BsK.client.Client",
+ "projectName": "BSK-All-in-One-Clinic-Management-System",
+ "cwd": "${workspaceFolder}",
+ "console": "internalConsole",
+ "internalConsoleOptions": "openOnSessionStart",
+ "args": [],
+ "vmArgs": [],
+ "env": {}
+ }
+ ]
+}
\ No newline at end of file
diff --git a/medical_invoice.pdf b/medical_invoice.pdf
index 175b575..f98e23a 100644
Binary files a/medical_invoice.pdf and b/medical_invoice.pdf differ
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java
index 30c94c1..282c379 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java
@@ -173,9 +173,30 @@ public class AddDialog extends JDialog {
// Set size of the dialog
setSize(1000, 400);
- // Put in the middle
- setLocationRelativeTo(null);
+ // Put in the middle of parent window
+ setLocationRelativeTo(parent);
setResizable(true);
+
+ // Ensure modal behavior and proper parent relationship
+ setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this
+
+ // Add window listener to handle minimize/restore events with parent
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (parent != null) {
+ parent.setState(Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (parent != null && parent.getState() == Frame.ICONIFIED) {
+ parent.setState(Frame.NORMAL);
+ }
+ }
+ });
// Send request to get the latest 20 patients in the database
sendGetRecentPatientRequest();
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
index 6505831..ec27195 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
@@ -38,6 +38,8 @@ import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
import java.awt.datatransfer.*;
import java.text.DecimalFormat;
import java.text.ParseException;
@@ -95,6 +97,11 @@ import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.io.InputStream;
+// Add new imports
+import BsK.common.entity.PatientHistory;
+import BsK.common.packet.req.GetPatientHistoryRequest;
+import BsK.common.packet.res.GetPatientHistoryResponse;
+
@Slf4j
public class CheckUpPage extends JPanel {
private MainFrame mainFrame;
@@ -105,7 +112,7 @@ public class CheckUpPage extends JPanel {
private JTable historyTable;
private final ResponseListener checkUpQueueUpdateListener = this::handleGetCheckUpQueueUpdateResponse;
private final ResponseListener checkUpQueueListener = this::handleGetCheckUpQueueResponse;
- private final ResponseListener customerHistoryListener = this::handleGetCustomerHistoryResponse;
+ private final ResponseListener patientHistoryListener = this::handleGetPatientHistoryResponse;
private final ResponseListener districtResponseListener = this::handleGetDistrictResponse;
private final ResponseListener wardResponseListener = this::handleGetWardResponse;
private final ResponseListener callPatientResponseListener = this::handleCallPatientResponse;
@@ -264,7 +271,7 @@ public class CheckUpPage extends JPanel {
ClientHandler.addResponseListener(GetCheckUpQueueResponse.class, checkUpQueueListener);
ClientHandler.addResponseListener(GetCheckUpQueueUpdateResponse.class, checkUpQueueUpdateListener);
- ClientHandler.addResponseListener(GetCustomerHistoryResponse.class, customerHistoryListener);
+ ClientHandler.addResponseListener(GetPatientHistoryResponse.class, patientHistoryListener);
ClientHandler.addResponseListener(GetDistrictResponse.class, districtResponseListener);
ClientHandler.addResponseListener(GetWardResponse.class, wardResponseListener);
ClientHandler.addResponseListener(CallPatientResponse.class, callPatientResponseListener);
@@ -1047,7 +1054,8 @@ public class CheckUpPage extends JPanel {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
int selectedRow = historyTable.getSelectedRow();
- // if (selectedRow != -1) { System.out.println("Clicked on history row: " + selectedRow); }
+ // send getPatientInfoByCheckupIdReq
+
}
}
});
@@ -1436,9 +1444,9 @@ public class CheckUpPage extends JPanel {
customerLastNameField.setText(selectedPatient.getCustomerLastName());
customerFirstNameField.setText(selectedPatient.getCustomerFirstName());
doctorComboBox.setSelectedItem(selectedPatient.getDoctorName());
- suggestionField.setText(selectedPatient.getSymptoms());
- diagnosisField.setText(selectedPatient.getDiagnosis());
- conclusionField.setText(""); // Clear conclusion field for now
+ suggestionField.setText(selectedPatient.getSuggestion()); // De nghi
+ diagnosisField.setText(selectedPatient.getDiagnosis()); // Chuan doan
+ conclusionField.setText(selectedPatient.getConclusion()); // Ket luan
// Handle RTF notes content
String notes = selectedPatient.getNotes();
@@ -1469,7 +1477,7 @@ public class CheckUpPage extends JPanel {
genderComboBox.setSelectedItem(selectedPatient.getCustomerGender());
checkupTypeComboBox.setSelectedItem(selectedPatient.getCheckupType());
- NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCustomerHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId())));
+ NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetPatientHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId())));
String fullAddress = selectedPatient.getCustomerAddress();
String[] addressParts = fullAddress.split(", ");
@@ -1578,9 +1586,13 @@ public class CheckUpPage extends JPanel {
}
}
- private void handleGetCustomerHistoryResponse(GetCustomerHistoryResponse response) {
- log.info("Received customer history");
+ private void handleGetPatientHistoryResponse(GetPatientHistoryResponse response) {
+ log.info("Received patient history");
this.history = response.getHistory();
+
+ // Access PatientHistory objects if needed
+ List patientHistories = response.getPatientHistoryList();
+
historyModel.setDataVector(this.history, new String[]{"Ngày Tháng","Mã khám bệnh", "Triệu chứng", "Chẩn đoán", "Ghi chú"});
}
@@ -2400,6 +2412,29 @@ public class CheckUpPage extends JPanel {
setIconImage(new ImageIcon("src/main/java/BsK/client/ui/assets/icon/database.png").getImage());
setSize(800, 600);
setLayout(new BorderLayout());
+
+ // Add window listener to handle minimize/restore events with parent
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (mainFrame != null) {
+ mainFrame.setState(java.awt.Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (mainFrame != null && mainFrame.getState() == java.awt.Frame.ICONIFIED) {
+ mainFrame.setState(java.awt.Frame.NORMAL);
+ }
+ }
+
+ @Override
+ public void windowClosing(WindowEvent e) {
+ // Hide instead of closing to maintain state
+ setVisible(false);
+ }
+ });
String[] columns = {"Mã khám bệnh", "Ngày sinh", "Họ", "Tên", "Bác Sĩ", "Loại khám"};
queueTableModel = new DefaultTableModel(preprocessPatientDataForTable(patientQueue), columns) {
@@ -2622,7 +2657,7 @@ public class CheckUpPage extends JPanel {
String customerNumber = customerPhoneField.getText();
String customerWeight = customerWeightSpinner.getValue().toString();
String customerHeight = customerHeightSpinner.getValue().toString();
- String doctorName = (String) doctorComboBox.getSelectedItem();
+ String doctorId = String.valueOf(doctorComboBox.getSelectedIndex() + 1);
String suggestions = suggestionField.getText();
String diagnosis = diagnosisField.getText();
String conclusion = conclusionField.getText();
@@ -2642,7 +2677,7 @@ public class CheckUpPage extends JPanel {
log.info("customerNumber: {}", customerNumber);
log.info("customerWeight: {}", customerWeight);
log.info("customerHeight: {}", customerHeight);
- log.info("doctorName: {}", doctorName);
+ log.info("doctorId: {}", doctorId);
log.info("suggestions: {}", suggestions);
log.info("diagnosis: {}", diagnosis);
log.info("conclusion: {}", conclusion);
@@ -2653,7 +2688,7 @@ public class CheckUpPage extends JPanel {
log.info("servicePrescription: {}", (Object) servicePrescription);
log.info("=== End SaveCheckupRequest Data ===");
- // Create and send the save request
+
SaveCheckupRequest request = new SaveCheckupRequest(
checkupId,
checkupDate,
@@ -2666,12 +2701,13 @@ public class CheckUpPage extends JPanel {
customerNumber,
customerWeight,
customerHeight,
- doctorName,
- suggestions,
- diagnosis,
+ doctorId,
+ suggestions, // De nghi
+ diagnosis, // Chuan doan
rtfContent, // Send RTF content as notes
status,
checkupType,
+ conclusion, // Ket luan
medicinePrescription,
servicePrescription
);
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java
index d617537..01fcc30 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java
@@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel;
import BsK.client.network.handler.ClientHandler;
import BsK.client.network.handler.ResponseListener;
+import BsK.common.entity.Medicine;
import BsK.common.packet.req.GetMedInfoRequest;
import BsK.common.packet.res.GetMedInfoResponse;
import BsK.common.util.network.NetworkUtil;
@@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -47,6 +50,7 @@ public class MedicineDialog extends JDialog {
private JTable selectedTable;
private boolean isProgrammaticallySettingMedicineNameField = false;
+ private List medicines = new ArrayList<>();
private String[][] medicinePrescription;
private static final Logger logger = LoggerFactory.getLogger(MedicineDialog.class);
@@ -57,6 +61,11 @@ public class MedicineDialog extends JDialog {
void getMedInfoHandler(GetMedInfoResponse response) {
logger.info("Received medicine data");
medicineData = response.getMedInfo();
+
+ // Get Medicine objects from the response
+ medicines = response.getMedicines();
+
+ // Set the data vector with the raw string arrays for backward compatibility
tableModel.setDataVector(medicineData, medcineColumns);
// resize column width
@@ -81,6 +90,27 @@ public class MedicineDialog extends JDialog {
setSize(1100, 600);
setLocationRelativeTo(parent);
setResizable(true);
+
+ // Ensure modal behavior and proper parent relationship
+ setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this
+
+ // Add window listener to handle minimize/restore events with parent
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (parent != null) {
+ parent.setState(Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (parent != null && parent.getState() == Frame.ICONIFIED) {
+ parent.setState(Frame.NORMAL);
+ }
+ }
+ });
// Initialize medicinePrescription as empty array
medicinePrescription = new String[0][0];
@@ -325,12 +355,29 @@ public class MedicineDialog extends JDialog {
JButton removeMedicineButton = new JButton("Xóa thuốc đã chọn");
removeButtonPanel.add(removeMedicineButton);
+ // Create titled panels for right side sections
+ JPanel availableMedicinesPanel = new JPanel(new BorderLayout());
+ availableMedicinesPanel.setBorder(BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(), "Thuốc trong kho",
+ javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP,
+ new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181)
+ ));
+ availableMedicinesPanel.add(medicineTableScrollPane, BorderLayout.CENTER);
+
+ JPanel chosenMedicinesPanel = new JPanel(new BorderLayout());
+ chosenMedicinesPanel.setBorder(BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(), "Thuốc đã chọn",
+ javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP,
+ new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60)
+ ));
+ chosenMedicinesPanel.setBackground(new Color(255, 240, 240)); // Light red background
+ selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background
+ chosenMedicinesPanel.add(selectedTableScrollPane, BorderLayout.CENTER);
+ chosenMedicinesPanel.add(removeButtonPanel, BorderLayout.SOUTH);
+
JPanel rightPanel = new JPanel(new BorderLayout(0, 5));
- rightPanel.add(medicineTableScrollPane, BorderLayout.CENTER);
- JPanel bottomRightsPanel = new JPanel(new BorderLayout(0,5));
- bottomRightsPanel.add(selectedTableScrollPane, BorderLayout.CENTER);
- bottomRightsPanel.add(removeButtonPanel, BorderLayout.SOUTH);
- rightPanel.add(bottomRightsPanel, BorderLayout.SOUTH);
+ rightPanel.add(availableMedicinesPanel, BorderLayout.CENTER);
+ rightPanel.add(chosenMedicinesPanel, BorderLayout.SOUTH);
JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel);
mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
@@ -439,12 +486,16 @@ public class MedicineDialog extends JDialog {
medicineTable.clearSelection();
} else {
List filteredData = new ArrayList<>();
+ List filteredMedicines = new ArrayList<>();
String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase());
- for (String[] row : medicineData) {
- if (row.length > 1 && row[1] != null && TextUtils.removeAccents(row[1].toLowerCase()).contains(lowerCaseFilterText)) {
- filteredData.add(row);
+
+ for (Medicine med : medicines) {
+ if (TextUtils.removeAccents(med.getName().toLowerCase()).contains(lowerCaseFilterText)) {
+ filteredMedicines.add(med);
+ filteredData.add(med.toStringArray());
}
}
+
tableModel.setDataVector(filteredData.toArray(new String[0][0]), medcineColumns);
resizeMedicineTableColumns();
if (!filteredData.isEmpty()) {
@@ -534,18 +585,23 @@ public class MedicineDialog extends JDialog {
currentPrice = tableModel.getValueAt(modelRow, 6).toString();
} else {
String searchName = medicineNameField.getText().trim();
- String foundId = null, foundUnit = null, foundPrice = null, actualName = null;
- for (String[] med : medicineData) {
- if (TextUtils.removeAccents(med[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) {
- foundId = med[0]; actualName = med[1]; foundUnit = med[5]; foundPrice = med[6];
+ Medicine foundMedicine = null;
+
+ for (Medicine med : medicines) {
+ if (TextUtils.removeAccents(med.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) {
+ foundMedicine = med;
break;
}
}
- if (foundId == null) {
+
+ if (foundMedicine == null) {
JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho thuốc: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE);
return;
}
- medicineId = foundId; name = actualName; unit = foundUnit; currentPrice = foundPrice;
+ medicineId = foundMedicine.getId();
+ name = foundMedicine.getName();
+ unit = foundMedicine.getUnit();
+ currentPrice = foundMedicine.getSellingPrice();
}
int quantity = (Integer) quantitySpinner.getValue();
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java
index 4439a53..1af05bf 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java
@@ -32,6 +32,8 @@ import java.util.ArrayList;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.text.DecimalFormat;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
public class MedicineInvoice{
@@ -107,6 +109,27 @@ public class MedicineInvoice{
dialog.setResizable(true);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setLayout(new BorderLayout());
+
+ // Ensure modal behavior and proper parent relationship
+ dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ dialog.setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this
+
+ // Add window listener to handle minimize/restore events with parent
+ dialog.addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (parent != null) {
+ parent.setState(Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (parent != null && parent.getState() == Frame.ICONIFIED) {
+ parent.setState(Frame.NORMAL);
+ }
+ }
+ });
JPanel buttonPanel = new JPanel(new GridLayout(1, 2));
JButton saveButton = new JButton("Save PDF");
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java
index f618674..e12bf5a 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java
@@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel;
import BsK.client.network.handler.ClientHandler;
import BsK.client.network.handler.ResponseListener;
+import BsK.common.entity.Service;
import BsK.common.packet.req.GetSerInfoRequest;
import BsK.common.packet.res.GetSerInfoResponse;
import BsK.common.util.network.NetworkUtil;
@@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -41,6 +44,7 @@ public class ServiceDialog extends JDialog {
private JTable selectedTable;
private boolean isProgrammaticallySettingNameField = false;
+ private List services = new ArrayList<>();
private String[][] servicePrescription;
private static final Logger logger = LoggerFactory.getLogger(ServiceDialog.class);
@@ -51,6 +55,11 @@ public class ServiceDialog extends JDialog {
void getSerInfoHandler(GetSerInfoResponse response) {
logger.info("Received service data");
serviceData = response.getSerInfo();
+
+ // Get Service objects from the response
+ services = response.getServices();
+
+ // Set the data vector with the raw string arrays for backward compatibility
tableModel.setDataVector(serviceData, serviceColumns);
resizeServiceTableColumns();
}
@@ -66,6 +75,27 @@ public class ServiceDialog extends JDialog {
setSize(1100, 600);
setLocationRelativeTo(parent);
setResizable(true);
+
+ // Ensure modal behavior and proper parent relationship
+ setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this
+
+ // Add window listener to handle minimize/restore events with parent
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (parent != null) {
+ parent.setState(Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (parent != null && parent.getState() == Frame.ICONIFIED) {
+ parent.setState(Frame.NORMAL);
+ }
+ }
+ });
// Initialize servicePrescription as empty array
servicePrescription = new String[0][0];
@@ -217,12 +247,29 @@ public class ServiceDialog extends JDialog {
JButton removeServiceButton = new JButton("Xóa dịch vụ đã chọn");
removeServiceButtonPanel.add(removeServiceButton);
+ // Create titled panels for right side sections
+ JPanel availableServicesPanel = new JPanel(new BorderLayout());
+ availableServicesPanel.setBorder(BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(), "Dịch vụ có",
+ javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP,
+ new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181)
+ ));
+ availableServicesPanel.add(serviceTableScrollPane, BorderLayout.CENTER);
+
+ JPanel chosenServicesPanel = new JPanel(new BorderLayout());
+ chosenServicesPanel.setBorder(BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(), "Dịch vụ đã chọn",
+ javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP,
+ new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60)
+ ));
+ chosenServicesPanel.setBackground(new Color(255, 240, 240)); // Light red background
+ selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background
+ chosenServicesPanel.add(selectedTableScrollPane, BorderLayout.CENTER);
+ chosenServicesPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH);
+
JPanel rightPanel = new JPanel(new BorderLayout(0,5));
- rightPanel.add(serviceTableScrollPane, BorderLayout.CENTER);
- JPanel bottomRightPanel = new JPanel(new BorderLayout(0,5));
- bottomRightPanel.add(selectedTableScrollPane, BorderLayout.CENTER);
- bottomRightPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH);
- rightPanel.add(bottomRightPanel, BorderLayout.SOUTH);
+ rightPanel.add(availableServicesPanel, BorderLayout.CENTER);
+ rightPanel.add(chosenServicesPanel, BorderLayout.SOUTH);
JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel);
mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
@@ -333,30 +380,30 @@ public class ServiceDialog extends JDialog {
private void filterServiceTable() {
String filterText = serviceNameField.getText().trim();
if (serviceData == null) return;
-
if (filterText.isEmpty()) {
tableModel.setDataVector(serviceData, serviceColumns);
resizeServiceTableColumns();
serviceTable.clearSelection();
} else {
List filteredData = new ArrayList<>();
+ List filteredServices = new ArrayList<>();
String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase());
- for (String[] row : serviceData) {
- if (row.length > 1 && row[1] != null) {
- String cellValue = TextUtils.removeAccents(row[1].toLowerCase());
- if (cellValue.contains(lowerCaseFilterText)) {
- filteredData.add(row);
- }
+
+ for (Service service : services) {
+ if (TextUtils.removeAccents(service.getName().toLowerCase()).contains(lowerCaseFilterText)) {
+ filteredServices.add(service);
+ filteredData.add(service.toStringArray());
}
}
+
tableModel.setDataVector(filteredData.toArray(new String[0][0]), serviceColumns);
resizeServiceTableColumns();
- if (!filteredData.isEmpty()) {
+ if (!filteredData.isEmpty()) {
serviceTable.setRowSelectionInterval(0,0);
handleServiceTableRowSelection(0);
} else {
- clearInputFields();
- }
+ clearInputFields();
+ }
}
}
@@ -416,9 +463,7 @@ public class ServiceDialog extends JDialog {
return;
}
- String serviceId;
- String name;
- String currentPrice;
+ String serviceId, name, currentPrice;
if (selectedRowInServiceTable != -1) {
int modelRow = serviceTable.convertRowIndexToModel(selectedRowInServiceTable);
@@ -427,26 +472,22 @@ public class ServiceDialog extends JDialog {
currentPrice = tableModel.getValueAt(modelRow, 2).toString();
} else {
String searchName = serviceNameField.getText().trim();
- String foundId = null;
- String foundPrice = null;
- String actualName = null;
-
- for (String[] serv : serviceData) {
- if (TextUtils.removeAccents(serv[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) {
- foundId = serv[0];
- actualName = serv[1];
- foundPrice = serv[2];
+ Service foundService = null;
+
+ for (Service service : services) {
+ if (TextUtils.removeAccents(service.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) {
+ foundService = service;
break;
}
}
-
- if (foundId == null) {
+
+ if (foundService == null) {
JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho dịch vụ: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE);
return;
}
- serviceId = foundId;
- name = actualName;
- currentPrice = foundPrice;
+ serviceId = foundService.getId();
+ name = foundService.getName();
+ currentPrice = foundService.getCost();
}
int quantity = (Integer) quantitySpinner.getValue();
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java
index 871f02b..2d01b35 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java
@@ -14,6 +14,8 @@ import javax.swing.text.StyleConstants;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
@Slf4j
public class TemplateDialog extends JDialog {
@@ -46,6 +48,27 @@ public class TemplateDialog extends JDialog {
super(mainFrame, "Quản lý mẫu", true);
this.mainFrame = mainFrame;
+ // Ensure modal behavior and proper parent relationship
+ setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
+ setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this
+
+ // Add window listener to handle minimize/restore events with parent
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowIconified(WindowEvent e) {
+ if (mainFrame != null) {
+ mainFrame.setState(Frame.ICONIFIED);
+ }
+ }
+
+ @Override
+ public void windowDeiconified(WindowEvent e) {
+ if (mainFrame != null && mainFrame.getState() == Frame.ICONIFIED) {
+ mainFrame.setState(Frame.NORMAL);
+ }
+ }
+ });
+
initializeComponents();
setupLayout();
setupListeners();
diff --git a/src/main/java/BsK/common/entity/Medicine.java b/src/main/java/BsK/common/entity/Medicine.java
new file mode 100644
index 0000000..bd6341f
--- /dev/null
+++ b/src/main/java/BsK/common/entity/Medicine.java
@@ -0,0 +1,46 @@
+package BsK.common.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Entity class for Medicine
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Medicine {
+ private String id;
+ private String name;
+ private String company;
+ private String description;
+ private String quantity;
+ private String unit;
+ private String sellingPrice;
+
+ /**
+ * Constructor to create Medicine from array data
+ * @param data Array of medicine data from backend
+ */
+ public Medicine(String[] data) {
+ if (data.length < 7) {
+ throw new IllegalArgumentException("Medicine data array must contain at least 7 elements");
+ }
+ this.id = data[0];
+ this.name = data[1];
+ this.company = data[2];
+ this.description = data[3];
+ this.quantity = data[4];
+ this.unit = data[5];
+ this.sellingPrice = data[6];
+ }
+
+ /**
+ * Convert the Medicine entity to a String array
+ * @return String array representation of Medicine
+ */
+ public String[] toStringArray() {
+ return new String[]{id, name, company, description, quantity, unit, sellingPrice};
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/entity/Patient.java b/src/main/java/BsK/common/entity/Patient.java
index 11f5f90..5152ee0 100644
--- a/src/main/java/BsK/common/entity/Patient.java
+++ b/src/main/java/BsK/common/entity/Patient.java
@@ -12,7 +12,7 @@ public class Patient {
private String customerLastName;
private String customerFirstName;
private String doctorName;
- private String symptoms;
+ private String suggestion;
private String diagnosis;
private String notes;
private String status;
@@ -24,6 +24,7 @@ public class Patient {
private String customerGender;
private String customerDob;
private String checkupType;
+ private String conclusion;
public Patient(String[][] array) {
this.checkupId = array[0][0];
@@ -31,7 +32,7 @@ public class Patient {
this.customerLastName = array[0][2];
this.customerFirstName = array[0][3];
this.doctorName = array[0][4];
- this.symptoms = array[0][5];
+ this.suggestion = array[0][5];
this.diagnosis = array[0][6];
this.notes = array[0][7];
this.status = array[0][8];
@@ -43,6 +44,7 @@ public class Patient {
this.customerGender = array[0][14];
this.customerDob = array[0][15];
this.checkupType = array[0][16];
+ this.conclusion = array[0][17];
}
// New constructor for a single patient data row
@@ -55,7 +57,7 @@ public class Patient {
this.customerLastName = data[2];
this.customerFirstName = data[3];
this.doctorName = data[4];
- this.symptoms = data[5];
+ this.suggestion = data[5];
this.diagnosis = data[6];
this.notes = data[7];
this.status = data[8];
@@ -67,5 +69,6 @@ public class Patient {
this.customerGender = data[14];
this.customerDob = data[15];
this.checkupType = data[16];
+ this.conclusion = data[17];
}
}
diff --git a/src/main/java/BsK/common/entity/PatientHistory.java b/src/main/java/BsK/common/entity/PatientHistory.java
new file mode 100644
index 0000000..c5e5826
--- /dev/null
+++ b/src/main/java/BsK/common/entity/PatientHistory.java
@@ -0,0 +1,44 @@
+package BsK.common.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Entity class for Patient History
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class PatientHistory {
+ private String checkupDate;
+ private String checkupId;
+ private String suggestion;
+ private String diagnosis;
+ private String prescriptionId;
+ private String notes;
+
+ /**
+ * Constructor to create PatientHistory from array data
+ * @param data Array of patient history data from backend
+ */
+ public PatientHistory(String[] data) {
+ if (data.length < 6) {
+ throw new IllegalArgumentException("PatientHistory data array must contain at least 6 elements");
+ }
+ this.checkupDate = data[0];
+ this.checkupId = data[1];
+ this.suggestion = data[2];
+ this.diagnosis = data[3];
+ this.prescriptionId = data[4];
+ this.notes = data[5];
+ }
+
+ /**
+ * Convert the PatientHistory entity to a String array
+ * @return String array representation of PatientHistory
+ */
+ public String[] toStringArray() {
+ return new String[]{checkupDate, checkupId, suggestion, diagnosis, prescriptionId, notes};
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/entity/Service.java b/src/main/java/BsK/common/entity/Service.java
new file mode 100644
index 0000000..9b88414
--- /dev/null
+++ b/src/main/java/BsK/common/entity/Service.java
@@ -0,0 +1,38 @@
+package BsK.common.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Entity class for Service
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Service {
+ private String id;
+ private String name;
+ private String cost;
+
+ /**
+ * Constructor to create Service from array data
+ * @param data Array of service data from backend
+ */
+ public Service(String[] data) {
+ if (data.length < 3) {
+ throw new IllegalArgumentException("Service data array must contain at least 3 elements");
+ }
+ this.id = data[0];
+ this.name = data[1];
+ this.cost = data[2];
+ }
+
+ /**
+ * Convert the Service entity to a String array
+ * @return String array representation of Service
+ */
+ public String[] toStringArray() {
+ return new String[]{id, name, cost};
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java
new file mode 100644
index 0000000..d1285be
--- /dev/null
+++ b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java
@@ -0,0 +1,14 @@
+package BsK.common.packet.req;
+
+import BsK.common.packet.Packet;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+@AllArgsConstructor
+@Data
+public class GetPatientHistoryRequest implements Packet {
+ int patientId;
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java
index c3852b0..69b1637 100644
--- a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java
+++ b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java
@@ -20,12 +20,13 @@ public class SaveCheckupRequest implements Packet {
private String customerNumber; // Phone number
private String customerWeight;
private String customerHeight;
- private String doctorName; // Or doctorId if preferred for backend processing
- private String symptoms;
+ private String doctorId; // Or doctorId if preferred for backend processing
+ private String suggestions;
private String diagnosis;
private String notes;
private String status;
private String checkupType;
+ private String conclusion;
private String[][] medicinePrescription; // Structure: [med_id, name, quantity, unit, dose_morning, dose_noon, dose_evening, unit_price, total_price, notes]
private String[][] servicePrescription; // Structure: [ser_id, name, quantity, unit_price, total_price, notes]
}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java
index c879912..effaa6a 100644
--- a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java
+++ b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java
@@ -1,13 +1,51 @@
package BsK.common.packet.res;
+import BsK.common.entity.Medicine;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
+import java.util.ArrayList;
+import java.util.List;
+
@Slf4j
@Data
@AllArgsConstructor
public class GetMedInfoResponse implements Packet {
private String[][] medInfo;
+ private transient List medicines;
+
+ public GetMedInfoResponse(String[][] medInfo) {
+ this.medInfo = medInfo;
+ this.medicines = convertToMedicineList(medInfo);
+ }
+
+ /**
+ * Converts the raw string array data to a list of Medicine objects
+ * @param medInfo Raw medicine data as string arrays
+ * @return List of Medicine objects
+ */
+ private List convertToMedicineList(String[][] medInfo) {
+ List result = new ArrayList<>();
+ if (medInfo != null) {
+ for (String[] data : medInfo) {
+ if (data.length >= 7) {
+ result.add(new Medicine(data));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the medicine list, converting from raw data if needed
+ * @return List of Medicine objects
+ */
+ public List getMedicines() {
+ if (medicines == null && medInfo != null) {
+ medicines = convertToMedicineList(medInfo);
+ }
+ return medicines;
+ }
}
diff --git a/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java
new file mode 100644
index 0000000..1f5f044
--- /dev/null
+++ b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java
@@ -0,0 +1,51 @@
+package BsK.common.packet.res;
+
+import BsK.common.entity.PatientHistory;
+import BsK.common.packet.Packet;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@Slf4j
+public class GetPatientHistoryResponse implements Packet {
+ private String[][] history;
+ private transient List patientHistoryList;
+
+ public GetPatientHistoryResponse(String[][] history) {
+ this.history = history;
+ this.patientHistoryList = convertToPatientHistoryList(history);
+ }
+
+ /**
+ * Converts the raw string array data to a list of PatientHistory objects
+ * @param history Raw patient history data as string arrays
+ * @return List of PatientHistory objects
+ */
+ private List convertToPatientHistoryList(String[][] history) {
+ List result = new ArrayList<>();
+ if (history != null) {
+ for (String[] data : history) {
+ if (data.length >= 6) {
+ result.add(new PatientHistory(data));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the patient history list, converting from raw data if needed
+ * @return List of PatientHistory objects
+ */
+ public List getPatientHistoryList() {
+ if (patientHistoryList == null && history != null) {
+ patientHistoryList = convertToPatientHistoryList(history);
+ }
+ return patientHistoryList;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java
index 5276232..f834346 100644
--- a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java
+++ b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java
@@ -1,13 +1,51 @@
package BsK.common.packet.res;
+import BsK.common.entity.Service;
import BsK.common.packet.Packet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
+import java.util.ArrayList;
+import java.util.List;
+
@Slf4j
@Data
@AllArgsConstructor
public class GetSerInfoResponse implements Packet {
private String[][] serInfo;
+ private transient List services;
+
+ public GetSerInfoResponse(String[][] serInfo) {
+ this.serInfo = serInfo;
+ this.services = convertToServiceList(serInfo);
+ }
+
+ /**
+ * Converts the raw string array data to a list of Service objects
+ * @param serInfo Raw service data as string arrays
+ * @return List of Service objects
+ */
+ private List convertToServiceList(String[][] serInfo) {
+ List result = new ArrayList<>();
+ if (serInfo != null) {
+ for (String[] data : serInfo) {
+ if (data.length >= 3) {
+ result.add(new Service(data));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the service list, converting from raw data if needed
+ * @return List of Service objects
+ */
+ public List getServices() {
+ if (services == null && serInfo != null) {
+ services = convertToServiceList(serInfo);
+ }
+ return services;
+ }
}
diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java
index 992d364..8fb40aa 100644
--- a/src/main/java/BsK/server/network/handler/ServerHandler.java
+++ b/src/main/java/BsK/server/network/handler/ServerHandler.java
@@ -19,6 +19,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.Han
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.io.IOException;
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -30,6 +31,9 @@ import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;
+import BsK.common.entity.Medicine;
+import BsK.common.entity.Service;
+import BsK.common.entity.PatientHistory;
import static BsK.server.Server.statement;
@@ -94,8 +98,8 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new ArrayList<>();
while (rs.next()) {
@@ -280,11 +284,11 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) {
+
+ // Get next prescription_id
+ String getNextIdSql = "SELECT COALESCE(MAX(prescription_id), 0) + 1 as next_id FROM MedicineOrder";
+ PreparedStatement nextIdStmt = conn.prepareStatement(getNextIdSql);
+ ResultSet rs = nextIdStmt.executeQuery();
+ if (rs.next()) {
+ prescriptionId = rs.getString("next_id");
+ }
+ rs.close();
+ nextIdStmt.close();
+ }
+
+ // 3. Insert/Update Checkup
+ String checkupSql = """
+ INSERT INTO Checkup (
+ checkup_id, customer_id, doctor_id, checkup_date,
+ suggestion, diagnosis, prescription_id, notes, status, checkup_type, conclusion
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ON CONFLICT(checkup_id) DO UPDATE SET
+ suggestion = excluded.suggestion,
+ diagnosis = excluded.diagnosis,
+ prescription_id = excluded.prescription_id,
+ notes = excluded.notes,
+ status = excluded.status,
+ checkup_type = excluded.checkup_type,
+ conclusion = excluded.conclusion
+ """;
+
+ PreparedStatement checkupStmt = conn.prepareStatement(checkupSql);
+ checkupStmt.setString(1, saveCheckupRequest.getCheckupId());
+ checkupStmt.setString(2, saveCheckupRequest.getCustomerId());
+ checkupStmt.setString(3, saveCheckupRequest.getDoctorId());
+ checkupStmt.setString(4, saveCheckupRequest.getCheckupDate());
+ checkupStmt.setString(5, saveCheckupRequest.getSuggestions());
+ checkupStmt.setString(6, saveCheckupRequest.getDiagnosis());
+ checkupStmt.setString(7, prescriptionId);
+ checkupStmt.setString(8, saveCheckupRequest.getNotes());
+ checkupStmt.setString(9, saveCheckupRequest.getStatus());
+ checkupStmt.setString(10, saveCheckupRequest.getCheckupType());
+ checkupStmt.setString(11, saveCheckupRequest.getConclusion());
+ checkupStmt.executeUpdate();
+ log.info("Prescription ID generated or edited: {}", prescriptionId);
+ log.info("Checkup saved successfully");
+ // 4. Handle Medicine Prescriptions
+ if (saveCheckupRequest.getMedicinePrescription() != null &&
+ saveCheckupRequest.getMedicinePrescription().length > 0 && prescriptionId != null) {
+
+ // Calculate total amount for medicine order
+ double totalMedicineAmount = 0;
+ for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
+ if (medicine.length > 8) {
+ try {
+ totalMedicineAmount += Double.parseDouble(medicine[8]); // total_price
+ } catch (NumberFormatException e) {
+ log.warn("Invalid medicine total price: {}", medicine[8]);
+ }
+ }
+ }
+
+ // Insert/Update MedicineOrder
+ String medicineOrderSql = """
+ INSERT INTO MedicineOrder (
+ prescription_id, checkup_id, customer_id,
+ total_amount, payment_method, status,
+ payment_status, processed_by, notes, datetime
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_DATE)
+ ON CONFLICT(prescription_id, checkup_id) DO UPDATE SET
+ customer_id = excluded.customer_id,
+ total_amount = excluded.total_amount,
+ payment_method = excluded.payment_method,
+ status = excluded.status,
+ payment_status = excluded.payment_status,
+ processed_by = excluded.processed_by,
+ notes = excluded.notes,
+ datetime = CURRENT_DATE
+ """;
+
+ PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql);
+ medicineOrderStmt.setString(1, prescriptionId);
+ medicineOrderStmt.setString(2, saveCheckupRequest.getCheckupId());
+ medicineOrderStmt.setString(3, saveCheckupRequest.getCustomerId());
+ medicineOrderStmt.setDouble(4, totalMedicineAmount);
+ medicineOrderStmt.setString(5, ""); // payment_method
+ medicineOrderStmt.setString(6, "Pending"); // status
+ medicineOrderStmt.setString(7, "Unpaid"); // payment_status
+ medicineOrderStmt.setString(8, ""); // processed_by
+ medicineOrderStmt.setString(9, ""); // notes
+ medicineOrderStmt.executeUpdate();
+ log.info("MedicineOrder saved successfully");
+ // Insert OrderItems
+ String orderItemSql = """
+ INSERT INTO OrderItem (
+ prescription_id, med_id, quantity_ordered,
+ dosage, duration, price_per_unit, total_price, checkup_id, notes
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ON CONFLICT(order_item_id) DO UPDATE SET
+ quantity_ordered = excluded.quantity_ordered,
+ dosage = excluded.dosage,
+ duration = excluded.duration,
+ price_per_unit = excluded.price_per_unit,
+ total_price = excluded.total_price,
+ checkup_id = excluded.checkup_id,
+ notes = excluded.notes
+ """;
+ log.info("OrderItem saved successfully");
+ PreparedStatement orderItemStmt = conn.prepareStatement(orderItemSql);
+
+ for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
+ if (medicine.length >= 10) {
+ orderItemStmt.setString(1, prescriptionId);
+ orderItemStmt.setString(2, medicine[0]); // med_id
+ orderItemStmt.setString(3, medicine[2]); // quantity
+
+ // Build dosage string from morning, noon, evening
+ String dosage = String.format("Sáng %s, Trưa %s, Chiều %s",
+ medicine[4], medicine[5], medicine[6]);
+ orderItemStmt.setString(4, dosage);
+ orderItemStmt.setString(5, ""); // duration - could be calculated or added to request
+ orderItemStmt.setString(6, medicine[7]); // unit_price
+ orderItemStmt.setString(7, medicine[8]); // total_price
+ orderItemStmt.setString(8, saveCheckupRequest.getCheckupId());
+ orderItemStmt.setString(9, medicine[9]); // notes
+ orderItemStmt.addBatch();
+ }
+ }
+ orderItemStmt.executeBatch();
+ }
+ log.info("Medicine prescriptions saved successfully");
+ // 5. Handle Service Prescriptions
+ if (saveCheckupRequest.getServicePrescription() != null &&
+ saveCheckupRequest.getServicePrescription().length > 0) {
+
+ String serviceSql = """
+ INSERT INTO CheckupService (
+ checkup_id, service_id, quantity,
+ total_cost, status, checkup_date
+ ) VALUES (?, ?, ?, ?, ?, CURRENT_DATE)
+ ON CONFLICT(order_id) DO UPDATE SET
+ quantity = excluded.quantity,
+ total_cost = excluded.total_cost,
+ status = excluded.status
+ """;
+
+ PreparedStatement serviceStmt = conn.prepareStatement(serviceSql);
+
+ for (String[] service : saveCheckupRequest.getServicePrescription()) {
+ if (service.length >= 6) {
+ serviceStmt.setString(1, saveCheckupRequest.getCheckupId());
+ serviceStmt.setString(2, service[0]); // service_id
+ serviceStmt.setString(3, service[2]); // quantity
+ serviceStmt.setString(4, service[4]); // total_cost
+ serviceStmt.setString(5, "PENDING"); // status
+
+ serviceStmt.addBatch();
+ }
+ }
+ serviceStmt.executeBatch();
+ }
+ log.info("Service prescriptions saved successfully");
+ // Commit transaction
+ conn.commit();
+ log.info("Successfully saved checkup {} with all related data", saveCheckupRequest.getCheckupId());
+
+ UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(true, "Checkup saved successfully"));
+
+ } catch (Exception e) {
+ log.error("Error saving checkup transaction", e);
+ // Rollback transaction on error
+ if (conn != null) {
+ try {
+ conn.rollback();
+ log.info("Transaction rolled back due to error");
+ } catch (SQLException rollbackEx) {
+ log.error("Error rolling back transaction", rollbackEx);
+ }
+ }
+ UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION));
+ } finally {
+ // Restore auto-commit
+ if (conn != null) {
+ try {
+ conn.setAutoCommit(true);
+ } catch (SQLException e) {
+ log.error("Error restoring auto-commit", e);
+ }
+ }
+ }
+ }
}
}
diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db
index 4a0d49f..7073513 100644
Binary files a/src/main/resources/database/BSK.db and b/src/main/resources/database/BSK.db differ
diff --git a/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg b/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg
deleted file mode 100644
index e46e8a2..0000000
Binary files a/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg and /dev/null differ