BỘ TÀI CHÍNH -------
CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM Độc lập - Tự do - Hạnh phúc ---------------
Số: 11239/BTC-CST V/v xin ý kiến về việc giảm thuế GTGT 6 tháng đầu năm 2024
Hà Nội, ngày 13 tháng 10 năm 2023
Kính gửi:
- Các Bộ, cơ quan ngang Bộ; cơ quan thuộc Chính phủ; - Ủy ban nhân dân các tỉnh, thành phố trực thuộc Trung ương; - Liên đoàn Thương mại và Công nghiệp Việt Nam; - Ủy ban trung ương Mặt trận Tổ quốc Việt Nam.
Tại mục a điểm 4 Thông báo số 399a/TB-VPCP ngày 29/9/2023 của Văn phòng Chính phủ thông báo Kết luận của Thường trực Chính phủ tại cuộc họp về tình hình kinh tế - xã hội tháng 9 và 9 tháng đầu năm 2023; tại điểm c khoản 3 mục I Nghị quyết số 164/NQ-CP ngày 04/10/2023 của Chính phủ về Phiên họp Chính phủ thường kỳ tháng 9 năm 2023 và 1 hội nghị trực tuyến Chính phủ với địa phương giao Bộ Tài chính: “Đề xuất việc trình Quốc hội tiếp tục giảm thuế giá trị gia tăng 2% trong 6 tháng đầu năm 2024 và đề xuất giao Ủy ban Thường vụ Quốc hội xem xét quyết định trong thời gian giữa 02 kỳ họp Quốc hội nếu tình hình kinh tế và doanh nghiệp vẫn còn khó khăn, báo cáo Thủ tướng Chính phủ trước ngày 07 tháng 10 năm 2023” .
Đồng thời, ngày 04/10/2023, Thủ tướng Chính phủ có công văn số 920/TTg-KTTH gửi Bộ trưởng Bộ Tài chính về việc đề xuất giảm thuế giá trị gia tăng (GTGT) 6 tháng đầu năm 2024. Tại công văn số 920/TTg-KTTH, Thủ tướng Chính phủ chỉ đạo: “Bộ Tài chính khẩn trương đề xuất việc trình Quốc hội tiếp tục giảm thuế GTGT 2% trong 6 tháng đầu năm 2024 và đề xuất giao Ủy ban Thường vụ Quốc hội xem xét, quyết định trong thời gian giữa 02 kỳ họp Quốc hội nếu tình hình kinh tế và doanh nghiệp vẫn còn khó khăn; báo cáo Thủ tướng Chính phủ trước ngày 05/10/2023 theo đúng kết luận của Thường trực Chính phủ tại Thông báo số 399a/TB-VPCP ngày 29/9/2023, đảm bảo tính khả thi, hiệu quả, đáp ứng tiến độ thời gian và đúng quy trình, trình tự, thủ tục, trong đó có tính đến phương án tổng hợp nội dung đề xuất này vào Báo cáo tình hình thực hiện ngân sách nhà nước năm 2023, dự toán ngân sách nhà nước năm 2024 như một trong những biện pháp điều hành dự toán ngân sách nhà nước năm 2024 để trình các cấp có thẩm quyền xem xét, quyết định”.
Thực hiện Kết luận của Thường trực Chính phủ, Chính phủ và ý kiến chỉ đạo của Thủ tướng Chính phủ tại các văn bản nêu trên, Bộ Tài chính đã có công văn số 10830/BTC-CST ngày 06/10/2023 báo cáo Thủ tướng Chính phủ về nội dung chính sách giảm thuế GTGT 2% trong 6 tháng đầu năm 2024.
Ngày 10/10/2023, Văn phòng Chính phủ có công văn số 7866/VPCP-KTTH về đề xuất giảm thuế GTGT 6 tháng đầu năm 2024. Theo đó, Phó Thủ tướng Chính phủ Lê Minh Khái đồng ý với đề xuất của Bộ Tài chính tại công văn số 10830/BTC-CST và giao: “Bộ Tài chính khẩn trương tổng hợp nội dung đề xuất trình Quốc hội tiếp tục giảm thuế GTGT 2% trong 6 tháng đầu năm 2024 và giao Ủy ban Thường vụ Quốc hội xem xét quyết định trong thời gian giữa 02 kỳ họp Quốc hội nếu tình hình kinh tế và doanh nghiệp vẫn còn khó khăn vào Báo cáo tình hình thực hiện ngân sách nhà nước năm 2023, dự toán ngân sách nhà nước năm 2024”.
Để đảm bảo đúng quy trình, trình tự, thủ tục theo ý kiến chỉ đạo của Thủ tướng Chính phủ tại công văn số 920/TTg-KTTH, Bộ Tài chính đề nghị quý đơn vị có ý kiến về nội dung chính sách giảm thuế GTGT 2% như sau:
1. Sự cần thiết tiếp tục ban hành chính sách giảm thuế giá trị gia tăng 2%
Đại dịch Covid-19 xảy ra đã gây hậu quả nặng nề và kéo dài, tác động đến mọi mặt kinh tế đời sống, kinh tế, xã hội, cùng với sự biến đổi khí hậu, thiên tai, bão lũ đã làm cho nền kinh tế xã hội nước ta phải đối mặt với nhiều khó khăn thách thức lớn, rất gay gắt, nặng nề, hầu hết các ngành lĩnh vực đều bị ảnh hưởng nghiêm trọng. Có thể thấy trong giai đoạn 2020-2023, các chính sách hỗ trợ cho nền kinh tế ở mức lớn chưa từng có. Từ năm 2020 đến nay tổng trị giá của các giải pháp miễn giảm, gia hạn các loại thuế, phí, lệ phí và tiền thuê đất (thuế thu nhập doanh nghiệp, thuế thu nhập cá nhân, thuế GTGT, thuế tiêu thụ đặc biệt, thuế nhập khẩu, thuế bảo vệ môi trường, phí, lệ phí và tiền thuê đất) đã lên đến khoảng 700 nghìn tỷ đồng. Riêng năm 2023 khoảng 196 nghìn tỷ đồng và tính đến tháng 9 năm 2023 đã thực hiện khoảng 152,2 nghìn tỷ đồng.
Việc tập trung thực hiện quyết liệt, đồng bộ các giải pháp về chính sách tài chính thời gian qua đã có tác động tích cực trong việc hỗ trợ doanh nghiệp và người dân ổn định sản xuất kinh doanh, đời sống của người dân được trở lại bình thường, góp phần ổn định kinh tế vĩ mô, tạo điều kiện thuận lợi để thúc đẩy phục hồi và tăng trưởng kinh tế. Tốc độ tăng trưởng GDP năm 2021 đạt 2,56%, năm 2022 tăng 8,02% và 9 tháng đầu năm 2023 tăng 4,24%.
Tuy nhiên, sau thời gian dài chống chịu với đại dịch Covid-19, doanh nghiệp trong nước đang trong quá trình phục hồi nên hoạt động sản xuất kinh doanh tiếp tục gặp khó khăn (số doanh nghiệp ra khỏi thị trường hoặc tạm thời ra khỏi thị trường tăng đến 19,9% so với 9 tháng đầu năm 2022); tính chung 9 tháng năm 2023, kim ngạch xuất khẩu hàng hóa giảm 8,2% so với cùng kỳ năm trước, kim ngạch nhập khẩu giảm 13,8%, thu ngân sách nhà nước (NSNN) ước đạt 1.223,8 nghìn tỷ đồng, bằng 75,5% dự toán năm.
Thời gian tới, dự báo tình hình thế giới tiếp tục diễn biến phức tạp, khó lường. Theo Báo cáo “Triển vọng kinh tế toàn cầu” tháng 6/2023 của Ngân hàng Thế giới (WB) và Báo cáo triển vọng kinh tế tháng 6/2023 của Tổ chức Hợp tác và Phát triển kinh tế (OECD) đều nhận định nền kinh tế toàn cầu vẫn bấp bênh, lạm phát kéo dài trong bối cảnh rủi ro nghiêm trọng mặc dù đã có những cải thiện trong những tháng đầu năm 2023. WB nhận định tăng trưởng thương mại hàng hóa toàn cầu chậm lại trong nửa đầu năm 2023, nhu cầu của nền kinh tế thế giới sẽ sụt giảm, hoạt động thương mại quốc tế sẽ sụt giảm trong nửa cuối năm 2023. Trong nước, mặc dù nhiều ngành, lĩnh vực tiếp tục xu hướng phục hồi và có triển vọng tích cực; nhiều chính sách, giải pháp tháo gỡ khó khăn, vướng mắc đã và đang phát huy hiệu quả; nhưng khó khăn, thách thức nhiều hơn thời cơ, thuận lợi. Tổng cầu tiêu dùng trong nước được xem là một động lực quan trọng để thúc đẩy tăng trưởng kinh tế. Khi hoạt động xuất khẩu giảm sút thì việc tăng tổng cầu tiêu dùng trong nước sẽ là biện pháp quan trọng giúp tiêu thụ hàng hóa cho các doanh nghiệp sản xuất và hỗ trợ phục hồi tăng trưởng. Do vậy, cần tiếp tục có giải pháp về chính sách tài chính để thúc đẩy tổng cầu tiêu dùng trong nước.
Trong năm 2022, Quốc hội đã ban hành Nghị quyết số 43/2022/QH15 ngày 11/01/2022 về chính sách tài khóa, tiền tệ hỗ trợ Chương trình phục hồi và phát triển kinh tế - xã hội trong đó đã đề ra giải pháp giảm thuế GTGT 2% đối với một số nhóm hàng hóa, dịch vụ chịu thuế suất GTGT 10% từ ngày 01/2/2022 đến hết ngày 31/12/2022. Năm 2023, trước khó khăn của nền kinh tế, Quốc hội tiếp tục thực hiện chính sách giảm thuế GTGT theo Nghị quyết số 43/2022/QH15 từ ngày 01/7/2023 đến hết ngày 31/12/2023 tại Nghị quyết số 101/2023/QH15 ngày 24/6/2023 của Quốc hội về Kỳ họp thứ 5, Quốc hội khóa XV. Giải pháp giảm thuế GTGT cùng với các giải pháp thuế, phí, lệ phí khác đang tạo điều kiện rất lớn giúp doanh nghiệp giảm được chi phí sản xuất, tăng lợi nhuận, tăng khả năng kích cầu.
Qua 03 tháng thực hiện (tháng 7, 8 và tháng 9 năm 2023), chính sách giảm thuế GTGT theo Nghị quyết số 101/2023/QH15 đã hỗ trợ cho doanh nghiệp và người dân tổng cộng khoảng 11,7 nghìn tỷ đồng, đã góp phần giảm giá thành hàng hóa, dịch vụ, từ đó thúc đẩy sản xuất kinh doanh và tạo thêm công ăn việc làm cho người lao động, góp phần kích cầu tiêu dùng, thúc đẩy sản xuất kinh doanh phát triển. Việc thực hiện các chính sách miễn, giảm, gia hạn thuế, phí, lệ phí và tiền thuê đất trong đó có chính sách giảm thuế GTGT theo Nghị quyết số 101/2023/QH15 đã góp phần vào sự tăng trưởng kinh tế, trong đó GDP quý 11/2023 và quý III/2023 cao hơn quý I/2023 (quý I tăng 3,28%, quý II tăng 4,05%, quý III tăng 5,33%). Từ tháng 7/2023 chỉ số mức tăng tổng mức bán lẻ hàng hóa và dịch vụ tiêu dùng đã có mức tăng 7,1%, tháng 8 là 7,6% và tháng 9 là 7,5% (chấm dứt đà suy giảm của chỉ số này kể từ tháng 01/2023 (tháng 01/2023 so với cùng kỳ năm 2022 tăng 20%; tháng 02 giảm còn 13,2%; tháng 3 là 13,4%; tháng 4, 5 giảm xuống 11,5%; tháng 6 giảm còn 6,5%). Tính chung 9 tháng đầu năm 2023, tổng mức bán lẻ hàng hóa và doanh thu dịch vụ tiêu dùng tăng 9,7% so với cùng kỳ năm 2022Để kịp thời ứng phó với diễn biến tình hình kinh tế - xã hội, đồng thời cân nhắc, tính toán phù hợp với điều kiện thực tế, cần triển khai quyết liệt, hiệu quả các giải pháp hỗ trợ về thuế, phí, lệ phí và tiền thuê đất đã ban hành trong năm 2023 và nghiên cứu, đề xuất một số giải pháp về giảm thuế, phí, lệ phí cho năm 2024 như: tiếp tục xem xét giảm thuế GTGT 2% và xem xét giảm mức thuế bảo vệ môi trường đối với xăng, dầu như đã áp dụng của năm 2023; tiếp tục rà soát giảm mức thuế suất thuế xuất khẩu, thuế nhập khẩu để hỗ trợ sản xuất, kinh doanh trong nước; giảm mức thu một số khoản phí, lệ phí. Trên cơ sở đánh giá kết quả đạt được của giải pháp giảm thuế GTGT 2% theo Nghị quyết số 101/2023/QH15, việc tiếp tục thực hiện giảm thuế GTGT trong 6 tháng đầu năm 2024 nhằm hỗ trợ doanh nghiệp, người dân là cần thiết.
2. Nội dung chính sách
Việc thực hiện giảm thuế GTGT đối với một số nhóm hàng hóa, dịch vụ đang áp dụng thuế suất thuế GTGT 10% đã được thực hiện ổn định trong năm 2022 và năm 2023. Do vậy, tại công văn số 10830/BTC-CST, Bộ Tài chính đã báo cáo Thủ tướng Chính phủ tiếp tục giảm thuế GTGT như nội dung đã được quy định tại Nghị quyết số 43/2022/QH15 và Nghị quyết số 101/2023/QH15, cụ thể:
- Giám 2% thuế suất thuế GTGT, áp dụng đối với các nhóm hàng hóa, dịch vụ đang áp dụng mức thuế suất 10% (còn 8%), trừ một số nhóm hàng hóa, dịch vụ sau: Viễn thông, công nghệ thông tin, hoạt động tài chính, ngân hàng, chứng khoán, bảo hiểm, kinh doanh bất động sản, sản xuất kim loại và sản xuất sản phẩm từ kim loại đúc sẵn, ngành khai khoáng (không kể khai thác than), sản xuất than cốc, dầu mỏ tinh chế, sản xuất hóa chất và sản phẩm hóa chất, các sản phẩm hàng hóa và dịch vụ chịu thuế tiêu thụ đặc biệt.
- Thời gian áp dụng: Từ ngày 01/01/2024 đến hết ngày 30/6/2024. Giao Ủy ban Thường vụ Quốc hội xem xét, quyết định việc tiếp tục giảm thuế GTGT sau thời điểm 30/6/2024 nếu tình hình kinh tế và doanh nghiệp, người dân vẫn còn khó khăn, báo cáo Quốc hội tại kỳ họp gần nhất.
3. Đánh giá tác động đến thu ngân sách nhà nước
Việc thực hiện chính sách giảm thuế GTGT 2% trong 06 tháng đầu năm 2024 dự kiến sẽ giảm thu NSNN khoảng 25 nghìn tỷ đồng (khoảng 4,175 nghìn tỷ/tháng, trong đó, giảm thu khâu nội địa là 2.700 tỷ đồng; giảm thu khâu nhập khẩu khoảng 1.475 tỷ đồng).
Số liệu trên được tính trên cơ sở dự kiến số giảm thu NSNN ở khâu nội địa trong 6 tháng cuối năm 2023 (bình quân mỗi tháng là khoảng 2.550 tỷ đồng), giả định tăng trưởng GDP năm 2024 khoảng 6-6,5%, tốc độ tăng trưởng thu NSNN năm 2024 khoảng 5-7%.
Để khắc phục và bù đắp các tác động đến thu NSNN trong ngắn hạn cũng như đảm bảo sự chủ động trong điều hành dự toán NSNN, Bộ Tài chính sẽ phối hợp với các Bộ, ngành có liên quan và địa phương chú trọng chỉ đạo thực hiện và triển khai hiệu quả các Luật thuế; tiếp tục cải cách hiện đại hóa hệ thống thuế, đơn giản hóa thủ tục hành chính thuế; đồng thời quyết liệt công tác quản lý thu NSNN, tập trung triển khai kịp thời, có hiệu quả các nhóm giải pháp quản lý thu, chống thất thu, chuyển giá, trốn thuế.
4. Về hình thức văn bản
Căn cứ quy định tại điểm b, điểm c khoản 2 Điều 15 Luật Ban hành văn bản quy phạm pháp luật Việc xây dựng Nghị quyết của Quốc hội phải thực hiện theo quy trình 02 bước:
- Bước 1: Lập đề nghị xây dựng Nghị quyết theo quy định của Luật Ban hành văn bản quy phạm pháp luật, báo cáo Chính phủ trình cơ quan có thẩm quyền xem xét thông qua để đưa vào Chương trình xây dựng luật, pháp lệnh của Quốc hội.
- Bước 2: Sau khi được đưa vào Chương trình xây dựng luật, pháp lệnh, cơ quan chủ trì soạn thảo tiến hành xây dựng dự thảo Nghị quyết theo quy định của Luật Ban hành văn bản quy phạm pháp luật để trình Chính phủ xem xét trình Quốc hội ban hành.
Theo Chương trình kỳ họp thứ 6, Quốc hội khóa XV dự kiến bắt đầu từ ngày 23/10/2023, vì vậy, việc xây dựng hồ sơ Nghị quyết của Quốc hội theo quy trình 02 bước là không thể kịp về mặt thời gian.
* Thực tế đã thực hiện trong thời gian qua:
- Trong năm 2023, chính sách giảm thuế GTGT 2% được đưa vào Nghị quyết số 101/2023/QH15 ngày 24/6/2023 của Quốc hội về Kỳ họp thứ 5 Quốc hội khóa XV (không ban hành Nghị quyết riêng). Tại Nghị quyết số 101 /2023/QH15 đã giao Chính phủ tổ chức thực hiện chính sách này. Trên cơ sở đó, Chính phủ đã ban hành Nghị định số 44/2023/NĐ-CP ngày 30/6/2023 quy định chính sách giảm thuế GTGT theo Nghị quyết số 101/2023/QH15.
- Trong năm 2020-2022:
Để tạo điều kiện cho doanh nghiệp, người dân, Chính phủ đã đề xuất trình Quốc hội ban hành giải pháp cho phép tính vào chi phí được trừ khi xác định thu nhập chịu thuế thu nhập doanh nghiệp đối với khoản chi ủng hộ, tài trợ của doanh nghiệp, tổ chức cho các hoạt động phòng, chống dịch Covid-19 tại Việt Nam cho kỳ tính thuế năm 2020-2022. Mặc dù các nội dung này thuộc thẩm quyền của Quốc hội, phải được quy định tại Luật hoặc Nghị quyết của Quốc hội về thuế nhưng Chính phủ đã trình Quốc hội đưa vào các Nghị quyết về dự toán NSNN để thực hiện (khoản 8 Điều 3 Nghị quyết số 128/2020/QH14 ngày 12/11/2020 của Quốc hội về dự toán NSNN năm 2021 và điểm 1.1 khoản 1 Điều 3 Nghị quyết số 43/2022/QH15 ngày 11/01/2022 của Quốc hội về chính sách tài khóa, tiền tệ hỗ trợ Chương trình phục hồi và phát triển kinh tế - xã hội). Tại các Nghị quyết cũng giao Chính phủ hướng dẫn, tổ chức thực hiện nội dung này. Trên cơ sở đó, Chính phủ đã ban hành Nghị định số 44/2021/NĐ-CP ngày 31/3/2021 và Nghị định số 15/2022/NĐ-CP ngày 28/01/2022 để hướng dẫn thực hiện.
Để đảm bảo tính khả thi, tiến độ thực hiện đồng thời có thể sớm đưa giải pháp vào thực hiện trong thực tế, đảm bảo tính liên tục và kịp thời hỗ trợ doanh nghiệp, người dân và nền kinh tế, trên cơ sở ý kiến chỉ đạo của Thủ tướng Chính phủ, để kịp tiến độ trình Quốc hội tại kỳ họp thứ 6, Quốc hội khóa XV, Bộ Tài chính trình Chính phủ đưa nội dung giảm thuế GTGT 2% trong 6 tháng đầu năm 2024 vào Báo cáo tình hình thực hiện NSNN năm 2023, dự toán NSNN năm 2024 để trình Quốc hội ra Nghị quyết chung (không ban hành Nghị quyết riêng về giảm thuế GTGT).
Bộ Tài chính đề nghị Quý cơ quan nghiên cứu có ý kiến về: (i) Nội dung đề xuất về chính sách giảm thuế GTGT 2% trong 6 tháng đầu năm 2024; và (ii) Hình thức văn bản: bổ sung nội dung này vào Báo cáo tình hình thực hiện NSNN năm 2023, dự toán NSNN năm 2024 để trình Quốc hội ra Nghị quyết chung (không ban hành Nghị quyết riêng về giảm thuế GTGT).
Ý kiến đóng góp đề nghị gửi về Bộ Tài chính trước ngày 20/10/2023 để tổng hợp, báo cáo Chính phủ (đồng thời gửi file về địa chỉ: nguyenngocanh3@mof.gov.vn; điện thoại: 024-22202828, máy lẻ 5137).
Trân trọng cảm ơn sự hợp tác của Quý cơ quan, đơn vị./.
Nơi nhận: - Như trên; - Bộ trưởng (để báo cáo); - Thủ tướng Chính phủ, Phó Thủ tướng Lê Minh Khái (để báo cáo); - Văn phòng Chính phủ (để phối hợp); - Cổng TTĐT Chính phủ (để đăng website); - Cổng TTĐT Bộ Tài chính (để đăng website); - TCT, TCHQ, Vụ PC, Vụ NSNN (để tham gia); - Lưu: VT, CST(10).
KT. BỘ TRƯỞNG THỨ TRƯỞNG Cao Anh Tuấn
lồng nhau (bên trong) hay không
const memberID = 0;
const vbID = '9706974c0d1cf7c304de9681a213f089';
const unlockAllPhanTich = true;
// State management cho phân tích
let isAnalyzing = false; // Có đang phân tích không
let currentAnalyzingAddress = null; // Address đang được phân tích
let currentAnalyzingElement = null; // Element đang được phân tích
let currentAnalyzingBadge = null; // Badge của element đang phân tích
let isPanelOpen = false; // Panel phân tích có đang mở không
// Typing effect state
let typingTimerId = null;
let typingCancelled = false;
// Thinking GIF state
let thinkingGifIntervalId = null;
let thinkingGifActive = false;
let thinkingGifCurrent = 0; // chỉ số GIF hiện tại 1..10
function isInViewportAndTabNoiDung(element) {
const rect = element.getBoundingClientRect();
const buffer = 1500; // Buffer to preload content below the viewport (approx. 50+ lines)
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
const isInViewport = rect.top < viewHeight + buffer && rect.bottom >= 0;
const isInTabNoiDung = $(element).closest('#tab_noi_dung_vb').length > 0;
return isInViewport && isInTabNoiDung;
}
function getAddress(element) {
const validTags = ['trichyeu', 'cancu', 'phan', 'chuong', 'muc', 'tieumuc', 'dieu', 'khoan', 'diem'];
const $parent = $(element).closest(validTags.join(','));
if (!$parent.length) {
return null;
}
let addr = $parent.attr('address');
if (!addr && $parent.prop('tagName').toLowerCase() === 'trichyeu') {
addr = 'trichyeu';
$parent.attr('address', addr);
}
return addr || null;
}
function processTnplClasses($element) {
const tnplKeysInLine = new Set(); // key = slug hoặc text (thường là slug)
$element.find('tnpl').each(function () {
const $tnpl = $(this);
const tnplSlug = ($tnpl.attr('slug') || '').trim().toLowerCase();
const tnplKey = tnplSlug || $tnpl.text().trim().toLowerCase();
// Đã xử lý trong cùng dòng => bỏ
if (tnplKeysInLine.has(tnplKey)) {
return;
}
tnplKeysInLine.add(tnplKey);
let tnplExists = false;
// Chỉ duyệt các tnpl đã được tô màu (class on)
$('tnpl.on').each(function () {
const $existingTnpl = $(this);
const existingSlug = ($existingTnpl.attr('slug') || '').trim().toLowerCase();
const existingKey = existingSlug || $existingTnpl.text().trim().toLowerCase();
if (
existingKey === tnplKey &&
isInViewportAndTabNoiDung($existingTnpl[0])
) {
tnplExists = true;
return false; // break each
}
});
if (!tnplExists) {
$tnpl.addClass('on');
}
});
}
function processQueue() {
while (pendingRequests < maxConcurrentRequests && requestQueue.length > 0) {
const task = requestQueue.shift();
pendingRequests++;
task()
.always(() => {
pendingRequests--;
processQueue();
});
}
}
function processVisibleParagraphs() {
try {
$('#tab_noi_dung_vb p:not([is-posted="1"])').each(function () {
let $element = $(this);
if (isInViewportAndTabNoiDung(this)) {
$element.attr('is-posted', '1');
$element.addClass('loading-content');
let p_innerHTML = $element.html();
let address = null;
if (cac_cau_hinh.loai_noi_dung.includes('docs')) {
address = getAddress($element);
}
const isSubP = $element.parents('p').length > 0;
if (isSubP && !allow_sub_p) {
$element.removeClass('loading-content');
return; // Không gửi nếu không cho phép
}
const postData = { p_content: p_innerHTML, cac_cau_hinh, address, vb_ngaybanhanh: '2023-10-13 00:00:00 AM' };
if (isSubP && allow_sub_p) {
postData.sub_p = 1;
}
requestQueue.push(() =>
$.ajax({
url: '//tnpl' + (Math.floor(Math.random() * 10) + 1) + '.hethongphapluat.com/tien-ich/tim.tien.ich.php',
type: 'POST',
data: postData,
success: function(response) {
$element.html(response);
processTnplClasses($element);
// Đợi CTTD và các tiện ích load xong rồi mới attach badge
if (((unlockAllPhanTich) || memberID === 4 || memberID === 3 || memberID === 2) && typeof attachPhanTichBadge === 'function') {
setTimeout(function() {
// $element chính là thẻ p, kiểm tra và attach badge trực tiếp
const $parent = $element.closest('phan, chuong, muc, tieumuc, dieu, khoan, diem');
if ($parent.length > 0 && $parent.find('.badge-phan-tich[data-for="' + $parent.attr('address') + '"]').length === 0) {
const address = $parent.attr('address');
$element.attr('data-address', address);
// Lấy tên loại thẻ cho tooltip
const parentType = getParentTypeName($parent.prop('tagName').toLowerCase());
// Append badge VÀO PARENT (dieu, khoan,...) thay vì vào để tránh xung đột CTTD
const extraClass = (unlockAllPhanTich && memberID <= 0) ? ' upgrade-require' : '';
const $badge = $('');
$parent.append($badge);
// Thêm class để CSS set position: relative CHỈ cho element có badge
$parent.addClass('has-phan-tich-badge');
}
// Xử lý các p con (nếu có sub-p)
attachPhanTichBadge($element);
}, 3); // Đợi 300ms để CTTD render xong
}
},
complete: function() {
$element.removeClass('loading-content');
}
})
);
processQueue();
}
});
} catch(e) {
}
}
$(window).on('scroll resize', function () {
processVisibleParagraphs();
});
processVisibleParagraphs();
// Chức năng phân tích điều luật (mở theo lịch unlockAllPhanTich cho tất cả, nhưng khách click sẽ mở modal đăng nhập/mua gói)
if ((unlockAllPhanTich) || memberID === 4 || memberID === 3 || memberID === 2) {
// Modal cảnh báo
function showWarningModal(message) {
// Tạo modal nếu chưa có
if ($('#warningModal').length === 0) {
const modalHTML = `
`;
$('body').append(modalHTML);
}
$('#warningModalBody').html('' + message + '
');
$('#warningModal').modal('show');
}
// Hàm lấy tên tiếng Việt của thẻ
function getParentTypeName(tagName) {
const typeNames = {
'phan': 'Phần',
'chuong': 'Chương',
'muc': 'Mục',
'tieumuc': 'Tiểu mục',
'dieu': 'Điều',
'khoan': 'Khoản',
'diem': 'Điểm'
};
return typeNames[tagName] || 'Nội dung';
}
// Chuyển Telex -> Unicode cho giá trị (ví dụ: dd->đ, oo->ô, ow->ơ, aa->â, ee->ê, aw->ă, uw->ư)
function telexToUnicode(str) {
if (!str) return str;
// Giữ nguyên số
if (/^\d+$/.test(str)) return str;
let s = String(str);
// dd / ĐĐ
s = s.replace(/dd/g, 'đ');
s = s.replace(/DD/g, 'Đ');
// nguyên âm có mũ/dấu
s = s.replace(/aa/g, 'â').replace(/AA/g, 'Â');
s = s.replace(/ee/g, 'ê').replace(/EE/g, 'Ê');
s = s.replace(/oo/g, 'ô').replace(/OO/g, 'Ô');
s = s.replace(/ow/g, 'ơ').replace(/OW/g, 'Ơ');
s = s.replace(/uw/g, 'ư').replace(/UW/g, 'Ư');
s = s.replace(/aw/g, 'ă').replace(/AW/g, 'Ă');
return s;
}
function attachPhanTichBadge($container) {
const validTags = 'phan, chuong, muc, tieumuc, dieu, khoan, diem';
$container.find('p').each(function() {
const $p = $(this);
const $parent = $p.closest(validTags);
if ($parent.length > 0) {
const address = $parent.attr('address');
// Kiểm tra đã có badge cho parent này chưa
if ($parent.find('.badge-phan-tich[data-for="' + address + '"]').length === 0) {
// Lưu address vào data attribute
$p.attr('data-address', address);
// Lấy tên loại thẻ cho tooltip
const parentType = getParentTypeName($parent.prop('tagName').toLowerCase());
// Append badge vào PARENT, không vào
const extraClass = (unlockAllPhanTich && memberID <= 0) ? ' upgrade-require' : '';
const $badge = $('');
$parent.append($badge);
// Thêm class để CSS set position: relative CHỈ cho element có badge
$parent.addClass('has-phan-tich-badge');
}
}
});
}
// Helper: Escape HTML entities
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return String(text).replace(/[&<>"']/g, function(m) { return map[m]; });
}
// Helper: Convert Markdown to HTML (đơn giản)
function markdownToHtml(markdown) {
if (!markdown) return '';
let html = markdown;
// Headers
html = html.replace(/^### (.*$)/gim, '
$1 ');
html = html.replace(/^## (.*$)/gim, '$1 ');
html = html.replace(/^# (.*$)/gim, '$1 ');
// Bold
html = html.replace(/\*\*(.*?)\*\*/g, '$1 ');
// Italic
html = html.replace(/\*(.*?)\*/g, '$1 ');
// Blockquote
html = html.replace(/^> (.*$)/gim, '$1 ');
html = html.replace(/^> (.*$)/gim, '$1 ');
// Lists (unordered)
html = html.replace(/^\- (.*$)/gim, '$1 ');
html = html.replace(/(.*<\/li>)/s, '');
// Lists (ordered)
html = html.replace(/^\d+\. (.*$)/gim, ' $1 ');
// Line breaks và paragraphs
html = html.split('\n\n').map(para => {
para = para.trim();
if (para.startsWith('')) {
return para;
}
if (para) {
return '' + para.replace(/\n/g, ' ') + '
';
}
return '';
}).join('\n');
// Clean up multiple line breaks
html = html.replace(/\n{3,}/g, '\n\n');
return html;
}
// Panel fixed position
function closePhanTichPanel() {
const $panel = $('#phanTichPanel');
if ($panel.length) {
$panel.removeClass('show');
setTimeout(() => {
$panel.remove();
}, 300);
}
// Stop typing animation nếu đang chạy
stopThinkingTyping();
// Reset highlight và badge khi đóng panel
if (currentAnalyzingElement) {
currentAnalyzingElement.removeClass('highlight-border-persistent');
}
if (currentAnalyzingBadge) {
currentAnalyzingBadge.text('Phân tích').removeClass('analyzing');
currentAnalyzingBadge.data('analyzing', false);
currentAnalyzingBadge.data('hovering', false);
currentAnalyzingBadge.css({display: 'none'}); // Ẩn badge khi đóng
}
// Reset tất cả các element khác (trong trường hợp có nhiều)
$('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent');
$('#tab_noi_dung_vb .badge-phan-tich-container.analyzing').each(function() {
$(this).text('Phân tích').removeClass('analyzing').data('analyzing', false);
});
// Check: có CTTD pointer đang mở không?
const $visiblePointers = $('.pointer:visible');
const hadCTTDOpen = $visiblePointers.length > 0;
if (hadCTTDOpen) {
// CÓ CTTD đang mở → giữ rightdocinfo ẩn
} else {
// KHÔNG có CTTD → SHOW lại rightdocinfo
const $rightdocinfo = $('#rightdocinfo');
if ($rightdocinfo.length > 0) {
$rightdocinfo.show();
}
}
// Reset state
isAnalyzing = false;
currentAnalyzingAddress = null;
currentAnalyzingElement = null;
currentAnalyzingBadge = null;
isPanelOpen = false; // Đánh dấu panel đã đóng
}
// Panel đã song song với rightdocinfo → không cần MutationObserver nữa
// Resize event để update panel dimensions khi browser resize
let resizeTimer;
$(window).on('resize', function() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
if (isPanelOpen && $('#phanTichPanel').length > 0) {
updatePanelDimensions();
if ($('#phanTichPanelBody').hasClass('thinking-mode')) {
updateThinkingGifHeight();
}
}
}, 250); // Debounce 250ms
});
// Function để detect và áp dụng dimensions từ rightdocinfo
function updatePanelDimensions() {
const $panel = $('#phanTichPanel');
const $rightdocinfo = $('#rightdocinfo');
const $docRightCol = $('#doc-right-col');
// Mobile: dùng bottom sheet → để CSS điều khiển, bỏ qua reposition bằng JS
if ($(window).width() <= 768) {
return;
}
if ($panel.length === 0) return;
// Ưu tiên: doc-right-col > rightdocinfo
let $reference = $docRightCol.length > 0 ? $docRightCol : $rightdocinfo;
// Nếu reference bị ẩn (display:none), tạm show để get dimensions
let wasHidden = false;
if ($reference.length > 0 && !$reference.is(':visible')) {
wasHidden = true;
$reference.css('visibility', 'hidden').show();
}
if ($reference.length > 0) {
const refWidth = $reference.outerWidth();
const refOffset = $reference.offset();
if (refWidth && refOffset) {
// Tính vị trí right từ edge màn hình
const windowWidth = $(window).width();
const rightPosition = windowWidth - (refOffset.left + refWidth);
$panel.css({
'width': refWidth + 'px',
'right': rightPosition + 'px'
});
} else {
}
// Restore trạng thái hidden nếu cần
if (wasHidden) {
$reference.hide().css('visibility', '');
}
}
}
// Hiệu ứng typing giả lập đang phân tích trong panel
function stopThinkingTyping() {
typingCancelled = true;
if (typingTimerId) {
clearTimeout(typingTimerId);
typingTimerId = null;
}
// Dừng trình chiếu ảnh khi dừng typing
stopThinkingImages();
}
// Helper GIF: chọn chỉ số ảnh mới 1..10 khác với exclude
function randomGifIndex(exclude) {
let n = exclude;
while (n === exclude) {
n = Math.floor(Math.random() * 10) + 1;
}
return n;
}
// Helper GIF: preload rồi gán src cho img, gọi callback sau khi load xong (hoặc lỗi)
function setGifSrc($img, idx, cb) {
const url = '/assets/images/gif/researching-' + idx + '.gif';
const updateWrapHeight = function(nW, nH){
try {
const $wrap = $img.closest('#thinkingGifWrapper');
if ($wrap.length && nW && nH) {
const wrapW = $wrap.width();
const maxW = wrapW * 0.9; // khớp với CSS max-width:90%
const displayW = Math.min(nW, maxW);
const displayH = nH * (displayW / nW);
$wrap.css('height', displayH + 'px');
}
} catch(e) { /* ignore */ }
};
if ($img.attr('src') === url) {
// Ảnh trùng src -> vẫn cập nhật lại chiều cao wrapper theo kích thước hiển thị hiện tại
const el = $img[0];
if (el && el.naturalWidth && el.naturalHeight) {
updateWrapHeight(el.naturalWidth, el.naturalHeight);
}
if (cb) cb();
return;
}
const pre = new Image();
pre.onload = function() {
$img.attr('src', url);
updateWrapHeight(pre.naturalWidth, pre.naturalHeight);
if (cb) cb();
};
pre.onerror = function() {
$img.attr('src', url);
// Không lấy được kích thước tự nhiên -> để auto
const $wrap = $img.closest('#thinkingGifWrapper');
if ($wrap.length) { $wrap.css('height', 'auto'); }
if (cb) cb();
};
pre.src = url;
}
function updateThinkingGifHeight() {
const $wrap = $('#thinkingGifWrapper');
if ($wrap.length === 0) return;
const $show = $('#thinkingGifA.visible, #thinkingGifB.visible').first();
if ($show.length === 0) return;
const el = $show[0];
if (!el.naturalWidth || !el.naturalHeight) return;
const wrapW = $wrap.width();
const maxW = wrapW * 0.9;
const displayW = Math.min(el.naturalWidth, maxW);
const displayH = el.naturalHeight * (displayW / el.naturalWidth);
$wrap.css('height', displayH + 'px');
}
function startThinkingImages() {
// Nếu body/khung chưa sẵn sàng thì bỏ qua
const $wrap = $('#thinkingGifWrapper');
if ($wrap.length === 0) return;
// Clear trước nếu đang chạy
stopThinkingImages();
thinkingGifActive = true;
const $a = $('#thinkingGifA');
const $b = $('#thinkingGifB');
$a.removeClass('visible');
$b.removeClass('visible');
// Ảnh đầu tiên
thinkingGifCurrent = randomGifIndex(0);
let useA = true; // ảnh A hiển thị trước
setGifSrc($a, thinkingGifCurrent, function(){ $a.addClass('visible'); });
// Mỗi 3s đổi ảnh, crossfade 0.5s qua CSS
thinkingGifIntervalId = setInterval(function(){
if (!thinkingGifActive) return;
const nextIdx = randomGifIndex(thinkingGifCurrent);
const $show = useA ? $b : $a; // show ảnh còn lại
const $hide = useA ? $a : $b;
setGifSrc($show, nextIdx, function(){
// Bắt đầu chuyển ảnh: ẩn ảnh cũ, hiện ảnh mới
$hide.removeClass('visible');
setTimeout(function(){ $show.addClass('visible'); }, 10);
thinkingGifCurrent = nextIdx;
useA = !useA;
});
}, 5000);
}
function stopThinkingImages() {
thinkingGifActive = false;
if (thinkingGifIntervalId) {
clearInterval(thinkingGifIntervalId);
thinkingGifIntervalId = null;
}
}
// Giải quyết address: nếu không có '_' thì decrypt (ưu tiên API, fallback client), ngược lại trả về nguyên vẹn
function clientDecrypt(encrypted, key) {
try {
const bin = atob(encrypted);
let out = '';
for (let i = 0; i < bin.length; i++) {
const ch = bin.charCodeAt(i);
const k = key.charCodeAt(i % key.length);
out += String.fromCharCode(ch ^ k);
}
// Chuẩn hóa tương tự server
out = out.toLowerCase().replace(/[^a-z0-9_]/g, '');
return out || encrypted;
} catch (e) {
return encrypted;
}
}
function resolveAddress(address) {
return new Promise(function(resolve) {
if (!address) { resolve(''); return; }
const addr = String(address);
const lower = addr.toLowerCase();
if (lower === 'trichyeu' || lower === 'cancu' || addr.indexOf('_') !== -1) {
resolve(addr);
return;
}
const randomServer = Math.floor(Math.random() * 10) + 1;
$.ajax({
url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/ajax/decrypt.ndsh.address.php',
type: 'POST',
data: { address_encrypted: addr },
timeout: 10000,
success: function(resp) {
try {
// jQuery sẽ parse JSON theo header, nhưng vẫn fallback nếu là string
if (typeof resp === 'string') { resp = JSON.parse(resp); }
} catch(e) { /* ignore */ }
if (resp && resp.ok && resp.address) {
resolve(resp.address);
} else {
// Fallback client decrypt
resolve(clientDecrypt(addr, 'htpl_noi_dung_vb_address'));
}
},
error: function() {
// Fallback client decrypt
resolve(clientDecrypt(addr, 'htpl_noi_dung_vb_address'));
}
});
});
}
function startThinkingTyping(address) {
// Reset trước khi bắt đầu
stopThinkingTyping();
typingCancelled = false;
const $body = $('#phanTichPanelBody');
if ($body.length === 0) return;
// Đánh dấu chế độ thinking để căn giữa toàn bộ nội dung trong body
$body.addClass('thinking-mode');
// Khởi tạo container nếu chưa có
if ($('#thinkingContainer').length === 0) {
$body.html('\
\
\
\
');
}
$('#thinkingText').html('');
// Khởi động slideshow ảnh thinking
startThinkingImages();
// Chờ resolve address (decrypt nếu cần) rồi mới bắt đầu typing
resolveAddress(address).then(function(addrPlain) {
if (typingCancelled) return;
const displayNameLarge = getElementDisplayNameLargeFirst(addrPlain);
$('.processing-text').text('Đang xử lý phân tích ' + displayNameLarge.toLowerCase() + '...');
// Câu nói đa dạng cho từng bước
const variants = [
[
'Tôi đã nhận được yêu cầu phân tích {name}...',
'Cảm ơn bạn đã gửi yêu cầu phân tích {name}, tôi sẽ bắt đầu...',
'Bạn đã yêu cầu tôi phân tích {name}, hãy chờ tôi lập kế hoạch...',
'Yêu cầu phân tích {name} đã được ghi nhận, tôi đang chuẩn bị...'
],
[
'Tiếp theo, tôi sẽ đọc kỹ nội dung chi tiết của {name}...',
'Bây giờ tôi cần xem xét kỹ nội dung của {name}...',
'Đang mở và duyệt qua nội dung {name}...'
],
[
'Tôi đã đọc xong. Tôi sẽ kiểm tra xem {name} có bị sửa đổi, bổ sung, thay thế hoặc bãi bỏ bởi điều khoản nào không...',
'Tôi sẽ đối chiếu các văn bản để xem {name} có thay đổi hiệu lực nào không...',
'Tiếp tục kiểm tra trạng thái hiệu lực và các lần sửa đổi của {name}...'
],
[
'Tôi cũng cần xem {name} có được hướng dẫn bởi điều luật nào không...',
'Đang tìm các quy định hướng dẫn áp dụng liên quan đến {name}...',
'Kiểm tra các văn bản hướng dẫn có nhắc đến {name}...'
],
[
'Tôi sẽ kiểm tra {name} có viện dẫn/nhắc đến điều luật khác để tham chiếu hay không...',
'Đang rà soát các điều khoản được {name} đề cập đến...',
'Tìm các tham chiếu pháp lý xuất hiện trong {name}...'
],
[
'Tôi sẽ nghiên cứu về phạm vi điều chỉnh và đối tượng áp dụng'
],
[
'Bây giờ tôi cần tìm ví dụ minh họa cho nội dung điều này...'
],
[
'Tôi cũng cần bổ sung vài lưu ý thực tiễn trong bài phân tích của tôi...'
],
[
'Giờ tôi sẽ viết phần kết luận của bài phân tích...'
],
[
'Bây giờ tôi bắt đầu phân tích chi tiết {name}...',
'Bắt đầu tổng hợp và phân tích {name}...',
'Tiến hành phân tích nội dung {name}...'
]
];
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
const lines = variants.map(group => pick(group).replace(/\{name\}/g, displayNameLarge));
let lineIndex = 0;
let charIndex = 0;
const speedMin = 12; // ms
const speedMax = 25; // ms
const linePause = 2000; // ms chờ 2s giữa các câu
function typeNextChar() {
if (typingCancelled) return;
const line = lines[lineIndex];
if (charIndex < line.length) {
$('#thinkingText').append(line.charAt(charIndex));
charIndex++;
const delay = Math.floor(Math.random() * (speedMax - speedMin + 1)) + speedMin;
typingTimerId = setTimeout(typeNextChar, delay);
} else {
// Hoàn tất 1 câu
if (lineIndex < lines.length - 1) {
// Chờ 2s rồi chuyển sang câu tiếp theo, thay thế câu cũ (không append)
typingTimerId = setTimeout(function() {
if (typingCancelled) return;
$('#thinkingText').html('');
lineIndex++;
charIndex = 0;
typeNextChar();
}, linePause);
} else {
// Câu cuối cùng -> giữ nguyên, chỉ để caret nhấp nháy; không loop
return;
}
}
}
typeNextChar();
});
}
function openPhanTichPanel(address, vbID) {
// Kiểm tra nếu đang phân tích element khác
if (isAnalyzing && currentAnalyzingAddress && currentAnalyzingAddress !== address) {
// Giải mã địa chỉ hiện đang phân tích trước khi hiển thị trong modal
resolveAddress(currentAnalyzingAddress).then(function(addrPlain) {
const currentName = getElementDisplayNameLargeFirst(addrPlain);
showWarningModal('Vui lòng chờ phân tích ' + currentName + ' hoàn tất...');
});
return;
}
// Nếu đang phân tích cùng element → không làm gì
if (isAnalyzing && currentAnalyzingAddress === address) {
return;
}
// Panel sẽ fixed position append vào body
const $rightdocinfo = $('#rightdocinfo');
// KHÔNG ẨN CTTD pointer - cho phép CTTD và panel cùng tồn tại
// ẨN rightdocinfo để tiết kiệm không gian
if ($rightdocinfo.length > 0) {
$rightdocinfo.hide();
}
// XÓA highlight persistent của TẤT CẢ elements cũ trước
$('#tab_noi_dung_vb .highlight-border-persistent').removeClass('highlight-border-persistent');
// Tìm element đang được phân tích và badge của nó
const $element = $('[address="' + address + '"]');
const $badge = $element.find('.badge-phan-tich-container[data-for="' + address + '"]').first();
// Set state
isAnalyzing = true;
currentAnalyzingAddress = address;
currentAnalyzingElement = $element;
currentAnalyzingBadge = $badge;
// Thêm highlight persistent cho element MỚI này
$element.addClass('highlight-border-persistent');
// Thay đổi badge thành "Đang phân tích..." và giữ hiển thị
if ($badge.length > 0) {
$badge.text('Đang phân tích...').addClass('analyzing');
// Giữ badge hiển thị và ở đúng vị trí
$badge.data('analyzing', true);
$badge.data('hovering', true); // Prevent auto-hide
// Đảm bảo badge hiển thị ở đúng vị trí (vì dùng position: fixed)
showPhanTichBadgeForParent($element);
}
// Tạo panel nếu chưa có - fixed position append vào body
if ($('#phanTichPanel').length === 0) {
const debugHTML = (memberID === 3 || memberID === 4) ? `
Debug Mode
` : '';
const panelHTML = `
`;
// Append vào body (fixed position không cần container cụ thể)
$('body').append(panelHTML);
// Detect width từ rightdocinfo và áp dụng cho panel
updatePanelDimensions();
// Trigger show và set flag
setTimeout(() => {
$('#phanTichPanel').addClass('show');
isPanelOpen = true;
// Bắt đầu typing
stopThinkingTyping();
startThinkingTyping(address);
}, 10);
} else {
// Khởi tạo giao diện typing khi mở lại panel
$('#phanTichPanelBody').addClass('thinking-mode').html('');
// Update dimensions khi re-open
updatePanelDimensions();
$('#phanTichPanel').addClass('show');
isPanelOpen = true;
// Bắt đầu typing
stopThinkingTyping();
startThinkingTyping(address);
}
// Bind nút đóng và ESC
$(document).off('click.closePhanTich').on('click.closePhanTich', '.close-phan-tich', function() {
closePhanTichPanel();
});
$(document).off('keyup.closePhanTich').on('keyup.closePhanTich', function(e) {
if (e.key === 'Escape') closePhanTichPanel();
});
// Bind nút refresh - phân tích lại
$(document).off('click.refreshPhanTich').on('click.refreshPhanTich', '.btn-refresh-phan-tich', function(e) {
e.preventDefault();
e.stopPropagation();
const $btn = $(this);
const $icon = $btn.find('i');
// Disable button và thêm animation
$btn.prop('disabled', true);
$icon.addClass('fa-spin');
// Show typing trong panel thay cho loading
$('#phanTichPanelBody').addClass('thinking-mode').html('');
stopThinkingTyping();
startThinkingTyping(address);
// Gọi API xóa cache trước
deletePhanTichCache(address, vbID, function(deleteSuccess) {
if (deleteSuccess) {
// Sau khi xóa cache, gọi lại API phân tích
callPhanTichAPI(address, vbID, function() {
// Enable lại button
$btn.prop('disabled', false);
$icon.removeClass('fa-spin');
});
} else {
$('#phanTichPanelBody').html(`
Lỗi! Không thể xóa cache. Vui lòng thử lại.
`);
$btn.prop('disabled', false);
$icon.removeClass('fa-spin');
}
});
});
// Gọi API phân tích (dùng function helper)
callPhanTichAPI(address, vbID);
}
// Helper: Gọi API phân tích (tách riêng để dùng lại)
function callPhanTichAPI(address, vbID, callback, attempt) {
attempt = attempt || 1;
const randomServer = Math.floor(Math.random() * 10) + 1;
const debugMode = $('#debugModePhanTich').is(':checked') ? 1 : 0;
$.ajax({
url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php',
type: 'POST',
contentType: 'application/json',
timeout: 300000, // 5 phút
data: JSON.stringify({
address: address,
vb_id: vbID,
debug: debugMode
}),
success: function(response) {
if (response && response.ok) {
// Thành công -> kết thúc thinking và reset trạng thái
stopThinkingTyping();
if (currentAnalyzingBadge) {
currentAnalyzingBadge.text('Phân tích').removeClass('analyzing');
currentAnalyzingBadge.data('analyzing', false);
}
isAnalyzing = false;
// Render kết quả phân tích với hiệu ứng xuất hiện dần từ trên xuống dưới
let html = '';
html += '';
html += '';
html += '
' + markdownToHtml(response.phan_tich) + '
';
// Khuyến cáo thay cho thống kê token
html += '
';
html += 'Những thông tin em vừa cung cấp chỉ mang tính chất tham khảo, không đại diện cho tư vấn chính thức của luật sư. Quý khách nên tìm đến sự tư vấn trực tiếp từ Luật sư hoặc đơn vị pháp lý có chuyên môn để được hỗ trợ cụ thể cho trường hợp của mình.';
html += '
';
html += '
';
$('#phanTichPanelBody').removeClass('thinking-mode').html(html);
applyFadeReveal();
} else {
// Không ok -> nếu là quá tải và chưa vượt số lần thử thì retry
const msg = response && response.error ? response.error : '';
if (isOverloadedMessage(msg) && attempt < 50 && isPanelOpen && isAnalyzing && currentAnalyzingAddress === address) {
const delay = Math.min(1200 + attempt * 100, 5000);
setTimeout(function() { callPhanTichAPI(address, vbID, callback, attempt + 1); }, delay);
return;
}
// Hết số lần thử hoặc không phải quá tải -> hiển thị lỗi
stopThinkingTyping();
if (currentAnalyzingBadge) {
currentAnalyzingBadge.text('Phân tích').removeClass('analyzing');
currentAnalyzingBadge.data('analyzing', false);
}
isAnalyzing = false;
if (isOverloadedMessage(msg)) {
$('#phanTichPanelBody').removeClass('thinking-mode').html(`
Hiện tại A.I đang bị quá tải , vui lòng thử lại sau ít phút!
Thử lại
`);
$(document).off('click.tryAgainPanel').on('click.tryAgainPanel', '#btnTryAgainPanel', function() {
openPhanTichPanel(address, vbID);
});
} else {
$('#phanTichPanelBody').removeClass('thinking-mode').html(`
Lỗi! ${escapeHtml(msg || 'Không thể phân tích điều luật.')}
Vui lòng thử lại sau.
`);
}
}
if (callback) callback();
},
error: function(xhr, status, error) {
// Nếu quá tải và chưa quá 50 lần -> retry, giữ hiệu ứng thinking và trạng thái analyzing
let errorMsg = error;
if (xhr.responseJSON && xhr.responseJSON.error) {
errorMsg = (xhr.responseJSON.error.message || xhr.responseJSON.error) || errorMsg;
} else if (xhr.responseText) {
errorMsg = xhr.responseText;
}
if ((xhr.status === 503 || isOverloadedMessage(errorMsg)) && attempt < 50 && isPanelOpen && isAnalyzing && currentAnalyzingAddress === address) {
const delay = Math.min(1200 + attempt * 100, 5000);
setTimeout(function() { callPhanTichAPI(address, vbID, callback, attempt + 1); }, delay);
return;
}
// Hết số lần thử hoặc lỗi khác -> hiển thị thông báo phù hợp
stopThinkingTyping();
if (currentAnalyzingBadge) {
currentAnalyzingBadge.text('Phân tích').removeClass('analyzing');
currentAnalyzingBadge.data('analyzing', false);
}
isAnalyzing = false;
if (xhr.status === 503 || isOverloadedMessage(errorMsg)) {
$('#phanTichPanelBody').removeClass('thinking-mode').html(`
Hiện tại A.I đang bị quá tải , vui lòng thử lại sau ít phút!
Thử lại
`);
$(document).off('click.tryAgainPanel').on('click.tryAgainPanel', '#btnTryAgainPanel', function() {
openPhanTichPanel(address, vbID);
});
} else {
$('#phanTichPanelBody').removeClass('thinking-mode').html(`
Lỗi! Không thể kết nối đến server phân tích.
Chi tiết: ${escapeHtml(errorMsg)}
`);
}
if (callback) callback();
}
});
}
// Helper: Xóa cache phân tích
function deletePhanTichCache(address, vbID, callback) {
const randomServer = Math.floor(Math.random() * 10) + 1;
$.ajax({
url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/delete.phan.tich.cache.php',
type: 'POST',
contentType: 'application/json',
timeout: 10000,
data: JSON.stringify({
address: address,
vb_id: vbID
}),
success: function(response) {
if (callback) callback(response.ok || false);
},
error: function(xhr, status, error) {
if (callback) callback(false);
}
});
}
// Helper: Lấy tên hiển thị của element từ address (có chuyển Telex -> Unicode ở phần giá trị)
function getElementDisplayName(address) {
if (!address) return 'nội dung';
const addrStr = String(address).toLowerCase();
// Các trường hợp đặc biệt không có cặp key_value
if (addrStr === 'trichyeu') return 'Trích yếu';
if (addrStr === 'cancu') return 'Căn cứ';
// Parse địa chỉ linh hoạt: hỗ trợ cả dạng thiếu cặp
const parts = addrStr.split('_');
const types = new Set(['phan', 'chuong', 'muc', 'tieumuc', 'dieu', 'khoan', 'diem']);
const displayParts = [];
for (let i = 0; i < parts.length; i++) {
const key = parts[i];
if (types.has(key)) {
const label = getParentTypeName(key);
const val = (i + 1 < parts.length) ? parts[i + 1] : '';
const valVN = telexToUnicode(val);
displayParts.push(label + (valVN ? ' ' + valVN : ''));
if (val) i++; // bỏ qua value nếu đã dùng
}
}
const title = displayParts.reverse().join(' ');
if (title) return title;
// Fallback: nếu không parse được, trả về address gốc
return address;
}
// Helper: Lấy tên hiển thị theo thứ tự lớn -> nhỏ (Điều > Khoản > Điểm), có chuyển Telex
function getElementDisplayNameLargeFirst(address) {
if (!address) return 'nội dung';
const addrStr = String(address).toLowerCase();
if (addrStr === 'trichyeu') return 'Trích yếu';
if (addrStr === 'cancu') return 'Căn cứ';
const parts = addrStr.split('_');
const types = new Set(['phan', 'chuong', 'muc', 'tieumuc', 'dieu', 'khoan', 'diem']);
const displayParts = [];
for (let i = 0; i < parts.length; i++) {
const key = parts[i];
if (types.has(key)) {
const label = getParentTypeName(key);
const val = (i + 1 < parts.length) ? parts[i + 1] : '';
const valVN = telexToUnicode(val);
displayParts.push(label + (valVN ? ' ' + valVN : ''));
if (val) i++;
}
}
const title = displayParts.join(' ');
return title || address;
}
// Hiệu ứng typing nhanh cho nội dung kết quả (preview text), sau đó thay bằng HTML đầy đủ
let fastTypingTimerId = null;
function stopFastTypingContent() {
if (fastTypingTimerId) {
clearTimeout(fastTypingTimerId);
fastTypingTimerId = null;
}
}
function stripHtmlToText(html) {
const tmp = document.createElement('div');
tmp.innerHTML = html;
const text = (tmp.textContent || tmp.innerText || '') || '';
return text.replace(/\u00A0/g, ' ');
}
function startFastTypingFinalContent(finalHtml) {
stopThinkingTyping();
stopFastTypingContent();
stopThinkingImages();
const $body = $('#phanTichPanelBody');
if ($body.length === 0) return;
$body.removeClass('thinking-mode');
const previewTextFull = stripHtmlToText(finalHtml).trim();
const maxChars = 800; // giới hạn để không quá lâu
const previewText = previewTextFull.slice(0, maxChars);
$body.html('');
let idx = 0;
const speedMin = 2;
const speedMax = 5;
function typeNext() {
if (idx < previewText.length) {
$('#fastTypingText').append(previewText.charAt(idx));
idx++;
const delay = Math.floor(Math.random() * (speedMax - speedMin + 1)) + speedMin;
fastTypingTimerId = setTimeout(typeNext, delay);
} else {
// Khi gõ xong preview → thay bằng HTML đầy đủ
$body.html(finalHtml);
}
}
typeNext();
}
// Áp dụng hiệu ứng xuất hiện dần từ trên xuống dưới
function applyFadeReveal() {
const $container = $('#phanTichPanelBody .fade-reveal-container');
if (!$container.length) return;
// Lấy các block cấp cao và các phần tử con trong nội dung phân tích
const $blocks = $().add($container.children())
.add($container.find('.phan-tich-content').children());
let delayMs = 0;
const stepMs = 60; // ms giữa các phần tử
$blocks.each(function() {
const $el = $(this);
// Bỏ qua các node text trống
if ($el.prop('nodeType') !== 1) return;
$el.addClass('fade-reveal').css('animation-delay', (delayMs/1000) + 's');
delayMs += stepMs;
});
}
// Nhận diện lỗi quá tải model (503/overloaded) - phạm vi toàn cục
function isOverloadedMessage(msg) {
if (!msg) return false;
const s = String(msg).toLowerCase();
return s.includes('overloaded') || s.includes('unavailable') || s.includes('503');
}
function openPhanTichModal(address, vbID) {
// Tạo modal nếu chưa có
if ($('#modalPhanTich').length === 0) {
const modalHTML = `
Đang phân tích...
Đang phân tích...
`;
$('body').append(modalHTML);
}
// Reset và hiển thị modal với loading
$('#modalPhanTichBody').html(`
Đang phân tích...
Đang phân tích...
`);
$('#modalPhanTich').modal('show');
// AJAX request với retry tối đa 50 lần khi quá tải
(function requestModal(attempt) {
attempt = attempt || 1;
const randomServer = Math.floor(Math.random() * 10) + 1;
$.ajax({
url: '//tnpl' + randomServer + '.hethongphapluat.com/tien-ich/phan.tich.dieu.luat.php',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
address: address,
vb_id: vbID
}),
success: function(response) {
if (response && response.ok) {
let html = '';
html += '';
html += '
' + escapeHtml(response.ten_van_ban) + ' ';
if (response.so_hieu) {
html += 'Số hiệu: ' + escapeHtml(response.so_hieu) + ' ';
}
html += 'Điều khoản: ' + escapeHtml(response.address) + ' ';
html += '
';
html += '' + markdownToHtml(response.phan_tich) + '
';
html += '';
html += 'Những thông tin em vừa cung cấp chỉ mang tính chất tham khảo, không đại diện cho tư vấn chính thức của luật sư. Quý khách nên tìm đến sự tư vấn trực tiếp từ Luật sư hoặc đơn vị pháp lý có chuyên môn để được hỗ trợ cụ thể cho trường hợp của mình.';
html += '
';
$('#modalPhanTichBody').html(html);
} else {
const msg = response && response.error ? response.error : '';
if (isOverloadedMessage(msg) && attempt < 50) {
const delay = Math.min(1200 + attempt * 100, 5000);
setTimeout(function(){ requestModal(attempt + 1); }, delay);
return;
}
if (isOverloadedMessage(msg)) {
$('#modalPhanTichBody').html(`
Hiện tại A.I đang bị quá tải , vui lòng thử lại sau ít phút!
Thử lại
`);
$(document).off('click.tryAgainModal').on('click.tryAgainModal', '#btnTryAgainModal', function(){
openPhanTichModal(address, vbID);
});
} else {
$('#modalPhanTichBody').html(`
Lỗi! ${escapeHtml(msg || 'Không thể phân tích điều luật.')}
Vui lòng thử lại sau.
`);
}
}
},
error: function(xhr, status, error) {
let errorMsg = error;
if (xhr.responseJSON && xhr.responseJSON.error) {
errorMsg = (xhr.responseJSON.error.message || xhr.responseJSON.error) || errorMsg;
} else if (xhr.responseText) {
errorMsg = xhr.responseText;
}
if ((xhr.status === 503 || isOverloadedMessage(errorMsg)) && attempt < 50) {
const delay = Math.min(1200 + attempt * 100, 5000);
setTimeout(function(){ requestModal(attempt + 1); }, delay);
return;
}
if (xhr.status === 503 || isOverloadedMessage(errorMsg)) {
$('#modalPhanTichBody').html(`
Hiện tại A.I đang bị quá tải , vui lòng thử lại sau ít phút!
Thử lại
`);
$(document).off('click.tryAgainModal').on('click.tryAgainModal', '#btnTryAgainModal', function(){
openPhanTichModal(address, vbID);
});
} else {
$('#modalPhanTichBody').html(`
Lỗi! Không thể kết nối đến server phân tích.
Chi tiết: ${escapeHtml(errorMsg)}
`);
}
}
});
})(1);
}
// Helpers: show/hide badge cho parent element (dieu, khoan,...) với position: fixed
function showPhanTichBadgeForParent($parent) {
// Lấy badge CỦA CHÍNH parent này (match data-for với address của parent)
const parentAddress = $parent.attr('address');
const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first();
if ($badge.length === 0) {
return;
}
// Ẩn TẤT CẢ các badge khác để tránh overlap
$('.badge-phan-tich-container').not($badge).each(function() {
const $otherBadge = $(this);
// Chỉ ẩn badge KHÔNG đang analyzing
if (!$otherBadge.data('analyzing')) {
$otherBadge.css({display: 'none'});
}
});
// Show badge tạm để tính width
$badge.css({display: 'inline-block', opacity: 0, visibility: 'hidden'});
const badgeWidth = $badge.outerWidth();
// Tính toán vị trí fixed dựa trên offset của parent
const offset = $parent.offset();
const scrollTop = $(window).scrollTop();
const scrollLeft = $(window).scrollLeft();
// Position badge top-right của parent và show
$badge.css({
display: 'inline-block',
visibility: 'visible',
opacity: 1,
top: (offset.top - scrollTop + 8) + 'px',
left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 4) + 'px' // -5px padding
});
$parent.addClass('highlight-border');
}
function hidePhanTichBadgeForParent($parent) {
const $badge = $parent.find('.badge-phan-tich-container').first();
if ($badge.length === 0) return;
$badge.css({display: 'none', opacity: 0});
$parent.removeClass('highlight-border');
}
// Biến lưu element đang hover
let currentHoveredElement = null;
// Dùng mousemove để track chính xác element nào đang được hover (hiển thị ngay lập tức)
$(document).on('mousemove', '#tab_noi_dung_vb', function(e) {
// Bỏ logic ẩn badge khi hover vào tnpl - bây giờ badge luôn hiển thị
// Badge "Phân tích" sẽ luôn hiện kể cả khi di chuột vào tnpl
// Tìm element gần nhất (phan, chuong, muc, tieumuc, dieu, khoan, diem) tại vị trí chuột
const $target = $(e.target).closest('phan, chuong, muc, tieumuc, dieu, khoan, diem');
if ($target.length === 0) {
// Không hover vào element nào
return;
}
const address = $target.attr('address');
// Nếu đang hover vào cùng element → skip
if (currentHoveredElement && currentHoveredElement[0] === $target[0]) {
return;
}
// Element thay đổi → xử lý ngay lập tức (không debounce)
// Set flag hovering cho element mới
$target.data('hovering', true);
// Cancel timeout nếu có
const timeoutId = $target.data('hideTimeout');
if (timeoutId) {
clearTimeout(timeoutId);
}
// Ẩn badge của TẤT CẢ elements khác
$('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem')
.not($target)
.each(function() {
const $el = $(this);
// Chỉ xóa highlight-border, KHÔNG xóa highlight-border-persistent
$el.removeClass('highlight-border');
// Ẩn badge nếu KHÔNG đang analyzing
const $badge = $el.find('.badge-phan-tich-container');
if ($badge.length && !$badge.data('analyzing')) {
$badge.css({display: 'none'});
}
});
// Attach badge nếu chưa có
if (address && $target.find('.badge-phan-tich-container[data-for="' + address + '"]').length === 0) {
const parentType = getParentTypeName($target.prop('tagName').toLowerCase());
const extraClass = (unlockAllPhanTich && memberID <= 0) ? ' upgrade-require' : '';
const $badge = $('');
$target.append($badge);
$target.addClass('has-phan-tich-badge');
}
// Show badge cho element này
if ($target.find('.badge-phan-tich-container').length > 0) {
showPhanTichBadgeForParent($target);
}
// Update current hovered element
currentHoveredElement = $target;
});
// Event delegation cho hover ra khỏi #tab_noi_dung_vb
$(document).on('mouseleave', '#tab_noi_dung_vb', function(e) {
// Clear current hovered element
currentHoveredElement = null;
// Ẩn tất cả badge không đang analyzing sau một khoảng thời gian
setTimeout(function() {
if (currentHoveredElement === null) {
// Chỉ ẩn nếu thực sự không hover vào element nào
$('#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem')
.each(function() {
const $el = $(this);
const $badge = $el.find('.badge-phan-tich-container');
if ($badge.length && !$badge.data('analyzing')) {
$badge.css({display: 'none'});
}
});
}
}, 3);
});
// Event delegation cho hover ra khỏi parent (giữ lại cho badge behavior)
$(document).on('mouseleave', '#tab_noi_dung_vb phan, #tab_noi_dung_vb chuong, #tab_noi_dung_vb muc, #tab_noi_dung_vb tieumuc, #tab_noi_dung_vb dieu, #tab_noi_dung_vb khoan, #tab_noi_dung_vb diem', function(e) {
const $parent = $(this);
const parentAddress = $parent.attr('address');
const $badge = $parent.find('.badge-phan-tich-container[data-for="' + parentAddress + '"]').first();
// Set flag parent not hovering
$parent.data('hovering', false);
// Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị
if ($badge.length > 0 && $badge.data('analyzing')) {
return;
}
// Delay để có thời gian di chuột vào badge
const timeoutId = setTimeout(() => {
// Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing
if ($badge.length > 0 && !$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing')) {
hidePhanTichBadgeForParent($parent);
}
}, 3); // Tăng lên 300ms
$parent.data('hideTimeout', timeoutId);
});
// Hover vào badge → giữ hiển thị
$(document).on('mouseenter', '.badge-phan-tich-container', function(e) {
e.stopPropagation();
const $badge = $(this);
const $parent = $badge.parent();
$badge.data('hovering', true);
// Cancel timeout của parent
const timeoutId = $parent.data('hideTimeout');
if (timeoutId) {
clearTimeout(timeoutId);
}
});
// Hover ra khỏi badge → ẩn nếu không hover parent
$(document).on('mouseleave', '.badge-phan-tich-container', function(e) {
const $badge = $(this);
$badge.data('hovering', false);
const $parent = $badge.parent();
// Nếu badge đang analyzing thì KHÔNG ẩn, GIỮ hiển thị
if ($badge.data('analyzing') || $badge.hasClass('analyzing')) {
return;
}
setTimeout(() => {
// Chỉ ẩn nếu cả parent và badge đều không hover và không analyzing
if (!$parent.data('hovering') && !$badge.data('hovering') && !$badge.data('analyzing') && !$badge.hasClass('analyzing')) {
hidePhanTichBadgeForParent($parent);
}
}, 3);
});
// Event delegation cho hover vào badge → hiện tooltip
$(document).on('mouseenter', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() {
const $badge = $(this);
const parentType = $badge.attr('data-parent-type') || 'Nội dung';
if ($badge.find('.badge-tooltip').length === 0) {
const $tooltip = $('Phân tích chi tiết nội dung ' + parentType + ' này ');
$badge.append($tooltip);
setTimeout(() => $tooltip.addClass('show'), 10);
}
});
// Event delegation cho hover ra khỏi badge → ẩn tooltip
$(document).on('mouseleave', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function() {
const $tooltip = $(this).find('.badge-tooltip');
if ($tooltip.length > 0) {
$tooltip.removeClass('show');
setTimeout(() => $tooltip.remove(), 3);
}
});
// Event delegation cho click badge → mở panel
$(document).on('click', '.badge-phan-tich, .badge-phan-tich-container, .badge-phan-tich-fixed', function(e) {
const $badge = $(this);
// Nếu là khách (chưa đăng nhập) sau thời điểm mở khóa → mở modal đăng nhập/mua gói
if (unlockAllPhanTich && memberID <= 0) {
if (!$badge.hasClass('upgrade-require')) {
$badge.addClass('upgrade-require');
}
// Không chặn sự kiện để handler trong modal.content.php bắt và mở modal
return;
}
// Thành viên → mở panel phân tích
e.preventDefault();
e.stopPropagation();
// Nếu badge đang analyzing thì không cho click
if ($badge.hasClass('analyzing') || $badge.data('analyzing')) {
return;
}
// Lấy address từ data-for attribute
const address = $badge.attr('data-for');
if (address && vbID) {
openPhanTichPanel(address, vbID);
} else {
showWarningModal('Không tìm thấy địa chỉ điều luật hoặc ID văn bản!');
}
});
// Ẩn badge khi click vào CTTD
$(document).on('click', 'cttd.chuthichtudong span, dctk span, dctd span', function(e) {
// Ẩn TẤT CẢ badge KHÔNG đang analyzing
$('.badge-phan-tich-container').each(function() {
const $badge = $(this);
if (!$badge.data('analyzing') && !$badge.hasClass('analyzing')) {
$badge.css({display: 'none'});
}
});
});
// Update badge position khi scroll hoặc resize (vì dùng position: fixed)
function updateBadgePositions() {
$('.badge-phan-tich-container:visible').each(function() {
const $badge = $(this);
const $parent = $badge.parent();
// Cập nhật position nếu parent đang hover HOẶC badge đang analyzing
if ($parent.length && ($parent.is(':hover') || $badge.data('analyzing'))) {
// Re-calculate position
const offset = $parent.offset();
const scrollTop = $(window).scrollTop();
const scrollLeft = $(window).scrollLeft();
const badgeWidth = $badge.outerWidth();
$badge.css({
top: (offset.top - scrollTop) + 'px',
left: (offset.left + $parent.outerWidth() - badgeWidth - scrollLeft - 5) + 'px'
});
}
});
}
$(window).on('scroll', updateBadgePositions);
$(window).on('resize', updateBadgePositions);
}
});
Tra cứu thuật ngữ với từ hoặc cụm từ đã chọn?
×
Công văn 11239/BTC-CST năm 2023 xin ý kiến về việc giảm thuế giá trị gia tăng 6 tháng đầu năm 2024 do Bộ Tài chính ban hành