duongvanthanh2011
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # 1. Module Quản lý nhân sự ### **a. Thông tin cá nhân cơ bản** * Họ và tên * Mã nhân viên * Ngày sinh * Giới tính * Số CMND/CCCD/Hộ chiếu * Địa chỉ liên hệ * Số điện thoại * Email * Ảnh nhân viên (nếu cần) * Trạng thái --- ### **b. Thông tin công việc** * Phòng ban / bộ phận * Chức danh / vị trí công việc * Loại hợp đồng (dài hạn, thời vụ, thử việc…) * Hình thức trả lương (theo tháng, ngày, giờ, sản phẩm…) * Ngày bắt đầu làm việc * Ngày kết thúc (nếu có) * Mã số thuế TNCN * Số tài khoản ngân hàng + Ngân hàng (để chuyển lương) --- ### **c. Thông tin phục vụ tính lương** * **Mức lương cơ bản** (hoặc đơn giá theo giờ/ngày/sản phẩm) * **Phụ cấp** (ăn trưa, xăng xe, nhà ở, điện thoại…) * **Khoản thưởng** mặc định hàng tháng (nếu có) * **Khoản phạt** mặc định (nếu có, ví dụ tiền ứng trước trừ dần) * Tỷ lệ đóng bảo hiểm (BHXH, BHYT, BHTN) * Tỷ lệ thuế TNCN (nếu phải nộp) * Số ngày phép còn lại trong năm * Ca làm việc tiêu chuẩn (ca sáng, ca tối, ca kíp…) --- ### **d. Thông tin chấm công & sản lượng** * Số ngày công thực tế * Số giờ làm thêm / tăng ca * Sản lượng sản phẩm (nếu tính theo sản phẩm) * Ngày nghỉ có phép / không phép * Các loại nghỉ khác (ốm, thai sản, nghỉ bù…) --- ### **e. Thông tin khác hỗ trợ quản lý** * Người quản lý trực tiếp * Mã máy chấm công (nếu dùng máy quét vân tay/khuôn mặt) * Tình trạng làm việc (đang làm, nghỉ phép dài hạn, nghỉ việc…) # **2. Module Quản lý Thành phần Lương (Salary Components)** Đây là "trái tim" của sự linh hoạt. Hệ thống cho phép người dùng tự tạo và quản lý các "biến số" sẽ được sử dụng trong công thức tính lương. * **Phân loại thành phần:** * **Thu nhập (Earnings):** Bao gồm các khoản làm tăng tổng thu nhập của nhân viên. * *Ví dụ:* Lương cơ bản, Lương theo giờ, Phụ cấp ăn trưa, Phụ cấp đi lại, Thưởng KPI, Thưởng doanh số, Tiền làm thêm giờ (OT). * **Khấu trừ (Deductions):** Bao gồm các khoản làm giảm tổng thu nhập. * *Ví dụ:* Bảo hiểm xã hội (BHXH), Bảo hiểm y tế (BHYT), Bảo hiểm thất nghiệp (BHTN), Thuế thu nhập cá nhân (TNCN), Các khoản tạm ứng, Khoản phạt. * **Thuộc tính của mỗi thành phần:** * **Mã thành phần:** Một mã định danh duy nhất (ví dụ: `LUONG_CO_BAN`, `PHU_CAP_AN_TRUA`). * **Tên thành phần:** Tên gọi dễ hiểu (ví dụ: "Lương cơ bản", "Phụ cấp ăn trưa"). * **Loại dữ liệu:** Số (Number), Chuỗi (Text), Ngày tháng (Date), Logic (Boolean - True/False). * **Nguồn dữ liệu:** * **Cố định (Fixed):** Giá trị được nhập tay và cố định cho từng nhân viên hoặc nhóm nhân viên (ví dụ: Phụ cấp ăn trưa 800,000 VNĐ/tháng). * **Từ hệ thống (System-based):** Tự động lấy từ các module khác. * *Ví dụ:* Lương cơ bản (lấy từ module Nhân sự), Ngày công thực tế (lấy từ module Chấm công), Doanh số đạt được (lấy từ module KPI/Bán hàng). * **Theo công thức (Formula-based):** Giá trị được tính toán từ các thành phần khác. # **3. Module Công cụ xây dựng Công thức (Formula Builder)** * **Giao diện Nhập công thức hoặc Gợi ý thông minh (IntelliSense):** Cho phép người dùng dễ dàng chọn các "Mã thành phần" đã tạo, các toán tử và các hàm có sẵn. * **Các toán tử hỗ trợ:** * **Toán tử số học:** `+`, `-`, `*`, `/`. * **Toán tử so sánh:** `>`, `<`, `>=`, `<=`, `==`, `!=`. * **Toán tử logic:** `AND`, `OR`, `NOT`. * **Thư viện hàm (Function Library):** Cung cấp các hàm tính toán phổ biến. * **Hàm logic:** `IF(điều kiện, giá trị đúng, giá trị sai)`. Ví dụ: `IF([KPI_Dat_Chi_Tieu] == TRUE, 500000, 0)` - Nếu đạt KPI thì thưởng 500,000, ngược lại thì 0. * **Hàm làm tròn:** `ROUND()`, `ROUNDUP()`, `ROUNDDOWN()`. * **Hàm tìm giá trị:** `MIN()`, `MAX()`. # **4. Module Quản lý Bảng lương & Kỳ lương (Payroll Period & Payslip)** * **Thiết lập kỳ lương:** Cho phép tạo các kỳ lương khác nhau (tháng, tuần) và áp dụng các chính sách lương/công thức khác nhau cho từng kỳ. * **Áp dụng chính sách lương:** Gán một hoặc nhiều bộ công thức (chính sách lương) cho các nhóm đối tượng khác nhau (ví dụ: Chính sách lương cho khối văn phòng, chính sách lương cho khối sản xuất, chính sách lương cho nhân viên thử việc). * **Quy trình chạy lương:** 1. **Chốt dữ liệu:** Hệ thống "chốt" dữ liệu từ các module liên quan (chấm công, KPI) tại một thời điểm nhất định. 2. **Tính toán tự động:** Hệ thống thực thi các công thức đã thiết lập cho từng nhân viên. 3. **Xem trước và rà soát:** Cho phép bộ phận C&B xem lại bảng lương nháp, kiểm tra và thực hiện các điều chỉnh cuối cùng (nếu cần). 4. **Phê duyệt và ban hành:** Sau khi đã chính xác, bảng lương được phê duyệt và gửi đến từng nhân viên. * **Phiếu lương cá nhân (Payslip):** Tự động tạo phiếu lương chi tiết cho từng nhân viên, thể hiện rõ ràng các khoản thu nhập, khấu trừ và công thức đã áp dụng. Nhân viên có thể truy cập qua cổng thông tin nhân viên (Employee Portal). # **5. Module Chấm công & Quản lý thời gian (Time & Attendance)** * Tích hợp với máy chấm công hoặc cho phép chấm công qua ứng dụng di động, web. * Quản lý đơn từ: Đơn xin nghỉ phép, đơn xin đi muộn/về sớm, đơn làm thêm giờ. * Tổng hợp dữ liệu và cung cấp các "biến số" cho module tính lương, ví dụ: `Ngay_Cong_Thuc_Te`, `So_Gio_Lam_Them`, `So_Ngay_Nghi_Phep`. # **6. Module Báo cáo & Phân tích (Reporting & Analytics)** * Xuất các báo cáo lương theo mẫu chuẩn của cơ quan nhà nước. * Tạo các báo cáo quản trị: Báo cáo chi phí lương theo phòng ban, biến động lương qua các kỳ, phân tích cơ cấu thu nhập... --- # **Ví dụ minh họa cách hoạt động** **Bài toán:** Công ty A muốn thiết lập công thức tính "Lương thực nhận" cho nhân viên văn phòng. **Bước 1: Định nghĩa các Thành phần lương** Người quản trị vào **Module Quản lý Thành phần lương** và tạo/kiểm tra các biến số: | Mã thành phần | Tên thành phần | Loại | Nguồn dữ liệu | Ví dụ giá trị | | :--- | :--- | :--- | :--- | :--- | | `LCB` | Lương Cơ Bản | Số | Từ Module Nhân sự | 10,000,000 | | `PC_ANTRUA` | Phụ cấp ăn trưa | Số | Cố định | 800,000 | | `NGAY_CONG_CHUAN` | Ngày công chuẩn | Số | Cố định | 24 | | `NGAY_CONG_THUCTE`| Ngày công thực tế | Số | Từ Module Chấm công | 23.5 | | `THUONG_KPI` | Thưởng KPI | Số | Nhập tay/Theo công thức| 500,000 | | `TAM_UNG` | Tạm ứng | Số | Nhập tay | 1,000,000 | | `BHXH`, `BHYT`, `BHTN` | Các khoản Bảo hiểm | Theo công thức | Theo công thức | - | | `GIAM_TRU_GIACANH`| Giảm trừ gia cảnh | Số | Cố định | 11,000,000 | | `SO_NGUOI_PHUTHUOC` | Số người phụ thuộc | Số | Từ Module Nhân sự | 1 | | `GIAM_TRU_NPT` | Giảm trừ người phụ thuộc | Theo công thức | Theo công thức | - | | `THUE_TNCN` | Thuế TNCN | Theo công thức | Theo công thức | - | **Bước 2: Xây dựng Công thức trong Formula Builder** Người quản trị vào **Module Công cụ xây dựng Công thức** và tạo một "Chính sách lương Văn phòng". 1. **Tính Lương thực tế theo ngày công:** * `LUONG_THUC_TE = ([LCB] / [NGAY_CONG_CHUAN]) * [NGAY_CONG_THUCTE]` 2. **Tính Tổng thu nhập:** * `TONG_THU_NHAP = [LUONG_THUC_TE] + [PC_ANTRUA] + [THUONG_KPI]` 3. **Tính các khoản giảm trừ bắt buộc:** * `BHXH = [LCB] * 8%` (Giả sử LCB là lương đóng BH) * `BHYT = [LCB] * 1.5%` * `BHTN = [LCB] * 1%` * `TONG_BAO_HIEM = [BHXH] + [BHYT] + [BHTN]` 4. **Tính Thu nhập chịu thuế:** * `GIAM_TRU_NPT = [SO_NGUOI_PHUTHUOC] * 4400000` * `THU_NHAP_TINH_THUE = [TONG_THU_NHAP] - [TONG_BAO_HIEM] - [GIAM_TRU_GIACANH] - [GIAM_TRU_NPT]` * `THU_NHAP_TINH_THUE = IF([THU_NHAP_TINH_THUE] < 0, 0, [THU_NHAP_TINH_THUE])` (Nếu âm thì bằng 0) 5. **Tính Thuế TNCN (dùng hàm bậc thang):** * `THUE_TNCN = VLOOKUP([THU_NHAP_TINH_THUE], Bang_Bieu_Thue_Luy_Tien)` 6. **Tính Lương cuối cùng (Thực nhận):** * **`LUONG_THUC_NHAN = [TONG_THU_NHAP] - [TONG_BAO_HIEM] - [THUE_TNCN] - [TAM_UNG]`** **Bước 3: Áp dụng và Chạy lương** * Hệ thống sẽ tự động gán "Chính sách lương Văn phòng" này cho tất cả nhân viên thuộc khối văn phòng. * Vào cuối kỳ, người quản trị chỉ cần thực hiện vài cú nhấp chuột: Chốt công -> Chạy lương -> Rà soát -> Phê duyệt. ### **Lợi ích của thiết kế này** * **Dễ dàng thay đổi:** Khi chính sách phụ cấp thay đổi, chỉ cần vào Module Thành phần lương và sửa lại giá trị của `PC_ANTRUA`. Khi luật thuế TNCN thay đổi, chỉ cần cập nhật lại `Bang_Bieu_Thue_Luy_Tien` mà không cần thay đổi toàn bộ công thức. * **Khả năng mở rộng:** Dễ dàng thêm các loại thưởng, phụ cấp mới mà không ảnh hưởng đến cấu trúc hệ thống. * **Giảm phụ thuộc vào IT:** Phòng nhân sự có thể chủ động quản lý hệ thống tính lương của mình. --- # Luồng hoạt động: **[Tại đây](https://drive.google.com/file/d/1Un2PhFHEIXS-PXldxKe_hYtxMVnEY7Na/view?usp=sharing)** --- ``` sql CREATE TABLE attendance_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), employee_id UUID NOT NULL, work_date DATE NOT NULL, -- Thông tin ca làm việc shift_id UUID, -- Ca làm việc được phân công actual_shift_start TIME, actual_shift_end TIME, -- Check in/out check_in_time TIMESTAMP, check_in_device_id VARCHAR(50), check_in_location VARCHAR(200), check_out_time TIMESTAMP, check_out_device_id VARCHAR(50), check_out_location VARCHAR(200), -- Tính toán thời gian total_work_minutes INTEGER, -- Tổng phút làm việc regular_work_minutes INTEGER, -- Phút làm việc thường overtime_minutes INTEGER, -- Phút làm thêm night_work_minutes INTEGER, -- Phút làm đêm weekend_work_minutes INTEGER, -- Phút làm cuối tuần holiday_work_minutes INTEGER, -- Phút làm ngày lễ -- Nghỉ phép & vi phạm late_minutes INTEGER DEFAULT 0, -- Phút đi muộn early_leave_minutes INTEGER DEFAULT 0, -- Phút về sớm break_minutes INTEGER DEFAULT 0, -- Phút nghỉ giải lao -- Loại ngày công attendance_type ENUM('present', 'absent', 'leave', 'sick', 'holiday', 'weekend') DEFAULT 'present', leave_type_id UUID, -- Loại nghỉ phép -- Ghi chú notes TEXT, manager_notes TEXT, -- Phê duyệt status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', approved_by UUID, approved_at TIMESTAMP, -- Metadata created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), sync_source VARCHAR(50), -- 'manual', 'device', 'import' FOREIGN KEY (employee_id) REFERENCES employees(id), FOREIGN KEY (shift_id) REFERENCES work_shifts(id), FOREIGN KEY (leave_type_id) REFERENCES leave_types(id), FOREIGN KEY (approved_by) REFERENCES employees(id), UNIQUE(employee_id, work_date) ); ``` ## **1. Thông tin định danh** * Mã nhân viên * Họ và tên * Mã máy chấm công / ID quẹt thẻ (nếu dùng máy chấm công) * Phòng ban / bộ phận * Ca làm việc được phân công (ca sáng, ca tối, ca kíp, ca linh hoạt) --- ## **2. Thông tin chấm công hàng ngày** * **Ngày làm việc** * **Giờ vào – giờ ra** (có thể nhiều lần trong ngày nếu ra/vào giữa ca) * Số giờ làm trong ca * Số giờ làm thêm / tăng ca * Số giờ làm ban đêm * Số giờ làm ngày nghỉ / ngày lễ (nếu có) * Ghi chú lý do ra/vào bất thường (nếu có) --- ## **3. Thông tin về vắng mặt** * Ngày nghỉ có phép * Ngày nghỉ không phép * Nghỉ ốm / nghỉ chế độ (thai sản, tang lễ…) * Nghỉ bù * Nghỉ công tác (đi làm ở ngoài, vẫn tính công) --- ## **4. Các thông tin bổ sung để tính lương** * Sản lượng hoàn thành (nếu kết hợp chấm công và tính theo sản phẩm) * Mức phạt đi muộn / về sớm (số phút và tiền phạt tương ứng) * Phụ cấp ca (ăn ca, xăng xe, ca đêm…) * Ghi chú của quản lý hoặc bộ phận nhân sự --- ## **5. Thông tin tổng hợp cuối kỳ** * Tổng số ngày công thực tế * Tổng số giờ làm thêm / tăng ca * Tổng số giờ làm ban đêm * Tổng số ngày nghỉ các loại * Dữ liệu chấm công sau khi đã được **xác nhận duyệt** từ quản lý --- # KIẾN TRÚC HỆ THỐNG TÍNH LƯƠNG CHI TIẾT ## 1. TỔNG QUAN KIẾN TRÚC HỆ THỐNG ### 1.1 Mô hình kiến trúc ``` ┌─────────────────────────────────────────────────────────────────────┐ │ CLIENT TIER │ ├─────────────────────────────────────────────────────────────────────┤ │ Web Browser │ Mobile App │ Desktop App │ API Client │ └─────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┐ │ PRESENTATION TIER │ ├─────────────────────────────────────────────────────────────────────┤ │ Web Server (Nginx/Apache) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Load Balancer│ │ CDN │ │ SSL/TLS │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┐ │ APPLICATION TIER │ ├─────────────────────────────────────────────────────────────────────┤ │ Backend Application Server │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ API Gateway │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │Auth Service │ │Rate Limiting│ │API Routing │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ Core Services │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │User Service │ │Employee Svc │ │Attendance │ │ │ │ │ └─────────────┘ └─────────────┘ │Service │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ └─────────────┘ │ │ │ │ │Salary Calc │ │Report Svc │ ┌─────────────┐ │ │ │ │ │Service │ └─────────────┘ │Payment Svc │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ Business Logic Layer │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │Formula │ │Tax │ │Workflow │ │ │ │ │ │Engine │ │Calculator │ │Engine │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┐ │ DATA TIER │ ├─────────────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │Primary DB │ │Cache Layer │ │File Storage │ │ │ │(PostgreSQL) │ │(Redis) │ │(MinIO/S3) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │Replica DB │ │Search Engine │ │Backup Storage│ │ │ │(Read Only) │ │(Elasticsearch│ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` ## 2. CHI TIẾT TỪNG LAYER ### 2.1 Client Tier (Tầng Client) #### **Web Application (React.js)** ```javascript // Cấu trúc thư mục Frontend src/ ├── components/ # Component tái sử dụng │ ├── common/ # Button, Input, Modal │ ├── forms/ # AttendanceForm, SalaryForm │ └── layout/ # Header, Sidebar, Footer ├── pages/ # Các trang chính │ ├── employees/ # Quản lý nhân viên │ ├── attendance/ # Chấm công │ ├── salary/ # Tính lương │ └── reports/ # Báo cáo ├── services/ # API calls ├── store/ # State management (Redux/Zustand) ├── utils/ # Utility functions └── hooks/ # Custom React hooks ``` #### **Mobile App (React Native)** - Chấm công qua GPS - Xem phiếu lương - Thông báo push - Offline capability #### **Desktop App (Electron)** - Chức năng đầy đủ như Web - Tích hợp máy chấm công - Import/Export Excel offline - Backup local ### 2.2 Presentation Tier (Tầng Trình bày) #### **Web Server (Nginx)** ```nginx # nginx.conf upstream backend { server app1:3000; server app2:3000; server app3:3000; } server { listen 443 ssl http2; server_name salary.company.com; # SSL Configuration ssl_certificate /etc/ssl/certs/salary.crt; ssl_certificate_key /etc/ssl/private/salary.key; # Security Headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; # API Routes location /api/ { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # Static Files location /static/ { root /var/www/salary-system; expires 30d; add_header Cache-Control "public, immutable"; } } ``` #### **Load Balancer & CDN** - **Nginx Load Balancer**: Phân tải request - **CloudFlare CDN**: Cache static files - **SSL/TLS**: Bảo mật HTTPS ### 2.3 Application Tier (Tầng Ứng dụng) #### **API Gateway** ```javascript // API Gateway Structure const express = require('express'); const rateLimit = require('express-rate-limit'); const helmet = require('helmet'); const app = express(); // Security Middleware app.use(helmet()); app.use(rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs })); // Authentication Middleware app.use('/api', authenticateToken); // Route Management app.use('/api/auth', authRoutes); app.use('/api/employees', employeeRoutes); app.use('/api/attendance', attendanceRoutes); app.use('/api/salary', salaryRoutes); app.use('/api/reports', reportRoutes); ``` #### **Core Services Architecture** ##### **User Service** ```javascript // user.service.js class UserService { async authenticate(username, password) { // JWT authentication logic } async authorize(user, resource, action) { // Role-based authorization } async getUserProfile(userId) { // User profile management } } ``` ##### **Employee Service** ```javascript // employee.service.js class EmployeeService { async createEmployee(employeeData) { // Create new employee // Validate data // Generate employee code // Save to database } async updateEmployee(id, updateData) { // Update employee information // Audit trail logging } async getEmployeesByType(type) { // Filter by employee type // (hanh_chinh, san_xuat, thoi_vu) } } ``` ##### **Attendance Service** ```javascript // attendance.service.js class AttendanceService { async recordAttendance(attendanceData) { // Validate attendance data // Apply business rules // Calculate temporary amount // Save to database } async calculateDailyEarnings(employeeId, date) { // Get attendance records // Apply salary formulas // Return calculated amount } async importAttendanceFromExcel(file) { // Parse Excel file // Validate data // Bulk insert with transactions } } ``` ##### **Salary Calculation Service** ```javascript // salary-calc.service.js class SalaryCalculationService { async calculateSalary(employeeId, periodId) { // Get employee configuration // Get attendance data // Apply salary formulas // Calculate taxes and deductions // Return salary breakdown } async calculateTax(grossSalary, employeeId) { // Get tax configuration // Apply tax brackets // Calculate personal income tax } async processBulkSalary(periodId) { // Get all active employees // Process in parallel batches // Handle errors gracefully // Generate reports } } ``` #### **Business Logic Layer** ##### **Formula Engine** ```javascript // formula-engine.js class FormulaEngine { constructor() { this.formulaCache = new Map(); } async evaluateFormula(formula, variables) { try { // Parse formula expression // Validate variables // Calculate result safely // Cache results return this.safeEval(formula, variables); } catch (error) { throw new Error(`Formula evaluation failed: ${error.message}`); } } safeEval(expression, context) { // Safe expression evaluation // Prevent code injection // Mathematical operations only } // Formula examples: // "so_qua * don_gia" // "luong_co_ban + phu_cap" // "so_gio * luong_gio * he_so" } ``` ##### **Tax Calculator** ```javascript // tax-calculator.js class TaxCalculator { constructor() { this.taxBrackets = [ { from: 0, to: 5000000, rate: 0.05 }, { from: 5000000, to: 10000000, rate: 0.10 }, { from: 10000000, to: 18000000, rate: 0.15 }, { from: 18000000, to: 32000000, rate: 0.20 }, { from: 32000000, to: 52000000, rate: 0.25 }, { from: 52000000, to: 80000000, rate: 0.30 }, { from: 80000000, to: Infinity, rate: 0.35 } ]; } calculatePersonalIncomeTax(taxableIncome) { let tax = 0; for (const bracket of this.taxBrackets) { if (taxableIncome > bracket.from) { const taxableAtThisBracket = Math.min( taxableIncome - bracket.from, bracket.to - bracket.from ); tax += taxableAtThisBracket * bracket.rate; } } return tax; } calculateSocialInsurance(grossSalary) { const maxBase = 29800000; // Mức đóng BHXH tối đa const baseAmount = Math.min(grossSalary, maxBase); return { bhxh: baseAmount * 0.08, // 8% bhyt: baseAmount * 0.015, // 1.5% bhtn: baseAmount * 0.01 // 1% }; } } ``` ##### **Workflow Engine** ```javascript // workflow-engine.js class WorkflowEngine { async processWorkflow(entityType, entityId, action) { const workflow = await this.getWorkflowDefinition(entityType); const currentState = await this.getCurrentState(entityId); if (this.canTransition(currentState, action, workflow)) { await this.executeTransition(entityId, action); await this.notifyStakeholders(entityId, action); } else { throw new Error(`Invalid transition: ${currentState} -> ${action}`); } } // Workflow states: // attendance: draft -> submitted -> approved -> processed // salary: draft -> calculated -> reviewed -> approved -> paid // payslip: generated -> sent -> viewed } ``` ### 2.4 Data Tier (Tầng Dữ liệu) #### **Primary Database (PostgreSQL)** ```sql -- Database Configuration -- postgresql.conf optimizations for salary system # Memory settings shared_buffers = 256MB effective_cache_size = 1GB work_mem = 4MB # Connection settings max_connections = 200 shared_preload_libraries = 'pg_stat_statements' # Logging log_statement = 'mod' log_min_duration_statement = 1000 # Performance random_page_cost = 1.1 effective_io_concurrency = 200 -- Key Indexes for Performance CREATE INDEX CONCURRENTLY idx_attendance_employee_date ON attendance_records(employee_id, work_date); CREATE INDEX CONCURRENTLY idx_salary_calc_period ON salary_calculations(period_id, status); CREATE INDEX CONCURRENTLY idx_employees_active ON employees(is_active, employee_type); ``` #### **Cache Layer (Redis)** ```javascript // redis-config.js const Redis = require('ioredis'); const redis = new Redis({ host: process.env.REDIS_HOST, port: process.env.REDIS_PORT, retryDelayOnFailover: 100, maxRetriesPerRequest: 3, }); // Cache Strategies class CacheService { async cacheEmployeeData(employeeId, data) { await redis.setex(`employee:${employeeId}`, 3600, JSON.stringify(data)); } async cacheSalaryFormulas() { const formulas = await this.getSalaryFormulas(); await redis.setex('salary:formulas', 7200, JSON.stringify(formulas)); } async cacheAttendanceSummary(employeeId, month) { const key = `attendance:${employeeId}:${month}`; // Cache for 1 day await redis.setex(key, 86400, JSON.stringify(summary)); } } ``` #### **File Storage (MinIO/S3)** ```javascript // file-storage.service.js const AWS = require('aws-sdk'); class FileStorageService { constructor() { this.s3 = new AWS.S3({ endpoint: process.env.MINIO_ENDPOINT, accessKeyId: process.env.MINIO_ACCESS_KEY, secretAccessKey: process.env.MINIO_SECRET_KEY, s3ForcePathStyle: true }); } async uploadPayslip(employeeId, periodId, pdfBuffer) { const key = `payslips/${periodId}/${employeeId}.pdf`; return await this.s3.upload({ Bucket: 'salary-documents', Key: key, Body: pdfBuffer, ContentType: 'application/pdf', ServerSideEncryption: 'AES256' }).promise(); } async uploadAttendanceFile(file) { const key = `attendance/${Date.now()}_${file.originalname}`; return await this.s3.upload({ Bucket: 'attendance-files', Key: key, Body: file.buffer, ContentType: file.mimetype }).promise(); } } ``` ## 3. BẢO MẬT VÀ TUÂN THỦ ### 3.1 Authentication & Authorization ```javascript // auth.middleware.js const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt'); class AuthService { async login(username, password) { const user = await User.findOne({ username }); if (!user || !await bcrypt.compare(password, user.password_hash)) { throw new Error('Invalid credentials'); } const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '8h' } ); // Log login activity await AuditLog.create({ user_id: user.id, action: 'login', ip_address: req.ip }); return { token, user }; } authorize(roles) { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; } } ``` ### 3.2 Data Encryption ```javascript // encryption.service.js const crypto = require('crypto'); class EncryptionService { constructor() { this.algorithm = 'aes-256-gcm'; this.secretKey = process.env.ENCRYPTION_KEY; } encrypt(text) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipher(this.algorithm, this.secretKey, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return { iv: iv.toString('hex'), authTag: authTag.toString('hex'), encryptedData: encrypted }; } decrypt(encryptedData) { const decipher = crypto.createDecipher( this.algorithm, this.secretKey, Buffer.from(encryptedData.iv, 'hex') ); decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex')); let decrypted = decipher.update(encryptedData.encryptedData, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } } // Encrypt sensitive data before storing const encryptionService = new EncryptionService(); // Before saving employee data employee.citizen_id = encryptionService.encrypt(employee.citizen_id); employee.bank_account = encryptionService.encrypt(employee.bank_account); ``` ## 4. HIỆU NĂNG VÀ TỐI ƯU HÓA ### 4.1 Database Optimization ```sql -- Partitioning for large tables CREATE TABLE attendance_records_y2024 PARTITION OF attendance_records FOR VALUES FROM ('2024-01-01') TO ('2025-01-01'); -- Materialized views for reports CREATE MATERIALIZED VIEW monthly_salary_summary AS SELECT e.department_id, DATE_TRUNC('month', sc.created_at) as salary_month, COUNT(*) as employee_count, SUM(sc.gross_salary) as total_gross, SUM(sc.net_salary) as total_net FROM salary_calculations sc JOIN employees e ON sc.employee_id = e.employee_id GROUP BY e.department_id, DATE_TRUNC('month', sc.created_at); -- Refresh materialized view daily SELECT cron.schedule('refresh-salary-summary', '0 1 * * *', 'REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_salary_summary;'); ``` ### 4.2 Application Performance ```javascript // connection-pool.js const { Pool } = require('pg'); const pool = new Pool({ host: process.env.DB_HOST, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, port: process.env.DB_PORT, max: 20, // Maximum connections min: 5, // Minimum connections idleTimeoutMillis: 30000, connectionTimeoutMillis: 10000, }); // Bulk operations for salary calculations class BulkSalaryProcessor { async processBatch(employeeIds, periodId) { const batchSize = 50; const batches = this.chunkArray(employeeIds, batchSize); const promises = batches.map(batch => this.processSalaryBatch(batch, periodId) ); return await Promise.allSettled(promises); } async processSalaryBatch(employeeIds, periodId) { const client = await pool.connect(); try { await client.query('BEGIN'); for (const employeeId of employeeIds) { await this.calculateEmployeeSalary(client, employeeId, periodId); } await client.query('COMMIT'); } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } } } ``` ## 5. MONITORING VÀ LOGGING ### 5.1 Application Monitoring ```javascript // monitoring.js const prometheus = require('prom-client'); // Custom metrics const salaryCalculationDuration = new prometheus.Histogram({ name: 'salary_calculation_duration_seconds', help: 'Duration of salary calculations', labelNames: ['employee_type', 'period'] }); const activeUsers = new prometheus.Gauge({ name: 'active_users_total', help: 'Number of active users' }); const errorRate = new prometheus.Counter({ name: 'salary_errors_total', help: 'Total number of salary calculation errors', labelNames: ['error_type'] }); // Middleware to track metrics function metricsMiddleware(req, res, next) { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; salaryCalculationDuration.observe(duration / 1000); }); next(); } ``` ### 5.2 Structured Logging ```javascript // logger.js const winston = require('winston'); const ElasticsearchTransport = require('winston-elasticsearch'); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'salary-system' }, transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), new ElasticsearchTransport({ level: 'info', clientOpts: { node: process.env.ELASTICSEARCH_URL }, index: 'salary-logs' }) ] }); // Usage examples logger.info('Salary calculation started', { employeeId: 123, periodId: 456, userId: req.user.id }); logger.error('Formula evaluation failed', { formula: 'so_qua * don_gia', variables: { so_qua: 100, don_gia: 500 }, error: error.message }); ``` ## 6. BACKUP VÀ DISASTER RECOVERY ### 6.1 Database Backup Strategy ```bash #!/bin/bash # backup-salary-db.sh DATE=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/backups/salary-system" DB_NAME="salary_db" # Full backup daily pg_dump -h $DB_HOST -U $DB_USER -Fc $DB_NAME > \ "$BACKUP_DIR/full_backup_$DATE.dump" # Incremental backup using WAL archiving pg_receivewal -h $DB_HOST -U $DB_USER -D "$BACKUP_DIR/wal" -v # Upload to cloud storage aws s3 cp "$BACKUP_DIR/full_backup_$DATE.dump" \ s3://salary-backups/database/ --storage-class STANDARD_IA # Cleanup old backups (keep 30 days) find $BACKUP_DIR -name "*.dump" -mtime +30 -delete # Test backup integrity pg_restore --list "$BACKUP_DIR/full_backup_$DATE.dump" > /dev/null if [ $? -eq 0 ]; then echo "Backup verification successful" else echo "Backup verification failed" | mail -s "Backup Alert" admin@company.com fi ``` ### 6.2 Application Recovery ```javascript // disaster-recovery.service.js class DisasterRecoveryService { async createSystemSnapshot() { const snapshot = { timestamp: new Date(), database_backup: await this.createDatabaseBackup(), file_storage_backup: await this.backupFileStorage(), configuration_backup: await this.backupConfigurations() }; return await this.uploadSnapshotToSecureStorage(snapshot); } async restoreFromSnapshot(snapshotId) { try { const snapshot = await this.downloadSnapshot(snapshotId); // Restore database await this.restoreDatabase(snapshot.database_backup); // Restore files await this.restoreFileStorage(snapshot.file_storage_backup); // Restore configurations await this.restoreConfigurations(snapshot.configuration_backup); // Verify system integrity await this.verifySystemIntegrity(); return { success: true, message: 'System restored successfully' }; } catch (error) { throw new Error(`Recovery failed: ${error.message}`); } } } ``` ## 7. TRIỂN KHAI VÀ CI/CD ### 7.1 Docker Configuration ```dockerfile # Dockerfile FROM node:18-alpine WORKDIR /app # Install dependencies COPY package*.json ./ RUN npm ci --only=production # Copy application code COPY . . # Create non-root user RUN addgroup -g 1001 -S nodejs RUN adduser -S nodejs -u 1001 # Change ownership and switch to non-root user CHOWN nodejs:nodejs /app USER nodejs # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node healthcheck.js EXPOSE 3000 CMD ["node", "server.js"] ``` ### 7.2 Kubernetes Deployment ```yaml # k8s-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: salary-system namespace: production spec: replicas: 3 selector: matchLabels: app: salary-system template: metadata: labels: app: salary-system spec: containers: - name: salary-app image: salary-system:latest ports: - containerPort: 3000 env: - name: DB_HOST valueFrom: secretKeyRef: name: db-credentials key: host resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: salary-service spec: selector: app: salary-system ports: - protocol: TCP port: 80 targetPort: 3000 type: LoadBalancer ``` ### 7.3 CI/CD Pipeline ```yaml # .github/workflows/deploy.yml name: Deploy Salary System on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:13 env: POSTGRES_PASSWORD: test POSTGRES_DB: salary_test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run linting run: npm run lint - name: Run tests run: npm test env: DATABASE_URL: postgresql://postgres:test@localhost:5432/salary_test - name: Run security audit run: npm audit --audit-level high build: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build Docker image run: docker build -t salary-system:${{ github.sha }} . - name: Push to registry run: | docker tag salary-system:${{ github.sha }} registry.company.com/salary-system:${{ github.sha }} docker push registry.company.com/salary-system:${{ github.sha }} deploy: needs: [test, build] runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Deploy to production run: | kubectl set image deployment/salary-system \ salary-app=registry.company.com/salary-system:${{ github.sha }} \ -n production kubectl rollout status deployment/salary-system -n production ``` ## 8. KẾT LUẬN Kiến trúc hệ thống tính lương này được thiết kế để: ### **Tính mở rộng (Scalability)** - Microservices architecture - Horizontal scaling với container - Database partitioning - Load balancing ### **Độ tin cậy (Reliability)** - Database replication - Automatic backup & recovery - Health checks & monitoring - Error handling & retry logic ### **Bảo mật (Security)** - Multi-layer authentication - Data encryption at rest & in transit - Audit trail cho tất cả actions - Role-based access control ### **Hiệu năng (Performance)** - Redis caching layer - Database optimization - Async processing - CDN cho static files ### **Bảo trì (Maintainability)** - Clean architecture - Comprehensive logging - Automated testing - CI/CD pipeline Hệ thống này có thể xử lý từ vài trăm đến hàng chục nghìn nhân viên, đáp ứng được các yêu cầu về tính lương linh hoạt và phức tạp của doanh nghiệp Việt Nam.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully