Try   HackMD

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
  • sysos: 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:

pip install PySide6

3.3. Chạy chương trình

Mở Terminal và chạy lệnh sau:

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, DonHangChiTietDonHang.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

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

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • 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:

# 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:

# 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):

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):

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:

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

3.2. Giao diện chính và thống kê

image

3.3. Giao diện danh sách sản phẩm

image

3.4. Giao diện chi tiết sản phẩm

image

3.5. Giao diện danh sách nhân viên

image

3.6. Giao diện chi tiết nhân viên

image

3.7. Giao diện danh sách đơn hàng

image

3.8. Giao diện chi tiết đơn hàng

image

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

  • 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
  • Thử tìm kiếm với từ khoá "Áo", sắp xếp ID giảm dần và đến trang 2:
    image

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
  • 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

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
  • 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

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
  • Thử sắp xếp tên tăng dần:
    image

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
  • 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

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
  • 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

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
  • Thử sắp xếp ID giảm dần:
    image

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
  • 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

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

    image
  • Thử chuyển trạng thái đơn hàng sang đang vận chuyển (nút Giao hàng):
    image
  • Hoặc có thể chuyển sang trạng thái huỷ đơn (nút Huỷ đơn):
    image

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

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