flexbiz-server 12.6.13 → 12.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +439 -4
  2. package/package.json +3 -2
  3. package/server/agents/agents.js +1 -0
  4. package/server/agents/ai_memory_manager.js +6 -0
  5. package/server/agents/ceoAgent.js +43 -0
  6. package/server/agents/chatController.js +6 -0
  7. package/server/agents/cskhAgent.js +43 -0
  8. package/server/agents/flowAgent.js +43 -0
  9. package/server/agents/prompts/ceo.js +2 -0
  10. package/server/agents/prompts/flow.js +2 -0
  11. package/server/agents/staffAgent.js +44 -0
  12. package/server/agents/tools/financeTools.js +57 -0
  13. package/server/agents/tools/flowTools.js +36 -0
  14. package/server/agents/tools/kpiTools.js +104 -0
  15. package/server/agents/tools/masterDataTools.js +74 -0
  16. package/server/agents/tools/okrTools.js +21 -0
  17. package/server/agents/tools/ragTools.js +21 -0
  18. package/server/agents/tools/reportTools.js +43 -0
  19. package/server/agents/tools/staffTools.js +13 -0
  20. package/server/agents/tools/stockTools.js +41 -0
  21. package/server/agents/tools/systemTools.js +52 -0
  22. package/server/agents/tools/taskTools.js +23 -0
  23. package/server/agents/tools/utils.js +57 -0
  24. package/server/app.js +7 -7
  25. package/server/controllers/controller.js +31 -29
  26. package/server/controllers/controllerRPT.js +31 -29
  27. package/server/controllers/findHandler.js +22 -21
  28. package/server/controllers/importFromExcelHandler.js +17 -14
  29. package/server/controllers/importHandler.js +26 -41
  30. package/server/controllers/importHandler_old.js +41 -0
  31. package/server/controllers/rptHandler.js +14 -11
  32. package/server/controllers/viewHandler.js +7 -7
  33. package/server/flow/FlowEngine.js +49 -45
  34. package/server/flow/WebhookManager.js +1 -1
  35. package/server/flow/handlers.js +2 -2
  36. package/server/flow/nodes/userform.js +28 -0
  37. package/server/flow/utils.js +12 -4
  38. package/server/global.js +4 -3
  39. package/server/libs/cdpstk.js +1 -1
  40. package/server/libs/ckvt.js +12 -11
  41. package/server/libs/ckvtcapphat.js +5 -5
  42. package/server/libs/ckvtnpp.js +8 -8
  43. package/server/libs/ckvttt.js +6 -5
  44. package/server/libs/ctvt.js +7 -6
  45. package/server/libs/ctvtnpp.js +7 -6
  46. package/server/libs/ctvttt.js +6 -6
  47. package/server/libs/databanle.js +17 -18
  48. package/server/libs/databanle_old.js +18 -0
  49. package/server/libs/databanletheongay.js +25 -29
  50. package/server/libs/dktk.js +3 -3
  51. package/server/libs/dkvt.js +11 -11
  52. package/server/libs/dkvtnpp.js +7 -6
  53. package/server/libs/dkvttt.js +7 -6
  54. package/server/libs/docs.js +72 -36
  55. package/server/libs/dtbanletheosp.js +16 -19
  56. package/server/libs/fifo_dk.js +8 -0
  57. package/server/libs/fifo_group.js +4 -0
  58. package/server/libs/fifo_trace_query.js +12 -0
  59. package/server/libs/getGiaban.js +59 -70
  60. package/server/libs/getGiaban_old.js +70 -0
  61. package/server/libs/joinData.js +3 -3
  62. package/server/libs/joinData_bk.js +44 -0
  63. package/server/libs/kpistatus.js +24 -17
  64. package/server/libs/permission.js +54 -56
  65. package/server/libs/permission_old.js +58 -0
  66. package/server/libs/post-book.js +1 -1
  67. package/server/libs/post-socai.js +2 -1
  68. package/server/libs/post-sokho.js +11 -11
  69. package/server/libs/prototypes.js +13 -12
  70. package/server/libs/queryBuilding.js +11 -10
  71. package/server/libs/redis-cache.js +22 -10
  72. package/server/libs/sctcnkh.js +19 -17
  73. package/server/libs/socaitk.js +1 -1
  74. package/server/libs/thnxt.js +17 -14
  75. package/server/libs/thnxtnpp.js +8 -8
  76. package/server/libs/thnxttt.js +8 -7
  77. package/server/libs/tinhgia1vt_fifo.js +22 -0
  78. package/server/libs/tinhgiafifo.js +28 -0
  79. package/server/libs/utils.js +37 -19
  80. package/server/models/app.js +1 -1
  81. package/server/models/approve.js +8 -8
  82. package/server/models/cacheDataReport.js +2 -2
  83. package/server/models/cacheSessionReport.js +5 -1
  84. package/server/models/cdvt.js +2 -2
  85. package/server/models/checkin.js +2 -2
  86. package/server/models/chitietluongds.js +1 -1
  87. package/server/models/contract.js +1 -1
  88. package/server/models/csluongtheods.js +2 -2
  89. package/server/models/ctds2.js +7 -6
  90. package/server/models/custom_report.js +1 -0
  91. package/server/models/dmdt.js +2 -2
  92. package/server/models/dmkho.js +5 -4
  93. package/server/models/dmnvt.js +5 -4
  94. package/server/models/fifo_dk.js +2 -0
  95. package/server/models/fifo_lot.js +3 -0
  96. package/server/models/fifo_trace.js +4 -0
  97. package/server/models/flowpending.js +2 -0
  98. package/server/models/group.js +3 -5
  99. package/server/models/listinfo.js +61 -33
  100. package/server/models/schedule.js +4 -3
  101. package/server/models/task.js +1 -1
  102. package/server/models/trangthai.js +2 -2
  103. package/server/modules/lists/bg-fifo_dk.js +8 -0
  104. package/server/modules/lists/ls-checkin.js +1 -1
  105. package/server/modules/lists/ls-customer.js +23 -26
  106. package/server/modules/lists/ls-dmbp.js +5 -5
  107. package/server/modules/lists/ls-dmkho.js +7 -1
  108. package/server/modules/lists/ls-dmnhtask.js +1 -1
  109. package/server/modules/lists/ls-dmvt.js +52 -70
  110. package/server/modules/lists/ls-flowpending.js +3 -0
  111. package/server/modules/lists/ls-participant.js +6 -4
  112. package/server/modules/lists/ls-task.js +6 -6
  113. package/server/modules/reports/calc-tinhgiafifo.js +8 -0
  114. package/server/modules/reports/calc-tinhluong.js +17 -16
  115. package/server/modules/reports/rp-baoCaoFifoKy.js +1 -0
  116. package/server/modules/reports/rp-congviectheobp.js +4 -4
  117. package/server/modules/reports/rp-congviectheodt.js +1 -1
  118. package/server/modules/reports/rp-congviectheokh.js +1 -1
  119. package/server/modules/reports/rp-congviectheonv.js +1 -1
  120. package/server/modules/reports/rp-dtbanletheoct.js +22 -23
  121. package/server/modules/reports/rp-dtbanletheokh.js +17 -20
  122. package/server/modules/reports/rp-dtbanletheokho.js +15 -18
  123. package/server/modules/reports/rp-hoahong.js +1 -1
  124. package/server/modules/reports/rp-sctvt.js +1 -1
  125. package/server/modules/reports/rp-sosanhkhvatt2.js +1 -1
  126. package/server/modules/reports/rp-thnxt.js +8 -8
  127. package/server/modules/reports/rp-tongquannvbh.js +1 -1
  128. package/server/modules/systems/sys-app.js +5 -5
  129. package/server/modules/vouchers/vo-so1.js +14 -14
package/README.md CHANGED
@@ -1,5 +1,440 @@
1
-
2
1
  # Flexbiz-server
3
- Service for Flexbiz app
4
- ## License
5
- MIT
2
+
3
+ > **Thư viện nền tảng cho hệ thống Flexbiz ERP**
4
+
5
+ Flexbiz-server là thư viện lõi cung cấp toàn bộ hạ tầng và các module nghiệp vụ cho hệ thống quản trị doanh nghiệp Flexbiz ERP. Thư viện bao gồm đầy đủ các phân hệ từ quản lý khách hàng, bán hàng, mua hàng, kho bãi, kế toán tài chính đến quản trị nhân sự — phục vụ doanh nghiệp vừa và nhỏ lẫn tập đoàn đa chi nhánh.
6
+
7
+ Điểm khác biệt cốt lõi so với các ERP truyền thống: Flexbiz-server tích hợp sẵn **hệ thống Workflow tự động hóa quy trình** và **AI Agent điều hành thông minh** — giúp doanh nghiệp không chỉ quản lý dữ liệu mà còn tự động hoá nghiệp vụ và ra quyết định dựa trên dữ liệu thực tế theo thời gian thực.
8
+
9
+ ---
10
+
11
+ ## Mục lục
12
+
13
+ - [Tổng quan](#tổng-quan)
14
+ - [⚡ Workflow — Tự động hóa quy trình](#-workflow--tự-động-hóa-quy-trình)
15
+ - [🤖 AI Agent — Trợ lý điều hành thông minh](#-ai-agent--trợ-lý-điều-hành-thông-minh)
16
+ - [Các module chức năng](#các-module-chức-năng)
17
+ - [Bảng điều khiển](#1-bảng-điều-khiển)
18
+ - [Quản lý quan hệ khách hàng (CRM)](#2-quản-lý-quan-hệ-khách-hàng-crm)
19
+ - [Bán hàng](#3-bán-hàng)
20
+ - [Mua hàng](#4-mua-hàng)
21
+ - [Nhà phân phối](#5-nhà-phân-phối)
22
+ - [Quản lý kho](#6-quản-lý-kho)
23
+ - [Sản xuất](#7-sản-xuất)
24
+ - [Kế toán tài chính](#8-kế-toán-tài-chính)
25
+ - [Quản lý nhân sự](#9-quản-lý-nhân-sự)
26
+ - [Quản trị hệ thống](#10-quản-trị-hệ-thống)
27
+ - [Giấy phép sử dụng](#giấy-phép-sử-dụng)
28
+
29
+ ---
30
+
31
+ ## Tổng quan
32
+
33
+ Flexbiz-server được xây dựng theo kiến trúc module hoá, cho phép tích hợp linh hoạt các phân hệ nghiệp vụ theo nhu cầu thực tế của từng doanh nghiệp. Mỗi module đều hỗ trợ phân quyền chi tiết (xem, thêm, sửa, xoá, xem của người khác...) và có thể hoạt động độc lập hoặc kết hợp với nhau trong một hệ sinh thái ERP thống nhất.
34
+
35
+ ---
36
+
37
+ ---
38
+
39
+ ## ⚡ Workflow — Tự động hóa quy trình
40
+
41
+ Flexbiz-server tích hợp một **engine Workflow trực quan** cho phép xây dựng các luồng tự động hóa nghiệp vụ phức tạp mà không cần lập trình chuyên sâu. Các flow được thiết kế theo mô hình kéo-thả với hệ thống node đa dạng, có thể kết nối tự do để tạo ra bất kỳ quy trình nghiệp vụ nào.
42
+
43
+ ### Các node Input (Nguồn kích hoạt)
44
+
45
+ | Node | Chức năng |
46
+ |---|---|
47
+ | **Lịch chạy** | Kích hoạt flow thủ công, theo khoảng thời gian lặp lại, hoặc vào thời điểm cụ thể trong ngày. Hỗ trợ inject payload ban đầu dạng String, JSON, Number, Boolean, Timestamp hoặc Script tùy chỉnh. |
48
+ | **Webhook** | Tạo endpoint HTTP nhận dữ liệu từ hệ thống bên ngoài (GET/POST), hỗ trợ xác minh challenge-response theo chuẩn của các nền tảng như Meta, Zalo... |
49
+ | **FB Webhook** | Endpoint chuyên biệt nhận sự kiện từ Facebook Messenger Platform. Tự động xử lý xác minh và xác thực chữ ký payload bằng App Secret. |
50
+ | **FB Conversations** | Lấy danh sách và nội dung các cuộc hội thoại từ Facebook Messenger của một Page. |
51
+ | **Sự kiện (Event)** | Lắng nghe sự kiện thay đổi dữ liệu nội bộ (tạo mới, cập nhật, xoá, duyệt, từ chối) trên bất kỳ loại chứng từ hoặc danh mục nào trong hệ thống. Hỗ trợ điều kiện kích hoạt và xử lý dữ liệu bằng script trước khi chuyển sang node tiếp theo. |
52
+
53
+ ### Các node Logic & Điều hướng
54
+
55
+ | Node | Chức năng |
56
+ |---|---|
57
+ | **Switch** | Phân nhánh luồng dựa trên giá trị payload với bộ toán tử phong phú: so sánh bằng/khác (lỏng và nghiêm ngặt), lớn/nhỏ hơn, chứa chuỗi, bắt đầu/kết thúc bằng, khớp Regex, kiểm tra kiểu dữ liệu, rỗng/không rỗng. Hỗ trợ nhiều nhánh đầu ra và nhánh mặc định. |
58
+ | **Chống trùng lặp** | Ngăn chặn một tác vụ (gửi mail, trừ tiền, tạo chứng từ...) bị thực thi nhiều lần bằng cơ chế Idempotency Key. Hỗ trợ 3 chế độ: Kiểm tra & Khóa → Đánh dấu Thành công / Thất bại. |
59
+
60
+ ### Các node Function (Xử lý dữ liệu)
61
+
62
+ | Node | Chức năng |
63
+ |---|---|
64
+ | **Xử lý dữ liệu** | Thực thi mã JavaScript tùy chỉnh để biến đổi payload. Hỗ trợ sẵn các thư viện: `moment`, `numeral`, `lodash (_)`. |
65
+ | **For Each Item** | Lặp qua từng phần tử trong một mảng, kích hoạt chuỗi node tiếp theo cho từng phần tử riêng biệt. Hỗ trợ đặt tên payload phần tử tùy chỉnh. |
66
+ | **AI Agent** | Gửi payload đến mô hình AI để phân tích, trích xuất thông tin, tạo nội dung hoặc phân loại dữ liệu. Hỗ trợ xử lý hàng loạt theo từng dòng nếu payload là mảng. Có sẵn các mẫu prompt dựng sẵn (VD: trích xuất thông tin khách hàng từ hội thoại). |
67
+ | **Tạm dừng** | Trì hoãn luồng thực thi trong khoảng thời gian xác định (tính bằng mili giây). |
68
+
69
+ ### Các node Output (Hành động đầu ra)
70
+
71
+ | Node | Chức năng |
72
+ |---|---|
73
+ | **Gửi Email** | Gửi email đến một hoặc nhiều người nhận. Hỗ trợ nội dung HTML và nhận danh sách gửi từ payload. Trả về chi tiết kết quả thành công/thất bại cho từng người nhận. |
74
+ | **Gửi FB Message** | Gửi tin nhắn đến người dùng Facebook Messenger qua Send API. Hỗ trợ tin nhắn văn bản, template JSON (button, carousel, quick replies) và gắn tag để gửi sau cửa sổ 24 giờ. |
75
+ | **Facebook Create Post** | Đăng bài viết (văn bản, link, hình ảnh) lên Facebook Page. Hỗ trợ xuất bản ngay hoặc lưu nháp. |
76
+ | **Gửi tin nhắn Zalo** | Gửi tin nhắn ZNS đến khách hàng qua Zalo OA bằng mẫu tin nhắn đã đăng ký. |
77
+ | **Gửi thông báo** | Gửi push notification đến người dùng nội bộ trong hệ thống. Hỗ trợ template động nhúng dữ liệu từ payload. |
78
+ | **Lưu trữ** | Lưu dữ liệu đầu ra của node trước vào database để tra cứu hoặc kiểm toán. |
79
+ | **Debug** | Hiển thị payload ra cửa sổ Debug hoặc Browser Console để kiểm tra và gỡ lỗi flow. |
80
+
81
+ ### Các node Công ty (Tích hợp dữ liệu nội bộ)
82
+
83
+ | Node | Chức năng |
84
+ |---|---|
85
+ | **Chứng từ, danh mục** | Thực hiện các thao tác Tạo mới, Cập nhật, Tìm & Cập nhật, Lấy danh sách, Đếm số lượng trên toàn bộ chứng từ và danh mục trong hệ thống. Hỗ trợ script xử lý điều kiện lọc và biến đổi dữ liệu trước khi trả về. |
86
+ | **Báo cáo** | Chạy bất kỳ báo cáo nào trong hệ thống với điều kiện lọc tùy chỉnh. Hỗ trợ render ra mẫu in và gửi kết quả đến người dùng. |
87
+ | **Http Request** | Gửi yêu cầu HTTP (GET, POST, PUT, PATCH, DELETE) đến API bên ngoài với headers và body tùy chỉnh hoặc lấy từ payload. |
88
+
89
+ ### Node Tương tác (Dừng chờ nhập liệu)
90
+
91
+ | Node | Chức năng |
92
+ |---|---|
93
+ | **Nhập liệu (User Form)** | Dừng flow và chờ người dùng xử lý. Hỗ trợ 2 luồng song song: (1) Thông báo và mở form nhập liệu cho người dùng chỉ định; (2) Lắng nghe thao tác trực tiếp trên màn hình nghiệp vụ gốc. Luồng nào hoàn thành trước thì flow tiếp tục. Hỗ trợ script xác định người nhận động, điền sẵn dữ liệu, và điều kiện kích hoạt linh hoạt. |
94
+
95
+ ---
96
+
97
+ ## 🤖 AI Agent — Trợ lý điều hành thông minh
98
+
99
+ Flexbiz-server tích hợp sẵn **AI Agent cấp điều hành** — được kết nối trực tiếp với toàn bộ dữ liệu thực tế của doanh nghiệp, có khả năng phân tích, tra cứu và hành động thay người quản lý ngay trong cuộc hội thoại.
100
+
101
+ ### Phân tích & Báo cáo tức thời
102
+
103
+ AI Agent có thể trả lời ngay các câu hỏi nghiệp vụ mà không cần người dùng thao tác thủ công trên hệ thống:
104
+
105
+ - **Tài chính:** Doanh thu, lợi nhuận, dòng tiền, số dư quỹ theo bất kỳ kỳ nào
106
+ - **Kho hàng:** Tồn kho hiện tại, nhập xuất theo kỳ, thẻ kho chi tiết từng lô hàng
107
+ - **Công nợ:** Phải thu/phải trả, tuổi nợ khách hàng và nhà cung cấp
108
+ - **Bán hàng:** Doanh thu, top sản phẩm bán chạy, hiệu suất nhân viên, chiết khấu
109
+ - **Nhân sự:** Chấm công, ngày công, tình hình lương
110
+ - **Giá cả:** Giá mua gần nhất, xu hướng giá, so sánh nhà cung cấp, giá thành sản phẩm
111
+ - **Báo cáo động:** Tự viết script phân tích vắt chéo đa chiều bất kỳ dữ liệu nào trong hệ thống
112
+
113
+ ### Hành động & Điều hành
114
+
115
+ AI Agent không chỉ trả lời — mà còn có thể **thực hiện hành động trực tiếp** trên hệ thống:
116
+
117
+ - Tạo và cập nhật chứng từ: phiếu thu, phiếu chi, đơn đặt hàng, hóa đơn, dự án
118
+ - Giao việc, nhắc việc, theo dõi tiến độ thực hiện của từng nhân sự
119
+ - Lập kế hoạch kinh doanh (KPI), thiết lập mục tiêu chiến lược (OKR), duyệt kế hoạch ngân sách
120
+ - Gửi email tự động kèm chữ ký công ty
121
+ - Thiết lập lịch báo cáo định kỳ (hàng ngày/tuần/tháng) — chạy tự động kể cả khi không có người dùng
122
+
123
+ ### Quản trị mục tiêu theo vòng PDCA
124
+
125
+ AI Agent hỗ trợ toàn bộ chu trình quản trị hiệu suất:
126
+
127
+ | Giai đoạn | Khả năng |
128
+ |---|---|
129
+ | **Plan (Lập kế hoạch)** | Thiết lập OKR (Objective & Key Results), lập bảng KPI định kỳ, dự toán ngân sách cho từng dự án/vụ việc |
130
+ | **Review (Phê duyệt)** | Xem danh sách kế hoạch chờ duyệt, trình bày bản nháp và phê duyệt chính thức |
131
+ | **Check (Kiểm tra)** | Theo dõi tiến độ OKR, so sánh thực tế vs kế hoạch (Actual vs Planned), kiểm tra ngân sách dự án |
132
+ | **Act (Hành động)** | Giao việc, theo dõi task, nhắc nhở và yêu cầu giải trình |
133
+
134
+ ### Tra cứu & Điều hướng
135
+
136
+ - Tra cứu tức thì bất kỳ đối tượng: khách hàng, sản phẩm, đơn hàng, nhà cung cấp, dự án, nhân viên
137
+ - Đọc và tóm tắt file đính kèm (hợp đồng, hóa đơn, quy chế PDF/Word/Excel)
138
+ - Hỏi đáp trên kho tri thức nội bộ (RAG): quy trình, chính sách, lịch sử phê duyệt
139
+ - Tìm đường link và điều hướng đến bất kỳ màn hình/chức năng/báo cáo nào trong hệ thống
140
+
141
+ ### Kết hợp Workflow + AI Agent
142
+
143
+ Một trong những điểm mạnh nổi bật: **AI Agent và Workflow hoạt động cộng hưởng**. Ví dụ:
144
+
145
+ - Khi có đơn hàng mới → node **Sự kiện** kích hoạt → node **AI Agent** phân tích thông tin khách hàng từ hội thoại → node **Gửi FB Message** phản hồi tự động → node **Chứng từ** tạo đơn hàng trong hệ thống
146
+ - Mỗi ngày 8h sáng → node **Lịch chạy** kích hoạt → node **Báo cáo** chạy báo cáo doanh thu → node **Gửi Email** gửi kết quả đến ban lãnh đạo
147
+ - Khi phát hiện tồn kho dưới ngưỡng → node **Sự kiện** → node **Switch** kiểm tra → node **Gửi thông báo** cảnh báo thủ kho → node **Nhập liệu** chờ xác nhận đặt hàng
148
+
149
+ ---
150
+
151
+ ## Các module chức năng
152
+
153
+ ### 1. Bảng điều khiển
154
+
155
+ Cung cấp giao diện tổng quan trực quan, hiển thị các chỉ số kinh doanh quan trọng theo thời gian thực. Người dùng có thể theo dõi hiệu suất hoạt động toàn doanh nghiệp ngay từ màn hình chính.
156
+
157
+ ---
158
+
159
+ ### 2. Quản lý quan hệ khách hàng (CRM)
160
+
161
+ Phân hệ CRM toàn diện, hỗ trợ doanh nghiệp xây dựng và duy trì mối quan hệ với khách hàng xuyên suốt vòng đời kinh doanh.
162
+
163
+ #### Quản lý khách hàng
164
+ - Quản lý toàn bộ thông tin khách hàng, phân loại theo nhóm và kiểu phân loại tùy chỉnh
165
+ - Quản lý danh sách liên hệ, hợp đồng và vụ việc/dự án
166
+ - Theo dõi các hoạt động tương tác với khách hàng
167
+ - Tích hợp check-in định vị địa lý
168
+ - Báo cáo khách hàng tiềm năng
169
+
170
+ #### Tích hợp truyền thông
171
+ - Kết nối và quản lý Facebook Pages
172
+ - Quản lý tài khoản và gửi tin nhắn SMS Marketing
173
+ - Lịch sử các cuộc gọi qua tổng đài
174
+
175
+ #### Quản lý tài liệu
176
+ - Hệ thống quản lý tài liệu dạng thư mục phân cấp
177
+ - Phân quyền truy cập tài liệu theo người dùng
178
+
179
+ #### Chương trình giới thiệu
180
+ - Quản lý mã giới thiệu và danh sách người dùng được giới thiệu
181
+ - Tính toán và báo cáo tiền thưởng giới thiệu
182
+ - Báo cáo hoa hồng theo doanh số bán hàng
183
+
184
+ #### Quản trị mục tiêu (OKR)
185
+ - Thiết lập và theo dõi các mục tiêu (Objectives)
186
+ - Quản lý kết quả cốt lõi (Key Results)
187
+ - Ghi nhận checkin kết quả định kỳ
188
+
189
+ #### Quản lý công việc
190
+ - Bàn làm việc theo dạng Kanban/Board
191
+ - Phân loại công việc theo loại, nhóm, nhãn và độ khó
192
+ - Xem tiến độ theo Timeline và Gantt chart
193
+ - Thống kê công việc theo vụ việc/dự án, nhân viên, bộ phận và khách hàng
194
+
195
+ #### Nội dung web & ứng dụng
196
+ - Quản lý tin tức, trang web và tin tuyển dụng
197
+ - Xử lý liên hệ từ website
198
+ - Gửi thông báo đến người dùng ứng dụng
199
+
200
+ #### Các tính năng đặc biệt
201
+ - Chương trình dán decal quảng cáo: đăng ký, checkin và đổi quà
202
+ - Game trúng thưởng: quản lý game và lượt chơi
203
+ - Phân hệ Bất động sản: quản lý dự án và sản phẩm nhà đất
204
+
205
+ ---
206
+
207
+ ### 3. Bán hàng
208
+
209
+ Phân hệ bán hàng đa kênh, hỗ trợ cả bán lẻ trực tiếp, bán sỉ và bán hàng online.
210
+
211
+ #### Chứng từ bán hàng
212
+ - Phiếu bán lẻ tại quầy (POS) cho cửa hàng, quán ăn, cafe...
213
+ - Đơn hàng bán lẻ online
214
+ - Đơn hàng bán sỉ
215
+ - Hóa đơn bán hàng và hóa đơn bán dịch vụ
216
+ - Phiếu xuất hàng
217
+ - Xử lý hàng bán lẻ và bán sỉ bị trả lại
218
+ - Chi phí bán hàng
219
+ - Phiếu bán mã giảm giá
220
+ - Quy trình đóng gói và giao hàng sỉ
221
+
222
+ #### Chính sách bán hàng
223
+ - Công thức tính giá bán theo giá mua
224
+ - Bảng giá bán và chính sách giá
225
+ - Quản lý chiết khấu theo sản phẩm và theo hóa đơn
226
+ - Chính sách khuyến mãi theo sản phẩm và theo hóa đơn
227
+ - Chính sách hoa hồng bán hàng
228
+ - Mã giảm giá (E-voucher)
229
+
230
+ #### Báo cáo bán hàng
231
+ - Cân đối phát sinh công nợ phải thu
232
+ - Sổ chi tiết công nợ khách hàng
233
+ - Công nợ cuối kỳ
234
+ - Chi tiết thu tiền theo hóa đơn
235
+ - Hóa đơn bán hàng theo hạn thanh toán
236
+ - Công nợ phải thu theo tuổi nợ
237
+
238
+ #### Danh mục
239
+ - Đơn vị vận chuyển và gói cước
240
+ - Phương thức thanh toán
241
+ - Danh mục khách hàng và hạn mức công nợ
242
+ - Số dư đầu kỳ công nợ khách hàng
243
+ - Quản lý Affiliate link
244
+
245
+ ---
246
+
247
+ ### 4. Mua hàng
248
+
249
+ Quản lý toàn bộ quy trình mua hàng từ đề nghị, đặt hàng đến nhập kho và thanh toán.
250
+
251
+ #### Chứng từ mua hàng
252
+ - Đề nghị mua hàng
253
+ - Đơn đăng ký bán hàng từ nhà cung cấp
254
+ - Báo giá của nhà cung cấp
255
+ - Đơn hàng mua
256
+ - Phiếu nhập hàng
257
+ - Hóa đơn mua hàng trong nước và mua dịch vụ
258
+ - Chi phí mua hàng
259
+ - Xử lý hàng trả lại nhà cung cấp
260
+
261
+ #### Báo cáo mua hàng
262
+ - Cân đối phát sinh công nợ phải trả
263
+ - Sổ chi tiết công nợ nhà cung cấp
264
+ - Công nợ cuối kỳ
265
+ - Chi tiết chi tiền theo hóa đơn
266
+ - Hóa đơn mua hàng theo hạn thanh toán
267
+ - Công nợ phải trả theo tuổi nợ
268
+
269
+ #### Danh mục
270
+ - Bảng giá mua và giá thị trường
271
+ - Danh mục nhà cung cấp và hạn mức công nợ
272
+ - Số dư đầu kỳ công nợ nhà cung cấp
273
+
274
+ ---
275
+
276
+ ### 5. Nhà phân phối
277
+
278
+ Phân hệ chuyên dụng để quản lý mạng lưới nhà phân phối (NPP), theo dõi hoạt động nhập-bán hàng và tồn kho tại từng NPP.
279
+
280
+ #### Chứng từ
281
+ - Nhập hàng và hóa đơn nhập hàng từ công ty
282
+ - Bán hàng và xử lý hàng bán bị trả lại
283
+ - Nhập/xuất kho nội bộ NPP
284
+
285
+ #### Báo cáo
286
+ - Doanh thu bán hàng của nhà phân phối
287
+ - Chi tiết bán hàng theo khách hàng và theo sản phẩm
288
+ - Tổng hợp nhập-xuất-tồn kho NPP
289
+ - Sổ chi tiết vật tư và bảng kê nhập-xuất kho NPP
290
+
291
+ #### Danh mục
292
+ - Chính sách hoa hồng NPP
293
+ - Danh sách khách hàng của từng NPP
294
+ - Số dư đầu kỳ vật tư NPP
295
+
296
+ ---
297
+
298
+ ### 6. Quản lý kho
299
+
300
+ Quản lý toàn diện hàng tồn kho thực tế và kế toán kho.
301
+
302
+ #### Nghiệp vụ kho
303
+ - Phiếu xuất/nhập kho nội bộ
304
+ - Đề nghị điều chuyển và phiếu xuất/nhập điều chuyển giữa các kho
305
+ - Phiếu kiểm kê kho
306
+
307
+ #### Báo cáo kho
308
+ - Tổng hợp nhập-xuất-tồn (kho thực tế và kế toán)
309
+ - Sổ chi tiết vật tư (kho thực tế và kế toán)
310
+ - Tồn cuối kỳ (kho thực tế và kế toán)
311
+ - Thời gian tồn kho
312
+ - Bảng kê nhập-xuất kho
313
+ - Tính giá trung bình và bảng giá trung bình
314
+
315
+ #### Danh mục kho
316
+ - Danh mục kho, ngành hàng, thương hiệu, xuất xứ
317
+ - Quản lý sản phẩm, vật tư đa thuộc tính
318
+ - Nhóm vật tư, đơn vị tính và quy đổi đơn vị tính
319
+ - Danh mục lô hàng
320
+ - Hạn mức tồn kho
321
+
322
+ ---
323
+
324
+ ### 7. Sản xuất
325
+
326
+ Quản lý quy trình sản xuất từ lập kế hoạch đến hoàn thành.
327
+
328
+ - Lập lệnh sản xuất và theo dõi tiến độ
329
+ - Quản lý định mức nguyên vật liệu (BOM)
330
+ - Theo dõi chi phí sản xuất
331
+ - Báo cáo năng suất và sản lượng
332
+
333
+ ---
334
+
335
+ ### 8. Kế toán tài chính
336
+
337
+ Phân hệ kế toán đầy đủ theo chuẩn mực kế toán Việt Nam.
338
+
339
+ #### Thu chi và tiền mặt
340
+ - Phiếu thu, phiếu chi
341
+ - Quản lý quỹ tiền mặt và tiền gửi ngân hàng
342
+ - Đối chiếu sao kê ngân hàng
343
+
344
+ #### Kế toán tổng hợp
345
+ - Nhật ký chung và sổ cái
346
+ - Bảng cân đối kế toán
347
+ - Báo cáo kết quả kinh doanh
348
+ - Báo cáo lưu chuyển tiền tệ
349
+
350
+ #### Thuế
351
+ - Quản lý hóa đơn VAT đầu vào và đầu ra
352
+ - Bảng kê thuế GTGT
353
+ - Báo cáo thuế định kỳ
354
+
355
+ #### Tài sản cố định
356
+ - Quản lý danh mục tài sản
357
+ - Tính và theo dõi khấu hao
358
+ - Báo cáo tài sản cố định
359
+
360
+ ---
361
+
362
+ ### 9. Quản lý nhân sự
363
+
364
+ Quản lý toàn bộ vòng đời nhân viên và chính sách nhân sự.
365
+
366
+ #### Hồ sơ nhân sự
367
+ - Quản lý thông tin nhân viên và hợp đồng lao động
368
+ - Quản lý cơ cấu tổ chức và bộ phận
369
+ - Hồ sơ năng lực và đánh giá nhân viên
370
+
371
+ #### Chấm công và lương
372
+ - Thiết lập ca làm việc và lịch làm việc
373
+ - Chấm công tự động (máy chấm công, app)
374
+ - Tính lương, phụ cấp, thưởng và các khoản khấu trừ
375
+ - Bảng lương chi tiết và tổng hợp
376
+
377
+ #### Phúc lợi và bảo hiểm
378
+ - Quản lý BHXH, BHYT, BHTN
379
+ - Báo cáo đóng bảo hiểm
380
+ - Quản lý nghỉ phép, nghỉ bệnh
381
+
382
+ #### Tuyển dụng
383
+ - Quản lý tin tuyển dụng
384
+ - Theo dõi quy trình tuyển dụng
385
+ - Quản lý hồ sơ ứng viên
386
+
387
+ ---
388
+
389
+ ### 10. Quản trị hệ thống
390
+
391
+ Công cụ cấu hình và vận hành hệ thống.
392
+
393
+ - Quản lý người dùng và phân quyền theo vai trò
394
+ - Cấu hình đơn vị, chi nhánh, năm tài chính
395
+ - Quản lý danh mục dùng chung (tỉnh thành, ngân hàng, nghề nghiệp...)
396
+ - Nhật ký hoạt động hệ thống
397
+ - Sao lưu và phục hồi dữ liệu
398
+ - Cấu hình in ấn và mẫu chứng từ
399
+
400
+ ---
401
+
402
+ ## Giấy phép sử dụng
403
+
404
+ **Flexbiz-server** được cấp phép sử dụng theo điều khoản sau:
405
+
406
+ | Hoạt động | Được phép |
407
+ |---|---|
408
+ | Sử dụng cá nhân hoặc nội bộ doanh nghiệp | ✅ |
409
+ | Xây dựng ứng dụng/sản phẩm riêng trên nền tảng này | ✅ |
410
+ | Kinh doanh sản phẩm được xây dựng trên nền tảng này | ✅ |
411
+ | Mở rộng tính năng bằng cách viết thêm module mới | ✅ |
412
+ | Override (ghi đè) tính năng có sẵn của thư viện | ✅ |
413
+ | Chỉnh sửa trực tiếp mã nguồn gốc của thư viện | ❌ |
414
+ | Phân phối lại hoặc bán lại thư viện này | ❌ |
415
+ | Kỹ thuật đảo ngược mã nguồn thư viện (Reverse Engineering) | ❌ |
416
+
417
+ ### Quyền được phép
418
+
419
+ Người dùng **được phép**:
420
+
421
+ - Sử dụng Flexbiz-server làm nền tảng lõi để xây dựng các ứng dụng, sản phẩm ERP hoặc phần mềm đặc thù theo nhu cầu riêng.
422
+ - Viết thêm các module, tính năng, giao diện mới bên trên thư viện này mà không giới hạn.
423
+ - Override (ghi đè) các hành vi, logic hoặc giao diện mặc định của thư viện bằng cách mở rộng từ phía ứng dụng của mình.
424
+ - Kinh doanh, cung cấp dịch vụ hoặc bán sản phẩm được xây dựng trên nền tảng Flexbiz-server một cách bình thường.
425
+
426
+ ### Quyền không được phép
427
+
428
+ Người dùng **không được phép**:
429
+
430
+ - Chỉnh sửa, thay đổi, vá (patch) trực tiếp vào mã nguồn gốc của thư viện Flexbiz-server.
431
+ - Sao chép một phần hoặc toàn bộ mã nguồn thư viện để tạo ra một sản phẩm cạnh tranh hoặc phân phối lại.
432
+ - Thực hiện kỹ thuật đảo ngược nhằm mục đích tái tạo hoặc sao chép thư viện.
433
+
434
+ > **Tóm lại:** Flexbiz-server là nền tảng mở để xây dựng — bạn toàn quyền phát triển ứng dụng của mình lên trên đó và kinh doanh tự do, nhưng bản thân thư viện lõi phải được giữ nguyên vẹn.
435
+
436
+ Mọi vi phạm điều khoản sẽ bị xử lý theo quy định của pháp luật hiện hành về sở hữu trí tuệ.
437
+
438
+ ---
439
+
440
+ © Flexbiz. All rights reserved.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "flexbiz-server",
3
3
  "main": "./server/app.js",
4
4
  "description": "Flexible Server",
5
- "version": "12.6.13",
5
+ "version": "12.6.15",
6
6
  "author": {
7
7
  "name": "Van Truong Pham",
8
8
  "email": "invncur@gmail.com"
@@ -15,6 +15,8 @@
15
15
  "scripts": {},
16
16
  "license": "MIT",
17
17
  "dependencies": {
18
+ "@ai-sdk/anthropic": "^3.0.84",
19
+ "@ai-sdk/google": "^3.0.82",
18
20
  "@ai-sdk/openai": "^3.0.63",
19
21
  "@ffmpeg-installer/ffmpeg": "^1.0.20",
20
22
  "agenda": "^4.1.3",
@@ -72,7 +74,6 @@
72
74
  "nodemailer-smtp-transport": "^2.7.4",
73
75
  "nodemailer-wellknown": "^0.2.3",
74
76
  "numeral": "^2.0.6",
75
- "openai": "^6.37.0",
76
77
  "passport": "^0.4.1",
77
78
  "passport-http": "^0.3.0",
78
79
  "passport-http-bearer": "^1.0.1",
@@ -0,0 +1 @@
1
+ const runFinanceAgent=require("./ceoAgent"),runStaffAgent=require("./staffAgent"),runCSKHAgent=require("./cskhAgent"),runWorkflowAgent=require("./flowAgent");module.exports={runFinanceAgent,runStaffAgent,runCSKHAgent,runWorkflowAgent};
@@ -0,0 +1,6 @@
1
+ const {generateObject}=require("ai"),{z}=require("zod"),ChatMessage=global.getModel("chat_message"),AiMemory=global.getModel("aimemory"),getMemory=async($id_app_userMemory$$,$memoryPrompt_user_id$$)=>{$id_app_userMemory$$=await AiMemory.findOne({id_app:$id_app_userMemory$$,user_id:$memoryPrompt_user_id$$}).lean();$memoryPrompt_user_id$$="";$id_app_userMemory$$&&$id_app_userMemory$$.core_memories.length>0&&($memoryPrompt_user_id$$=$id_app_userMemory$$.core_memories.map($m$$=>`- **${$m$$}`).join("\n"));
2
+ return $memoryPrompt_user_id$$},summarizeUserMemory=async($id_app$$,$user_id$$,$session_id$$,$model$$)=>{try{let $memory$$=await AiMemory.findOne({id_app:$id_app$$,user_id:$user_id$$});$memory$$||=new AiMemory({id_app:$id_app$$,user_id:$user_id$$,core_memories:[]});$id_app$$={id_app:$id_app$$,user_id:$user_id$$,session_id:$session_id$$};$memory$$.last_summarized_date&&($id_app$$.date_created={$gt:$memory$$.last_summarized_date});const $unsummarizedMessages$$=await ChatMessage.find($id_app$$).sort({date_created:1}).limit(20).lean();
3
+ if(!($unsummarizedMessages$$.length<10)){var $chatText$$=$unsummarizedMessages$$.filter($msg$$=>$msg$$.role!=="tool"&&$msg$$.content).map($msg$$=>`${$msg$$.role.toUpperCase()}: ${$msg$$.content}`).join("\n"),{object:$object$$}=await generateObject({model:$model$$,schema:z.object({extracted_facts:z.array(z.string()).describe("M\u1ea3ng c\u00e1c th\u00f4ng tin quan tr\u1ecdng, s\u1edf th\u00edch, y\u00eau c\u1ea7u ho\u1eb7c b\u1ed1i c\u1ea3nh d\u00e0i h\u1ea1n c\u1ee7a ng\u01b0\u1eddi d\u00f9ng. B\u1ecf qua c\u00e1c c\u00e2u giao ti\u1ebfp th\u00f4ng th\u01b0\u1eddng.")}),
4
+ prompt:`D\u01b0\u1edbi \u0111\u00e2y l\u00e0 \u0111o\u1ea1n h\u1ed9i tho\u1ea1i g\u1ea7n nh\u1ea5t. H\u00e3y tr\u00edch xu\u1ea5t c\u00e1c th\u00f4ng tin quan tr\u1ecdng m\u00e0 AI c\u1ea7n GHI NH\u1eda D\u00c0I H\u1ea0N v\u1ec1 ng\u01b0\u1eddi d\u00f9ng (V\u00ed d\u1ee5: quy \u0111\u1ecbnh l\u00e0m vi\u1ec7c, v\u1ecb tr\u00ed \u0111\u1ecba l\u00fd, th\u00f3i quen thao t\u00e1c, \u0111\u1ecbnh d\u1ea1ng b\u00e1o c\u00e1o y\u00eau th\u00edch, t\u00ean d\u1ef1 \u00e1n \u0111ang l\u00e0m...). N\u1ebfu kh\u00f4ng c\u00f3 g\u00ec quan tr\u1ecdng, tr\u1ea3 v\u1ec1 m\u1ea3ng r\u1ed7ng.\n\nH\u1ed9i tho\u1ea1i:\n${$chatText$$}`});
5
+ $object$$.extracted_facts&&$object$$.extracted_facts.length>0&&($memory$$.core_memories=[...$memory$$.core_memories,...$object$$.extracted_facts].slice(-50));$memory$$.last_summarized_date=$unsummarizedMessages$$[$unsummarizedMessages$$.length-1].date_created;await $memory$$.save();console.log(`[Memory Worker] \u0110\u00e3 c\u1eadp nh\u1eadt ${$object$$.extracted_facts.length} tr\u00ed nh\u1edb m\u1edbi cho user ${$user_id$$}`)}}catch($error$$){console.error("[Memory Worker Error]:",$error$$)}};
6
+ module.exports={summarizeUserMemory,getMemory};
@@ -0,0 +1,43 @@
1
+ const {generateText,stepCountIs,pruneMessages}=require("ai"),chatController=require("./chatController"),{decrypt}=require("../libs/utils"),{summarizeUserMemory,getMemory}=require("./ai_memory_manager"),{createFinanceTools}=require("./tools/financeTools"),{createStockTools}=require("./tools/stockTools"),{createRagTools}=require("./tools/ragTools"),{createStaffTools}=require("./tools/staffTools"),{createSystemTools}=require("./tools/systemTools"),{createMasterDataTools}=require("./tools/masterDataTools"),
2
+ {createKpiTools}=require("./tools/kpiTools"),{createOkrTools}=require("./tools/okrTools"),{createTaskTools}=require("./tools/taskTools"),{createReportTools}=require("./tools/reportTools"),{createFlowTools}=require("./tools/flowTools"),tenantConfigs={},SYSTEM_PROMPT=require("./prompts/ceo"),{isValidObjectId}=require("mongoose");
3
+ async function runFinanceAgent($id_app$$,$user_id$$,$session_id$$,$userPrompt$$,$files_of_message$$){try{let $is_pure_file_upload$$=!1;(!$userPrompt$$||$userPrompt$$.trim()==="")&&$files_of_message$$&&$files_of_message$$.length>0&&($is_pure_file_upload$$=!0,$userPrompt$$=`Anh v\u1eeba t\u1ea3i l\u00ean ${$files_of_message$$.length} file t\u00e0i li\u1ec7u m\u00e0 ch\u01b0a ghi ch\u00fa g\u00ec. Em h\u00e3y \u0111\u1ecdc l\u01b0\u1edbt qua d\u1eef li\u1ec7u th\u00f4 c\u1ee7a file n\u00e0y, nh\u1eadn di\u1ec7n xem \u0111\u00e2y l\u00e0 lo\u1ea1i gi\u1ea5y t\u1edd g\u00ec (H\u00f3a \u0111\u01a1n, h\u1ee3p \u0111\u1ed3ng, b\u00e1o c\u00e1o...), tr\u00edch xu\u1ea5t 3 th\u00f4ng tin quan tr\u1ecdng nh\u1ea5t v\u00e0 \u0111\u01b0a ra c\u00e1c g\u1ee3i \u00fd h\u00e0nh \u0111\u1ed9ng k\u1ebf ti\u1ebfp trong h\u1ec7 th\u1ed1ng ERP \u0111\u1ec3 anh ch\u1ecdn nh\u00e9.`);
4
+ var $app_systemInstruction$$=await global.getModel("app").findOne({_id:$id_app$$},{ai_name:1,ai_api_key:1,ai_model:1});if(!$app_systemInstruction$$||!$app_systemInstruction$$.ai_api_key||!$app_systemInstruction$$.ai_model)throw Error("Ch\u01b0a khai b\u00e1o API Key ho\u1eb7c Model cho h\u1ec7 th\u1ed1ng.");var $gioi_tinh_isCacheStale_messagesToSend$$=tenantConfigs[$id_app$$]&&tenantConfigs[$id_app$$].modelName!==$app_systemInstruction$$.ai_model;if(!tenantConfigs[$id_app$$]||$gioi_tinh_isCacheStale_messagesToSend$$){const $api_key$$=
5
+ decrypt($app_systemInstruction$$.ai_api_key.replace("flexbiz::",""),!1),$aiType$$=($app_systemInstruction$$.ai_name||"GPT").toUpperCase();let $aiModelInstance$$;switch($aiType$$){case "CLAUDE":case "ANTHROPIC":const {createAnthropic:$createAnthropic$$}=require("@ai-sdk/anthropic");$aiModelInstance$$=$createAnthropic$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model);break;case "GEMINI":case "GOOGLE":const {createGoogleGenerativeAI:$createGoogleGenerativeAI$$}=require("@ai-sdk/google");$aiModelInstance$$=
6
+ $createGoogleGenerativeAI$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model);break;default:const {createOpenAI:$createOpenAI$$}=require("@ai-sdk/openai");$aiModelInstance$$=$createOpenAI$$({apiKey:$api_key$$})($app_systemInstruction$$.ai_model)}tenantConfigs[$id_app$$]={model:$aiModelInstance$$,modelName:$app_systemInstruction$$.ai_model};Logger.info(`[Multi-LLM] \u0110\u00e3 kh\u1edfi t\u1ea1o th\u00e0nh c\u00f4ng model ${$app_systemInstruction$$.ai_model} (${$aiType$$}) cho App: ${$id_app$$}`)}const {model:$model$$}=
7
+ tenantConfigs[$id_app$$];$session_id$$||=(await chatController.createSession($id_app$$,$user_id$$,$userPrompt$$.substring(0,1024)))._id.toString();const $activeTools$$={...createStaffTools($id_app$$,$user_id$$,$session_id$$),...createFinanceTools($id_app$$,$user_id$$,$session_id$$),...createStockTools($id_app$$,$user_id$$,$session_id$$),...createRagTools($id_app$$,$user_id$$,$session_id$$,$files_of_message$$),...createSystemTools($id_app$$,$user_id$$,$session_id$$),...createMasterDataTools($id_app$$,
8
+ $user_id$$,$session_id$$),...createKpiTools($id_app$$,$user_id$$,$session_id$$),...createOkrTools($id_app$$,$user_id$$,$session_id$$),...createTaskTools($id_app$$,$user_id$$,$session_id$$),...createReportTools($id_app$$,$user_id$$,$session_id$$),...createFlowTools($id_app$$,$user_id$$,$session_id$$)};$app_systemInstruction$$=`${SYSTEM_PROMPT}`;const [$previousMessages$$,$userInfoDoc$$,$appInfoDoc$$,$memory$$]=await Promise.all([chatController.getChatHistory($id_app$$,$session_id$$,30),global.getModel("participant").findOne({id_app:$id_app$$,
9
+ email:$user_id$$}).lean(),global.getModel("app").findOne({_id:$id_app$$}).lean(),getMemory($id_app$$,$user_id$$)]);$gioi_tinh_isCacheStale_messagesToSend$$=[...$previousMessages$$,{role:"user",content:$userPrompt$$}];const $optimizedMessages$$=pruneMessages({messages:$gioi_tinh_isCacheStale_messagesToSend$$,reasoning:"all",toolCalls:"before-last-message"});$appInfoDoc$$&&($app_systemInstruction$$+=`
10
+ \n# [TH\u00d4NG TIN DOANH NGHI\u1ec6P C\u1ee6A B\u1ea0N]
11
+ B\u1ea1n \u0111ang \u0111\u1ea1i di\u1ec7n v\u00e0 \u0111i\u1ec1u h\u00e0nh h\u1ec7 th\u1ed1ng cho doanh nghi\u1ec7p sau:
12
+ - **T\u00ean doanh nghi\u1ec7p:** ${$appInfoDoc$$.name||"H\u1ec7 th\u1ed1ng n\u1ed9i b\u1ed9"}
13
+ - **M\u00e3 s\u1ed1 thu\u1ebf:** ${$appInfoDoc$$.ma_so_thue||"Ch\u01b0a c\u1eadp nh\u1eadt"}
14
+ - **\u0110\u1ecba ch\u1ec9:** ${$appInfoDoc$$.address||$appInfoDoc$$.dia_chi||"Ch\u01b0a c\u1eadp nh\u1eadt"}
15
+ - **\u0110\u1ecba ch\u1ec9 xu\u1ea5t ho\u00e1 \u0111\u01a1n:** ${$appInfoDoc$$.dia_chi_xuat_hoa_don||"Ch\u01b0a c\u1eadp nh\u1eadt"}
16
+ - **Li\u00ean h\u1ec7:** ${$appInfoDoc$$.phone||$appInfoDoc$$.dien_thoai||""} ${$appInfoDoc$$.email?" | "+$appInfoDoc$$.email:""}
17
+
18
+ [CH\u1ec8 TH\u1eca C\u1ed0T L\u00d5I V\u1ec0 DOANH NGHI\u1ec6P]:
19
+ M\u1ecdi s\u1ed1 li\u1ec7u doanh thu, chi ph\u00ed, c\u00f4ng n\u1ee3 m\u00e0 b\u1ea1n ph\u00e2n t\u00edch \u0111\u1ec1u l\u00e0 t\u00e0i s\u1ea3n c\u1ee7a doanh nghi\u1ec7p n\u00e0y. Khi c\u00f3 y\u00eau c\u1ea7u so\u1ea1n th\u1ea3o v\u0103n b\u1ea3n, h\u1ee3p \u0111\u1ed3ng, email ho\u1eb7c Zalo g\u1eedi \u0111\u1ed1i t\u00e1c ngo\u00e0i, B\u1eaeT BU\u1ed8C ph\u1ea3i d\u00f9ng ch\u00ednh x\u00e1c th\u00f4ng tin \u0111\u1ecbnh danh ph\u00eda tr\u00ean \u0111\u1ec3 l\u00e0m ti\u00eau \u0111\u1ec1 ho\u1eb7c ch\u1eef k\u00fd.
20
+ `);if($userInfoDoc$$){const $thongtinnhanvien$$=await global.getModel("dmnv").findOne({id_app:$id_app$$,user:$user_id$$}).lean(),$fullName$$=$thongtinnhanvien$$?.ten_nv||$userInfoDoc$$.name||"S\u1ebfp",$shortName$$=$fullName$$!=="S\u1ebfp"?$fullName$$.split(" ").pop():"S\u1ebfp";$gioi_tinh_isCacheStale_messagesToSend$$=$thongtinnhanvien$$?.gioi_tinh||"Kh\u00e1c";let $chuc_vu$$,$bo_phan$$;$thongtinnhanvien$$?.chuc_vu&&isValidObjectId($thongtinnhanvien$$?.chuc_vu)&&($chuc_vu$$=(await global.getModel("group").findOne({id_app:$id_app$$,
21
+ _id:$thongtinnhanvien$$.chuc_vu}).lean())?.group_name);$thongtinnhanvien$$?.ma_bp&&($bo_phan$$=(await global.getModel("dmbp").findOne({id_app:$id_app$$,ma_bp:$thongtinnhanvien$$.ma_bp}).lean())?.ten_bp);$app_systemInstruction$$+=`
22
+ \n# [TH\u00d4NG TIN \u0110\u1ed0I T\u00c1C GIAO TI\u1ebeP C\u1ee6A B\u1ea0N]
23
+ B\u1ea1n \u0111ang b\u00e1o c\u00e1o v\u00e0 h\u1ed7 tr\u1ee3 tr\u1ef1c ti\u1ebfp cho nh\u00e2n s\u1ef1 sau:
24
+ - **H\u1ecd v\u00e0 t\u00ean:** ${$fullName$$}
25
+ - **Gi\u1edbi t\u00ednh:** ${$gioi_tinh_isCacheStale_messagesToSend$$}
26
+ - **T\u00e0i kho\u1ea3n \u0111\u1ecbnh danh:** ${$userInfoDoc$$.email||$user_id$$}
27
+ - **Vai tr\u00f2/Ch\u1ee9c v\u1ee5:** ${$chuc_vu$$||$userInfoDoc$$.role||$userInfoDoc$$.chuc_vu||"Ban \u0110i\u1ec1u H\u00e0nh"}
28
+ - **Ph\u00f2ng ban c\u00f4ng t\u00e1c:** ${$bo_phan$$||"V\u0103n ph\u00f2ng T\u1ed5ng Gi\u00e1m \u0110\u1ed1c"}
29
+ - **S\u1ed1 \u0111i\u1ec7n tho\u1ea1i/Li\u00ean h\u1ec7:** ${$thongtinnhanvien$$?.dien_thoai||$userInfoDoc$$.dien_thoai||"Ch\u01b0a c\u1eadp nh\u1eadt"}
30
+
31
+ [CH\u1ec8 TH\u1eca C\u00c1 NH\u00c2N H\u00d3A - B\u1eaeT BU\u1ed8C T\u01af\u01a0NG T\u00c1C]:
32
+ 1. **X\u01b0ng h\u00f4 th\u00e2n m\u1eadt nh\u01b0ng chuy\u00ean nghi\u1ec7p:** Trong c\u00e1c b\u00e1o c\u00e1o quan tr\u1ecdng ho\u1eb7c khi ch\u1ed1t v\u1ea5n \u0111\u1ec1, thi tho\u1ea3ng h\u00e3y g\u1ecdi ng\u01b0\u1eddi d\u00f9ng b\u1eb1ng t\u00ean thay v\u00ec ch\u1ec9 d\u00f9ng t\u1eeb "S\u1ebfp" chung chung (V\u00ed d\u1ee5: "D\u1ea1 th\u01b0a anh/ch\u1ecb ${$shortName$$}...", "Theo em, gi\u1ea3i ph\u00e1p t\u1ed1t nh\u1ea5t cho ph\u00f2ng ${$bo_phan$$||"c\u1ee7a anh/ch\u1ecb"} l\u00e0...").
33
+ 2. **T\u01b0 duy theo v\u1ecb tr\u00ed:** H\u00e3y t\u1ef1 \u0111\u1ed9ng \u0111i\u1ec1u ch\u1ec9nh \u0111\u1ed9 chi ti\u1ebft c\u1ee7a b\u00e1o c\u00e1o d\u1ef1a tr\u00ean ch\u1ee9c v\u1ee5. N\u1ebfu S\u1ebfp thu\u1ed9c kh\u1ed1i Qu\u1ea3n tr\u1ecb, t\u1eadp trung v\u00e0o d\u00f2ng ti\u1ec1n v\u00e0 t\u1ed5ng quan. N\u1ebfu S\u1ebfp thu\u1ed9c kh\u1ed1i V\u1eadn h\u00e0nh/Sale, t\u1eadp trung v\u00e0o chi ti\u1ebft \u0111\u01a1n h\u00e0ng v\u00e0 kh\u00e1ch h\u00e0ng.
34
+ `}$memory$$&&($app_systemInstruction$$=`${$app_systemInstruction$$}
35
+ # [TH\u00d4NG TIN L\u01afU TR\u1eee V\u1ec0 NG\u01af\u1edcI D\u00d9NG]
36
+ *B\u1ea1n ph\u1ea3i ghi nh\u1edb v\u00e0 tu\u00e2n th\u1ee7 c\u00e1c th\u00f4ng tin sau:
37
+ ${$memory$$}
38
+ `);if($files_of_message$$&&$files_of_message$$.length>0){Logger.info(`[Backend Core] Ph\u00e1t hi\u1ec7n ${$files_of_message$$.length} file \u0111\u00ednh k\u00e8m. \u0110ang ti\u1ebfn h\u00e0nh nh\u1ed3i context tr\u1ef1c ti\u1ebfp...`);const $chunks$$=await global.getModel("filechunk").find({id_app:$id_app$$,file_id:{$in:$files_of_message$$}}).sort({file_id:1,chunk_index:1}).lean();if($chunks$$&&$chunks$$.length>0)if($chunks$$.length<=40){let $fileContextBlock$$="\n\n# [D\u1eee LI\u1ec6U \u0110\u1eb6C BI\u1ec6T: N\u1ed8I DUNG FILE \u0110\u00cdNH K\u00c8M C\u1ee6A TIN NH\u1eaeN N\u00c0Y]\n S\u1ebfp \u0111\u00e3 t\u1ea3i l\u00ean c\u00e1c file t\u00e0i li\u1ec7u/h\u00f3a \u0111\u01a1n \u0111i k\u00e8m v\u1edbi c\u00e2u h\u1ecfi n\u00e0y. H\u1ec7 th\u1ed1ng \u0111\u00e3 tr\u00edch xu\u1ea5t to\u00e0n b\u1ed9 v\u0103n b\u1ea3n d\u01b0\u1edbi \u0111\u00e2y. \n B\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i \u0111\u1ecdc n\u1ed9i dung n\u00e0y \u0111\u1ec3 tr\u1ea3 l\u1eddi S\u1ebfp NGAY L\u1eacP T\u1ee8C m\u00e0 KH\u00d4NG C\u1ea6N g\u1ecdi tool 'searchKnowledgeBaseTool':\n";
39
+ $chunks$$.forEach($c$$=>{$fileContextBlock$$+=`\n[M\u00e3 File: ${$c$$.file_id} | \u0110o\u1ea1n ${$c$$.chunk_index}]:\n${$c$$.content}\n`});$fileContextBlock$$+="\n[K\u1ebeT TH\u00daC V\u0102N B\u1ea2N FILE]. H\u00e3y ph\u00e2n t\u00edch d\u1eef li\u1ec7u tr\u00ean v\u00e0 tr\u1ea3 l\u1eddi tr\u1ef1c di\u1ec7n y\u00eau c\u1ea7u c\u1ee7a S\u1ebfp.";$app_systemInstruction$$+=$fileContextBlock$$;Logger.info(`[Backend Core] \u0110\u00e3 nh\u1ed3i th\u00e0nh c\u00f4ng ${$chunks$$.length} chunks v\u00e0o System Prompt.`)}else $app_systemInstruction$$+=
40
+ "\n\n[C\u1ea2NH B\u00c1O H\u1ec6 TH\u1ed0NG]: S\u1ebfp v\u1eeba g\u1eedi c\u00e1c file c\u00f3 dung l\u01b0\u1ee3ng c\u1ef1c l\u1edbn. B\u1ea1n B\u1eaeT BU\u1ed8C ph\u1ea3i g\u1ecdi c\u00f4ng c\u1ee5 'searchKnowledgeBaseTool' \u0111\u1ec3 qu\u00e9t d\u1eef li\u1ec7u, kh\u00f4ng \u0111\u01b0\u1ee3c t\u1ef1 \u00fd \u0111o\u00e1n m\u00f2."}$is_pure_file_upload$$&&($app_systemInstruction$$+="\n\n[CH\u1ec8 TH\u1eca \u0110\u1eb6C BI\u1ec6T]: Ng\u01b0\u1eddi d\u00f9ng v\u1eeba upload file thu\u1ea7n t\u00fay. H\u00e3y hi\u1ec3n th\u1ecb k\u1ebft qu\u1ea3 c\u1ef1c k\u1ef3 ng\u1eafn g\u1ecdn d\u1ea1ng Dashboard mini. Tr\u00edch xu\u1ea5t c\u00e1c th\u1ef1c th\u1ec3 ch\u00ednh (M\u00e3, S\u1ed1 ti\u1ec1n, \u0110\u1ed1i t\u00e1c) v\u00e0 li\u1ec7t k\u00ea 3 h\u00e0nh \u0111\u1ed9ng th\u1ef1c t\u1ebf d\u1ef1a tr\u00ean c\u00e1c tool h\u1ec7 th\u1ed1ng \u0111ang c\u00f3 (V\u00ed d\u1ee5: d\u00f9ng manageMasterDataDocumentTool \u0111\u1ec3 t\u1ea1o phi\u1ebfu, d\u00f9ng sendEmailTool \u0111\u1ec3 g\u1eedi...).");
41
+ const $result$$=await generateText({model:$model$$,system:$app_systemInstruction$$,messages:$optimizedMessages$$,tools:$activeTools$$,stopWhen:stepCountIs(10)});Logger.info("[ceoAgent] l\u01b0u messages v\u00e0o database..");const $msgsToSave$$=[{role:"user",content:$is_pure_file_upload$$?"":$userPrompt$$,files:$files_of_message$$},...$result$$.response.messages.map($msg$$=>({role:$msg$$.role,content:$msg$$.content?Array.isArray($msg$$.content)?$msg$$.content[0]?.text:$msg$$.content:"",tool_calls:$msg$$.tool_calls||
42
+ null,tool_call_id:$msg$$.tool_call_id||null,messages:[$msg$$]}))];try{await chatController.saveMessages($id_app$$,$session_id$$,$msgsToSave$$)}catch($e$$){Logger.error("[agents][runFinanceAgent] kh\u00f4ng th\u1ec3 l\u01b0u n\u1ed9i dung chat. Err:",$e$$)}summarizeUserMemory($id_app$$,$user_id$$,$session_id$$,$model$$);return{session_id:$session_id$$,answer:$msgsToSave$$.length>1?$msgsToSave$$.pop()?.content||"Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i":"Kh\u00f4ng c\u00f3 k\u1ebft qu\u1ea3 ph\u1ea3n h\u1ed3i"}}catch($error$$){throw console.error(`[L\u1ed7i Agent CEO] App: ${$id_app$$} | Session: ${$session_id$$} | Err:`,
43
+ $error$$),Error($error$$.message);}}module.exports=runFinanceAgent;
@@ -0,0 +1,6 @@
1
+ const ChatSession=getModel("chat_session"),ChatMessage=getModel("chat_message"),utils=require("../libs/utils"),sanitizeMongoKeys=$obj$$=>{if($obj$$===null||typeof $obj$$!=="object")return $obj$$;if(Array.isArray($obj$$))return $obj$$.map(sanitizeMongoKeys);const $newObj$$={};for(const [$key$$,$value$$]of Object.entries($obj$$))$obj$$=$key$$,$obj$$.startsWith("$")&&($obj$$="_"+$obj$$),$obj$$.includes(".")&&($obj$$=$obj$$.replace(/\./g,"_")),$newObj$$[$obj$$]=sanitizeMongoKeys($value$$);return $newObj$$},
2
+ chatController={createSession:async($id_app_session$$,$user_id$$,$title$$="\u0110o\u1ea1n chat m\u1edbi")=>{$id_app_session$$=new ChatSession({id_app:$id_app_session$$,user_id:$user_id$$,title:$title$$});await $id_app_session$$.save();return $id_app_session$$},getChatHistory:async($id_app$$,$session_id$$,$limit$$=30)=>{$id_app$$=await ChatMessage.find({id_app:$id_app$$,session_id:$session_id$$}).sort({date_created:-1}).limit($limit$$).lean();$id_app$$.sort(($a$$,$b$$)=>new Date($a$$.date_created)-
3
+ new Date($b$$.date_created));return $id_app$$.map($msg$$=>({role:$msg$$.role,content:$msg$$.content||"",tool_calls:$msg$$.tool_calls||void 0,tool_call_id:$msg$$.tool_call_id||void 0,messages:$msg$$.messages||void 0}))},getPublicChatHistory:async($id_app$$,$messages$jscomp$1_session_id$$,$limit$$=20)=>{$messages$jscomp$1_session_id$$=await ChatMessage.find({id_app:$id_app$$,session_id:$messages$jscomp$1_session_id$$,$and:[{role:{$ne:"tool"}},{content:{$ne:""}},{content:{$ne:null}}]}).sort({date_created:-1}).limit($limit$$).lean();
4
+ for(let $msg$$ of $messages$jscomp$1_session_id$$)$msg$$.files?.length>0&&($msg$$.files=$msg$$.files.map($f$$=>({file_id:$f$$})),await $msg$$.files.asyncJoinModel2($id_app$$,"file",{where:{file_id:"_id"},fields:{info:"file"}}));$messages$jscomp$1_session_id$$.sort(($a$$,$b$$)=>new Date($a$$.date_created)-new Date($b$$.date_created));return $messages$jscomp$1_session_id$$.map($msg$$=>({_id:$msg$$._id,role:$msg$$.role==="assistant"?"assistant":"user",content:$msg$$.content||"",messages:$msg$$.messages,
5
+ files:$msg$$.files}))},saveMessages:async($id_app$$,$session_id$$,$msgsToInsert_newMessages$$)=>{$msgsToInsert_newMessages$$&&$msgsToInsert_newMessages$$.length!==0&&(Logger.info("[chatController][saveMessages]..."),$msgsToInsert_newMessages$$=JSON.parse(JSON.stringify($msgsToInsert_newMessages$$)),$msgsToInsert_newMessages$$=$msgsToInsert_newMessages$$.map($msg$$=>({id_app:$id_app$$,session_id:$session_id$$,role:$msg$$.role,content:$msg$$.content,files:$msg$$.files,messages:sanitizeMongoKeys($msg$$.messages)})),
6
+ $msgsToInsert_newMessages$$=utils.restoreObjectIds($msgsToInsert_newMessages$$),await ChatMessage.create($msgsToInsert_newMessages$$),await ChatSession.updateOne({_id:$session_id$$},{$set:{date_updated:new Date}}))}};module.exports=chatController;
@@ -0,0 +1,43 @@
1
+ const {generateText,stepCountIs,pruneMessages}=require("ai"),{createOpenAI}=require("@ai-sdk/openai"),chatController=require("./chatController"),{decrypt}=require("../libs/utils"),{createFinanceTools}=require("./tools/financeTools"),{createStockTools}=require("./tools/stockTools"),{createRagTools}=require("./tools/ragTools"),tenantConfigs={},SYSTEM_PROMPT=`# VAI TR\u00d2 V\u00c0 \u0110\u1ecaA V\u1eca C\u1ee6A B\u1ea0N
2
+ B\u1ea1n l\u00e0 Tr\u1ee3 l\u00fd AI chuy\u00ean nghi\u1ec7p thu\u1ed9c ph\u00f2ng Ch\u0103m s\u00f3c Kh\u00e1ch h\u00e0ng v\u00e0 Qu\u1ea3n tr\u1ecb Quan h\u1ec7 Kh\u00e1ch h\u00e0ng (CRM). Nhi\u1ec7m v\u1ee5 c\u1ed1t l\u00f5i c\u1ee7a b\u1ea1n l\u00e0 h\u1ed7 tr\u1ee3 nh\u00e2n vi\u00ean CSKH tra c\u1ee9u th\u00f4ng tin kh\u00e1ch h\u00e0ng, ki\u1ec3m tra tr\u1ea1ng th\u00e1i s\u1ea3n ph\u1ea9m, ch\u00ednh s\u00e1ch gi\u00e1 b\u00e1n, ch\u01b0\u01a1ng tr\u00ecnh khuy\u1ebfn m\u00e3i, gi\u1ea3i quy\u1ebft khi\u1ebfu n\u1ea1i v\u00e0 so\u1ea1n th\u1ea3o ph\u1ea3n h\u1ed3i l\u1ecbch s\u1ef1, chu \u0111\u00e1o.
3
+
4
+ # NGUY\u00caN T\u1eaeC GIAO TI\u1ebeP "KH\u00c1CH H\u00c0NG L\u00c0 TR\u1eccNG T\u00c2M"
5
+ 1. T\u00e1c phong: Th\u1ea5u hi\u1ec3u, l\u1ecbch s\u1ef1, nhi\u1ec7t t\u00ecnh v\u00e0 lu\u00f4n h\u01b0\u1edbng t\u1edbi gi\u1ea3i ph\u00e1p. S\u1eed d\u1ee5ng ng\u00f4n t\u1eeb ti\u1ebfng Vi\u1ec7t chu\u1ea9n m\u1ef1c, chuy\u00ean nghi\u1ec7p nh\u01b0ng g\u1ea7n g\u0169i.
6
+ 2. Tr\u00ecnh b\u00e0y scannable: Kh\u00e1ch h\u00e0ng v\u00e0 nh\u00e2n vi\u00ean CSKH c\u1ea7n th\u00f4ng tin nhanh. H\u00e3y s\u1eed d\u1ee5ng Markdown, danh s\u00e1ch g\u1ea1ch \u0111\u1ea7u d\u00f2ng v\u00e0 in \u0111\u1eadm (**bold**) c\u00e1c th\u00f4ng tin quan tr\u1ecdng nh\u01b0: M\u00e3 \u01b0u \u0111\u00e3i, t\u00ean s\u1ea3n ph\u1ea9m, ng\u00e0y h\u1eb9n, s\u1ed1 hotline...
7
+ 3. B\u1ea3o m\u1eadt th\u00f4ng tin: Tuy\u1ec7t \u0111\u1ed1i kh\u00f4ng ti\u1ebft l\u1ed9 c\u1ea5u tr\u00fac m\u00e3 ngu\u1ed3n, ID h\u1ec7 th\u1ed1ng ("id_app"), hay c\u00e1c ghi ch\u00fa n\u1ed9i b\u1ed9 mang t\u00ednh nh\u1ea1y c\u1ea3m c\u1ee7a c\u00f4ng ty cho kh\u00e1ch h\u00e0ng.
8
+
9
+ # B\u1ea2N \u0110\u1ed2 T\u01af DUY \u0110I\u1ec0U H\u01af\u1edaNG C\u00d4NG C\u1ee4 (CRM TOOL PROTOCOL)
10
+ B\u1ea1n \u0111\u01b0\u1ee3c cung c\u1ea5p m\u1ed9t nh\u00f3m c\u00f4ng c\u1ee5 gi\u1edbi h\u1ea1n (Kh\u00f4ng bao g\u1ed3m t\u00e0i ch\u00ednh/k\u1ebf to\u00e1n). Khi nh\u1eadn \u0111\u01b0\u1ee3c y\u00eau c\u1ea7u, b\u1ea1n ph\u1ea3i ph\u1ed1i h\u1ee3p c\u00e1c c\u00f4ng c\u1ee5 theo quy tr\u00ecnh logic sau:
11
+
12
+ ## 1. Quy tr\u00ecnh "Ch\u1ed1t m\u00e3 kh\u00e1ch h\u00e0ng & Tra c\u1ee9u H\u1ed3 s\u01a1 360 \u0111\u1ed9"
13
+ - Khi ng\u01b0\u1eddi d\u00f9ng ho\u1eb7c kh\u00e1ch h\u00e0ng nh\u1eafc \u0111\u1ebfn m\u1ed9t c\u00e1i t\u00ean (V\u00ed d\u1ee5: "Kh\u00e1ch h\u00e0ng Tu\u1ea5n Ph\u00e1t", "Anh Nguy\u1ec5n V\u0103n A"), b\u1ea1n KH\u00d4NG \u0110\u01af\u1ee2C t\u1ef1 \u0111o\u00e1n m\u00e3.
14
+ - B\u1eaeT BU\u1ed8C g\u1ecdi "lookupMasterDataTool" \u0111\u1ea7u ti\u00ean (ch\u1ecdn type: "customer" ho\u1eb7c "product") \u0111\u1ec3 l\u1ea5y ch\u00ednh x\u00e1c M\u00e3 s\u1ed1 ("ma_kh", "ma_vt"). N\u1ebfu tool lookupMasterDataTool tr\u1ea3 v\u1ec1 nhi\u1ec1u k\u1ebft qu\u1ea3 th\u00ec c\u1ea7n hi\u1ec7n ra danh s\u00e1ch \u0111\u00e3 t\u00ecm th\u1ea5y v\u00e0 h\u1ecfi ng\u1eeboi d\u00f9ng xem ch\u1ecdn c\u00e1i n\u00e0o.
15
+ - **X\u1eed l\u00fd chuy\u00ean s\u00e2u:** \u0110\u1ec3 xem h\u1ed3 s\u01a1 chi ti\u1ebft c\u1ee7a kh\u00e1ch h\u00e0ng ph\u1ee5c v\u1ee5 x\u1eed l\u00fd khi\u1ebfu n\u1ea1i ho\u1eb7c t\u01b0 v\u1ea5n, h\u00e3y b\u1eadt tham s\u1ed1 "include_relations: true". C\u00f4ng c\u1ee5 n\u00e0y s\u1ebd tr\u1ea3 v\u1ec1 to\u00e0n b\u1ed9: L\u1ecbch s\u1eed cu\u1ed9c g\u1ecdi t\u1ed5ng \u0111\u00e0i g\u1ea7n nh\u1ea5t, c\u00e1c h\u1ee3p \u0111\u1ed3ng \u0111\u00e3 k\u00fd, th\u00f4ng tin li\u00ean h\u1ec7 v\u00e0 c\u00e1c ghi ch\u00fa d\u1eb7n d\u00f2 tr\u01b0\u1edbc \u0111\u00f3 v\u1ec1 kh\u00e1ch h\u00e0ng n\u00e0y.
16
+
17
+ ## 2. Tra c\u1ee9u Gi\u00e1 b\u00e1n & Ch\u01b0\u01a1ng tr\u00ecnh Khuy\u1ebfn m\u00e3i
18
+ - Khi kh\u00e1ch h\u00e0ng h\u1ecfi v\u1ec1 gi\u00e1, mu\u1ed1n nh\u1eadn b\u00e1o gi\u00e1 ho\u1eb7c h\u1ecfi v\u1ec1 c\u00e1c ch\u01b0\u01a1ng tr\u00ecnh \u01b0u \u0111\u00e3i hi\u1ec7n c\u00f3 cho m\u1ed9t m\u1eb7t h\u00e0ng $\rightarrow$ H\u00e3y g\u1ecdi c\u00f4ng c\u1ee5 "getProductPriceAndPromotionTool".
19
+ - *L\u01b0u \u00fd:* C\u00f4ng c\u1ee5 n\u00e0y b\u1eaft bu\u1ed9c ph\u1ea3i truy\u1ec1n v\u00e0o "ma_vt" (\u0111\u00e3 l\u1ea5y t\u1eeb b\u01b0\u1edbc tra c\u1ee9u danh m\u1ee5c). B\u1ea1n c\u00f3 th\u1ec3 truy\u1ec1n th\u00eam m\u00e3 kh\u00e1ch h\u00e0ng ho\u1eb7c s\u1ed1 l\u01b0\u1ee3ng d\u1ef1 ki\u1ebfn \u0111\u1ec3 h\u1ec7 th\u1ed1ng t\u00ednh to\u00e1n m\u1ee9c chi\u1ebft kh\u1ea5u ch\u00ednh x\u00e1c nh\u1ea5t cho kh\u00e1ch h\u00e0ng.
20
+
21
+ ## 3. Ki\u1ec3m tra Tr\u1ea1ng th\u00e1i H\u00e0ng h\u00f3a (\u0110\u1ec3 t\u01b0 v\u1ea5n \u0111\u1eb7t h\u00e0ng)
22
+ - Khi kh\u00e1ch h\u00e0ng h\u1ecfi "S\u1ea3n ph\u1ea9m n\u00e0y c\u00f2n h\u00e0ng trong kho kh\u00f4ng?" ho\u1eb7c "Kho n\u00e0o c\u00f2n h\u00e0ng?" $\rightarrow$ G\u1ecdi c\u00f4ng c\u1ee5 "getInventorySummaryTool" \u0111\u1ec3 xem s\u1ed1 l\u01b0\u1ee3ng t\u1ed3n kho th\u1ef1c t\u1ebf, gi\u00fap ph\u1ea3n h\u1ed3i kh\u00e1ch h\u00e0ng ch\u00ednh x\u00e1c, tr\u00e1nh vi\u1ec7c b\u00e1n h\u00e0ng v\u01b0\u1ee3t qu\u00e1 s\u1ed1 l\u01b0\u1ee3ng t\u1ed3n.
23
+
24
+ ## 4. Tra c\u1ee9u Tri th\u1ee9c, Quy tr\u00ecnh x\u1eed l\u00fd & L\u1ecbch s\u1eed h\u1ed7 tr\u1ee3
25
+ - Khi g\u1eb7p c\u00e1c c\u00e2u h\u1ecfi kh\u00f3 v\u1ec1 ch\u00ednh s\u00e1ch b\u1ea3o h\u00e0nh, quy tr\u00ecnh \u0111\u1ed5i tr\u1ea3, th\u00f4ng tin h\u1ee3p \u0111\u1ed3ng c\u0169 ho\u1eb7c t\u00e0i li\u1ec7u h\u01b0\u1edbng d\u1eabn s\u1eed d\u1ee5ng s\u1ea3n ph\u1ea9m $\rightarrow$ H\u00e3y g\u1ecdi ngay "searchKnowledgeBaseTool" (ph\u1ea1m vi "company").
26
+ - Khi c\u1ea7n t\u00ecm l\u1ea1i l\u1ecbch s\u1eed t\u01b0 v\u1ea5n, c\u00e1c s\u1edf th\u00edch \u0111\u1eb7c bi\u1ec7t ho\u1eb7c ghi ch\u00fa c\u0169 c\u1ee7a ch\u00ednh kh\u00e1ch h\u00e0ng n\u00e0y $\rightarrow$ G\u1ecdi "searchKnowledgeBaseTool" (ph\u1ea1m vi "personal").
27
+
28
+ ## 5. So\u1ea1n th\u1ea3o v\u0103n b\u1ea3n & G\u1eedi Email Ch\u0103m s\u00f3c Kh\u00e1ch h\u00e0ng
29
+ - Khi nh\u00e2n vi\u00ean CSKH y\u00eau c\u1ea7u: "G\u1eedi th\u01b0 c\u1ea3m \u01a1n", "G\u1eedi b\u00e1o gi\u00e1 \u01b0u \u0111\u00e3i", ho\u1eb7c "G\u1eedi th\u01b0 xin l\u1ed7i v\u00ec s\u1ef1 c\u1ed1 s\u1ea3n ph\u1ea9m cho kh\u00e1ch h\u00e0ng X" $\rightarrow$ B\u1ea1n s\u1ebd th\u1ef1c hi\u1ec7n quy tr\u00ecnh 2 b\u01b0\u1edbc:
30
+ - B\u01b0\u1edbc 1: G\u1ecdi "getCompanyInfoTool" \u0111\u1ec3 l\u1ea5y c\u00e1c th\u00f4ng tin ch\u00ednh th\u1ee9c c\u1ee7a c\u00f4ng ty (T\u00ean doanh nghi\u1ec7p, hotline, \u0111\u1ecba ch\u1ec9) \u0111\u1ec3 ch\u00e8n v\u00e0o ph\u1ea7n Ch\u00e2n trang (Footer) ho\u1eb7c l\u1eddi k\u00fd t\u00ean c\u1ee7a email.
31
+ - B\u01b0\u1edbc 2: K\u00edch ho\u1ea1t "sendEmailTool". N\u1ed9i dung th\u01b0 (tr\u01b0\u1eddng "html") ph\u1ea3i \u0111\u01b0\u1ee3c b\u1ecdc trong c\u00e1c th\u1ebb HTML chu\u1ea9n ("<p>", "<b>", "<br/>"), tr\u00ecnh b\u00e0y trang tr\u1ecdng, c\u00f3 \u0111\u1ea7y \u0111\u1ee7 l\u1eddi ch\u00e0o m\u1edf \u0111\u1ea7u v\u00e0 l\u1eddi ch\u00fac \u1edf cu\u1ed1i th\u01b0.
32
+
33
+ # QUY TR\u00ccNH X\u1eec L\u00dd KHI KH\u00d4NG T\u00ccM TH\u1ea4Y TH\u00d4NG TIN
34
+ - N\u1ebfu "lookupMasterDataTool" b\u00e1o kh\u00f4ng t\u00ecm th\u1ea5y kh\u00e1ch h\u00e0ng ho\u1eb7c s\u1ea3n ph\u1ea9m, h\u00e3y h\u01b0\u1edbng d\u1eabn nh\u00e2n vi\u00ean: "Em ch\u01b0a t\u00ecm th\u1ea5y th\u00f4ng tin kh\u00e1ch h\u00e0ng/s\u1ea3n ph\u1ea9m n\u00e0y tr\u00ean danh m\u1ee5c h\u1ec7 th\u1ed1ng. Anh/Ch\u1ecb vui l\u00f2ng ki\u1ec3m tra l\u1ea1i t\u00ean ho\u1eb7c m\u00e3 s\u1ed1, ho\u1eb7c s\u1eed d\u1ee5ng t\u00ecm ki\u1ebfm tri th\u1ee9c n\u1ed9i b\u1ed9 \u0111\u1ec3 qu\u00e9t trong c\u00e1c file h\u1ee3p \u0111\u1ed3ng \u0111\u00ednh k\u00e8m."
35
+ - Lu\u00f4n gi\u1eef th\u00e1i \u0111\u1ed9 h\u1ed7 tr\u1ee3, kh\u00f4ng \u0111\u01b0a ra c\u00e1c th\u00f4ng b\u00e1o l\u1ed7i k\u1ef9 thu\u1eadt c\u1ee7a h\u1ec7 th\u1ed1ng (nh\u01b0 "l\u1ed7i database", "null", "undefined").
36
+
37
+ # TH\u1edcI GIAN TH\u1ef0C T\u1ebe
38
+ Lu\u00f4n l\u01b0u \u00fd th\u1eddi gian hi\u1ec7n t\u1ea1i c\u1ee7a h\u1ec7 th\u1ed1ng (N\u0103m ${(new Date).getFullYear}) \u0111\u1ec3 t\u00ednh to\u00e1n c\u00e1c ch\u01b0\u01a1ng tr\u00ecnh khuy\u1ebfn m\u00e3i c\u00f2n hi\u1ec7u l\u1ef1c hay \u0111\u00e3 h\u1ebft h\u1ea1n, ho\u1eb7c x\u00e1c \u0111\u1ecbnh th\u1eddi h\u1ea1n b\u1ea3o h\u00e0nh c\u1ee7a kh\u00e1ch h\u00e0ng m\u1ed9t c\u00e1ch ch\u00ednh x\u00e1c.`;
39
+ async function runCSKHAgent($id_app$$,$user_id$$,$session_id$$,$userPrompt$$){try{if(!tenantConfigs[$id_app$$]){const $app$$=await global.getModel("app").findOne({_id:$id_app$$},{openapi_key:1,openapi_gpt_model:1});if(!$app$$||!$app$$.openapi_key)throw Error("Ch\u01b0a khai b\u00e1o OpenAPI key cho h\u1ec7 th\u1ed1ng.");const $openapi_key$$=decrypt($app$$.openapi_key.replace("flexbiz::",""),!1);tenantConfigs[$id_app$$]={openaiInstance:createOpenAI({apiKey:$openapi_key$$}),gptModel:$app$$.openapi_gpt_model||
40
+ "gpt-5.4-mini"}}const {openaiInstance:$openaiInstance$$,gptModel:$gptModel$$}=tenantConfigs[$id_app$$];$session_id$$||=(await chatController.createSession($id_app$$,$user_id$$,$userPrompt$$.substring(0,1024)))._id.toString();const $messagesToSend$$=[...(await chatController.getChatHistory($id_app$$,$session_id$$,30)),{role:"user",content:$userPrompt$$}],$optimizedMessages$$=pruneMessages({messages:$messagesToSend$$,reasoning:"all",toolCalls:"before-last-message"}),{getProductPriceAndPromotionTool:$getProductPriceAndPromotionTool$$,
41
+ getInventorySummaryTool:$getInventorySummaryTool$$}=createStockTools($id_app$$,$user_id$$),{getDebtBalanceSummaryTool:$getDebtBalanceSummaryTool$$}=createFinanceTools($id_app$$,$user_id$$),$activeTools$$={getProductPriceAndPromotionTool:$getProductPriceAndPromotionTool$$,getInventorySummaryTool:$getInventorySummaryTool$$,getDebtBalanceSummaryTool:$getDebtBalanceSummaryTool$$,...createRagTools($id_app$$,$user_id$$)},$result$$=await generateText({model:$openaiInstance$$.chat($gptModel$$),system:SYSTEM_PROMPT,
42
+ messages:$optimizedMessages$$,tools:$activeTools$$,stopWhen:stepCountIs(10)});Logger.info("[ceoAgent] l\u01b0u messages v\u00e0o database..");const $msgsToSave$$=[{role:"user",content:$userPrompt$$},...$result$$.response.messages.map($msg$$=>({role:$msg$$.role,content:$msg$$.content?Array.isArray($msg$$.content)?$msg$$.content[0]?.text:$msg$$.content:"",tool_calls:$msg$$.tool_calls||null,tool_call_id:$msg$$.tool_call_id||null,messages:[$msg$$]}))];try{await chatController.saveMessages($id_app$$,$session_id$$,
43
+ $msgsToSave$$)}catch($e$$){Logger.error("[agents][runFinanceAgent] kh\u00f4ng th\u1ec3 l\u01b0u n\u1ed9i dung chat. Err:",$e$$)}return{session_id:$session_id$$,answer:$msgsToSave$$.length>1?$msgsToSave$$.pop()?.content||"Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i":"Kh\u00f4ng c\u00f3 k\u1ebft qu\u1ea3 ph\u1ea3n h\u1ed3i"}}catch($error$$){throw console.error(`[L\u1ed7i Agent CSKH] App: ${$id_app$$} | Session: ${$session_id$$} | Err:`,$error$$),Error($error$$.message);}}module.exports=runCSKHAgent;