# Quản lý bán hàng ## Chương 1: Cơ sở lý thuyết ### 1. Các công cụ được sử dụng để xây dựng phần mềm - Python 3.12: Ngôn ngữ lập trình chính - Visual Studio Code và PyCharm: Công cụ lập trình và debug code - Database: SQLite3 ### 2. Các module/thư viện được sử dụng - `PyQt6`: Thư viện xây dựng giao diện đồ họa (GUI) cho ứng dụng Python (PyQt6 là phiên bản mới nhất của PyQt) - `hashlib`: Thư viện mã hóa mật khẩu - `sqlite3`: Thư viện kết nối và thao tác với database SQLite3 - `datetime`: Thư viện xử lý thời gian - `sys` và `os`: Thư viện xử lý các tác vụ liên quan đến hệ thống và hệ điều hành - `shutil`: Thư viện xử lý các tác vụ liên quan đến file và thư mục ### 3. Hướng dẫn cài đặt môi trường và chạy chương trình #### 3.1. Cài đặt Python 3.12 - Truy cập trang chủ của Python tại trang chủ (https://www.python.org/downloads/) - Tải về phiên bản Python 3.12 phù hợp với hệ điều hành của bạn #### 3.2. Cài đặt PyQt6 Mở Terminal và chạy lệnh sau: ```bash pip install PySide6 ``` #### 3.3. Chạy chương trình Mở Terminal và chạy lệnh sau: ```bash python main.py ``` ## Chương 2: Xây dựng chương trình ### 1. Cấu trúc dữ liệu Dưới đây là mô hình cơ sở dữ liệu của chương trình, gồm 4 bảng: `NhanVien`, `SanPham`, `DonHang` và `ChiTietDonHang`. ![DBDiagram](https://hackmd.io/_uploads/BJQaODyUC.png) Mô tả các bảng: - `NhanVien`: Lưu thông tin của nhân viên gồm các trường: `id`, `ten_nv`, `dia_chi`, `sdt`, `email`, `vai_tro`, `ten_dang_nhap`, `mat_khau`, `quyen_han` lần lượt là mã định danh, tên nhân viên, địa chỉ, số điện thoại, email, vai trò (nhân viên, quản lý, chủ cửa hàng,...), tên đăng nhập, mật khẩu và quyền hạn trên ứng dụng (phân hệ quản trị viên và người dùng bình thuường). - `SanPham`: Lưu thông tin của sản phẩm gồm các trường: `id`, `ten_sp`, `so_luong`, `don_gia`, `hinh_anh`, `mo_ta` lần lượt là mã định danh, tên sản phẩm, số lượng, đơn giá, hình ảnh và mô tả sản phẩm. - `DonHang`: Lưu thông tin của đơn hàng gồm các trường: `id`, `nguoi_mua`, `sdt_nguoi_mua`, `dia_chi_giao_hang`, `tong_tien`, `trang_thai`, `created_at` lần lượt là mã định danh, tên người mua, số điện thoại người mua, địa chỉ giao hàng, tổng tiền, trạng thái đơn hàng, thời gian tạo đơn. Ở đây trạng thái có 4 giá trị: `Chờ xác nhận`, `Đang giao`, `Đã giao`, `Đã hủy`. - `ChiTietDonHang`: Lưu thông tin chi tiết của đơn hàng gồm các trường: `id`, `id_don_hang`, `id_san_pham`, `so_luong`, `don_gia` lần lượt là mã định danh, mã đơn hàng, mã sản phẩm, số lượng sản phẩm, đơn giá sản phẩm. ### 2. Cấu trúc mã nguồn chương trình ![CTCT](https://hackmd.io/_uploads/HkJE2vkLR.png) - `main.py`: Chương trình sẽ chạy từ file này, chứa cấu hình và khởi tạo ứng dụng. - `db`: Thư mục chứa file `db.py` để kết nối và thao tác với cơ sở dữ liệu. - `models`: Thư mục chứa các file `*.py` để định nghĩa các lớp tương ứng với các bảng trong cơ sở dữ liệu. - `products`: Thư mục chứa tài nguyên ảnh cho sản phẩm. - `resources`: Thư mục chứa các file tài nguyên như icon, ảnh xuất hiện trong giao diện. - `states`: Thư mục chứa các file `*.py` để định nghĩa các trạng thái của các đối tượng trong ứng dụng. Hiện tại có file `UserState.py` định nghĩa trạng thái của người dùng đã đăng nhập (là thông tin đăng nhập của người dùng đó trong xuyên suốt ứng dụng). - `utils`: Thư mục chứa các file `*.py` để định nghĩa các hàm tiện ích. - `dialogs`: Chứa các giao diện dạng dialog cho ứng dụng. Chia làm các thư mục con tương ứng với các màn hình cụ thể như `login` (màn hình đăng nhập), `main` (màn hình chính), `products` (màn hình quản lý sản phẩm), `orders` (màn hình quản lý đơn hàng), `employees` (màn hình quản lý nhân viên). - `shop.db`: Tập tin CSDL SQLite3. Dưới đây ta sẽ trình bày cấu trúc các file mã nguồn trong chương trình. Tuy nhiên vì một số xử lý phức tạp, ta sẽ vắn tắt trình bày một số phần quan trọng. Đầy đủ mã nguồn có thể xem tại các file mã nguồn trong các thư mục tương ứng như đã trình bày phía trên. #### 2.1. Các lớp mô tả dữ liệu Các lớp mô tả dữ liệu được định nghĩa trong thư mục `models`, mỗi file `*.py` tương ứng với một bảng trong cơ sở dữ liệu. Mỗi lớp mô tả dữ liệu sẽ có các phương thức để thao tác với dữ liệu trong bảng đó. Ví dụ với lớp `DonHang`, ta sẽ có một số phương thức với các tính năng như sau: ```python # Các trạng thái của đơn hàng TrangThaiDonHang = { "DANG_XU_LY": 1, "DANG_GIAO": 2, "DA_GIAO": 3, "DA_HUY": 4 } class DonHang(): def __init__(self, nguoi_mua, sdt_nguoi_mua, dia_chi_giao_hang): self.nguoi_mua = nguoi_mua self.sdt_nguoi_mua = sdt_nguoi_mua self.dia_chi_giao_hang = dia_chi_giao_hang self.tong_tien = 0 self.trang_thai = TrangThaiDonHang["DANG_XU_LY"] def update(self, nguoi_mua, sdt_nguoi_mua, dia_chi_giao_hang): # Cập nhật thông tin đơn hàng pass def dang_giao_hang(self): # Cập nhật trạng thái đơn hàng đang giao pass def da_giao_hang(self): # Cập nhật trạng thái đơn hàng đã giao pass def huy_don_hang(self): # Hủy đơn hàng pass def them_sp(self, id_san_pham, so_luong, don_gia): # Thêm sản phẩm vào đơn hàng pass def cap_nhat_sp(self, id_san_pham, so_luong, don_gia): # Cập nhật số lượng và đơn giá của sản phẩm trong đơn hàng pass def xoa_sp(self, id_san_pham): # Xóa sản phẩm có mã `id_san_pham` khỏi đơn hàng pass @classmethod def tong_doanh_thu(cls): # Tính tổng doanh thu của cửa hàng pass ``` #### 2.2. Các lớp mô tả giao diện Các lớp mô tả giao diện được định nghĩa trong thư mục `dialogs`, mỗi file `*.py` tương ứng với một màn hình cụ thể trong ứng dụng. Mỗi lớp mô tả giao diện sẽ có các phương thức để hiển thị và xử lý sự kiện của giao diện đó. Ví dụ với lớp `LoginDialog`, ta sẽ có một số phương thức với các tính năng như sau: ```python # Lớp mô tả giao diện đăng nhập kế thừa từ lớp QDialog (lớp cơ sở của các dialog trong PyQt6) class LoginDialog(QDialog): def __init__(self, parent=None): super(LoginDialog, self).__init__(parent) # Khởi tạo giao diện self.setFixedSize(600, 600) # ... # Tạo layout chính cho dialog self.layout = QStackedLayout() self.layout.setStackingMode(QStackedLayout.StackingMode.StackAll) self.setLayout(self.layout) # Tạo tiêu đề cho dialog self.setWindowTitle("Đăng nhập") # Tạo các widget trên dialog như username (QLineEdit), password (QLineEdit), login (QPushButton) # ... # Kết nối sự kiện click cho nút login self.login.clicked.connect(self.dang_nhap) def dang_nhap(self): # Xử lý sự kiện đăng nhập: kiểm tra thông tin đăng nhập và chuyển đến màn hình chính nếu đăng nhập thành công # Nếu đăng nhập thất bại, hiển thị thông báo lỗi if NhanVien.dang_nhap(self.username.text(), self.password.text()): UserState.set_user(NhanVien.fetch_all(where={"ten_dang_nhap": self.username.text()})[0]) self.accept() else: QMessageBox.warning(self, "Thông báo", "Đăng nhập thất bại") ``` #### 2.3. Các hàm tiện ích Các hàm tiện ích được định nghĩa trong thư mục `utils`, mỗi file `*.py` tương ứng với một chức năng cụ thể trong ứng dụng. Các hàm tiện ích này sẽ giúp cho việc xử lý dữ liệu và giao diện trở nên dễ dàng hơn. Ví dụ với hàm `hash_password` để mã hóa mật khẩu (mã hoá md5 bằng thư viện `hashlib`): ```python def hash_password(password): if password is None: return None return hashlib.md5(password.encode()).hexdigest() ``` #### 2.4. Các trạng thái của ứng dụng Các trạng thái của ứng dụng được định nghĩa trong thư mục `states`, mỗi file `*.py` tương ứng với một trạng thái cụ thể của ứng dụng. Hiện tại có file `UserState.py` định nghĩa trạng thái của người dùng đã đăng nhập (là thông tin đăng nhập của người dùng đó trong xuyên suốt ứng dụng): ```python class UserState: user = None @classmethod def set_user(cls, user): cls.user = user @classmethod def get_user(cls): return cls.user ``` Đây là một singleton để lưu trữ thông tin người dùng đã đăng nhập, giúp cho việc truy cập thông tin người dùng dễ dàng hơn. #### 2.5. Các hàm xử lý kết nối và thao tác với cơ sở dữ liệu Các hàm xử lý kết nối và thao tác với cơ sở dữ liệu được định nghĩa trong file `db.py` trong thư mục `db`. Các hàm này sẽ giúp cho việc kết nối và thao tác với cơ sở dữ liệu trở nên dễ dàng hơn. Ví dụ với hàm `create_db` để tạo cơ sở dữ liệu và bảng trong cơ sở dữ liệu, lớp `DbConnection` để kết nối và thao tác với cơ sở dữ liệu: ```python from sqlite3 import connect def create_db(): conn = connect('shop.db') # Tạo bảng DonHang conn.execute(''' CREATE TABLE IF NOT EXISTS DonHang ( id INTEGER PRIMARY KEY AUTOINCREMENT, nguoi_mua TEXT NOT NULL, sdt_nguoi_mua TEXT NOT NULL, dia_chi_giao_hang TEXT NOT NULL, trang_thai INTEGER NOT NULL DEFAULT 1, tong_tien NUMERIC NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ''') # Tạo các bảng khác # ... # Lưu thay đổi và đóng kết nối conn.commit() conn.close() class DbConnection: _instance = None # Hàm khởi tạo kết nối đến cơ sở dữ liệu khi lớp được khởi tạo def __new__(cls): if cls._instance is None: create_db() cls._instance = connect('shop.db') cls._instance.execute('PRAGMA foreign_keys = ON;') return cls._instance # Khi đối tượng bị hủy, đóng kết nối def __del__(self): self._instance.close() @classmethod def insert(cls, self): # Hàm thực thi câu lệnh SQL dạng INSERT pass @classmethod def delete(cls, self): # Hàm thực thi câu lệnh SQL dạng DELETE pass @classmethod def update(cls, self): # Hàm thực thi câu lệnh SQL dạng UPDATE pass @classmethod def fetch_all(cls, where, order_by, offset, limit, like): # Hàm thực thi câu lệnh SQL dạng SELECT và trả về tất cả kết quả # Có thể thêm điều kiện WHERE, ORDER BY, OFFSET, LIMIT, LIKE pass @classmethod def count(cls, where, like): # Hàm thực thi câu lệnh SQL dạng SELECT và trả về số lượng kết quả # Có thể thêm điều kiện WHERE, LIKE pass @classmethod def sum(cls, param, where): # Hàm thực thi câu lệnh SQL dạng SELECT và trả về tổng giá trị của cột `param` # Có thể thêm điều kiện WHERE pass ``` ### 3. Các giao diện chính của ứng dụng #### 3.1. Giao diện đăng nhập ![image](https://hackmd.io/_uploads/BkZL1DxL0.png) #### 3.2. Giao diện chính và thống kê ![image](https://hackmd.io/_uploads/r1Ht1wlLA.png) #### 3.3. Giao diện danh sách sản phẩm ![image](https://hackmd.io/_uploads/rJeFgDx8C.png) #### 3.4. Giao diện chi tiết sản phẩm ![image](https://hackmd.io/_uploads/S1kjewgIA.png) #### 3.5. Giao diện danh sách nhân viên ![image](https://hackmd.io/_uploads/HJ7axweIA.png) #### 3.6. Giao diện chi tiết nhân viên ![image](https://hackmd.io/_uploads/Hys0lDxU0.png) #### 3.7. Giao diện danh sách đơn hàng ![image](https://hackmd.io/_uploads/SJXREvg80.png) #### 3.8. Giao diện chi tiết đơn hàng ![image](https://hackmd.io/_uploads/B13-rPxLA.png) ## Chương 3: Kết quả thực nghiệm của chương trình ### 1. Chức năng đăng nhập - Thử với thông tin đăng nhập sai: nhận thông báo đăng nhập thất bại ![image](https://hackmd.io/_uploads/r1ce2PxIC.png) - Thử với thông tin đăng nhập đúng: chuyển đến màn hình chính ### 2. Chức năng quản lý sản phẩm #### 2.1. Hiển thị danh sách sản phẩm Bấm vào nút "Sản phẩm" tại màn hình chính, màn hình xem danh sách sản phẩm sẽ hiện ra, ta có thể tìm kiếm, lọc và di chuyển giữa các trang. - Thử tìm kiếm với từ khoá "jeans": ![image](https://hackmd.io/_uploads/BkjqhveU0.png) - Thử tìm kiếm với từ khoá "Áo", sắp xếp ID giảm dần và đến trang 2: ![image](https://hackmd.io/_uploads/H1CR3ve8A.png) #### 2.2. Thêm sản phẩm mới (cho quan hệ quản trị viên) Để thêm sản phẩm mới, ta chọn vào màn hình "Sản phẩm", lựa chọn nút thêm mới (dấu cộng màu trắng nền xanh ở góc dưới bên phải của màn hình xem danh sách). Sau đó điền các thông tin. - Thử thêm một sản phẩm mới nhưng chưa điền đầy đủ thông tin: ![image](https://hackmd.io/_uploads/HJhXRvlUC.png) - Nếu điền đầy đủ thông tin, sản phẩm mới sẽ được thêm và xuất hiện tại màn hình danh sách: ![image](https://hackmd.io/_uploads/S1jwCwlUC.png) #### 2.3. Sửa/Xoá sản phẩm (cho phân hệ quản trị viên) Trong màn hình danh sách sản phẩm, ta lựa chọn một sản phẩm bất kỳ và tiến hành sửa thông tin hoặc xoá hẳn sản phẩm. - Thử chỉnh sửa giá của sản phầm vừa thêm và kết quả sẽ cập nhật tại màn hình danh sách: ![image](https://hackmd.io/_uploads/rJiWk_lU0.png) - Thử xoá hẳn sản phẩm này, màn hình danh sách sẽ không còn xuất hiện nữa: ![image](https://hackmd.io/_uploads/Hys91OlIA.png) ### 3. Chức năng quản lý nhân viên #### 3.1. Hiển thị danh sách nhân viên Bấm vào nút "Nhân viên" tại màn hình chính, màn hình xem danh sách nhân viên sẽ hiện ra, ta có thể tìm kiếm, lọc và di chuyển giữa các trang. - Thử tìm kiếm với từ khoá "Nguyễn": ![image](https://hackmd.io/_uploads/HyazeOeLA.png) - Thử sắp xếp tên tăng dần: ![image](https://hackmd.io/_uploads/SycSxueU0.png) #### 3.2. Thêm nhân viên mới (cho phân hệ quản trị viên) Để thêm nhân viên mới, ta chọn vào màn hình "Nhân viên", lựa chọn nút thêm mới (dấu cộng màu trắng nền xanh ở góc dưới bên phải của màn hình xem danh sách). Sau đó điền các thông tin. - Thử thêm một nhân viên mới nhưng chưa điền đầy đủ thông tin: ![image](https://hackmd.io/_uploads/rJnzWulUA.png) - Nếu điền đầy đủ thông tin, nhân viên mới sẽ được thêm và xuất hiện tại màn hình danh sách: ![image](https://hackmd.io/_uploads/S1dU-dx80.png) #### 3.3. Sửa/Xoá nhân viên (cho phân hệ quản trị viên) Trong màn hình danh sách nhân viên, ta lựa chọn một nhân viên bất kỳ và tiến hành sửa thông tin hoặc xoá hẳn nhân viên, ngoài ra mật khẩu cho tài khoản nhân viên cũng có thể được cài lại ở đây. - Thử chỉnh sửa giá của nhân viên vừa thêm và kết quả sẽ cập nhật tại màn hình danh sách: ![image](https://hackmd.io/_uploads/ryAvzOl8R.png) - Thử xoá hẳn sản phẩm này, màn hình danh sách sẽ không còn xuất hiện nữa: ![image](https://hackmd.io/_uploads/r1atfOlU0.png) ### 4. Chức năng quản lý đơn hàng #### 4.1. Hiển thị danh sách đơn hàng Bấm vào nút "Đơn hàng" tại màn hình chính, màn hình xem danh sách sản phẩm sẽ hiện ra, ta có thể tìm kiếm, lọc và di chuyển giữa các trang. Mỗi đơn hàng có một trạng thái thể hiện thông qua icon nằm bên phải thông tin: có 4 loại trạng thái (đang chờ xử lý - icon đồng hồ màu vàng, đang vận chuyển - xe tải màu xanh lá, đã giao hàng - dấu tích màu xanh, đã huỷ - dấu X màu đỏ). - Thử tìm kiếm với từ khoá "2", đơn hàng có mã là 2 sẽ xuất hiện ![image](https://hackmd.io/_uploads/BJyB7_xLA.png) - Thử sắp xếp ID giảm dần: ![image](https://hackmd.io/_uploads/ByYd7_gUR.png) #### 4.2. Thêm đơn hàng mới (cho phân hệ quản trị viên) Để thêm đơn hàng mới, ta chọn vào màn hình "Đơn hàng", lựa chọn nút thêm mới (dấu cộng màu trắng nền xanh ở góc dưới bên phải của màn hình xem danh sách). Sau đó điền các thông tin. - Thử thêm một đơn hàng mới nhưng không chọn sản phẩm nào ![image](https://hackmd.io/_uploads/r1BSE_xLA.png) - Nếu điền đầy đủ thông tin, đơn hàng mới sẽ xuất hiện, giá sẽ được tính, đơn hàng ở trạng thái chờ xử lý và xuất hiện tại màn hình danh sách: ![image](https://hackmd.io/_uploads/rJQoE_gUC.png) #### 4.3. Sửa thông tin đơn hàng và cập nhật trạng thái (cho phân hệ quản trị viên) Trong màn hình danh sách đơn hàng, ta lựa chọn một đơn hàng bất kỳ và tiến hành sửa thông tin, điều chỉnh số lượng hoặc chuyển sang trạng thái kế tiếp cho đơn hàng. - Thử chỉnh sửa số lượng sản phầm của một sản phẩm bất kỳ về 0 (xoá sản phẩm đó), lưu lại đơn hàng và kết quả sẽ cập nhật tại màn hình danh sách: ![image](https://hackmd.io/_uploads/HJwk8ug8A.png) ![image](https://hackmd.io/_uploads/B1x8S_x8A.png) - Thử chuyển trạng thái đơn hàng sang đang vận chuyển (nút Giao hàng): ![image](https://hackmd.io/_uploads/S1kiSOeIA.png) - Hoặc có thể chuyển sang trạng thái huỷ đơn (nút Huỷ đơn): ![image](https://hackmd.io/_uploads/S1TgUOgU0.png) ### 5. Chức năng thống kê và đăng xuất Ngoài ra, tại giao diện chính ứng dụng còn hỗ trợ thống kê số lượng và doanh thu các đơn hàng thành công cho cửa hàng: ![image](https://hackmd.io/_uploads/ryZ88OlI0.png) Hoặc nếu muốn đăng xuất tài khoản hiện tại, ta chọn vào nút đỏ ở góc dưới bên trái màn hình và xác nhận đăng xuất, giao diện đăng nhập sẽ hiện ra trở lại: ![image](https://hackmd.io/_uploads/HyM5LOxUC.png)