mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-18 20:14:31 +00:00
[update] Remove deprecated app_settings.properties file and update config.properties with new clinic and server settings. Refactor LocalStorage to centralize clinic information and server configurations. Enhance CheckUpPage and related components for improved data handling and validation. Streamline database queries in ServerHandler for better performance and clarity.
This commit is contained in:
1 parent
8c05076934
commit
736f16d818
15 files changed
+433
-377
No files matched your search
@@ -1,17 +0,0 @@
|
||||
#BSK Client Application Settings
|
||||
#Wed Jul 23 13:22:55 ICT 2025
|
||||
app.auto_save=false
|
||||
app.auto_save_interval=5
|
||||
app.language=Ti\u1EBFng Vi\u1EC7t
|
||||
app.theme=T\u1ED1i
|
||||
backup.auto_enabled=false
|
||||
backup.frequency=H\u1EB1ng ng\u00E0y
|
||||
backup.path=
|
||||
clinic.address=Khu v\u1EF1c 5, th\u1ECB tr\u1EA5n \u0110\u1EE9c H\u00F2a, huy\u1EC7n \u0110\u1EE9c H\u00F2a, t\u1EC9nh Long An
|
||||
clinic.name=Ph\u00F2ng kh\u00E1m B\u00E1c S\u0129 Kh\u1EAFp
|
||||
clinic.phone=0912345678
|
||||
server.address=
|
||||
server.auto_connect=false
|
||||
server.port=1999
|
||||
storage.image_path=
|
||||
storage.pdf_path=
|
||||
@@ -1,12 +1,13 @@
|
||||
#BSK Clinic Management System Configuration - Updated via Settings Dialog
|
||||
#Wed Jul 23 13:42:36 ICT 2025
|
||||
clinic.address=S\u1ED1 ABC, \u0110\u01B0\u1EDDng XYZ, Qu\u1EADn 1, TP.HCM
|
||||
clinic.name=Ph\u00F2ng kh\u00E1m BSK
|
||||
#Sat Aug 02 11:33:13 ICT 2025
|
||||
clinic.address=So ABC, Duong XYZ, Quan 1, TP.HCM
|
||||
clinic.name=Phong kham BSK
|
||||
clinic.phone=0123456789
|
||||
clinic.prefix=BSK
|
||||
google.drive.enabled=true
|
||||
google.drive.root.folder=BSK_Clinic_Patient_Files
|
||||
server.address=127.0.0.1
|
||||
server.port=1999
|
||||
storage.checkup_media_base_dir=src/main/resources/image/checkup_media
|
||||
storage.image_db_path=img_db
|
||||
app.project_path=D:/Github Clones/BSK-All-in-One-Clinic-Management-System
|
||||
storage.ultrasound_folder_path=ANH SIEU AM
|
||||
app.auto_change_status_to_finished=false
|
||||
@@ -9,7 +9,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -19,32 +18,38 @@ public class LocalStorage {
|
||||
public static String username;
|
||||
public static Role role;
|
||||
public static int userId;
|
||||
public static String ClinicName = "Phòng khám BSK";
|
||||
public static String ClinicAddress = "Số ABC, Đường XYZ, Quận 1, TP.HCM";
|
||||
public static String ClinicPhone = "0123456789";
|
||||
public static String ClinicPrefix = "BSK";
|
||||
|
||||
public static List<String> chatHistory = new ArrayList<>();
|
||||
public static String pathToProject = "";
|
||||
|
||||
public static String dataDialogSearchTerm = "";
|
||||
public static Date dataDialogFromDate = new Date(); // Default to today
|
||||
public static Date dataDialogToDate = new Date(); // Default to today
|
||||
public static String dataDialogDoctorName = "Tất cả"; // Default to "All"
|
||||
public static Date dataDialogFromDate = new Date();
|
||||
public static Date dataDialogToDate = new Date();
|
||||
public static String dataDialogDoctorName = "Tất cả";
|
||||
|
||||
public static List<DoctorItem> doctorsName = new ArrayList<>();
|
||||
public static String[] provinces = new String[]{};
|
||||
public static String[] wards = new String[]{};
|
||||
public static Map<String, String> provinceToId = new java.util.HashMap<>();
|
||||
public static HashMap<String, String[]> cachedWards; // cache local areas later will do
|
||||
public static HashMap<String, String[][]> medUnitConversion; // hash by med id inside is unit id and unit name and conversion rate and unit price
|
||||
public static HashMap<String, String[]> cachedWards;
|
||||
public static HashMap<String, String[][]> medUnitConversion;
|
||||
|
||||
// --- Settings managed in the SettingsDialog ---
|
||||
|
||||
// these variable can be changed by settings page
|
||||
// need restart
|
||||
public static String checkupMediaBaseDir = "src/main/resources/image/checkup_media";
|
||||
public static String ULTRASOUND_FOLDER_PATH = "ANH SIEU AM";
|
||||
|
||||
public static String ClinicName = "Phòng khám BSK";
|
||||
public static String ClinicAddress = "Số ABC, Đường XYZ, Quận 1, TP.HCM";
|
||||
public static String ClinicPhone = "0123456789";
|
||||
public static String ClinicPrefix = "BSK";
|
||||
public static String serverAddress = "127.0.0.1";
|
||||
public static String serverPort = "1999";
|
||||
public static boolean googleDriveEnabled = true;
|
||||
public static String googleDriveRootFolder = "BSK_Clinic_Patient_Files";
|
||||
|
||||
// does not need restart
|
||||
public static Boolean autoChangeStatusToFinished = false; // turn this on will change status to finished when checkup is save by hitting print
|
||||
public static Boolean autoChangeStatusToFinished = false;
|
||||
|
||||
}
|
||||
@@ -2229,7 +2229,7 @@ public class CheckUpPage extends JPanel {
|
||||
log.warn("Doctor '{}' not found in the list for patient selection.", selectedPatient.getDoctorName());
|
||||
}
|
||||
|
||||
DoctorItem ultrasoundDoctorToSelect = findDoctorByName(selectedPatient.getDoctorUltrasoundId());
|
||||
DoctorItem ultrasoundDoctorToSelect = findDoctorById(selectedPatient.getDoctorUltrasoundId());
|
||||
if (ultrasoundDoctorToSelect != null) {
|
||||
ultrasoundDoctorComboBox.setSelectedItem(ultrasoundDoctorToSelect);
|
||||
} else if (ultrasoundDoctorComboBox.getItemCount() > 0) {
|
||||
@@ -4615,7 +4615,19 @@ public class CheckUpPage extends JPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
Date checkupDate = (Date) datePicker.getModel().getValue();
|
||||
Date dob = (Date) dobPicker.getModel().getValue();
|
||||
Date recheckupDate = (Date) recheckupDatePicker.getModel().getValue();
|
||||
|
||||
// Add validation for the required dates
|
||||
if (checkupDate == null) {
|
||||
JOptionPane.showMessageDialog(this, "Ngày khám không được để trống.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (dob == null) {
|
||||
JOptionPane.showMessageDialog(this, "Ngày sinh không được để trống.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (needRecheckupCheckbox.isSelected() && recheckupDate == null) {
|
||||
JOptionPane.showMessageDialog(this, "Vui lòng chọn ngày tái khám.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
@@ -4627,7 +4639,7 @@ public class CheckUpPage extends JPanel {
|
||||
Integer.parseInt(customerIdField.getText()),
|
||||
Integer.parseInt(doctorIdStr),
|
||||
ultrasoundDoctorIdStr != null ? Integer.parseInt(ultrasoundDoctorIdStr) : null,
|
||||
DateUtils.convertToDate(datePicker.getJFormattedTextField().getText()).getTime(),
|
||||
checkupDate.getTime(),
|
||||
suggestionField.getText(),
|
||||
diagnosisField.getText(),
|
||||
getRtfContentAsString(),
|
||||
@@ -4638,7 +4650,7 @@ public class CheckUpPage extends JPanel {
|
||||
needRecheckupCheckbox.isSelected(),
|
||||
customerFirstNameField.getText(),
|
||||
customerLastNameField.getText(),
|
||||
DateUtils.convertToDate(dobPicker.getJFormattedTextField().getText()).getTime(),
|
||||
dob.getTime(),
|
||||
(String) genderComboBox.getSelectedItem(),
|
||||
customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""),
|
||||
customerPhoneField.getText(),
|
||||
@@ -4954,6 +4966,25 @@ public class CheckUpPage extends JPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a DoctorItem in the LocalStorage list by their ID.
|
||||
* @param id The ID of the doctor to find.
|
||||
* @return The matching DoctorItem, or null if not found.
|
||||
*/
|
||||
private DoctorItem findDoctorById(String id) {
|
||||
// Return null if the ID is invalid or the doctor list is not available
|
||||
if (id == null || id.isEmpty() || id.equals("0") || LocalStorage.doctorsName == null) {
|
||||
return null;
|
||||
}
|
||||
// Loop through the doctors and compare by ID
|
||||
for (DoctorItem doctor : LocalStorage.doctorsName) {
|
||||
if (doctor.getId().equals(id)) {
|
||||
return doctor; // Found a match
|
||||
}
|
||||
}
|
||||
return null; // No match found
|
||||
}
|
||||
|
||||
public void loadPatientByCheckupId(Patient patient) {
|
||||
handleRowSelection(-1, patient);
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ public class MedicineInvoice{
|
||||
// --- CORRECTED THIS LINE ---
|
||||
// Swapped 'amount' and 'supRoute' to match the method signature in InvoiceItem.java
|
||||
// Call signature: (Tên, Ghi chú, Liều dùng, Số lượng, Đường dùng)
|
||||
supplementItems.add(InvoiceItem.createSupplement(supName, supNote, dosageInfo, amount, supRoute));
|
||||
supplementItems.add(InvoiceItem.createSupplement(supName, supNote, dosageInfo, supRoute, amount));
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error parsing supplement data row: " + java.util.Arrays.toString(supplement) + " | Error: " + e.getMessage());
|
||||
@@ -323,11 +323,20 @@ public class MedicineInvoice{
|
||||
parameters.put("checkupNote", notes != null ? notes : "");
|
||||
parameters.put("barcodeNumber", id != null ? id : "");
|
||||
parameters.put("driveURL", driveURL != null ? driveURL : "");
|
||||
parameters.put("logoImage", LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg");
|
||||
parameters.put("hasMedicines", !medicineItems.isEmpty());
|
||||
parameters.put("hasServices", !serviceItems.isEmpty());
|
||||
parameters.put("hasSupplements", !supplementItems.isEmpty());
|
||||
|
||||
String logoPath = "/BsK/client/ui/assets/icon/logo.jpg";
|
||||
// Load the logo as an InputStream
|
||||
InputStream logoStream = MedicineInvoice.class.getResourceAsStream(logoPath);
|
||||
if (logoStream == null) {
|
||||
System.err.println("ERROR: Logo image not found on classpath: " + logoPath);
|
||||
parameters.put("logoImage", null);
|
||||
} else {
|
||||
// Pass the InputStream to the report parameter
|
||||
parameters.put("logoImage", logoStream);
|
||||
}
|
||||
String resourcePath = "/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml";
|
||||
try (InputStream inputStream = MedicineInvoice.class.getResourceAsStream(resourcePath)) {
|
||||
if (inputStream == null) {
|
||||
|
||||
@@ -173,9 +173,9 @@ public class UltrasoundResult {
|
||||
public JasperPrint createJasperPrint() throws JRException, IOException {
|
||||
String jrxmlPath;
|
||||
if ("Dọc".equalsIgnoreCase(this.printType)) {
|
||||
jrxmlPath = LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml";
|
||||
jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml";
|
||||
} else {
|
||||
jrxmlPath =LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml";
|
||||
jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml";
|
||||
}
|
||||
|
||||
log.info("Using JRXML template: {}", jrxmlPath);
|
||||
@@ -209,8 +209,8 @@ public class UltrasoundResult {
|
||||
// Vitals data
|
||||
parameters.put("checkupHeight", this.customerHeight != null ? this.customerHeight : "");
|
||||
parameters.put("checkupWeight", this.customerWeight != null ? this.customerWeight : "");
|
||||
parameters.put("checkupHeartBeat", this.heartRate != null ? this.heartRate : "");
|
||||
parameters.put("checkupBloodPressure", this.bloodPressure != null ? this.bloodPressure : "");
|
||||
parameters.put("checkupHeartBeat", this.heartRate);
|
||||
parameters.put("checkupBloodPressure", this.bloodPressure);
|
||||
|
||||
// Template title
|
||||
parameters.put("templateTitle", this.templateTitle);
|
||||
@@ -227,8 +227,7 @@ public class UltrasoundResult {
|
||||
int numberOfImages = this.selectedImages.size();
|
||||
parameters.put("numberImage", numberOfImages);
|
||||
|
||||
String projectDir = LocalStorage.pathToProject;
|
||||
parameters.put("logoImage", projectDir + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg");
|
||||
parameters.put("logoImage", System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg");
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (i < numberOfImages) {
|
||||
|
||||
+8
-7
@@ -12,7 +12,6 @@ public class InvoiceItem {
|
||||
private String medName;
|
||||
private String dosage;
|
||||
private String amount;
|
||||
private Double unitPrice;
|
||||
private String medRoute;
|
||||
private String medNote;
|
||||
|
||||
@@ -24,10 +23,14 @@ public class InvoiceItem {
|
||||
|
||||
// --- Supplement Fields ---
|
||||
private String supName;
|
||||
private String supNote;
|
||||
private String supDosage;
|
||||
private String supAmount;
|
||||
private String supRoute;
|
||||
private String supNote;
|
||||
|
||||
|
||||
|
||||
|
||||
// Private constructor to be used by factory methods
|
||||
private InvoiceItem() {}
|
||||
|
||||
@@ -40,7 +43,6 @@ public class InvoiceItem {
|
||||
item.dosage = dosage;
|
||||
item.medRoute = route;
|
||||
item.amount = amount;
|
||||
item.unitPrice = 0.0;
|
||||
item.medNote = medNote;
|
||||
return item;
|
||||
}
|
||||
@@ -55,14 +57,14 @@ public class InvoiceItem {
|
||||
return item;
|
||||
}
|
||||
|
||||
public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supAmount, String supRoute) {
|
||||
public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supRoute, String supAmount) {
|
||||
InvoiceItem item = new InvoiceItem();
|
||||
item.type = "SUP";
|
||||
item.supName = supName;
|
||||
item.supNote = supNote;
|
||||
item.supDosage = supDosage;
|
||||
item.supAmount = supAmount;
|
||||
item.supRoute = supRoute;
|
||||
item.supAmount = supAmount;
|
||||
item.supNote = supNote;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -74,7 +76,6 @@ public class InvoiceItem {
|
||||
public String getDosage() { return dosage; }
|
||||
public String getMedRoute() { return medRoute; }
|
||||
public String getAmount() { return amount; }
|
||||
public Double getUnitPrice() { return unitPrice; }
|
||||
public String getMedNote() { return medNote; }
|
||||
public String getSerName() { return serName; }
|
||||
public String getSerNote() { return serNote; }
|
||||
|
||||
+5
-6
@@ -17,7 +17,6 @@
|
||||
<field name="medName" class="java.lang.String"/>
|
||||
<field name="dosage" class="java.lang.String"/>
|
||||
<field name="amount" class="java.lang.String"/>
|
||||
<field name="unitPrice" class="java.lang.Double"/>
|
||||
<field name="medNote" class="java.lang.String"/>
|
||||
<field name="serName" class="java.lang.String"/>
|
||||
<field name="serNote" class="java.lang.String"/>
|
||||
@@ -62,7 +61,7 @@
|
||||
<parameter name="patientName" class="java.lang.String"/>
|
||||
<parameter name="patientDOB" class="java.lang.String"/>
|
||||
<parameter name="clinicAddress" class="java.lang.String"/>
|
||||
<parameter name="logoImage" class="java.lang.String"/>
|
||||
<parameter name="logoImage" class="java.io.InputStream"/>
|
||||
<parameter name="barcodeNumber" class="java.lang.String"/>
|
||||
<parameter name="patientGender" class="java.lang.String"/>
|
||||
<parameter name="hasMedicines" class="java.lang.Boolean"/>
|
||||
@@ -211,16 +210,16 @@
|
||||
<band height="62">
|
||||
<printWhenExpression><![CDATA[$P{hasSupplements}]]></printWhenExpression>
|
||||
<element kind="component" uuid="71f6b965-7b44-475c-aa38-89e77ce55e87" x="0" y="1" width="555" height="59">
|
||||
<printWhenExpression><![CDATA[$P{hasMedicines}]]></printWhenExpression>
|
||||
<printWhenExpression><![CDATA[$P{hasSupplements}]]></printWhenExpression>
|
||||
<component kind="table">
|
||||
<datasetRun uuid="bddd7b63-0aeb-492a-99ec-561999a49e96" subDataset="med_pre">
|
||||
<dataSourceExpression><![CDATA[$P{medicineDS} == null ? new net.sf.jasperreports.engine.JREmptyDataSource() : $P{medicineDS}]]></dataSourceExpression>
|
||||
<datasetRun uuid="54f95387-084f-440b-ad64-df3ba24f449d" subDataset="med_pre">
|
||||
<dataSourceExpression><![CDATA[$P{supplementDS} == null ? new net.sf.jasperreports.engine.JREmptyDataSource() : $P{supplementDS}]]></dataSourceExpression>
|
||||
<returnValue toVariable="MedicineGrandTotal" fromVariable="medTotal"/>
|
||||
</datasetRun>
|
||||
<column kind="single" uuid="6ecff449-3a01-40a2-a424-cb32c6291fa4" width="350">
|
||||
<columnHeader height="19" rowSpan="1">
|
||||
<element kind="staticText" uuid="3891585b-57df-4552-a45b-87caaa47f731" x="0" y="0" width="350" height="19" fontName="Times New Roman" fontSize="16.0" bold="true" hTextAlign="Left" vTextAlign="Middle">
|
||||
<text><![CDATA[THUỐC]]></text>
|
||||
<text><![CDATA[THỰC PHẨM BỔ SUNG]]></text>
|
||||
<box>
|
||||
<topPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
|
||||
<leftPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
|
||||
|
||||
@@ -26,7 +26,6 @@ import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays; // Import Arrays for utility if needed, though not strictly required here
|
||||
import java.util.List;
|
||||
|
||||
public class DataDialog extends JDialog {
|
||||
@@ -49,7 +48,7 @@ public class DataDialog extends JDialog {
|
||||
private JPanel mainPanel;
|
||||
private JPanel paginationPanel;
|
||||
private CheckUpPage checkUpPageInstance;
|
||||
private String[][] currentCheckupData; // <<< MODIFIED: Changed type from List<String[]> to String[][]
|
||||
private String[][] currentCheckupData;
|
||||
//</editor-fold>
|
||||
|
||||
// --- Panels for the other tabs
|
||||
@@ -352,7 +351,8 @@ public class DataDialog extends JDialog {
|
||||
}
|
||||
|
||||
try {
|
||||
ExcelExporter.exportToExcel(patientsToExport, this.fileForExport);
|
||||
// <<< MODIFIED: Pass the doctors list to the exporter
|
||||
ExcelExporter.exportToExcel(patientsToExport, LocalStorage.doctorsName, this.fileForExport);
|
||||
JOptionPane.showMessageDialog(this, "Xuất file Excel thành công!\n" + this.fileForExport.getAbsolutePath(), "Thành công", JOptionPane.INFORMATION_MESSAGE);
|
||||
} catch (IOException ex) {
|
||||
JOptionPane.showMessageDialog(this, "Lỗi khi ghi file Excel: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE);
|
||||
@@ -368,13 +368,11 @@ public class DataDialog extends JDialog {
|
||||
this.totalPages = response.getTotalPages();
|
||||
this.totalRecords = response.getTotalRecords();
|
||||
this.recordsPerPage = response.getPageSize();
|
||||
// This assignment is now correct as both types are String[][]
|
||||
this.currentCheckupData = response.getCheckupData();
|
||||
|
||||
tableModel.setRowCount(0);
|
||||
if (this.currentCheckupData != null) {
|
||||
int stt = (currentPage - 1) * recordsPerPage + 1;
|
||||
// Enhanced for-loop works perfectly on a 2D array
|
||||
for (String[] rowData : this.currentCheckupData) {
|
||||
try {
|
||||
Patient patient = new Patient(rowData);
|
||||
@@ -518,10 +516,15 @@ public class DataDialog extends JDialog {
|
||||
setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
|
||||
|
||||
ImageIcon editIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/edit.png");
|
||||
editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
if (editIcon.getImage() != null) {
|
||||
editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
}
|
||||
editButton = new JButton(editIcon);
|
||||
|
||||
ImageIcon deleteIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/delete.png");
|
||||
deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
if (deleteIcon.getImage() != null) {
|
||||
deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
}
|
||||
deleteButton = new JButton(deleteIcon);
|
||||
|
||||
editButton.setPreferredSize(new Dimension(30, 25));
|
||||
@@ -554,10 +557,15 @@ public class DataDialog extends JDialog {
|
||||
panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
|
||||
|
||||
ImageIcon editIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/edit.png");
|
||||
editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
if (editIcon.getImage() != null) {
|
||||
editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
}
|
||||
editButton = new JButton(editIcon);
|
||||
|
||||
ImageIcon deleteIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/delete.png");
|
||||
deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
if (deleteIcon.getImage() != null) {
|
||||
deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
|
||||
}
|
||||
deleteButton = new JButton(deleteIcon);
|
||||
|
||||
editButton.setPreferredSize(new Dimension(30, 25));
|
||||
@@ -572,9 +580,7 @@ public class DataDialog extends JDialog {
|
||||
}
|
||||
int modelRow = dataTable.convertRowIndexToModel(selectedViewRow);
|
||||
|
||||
// <<< MODIFIED: Use .length for array and check index validity
|
||||
if (currentCheckupData != null && modelRow >= 0 && modelRow < currentCheckupData.length) {
|
||||
// <<< MODIFIED: Use array index access `[modelRow]` instead of `.get()`
|
||||
String[] rowData = currentCheckupData[modelRow];
|
||||
try {
|
||||
Patient selectedPatient = new Patient(rowData);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package BsK.client.ui.component.DataDialog;
|
||||
|
||||
import BsK.common.entity.DoctorItem;
|
||||
import BsK.common.entity.Patient;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
@@ -13,15 +14,34 @@ import java.util.List;
|
||||
|
||||
public class ExcelExporter {
|
||||
|
||||
public static void exportToExcel(List<Patient> patientList, File file) throws IOException {
|
||||
/**
|
||||
* Finds a doctor's name from a list based on their ID.
|
||||
* @param id The doctor ID to look for.
|
||||
* @param doctorList The list of all available doctors.
|
||||
* @return The doctor's name, or an empty string if not found.
|
||||
*/
|
||||
private static String getDoctorNameById(String id, List<DoctorItem> doctorList) {
|
||||
if (id == null || id.isEmpty() || id.equals("0") || doctorList == null) {
|
||||
return "";
|
||||
}
|
||||
for (DoctorItem doctor : doctorList) {
|
||||
if (doctor.getId().equals(id)) {
|
||||
return doctor.getName();
|
||||
}
|
||||
}
|
||||
return ""; // Return empty if no match is found
|
||||
}
|
||||
|
||||
// <<< MODIFIED: Method signature now accepts the list of doctors
|
||||
public static void exportToExcel(List<Patient> patientList, List<DoctorItem> doctorList, File file) throws IOException {
|
||||
// Create a new Excel workbook
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook()) {
|
||||
XSSFSheet sheet = workbook.createSheet("DanhSachKhamBenh");
|
||||
|
||||
// Define header titles
|
||||
// <<< MODIFIED: Added "Bác Sĩ Siêu Âm" column
|
||||
String[] headers = {
|
||||
"Mã Khám", "Mã BN", "Họ", "Tên", "Năm Sinh", "Giới Tính", "Số Điện Thoại",
|
||||
"Địa Chỉ", "Ngày Khám", "Loại Khám", "Bác Sĩ Khám", "CCCD/DDCN", "Cân Nặng (kg)",
|
||||
"Địa Chỉ", "Ngày Khám", "Loại Khám", "Bác Sĩ Khám", "Bác Sĩ Siêu Âm", "CCCD/DDCN", "Cân Nặng (kg)",
|
||||
"Chiều Cao (cm)", "Nhịp Tim (l/p)", "Huyết Áp (mmHg)", "Chẩn Đoán", "Kết Luận",
|
||||
"Đề Nghị", "Ghi Chú", "Ngày Tái Khám"
|
||||
};
|
||||
@@ -48,16 +68,18 @@ public class ExcelExporter {
|
||||
row.createCell(8).setCellValue(patient.getCheckupDate());
|
||||
row.createCell(9).setCellValue(patient.getCheckupType());
|
||||
row.createCell(10).setCellValue(patient.getDoctorName());
|
||||
row.createCell(11).setCellValue(patient.getCccdDdcn());
|
||||
row.createCell(12).setCellValue(patient.getCustomerWeight());
|
||||
row.createCell(13).setCellValue(patient.getCustomerHeight());
|
||||
row.createCell(14).setCellValue(patient.getHeartBeat());
|
||||
row.createCell(15).setCellValue(patient.getBloodPressure());
|
||||
row.createCell(16).setCellValue(patient.getDiagnosis());
|
||||
row.createCell(17).setCellValue(patient.getConclusion());
|
||||
row.createCell(18).setCellValue(patient.getSuggestion());
|
||||
row.createCell(19).setCellValue(patient.getNotes());
|
||||
row.createCell(20).setCellValue(patient.getReCheckupDate());
|
||||
String ultrasoundDoctorName = getDoctorNameById(patient.getDoctorUltrasoundId(), doctorList);
|
||||
row.createCell(11).setCellValue(ultrasoundDoctorName);
|
||||
row.createCell(12).setCellValue(patient.getCccdDdcn());
|
||||
row.createCell(13).setCellValue(patient.getCustomerWeight());
|
||||
row.createCell(14).setCellValue(patient.getCustomerHeight());
|
||||
row.createCell(15).setCellValue(patient.getHeartBeat());
|
||||
row.createCell(16).setCellValue(patient.getBloodPressure());
|
||||
row.createCell(17).setCellValue(patient.getDiagnosis());
|
||||
row.createCell(18).setCellValue(patient.getConclusion());
|
||||
row.createCell(19).setCellValue(patient.getSuggestion());
|
||||
row.createCell(20).setCellValue(patient.getNotes());
|
||||
row.createCell(21).setCellValue(patient.getReCheckupDate());
|
||||
}
|
||||
|
||||
// Write the output to a file
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package BsK.client.ui.component.SettingsPage;
|
||||
|
||||
import BsK.client.Client;
|
||||
import BsK.client.LocalStorage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -14,15 +13,27 @@ import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
public class SettingsDialog extends JDialog {
|
||||
// Existing fields
|
||||
private JTextField mediaDirectoryField;
|
||||
private JTextField ultrasoundFolderField;
|
||||
private JCheckBox autoChangeStatusCheckBox;
|
||||
|
||||
// Fields for additional settings
|
||||
private JTextField clinicNameField;
|
||||
private JTextField clinicAddressField;
|
||||
private JTextField clinicPhoneField;
|
||||
private JTextField clinicPrefixField;
|
||||
private JTextField serverAddressField;
|
||||
private JTextField serverPortField;
|
||||
|
||||
// <<< REMOVED: Google Drive UI fields are gone
|
||||
|
||||
private Properties originalProps;
|
||||
private boolean hasChanges = false;
|
||||
|
||||
public SettingsDialog(JFrame parent) {
|
||||
super(parent, "Cài đặt hệ thống", true); // Modal dialog
|
||||
setSize(700, 600);
|
||||
setSize(700, 700); // Adjusted height
|
||||
setLocationRelativeTo(parent);
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
@@ -76,10 +87,10 @@ public class SettingsDialog extends JDialog {
|
||||
JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (choice == JOptionPane.YES_OPTION) {
|
||||
dispose(); // Close dialog without saving
|
||||
dispose();
|
||||
}
|
||||
} else {
|
||||
dispose(); // No changes, just close
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -100,28 +111,22 @@ public class SettingsDialog extends JDialog {
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
// Auto change status setting
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 1.0;
|
||||
|
||||
autoChangeStatusCheckBox = new JCheckBox("Tự động chuyển trạng thái thành 'Hoàn thành' khi in");
|
||||
autoChangeStatusCheckBox.setFont(new Font("Arial", Font.PLAIN, 12));
|
||||
autoChangeStatusCheckBox.setSelected(LocalStorage.autoChangeStatusToFinished);
|
||||
autoChangeStatusCheckBox.addActionListener(e -> hasChanges = true);
|
||||
parent.add(autoChangeStatusCheckBox, gbc);
|
||||
|
||||
gbc.gridy = 1;
|
||||
gbc.weighty = 0;
|
||||
JLabel autoStatusDescription = new JLabel("<html><i>Khi bật, trạng thái khám bệnh sẽ tự động chuyển thành 'Hoàn thành' sau khi in hóa đơn</i></html>");
|
||||
autoStatusDescription.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
autoStatusDescription.setForeground(Color.GRAY);
|
||||
parent.add(autoStatusDescription, gbc);
|
||||
|
||||
// Add vertical glue to push content to top
|
||||
gbc.gridy = 2;
|
||||
gbc.weighty = 1.0;
|
||||
gbc.fill = GridBagConstraints.VERTICAL;
|
||||
@@ -132,120 +137,88 @@ public class SettingsDialog extends JDialog {
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
gbc.anchor = GridBagConstraints.NORTHWEST;
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
|
||||
// Media directory setting
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 0;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
gbc.weightx = 0;
|
||||
|
||||
JLabel mediaLabel = new JLabel("Thư mục lưu trữ ảnh khám bệnh:");
|
||||
mediaLabel.setFont(new Font("Arial", Font.BOLD, 12));
|
||||
parent.add(mediaLabel, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 1;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 1.0;
|
||||
|
||||
JPanel mediaInputPanel = new JPanel(new BorderLayout(5, 0));
|
||||
mediaDirectoryField = new JTextField();
|
||||
mediaDirectoryField.setText(LocalStorage.checkupMediaBaseDir);
|
||||
mediaDirectoryField.setPreferredSize(new Dimension(300, 25));
|
||||
mediaDirectoryField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
int y = 0;
|
||||
|
||||
javax.swing.event.DocumentListener changeListener = new javax.swing.event.DocumentListener() {
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
});
|
||||
};
|
||||
|
||||
JButton mediaBrowseButton = new JButton("Chọn thư mục");
|
||||
mediaBrowseButton.setPreferredSize(new Dimension(120, 25));
|
||||
mediaBrowseButton.addActionListener(e -> {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
chooser.setCurrentDirectory(new File(mediaDirectoryField.getText()));
|
||||
|
||||
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
mediaDirectoryField.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
hasChanges = true;
|
||||
}
|
||||
});
|
||||
|
||||
mediaInputPanel.add(mediaDirectoryField, BorderLayout.CENTER);
|
||||
mediaInputPanel.add(mediaBrowseButton, BorderLayout.EAST);
|
||||
parent.add(mediaInputPanel, gbc);
|
||||
|
||||
gbc.gridy = 2;
|
||||
gbc.weighty = 0;
|
||||
JLabel mediaDescription = new JLabel("<html><i>Thư mục chứa ảnh được chụp trong quá trình khám bệnh</i></html>");
|
||||
mediaDescription.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
mediaDescription.setForeground(Color.GRAY);
|
||||
parent.add(mediaDescription, gbc);
|
||||
|
||||
// Ultrasound folder setting
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 3;
|
||||
gbc.gridwidth = 1;
|
||||
gbc.fill = GridBagConstraints.NONE;
|
||||
gbc.weightx = 0;
|
||||
gbc.insets = new Insets(15, 5, 5, 5); // Add extra top margin
|
||||
|
||||
JLabel ultrasoundLabel = new JLabel("Thư mục theo dõi ảnh siêu âm:");
|
||||
ultrasoundLabel.setFont(new Font("Arial", Font.BOLD, 12));
|
||||
parent.add(ultrasoundLabel, gbc);
|
||||
|
||||
gbc.gridx = 0;
|
||||
gbc.gridy = 4;
|
||||
gbc.gridwidth = 2;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.weightx = 1.0;
|
||||
gbc.insets = new Insets(5, 5, 5, 5); // Reset insets
|
||||
|
||||
JPanel ultrasoundInputPanel = new JPanel(new BorderLayout(5, 0));
|
||||
ultrasoundFolderField = new JTextField();
|
||||
ultrasoundFolderField.setText(LocalStorage.ULTRASOUND_FOLDER_PATH);
|
||||
ultrasoundFolderField.setPreferredSize(new Dimension(300, 25));
|
||||
ultrasoundFolderField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
||||
public void insertUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
public void removeUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
public void changedUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; }
|
||||
});
|
||||
|
||||
JButton ultrasoundBrowseButton = new JButton("Chọn thư mục");
|
||||
ultrasoundBrowseButton.setPreferredSize(new Dimension(120, 25));
|
||||
ultrasoundBrowseButton.addActionListener(e -> {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
chooser.setCurrentDirectory(new File(ultrasoundFolderField.getText()));
|
||||
|
||||
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
ultrasoundFolderField.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
hasChanges = true;
|
||||
}
|
||||
});
|
||||
|
||||
ultrasoundInputPanel.add(ultrasoundFolderField, BorderLayout.CENTER);
|
||||
ultrasoundInputPanel.add(ultrasoundBrowseButton, BorderLayout.EAST);
|
||||
parent.add(ultrasoundInputPanel, gbc);
|
||||
|
||||
gbc.gridy = 5;
|
||||
gbc.weighty = 0;
|
||||
JLabel ultrasoundDescription = new JLabel("<html><i>Thư mục được hệ thống theo dõi để tự động nhận ảnh siêu âm mới</i></html>");
|
||||
ultrasoundDescription.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
ultrasoundDescription.setForeground(Color.GRAY);
|
||||
parent.add(ultrasoundDescription, gbc);
|
||||
|
||||
// Add vertical glue to push content to top
|
||||
gbc.gridy = 6;
|
||||
// --- Clinic Info ---
|
||||
clinicNameField = addTextFieldSetting(parent, gbc, y++, "Tên phòng khám:", LocalStorage.ClinicName, "Tên sẽ hiển thị trên các phiếu in.", changeListener);
|
||||
clinicAddressField = addTextFieldSetting(parent, gbc, y++, "Địa chỉ phòng khám:", LocalStorage.ClinicAddress, "Địa chỉ sẽ hiển thị trên các phiếu in.", changeListener);
|
||||
clinicPhoneField = addTextFieldSetting(parent, gbc, y++, "SĐT phòng khám:", LocalStorage.ClinicPhone, "Số điện thoại sẽ hiển thị trên các phiếu in.", changeListener);
|
||||
clinicPrefixField = addTextFieldSetting(parent, gbc, y++, "Tiền tố mã:", LocalStorage.ClinicPrefix, "Tiền tố cho mã bệnh nhân và các mã khác (ví dụ: BSK).", changeListener);
|
||||
|
||||
// --- Server Connection ---
|
||||
serverAddressField = addTextFieldSetting(parent, gbc, y++, "Địa chỉ máy chủ:", LocalStorage.serverAddress, "Địa chỉ IP hoặc tên miền của máy chủ.", changeListener);
|
||||
serverPortField = addTextFieldSetting(parent, gbc, y++, "Cổng máy chủ (Port):", LocalStorage.serverPort, "Cổng kết nối tới máy chủ.", changeListener);
|
||||
|
||||
// --- Storage Settings ---
|
||||
mediaDirectoryField = addTextFieldSettingWithBrowse(parent, gbc, y++, "Thư mục lưu trữ ảnh khám bệnh:", LocalStorage.checkupMediaBaseDir, "Thư mục chứa ảnh được chụp trong quá trình khám bệnh.", changeListener);
|
||||
ultrasoundFolderField = addTextFieldSettingWithBrowse(parent, gbc, y++, "Thư mục theo dõi ảnh siêu âm:", LocalStorage.ULTRASOUND_FOLDER_PATH, "Thư mục được hệ thống theo dõi để tự động nhận ảnh siêu âm mới.", changeListener);
|
||||
|
||||
// <<< REMOVED: Google Drive Integration UI is gone.
|
||||
|
||||
// Vertical glue to push all content up
|
||||
gbc.gridy = y * 3; // Adjust glue position
|
||||
gbc.weighty = 1.0;
|
||||
gbc.fill = GridBagConstraints.VERTICAL;
|
||||
parent.add(Box.createVerticalGlue(), gbc);
|
||||
}
|
||||
|
||||
private JTextField addTextFieldSetting(JPanel parent, GridBagConstraints gbc, int yPos, String labelText, String initialValue, String description, javax.swing.event.DocumentListener listener) {
|
||||
gbc.gridx = 0; gbc.gridy = yPos * 3; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; gbc.insets = (yPos == 0) ? new Insets(5, 5, 5, 5) : new Insets(15, 5, 5, 5);
|
||||
JLabel label = new JLabel(labelText);
|
||||
label.setFont(new Font("Arial", Font.BOLD, 12));
|
||||
parent.add(label, gbc);
|
||||
|
||||
gbc.gridy = yPos * 3 + 1; gbc.insets = new Insets(5, 5, 5, 5);
|
||||
JTextField textField = new JTextField(initialValue);
|
||||
textField.getDocument().addDocumentListener(listener);
|
||||
parent.add(textField, gbc);
|
||||
|
||||
gbc.gridy = yPos * 3 + 2;
|
||||
JLabel descLabel = new JLabel("<html><i>" + description + "</i></html>");
|
||||
descLabel.setFont(new Font("Arial", Font.PLAIN, 10));
|
||||
descLabel.setForeground(Color.GRAY);
|
||||
parent.add(descLabel, gbc);
|
||||
|
||||
return textField;
|
||||
}
|
||||
|
||||
private JTextField addTextFieldSettingWithBrowse(JPanel parent, GridBagConstraints gbc, int yPos, String labelText, String initialValue, String description, javax.swing.event.DocumentListener listener) {
|
||||
JTextField textField = addTextFieldSetting(parent, gbc, yPos, labelText, initialValue, description, listener);
|
||||
parent.remove(textField);
|
||||
|
||||
JPanel inputPanel = new JPanel(new BorderLayout(5, 0));
|
||||
inputPanel.add(textField, BorderLayout.CENTER);
|
||||
JButton browseButton = new JButton("Chọn...");
|
||||
browseButton.addActionListener(e -> {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
try {
|
||||
File currentDir = new File(textField.getText());
|
||||
if (currentDir.exists()) {
|
||||
chooser.setCurrentDirectory(currentDir);
|
||||
}
|
||||
} catch (Exception ex) { /* Ignore */ }
|
||||
|
||||
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
textField.setText(chooser.getSelectedFile().getAbsolutePath());
|
||||
}
|
||||
});
|
||||
inputPanel.add(browseButton, BorderLayout.EAST);
|
||||
|
||||
gbc.gridy = yPos * 3 + 1;
|
||||
parent.add(inputPanel, gbc);
|
||||
|
||||
return textField;
|
||||
}
|
||||
|
||||
private JPanel createSection(String title, Color backgroundColor) {
|
||||
JPanel panel = new JPanel(new GridBagLayout()); // Changed to GridBagLayout
|
||||
JPanel panel = new JPanel(new GridBagLayout());
|
||||
panel.setBackground(backgroundColor);
|
||||
panel.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createTitledBorder(
|
||||
@@ -261,7 +234,6 @@ public class SettingsDialog extends JDialog {
|
||||
panel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
private void loadSettings() {
|
||||
try {
|
||||
@@ -275,58 +247,64 @@ public class SettingsDialog extends JDialog {
|
||||
hasChanges = false;
|
||||
} catch (IOException e) {
|
||||
log.error("Error loading config", e);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Lỗi khi tải cài đặt: " + e.getMessage(),
|
||||
"Lỗi",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Lỗi khi tải cài đặt: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveSettings() {
|
||||
// Check if there are any changes
|
||||
boolean mediaChanged = !mediaDirectoryField.getText().equals(LocalStorage.checkupMediaBaseDir);
|
||||
boolean ultrasoundChanged = !ultrasoundFolderField.getText().equals(LocalStorage.ULTRASOUND_FOLDER_PATH);
|
||||
boolean autoStatusChanged = autoChangeStatusCheckBox.isSelected() != LocalStorage.autoChangeStatusToFinished;
|
||||
|
||||
if (!hasChanges && !mediaChanged && !ultrasoundChanged && !autoStatusChanged) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Không có thay đổi nào để lưu.",
|
||||
"Thông báo",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
// <<< MODIFIED: Removed Google Drive fields from change detection
|
||||
boolean restartRequiredChanges =
|
||||
!mediaDirectoryField.getText().trim().equals(LocalStorage.checkupMediaBaseDir) ||
|
||||
!ultrasoundFolderField.getText().trim().equals(LocalStorage.ULTRASOUND_FOLDER_PATH) ||
|
||||
!clinicNameField.getText().trim().equals(LocalStorage.ClinicName) ||
|
||||
!clinicAddressField.getText().trim().equals(LocalStorage.ClinicAddress) ||
|
||||
!clinicPhoneField.getText().trim().equals(LocalStorage.ClinicPhone) ||
|
||||
!clinicPrefixField.getText().trim().equals(LocalStorage.ClinicPrefix) ||
|
||||
!serverAddressField.getText().trim().equals(LocalStorage.serverAddress) ||
|
||||
!serverPortField.getText().trim().equals(LocalStorage.serverPort);
|
||||
|
||||
boolean noRestartChanges = autoChangeStatusCheckBox.isSelected() != LocalStorage.autoChangeStatusToFinished;
|
||||
|
||||
if (!restartRequiredChanges && !noRestartChanges) {
|
||||
JOptionPane.showMessageDialog(this, "Không có thay đổi nào để lưu.", "Thông báo", JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
if (originalProps != null) {
|
||||
props.putAll(originalProps); // Preserve other settings
|
||||
props.putAll(originalProps);
|
||||
}
|
||||
|
||||
// Update settings that require restart
|
||||
String newMediaDir = mediaDirectoryField.getText().trim();
|
||||
String newUltrasoundFolder = ultrasoundFolderField.getText().trim();
|
||||
props.setProperty("storage.checkup_media_base_dir", newMediaDir);
|
||||
props.setProperty("storage.ultrasound_folder_path", newUltrasoundFolder);
|
||||
// Clinic Info
|
||||
props.setProperty("clinic.name", clinicNameField.getText().trim());
|
||||
props.setProperty("clinic.address", clinicAddressField.getText().trim());
|
||||
props.setProperty("clinic.phone", clinicPhoneField.getText().trim());
|
||||
props.setProperty("clinic.prefix", clinicPrefixField.getText().trim());
|
||||
|
||||
// Update settings that don't require restart
|
||||
// Server
|
||||
props.setProperty("server.address", serverAddressField.getText().trim());
|
||||
props.setProperty("server.port", serverPortField.getText().trim());
|
||||
|
||||
// Storage
|
||||
props.setProperty("storage.checkup_media_base_dir", mediaDirectoryField.getText().trim());
|
||||
props.setProperty("storage.ultrasound_folder_path", ultrasoundFolderField.getText().trim());
|
||||
|
||||
// <<< REMOVED: Saving of Google Drive properties
|
||||
|
||||
// App settings (no restart)
|
||||
boolean newAutoStatus = autoChangeStatusCheckBox.isSelected();
|
||||
props.setProperty("app.auto_change_status_to_finished", String.valueOf(newAutoStatus));
|
||||
LocalStorage.autoChangeStatusToFinished = newAutoStatus; // Update immediately
|
||||
LocalStorage.autoChangeStatusToFinished = newAutoStatus;
|
||||
|
||||
// Ensure config directory exists
|
||||
// Save properties to file
|
||||
new File("config").mkdirs();
|
||||
|
||||
// Save properties
|
||||
File configFile = new File("config/config.properties");
|
||||
try (var output = Files.newOutputStream(configFile.toPath())) {
|
||||
props.store(output, "BSK Clinic Management System Configuration - Updated via Settings Dialog");
|
||||
}
|
||||
|
||||
// Check if restart is required
|
||||
boolean restartRequired = mediaChanged || ultrasoundChanged;
|
||||
|
||||
if (restartRequired) {
|
||||
// Show success message with restart required
|
||||
if (restartRequiredChanges) {
|
||||
int choice = JOptionPane.showConfirmDialog(this,
|
||||
"Cài đặt đã được lưu. Một số thay đổi cần khởi động lại ứng dụng để có hiệu lực.\n\nBạn có muốn khởi động lại ngay bây giờ không?",
|
||||
"Cần khởi động lại",
|
||||
@@ -334,28 +312,20 @@ public class SettingsDialog extends JDialog {
|
||||
JOptionPane.QUESTION_MESSAGE);
|
||||
|
||||
if (choice == JOptionPane.YES_OPTION) {
|
||||
// Exit the application
|
||||
System.exit(0);
|
||||
} else {
|
||||
dispose(); // Close dialog after saving
|
||||
dispose();
|
||||
}
|
||||
} else {
|
||||
// No restart required
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Cài đặt đã được lưu thành công.",
|
||||
"Thành công",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Cài đặt đã được lưu thành công.", "Thành công", JOptionPane.INFORMATION_MESSAGE);
|
||||
dispose();
|
||||
}
|
||||
|
||||
hasChanges = false;
|
||||
|
||||
|
||||
} catch (IOException ex) {
|
||||
log.error("Error saving settings", ex);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Lỗi khi lưu cài đặt: " + ex.getMessage(),
|
||||
"Lỗi",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this, "Lỗi khi lưu cài đặt: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,73 @@
|
||||
package BsK.client.ui.component.common;
|
||||
package BsK.client.ui.component.common;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.jdatepicker.*;
|
||||
import org.jdatepicker.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.*;
|
||||
|
||||
public class DateLabelFormatter extends JFormattedTextField.AbstractFormatter {
|
||||
public class DateLabelFormatter extends JFormattedTextField.AbstractFormatter {
|
||||
|
||||
private String datePattern = "dd/MM/yyyy";
|
||||
private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
|
||||
private String datePattern = "dd/MM/yyyy";
|
||||
private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
|
||||
|
||||
public DateLabelFormatter() {
|
||||
// Enable lenient parsing to be more flexible with user input
|
||||
dateFormatter.setLenient(false);
|
||||
}
|
||||
public DateLabelFormatter() {
|
||||
// Enable lenient parsing to be more flexible with user input
|
||||
dateFormatter.setLenient(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object stringToValue(String text) throws ParseException {
|
||||
if (text == null || text.trim().isEmpty()) {
|
||||
@Override
|
||||
public Object stringToValue(String text) throws ParseException {
|
||||
if (text == null || text.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clean up the input text
|
||||
text = text.trim();
|
||||
java.util.Date parsedDate;
|
||||
|
||||
// Handle different input formats that users might type
|
||||
if (text.matches("\\d{1,2}/\\d{1,2}/\\d{4}")) {
|
||||
// Standard format: dd/mm/yyyy or d/m/yyyy
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{1,2}-\\d{1,2}-\\d{4}")) {
|
||||
// Handle dash format: dd-mm-yyyy
|
||||
text = text.replace("-", "/");
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{1,2}\\.\\d{1,2}\\.\\d{4}")) {
|
||||
// Handle dot format: dd.mm.yyyy
|
||||
text = text.replace(".", "/");
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{8}")) {
|
||||
// Handle compact format: ddmmyyyy
|
||||
String day = text.substring(0, 2);
|
||||
String month = text.substring(2, 4);
|
||||
String year = text.substring(4, 8);
|
||||
text = day + "/" + month + "/" + year;
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else {
|
||||
// If none of the formats match, try default parsing
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
}
|
||||
|
||||
if (parsedDate != null) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(parsedDate);
|
||||
return cal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Clean up the input text
|
||||
text = text.trim();
|
||||
java.util.Date parsedDate;
|
||||
@Override
|
||||
public String valueToString(Object value) throws ParseException {
|
||||
if (value != null) {
|
||||
Calendar cal = (Calendar) value;
|
||||
return dateFormatter.format(cal.getTime());
|
||||
}
|
||||
|
||||
// Handle different input formats that users might type
|
||||
if (text.matches("\\d{1,2}/\\d{1,2}/\\d{4}")) {
|
||||
// Standard format: dd/mm/yyyy or d/m/yyyy
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{1,2}-\\d{1,2}-\\d{4}")) {
|
||||
// Handle dash format: dd-mm-yyyy
|
||||
text = text.replace("-", "/");
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{1,2}\\.\\d{1,2}\\.\\d{4}")) {
|
||||
// Handle dot format: dd.mm.yyyy
|
||||
text = text.replace(".", "/");
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else if (text.matches("\\d{8}")) {
|
||||
// Handle compact format: ddmmyyyy
|
||||
String day = text.substring(0, 2);
|
||||
String month = text.substring(2, 4);
|
||||
String year = text.substring(4, 8);
|
||||
text = day + "/" + month + "/" + year;
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
} else {
|
||||
// If none of the formats match, try default parsing
|
||||
parsedDate = (java.util.Date) dateFormatter.parseObject(text);
|
||||
return "";
|
||||
}
|
||||
|
||||
if (parsedDate != null) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(parsedDate);
|
||||
return cal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(Object value) throws ParseException {
|
||||
if (value != null) {
|
||||
Calendar cal = (Calendar) value;
|
||||
return dateFormatter.format(cal.getTime());
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ public class MedicineDialog extends JDialog {
|
||||
|
||||
private void init(final Frame parent) {
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(1200, 700);
|
||||
setSize(1200, 800);
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(true);
|
||||
|
||||
@@ -249,7 +249,7 @@ public class MedicineDialog extends JDialog {
|
||||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 1.0;
|
||||
medicineDescriptionField = new JTextArea(3, 20);
|
||||
medicineDescriptionField = new JTextArea(3, 15);
|
||||
medicineDescriptionField.setFont(textFont);
|
||||
medicineDescriptionField.setLineWrap(true);
|
||||
medicineDescriptionField.setWrapStyleWord(true);
|
||||
@@ -808,11 +808,7 @@ public class MedicineDialog extends JDialog {
|
||||
double price = priceField.getText().isEmpty() ? 0 : Double.parseDouble(priceField.getText());
|
||||
double totalValue = quantity * price;
|
||||
|
||||
// Create formatter for Vietnamese locale
|
||||
NumberFormat formatter = NumberFormat.getInstance(new Locale("vi", "VN"));
|
||||
|
||||
// Format the total and set the text
|
||||
totalField.setText(formatter.format(totalValue));
|
||||
totalField.setText(String.format("%.0f", totalValue));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
totalField.setText("Giá trị không hợp lệ");
|
||||
|
||||
@@ -109,15 +109,24 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
log.debug("Received GetCheckUpQueueRequest");
|
||||
try {
|
||||
ResultSet rs = statement.executeQuery(
|
||||
"select a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,\n" +
|
||||
"d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, a.status, a.customer_id, \n" +
|
||||
"c.customer_number, c.customer_address, a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob,\n" +
|
||||
"a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number\n" +
|
||||
"from checkup as a\n" +
|
||||
"join customer as c on a.customer_id = c.customer_id\n" +
|
||||
"join Doctor D on a.doctor_id = D.doctor_id\n" +
|
||||
"where a.checkup_date >= CAST(strftime('%s', date('now', 'start of day')) AS INTEGER) * 1000\n" +
|
||||
"AND a.checkup_date < CAST(strftime('%s', date('now', 'start of day', '+1 day')) AS INTEGER) * 1000"
|
||||
"""
|
||||
SELECT
|
||||
a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,
|
||||
d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes,
|
||||
a.status, a.customer_id, c.customer_number, c.customer_address,
|
||||
a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob,
|
||||
a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat,
|
||||
a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number
|
||||
FROM
|
||||
checkup AS a
|
||||
JOIN
|
||||
customer AS c ON a.customer_id = c.customer_id
|
||||
JOIN
|
||||
Doctor D ON a.doctor_id = D.doctor_id
|
||||
WHERE
|
||||
a.checkup_date >= CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000
|
||||
AND
|
||||
a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000"""
|
||||
);
|
||||
|
||||
if (!rs.isBeforeFirst()) {
|
||||
@@ -730,56 +739,58 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
Integer newPrescriptionId = null;
|
||||
|
||||
// 2. Handle Medicine Prescriptions
|
||||
|
||||
// First, clear old data for this checkup
|
||||
try (PreparedStatement deleteOrderItemsStmt = conn.prepareStatement("DELETE FROM OrderItem WHERE checkup_id = ?")) {
|
||||
deleteOrderItemsStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
deleteOrderItemsStmt.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement deleteMedicineOrderStmt = conn.prepareStatement("DELETE FROM MedicineOrder WHERE checkup_id = ?")) {
|
||||
deleteMedicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
deleteMedicineOrderStmt.executeUpdate();
|
||||
}
|
||||
log.info("Cleared previous medicine prescription for checkup_id: {}", saveCheckupRequest.getCheckupId());
|
||||
|
||||
// A. Insert a new MedicineOrder record to get the auto-generated ID
|
||||
String medicineOrderSql = """
|
||||
INSERT INTO MedicineOrder (
|
||||
checkup_id, customer_id, total_amount, payment_method,
|
||||
status, payment_status, processed_by, notes
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
// Use RETURN_GENERATED_KEYS to get the new ID back
|
||||
if (saveCheckupRequest.getMedicinePrescription() != null &&
|
||||
saveCheckupRequest.getMedicinePrescription().length > 0) {
|
||||
try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
|
||||
double totalMedicineAmount = 0;
|
||||
for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
|
||||
if (medicine.length > 8) {
|
||||
totalMedicineAmount += Double.parseDouble(medicine[8]);
|
||||
}
|
||||
}
|
||||
|
||||
medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId());
|
||||
medicineOrderStmt.setDouble(3, totalMedicineAmount);
|
||||
medicineOrderStmt.setString(4, ""); // payment_method
|
||||
medicineOrderStmt.setString(5, "Pending"); // status
|
||||
medicineOrderStmt.setString(6, "Unpaid"); // payment_status
|
||||
medicineOrderStmt.setString(7, ""); // processed_by
|
||||
medicineOrderStmt.setString(8, ""); // notes
|
||||
medicineOrderStmt.executeUpdate();
|
||||
|
||||
// B. Retrieve the generated key
|
||||
try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
newPrescriptionId = generatedKeys.getInt(1);
|
||||
log.info("New prescription_id generated: {}", newPrescriptionId);
|
||||
} else {
|
||||
throw new SQLException("Creating medicine order failed, no ID obtained.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First, clear old data for this checkup
|
||||
try (PreparedStatement deleteOrderItemsStmt = conn.prepareStatement("DELETE FROM OrderItem WHERE checkup_id = ?")) {
|
||||
deleteOrderItemsStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
deleteOrderItemsStmt.executeUpdate();
|
||||
}
|
||||
try (PreparedStatement deleteMedicineOrderStmt = conn.prepareStatement("DELETE FROM MedicineOrder WHERE checkup_id = ?")) {
|
||||
deleteMedicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
deleteMedicineOrderStmt.executeUpdate();
|
||||
}
|
||||
log.info("Cleared previous medicine prescription for checkup_id: {}", saveCheckupRequest.getCheckupId());
|
||||
|
||||
// A. Insert a new MedicineOrder record to get the auto-generated ID
|
||||
String medicineOrderSql = """
|
||||
INSERT INTO MedicineOrder (
|
||||
checkup_id, customer_id, total_amount, payment_method,
|
||||
status, payment_status, processed_by, notes
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
// Use RETURN_GENERATED_KEYS to get the new ID back
|
||||
try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) {
|
||||
// ... (your existing totalMedicineAmount calculation) ...
|
||||
double totalMedicineAmount = 0;
|
||||
for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) {
|
||||
if (medicine.length > 8) { totalMedicineAmount += Double.parseDouble(medicine[8]); }
|
||||
}
|
||||
|
||||
medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId());
|
||||
medicineOrderStmt.setDouble(3, totalMedicineAmount);
|
||||
medicineOrderStmt.setString(4, ""); // payment_method
|
||||
medicineOrderStmt.setString(5, "Pending"); // status
|
||||
medicineOrderStmt.setString(6, "Unpaid"); // payment_status
|
||||
medicineOrderStmt.setString(7, ""); // processed_by
|
||||
medicineOrderStmt.setString(8, ""); // notes
|
||||
medicineOrderStmt.executeUpdate();
|
||||
|
||||
// B. Retrieve the generated key
|
||||
try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
newPrescriptionId = generatedKeys.getInt(1);
|
||||
log.info("New prescription_id generated: {}", newPrescriptionId);
|
||||
} else {
|
||||
throw new SQLException("Creating medicine order failed, no ID obtained.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C. Insert OrderItems using the new prescription_id
|
||||
String orderItemSql = """
|
||||
@@ -812,7 +823,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
// 3. Insert/Update Checkup with the correct newPrescriptionId
|
||||
String checkupSql = """
|
||||
INSERT INTO Checkup (
|
||||
checkup_id, customer_id, doctor_id, checkup_date, suggestion, diagnosis,
|
||||
checkup_date, checkup_id, customer_id, doctor_id, suggestion, diagnosis,
|
||||
prescription_id, notes, status, checkup_type, conclusion, reCheckupDate,
|
||||
customer_weight, customer_height, heart_beat, blood_pressure, doctor_ultrasound_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -823,14 +834,15 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
conclusion = excluded.conclusion, reCheckupDate = excluded.reCheckupDate,
|
||||
customer_weight = excluded.customer_weight, customer_height = excluded.customer_height,
|
||||
heart_beat = excluded.heart_beat, blood_pressure = excluded.blood_pressure,
|
||||
doctor_ultrasound_id = excluded.doctor_ultrasound_id, doctor_id = excluded.doctor_id
|
||||
doctor_ultrasound_id = excluded.doctor_ultrasound_id, doctor_id = excluded.doctor_id,
|
||||
checkup_date = excluded.checkup_date
|
||||
""";
|
||||
|
||||
try (PreparedStatement checkupStmt = conn.prepareStatement(checkupSql)) {
|
||||
checkupStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
checkupStmt.setInt(2, saveCheckupRequest.getCustomerId());
|
||||
checkupStmt.setInt(3, saveCheckupRequest.getDoctorId());
|
||||
checkupStmt.setLong(4, saveCheckupRequest.getCheckupDate());
|
||||
checkupStmt.setLong(1, saveCheckupRequest.getCheckupDate());
|
||||
checkupStmt.setInt(2, saveCheckupRequest.getCheckupId());
|
||||
checkupStmt.setInt(3, saveCheckupRequest.getCustomerId());
|
||||
checkupStmt.setInt(4, saveCheckupRequest.getDoctorId());
|
||||
checkupStmt.setString(5, saveCheckupRequest.getSuggestions());
|
||||
checkupStmt.setString(6, saveCheckupRequest.getDiagnosis());
|
||||
// Use setObject to handle the case where newPrescriptionId is null
|
||||
@@ -852,9 +864,30 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
checkupStmt.executeUpdate();
|
||||
log.info("Checkup saved successfully");
|
||||
}
|
||||
|
||||
// 4. Handle Service Prescriptions (Now safe with try-with-resources)
|
||||
// ... (your existing service handling logic is fine, just wrap it) ...
|
||||
|
||||
// 4. Handle Service Prescriptions
|
||||
// clear old data
|
||||
try (PreparedStatement deleteServiceStmt = conn.prepareStatement("DELETE FROM CheckupService WHERE checkup_id = ?")) {
|
||||
deleteServiceStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
deleteServiceStmt.executeUpdate();
|
||||
}
|
||||
log.info("Cleared previous service prescription for checkup_id: {}", saveCheckupRequest.getCheckupId());
|
||||
|
||||
String serviceSql = """
|
||||
INSERT INTO CheckupService (checkup_id, service_id, quantity, total_cost, notes)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""";
|
||||
try (PreparedStatement serviceStmt = conn.prepareStatement(serviceSql)) {
|
||||
for (String[] service : saveCheckupRequest.getServicePrescription()) {
|
||||
serviceStmt.setInt(1, saveCheckupRequest.getCheckupId());
|
||||
serviceStmt.setString(2, service[0]);
|
||||
serviceStmt.setInt(3, Integer.parseInt(service[2]));
|
||||
serviceStmt.setDouble(4, Double.parseDouble(service[4]));
|
||||
serviceStmt.setString(5, service[5]);
|
||||
serviceStmt.executeUpdate();
|
||||
}
|
||||
log.info("Service saved successfully");
|
||||
}
|
||||
|
||||
conn.commit(); // Commit the entire transaction
|
||||
log.info("Successfully saved checkup {} with all related data", saveCheckupRequest.getCheckupId());
|
||||
@@ -960,7 +993,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
S.service_name,
|
||||
CS.quantity,
|
||||
S.service_cost,
|
||||
CS.total_cost
|
||||
CS.total_cost,
|
||||
CS.notes
|
||||
FROM CheckupService CS
|
||||
JOIN Service S ON CS.service_id = S.service_id
|
||||
WHERE CS.checkup_id = ?
|
||||
@@ -977,7 +1011,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
ser[2] = serRs.getString("quantity");
|
||||
ser[3] = serRs.getString("service_cost");
|
||||
ser[4] = serRs.getString("total_cost");
|
||||
ser[5] = ""; // Notes - not available in CheckupService table
|
||||
ser[5] = serRs.getString("notes");
|
||||
serList.add(ser);
|
||||
}
|
||||
servicePrescription = serList.toArray(new String[0][]);
|
||||
@@ -2169,8 +2203,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
"from checkup as a\n" +
|
||||
"join customer as c on a.customer_id = c.customer_id\n" +
|
||||
"join Doctor D on a.doctor_id = D.doctor_id\n" +
|
||||
"where a.checkup_date >= CAST(strftime('%s', date('now', 'start of day')) AS INTEGER) * 1000\n" +
|
||||
"AND a.checkup_date < CAST(strftime('%s', date('now', 'start of day', '+1 day')) AS INTEGER) * 1000"
|
||||
"where a.checkup_date >= CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000\n" +
|
||||
"AND a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000"
|
||||
);
|
||||
|
||||
String[][] resultArray;
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user