# CÀI ĐẶT DTECH


# PHIÊN BẢN ĐÓNG GÓI

Hướng dẫn cài đặt máy chủ và trạm cho phiên bản đóng gói (chạy setup)

## Các bước chính

* Tạo thư mục cài đặt trên máy chủ khách hàng
* Copy bộ cài sang máy chủ khách hàng
* Cài đặt chương trình
* Cập nhật hệ thống và Sao lưu dữ liệu
* Cài đặt trạm nếu có

## Hướng dẫn thao tác

## B1: Tạo thư mục cài đặt

* Tạo thư mục: **Dtech**
* Tạo thư mục cài đặt phần mềm theo bảng nguyên tắc dưới đây:
  * "**Dtech**" + "Service - nếu là bản dịch vụ" + "**Ký hiệu sản phẩm**" + "**Tên đơn vị** - áp dụng với bản lẻ" + "**Phân loại khác** - Nếu có phân loại khác ( phiên bản quản trị, phiên bản thuế, phiên bản ngân hàng, phiên bản mua thứ nn"
  * *Dtech*: Bắt buộc
  * *Service*: Nếu là bản dịch vụ
  * *Ký hiệu sản phẩm*: Dựa vào danh sách cung cấp ở bảng ở dưới. Với bản kế toán doanh nghiệp thì được xem là bản gốc, nên không có ký hiệu sản phẩm
  * *Tên đơn vị*: Viết tắt tên đơn vị hoặc thương hiệu hoặc từ mang ý nghĩa của đơn vị. Với trường hợp chưa có thông tin đơn vị thì bỏ qua hoặc tìm từ liên quan nhất
  * *Phân loại khác*: Dùng để phân loại các nội dung khác như: Phiên bản quản trị, thuế, ngân hàng, mua thêm phiên bản thứ n+1

<table><thead><tr><th width="238">Ký hiệu sản phẩm</th><th width="226">Mô tả</th><th width="170">Ký hiệu khác</th><th>Mô tả</th></tr></thead><tbody><tr><td></td><td>kế toán Doanh nghiệp</td><td>QT</td><td>quản trị</td></tr><tr><td>HKD</td><td>kế toán Hộ kinh doanh</td><td>CB</td><td>thuế</td></tr><tr><td>HTX</td><td>kế toán Hợp tác xã</td><td>NH</td><td>ngân hàng</td></tr><tr><td>HCSN</td><td>kế toán Hành chính sự nghiệp</td><td>_PB2</td><td>phiên bản số 2, dùng song song pb1</td></tr><tr><td>HRM</td><td>Nhân sự</td><td>_V2</td><td>Phiên bản số 2, bỏ phiên bản 1</td></tr><tr><td>SHOP</td><td>Bán hàng</td><td></td><td></td></tr><tr><td>WH</td><td>Kho</td><td></td><td></td></tr><tr><td>DTECH88</td><td>Kế toán hộ kinh doanh theo TT88</td><td>_TÊN NGẮN HKD</td><td>Bản 1 hộ</td></tr><tr><td>DTECH88_SERVICE</td><td>Kế toán hộ kinh doanh theo TT88 ( bản dịch vụ)</td><td></td><td>Bản nhiều hộ</td></tr><tr><td>DTECH88_SERVICE_Y</td><td>Kế toán HKD theo TT88 ( bản dịch vụ thuê bao theo năm)</td><td>_Y ( Đánh dấu bản  theo năm)</td><td>Bản nhiều hộ</td></tr><tr><td>DTECH_SERVICE</td><td>Kế toán doanh nghiệp theo TT200 hoặc TT133</td><td></td><td>Bản nhiều Cty</td></tr><tr><td>DTECH_SERVICE_Y</td><td>Kế toán doanh nghiệp theo TT200 hoặc TT133 thuê bao theo năm</td><td>_Y ( Đánh dấu bản theo năm</td><td>Bản nhiều Cty</td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table>

Ví dụ

|                   |                   |                        |
| ----------------- | ----------------- | ---------------------- |
| DTECH TUNGVI      | DTECH TUNGVI\_QT  | DTECH SERVICE HKD\_PB2 |
| DTECH SERVICE     | DTECH TUNGVI\_CB  | DTECH HRM HAILONG      |
| DTECH HKD TUNGVI  | DTECH TUNGVI\_NH  | DTECH WH HAILONG       |
| DTECH SERVICE HKD | DTECH SERVICE\_V2 | DTECH SHOP HAILONG     |

## B2: Copy bộ cài

* Kỹ thuật Dtech sẽ được cấp tài khoản để đăng nhập FTP để copy bộ cài đặt tương ứng với nội dung cài đặt được bàn giao
* Copy đúng bộ cài và chuyển vào thư mục đã tạo ở bước 1

## B3: Chạy bộ cài

* Cài đặt chương trình vào đúng thư mục đã tạo
* Khi tạo data dữ liệu, tên data dữ liệu đặt khớp với tên thư mục
* Link mở file tài liệu word cài đặt phần mềm \[bổ sung]

## B4: Đăng ký mở khoá

* \[bổ sung] ảnh chụp lúc đăng ký khoá hoặc nằm ở trong tài liệu bước 3

## B5: Nhận khoá và mở khoá

* Đăng nhập Iso và tìm kiếm đơn hàng cần mở khoá. Bấm mở khoá \[bổ sung ảnh mở khoá]
* Copy file lic sang máy khách hàng để áp dụng khoá \[bổ sung ảnh áp dụng khoá]

## B6: Đăng nhập phần mềm

* Chạy thử sao lưu dữ liệu: Để đảm bảo việc sao lưu hoạt động bình thường
* Chạy cập nhật hệ thống: Để đảm bảo hệ thống được mới nhất
* Sửa tên biểu tượng phần mềm ở màn hình desktop (Shortcut)

## B7: Cài đặt trạm

{% embed url="<https://youtu.be/xNdFa3COiAY>" %}
Cấu hình máy chủ + cài trạm online
{% endembed %}

### B7.1 Cấu hình máy chủ

Nếu máy trạm cùng mạng Lan với máy chủ thì có thể bỏ qua bước này. Chỉ áp dụng với máy trạm chạy online hoặc mạng Lan nhưng không kết nối được với máy chủ nên chuyển cài trạm dạng online

* Cài đặt chương trình Radmin VPN
* Tạo User và mật khẩu mạng ảo theo nguyên tắc:
  * Tên mạng ảo: Dte\_tenDonvi
  * Mật khẩu mạng ảo: Dtech\@123
* Cấu hình mở port trên Sql: 2866
* Cấu hình mở port trên tường lửa: In và out cho port 2866

### B7.2 Cài đặt máy trạm

* Copy 01 thư mục trạm cùng dòng phiên bản hoặc file setup trạm tương ứng bộ cài máy chủ trên FTP
  * Không nhất thiết là thư mục của phần mềm đã cài trên máy chủ khách hàng
  * Chỉ cần là cùng dòng sản phẩm: Kế toán (TT200,133,88,24,107), Hrm, Wh, Bh
* Tạo thư mục: Tương tự như máy chủ
  * Tạo thư mục **Dtech**
  * Tạo thư mục cài đặt phần mềm:&#x20;
    * ***Nguyên tắc***: Giống thư mục máy chủ + "id trạm"
    * ***Ví dụ***: Dtech02 hoặc DtechNB02, DtechCB02, DtechNH02, DtechWH02
    * Hiện tại: 10/2024 Có thể không cần id trạm. để tên thư mục giống máy chủ
* Tạo biểu tượng làm việc (Shortcut) ra desktop
  * Đặt tên giống tên thư mục
* Chạy exe và kết nối với máy chủ để tạo trạm
  * Với máy trạm online thì cần cài đặt Radmin và kết nối vào mạng ảo đã tạo ở máy chủ
  * Xoá file Tram (nếu có) trước khi chạy exe


# PHIÊN BẢN CUSTOMIZE

## Các bước chính

* Tạo phiên bản lập trình: Phiên bản lập trình dùng để lập trình chức năng
* Tạo bộ cài đặt sang máy khách hàng từ bản lập trình
* Cài đặt phần mềm bên máy khách hàng

## Hướng dẫn thao tác

#### B1: Cài đặt phiên bản lập trình trên server

* Tạo data cho bản lập trình
  * Format: Tên công ty + Tên phiên bản chuẩn
  * Ví dụ: Caosu75\_Dev20
    * caosu75: Tên đơn vị
    * Dev20: Phiên bản chuẩn nhân ra để làm bản lập trình
* Restore bản dev chuẩn sang cho bản lập trình
* Copy thư mục bản dev chuẩn thành thư mục bản lập trình. Xoá tệp Tram
* Chạy exe kết nối exe với data lập trình
* Với bản lập trình. Tram mặc định =1 ở tất cả các máy
* Chạy lệnh loại bỏ một số modul Erp đang lập trình dở dang

```sql
// chạy code để disable các modul Erp đang dở dang
update _giaodien set ModiDate=GETDATE(),Disable=1  where ma>='16'
Update report set HeThong=1,Modidate=getdate() where left(ma,3)='KHO' and Report>1000
```

#### B2: Tạo data relese để cài đặt cho khách hàng

* Sau khi lập trình xong, Restore data lập trình sang data tmpRelese
* Chạy tệp lệnh relese cho data tmpRelese
* Backup data tmpRelese để cài đặt
* Nén thư mục lập trình để cài đặt: Loại bỏ exe, tram
* copy lên ftp\://trienkhai/tên thư mục của đơn vị
  * Để có thể copy sang cho khách hàng
  * Để lưu vết cấu hình bản lập trình tương ứng nếu sau này cần

#### B3: Chuẩn bị máy chủ khách hàng

* Tạo thư mục Dtech và thư mục cài đặt
* Copy bộ cài
  * Thư mục phần mềm , Data rele
  * Sql Express
  * Radmin (VPN ảo) nếu có trạm online&#x20;

#### B4: Cài đặt máy chủ

* Cài đặt sqlExpress
* Chạy exe để kết nối data
  * Tạo data khách hàng
  * Restore data rele vào data khách hàng
* Đăng ký và mở khoá
  * Lưu ý: Khoá phải chọn loại sản phẩm là customize. để phần mềm của khách hàng không được phép cập nhật từ hệ thống chung bản đóng gói
* Chạy sao lưu kiểm tra: Vì có thể data đang lưu thông tin địa chỉ của máy chủ cũ
* Ẩn chức năng update hệ thống: Bản cus không update từ hệ thống. Nếu exe mới thì phần mềm đã tự động cấm chức năng
* Tạo biểu tượng phần mềm trên desktop (shortcut)

#### B5: Cài đặt máy trạm

* Cài đặt tương tự như bản đóng gói


# UPDATE LẬP TRÌNH SANG KHÁCH HÀNG

#### 1. update object mới trên form chung

* Update chọn form02 + object form02

#### 2. update code của object cũ trên form chung

* Update chọn form02 + object cần xử lý

#### 3. update code của cột nhập liệu chi tiết

* Update brow Nhaplieuct
* Update brow cột cần update

#### 4. update code form chung nhập liệu

* Update form và object

#### 5. update table mới thêm

* update table, titbrow, dòng quản lý bảng trong table Manager
* Tạo lại view VR: Vào báo cáo để bấm nút tạo lại view
* Tạo lại view Phiếu in: Có thể bỏ qua nếu không chỉnh sửa mẫu in bên khách hàng. Còn đầy đủ thì vào phiếu in để bấm nút tạo lại view Phiếu in


# CHUYỂN MÁY CHỦ ĐÓNG GÓI

## Ý nghĩa chức năng

Hướng dẫn chuyển máy chủ phần mềm từ máy A sang máy B

## Hướng dẫn thao tác

### I. Máy chủ cũ

1. Backup dữ liệu trước khi thực hiện
   * Backup dữ liệu để đề phòng trường hợp tắt dịch vụ sql bị lỗi data
   * Cần kiểm tra thư mục đường dẫn của phần mềm: Tránh xử lý sai thư mục (do có nhiều phần mềm).
2. Tắt dịch vụ Sql để chuẩn bị copy toàn bộ data
   * Tắt dịch vụ sql thì mới copy được data sql: Áp dụng với 1 phần mềm
   * Chạy exe để đăng nhập vào chức năng quản lý dữ liệu: Detach riêng data cần chuyển. Áp dụng với máy chủ có nhiều phần mềm hoặc 1 phần mềm
3. Xử lý các file ở máy chủ cũ để máy chủ cũ không vào được
   * Xoá file Tram, DTechV001.exe, CheckForUpdate.exe,  \*\*.Lic&#x20;
   * Xoá biểu tượng shortcut trên màn hình, taskbar (nếu có), starMenu (nếu có)
   * Tạo file notepad: Nội dung là chuyển máy chủ ngày dd/MM/yyyy để phòng trường hợp sau này khách hàng gửi lại máy chủ cũ báo là phần mềm không vào được ( Hoặc có thể sửa tên thư mục của phần mềm - Gắn thêm nội dung "*Chuyển máy chủ*")
4. Nén thư mục của phần mềm để chuyển sang máy chủ mới
   * Nén thư mục data riêng, các thư mục khác riêng, bỏ thư mục backup và temp
5. Chuyển thư mục nén qua máy chủ mới
   * Có thể chuyển qua ultra, anydesk hoặc FTP

### II. Máy chủ mới

1. Tạo thư mục theo chuẩn&#x20;
   * Xem hướng dẫn [***Xem tại đây***](/cai-dat-dtech/phien-ban-dong-goi#b1-tao-thu-muc-cai-dat)
2. Copy bộ cài từ máy chủ cũ sang máy chủ mới
   * Data, cấu hình của phần mềm
   * Sql Express 2012
   * Radmin (nếu cần cài trạm online) hoặc cứ copy đề phòng
3. Cài đặt SqlExpress2012
   * Namestance : Dtech\_Express
   * User: sa
   * Mk:&#x20;
4. Chạy exe để kết nối giữa Sql và data
   * Phân quyền full control cho data trước khi kết nối
     * Với trường hợp nhiều data (bản dịch vụ), thì việc phân quyền full control cho nhiều data thực hiện bằng lệnh cmd
     * Tìm cmd, chuột phải vào cmd chọn runadmin sau đó gõ câu lệnh sau vào
     * ICACLS "D:\DTECH\DTECH88 Service\DataSQL" /grant "Everyone":F /T
     * sau khi chạy xong,thì cmd trả kết quả là một list các data được add
   * Nếu không attach được thì thử:
     * Xoá file \*\_log.ldf rồi kết nối lại
     * Khởi động lại Sql
     * Khởi động lại máy tính
   * Có thể phát sinh lỗi không attach data vào được (Cập nhật các lỗi kết nối)
5. Đăng ký khoá và mở khoá
6. Chạy phần mềm
   * Tạo shortcut ra màn hình desktop
   * Chạy sao lưu dữ liệu: Để kiểm tra sao lưu đúng đường dẫn
   * Cập nhật hệ thống mới: Để đảm bảo hệ thống mới nhất cho khách hàng
7. Cài đặt trạm nếu có
   * Xem hướng dẫn [***Xem tại đây***](/cai-dat-dtech/phien-ban-dong-goi#b7-cai-dat-tram)


# LỖI KẾT NỐI MÁY CHỦ

## HƯỚNG DẪN

#### TH1: Máy chủ bị đổi tên

* Xoá file tram
* Chạy exe kết nối lại với thông tin máy chủ mới

#### TH2: Dịch vụ SQL server bị tắt

* Mở services
* Tìm tới **SQL SERVER (dtech\_express)**: Chuột phải và chọn restart lại dịch vụ
* Có trường hợp Sqlserver vẫn đang running nhưng cũng không vào được phần mềm, thì cũng có thể thử restart lại dịch vụ

#### TH3: Data Sql bị lỗi hoặc phân vùng ổ cứng bị  lỗi

* Sử dụng lệnh check data sql

#### TH4: Không tìm thấy sql config để vào restart

* Vào theo đường dẫn sau để tìm file&#x20;
* C:\Windows\SysWOW64\sqlserverManager...

#### TH5: không restart được sql

* Vào sqlconfig kiểm tra tcip và via phải enable hết lên
* restart báo lỗi "***time fashion...."*** thì gỡ sql cũ ra cài lại


# LỖI ATTACH DATA KHÔNG NHẬN

## Hướng dẫn

#### 1. Hướng dẫn attach data

* Cần phân quyền data và log data full quyền cho user everyone
* Đăng nhập exe, vào chức năng QL dữ liệu

#### 2. Nếu không attach data được&#x20;

* Kiểm tra đã phân quyền everyone data và log chưa?
* Thử cắt log đi sang thư mục khác và add lại
* Thử khởi động lại máy rồi add lại
* Thử khởi động lại sql rồi add lại
* Thử drop databse và attach lại bằng lệnh

```sql
-- Trước khi làm, hãy copy data ra thư mục khác
-- khi drop data có thể data sẽ bị xoá
-- xoá data BichHang. do Sql vẫn đang hiểu là quản lý data Bichhang
Drop database BichHang
-- attach lại data BichHang vào sql ( sử dụng khi mà dùng chức năng DL dữ liệu không được)
EXEC sp_attach_db @dbname = N'BichHang',   
    @filename1 =   
N'D:\Dtech\Data\BichHang.mdf',   
    @filename2 =   
N'D:\Dtech\Data\BichHang_log.ldf';  
-- nếu attach mà báo lỗi access denied, thì phân quyền full control cho data

```

* Có thể kiểm tra file log trong thư mục temp để đoán lỗi không add được
* nếu không được thì khả năng lỗi data


# QUẢN LÝ TRẠM

Một số phiên bản cũ chưa có bảng kiểm tra trạm đăng nhập

## KIỂM TRA TRẠM, USER ĐANG NHẬP

Nhìn vào thời gian nào đăng nhập xa nhất, thì có thể là trạm đó không sử dụng. Báo lại khách hàng kiểm tra user đó còn hoạt động hay không để xử lý tiếp

```sql
  with cte_lichsu as (
  select ComputerName as tenmaytinh, CreateDate as ngayTao,tram, [user] as userDangnhap
  , ROW_NUMBER() over(partition by tram order by createdate desc) as stt
  From chamcong
  where CreateDate>='01/01/2025' --- chặn thời gian cho phù hợp.
  )
  select cte_lichsu.*, users.ten 
  from cte_lichsu 
  inner join [users]  on cte_lichsu.userDangnhap=[users].[user]
  where cte_lichsu.stt=1
  order by tram
------------------ code dưới không sử dụng. vì không chuẩn------
-- Liệt kê các trạm đăng nhập
select T1.*, T2.ten
from
(
    select max(computername) as tenmaytinh,max(noidung) as noidung,
        max(createdate) as thoigian,
        max([user]) as nguoidung,
        tram 
    From chamcong 
	--WHERE NOIDUNG LIKE N'%thuỷ%' -- where thêm ở nội dung sẽ chuẩn hơn
    Group by tram
)T1 
inner join 
(select [user] as nguoidung,* from [users])T2 
on T1.nguoidung = T2.nguoidung
Order by thoigian des
```

## TRẠM ĐĂNG NHẬP BẢN DỊCH VỤ

1. Danh sách tất cả các database và trạm thỏa mãn điều kiện

```sql
DECLARE @DBName NVARCHAR(128);
DECLARE @SQL NVARCHAR(MAX) = N'';
DECLARE @CheckSQL NVARCHAR(MAX);
DECLARE @IsTargetDB INT;

-- 1. Quét các Database đang Online và không phải DB hệ thống
DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.databases
WHERE database_id > 4 AND state = 0;

OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @DBName;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @IsTargetDB = 0;

    -- 2. KIỂM TRA CẤU TRÚC VÀ ĐIỀU KIỆN THÔNG TƯ ĐẦU VÀO
    -- Lớp 1: Kiểm tra sự tồn tại của bảng và cột để tránh lỗi biên dịch (Compile Error)
    -- Lớp 2: Kiểm tra xem ThongTuApp có thuộc danh sách '88' hoặc '152' không
    SET @CheckSQL = N'
        SET @Result = 0;
        
        IF OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') IS NOT NULL
           AND EXISTS (SELECT 1 FROM [' + @DBName + N'].sys.columns WHERE object_id = OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') AND name = ''ThongTuApp'')
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.chamcong'') IS NOT NULL
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.users'') IS NOT NULL
        BEGIN
            -- Thực thi SQL động cấp 2: Dùng IN để lọc cùng lúc nhiều thông tư
            DECLARE @InnerSQL NVARCHAR(MAX) = N''IF EXISTS (SELECT 1 FROM [' + @DBName + N'].dbo.CustomsInfor WHERE ThongTuApp IN (''''88'''', ''''152'''')) SET @Match = 1;'';
            EXEC sp_executesql @InnerSQL, N''@Match INT OUTPUT'', @Match = @Result OUTPUT;
        END';

    -- Thực thi lệnh kiểm tra và gán kết quả vào @IsTargetDB
    EXEC sp_executesql @CheckSQL, N'@Result INT OUTPUT', @Result = @IsTargetDB OUTPUT;

    -- 3. NẾU DATABASE NÀY HỢP LỆ (Thỏa mãn 1 trong 2 thông tư) -> Tiến hành nối chuỗi UNION ALL
    IF @IsTargetDB = 1
    BEGIN
        SET @SQL = @SQL +
            N'SELECT ''' + @DBName + N''' AS TenDatabase, ' +
            -- Thêm cột hiển thị thông tư thực tế của database để bạn dễ quản lý, theo dõi
            N'       (SELECT TOP 1 ThongTuApp FROM [' + @DBName + N'].dbo.CustomsInfor) AS ThongTuApDung, ' +
            N'       c.tenmaytinh, c.ngayTao, c.tram, c.userDangnhap, u.ten ' +
            N'FROM ( ' +
            N'    SELECT ComputerName AS tenmaytinh, CreateDate AS ngayTao, tram, [user] AS userDangnhap, ' +
            N'           ROW_NUMBER() OVER(PARTITION BY tram ORDER BY CreateDate DESC) AS stt ' +
            N'    FROM [' + @DBName + N'].dbo.chamcong ' +
            N'    WHERE CreateDate >= ''20250101'' ' + 
            N') c ' +
            N'INNER JOIN [' + @DBName + N'].dbo.[users] u ON c.userDangnhap = u.[user] ' +
            N'WHERE c.stt = 1 ' +
            N'UNION ALL ' + CHAR(13) + CHAR(10);
    END

    FETCH NEXT FROM db_cursor INTO @DBName;
END

CLOSE db_cursor;
DEALLOCATE db_cursor;

-- 4. Chạy câu lệnh tổng hợp
IF LEN(@SQL) > 0
BEGIN
    -- Cắt bỏ chữ 'UNION ALL ' và ký tự xuống dòng ở cuối chuỗi
    SET @SQL = LEFT(@SQL, LEN(@SQL) - 12); 
    
    -- Sắp xếp theo Tên Database, Thông tư áp dụng và Tên trạm
    SET @SQL = @SQL + N' ORDER BY TenDatabase, ThongTuApDung, tram;';

    -- Bạn có thể mở comment dòng PRINT dưới đây để kiểm tra cấu trúc câu lệnh hoàn chỉnh trước khi thực thi
    -- PRINT CAST(@SQL AS NTEXT); 
    
    EXEC sp_executesql @SQL;
END
ELSE
BEGIN
    PRINT N'Không tìm thấy cơ sở dữ liệu nào áp dụng Thông tư 88 hoặc 152.';
END
```

2. Danh sách các trạm thỏa mãn điều kiện:

```sql
DECLARE @DBName NVARCHAR(128);
DECLARE @SQL NVARCHAR(MAX) = N'';
DECLARE @CheckSQL NVARCHAR(MAX);
DECLARE @IsTargetDB INT;

-- 1. Quét các Database
DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.databases
WHERE database_id > 4 AND state = 0;

OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @DBName;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @IsTargetDB = 0;

    -- 2. Kiểm tra điều kiện Database (Chứa bảng/cột và ThongTuApp = 88 hoặc 152)
    SET @CheckSQL = N'
        SET @Result = 0;
        
        IF OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') IS NOT NULL
           AND EXISTS (SELECT 1 FROM [' + @DBName + N'].sys.columns WHERE object_id = OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') AND name = ''ThongTuApp'')
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.chamcong'') IS NOT NULL
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.users'') IS NOT NULL
        BEGIN
            DECLARE @InnerSQL NVARCHAR(MAX) = N''IF EXISTS (SELECT 1 FROM [' + @DBName + N'].dbo.CustomsInfor WHERE ThongTuApp IN (''''88'''', ''''152'''')) SET @Match = 1;'';
            EXEC sp_executesql @InnerSQL, N''@Match INT OUTPUT'', @Match = @Result OUTPUT;
        END';

    EXEC sp_executesql @CheckSQL, N'@Result INT OUTPUT', @Result = @IsTargetDB OUTPUT;

    -- 3. Ghép chuỗi truy vấn: Đã bỏ các cột Tên Database và Thông tư
    IF @IsTargetDB = 1
    BEGIN
        SET @SQL = @SQL +
            N'SELECT c.tenmaytinh, c.ngayTao, c.tram, c.userDangnhap, u.ten ' +
            N'FROM ( ' +
            N'    SELECT ComputerName AS tenmaytinh, CreateDate AS ngayTao, tram, [user] AS userDangnhap, ' +
            N'           ROW_NUMBER() OVER(PARTITION BY tram ORDER BY CreateDate DESC) AS stt ' +
            N'    FROM [' + @DBName + N'].dbo.chamcong ' +
            N'    WHERE CreateDate >= ''20250101'' ' + 
            N') c ' +
            N'INNER JOIN [' + @DBName + N'].dbo.[users] u ON c.userDangnhap = u.[user] ' +
            N'WHERE c.stt = 1 ' +
            N'UNION ALL ' + CHAR(13) + CHAR(10);
    END

    FETCH NEXT FROM db_cursor INTO @DBName;
END

CLOSE db_cursor;
DEALLOCATE db_cursor;

-- 4. Bọc toàn bộ chuỗi SQL vào một CTE lớn và lọc lại lần cuối trên toàn hệ thống
IF LEN(@SQL) > 0
BEGIN
    -- Cắt bỏ chữ 'UNION ALL ' ở cuối chuỗi
    SET @SQL = LEFT(@SQL, LEN(@SQL) - 12); 
    
    -- Khởi tạo câu lệnh Final bằng cách bọc @SQL vào bên trong
    DECLARE @FinalSQL NVARCHAR(MAX) = N'
WITH AllDB_Data AS (
    ' + @SQL + N'
),
Global_Rank AS (
    SELECT tenmaytinh, ngayTao, tram, userDangnhap, ten,
           ROW_NUMBER() OVER(PARTITION BY tram ORDER BY ngayTao DESC) AS global_stt
    FROM AllDB_Data
)
SELECT tenmaytinh, ngayTao, tram, userDangnhap, ten
FROM Global_Rank
WHERE global_stt = 1
ORDER BY tram,ngayTao DESC;'; -- Ưu tiên hiển thị các đăng nhập mới nhất lên trên cùng

    -- PRINT CAST(@FinalSQL AS NTEXT); -- Kiểm tra chuỗi sinh ra nếu cần
    EXEC sp_executesql @FinalSQL;
END
ELSE
BEGIN
    PRINT N'Không tìm thấy cơ sở dữ liệu nào áp dụng Thông tư 88 hoặc 152.';
END
```

3. Tìm kiếm tên máy tính hoặc số trạm xuất hiện trên những database nào

```sql
-- =====================================================================
-- 0. CẤU HÌNH TÌM KIẾM (TÙY CHỈNH TẠI ĐÂY)
-- =====================================================================
-- Nhập tên máy tính hoặc số trạm cần tìm. 
-- Ví dụ: N'TRAM-01' hoặc N'PC-KETOAN'. 
-- Để chuỗi rỗng N'' nếu muốn lấy lên toàn bộ danh sách.
--DECLARE @SearchKeyword NVARCHAR(100) = N'ADMIN01'; --Tìm tên máy tính
DECLARE @SearchKeyword NVARCHAR(100) = N'11';  --- Tìm theo số trạm

-- =====================================================================

DECLARE @DBName NVARCHAR(128);
DECLARE @SQL NVARCHAR(MAX) = N'';
DECLARE @CheckSQL NVARCHAR(MAX);
DECLARE @IsTargetDB INT;

-- 1. Quét các Database đang Online và không phải DB hệ thống
DECLARE db_cursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.databases
WHERE database_id > 4 AND state = 0;

OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @DBName;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @IsTargetDB = 0;

    -- 2. KIỂM TRA CẤU TRÚC VÀ ĐIỀU KIỆN THÔNG TƯ ĐẦU VÀO
    SET @CheckSQL = N'
        SET @Result = 0;
        
        IF OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') IS NOT NULL
           AND EXISTS (SELECT 1 FROM [' + @DBName + N'].sys.columns WHERE object_id = OBJECT_ID(N''[' + @DBName + N'].dbo.CustomsInfor'') AND name = ''ThongTuApp'')
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.chamcong'') IS NOT NULL
           AND OBJECT_ID(N''[' + @DBName + N'].dbo.users'') IS NOT NULL
        BEGIN
            -- Lọc các database đang áp dụng TT 88 hoặc 152
            DECLARE @InnerSQL NVARCHAR(MAX) = N''IF EXISTS (SELECT 1 FROM [' + @DBName + N'].dbo.CustomsInfor WHERE ThongTuApp IN (''''133'''', ''''200'''', ''''99'''')) SET @Match = 1;'';
            EXEC sp_executesql @InnerSQL, N''@Match INT OUTPUT'', @Match = @Result OUTPUT;
        END';

    EXEC sp_executesql @CheckSQL, N'@Result INT OUTPUT', @Result = @IsTargetDB OUTPUT;

    -- 3. GHI NHẬN DATABASE HỢP LỆ VÀ NỐI CHUỖI
    IF @IsTargetDB = 1
    BEGIN
        SET @SQL = @SQL +
            N'SELECT ''' + @DBName + N''' AS TenDatabase, ' +
            N'       (SELECT TOP 1 ThongTuApp FROM [' + @DBName + N'].dbo.CustomsInfor) AS ThongTuApDung, ' +
            N'       c.tenmaytinh, c.ngayTao, c.tram, c.userDangnhap, u.ten ' +
            N'FROM ( ' +
            N'    SELECT ComputerName AS tenmaytinh, CreateDate AS ngayTao, tram, [user] AS userDangnhap, ' +
            N'           ROW_NUMBER() OVER(PARTITION BY tram ORDER BY CreateDate DESC) AS stt ' +
            N'    FROM [' + @DBName + N'].dbo.chamcong ' +
            N'    WHERE CreateDate >= ''20200101'' ' + 
            N') c ' +
            N'INNER JOIN [' + @DBName + N'].dbo.[users] u ON c.userDangnhap = u.[user] ' +
            N'WHERE c.stt = 1 ' +
            -- Áp dụng bộ lọc tìm kiếm tương đối (LIKE) đối với tên máy tính HOẶC tên trạm
            N'  AND (@SearchKeyword = N'''' ' + 
            N'       OR c.tenmaytinh LIKE N''%'' + @SearchKeyword + N''%'' ' + 
            N'       OR c.tram LIKE N''%'' + @SearchKeyword + N''%'') ' +
            N'UNION ALL ' + CHAR(13) + CHAR(10);
    END

    FETCH NEXT FROM db_cursor INTO @DBName;
END

CLOSE db_cursor;
DEALLOCATE db_cursor;

-- 4. THỰC THI TRUY VẤN
IF LEN(@SQL) > 0
BEGIN
    -- Cắt bỏ chữ 'UNION ALL ' ở cuối chuỗi
    SET @SQL = LEFT(@SQL, LEN(@SQL) - 12); 
    
    -- Sắp xếp theo Tên Database, thời gian đăng nhập mới nhất và Tên trạm
    SET @SQL = @SQL + N' ORDER BY TenDatabase, c.ngayTao DESC, tram;';

    -- Thực thi SQL Động và truyền biến @SearchKeyword vào ngữ cảnh thực thi
    EXEC sp_executesql @SQL, 
                       N'@SearchKeyword NVARCHAR(100)', 
                       @SearchKeyword = @SearchKeyword;
END
ELSE
BEGIN
    PRINT N'Không tìm thấy cơ sở dữ liệu nào áp dụng Thông tư 133 hoặc 200,99.';
END
```

## RESET ID TRẠM BẢN DEV Cũ

```sql
--ÁP DỤNG CHO BẢN DEV CŨ KHÔNG CÓ RESET NHIỀU DATABASE
-- CHÚ Ý CHỈ ÁP DỤNG CHO MÁY CHỈ CÓ RIÊNG BẢN KẾ TOÁN DEV CŨ, 
--Nếu có cả DEV mới và 88 thì không dùng được

--1. Kiểm tra danh sách các database
SELECT name,database_id FROM sys.databases
WHERE database_id>6 -- Lấy ra Vị trí của database bắt đầu dùng DTECH, lại đi các db của hệ thống (master, model,msdb,tempdb,report...)

--2. Dựa theo vị trí để điền lại vào @id_data

DECLARE @vitri int -- Vị trí những mã trống theo thứ tự tăng dần
DECLARE @tendb NVARCHAR(300)
DECLARE @id_tram INT
DECLARE @id_data INT
DECLARE @sql_cmm NVARCHAR(MAX)
Declare @err_state int =0;  -- Trạng thái lỗi trả về

SET @id_tram =1  -- ID trạm cần reset
SET @id_data = 7  -- Vị trí id của database bắt đầu dùng DTECH

--Tạo 1 bảng tạm quét danh sách các database
declare bangdsdata cursor for
select ROW_NUMBER() OVER(ORDER BY name ASC) AS vitri,name from sys.databases where database_id>=@id_data
open bangdsdata 
Fetch next from bangdsdata
	into @vitri,@tendb
While @@FETCH_STATUS=0
BEGIN
	print '--vitri: '+ CONVERT(NVARCHAR,@vitri)
	--print 'Ma: '+@kytu+FORMAT(@maxso+@vitri,'00000')
	--PRINT 'Update: '+'Update ['+@tendb+'].dbo.Tram set trangthai=0,modidate=getdate() where tram='+CONVERT(NVARCHAR,@id_tram)
	SET @sql_cmm='Update ['+@tendb+'].dbo.Tram set trangthai=0,modidate=getdate() where tram='+CONVERT(NVARCHAR,@id_tram)
	BEGIN TRY
		PRINT @sql_cmm
		EXEC(@sql_cmm)
	END TRY
	BEGIN CATCH
		SELECT @err_state = ERROR_STATE()
	END CATCH;
	Fetch next from bangdsdata
	into @vitri,@tendb
END
CLOSE bangdsdata
DEALLOCATE bangdsdata


```

TRƯỜNG HỢP CÓ CẢ DATABASE BẢN DEV CŨ VÀ DEV MỚI thì cần lọc theo tên thư mục cài đặt để reset

```sql
--0. Kiểm tra danh sách các database

SELECT T1.name,T1.database_id,T2.physical_name
FROM sys.databases T1
join sys.master_files T2
on T1.database_id = T2.database_id
WHERE T2.type_desc = 'ROWS'
and Charindex(N'\DtechService\',T2.physical_name)>0
---------------------------------------------------------
--1. Khai báo các tham số
DECLARE @vitri INT -- Vị trí thứ tự tăng dần
DECLARE @tendb NVARCHAR(300)
DECLARE @id_tram INT
DECLARE @sql_cmm NVARCHAR(MAX)
DECLARE @err_state INT = 0; -- Trạng thái lỗi trả về

SET @id_tram = 4 -- ID trạm cần reset

-- 2. Sửa Cursor để duyệt qua danh sách database thỏa mãn điều kiện đường dẫn vật lý
DECLARE bangdsdata CURSOR FOR
SELECT 
    ROW_NUMBER() OVER(ORDER BY T1.name ASC) AS vitri,
    T1.name 
FROM sys.databases T1
JOIN sys.master_files T2 ON T1.database_id = T2.database_id
WHERE T2.type_desc = 'ROWS' -- Chỉ lấy file dữ liệu, tránh lấy file LOG
AND CHARINDEX(N'\DtechService\', T2.physical_name) > 0

OPEN bangdsdata
FETCH NEXT FROM bangdsdata INTO @vitri, @tendb

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT N'-- Đang xử lý vị trí: ' + CONVERT(NVARCHAR, @vitri) + ' | Database: ' + @tendb
    
    -- Xây dựng câu lệnh Update động
    SET @sql_cmm = 'UPDATE [' + @tendb + '].dbo.Tram SET trangthai = 0, modidate = GETDATE() WHERE tram = ' + CONVERT(NVARCHAR, @id_tram)
    
    BEGIN TRY
        PRINT N'Lệnh thực thi: ' + @sql_cmm
        --EXEC(@sql_cmm)
    END TRY
    BEGIN CATCH
        PRINT N'Lỗi tại Database: ' + @tendb
        SELECT @err_state = ERROR_STATE()
    END CATCH;

    FETCH NEXT FROM bangdsdata INTO @vitri, @tendb
END

CLOSE bangdsdata
DEALLOCATE bangdsdata

```

## CẬP NHẬT ĐƯỜNG DẪN BACKUP TẤT CẢ CÁC DATABASE BẢN DỊCH VỤ

```sql
--ÁP DỤNG CHO BẢN DEV CŨ KHÔNG CÓ RESET NHIỀU DATABASE
-- CHÚ Ý CHỈ ÁP DỤNG CHO MÁY CHỈ CÓ RIÊNG BẢN KẾ TOÁN DEV CŨ, 

--1. Kiểm tra danh sách các database
SELECT name,database_id FROM sys.databases
WHERE database_id>4 -- Lấy ra Vị trí của database bắt đầu dùng DTECH, lại đi các db của hệ thống (master, model,msdb,tempdb,report...)

--2. Dựa theo vị trí để điền lại vào @id_data

DECLARE @vitri int -- Vị trí những mã trống theo thứ tự tăng dần
DECLARE @tendb NVARCHAR(300)
DECLARE @id_tram INT
DECLARE @id_data INT
DECLARE @sql_cmm NVARCHAR(MAX)
Declare @err_state int =0;  -- Trạng thái lỗi trả về
DECLARE @path NVARCHAR(MAX)
DECLARE @value NVARCHAR(MAX)

SET @path=N'E:\DTECH\DtechService\Backup'  -- Đường dẫn backup SQL
SET @value =N'SelectedPath'

--SET @id_tram =1  -- ID trạm cần reset
SET @id_data = 5  -- Vị trí id của database bắt đầu dùng DTECH

--Tạo 1 bảng tạm quét danh sách các database
declare bangdsdata cursor for
select ROW_NUMBER() OVER(ORDER BY name ASC) AS vitri,name from sys.databases where database_id>=@id_data
open bangdsdata 
Fetch next from bangdsdata
	into @vitri,@tendb
While @@FETCH_STATUS=0
BEGIN
	print '--vitri: '+ CONVERT(NVARCHAR,@vitri)
	--print 'Ma: '+@kytu+FORMAT(@maxso+@vitri,'00000')
	--PRINT 'Update: '+'Update ['+@tendb+'].dbo.Tram set trangthai=0,modidate=getdate() where tram='+CONVERT(NVARCHAR,@id_tram)
	--SET @sql_cmm='Update ['+@tendb+'].dbo.Tram set trangthai=0,modidate=getdate() where tram='+CONVERT(NVARCHAR,@id_tram)
	SET @sql_cmm='Update ['+@tendb+'].dbo.Dregistry set value='''+@path+''' where name='''+@value+''''
	BEGIN TRY
		PRINT @sql_cmm
		EXEC(@sql_cmm)
	END TRY
	BEGIN CATCH
		SELECT @err_state = ERROR_STATE()
	END CATCH;
	Fetch next from bangdsdata
	into @vitri,@tendb
END
CLOSE bangdsdata
DEALLOCATE bangdsdata


```


# BẢN SERVICE


# KHÔNG NHẬN ĐỦ DATA KHI ĐĂNG NHẬP

## Nguyên nhân

* Có thể có data bị lỗi
* Có thể dịch vụ Sql lỗi&#x20;
* Có thể phiên bản cũ, khi mở khóa, khóa không ăn hết vào data. dẫn đến các data chưa nhận khóa sẽ không hiển thị

## Hướng dẫn xử lý

* Kiểm tra tệp ghi log của phần mềm để xem liệu có phải do lỗi data
* Thử Restart lại dịch vụ Sql
* Nếu không được thì phải check data lỗi: dbcc check database
*


# KHÔNG TẠO ĐƯỢC DATA MỚI

## NGUYÊN NHÂN, BIỂU HIỆN

* Khi tạo data mới, chương trình báo "Khởi tạo data thất bại"
* Nếu quay lại giao diện tạo data thì chương trình lại báo là đã tồn tại data, nghĩa là:
  * Data đã được khởi tạo
  * Data chưa restore được từ file backup khởi tạo mặc định trong thư mục

## HƯỚNG DẪN XỬ LÝ

* Kiểm tra xem dung lượng ổ cứng còn không
* Kiểm tra hoặc copy lại file backup khởi tạo mặc định&#x20;
  * DTECH\ ....\DCONFIG \SEP \DT \ BACKUPTT88.DTE
* Nếu vẫn không được thì khả năng liên quan phân vùng ổ cứng


# KIỂM TRA KHÓA BẢN DỊCH VỤ

```sql
// Đoạn lệnh kiểm tra

select Name from DRegistry2 where Name = 'FileContentLIC'

//Đoạn copy khóa từ database gốc sang database khác
declare @key varbinary(max)
select @key=Value from [HoangKha_PhuQuoc].dbo.DRegistry2 where Name = 'FileContentLIC'
update DRegistry2
set value=@key
where Name = 'FileContentLIC'
```


# LỖI ĐĂNG NHẬP TRÊN MÁY CHỦ

## 1. Đăng nhập máy chủ không thấy phản hồi

* Kiểm tra Task Manager và tắt hết exe Dtech đang chạy ngầm
* Xoá file trạm và kết nối lại để kiểm tra xem có vào được không
* Vào QL dữ liệu, nếu có data nghĩa là SQL đã chạy được
* Vậy khả năng do các file dll của phần mềm lỗi

## 2. Sau khi nhập user và mật khẩu, thì bật lỗi 11cannot per....

<figure><img src="/files/yGFoDKt5JIjeH0pRBt6d" alt=""><figcaption><p>Báo lỗi sau khi đã nhập user và mật khẩu. </p></figcaption></figure>

* Lỗi khả năng liên quan đến mở giao diện nhập liệu bị lỗi
* Vào thư mục Dtech/Dtech..../Temp&#x20;
  * Tìm tới tệp "LastTimeUpdateSource"+ dataName
  * Sửa thời gian trong file về trước ngày hôm nay
  * Rồi chạy file checkupdate để phần mềm cập nhật lại các file code của phần mềm&#x20;
  * Nếu không được, thì thử copy exe mới nhất sang (lưu ý thử file 64 hoặc 86 tuỳ máy)

## 3. Đăng nhập phần mềm nhưng không hiện danh sách user để chọn

* Nguyên nhân:&#x20;
  * Lỗi có thể gặp cả ở máy trạm hoặc máy chủ
  * Hỏng file trong thư mục&#x20;

    <figure><img src="/files/9gMR6kDuWJjNUrwPjp4x" alt="" width="563"><figcaption><p>Đăng nhập không có user để chọn</p></figcaption></figure>
* Giải pháp:
  * Đăng nhập vào thư mục: Dtech/Dconfig/ Sep/DT/ tìm và xoá thư mục có tên database đăng nhập bị lỗi ( bản dịch vụ sẽ có nhiều thư mục database)&#x20;

    <figure><img src="/files/dQus82n7AByjzjywXXov" alt="" width="563"><figcaption><p>Xoá thư mục có tên data tương ứng</p></figcaption></figure>

## 4. Font "Segoe UI: does not support style Regular

<figure><img src="/files/uolU1SAJ2rMA6VO2igcu" alt=""><figcaption></figcaption></figure>

**Nguyên nhân:**

* Font chữ trong hệ thống đăng ký với Registry bị thay đổi sang 1 tên có dấu \_0 dẫn tới hệ thống nhận không đúng
* Đường dẫn Font hệ thống:

```
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
```

<figure><img src="/files/fzdDFpMLeKvNfyFnCsCL" alt=""><figcaption></figcaption></figure>

**Khắc phục:**

* Mở registry lên theo đường dẫn font hệ thống sau, nhấn Ctrl+F để tìm font Segoe UI kia, click chuột phải vào chọn Modify, bổ sung đúng đường dẫn font và tên của font bỏ đi \_0

<figure><img src="/files/2obLy0cvwpxrxR5uSp9K" alt=""><figcaption></figcaption></figure>

* Sửa xong đóng registry lại khởi động lại máy tính là được.


# LỖI ĐĂNG NHẬP MÁY TRẠM

#### 1. Trạm Online

* Kiểm tra kết nối vpn nếu là trạm online
* Kiểm tra tham số kết nối: Nếu đang kết nối theo ip vpn thì thử đổi sang tên của máy chủ
* Cấu hình trạm lỗi hoặc khác biệt so với cấu hình của máy chủ

#### 2. Trạm Lan

* Kiểm tra kết nối qua cổng mặc định 1433
* Mở cổng Sql trong tường lửa: 2866 và kết nối thử lại qua 2866
* Trường hợp không kiểm tra được thì có thể cài RadminVPN&#x20;

### 3. Chạy exe báo lỗi side-by-side

* Chạy exe báo lỗi
* copy exe không tác dụng
* copy tệp "dtech.exe.config" từ máy khác hoặc bản chuẩn&#x20;

  <figure><img src="/files/p3cUDRve82S7iyWPiRbH" alt=""><figcaption><p>side-by-side</p></figcaption></figure>


# CÀI ĐẶT SQL LỖI

#### 1. Khởi tạo data thất bại

* Kiểm tra thư mục DataSql xem có tồn tại data vừa khởi tạo không. Vì nguyên tắc của phần mềm khi tạo data gồm 2 bước
  * B1: Tạo data
  * B2: Restore file backup vào data vừa khởi tạo
* Nếu data được khởi tạo: Nghĩa là bước 1 đã pass, bước 2 bị lỗi
  * Kiểm tra file backup ở đường dẫn sau có tồn tại không:&#x20;
    * ....\DCONFIG\SEP\DT
    * Kiểm tra file backup có tên: BackupTT88, BackupTT200, BackupTT133
    * Nếu không có, thì copy từ nơi khác sang. Sau đó, restore vào data đã được khởi tạo
  * Nếu có file, thì khả năng lúc restore sẽ lỗi
    * Phân quyền data full control
    * Phân quyền thư mục full control
    * Kiểm tra dung lượng ổ cứng làm việc
  * Không được thì kiểm tra lại log xem có ghi được nguyên nhân không
* Nếu data không được khởi tạo thì:
  * Thư mục bị đặt readonly
  * Thư mục không full quyền everyone
  * Ổ cứng bị đặt chế độ nén: Bỏ check ở mục 1&#x20;

    <figure><img src="/files/bDAZZRtQPzI8JqDfgHpy" alt="" width="270"><figcaption></figcaption></figure>


# XỬ LÝ LIÊN QUAN KEY HỆ THỐNG

## 1. KEY MẸ CON BỊ TRÙNG

```sql
// KEYMECON bị trùng, dẫn đến báo cáo vật tư chi tiết lấy đơn giá bán bị sai
// xử lý, gán vattu vào keymecon. có thể sai nếu vattu theo lenhsanxuat
// thì vattu xuất hiện 2 dòng
Update  DuLieuKeToan set KeyMeCon = VatTu
from
(
select a,Max(KeyCT) as KeyCt,count(*) as dongtrung
from
(
select ltrim(rtrim(str(keyct)))+'_'+ ltrim(rtrim(str(KeyMeCon))) as a,KeyCT from DulieuKeToan where psco=0 group by KeyCT,KeyMeCon,VatTu
)
temp
group by a having count(*)>1
)a 
where DulieuKeToan.KeyCt = a.KeyCT and DulieuKeToan.VatTu>0
```

## 2.UPDATE LẠI KEYCT

```sql
/** Update lại hết Keyct về =0 hết rồi mới update lại.**/
Update DuLieuKeToan
set KeyCT=0 
Where NghiepVu=301 -- Tuỳ điều kiện where cần đánh lại
```

```sql
/**Update lại Keyct nếu Keyct=0**/
update DuLieuKeToan set KeyDong = Case When KeyDong=0 then identityKey else KeyDong end
where KeyDong =0
update DuLieuKeToan set KeyCT = case when KeyCT=0 then a.KeyCTnew else KeyCT end
from
(
select ChungTu,Ngay,NghiepVu,MIN(identityKey) as KeyCTnew from DuLieuKeToan where KeyCT = 0 group by ChungTu,Ngay,NghiepVu
)a
where (KeyCT =0 and DuLieuKeToan.ChungTu = a.ChungTu and DuLieuKeToan.Ngay = a.Ngay and DuLieuKeToan.NghiepVu = a.NghiepVu)
```

## 3. KIỂM TRA KEYCT

```sql
---- KIỂM TRA TRÙNG KEYCT
select keyct from
(
select keyct,ChungTu from DuLieuKeToan
group by KeyCT,ChungTu
)a  group by KeyCT
having COUNT (*)>1

---- UPDATE KEYCT TRÙNG (CHUNGTU SỐ 2 TRỜ ĐI) VỀ = 0
update DuLieuKeToan set KeyCT=0 where KeyCT in (

select keyct from
(
select keyct,ngay,NghiepVu from DuLieuKeToan
group by keyct,ngay,NghiepVu,chungtu
)a group by KeyCT
having COUNT(*)>1)

----- ĐÁNH LẠI KEYCT
update DuLieuKeToan set KeyDong = Case When KeyDong=0 then identityKey else KeyDong end
where KeyDong =0
update DuLieuKeToan set KeyCT = case when KeyCT=0 then a.KeyCTnew else KeyCT end
from
(
select ChungTu,Ngay,NghiepVu,MIN(identityKey) as KeyCTnew from DuLieuKeToan where KeyCT = 0 group by ChungTu,Ngay,NghiepVu
)a
where (KeyCT =0 and DuLieuKeToan.ChungTu = a.ChungTu and DuLieuKeToan.Ngay = a.Ngay and DuLieuKeToan.NghiepVu = a.NghiepVu)
```

## 4. XỬ LÝ SỐ CHỨNG TỪ PHIẾU XUẤT BÁN HÀNG DO NĂM 23 CHƯA CÓ

```
-- 301 NGHIỆP VỤ BÁN HÀNG - 201 PHIẾU XUẤT - PSCO=11 BT THÊM XUẤT KHO
update dulieuketoan 
set nghiepvukemtheo=201
where nghiepvu=301 and psco=11 and nghiepvukemtheo=0
```


# Lỗi đọc thông tin phần cứng (Alias not found)

Máy tính KHÔNG thay đổi phần cứng về bo mạch chủ ( Mainboard) hoặc ổ cứng (HDD) nhưng khi mở khoá sẽ báo mở khoá sai. Kiểm tra lấy thông tin phần cứng qua lệnh CMD sẽ báo lỗi Alias not found.

<figure><img src="/files/yeTbeuJlCG7vqmv24vGv" alt=""><figcaption><p>Thông báo lỗi khi kiểm tra bằng lệnh cmd</p></figcaption></figure>

## KIỂM TRA XÁC ĐỊNH LỖI

* Mở CMD lên chạy lệnh sau:
* Lệnh kiểm tra lấy thông số Mainboard:

```
wmic baseboard get product,manufacturer,version,serialnumber
```

* Lệnh kiểm tra lấy thông số ổ cứng HDD:

```
 wmic diskdrive get model,serialNumber,size,mediaType
```

* Kết quả trả về <mark style="color:red;">**Alias not found**</mark> là xác định đúng lỗi không đọc được thông tin phần cứng máy tính

## CÁCH KHẮC PHỤC

Nhấn vào Start hoặc biểu tượng cửa sổ Windows, gõ vào tìm kiếm cmd

<figure><img src="/files/ZxsJFHLp8eEeFYHqHCNO" alt=""><figcaption></figcaption></figure>

1. Để dừng dịch vụ **winmgmt** gõ vào cmd lệnh sau: **net stop winmgmt** và enter để thực hiện

```
net stop winmgmt
```

<figure><img src="/files/PvOeViSaZqxAoyujRa98" alt=""><figcaption></figcaption></figure>

2. Vào đường dẫn sau xoá bỏ hoặc đổi tên thư mục **repository** đi:

```
C:\Windows\System32\wbem\repository
```

<figure><img src="/files/urHsOtzKjhIlbQNldiI9" alt=""><figcaption></figcaption></figure>

3. Quay lại màn hình CMD, copy câu lệnh sau và Click chuột phải paste vào cmd , nhấn Enter để chuyển tới đường dẫn: &#x20;

```
cd C:\Windows\System32\wbem\
```

<figure><img src="/files/kzH4rvszsqvZEukDowHy" alt=""><figcaption></figcaption></figure>

4. Copy lệnh sau và paste vào cửa sổ CMD nhấn Enter để thực hiện tiếp.

```
for /f %s in ('dir /b *.mof') do mofcomp %s
```

<figure><img src="/files/Epe1ypBxATpB7lHiqf9d" alt=""><figcaption></figcaption></figure>

5. Bước trên thực hiện báo Done! Xong tiếp tục copy lệnh sau vào cửa sổ CMD nhấn Enter để thực hiện tiếp.

```
for /f %s in ('dir /b en-us\*.mfl') do mofcomp en-us\%s
```

<figure><img src="/files/bLSixM86XjvHNbxHijRA" alt=""><figcaption></figcaption></figure>

6. Thông báo **MOF file has been successfully parsed !** thì khởi động lại máy tính.
7. Thực hiện lại việc kiểm tra thông tin phần cứng ra thông số Mainboard, HDD thì tiếp reset trạm để vào lại DTECH.


# A severe error occurred on the current command

Lỗi khi select một bảng table trên database trả ra lỗi này hoặc xem báo cáo như Cân đối phát sinh không ra số liệu, tìm kiếm vẫn có.

## TẢI VỀ BẢN VÁ LỖI CỦA MICROSFOT&#x20;

## KB3137745 - Cumulative update package 11 for SQL Server 2012 SP2 - [TẠI ĐÂY](https://support.microsoft.com/en-us/topic/kb3137745-cumulative-update-package-11-for-sql-server-2012-sp2-da013001-f4f1-1ef4-b205-6e491ddf508b) <a href="#page-header" id="page-header"></a>

<figure><img src="/files/nZUlVzHncyjGI07SMk0d" alt=""><figcaption></figcaption></figure>

Download tương ứng version SQL cài đặt , thường là x86.

Cài đặt xong chạy lại SQL select để kiểm tra kết quả.

Tham khảo tại đây:

{% embed url="<https://support.microsoft.com/en-us/topic/kb3139489-fix-a-severe-error-occurred-on-the-current-command-when-a-table-valued-user-defined-function-is-referred-to-by-a-synonym-f17d05df-3012-7558-096d-af04dad17e43>" %}

{% embed url="<https://blog.sqlauthority.com/2017/11/02/sql-server-msg-0-level-11-severe-error-occurred-current-command-results-discarded/>" %}

{% embed url="<https://www.mssqltips.com/sqlservertip/1476/how-to-read-log-file-in-sql-server-using-tsql/>" %}


# GỬI HƯỚNG DẪN CÀI ĐẶT XONG

Những hướng dẫn cần gửi khi cài đặt xong các phiên bản

## KẾ TOÁN DOANH NGHIỆP

```
Anh/Chị sử dụng User Admin, để trống mật khẩu đăng nhập vào dùng nhé.
a) Hướng dẫn sử dụng phần mềm tại website: https://huongdanketoan.dtechvn.com

b) Video hướng dẫn sử dụng của Dtech.NET Enterprise cho doanh nghiệp, công ty
- https://youtu.be/Os5Ivep_3Dk
- https://youtu.be/2ibXDn4HIYY
- https://youtu.be/LJVyPnesLa0
- https://youtu.be/cKQpeMyr6_4
- https://youtu.be/kBC8YpOowzY

Hướng dẫn sử dụng mới 2024 - Có chức năng Lấy hoá đơn từ CQT
Phần 1: https://www.youtube.com/watch?v=7cPbt9o5R8g
Phần 2: https://www.youtube.com/watch?v=JDi9LgLV5_0

Một số hướng dẫn nhanh em gửi thêm ạ:
1. Hướng dẫn tạo mới dữ liệu cho 1 Công ty (áp dụng cho bản Dtech Service)
https://huongdanketoan.dtechvn.com/he-thong/tao-moi-du-lieu
https://www.youtube.com/watch?v=wTABblQ3wd0

2. Hướng dẫn Import dữ liệu từ file Excel vào DTECH
https://huongdanketoan.dtechvn.com/nghiep-vu-phat-sinh/tinh-nang-tien-ich/nhap-khau-import-excel

3. Hướng dẫn kết nối ra HTKK:
https://huongdanketoan.dtechvn.com/ket-noi-htkk/to-khai-thue-gtgt

4. Hướng dẫn kết xuất phiếu in, báo cáo ra Excel:
https://huongdanketoan.dtechvn.com/nghiep-vu-phat-sinh/tinh-nang-tien-ich/xuat-mau-in-ra-excel

5. Hướng dẫn quy đổi quản lý nhiều đơn vị tính của vật tư:
https://huongdanketoan.dtechvn.com/nghiep-vu-phat-sinh/tinh-nang-tien-ich/quan-ly-nhieu-don-vi-tinh

6. Hướng dẫn đổi mật khẩu người dùng:
https://huongdanketoan.dtechvn.com/he-thong/doi-mat-khau

7. Hướng dẫn cấu hình hệ thống:
https://huongdanketoan.dtechvn.com/he-thong/cau-hinh

8. Hướng dẫn sao lưu dữ liệu:
https://huongdanketoan.dtechvn.com/he-thong/sao-luu-du-lieu
```

## KÉO HÓA ĐƠN, KIỂM TRA HÓA ĐƠN

```
Em gửi Anh/Chị hướng dẫn sử dụng chức năng:
1. Kéo hóa đơn Mua vào, bán ra từ trang Hoadondientu.gdt.gov.vn về hạch toán
    https://huongdanketoan.dtechvn.com/tien-ich/lay-hoa-don-tu-co-quan-thue
2. Kiểm tra hóa đơn trên trang Hoadondientu.gdt.gov.vn với dữ liệu trên phần mềm
    https://huongdanketoan.dtechvn.com/tien-ich/kiem-tra-hoa-don
```

## KẾ TOÁN HỘ KINH DOANH

```
Anh/Chị sử dụng user ADMIN, để trống mật khẩu nhấn ĐĂNG NHẬP vào luôn:
1) Website hướng dẫn sử dụng nhanh: https://batdau88.dtechvn.com
2) Hướng dẫn sử dụng chi tiết Dtech88: https://huongdan88.dtechvn.com
3) Video hướng dẫn sử dụng của Dtech88 - Hộ Kinh Doanh
Phần 1: https://www.youtube.com/watch?v=yhFWSXM2ZQY
Phần 2: https://www.youtube.com/watch?v=2EQNRg7uKgQ
Phần 3: https://www.youtube.com/watch?v=I885qwQaqQg
```

```
Hướng dẫn thêm bản Dtech88 dành cho Hộ Kinh Doanh:
1) Tạo mới dữ liệu 01 hộ kinh doanh ( áp dụng cho bản dịch vụ nhiều hộ):
- https://huongdan88.dtechvn.com/he-thong/tao-moi-du-lieu
https://www.youtube.com/watch?v=UIUnzRnaUoc
2) Hướng dẫn import dữ liệu từ Excel vào:
- Nghiệp vụ phát sinh: - https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/quan-ly-du-lieu-nghiep-vu/nhap-khau-import-excel
- Số dư ban đầu: https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/so-du-ban-dau#id-6.-import-nhap-khau-so-du-ton-kho
3) Hướng dẫn quản lý nhiều đơn vị tính của vật tư, hàng hoá, sản phẩm:
- https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/quan-ly-du-lieu-nghiep-vu/quan-ly-nhieu-don-vi-tinh
4) Hướng dẫn Nhập mua hàng:
- Nhập mua hàng có giảm thuế: https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/mot-so-cau-h-i-lien-quan/nhap-mua-hang-co-giam-thue
- Nhập mua hàng có chiết khấu: https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/mot-so-cau-h-i-lien-quan/nhap-mua-hang-co-chiet-khau
5) Hướng dẫn Bán hàng:
- https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/ban-hang
6) Hoá đơn điện tử tích hợp từ DTECH:
- https://huongdan88.dtechvn.com/hoa-don-dien-t/phat-hanh-hoa-don
7) Hướng dẫn Tính lại Giá vốn:
- https://huongdan88.dtechvn.com/cuoi-ky/gia-von
8) Kết chuyển Thuế ( GTGT - TNCN):
- https://huongdan88.dtechvn.com/cuoi-ky/ket-chuyen-thue
9) Hướng dẫn Tính giá thành cho sản phẩm theo định mức:
- https://huongdan88.dtechvn.com/cuoi-ky/gia-thanh
- Dùng lệnh sản xuất: https://huongdan88.dtechvn.com/lenh-san-xuat
10) Hướng dẫn Sao lưu dữ liệu:
- https://huongdan88.dtechvn.com/he-thong/sao-luu-du-lieu
```

```
Hướng dẫn tiện ích tuỳ chỉnh trên Dtech88:
1) Hướng dẫn đánh lại số chứng từ:
- https://huongdan88.dtechvn.com/tien-ich/danh-so-chung-tu
2) Hướng dẫn in đồng loạt chứng từ:
- https://huongdan88.dtechvn.com/tien-ich/in-dong-loat
3) Hướng dẫn cấu hình tên người ký trên phiếu in - báo cáo:
- https://huongdan88.dtechvn.com/cau-h-i-thuong-gap/1.-cach-thay-doi-ten-nguoi-ky-o-phieu-in-bao-cao
4) Hướng dẫn kết xuất phiếu in - báo cáo ra các định dạng ( PDF, Excel, Word...)
- https://huongdan88.dtechvn.com/cau-h-i-thuong-gap/3.-ket-xuat-chung-tu-ra-cac-tep-dinh-dang-pdf-word-excel
5) Hướng dẫn thay đổi mẫu in trên giao diện nhập liệu:
- https://huongdan88.dtechvn.com/nghiep-vu-phat-sinh/quan-ly-du-lieu-nghiep-vu/doi-mau-in-nghiep-vu
- https://huongdan88.dtechvn.com/cau-h-i-thuong-gap/2.-lua-chon-mau-phieu-in-khac-tren-man-hinh-nhap-lieu
6) Cách thay đổi số ký tự sau hàng thập phân:
- https://huongdan88.dtechvn.com/cau-h-i-thuong-gap/4.-cach-thay-doi-so-ky-tu-sau-hang-thap-phan
```

## BÁN HÀNG

```
Anh/Chị xem hướng dẫn phần mềm bán hàng DtechShop tại đây nhé:
https://huongdanbanhang.dtechvn.com/hoa-don-dien-t/lay-du-lieu-tu-phan-mem-ke-toan-sang-phan-mem-ban-hang
```

## DTax - Tải tờ khai thuế

```
Anh/Chị xem hướng dẫn tải TỜ KHAI THUẾ tại đây nhé:
https://taitokhai.dtechvn.com/huong-dan-su-dung
* Chú ý: Tài khoản đăng nhập trang Thuedientu.gdt.gov.vn, ví dụ: 0109153660-ql
```

## DFin - Tải hóa đơn điện tử

```
Anh/Chị xem hướng dẫn tải HOÁ ĐƠN ĐIỆN TỬ tại đây nhé:
https://taihoadon.dtechvn.com/huong-dan-su-dung
* Chú ý: Tài khoản đăng nhập trang hoadondientu.gdt.gov.vn được Cơ quan Thuế cấp khi làm tờ khai DK01 dùng hoá đơn điện tử được gửi vào email của mình.
```

## SAO LƯU DỮ LIỆU DRIVE

```
Em cài đặt xong Drive rồi nhé, chú ý định kỳ hàng tuần, tháng, quý, Anh/Chị chủ động vào thư mục backup đường dẫn (1) copy các file backup mới nhất (2) theo ngày đưa sang đường dẫn backup Drive (3). File cũ có thể xoá đi cho nhẹ dung lượng Drive
```

## HƯƠNG DẪN HÓA ĐƠN ĐIỆN TỬ

<figure><img src="/files/hcn1ouX30m9GhbZ0kW0K" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/zhNJF3aaaGdmX5ENDkWU" alt=""><figcaption></figcaption></figure>


# PHIÊN BẢN CŨ

## 1. CÀI ĐẶT MÁY CHỦ

* Mở khoá tại máy chủ khách hàng
* Không có bản lập trình
* Chỉnh sửa trực tiếp

## 2. CÀI ĐẶT TRẠM

* Tạo setup để kết nối
* reset trạm cần cài đặt

## 3. LỖI THƯỜNG GẶP

* Đăng nhập trạm bị lỗi: Báo đường dẫn.&#x20;
* vào thư mục temp. xoá toàn bộ các file và thư mục. đăng nhập lại

  <figure><img src="/files/SKrDqwejBhh8W4C0KvFz" alt=""><figcaption></figcaption></figure>


# HÓA ĐƠN ĐIỆN TỬ


# CHỮ KÝ SỐ - CKS

## 1. Gia hạn, đổi cks thì ảnh hưởng tới phần mềm như nào?

* Sau khi gia hạn, đổi cks thì người dùng cần:
  * Đăng nhập web hoá đơn, lập tờ khai dk01, chọn thay đổi thông tin và ký gửi lên thuế
  * sau 24h thì thuế sẽ trả kết quả duyệt hoặc không duyệt
* Sau khi thuế duyệt thì cần xử lý trên dtech
  * cấu hình lại thông tin cks trên phần mềm dtech với một số nhà cung cấp hoá đơn như:
    * Viettel
    * vnpt
  * Các đơn vị khác thì chỉ cần add trên hệ thống web
* dk01 thì đơn vị nào mua hoá đơn điện tử thông qua dtech thì dtech sẽ hỗ trợ làm dk01 thay đổi thông tin. ( thường là Minvoice)

{% embed url="<https://www.youtube.com/watch?v=QCUDtrKGSFg>" %}


# CẤU HÌNH KẾT NỐI

## Ý nghĩa chức năng

Khai báo tham số kết nối giữa phần mềm Dtech và các phần mềm hóa đơn điện tử

## Hướng dẫn thực hiện

### 1. Hóa đơn M-invoice

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Version***: Chọn V2. V1 thường là khách hàng cũ, và thường sẽ yêu cầu chuyển lên v2
* ***Url***: Phần mềm tự động. Chỉ cần điền Mã số thuế thì phần mềm tự động tính URL
* ***User***: Thông tin đăng nhập web M-invoice. Thường là mã số thuế. ***Khách hàng cung cấp***
* ***Pass***: Mật khẩu đăng nhập web M-invoice. ***Khách hàng cung cấp***

### 2. Hóa đơn eHoadon - Bkav

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Token***: Liên hệ với kỹ thuật bkav (nhóm kết nối với bkav) để lấy thông tin. **Gửi MST cho bkav**
* ***Guid***: Liên hệ với kỹ thuật bkav (nhóm kết nối với bkav) để lấy thông tin. **Gửi MST cho bkav**

### 3. Hóa đơn Viettel

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Version***: Tích chọn v1 hoặc v2. Sau 01/11/2021 thì là V2
* ***Url***: Đường dẫn phần mềm đã mặc định
* ***User***: Thông tin đăng nhập web viettel. Thường là mã số thuế. ***Khách hàng cung cấp***
* ***Pass***: Mật khẩu đăng nhập web viettel. ***Khách hàng cung cấp***
* ***Mẫu hóa đơn***: Đăng nhập vào web viettel để tìm. Thường là 1/001 và 2/001
* ***Ký hiệu hóa đơn***: Đăng nhập vào web viettel để tìm. Thông tin không đổi theo các năm. Ví dụ C23TAB. Sang năm đổi thành C24TAB nhưng với viettel thì không thay đổi. Do đó, cần vào web viettel để xem seri lúc đăng ký lần đầu là gì.

### 4. Hóa đơn VNPT

*Khách hàng báo với VNPT **xin thông tin kết nối** theo format phía dưới*

<figure><img src="/files/EffawgtLzDbu0ifEBFRJ" alt=""><figcaption><p>Thông tin cấu hình kết nối vnpt</p></figcaption></figure>

* ***Mã số thuế***: Mã số thuế của đơn vị
* **Url**: Lấy tại dòng: ***Portal***. Là thông tin <https://8043491124-tt78cadmin.vnpt-invoice.com.vn>
* ***User***: Lấy tại dòng: ***username/password service**.* Là thông tin **thaha**
* ***Pass***: Lấy tại dòng: ***username/password service**.* Là thông tin **Vnpt**
* ***AcName***: Lấy tại dòng: ***Account/ACPass admin***. Là thông tin **8044**
* ***AcPass***: Lấy tại dòng: ***Account/ACPass admin***. Là thông tin **Einv**
* ***Mẫu hóa đơn***: Lấy tại dòng: ***Pattern***. Là thông tin 2/001

### 5. Hóa đơn Nacencom

*Khách hàng hỏi nacencom thông tin kết nối*. Nacencom sẽ gửi email cho khách hàng

* ***Url***: Thông tin mặc định. <http://apitichhop78ca2.nacencomm.vn>
* ***Mã số thuế***: Mã số thuế của đơn vị
* ***User***: *Nacencom sẽ gửi email cho khách hàng*. Nếu không có thì liên hệ nhóm zalo kết nối
* ***Pass***: *Nacencom sẽ gửi email cho khách hàng*. Nếu không có thì liên hệ nhóm zalo kết nối

### 6. Hóa đơn EasyInvoice - SoftDream

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***User***: Thông tin đăng nhập web EasyInvoice. Thường là mã số thuế. ***Khách hàng cung cấp***
* ***Pass***: Mật khẩu đăng nhập web EasyInvoice. ***Khách hàng cung cấp***
* ***Url***: Đường dẫn trang web đăng nhập hóa đơn điện tử. ***Khách hàng cung cấp***

### 7. Hóa đơn Mobifone

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin đăng nhập web Mobifone
* ***User***: Thông tin đăng nhập web Mobifone
* ***Pass***: Thông tin đăng nhập web Mobifone
* ***cer\_serial***: Là serial của chữ ký số HSM. Mobifone không có ký token. Nếu ký HSM thì báo Mobifone cung cấp thông tin

### 8. Hóa đơn FPT

*Không cần hỏi khách hàng*. Trường hợp không đúng thì hỏi lại kỹ thuật FPT

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin mặc định <https://api.einvoice.fpt.com.vn>
* ***User***: Thông tin mặc định: "***masothue.api***". Nếu không được thì hỏi lại kỹ thuật nhóm kết nối
* ***Pass***: Thông tin mặc định: "***admin\@123456***". Nếu không được thì hỏi lại kỹ thuật nhóm kết nối

### 9. Hóa đơn CMC

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin đăng nhập web CMC
* ***User***: Thông tin đăng nhập web CMC
* ***Pass***: Thông tin đăng nhập web CMC

### 10. Hóa đơn CYBERLOTUS

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin mặc định. <http://ws.cyberbill.vn>
* ***User***: Thông tin đăng nhập web Cyberlotus
* ***Pass***: Thông tin đăng nhập web Cyberlotus

### 11. Hóa đơn WinInvoice

Chưa có thông tin.&#x20;

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Hỏi lại Thắng
* ***User***: Hỏi lại Thắng
* ***Pass***: Hỏi lại Thắng

### 12. Hóa đơn Vin-Hoadon (Visnam)

*Thông tin mặc định. Nên **không cần phải hỏi** khách hàng hoặc bên hóa đơn*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin mặc định: <https://ws.vin-hoadon.com>
* ***User***: Thông tin mặc định: **ktt**
* ***Pass***: Thông tin mặc định: **12345678**

### 13. Hóa đơn Hilo

*Hỏi khách hàng **thông tin đăng nhập web** hóa đơn là đủ*

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***: Thông tin đăng nhập web Hilo
* ***User***: Thông tin đăng nhập web Hilo
* ***Pass***: Thông tin đăng nhập web Hilo

### 14. Hóa đơn điện tử Vina

Chưa có thông tin. Chưa có khách hàng dùng thật nên chưa đủ thông tin

* ***Mã số thuế***: Mã số thuế của đơn vị
* ***Url***:&#x20;
* ***User***:&#x20;
* ***Pass***:&#x20;


# CÔNG CỤ LIÊN QUAN

#### 1. WEB check json lỗi

* Đăng nhập web. copy paste chuỗi json vào. web sẽ báo lỗi dòng lệnh&#x20;
* Áp dụng với trường hợp: Hoá đơn viettel báo lỗi
  * {"code":400,"message":"JSON\_PARSE\_ERROR","data":"Lỗi định dạng dữ liệu truyền vào"}
  * do định dạng dòng mặt hàng bị xuống dòng, dẫn tới sai cấu trúc json

{% embed url="<https://jsonlint.com/>" %}
web check lỗi json
{% endembed %}


# TỔNG HỢP LỖI CHUNG

## Dữ liệu không đúng định dạng - Email không hợp lệ

### Nguyên nhân

* Trường thông tin email đẩy lên trống hoặc không đúng định dạng

### Xử lý

* Người dùng quay về phiếu bán hàng, nhập bổ sung hoặc sửa lại thông tin ô "email"
* Người dùng khai báo lại thông tin khách hàng trong "Danh mục đối tượng công nợ"


# Update trạng thái huỷ cho chứng từ

## Mô tả

* Trong một số trường hợp, bấm nút "kiểm tra" trạng thái hoá đơn, phần mềm không nhận được trạng thái đúng của hoá đơn trên web ( do web thay đổi trạng thái, lỗi hệ thống....). Thì sử dụng câu lệnh sau để update tạm thời và báo lại lập trình check sau&#x20;

  <figure><img src="/files/vxKnBJ4Zu6TefL0bUsvW" alt=""><figcaption><p>Lấy ID_Keyct để sử dụng update</p></figcaption></figure>

```sql

-- Update DuLieuHoaDon
Update DuLieuHoaDon 
set LoaiHoaDon=N'Hủy',loaidl=63, trangthaihd=7,userKyhd=2,ngayKyhd=getdate()
where keyct= 1252053


-- Update DuLieuKetoan
-- loaidl=63: dữ liệu hoá đơn bị huỷ
Update DuLieuKeToan set loaidl=63 
where keyct= 1252053
```


# update trạng thái hoá đơn effect

update trạng thái hoá đơn là đã ký

```sql
-- Some code
update _THU_TIEN set tthaihd='ck2phhd',Muserk2=MA_USER,Date2=GETDATE where
```


# Xoá menu chuột phải bị lặp lại

* Khi cập nhật kỹ thuật, menu chuột phải bị lặp lại nhiều lần

```
1. vào bản chuẩn 
2. vào chức năng updatemanager
3. tìm dòng update kỹ thuật của hoá đơn. click đúp vào dòng
4. ở mục code chạy cuối cùng
- copy câu lệnh để chạy
- bình thường, khi update kỹ thuật hoá đơn là tự chạy câu lệnh cuối cùng, 
nhưng một số đơn vị không chạy được thì phải copy chạy tay
```


# TỔNG HỢP LỖI M-INVOICE

## 1. View PDF báo lỗi

* Do api trả về kết quả lỗi: Do mạng internet hoặc ghi file hoặc hệ thống Minvoice lỗi
* Báo khách hàng đợi khoảng 5 phút thao tác lại hoặc lên web hoá đơn kiểm tra view pdf có lỗi không
* nếu view pdf trên web lỗi thì chắc chắn ở dưới lỗi

## 2. View pdf ở dtech khác với view pdf ở Minvoice

* Báo lại Minvoice check update hệ thống: do pdf của api khác với view pdf trên web

## 3. 01 chứng từ tạo được 02 hoá đơn

* lần 1: đẩy chứng từ lên hệ thống thành hoá đơn nháp 01
* bấm nút kiểm tra trên dtech, thì api trả về lỗi, dtech update chungtu về trạng thái hoá đơn mới ( thay vì giữ nguyên là chờ ký)
* khách hàng lại bấm tiếp "tạo hoá đơn", dẫn tới 01 chứng từ tạo được 02 hoá đơn
* Với bản shop:
  * Do không xoá được trên web hoá đơn nháp 2, nên ở lưới hoá đơn điện tử của bản shop, chuột phải vào hoá đơn và chọn xoá hoá đơn chờ ký
  * Sử dụng cấu lệnh update cho chứng từ các thông tin để nó hiểu là tương ứng với hoá đơn nháp 1
* Với bản kế toán
  * lên web xoá bỏ hoá đơn nháp số 2
  * Sử dụng cấu lệnh update cho chứng từ các thông tin để nó hiểu là tương ứng với hoá đơn nháp 1

## 4. "Có lỗi khi lấy thông tin cks đã đăng ký"

* Bấm "Ký phát hành" thì bật lỗi
* Do hệ thống web chưa khai báo thông tin "mặc định ký"&#x20;
* Đăng nhập web hoá đơn/ "hệ thống"/ "Đăng ký chứng thư số"/ "sửa chứng thư số"/ check vào "mặc định ký"&#x20;

## 5. Đẩy hoá đơn báo lỗi "token hết hạn hoặc không đúng, vui lòng kiểm tra lại"

* Khi bấm đẩy hoá đơn nháp thì báo lỗi
* nguyên nhân: Do thay đổi mật khẩu đăng nhập trang web. Hỏi lại khách hàng để vào cấu hình lại tham số kết nối

## 6. Đẩy chờ ký hoặc phát hành báo lỗi 401-Unauthorized

* do thay đổi mật khẩu. kiểm tra lại mật khẩu khai báo và mật khẩu đăng nhập web
* nếu mất khẩu đúng, thì khả năng do mạng kết nối hoặc api kết nối với minvoi chập chờn. đợi một lúc sau bấm đẩy lại&#x20;

  <figure><img src="/files/UodjFqRMBdPmbRwRVJEA" alt="" width="503"><figcaption></figcaption></figure>

## 7. Ký hóa đơn báo chưa cài đặt Plugin ký số

Khi ký hóa đơn trên web Minvoice thì báo chưa được cài đặt Plugin

<figure><img src="/files/2VNrmLA1INyEPpf37L88" alt=""><figcaption></figcaption></figure>

Nguyên nhấn:&#x20;

* Do sử dụng trình duyệt web nhân Chromium sẽ bị giới hạn quyền truy cập mạng nội bộ như: Google Chrome, Cốc Cốc, Microsoft Edge mới...

Cách sửa:

* Người sử dụng mở trình duyệt web lên, copy giá trị sau và Paste vào đường dẫn trình duyệt web nhấn Enter để chạy:

```
chrome://flags/#local-network-access-check
```

Đổi giá trị từ Default sang Disable và khởi động lại trình duyệt web, đăng nhập vào trang hóa đơn để ký lại xem.

<figure><img src="/files/13wsdnw4gAHOVUF9GVU3" alt=""><figcaption></figcaption></figure>


# HDSD M-INVOICE THƯỜNG GẶP

### 1. Link hướng dẫn v2

{% embed url="<https://hdsd.minvoice.com.vn/minvoice2/xu-ly-sai-sot/dieu-chinh-va-thay-the-tren-he-thong-khac/>" %}

### 2. Link hướng dẫn v1

{% embed url="<https://hdsd.minvoice.com.vn/minvoice1/huong-dan/dang-nhap/>" %}

### 3. Điều chỉnh/ thay thế hóa đơn trên hệ thống khác

{% embed url="<https://hdsd.minvoice.com.vn/minvoice2/xu-ly-sai-sot/dieu-chinh-va-thay-the-tren-he-thong-khac/>" %}


# TỔNG HỢP LỖI VNPT

## 1. Danh sách hoá đơn tồn tại hoá đơn trùng Fkey&#x20;

* Nguyên nhân
  * Do trùng key liên kết của dtech và vnpt
  * Phiên bản cũ sẽ là keyct dạng số
* Lý do có thể
  * Đang dùng phần mềm dtech và đẩy lên vnpt. Sau đó chuyển sang phần mềm khác (mất dữ liệu, convert...) và nhập lại từ đầu. thì keyct sẽ bắt đầu lại từ 1. Khi đó, nếu đẩy dữ liệu lên vnpt sẽ bị trùng
  * Do mất dữ liệu. Nên người dùng nhập dữ liệu với ngày hạch toán là thời gian sau mất dữ liệu
  * Do chuyển phiên bản. Phiên bản cũ đang có Keyct Max(xx). Sang phiên bản mới thì keyct không được xử lý hoặc sai
  * Do sử dụng xuất nhập dữ liệu. Khi xuất nhập dữ liệu từ nơi khác về thì sẽ được đánh lại keyct. Dẫn tới keyct có thể bị trùng lại nếu kết nối lại với hoá đơn ( xuất nhập dữ liệu không lấy bảng dulieuhoadon)

## 2. Đẩy hoá đơn báo lỗi Erro20

<figure><img src="/files/PGZzHKrZHYkAXLWS3GNh" alt="" width="375"><figcaption><p>Tạo hoá đơn báo lỗi erro20</p></figcaption></figure>

**Nguyên nhân báo lỗi** (Đây là lỗi báo chung của VNPT)

* Hết số lượng hóa đơn gói đăng ký

<figure><img src="/files/66WzH9FHjpB1uAwBVLzE" alt=""><figcaption></figcaption></figure>

* Sai SeriHD của chứng từ đẩy lên
* Thay đổi mật khẩu tài khoản Admin trên web VNPT


# TỔNG HỢP LỖI VIETTEL

## 1. *Ký phát hành* báo lỗi "hashString null"

* Đăng nhập web viettel. Kiểm tra hoá đơn chờ ký. Xoá hoá đơn chờ ký&#x20;

  <figure><img src="/files/5FONY2GlbjG7pmXre5ZT" alt=""><figcaption><p>Tìm kiếm hoá đơn nháp trên viettel để xoá</p></figcaption></figure>
* Đăng nhập Dtech. Ký phát hành lại hoá đơn

## 2. Bấm nút "Kiểm tra" phần mềm báo không tồn tại hoá đơn

* Kiểm tra log đẩy hoá đơn thì có phát sinh đẩy từ dtech lên viettel
* Bấm nút "Kiểm tra" thì báo không tồn tại hoá đơn ( nghĩa là không liên kết)
* Lý do: Do đặc thù viettel, nếu sửa hoá đơn trên web viettel thì viettel sẽ xoá hoá đơn cũ và tạo lại hoá đơn mới. Do vậy, sẽ bị mất key liên kết giữa 2 phần mềm ở trên tờ hoá đơn cũ.&#x20;
* Trường hợp này: thì người dùng chấp nhận mất liên kết và bổ sung lại thông tin số hoá đơn cho phiếu kế toán và không xuất hoá đơn nữa
* chứng từ bán hàng ở bên tab hoá đơn điện tử đang ở trạng thái chờ ký: xoá bỏ

## 3. Ký phát hành báo lỗi cks "code 400 signature not compare - thông tin usb token không đúng với cks đăng ký"

* Nguyên tắc cấu hình cks token: Pm dtech sẽ lưu dải seri của token vào cấu hình
* khi thay đổi cks hoặc gia hạn cks thì cần cấu hình lưu thông tin cks mới vào dtech
* Vào tab Phát hành hoá đơn/ cấu hình/ bấm nút Chọn cks và lưu lại

## 4. Ký phát hành báo lỗi "{"code":400,"message":"BAD\_REQUEST\_EXISTS\_OTHER\_USB\_SIGN\_PROCESSING","data":"BAD\_REQUEST\_EXISTS\_OTHER\_USB\_SIGN\_PROCESSING","errorCode":null}"

* do có hoá đơn nháp trên viettel bị lỗi
* lên web viettel và xoá hoá đơn nháp&#x20;

  <figure><img src="/files/YvNwaRBPXaY5vAz7FxIi" alt=""><figcaption><p>Xoá hoá đơn nháp gây lỗi khi ký hoá đơn viettel</p></figcaption></figure>

## 5. view hoặc đẩy báo lối "... lỗi định dạng truyền dữ liệu vào..."

* Do tên hàng hoá có ký tự xuống dòng (dạng Alt +enter) dẫn tới json bị lỗi
* Link web: <https://jsonlint.com/>
* Vào phần hành phát hành hoá đơn/ nút cấu hình/ log truyền đẩy/ copy dòng "cấu trúc đẩy hoá đơn" / paste nội dung lên web để check lỗi ở dòng mặt hàng nào
* Để kiểm tra thì có thể làm bằng cách: Copy chuỗi đẩy lên và paste vào link web kiểm tra json để nhìn báo lỗi ở nội dung nào&#x20;

  <figure><img src="/files/U0Z61AktEfEfPImlbDKM" alt=""><figcaption><p>Thông báo lỗi</p></figcaption></figure>

## 6. Tạo hoá đơn báo lỗi: {"code":400,"message":"SIGNATURE\_NOT\_FOUND","data":" Không tìm thấy chứng thư số","errorCode":null}

Vào cấu hình chọn lại chỉ tạo hoá đơn chờ ký thôi.

<figure><img src="/files/sG9J2r6rIrv6gmffV6HA" alt=""><figcaption></figcaption></figure>


# TỔNG HỢP LỖI BKAV

## 1. Đẩy lên chờ ký thì báo lỗi "Hoá đơn không liên tục: đã tồn tại ... ngày ... lớn hơn ngày của hoá đơn này"

* Bấm đẩy lên chờ ký thì báo lỗi&#x20;
* Người dùng lên web hoá đơn, kiểm tra xem có hoá đơn nào có ngày hoá đơn lớn hơn (>) ngày hoá đơn của hoá đơn hiện thời không. Nếu có thì cần xoá bỏ hoá đơn trên web (nếu chưa ký) hoặc sửa lại hoá đơn hiện thời cần đẩy lên (nếu hoá đơn trên web đã ký)

  <figure><img src="/files/eZjyrrwtiVbUVZASdvCH" alt=""><figcaption><p>Thông báo lỗi khi "đẩy chờ kỳ"</p></figcaption></figure>

## 2. Đẩy lên chờ ký thì báo lỗi "Tài khoản chưa phát hành mẫu hoá đơn hoặc dải số hoặc dải số đã hết"

* Bấm đẩy lên chờ ký thì báo lỗi
* Người dùng kiểm tra lại các nguyên nhân có thể:
  * Seri hoá đơn của hoá đơn hiện thời đã đúng chưa: Mẫu (01 hoặc 02) + seri (thông tin năm). Người dùng có thể lên web hoá đơn, bấm lập nháp, để nhìn thấy thông tin seri khi lập nháp
  * Kiểm tra lại đúng là hết số lượng hoá đơn đã mua của hệ thống web hoá đơn chưa. Nếu hết thì liên hệ với bên cung cấp để mua thêm&#x20;

    <figure><img src="/files/DsmfCuhnGkJEH85JoMto" alt=""><figcaption><p>Thông báo lỗi khi đẩy chờ ký</p></figcaption></figure>

    <figure><img src="/files/mmOJfcnlNit6qn4wk7Wt" alt=""><figcaption><p>Cần kiểm tra mẫu, seri của hoá đơn lỗi - Sai thông tin seri</p></figcaption></figure>


# TỔNG HỢP LỖI VIN

## 1. Đẩy chờ ký báo lỗi "Lỗi GetToken Unexpected character  encountered while parsing value:<.Path, line 0, position 0."

* Người dùng lên web:&#x20;
  * Kiểm tra đăng nhập được không, có bị chậm không
  * Thử lập hoá đơn nháp có được không
* Người dùng đợi 5-10 phút sau thử lại.&#x20;

<figure><img src="/files/pxQSwhFyXgVXPue72HcH" alt=""><figcaption><p>đẩy chờ ký báo lỗi</p></figcaption></figure>

## 2. Đẩy chờ ký báo lỗi "Lỗi hàm Token 0, Có lỗi xảy ra. Tên đăng nhập hoặc mật khẩu không đúng"

* Người dùng kiểm tra lại:
  * Có thay đổi mật khẩu web không
  * Đăng nhập web:&#x20;
    * Xem có đăng nhập được không
    * Có bị chậm không
    * Lập hoá đơn nháp được không
* Người dùng thử đợi 5-10 phút sau thực hiện lại&#x20;

  <figure><img src="/files/sLkrBc7dxBYXcZVq7yFo" alt=""><figcaption><p>Đẩy báo lỗi </p></figcaption></figure>


# TỎNG HỢP LỖI EASYINVOICE

## 5\_Chuỗi xác thực không hợp lệ

<figure><img src="/files/HiRpJ4XVwXNxQAam3Sl7" alt=""><figcaption></figcaption></figure>

**Mô tả**: Nguyên nhân là máy tính đẩy hoá đơn đang bị sai giờ hệ thống so với thực tế

<figure><img src="/files/UIK6cSUKVl1Q3S6j71O3" alt=""><figcaption></figcaption></figure>

**Cách sửa**: Chỉnh lại ngày giờ máy tính đúng với giờ thực tế


# LƯU Ý HOÁ ĐƠN ĐIỆN TỬ

Những lưu ý về hoá đơn đơn điện tử

## HOÁ ĐƠN TỪ MÁY TÍNH TIỀN

<table data-full-width="true"><thead><tr><th width="174">GHI CHÚ\HÃNG</th><th>M-invoice</th><th>BKAV</th><th>Viettel</th><th>VNPT</th><th>Nacenncom</th><th>SoftDream</th></tr></thead><tbody><tr><td>Mã tra cứu</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Hình thức ký số</td><td>Ký trên hóa đơn</td><td>ký trên thông điêp gửi thuế</td><td>ký trên thông điêp gửi thuế</td><td>ký trên thông điêp gửi thuế</td><td>ký trên thông điêp gửi thuế</td><td>ký trên thông điêp gửi thuế</td></tr><tr><td>Ghi chú kết nối</td><td></td><td></td><td></td><td></td><td></td><td></td></tr><tr><td>Xem hóa đơn từ phần mềm</td><td>Có</td><td>Có</td><td>Có</td><td>Chỉ xem được hóa đơn đã phát hành</td><td>không</td><td>Có</td></tr><tr><td>Sửa hóa đơn chưa ký</td><td>Có</td><td>Có</td><td>Phải lên web hóa đơn xóa đi, và dưới phần mềm tạo lại hóa đơn</td><td>Có</td><td>Có</td><td>Có</td></tr><tr><td>Xóa hóa đơn chưa ký</td><td>Có</td><td>Có</td><td>Có</td><td>Có</td><td>không</td><td>Có</td></tr><tr><td>Lỗi hay gặp</td><td>Cuối tháng hay bị lỗi</td><td></td><td></td><td></td><td>Vào web hóa đơn lúc nào cũng chậm</td><td></td></tr></tbody></table>

| <p>1. Tạo hóa đơn, phần mềm nhận được mã tra cứu, hóa đơn chưa có số<br>2. Ký hóa đơn lên web để ký và gửi cơ quan thế</p> |
| -------------------------------------------------------------------------------------------------------------------------- |

| <p>1. Tạo hóa đơn<br>Phần mềm nhận được mã tra cứu, số hóa đơn , mã cơ quan thuế<br>2. Ký hóa đơn<br>Lên web hóa đơn để ký gửi cơ quan thuế</p> |
| ----------------------------------------------------------------------------------------------------------------------------------------------- |

| <p>1. Tạo hóa đơn<br>Phần mềm nhận được mã tra cứu<br>2. Ký hóa đơn<br>Lên web hóa đơn để ký gửi cơ quan thuế</p> |
| ----------------------------------------------------------------------------------------------------------------- |

| <p>Dtech truyền lên<br>Nên trong trường hợ mất mạng, vẫn báng hàng bình thường<br>Trên phiếu in cho khách hàng đã có mã tra cứu</p> |
| ----------------------------------------------------------------------------------------------------------------------------------- |

| <p>1. Tạo hóa đơn<br>Phần mềm nhận được mã tra cứu<br>2. Ký hóa đơn<br>Lên web hóa đơn để ký gửi cơ quan thuế<br>-------<br>Hiện viettel cũng có tạo hóa đơn là có số mà mã cqt luôn, tuy nhiên hóa đơn tạo kiểu này k cho phép sửa nên tạm đang xử lý như trên<br><br>Đối vơi viettel thì mẫu hóa đơn hay có thêm trường ĐỘNG, vì vậy chỗ này sẽ phải xử lý cho từng khách hàng<br>Hiện tại đã gặp trường động lưu thông tin đơn vị hoặc thông tin họ tên người mua, thì khi kết nối phải truyền vào trường động này thay vì truyền vào cái trường mặc định</p> |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| <p>1. Tạo hóa đơn<br>Phần mềm nhận được số hóa đơn,mã tra cứu,mã cơ quan thuế<br>2. Ký hóa đơn<br>Lên web hóa đơn để ký gửi cơ quan thuế</p> |
| -------------------------------------------------------------------------------------------------------------------------------------------- |

| <p>1. Tạo hóa đơn<br>Phần mềm nhận được số hóa đơn,mã tra cứu,mã cơ quan thuế<br>2. Ký hóa đơn, có thể lên web hóa đơn ký hoặc ký ở giao diện hóa đơn điện tử thông thường</p> |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

| <p>Dtech truyền lên<br>Nên trong trường hợ mất mạng, vẫn báng hàng bình thường<br>Trên phiếu in cho khách hàng đã có mã tra cứu</p> |
| ----------------------------------------------------------------------------------------------------------------------------------- |


# THIẾT LẬP HD M-INVOICE \*

Hướng dẫn thiết lập hóa đơn M-invoice khi phát sinh khách hàng mới

## CÔNG VIỆC

* Đăng ký tờ khai
* Chờ thuế duyệt
* Cấu hình hóa đơn M-invoice
* Kết nối phần mềm dtech (kế toán, shop) với hóa đơn
* Cung cập lại thông tin cho khách hàng

## HƯỚNG DẪN

### 1. CẤU HÌNH HÓA ĐƠN M-INVOICE

* Add chữ ký số
* Cấp số hóa đơn sau khi ký
* Tạo mẫu hóa đơn: nhiều thuế suất hoặc 1 thuế suất
* Tạo mẫu hóa đơn: thay đổi serihd
* Gửi email tự động
*


# LỖI THƯỜNG GẶP


# LƯU BÁO LỖI

### 1. Lưu báo lỗi could not load file assembly 2936 byte..from dtechV001

* Do quy tắc kiểm tra tổng thể
* cập nhật all quy tắc kiểm tra tổng thể&#x20;

  <figure><img src="/files/Cs6p4mT0m0CGmAbpVDd3" alt=""><figcaption></figcaption></figure>

### 2. Đăng nhập menu trái/ báo cáo bật lỗi

* Do mất menu trên/ báo cáo
* Báo khách hàng vào Menu trên/ hệ thống/ cập nhật lại trạm

### 3. Xem báo cân đối phát sinh ra số liệu linh tinh

* Do mất menu trên/ danh mục
* Báo khách hàng vào Menu trên/ hệ thống/ cập nhật lại trạm


# GIAO DIỆN VỠ FONT

## 1. Đăng nhập bị co màn hình lại

* do hepl hướng dẫn của pm ảnh hưởng
* bỏ file hệ thống liên quan help&#x20;

  <figure><img src="/files/IuyX5zgLqptecTyUeOtQ" alt=""><figcaption><p>bỏ fiel chrome này đi thì sẽ không bị co màn hình thu nhỏ</p></figcaption></figure>


# ĐĂNG NHẬP MẤT USER

* Vào thư mục ...\DCONFIG\SEP\DT\\
* Tìm thư mục có tên data đang đăng nhập
* xóa thư mục và đăng nhập thử lại
* Nếu vẫn trống, thì thử gõ tên user để xâu lọc xem có được không? nếu được thì là do mất file lưu vết user đăng nhập cuối cùng nên hiển thị trắng user


# CONVERT DỮ LIỆU


# QUY TRÌNH

### 1. Kiểm tra nghiệp vụ có sử dụng

```sql
-- code check nghiepvu có phát sinh dữ liệu
select T1.nghiepvu,t1.ngayMax, T2.ma,T2.vietnam
from (select nghiepvu,max(ngay) as ngayMax from dulieuketoan group by nghiepvu)T1 left join 
(select * from nghiepvu where nghiepvu<>0)T2 on t1.nghiepvu=t2.nghiepvu
order by t2.ma
```

### 2. Kiểm tra danh mục mới

### 3. Kiểm tra tableManage có dòng mới

### 4. Kiểm tra danh mục sử dụng

### 5. Kiểm tra cột mới trên dulieuketoan

```sql
/* bản cũ thì chekc bằng mắt
- bản sql thì đọ 2 bảng bằng lệnh sql
- câu lệnh dưới tìm ra cột lệch 2 bảng và cấu trúc lệch
*/
declare @tableName nvarchar(20)= 'dulieuketoan'
select t1.*, t2.*
from
(
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH,TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME=@tableName
)T1
full outer join 
(
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH,TABLE_NAME
FROM [sql2022].[24h_kiemtra].INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME=@tableName
)T2 on t1.COLUMN_NAME = t2.column_name
where t1.COLUMN_NAME is null or t2.column_name is null 
or t1.CHARACTER_MAXIMUM_LENGTH<> t2.CHARACTER_MAXIMUM_LENGTH
or t1.DATA_TYPE <> t2.DATA_TYPE
```


# EFFECT 2.0


# EFFECT 3.0 - 4.0 - 6.0

## DANH MỤC

#### 1. VẬT TƯ

| EFFECT   | DTECH       | GHI CHÚ                                                                   |
| -------- | ----------- | ------------------------------------------------------------------------- |
| loai\_tc | thanhphamcu | <p>sau này xử lý:</p><p>+ dulieuketoan.sanpham </p><p>+ vattu.sanpham</p> |
| dv\_do   | dvdo1       |                                                                           |

```sql
-- update vattu.sanpham=1 nếu vattu.thanhphamcu <>0
update vattu set sanpham=1,modidate=getdate()
where thanhphamcu<>0

-- update dulieuketoan.sanpham = vattu.thanhphamcu
update T1 set T1.sanpham=T2.vattu from
(select * from dulieuketoan where sanpham<>0) T1 
inner join 
(select * from vattu where thanhphamcu<>0) T2 
on T1.sanpham = T2.thanhphamcu and T1.donvi = T2.donvi

```

#### 2. V

## DỮ LIỆU

#### 1. ĐƠN VỊ TIỀN

* Chuyển loại tiền VNĐ thành VND

```sql
-- update loaitien
Update dulieuketoan set loaitien='VND' where loaitien like N'VN%'
```

#### 2. CHI PHÍ PHÂN BỔ

* Khi convert, thutien.taisan sẽ được ghi nhận vào dulieuketoan.taisan
* taisan.taisan lại bao gồm cả (taisan + phanbo) nên cần phải xử lý
* Các nghiệp vụ liên quan 242 thì gán dulieuketoan.phanbo=dulieuketoan.taisan
* Xoá các mã phanbo.phanbo nếu liên quan tới 211,214 hoặc phanbo.phanbo không liên quan tới 242

```sql
-- Gán dulieuketoan.phanbo = dulieuketoan.taisan nếu tkno,tkco=242
Update dulieuketoan 
set dulieuketoan.phanbo = dulieuketoan.taisan
where taisan<>0 and (tkno like '242%' or tkco like '242%')

-- xoá phanbo.phanbo nếu không liên quan 242
delete T1
from 
    (select * from phanbo where phanbo>0)T1 
left join 
    (select phanbo from dulieuketoan where phanbo>0 group by phanbo)T2
on T1.phanbo = T2.phanbo
Where T2.phanbo is null



```

#### 3. NGHIỆP VỤ KẾT CHUYỂN THUẾ 333/133

* Các bút toán 333/133 sẽ được ghi nhận vào id= - nhomkc = -14

```sql
-- update nghiepvu kết chuyển thuế
update dulieuketoan set nghiepvu=-14 where tkno like '333%' and tkco like '1331%'
```

#### 4. SẢN PHẨM

* Khi convert thì thutien.loaitc sẽ lưu vào dulieuketoan.sanpham
* dulieuketoan.sanpham đang là giá trị cũ, cần chuyển đổi thành giá trị mới của vattu.vattu tương ứng

```sql
-- Some code
Update T1 set T1.sanpham = T2.vattu
from
(select * from dulieuketoan where sanpham<>0 and donvi=1) T1
inner join
(select * from vattu where thanhphamcu<>0 and vattu<>0 and donvi=1) T2
on T1.sanpham = T2.thanhphamcu
```

#### 5.  Convert danh sách user

* Cần khởi tạo danh sách user trên dtech tương ứng với bản cũ
* để khi convert thì dữ liệu vào đúng user và user được phép tìm kiếm dữ liệu của user khác theo phân quyền

```sql
/*
- xóa danh sách user cũ trên dtech: áp dụng với bản cus
- insert danh sách mới vào
*/
delete users where [user] not in (1,2)

with cte_user as (
select T1.ma_user, T2.ma, T2.ten 
from (select ma_user from _thu_tien group by ma_user)T1
inner join _users T2 on t1.ma_user = t2.ma_user
)
Insert into user (ma,ten,[user])
select ma,ten,ma_user from cte_user
```


# MISA VERSION DESKTOP

## Ý nghĩa chức năng

Dùng để convert số liệu từ misa desktop sang Dtech

## Quy trình chung

* Sử dụng chức năng convert của phần mềm
* Kết nối data Misa là nguồn: Thông tin user và mk theo tệp tài liệu
* Kết nối data Dtech là đích
* Bấm nút convert ( tích chọn đầu kỳ)
* Cập nhật lại trạm: Để phần mềm reset lại danh mục mới nhất
* Kiểm tra danh mục:
  * Vattu: các cột tài khoản
  * Taisan: các cột tài khoản
  * Phanbo: Các cột tài khoản
  * Dulieuketoan: Cột tài khoản nợ, có
* Chạy lệnh Sql để xử lý lại một số nội dung:&#x20;
  * Update nghiepvu dựa theo left(chungtu,3)
  * Update keyct
  * Update psco cho các dòng nào là bút toán thêm. Nếu không update thì sẽ bị các lỗi sau:
    * không in được phiếu nhập, bán, chi tiền: do tính toán sai liên quan bút toán thêm
    * F2 lên giao diện sẽ lên cả bút toán thêm và sẽ bị báo đỏ cột tài khoản&#x20;
    * Giao diện xem trước dữ liệu bị hiển thị thừa và sum cột sẽ bị sai, dẫn tới khách hàng hiểu sai, bị double dữ liệu hoặc sai số tiền

```sql
--- Some code
----delete dulieuketoan where loaidl=-99  ---- lệnh xoá của data đặc thù, không quan tâm
----select importinfor,* from dulieuketoan where importinfor<>'' --- lệnh kiểm tra dữ liệu convert misa
-----I. Câu lệnh kiểm tra xem: các nghiệp vụ phát sinh trên dữ liệu, và có sử dụng btthems
----- lưu ý: importinfor đánh dấu dữ liệu convert misa bằng ký tự 'misa'
----- câu lệnh dưới đang trả về các nghiệp vụ: 101, 301, 501, 1013 ( với dữ liệu khác thì chạy và lấy ra nghiepvu liên quan để kiểm tra tiếp phía dưới)
select nghiepvu from 
(
select T1.*, T2.no, T2.co, T2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
)T group by T.nghiepvu

----II. Chạy từng nghiepvu. Chạy câu select để kiểm tra trước khi chạy câu update 
----1. nhập kho nội địa
select T1.*, T2.*
--update t1 set t1.psco = t2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=101 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
order by t1.ngay, t1.chungtu


update t1 set t1.psco = t2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=101 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
where t2.nghiepvu is not null


----2. bán hàng nội địa; 301 ; btthems vat = 9
select T1.*, T2.*
from 
	(select ngay,chungtu,sohd,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=301 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
order by t1.ngay, t1.chungtu


update t1 set t1.psco = t2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=301 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null


----3. chi tiền mặt ; 501
select T1.*, T2.*
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=501 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
order by t1.ngay, t1.chungtu


update t1 set t1.psco = t2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=501 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null

----4. Ghi tăng chi phí trả trước; 1013
select T1.*, T2.*
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=1013 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
order by t1.ngay, t1.chungtu


update t1 set t1.psco = t2.btthems
from 
	(select ngay,chungtu,tkno,tkco,nghiepvu,psco from dulieuketoan where nghiepvu=1013 and importinfor='misa')T1 left join
	(select ghichu,no,co,btthems,nghiepvu from btthems )T2 on T1.nghiepvu = T2.nghiepvu and (t1.tkno = t2.no or t1.tkco = t2.co)
--where t2.nghiepvu is null -- kiểm tra các dòng bút toán mẹ. tránh trường hợp ghép thiếu
where t2.nghiepvu is not null
```

## Cập nhật Nghiệp vụ trên DTECH  doanh nghiệp dựa vào chứng từ MISA - Doanh Nghiệp

```sql
select Left(ChungTu,3) as ChungTu,Max(ChungTu) as Vidu from DuLieuKeToan
Group by Left(ChungTu,3) 


-- PNVK => 702
-- BH
Update DuLieuKeToan
set NghiepVu=301
where ChungTu like 'BH%' and Nghiepvu=702
-- NK
Update DuLieuKeToan
set NghiepVu=101
where ChungTu like 'NK%' and Nghiepvu=702

--PC

Update DuLieuKeToan
set NghiepVu=501
where ChungTu like 'PC%' and Nghiepvu=702

-- NTT

Update DuLieuKeToan
set NghiepVu=405
where ChungTu like 'NTT%' and Nghiepvu=702

-- XK

Update DuLieuKeToan
set NghiepVu=202
where ChungTu like 'XK%' and Nghiepvu=702

-- PT

Update DuLieuKeToan
set NghiepVu=401
where ChungTu like 'PT%' and Nghiepvu=702


--NVK ==> 702

-- KH

Update DuLieuKeToan
set NghiepVu=621
where ChungTu like 'KH%' and Nghiepvu=702

-- UNC

Update DuLieuKeToan
set NghiepVu=505
where ChungTu like 'UNC%' and Nghiepvu=702

-- PBC

Update DuLieuKeToan
set NghiepVu=1014
where ChungTu like 'PBC%' and Nghiepvu=702


--- BTL: Hàng bán bị trả lại
-- TT200 là 105, TT133 là 1071
Update DuLieuKeToan
set NghiepVu=105
where ChungTu like 'BTL%' and Nghiepvu=702
```

## Cập nhật Lý do, nghiệp vụ DTECH88 - Convert MISA Doanh nghiệp sang bản Dtech88 cho HKD

```sql
--- Update bút toán doanh số

Update DuLieuKeToan
set NghiepVu=301,NganhNghe=2,
DinhKhoanTuDong=37,TKno='1311',TKCo='91111'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'BH%'


--Update kho cho but toan ban hang
Update DuLieuKeToan
set Kho=3
where ngay>='01/01/2022'
and NghiepVu=301
and Chungtu like 'BH%'

--- Update bút toán gia von xuat kho

Update DuLieuKeToan
set NghiepVu=301,NganhNghe=2,
DinhKhoanTuDong=37,TKno='91121',TKCo='1523'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'XK%'

-- update kho nhan ve =0
Update DuLieuKeToan
set khoo=0
where ngay>='01/01/2022'
and NghiepVu=301
and Chungtu like 'XK%' and khoo>0

--- Update nhạp kho

Update DuLieuKeToan
set NghiepVu=101,DinhKhoanTuDong=26,
TkNo='1523',TKCo='33181'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'NK%'


--- Update chiet khau mua hang

Update DuLieuKeToan
set NghiepVu=101,DinhKhoanTuDong=26,
TkNo='1523',TKCo='33181',Psno= -Psno
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'MGG%'

-- Nhap mua dịch vu
Update DuLieuKeToan
set NghiepVu=101,DinhKhoanTuDong=123,
TkNo='91128',TKCo='33181'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'MDV%'


--- Update Thu tien ngan hang

Update DuLieuKeToan
set NghiepVu=405,DinhKhoanTuDong=72,
TkNo='1112',TKCo='1311'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'NTTK%'

--- Update Thu tien tien

Update DuLieuKeToan
set NghiepVu=401,DinhKhoanTuDong=3,
TkNo='1111',TKCo='1311'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'PT%'

--- Update chi tien ngan hang

Update DuLieuKeToan
set NghiepVu=505,DinhKhoanTuDong=60,
TkNo='33181',TKCo='1112'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'UNC%'

--- Update chi tien mat

Update DuLieuKeToan
set NghiepVu=501,DinhKhoanTuDong=12,
TkNo='1112',TKCo='1111'
where ngay>='01/01/2022'
and NghiepVu=702
and Chungtu like 'PC%'
```


# 3T SOFT


# CONVERT TT99 DEV

## QUY TRÌNH CHUNG

* Đóng bản lập trình 200: để dự phòng trường hợp bản 200 vẫn phải chỉnh sửa cập nhật đặc thù. có thể cân đối sửa thẳng thành 99
* Nhân đổi bản lập trình 200 lên thành 99: phục vụ là bản lập trình cho bản tt99
* Cập nhật bản lập trình thành tt99
  * Tài khoản
  * Báo cáo tài chính, báo cáo khác
  * Chi phí trả trước ⇒ chi phí chờ phân bổ
* Cài đặt bản khách hàng:
  * Hoặc: Nhân bản tt200 của khách hàng thành bản lưu tt200 + mở khóa. Bản gốc tt200 trở thành bản tt99
  * Hoặc: giữ nguyển bản tt200, nhân bản thành bản tt99
  * việc này sẽ tương ứng với dòng mở khóa iso
* Bản tt99 khách hàng
  * Table dulieuketoan cần mở thêm các cột (nếu chưa có):&#x20;
    * tknocu -nvarchar(10)
    * tkcocu - nvarchar(10)
    * nghiepvucu - int
  * Cập nhật danh mục tài khoản 99 đã chuẩn bị trên bản lập trình
  * Cập nhật lại các cột: tknocu,tkcocu,nghiepvucu
  * Xử lý lại dữ liệu tài khoản chuyển đổi đã test bên lập trình
  * Cập nhật báo cáo: từ file dat&#x20;
    * báo cáo tài chính
    * báo cáo phụ&#x20;
  * Kiểm tra lại báo cáo
    * cân đối phát sinh: cân
    * cân đối kế toán: cân
    * lưu chuyển tiền: cân + đặc thù. cus hay bị sửa lại các chỉ tiêu. cần khớp giữa 2 báo cáo (vì cơ bản ko đổi chỉ tiêu)
    * kết quả kinh doanh: khớp 2 báo cáo 2 bản


# KỸ THUẬT CHUYỂN GIAO


# CHỈNH TITBROW NHẬP LIỆU

## 1. Thêm cột vào form nhập liệu

* Cột cần thêm vào form có 2 dạng: cột thật và cột ảo.
* Cột thật: Là cột tồn tại trên bảng dulieuketoan
* Cột ảo: Là cột không tồn tại trên bảng dulieuketoan
* Đặt ẩn hiện cột trên brow của bảng NhaplieuCT
* Vào cấu hình nghiệp vụ/ đặt thêm trường. Lưu ý
  * nếu cột mới thêm thì nên cập nhật lại trạm trước khi xử lý
  * Thêm xong trường thêm thì thoát màn hình nhập liệu rồi vào lại
* Đặt ẩn hiện form chung nhập liệu
* cột ảo:&#x20;
  * Thêm cột ảo bằng cách đăng nhập brow NhaplieuCT và thêm 1 dòng. tích vào 'cột ảo' và đặt stt
  * Cột ảo không lưu dữ liệu mà sẽ được lưu vào moreValue. Nên trường hợp cần tìm kiếm dữ liệu tổng quát sẽ không tìm được

## 2. Thêm cột vào lưới nhập liệu

* Vào màn hình nhập liệu, đặt chuột ở lưới, bấm ctrl+f12 để gọi brow nhaplieuCT
* Lọc tìm cột, đặt stt<>0, sửa tên hiển thị và đóng lại
* Vào cấu hình nghiệp vụ, đặt ẩn hiện cột ở lưới

## 3. Thêm cột vào bảng tìm kiếm dữ liệu tổng quát

* Vào màn hình tìm kiếm tổng quát, đặt chuột ở lưới kết quả, bấm ctrl+F12 để gọi brow Dulieuketoan
* Lọc tìm cột, đặt stt<>0, sửa tên hiển thị và đóng lại


# CHỈNH PHIẾU IN

## 1. Thêm mới dòng mẫu in + mẫu in&#x20;

## 2. Sửa mẫu in

* Text
* công thức cột

## 3. Lưu ý


# CHỈNH BÁO CÁO


# SỬA CẤU TRÚC TABLE


# TẠO UPDATE SANG KHÀNG

## Yêu cầu

* Sau khi chỉnh sửa bản lập trình, kỹ thuật tạo bản cập nhật lên hệ thống để khách hàng có thể tải về
* Lưu ý: Khi báo khách hàng cập nhật thì nhắc khách hàng cập nhật xong thoát ra vào lại

## Hướng dẫn

### 1. Tạo cập nhật

### 2. Cách chọn nội dung cập nhật

* Cập nhật cấu trúc của bảng
* Cập nhật brow của bảng
* Cập nhật brow của nhập liệu kế toán
* Cập nhật brow của Erp - dulieunhapxuat
* Cập nhật code của object trên nhập liệu kế toán
* Cập nhật thêm nút trên nhập liệu kế toán
* Cập nhật Báo cáo
* Cập nhật Nghiệp vụ
* Cập nhật Mẫu in
* Cập nhật Giá thành


# EXCEL

#### 1. Xuất dữ liệu ra tệp excel nhưng không có biểu hiện gì, không sinh ra tệp

**Trường hợp 1:**

* Do regedit đang chứa 2 version excel. 1 version chưa gỡ hết. Kiểm tra xem Excel đang chạy là excel phiên bản bao nhiêu. giữ lại phiên bản đang chạy, xóa bỏ phiên bản đã gỡ
* Đăng nhập regedit theo đường dẫn: Computer\HKEY\_CLASSES\_ROOT\TypeLib{00020813-0000-0000-C000-000000000046}
* Nếu nhìn thấy nhiều hơn 1 version thì xóa version không chạy (Mở excel xem phiên bản nào chạy)&#x20;
  * Mục 1.9 là phiên bản 2019
  * Mục 1.8 là phiên bản 2013
  * Mục 1.7 là phiên bản 2010

**Trường hợp 2:**

Nếu không rơi trường hợp 1, thì thử Repair lại office

* Đăng nhập: **Control panel / Programs and Features / Microsoft office ....**&#x20;
* Chuột phải vào dòng Microsoft office:  Chọn **change/ Chọn repair**

**Trường hợp 3:**

Nếu không được 2 trường hợp trên, thì đổi exe dtech (86,64) tương ứng với excel

do không biết excel (86,64) nên đổi lần lượt exe dtech (86,64)

#### 2. Import excel nhưng cột ngày thể hiện không đúng theo excel. Hoặc có thể bị đảo ngày tháng hoặc có thể là điền mặc định thành ngày hiện tại

**Nguyên nhân**: là do format của cột hoặc ô excel đang không nhận được đúng kiểu MM/dd/yyyy. Cần format về kiểu date hoặc text format MM/dd/yyyy

* Sử dụng câu lệnh của excel: Text (ô giá trị cần xử lý, "MM/dd/yyyy)
* Tạo thêm 01 cột. Chạy câu lệnh phía trên
* Sau đó, copy cột chạy công thức, paste value vào cột đang cần xử lý

#### 3. Import excel gặp lỗi "Exception from HRESULT 0x800AC472"

* Thử tắt hết file excel đang mở và thử lại
* Nếu không được, thì bật taskManager lên, tìm tới dòng Excel và bấm Endtask. Rồi thử lại&#x20;

#### 4. Import excel Bán hàng, Khách hàng báo cột tiền không có giá trị, trong khi tệp excel thì có

* Kiểm tra lại tệp excel. Với giao diện bán hàng, tùy thuộc vào việc cấu hình sẽ có thể có: Cột thanh toán, thành tiền, tiền bản vị liên quan.&#x20;
* Thường khách hàng điền không hết các cột tiền liên quan, nên khi import vào sẽ bị thiếu thông tin

#### 5. Import excel xong, khách hàng lên sổ sách không thấy số liệu

* Kiểm tra lại thông tin excel: Hạch toán tài khoản sai không? cột tiền có giá trị không? ngày sai không?
* Vì lên báo cáo xem theo điều kiện quan trọng là: tài khoản, ngày, giá trị


# DANH MỤC

Xử lý các vấn đề liên quan danh mục


# INSERT BANK FROM KHACHHANG CONVERT

## Mô tả bài toán

* Khi convert dữ liệu cũ, thường sẽ khai báo ngân hàng trong danh mục khách hàng
* muốn chuyển vào danh mục bank thì làm thế nào

## Hướng dẫn

* Convert khách hàng lên đúng như bản cũ
* Sử dụng lệnh insert bank from select khachhang

```sql
-- gán dữ liệu ngân hàng từ bảng khách hàng sang bảng bank
insert BankAccount (SoTaiKhoan,ten,khachhang,donvi)
select tkNganhang,tenNganhang,khachhang,1
from khachhang
where tkNganhang<>''
```


# KIỂM TRA TÀI KHOẢN CÓ LỖI TRÊN DULIEUKETOAN

## 1. Kiểm tra

* Tài khoản không tồn tại
* Tài khoản hạch toán vào cấp mẹ

```sql
// Some code
select  t1.tkno,t2.ma, t2.ten
from 
(select tkno from vr group by tkno)T1
full outer join
(select * from taikhoan) t2 
on t1.tkno = t2.ma
where 
t2.ma is null  -- tài khoản không tồn tại trên danh mục tài khoản
or (t2.Cap=1 and t2.ma = t1.TKNO) -- tài khoản hạch toán vào mã mẹ
--or t1.TKNO is null
order by t1.tkno
```


# UPDATE BANKACCOUNT THEO TÀI KHOẢN KẾ TOÁN

## Ý nghĩa chức năng

Người dùng mở tiểu khoản 1121 và hạch toán kế toán theo tiểu khoản thay vì hạch toán theo danh mục ngân hàng

Kỹ thuật cần chuyển đổi các bút toán ngân hàng:&#x20;

* Tìm các bút toán liên quan 1121, update bổ sung cột bankaccount, vbankaccount
* update tiểu khoản về 1121
* xoá bỏ 1121 tiểu khoản trong danh mục tài khoản

## Hướng dẫn sử dụng

#### Bước 1: Backup dữ liệu + xuất báo cáo cân đối phát sinh để so sánh sau khi xử lý

* Sao lưu dữ liệu: An toàn dữ liệu và phòng sai sót khi xử lý.&#x20;
* Xuất báo cáo cân đối phát sinh: Dùng để đối chiếu so sánh với kết quả sau xử lý

#### Bước 2: Tạo danh mục ngân hàng và khai báo cột "tài khoản"

* Thêm mới danh mục ngân hàng dựa vào tiểu khoản của tài khoản kế toán 1121
* Khai báo cột "Tài khoản": Khai báo tài khoản tiểu khoản kế toán 1121 tương ứng&#x20;

#### Bước 3: Chạy câu lệnh xử lý dữ liệu gán bankaccount và vbankaccount

* Chậy câu lệnh sau để xử lý dữ liệu (Nếu sử dụng cột tknocu, tkcocu)

```sql
--1. bút toán chuyển tiền giữa các ngân hàng
--select benno,tknocu,tkcocu,bankaccount,vbankaccount,* from vr where tknocu like '112%' and tkcocu like '112%' and benno=1
update T1 set T1.bankaccount = T2.bankaccount
from (select * from dulieuketoan where tknocu like '112%' and tkcocu like '112%')T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tknocu = T2.taikhoan

update T1 set T1.vbankaccount = T2.bankaccount
from (select * from dulieuketoan where tknocu like '112%' and tkcocu like '112%')T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkcocu = T2.taikhoan

--2. bút toán thu ngân hàng
select benno,tknocu,tkcocu,bankaccount,vbankaccount,psno,psco,* from vr where tknocu like '112%' and tkcocu not like '112%' and psno>0
update T1 set T1.bankaccount = T2.bankaccount 
from (select * from dulieuketoan where tknocu like '112%' and tkcocu not like '112%') T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tknocu = T2.taikhoan

--3. bút toán chi ngân hàng
select benno,tknocu,tkcocu,bankaccount,vbankaccount,psno,psco,* from vr where tknocu like '112%' and tkcocu not like '112%' and psco>0
update T1 set T1.vbankaccount = T2.bankaccount, T1.bankaccount = T2.bankaccount
from (select * from dulieuketoan where tknocu not like '112%' and tkcocu like '112%') T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkcocu = T2.taikhoan
```

* Chạy câu lệnh sau để xử lý dữ liệu (Nếu sử dụng cột tkno, tkco)

```sql
// bút toán chuyển tiền giữa các ngân hàng
--select benno,tkno,tkco,bankaccount,vbankaccount,* from vr where tkno like '112%' and tkco like '112%' and benno=1
update T1 set T1.bankaccount = T2.bankaccount
from (select * from dulieuketoan where tkno like '112%' and tkco like '112%')T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkno = T2.taikhoan

update T1 set T1.vbankaccount = T2.bankaccount
from (select * from dulieuketoan where tkno like '112%' and tkco like '112%')T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkco = T2.taikhoan

-- bút toán thu ngân hàng
select benno,tkno,tkco,bankaccount,vbankaccount,psno,psco,* from vr where tkno like '112%' and tkco not like '112%' and psno>0
update T1 set T1.bankaccount = T2.bankaccount 
from (select * from dulieuketoan where tkno like '112%' and tkco not like '112%') T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkno = T2.taikhoan

-- bút toán chi ngân hàng
select benno,tkno,tkco,bankaccount,vbankaccount,psno,psco,* from vr where tkno like '112%' and tkco not like '112%' and psco>0
update T1 set T1.vbankaccount = T2.bankaccount, T1.bankaccount = T2.bankaccount
from (select * from dulieuketoan where tkno not like '112%' and tkco like '112%') T1 inner join 
(select * from bankaccount where bankaccount>0 and taikhoan<>'')T2 on T1.tkco = T2.taikhoan
```

#### Bước 4: Chuyển hạch toán tiểu khoản của 1121 về tài khoản 1121

* Sử dụng chức năng "Chuyển dữ liệu danh mục" để chuyển dữ liệu của tiểu khoản 1121 về tài khoản 1121

#### Bước 5: Xoá tài khoản tiểu khoản của 1121 và khai báo lại cột "tài khoản" trong danh mục ngân hàng

* vào danh mục ngân hàng, xoá bỏ nội dung ở cột "tài khoản"
* vào danh mục tài khoản, xoá bỏ dòng tiểu khoản của tài khoản 1121

#### Bước 6: Kiểm tra lại báo cáo

* Xuất báo cáo cân đối phát sinh sau khi xử lý
* So sánh với báo cáo cân đối phát sinh trước khi xử lý (bước 1)


# TẠO CỘT MÃ TĂNG TỰ ĐỘNG

Update lại cột mã cho khách hàng nếu muốn tăng tự động

1. **Tìm kiếm và update trên DTECH update cột mã vật tư**

<pre class="language-sql"><code class="lang-sql"><strong>-- Lấy cột Vattu là mã ngầm của danh mục Vật tư tăng dần để làm ID tăng dần
</strong><strong>select Ma,ten,vattu,'VT'+ FORMAT(Vattu, '000000') from Vattu
</strong>where vattu&#x3C;>0
order by vattu

Update Vattu
set Ma = 'VT'+ FORMAT(Vattu, '000000'),Modidate=getdate()
where vattu&#x3C;>0
</code></pre>

#### Đánh lại cột mã, bổ sung thêm số 00, không ảnh hưởng mã hiện tại thứ tự

<pre class="language-sql"><code class="lang-sql">

-- Đối tượng công nợ
<strong>--1. Kiểm tra trước
</strong>select Ma,ten,KhachHang,'NCC'+ FORMAT(TRY_CAST(REPLACE(ma, 'NCC', '') AS INT), '00000') from KhachHang
where KhachHang&#x3C;>0 and ma like 'NCC%' and charindex('0',ma)=0

--2. Update lại
Update KhachHang
set Ma = 'NCC'+ FORMAT(TRY_CAST(REPLACE(ma, 'NCC', '') AS INT), '00000'),Modidate=getdate()
where KhachHang&#x3C;>0 and ma like 'NCC%' and charindex('0',ma)=0

-- Vật tư hàng hóa:
--1. Kiểm tra trước

select Ma,ten,Vattu,'RC'+ FORMAT(TRY_CAST(REPLACE(ma, 'RC', '') AS INT), '00000') from vattu
where vattu&#x3C;>0 and ma like 'RC%' -and charindex('0',ma)=0


select Ma,ten,Vattu,'R'+ FORMAT(TRY_CAST(REPLACE(ma, 'R', '') AS INT), '00000') from vattu
where vattu&#x3C;>0 and ma like 'R%' and ma not like 'RC%' --and charindex('0',ma)=0

--2. Update lại

Update Vattu
set Ma = 'RC'+ FORMAT(TRY_CAST(REPLACE(ma, 'RC', '') AS INT), '00000'),Modidate=getdate()
where vattu&#x3C;>0 and ma like 'RC%' --and charindex('0',ma)=0

Update Vattu
set Ma = 'R'+ FORMAT(TRY_CAST(REPLACE(ma, 'R', '') AS INT), '00000'),Modidate=getdate()
where vattu&#x3C;>0 and ma like 'R%' and ma not like 'RC%'  --and charindex('0',ma)=0




</code></pre>

#### **2. Kiểu chữ cái trước và số sau: “KH001 > KH002 > … > KH999”**

<pre class="language-sql"><code class="lang-sql"><strong>--1. FUNCTION
</strong><strong>CREATE FUNCTION AUTO_IDKH()
</strong>RETURNS VARCHAR(5)
AS
BEGIN
	DECLARE @ID VARCHAR(5)
	IF (SELECT COUNT(MAKH) FROM KHACHHANG) = 0
		SET @ID = '0'
	ELSE
		SELECT @ID = MAX(RIGHT(MAKH, 3)) FROM KHACHHANG
		SELECT @ID = CASE
			WHEN @ID >= 0 and @ID &#x3C; 9 THEN 'KH00' + CONVERT(CHAR, CONVERT(INT, @ID) + 1)
			WHEN @ID >= 9 THEN 'KH0' + CONVERT(CHAR, CONVERT(INT, @ID) + 1)
		END
	RETURN @ID
END
--2. Thiết lập mặc định hàm DBO.AUTO_IDKH() cho trường MAKH
CREATE TABLE KHACHHANG(
	MAKH CHAR(5) PRIMARY KEY CONSTRAINT IDKH DEFAULT DBO.AUTO_IDKH(),
	HOTEN NVARCHAR(50) NOT NULL,
	SDT VARCHAR(11) NULL,
	DIACHI NVARCHAR(100) NULL,
	EMAIL VARCHAR(50) NULL
)
--Các bạn có thể tăng chiều dài mã bằng cách khai báo tăng chiều dài của MAKH và thêm nhiều nhánh CASE hơn.
</code></pre>

3. #### **Kiểu chỉ bao gồm chữ cái: AAAA > AAAB > … > ZZZZZ**

```sql
// Function tạo mã
CREATE FUNCTION TAOMAVE(@BEFORECODE CHAR(6))---65->90
RETURNS CHAR(6)
AS
BEGIN
	DECLARE @ONE AS SMALLINT, @TWO SMALLINT, @THREE SMALLINT, @FOUR SMALLINT, @FIVE SMALLINT, @SIX SMALLINT
	DECLARE @MAVE CHAR(6)
	SELECT @ONE=ASCII(LEFT(@BEFORECODE,1))
	SELECT @TWO=ASCII(SUBSTRING(@BEFORECODE,2,1))
	SELECT @THREE=ASCII(SUBSTRING(@BEFORECODE,3,1))
	SELECT @FOUR=ASCII(SUBSTRING(@BEFORECODE,4,1))
	SELECT @FIVE=ASCII(SUBSTRING(@BEFORECODE,5,1))
	SELECT @SIX=ASCII(RIGHT(@BEFORECODE,1))
	IF @SIX<90
		SELECT @SIX=@SIX+1
	ELSE
		BEGIN
			SELECT @SIX=65
			IF @FIVE<90
				SELECT @FIVE=@FIVE+1
			ELSE
				BEGIN
					SELECT @FIVE=65
					IF @FOUR<90
						SELECT @FOUR=@FOUR+1
					ELSE
						BEGIN
							SELECT @FOUR=65
							IF @THREE<90
								SELECT @THREE=@THREE+1
							ELSE
								BEGIN
									SELECT @THREE=65
									IF @TWO<90
										SELECT @TWO=@TWO+1
									ELSE
										BEGIN
											SELECT @TWO=65
											IF @ONE<90
												SELECT @ONE=@ONE+1
											ELSE
												RETURN NULL
										END				
								END			
						END		
				END	
		END
	SELECT @MAVE=CHAR(@ONE)+CHAR(@TWO)+CHAR(@THREE)+CHAR(@FOUR)+CHAR(@FIVE)+CHAR(@SIX)
	RETURN @MAVE
END
```

4. #### **Kiểu Ngày Tháng Năm + Chữ cái + Số tự động tăng: 01012001DV00001 > 02012001DV00002**

```sql
// Function
CREATE FUNCTION auto_iddv()
RETURNS VARCHAR(15)
AS
BEGIN
	DECLARE @id VARCHAR(15)
	IF (SELECT COUNT(MaDV) FROM DATVE) = 0
		SET @id = '0'
	ELSE
		SELECT @id = MAX(RIGHT(MaDV, 5)) FROM DATVE
		SELECT @id = CASE
			WHEN @id = 99999 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV00001'
			WHEN @id >= 0 and @id < 9 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV0000' + CONVERT(CHAR, CONVERT(INT, @id) + 1)
			WHEN @id >= 9 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV000' + CONVERT(CHAR, CONVERT(INT, @id) + 1)
			WHEN @id >= 99 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV00' + CONVERT(CHAR, CONVERT(INT, @id) + 1)
			WHEN @id >= 999 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV0' + CONVERT(CHAR, CONVERT(INT, @id) + 1)
			WHEN @id >= 9999 THEN CONVERT(VARCHAR,GETDATE(),112) + 'DV' + CONVERT(CHAR, CONVERT(INT, @id) + 1)
		END
	RETURN @id
END
```

5. **Cập nhật những dòng bị trống cột mã tăng dần theo cái có sẵn:**

```sql
declare @vitri int -- Vị trí những mã trống theo thứ tự tăng dần
declare @maxso int -- Mã hàng đang đánh số to nhất trên cột mã
declare @kytu nvarchar(30) -- Ký tự đầu của cột mã
declare @vattu int
--HH02672 Mã hàng đang đánh số to nhất trên cột mã
set @maxso = 2672
set @kytu=N'HH' 
declare bangvattu cursor for -- Bảng select ra những mã hàng bị trống cột Mã
--select ROW_NUMBER() OVER(ORDER BY ten ASC) AS vitri,ma,ten,vattu,donvi from vattu where ma='' and donvi=1 and vattu<>0
select ROW_NUMBER() OVER(ORDER BY ten ASC) AS vitri,vattu from vattu where ma='' and donvi=1 and vattu<>0
open bangvattu
Fetch next from bangvattu
	into @vitri,@vattu
While @@FETCH_STATUS=0
BEGIN
	print 'vitri: '+ CAST(@vitri as nvarchar)
	print 'Ma: '+@kytu+FORMAT(@maxso+@vitri,'00000')
	Update VatTu set ma=@kytu+FORMAT(@maxso+@vitri,'00000') where vattu=@vattu and donvi=1
	Fetch next from bangvattu
	into @vitri,@vattu
END

CLOSE bangvattu
DEALLOCATE bangvattu

--select ma,ten,VatTu,donvi from VatTu order by ma
```

6. Cập nhật lại mã đối tượng theo nhóm

```sql
declare @vitri int -- Vị trí những mã trống theo thứ tự tăng dần
declare @maxso int -- Mã hàng đang đánh số to nhất trên cột mã
declare @kytu nvarchar(30) -- Ký tự đầu của cột mã
declare @khachhang int

--HH02672 Mã hàng đang đánh số to nhất trên cột mã
set @maxso = 0
set @kytu=N'NCC.' 

declare bangkh cursor for -- Bảng select ra những mã hàng bị trống cột Mã
--select ROW_NUMBER() OVER(ORDER BY ten ASC) AS vitri,ma,ten,vattu,donvi from vattu where ma='' and donvi=1 and vattu<>0
select ROW_NUMBER() OVER(ORDER BY ten ASC) AS vitri,khachhang from khachhang where ma like 'NCC%' and donvi=1 and khachhang<>0
open bangkh
Fetch next from bangkh
	into @vitri,@khachhang
While @@FETCH_STATUS=0
BEGIN
	print 'vitri: '+ CAST(@vitri as nvarchar)
	print 'Ma: '+@kytu+FORMAT(@maxso+@vitri,'00000')
	Update khachhang set ma=@kytu+FORMAT(@maxso+@vitri,'00000'),modidate=getdate() where khachhang=@khachhang and donvi=1
	Fetch next from bangkh
	into @vitri,@khachhang
END

CLOSE bangkh
DEALLOCATE bangkh
```

7. update khachhang trống mã theo nhóm KH

```sql
---
declare @idMax int = 318
declare @kituDau nvarchar(10)='KH'
UPDATE T1 SET T1.MA= T2.MAMOI
--SELECT T1.*, T2.*
FROM 
(SELECT * FROM KHACHHANG) T1 INNER JOIN 
(
SELECT MA,TEN,KHACHHANG,
@kituDau + FORMAT( ROW_NUMBER() OVER(ORDER BY khachhang ASC) + @idMax, '0000000') AS MAMOI
FROM KHACHHANG
where khachhang<>0 and ma=''
)T2 ON T1.KHACHHANG = T2.KHACHHANG
```


# XOÁ BỎ CÁC DÒNG TRÙNG

Do import hoặc lỗi nào đó, dẫn đến có hiện tượng 1 đối tượng được mở làm nhiều dòng danh mục.

## Yêu cầu

* Xác định các dòng bị trùng nhau theo thông tin: Ví dụ, Trùng đối tượng công nợ theo cột mã số thuế
* Dồn dữ liệu phát sinh của các mã bị trùng về 1 mã
* Xoá các dòng danh mục đã dồn

## Hướng dẫn

* Tạo ra bảng tạm cte để xác định các dòng trùng theo yêu cầu và đánh số thứ tự
* Gộp dữ liệu các dòng stt> 1 về dòng có stt=1
* Xoá các dòng stt > 1

```sql
// Tạo bảng tạm cte gộp yếu tố trùng, đánh số stt 
WITH cte AS (
    SELECT ma,ten,vattu,identitykey, 
        ROW_NUMBER() OVER (PARTITION BY ma,ten ORDER BY identitykey) row_num
    FROM vattu
    Where vattu<>0 and ma<>''
)
// select bảng cte để kiểm tra
--select * from cte

// tạo câu lệnh select kiểm tra
--select T1.*, T2.vattu as vattuGoc from (select * from cte where row_num>1) T1 inner join (select * from cte where row_num=1)T2 on T1.ma=T2.ma and T1.ten=T2.ten

// tạo câu lệnh update dồn dữ liệu
update T1 set T1.vattu= T2.vattuGoc
from
(select * from dulieuketoan where vattu<>0) T1
inner join 
(
	select b1.*, b2.vattu as vattuGoc 
	from
	(select * from cte where row_num>1) b1 
	inner join 
	(select * from cte where row_num=1)b2 
	on b1.ma=b2.ma and b1.ten=b2.ten
)T2 
on T1.vattu = T2.vattu

// xoá các dòng có row_num> 1
delete cte where row_num>1
```


# XÓA BỎ DÒNG KHÔNG PHÁT SINH

## YÊU CẦU

* Nếu danh mục không phát sinh thì xóa bỏ

## HƯỚNG DẪN

* Code mặc định 1 danh mục muốn xử lý

<pre class="language-sql"><code class="lang-sql">-- chạy select  kiểm tra
-- okie thì comment select, mở delete
select  t1.ma,t1.ten,t1.VuViec as id_danhmuc, t2.vuviec
<strong>-- delete T1
</strong>from
(select * from vuviec where vuviec&#x3C;>0 and cap=0)T1 left join
(
	select vuviec 
	from dulieuketoan 
	where vuviec&#x3C;>0
	group by vuviec 
)T2 on t1.VuViec = t2.VuViec
where t2.VuViec is null
</code></pre>


# KHÔNG SỬ DỤNG DANH MỤC

## YÊU CẦU

* Đơn vị không sử dụng danh mục. Ví dụ, phân xưởng
* Nội dung cần xử lý
  * ẩn danh mục ở menu danh mục
  * ẩn cột trên giao diện nhập liệu: Giao diện chung, giao diện lưới
  * ẩn cột trên tìm kiếm
  * ẩn cột trên giao diện titbrow của danh mục khác nếu có liên kết
  * ẩn cột trên báo cáo nếu có sử dụng

## HƯỚNG DẪN

```sql
-- loại bỏ danh mục
SELECT * FROM TableManage WHERE TableName LIKE 'phanxuong'
update TableManage set stt=0,ModiDate=GETDATE()  WHERE TableName LIKE 'phanxuong'
-- ẩn cột trên tìm kiếm

-- ẩn cột trên giao diện titbrow của danh mục khác
select t1.*
from
(
SELECT TABLENAME FROM BrowConfig WHERE stt<>0 and FIELDNAME like 'phanxuong'
)T1 inner join
(
	select * from TableManage where stt<>0
)T2 on t1.TABLENAME = t2.TableName

-- ẩn cột trên báo cáo 
```


# MỞ RỘNG KÝ TỰ CHO CỘT MÃ

```
// Tăng ký 001 => 0001 do tràn ký tự
// sử dụng hàm STUFF(string gốc, vị trí chèn, số lượng ký tự xoá, ký tự mới cần chèn)
// số lượng ký tự xoá: nếu không xoá thì để =0
select ma,stuff(ma,7,0,'0'),vattu from vattu where ma like 'lodot.%'
update vattu set ma = stuff(ma,7,0,'0'),modidate=getdate() where ma like 'lodot.%'
```


# XỬ LÝ SỐ LƯỢNG QUY ĐỔI

## Yêu cầu

* dữ liệu kế toán nhập theo số lượng đơn vị phụ, nhập vào cột soluong,dongia,psno
* sau đó, khai báo tỷ lệ quy đổi đvt chính và đvt phụ
* Mong muốn

  * Cập nhật lại soluong,dongia => soluong2, dongia2
  * soluong = soluong2 \* tyle
  * dongia = psno / (soluong2 \* tyle)

## Thao tác

* update soluong2=soluong, dongia2=dongia

```sql
declare @nghiepvu int = 101
declare @tungay datetime = '01/01/2022'
declare @denngay datetime = '12/31/2022'
update T1 set T1.soluong2 = T1.soluong, T1.dongia2=T1.dongia2
from 
(
	select *
	from dulieuketoan
	where nghiepvu=@nghiepvu
	and tkno like '15%'
	and ngay>=@tungay and ngay<=@denngay
	and loaidl=0 
	and vattu in (26,21,25,22,23,24,173,172,46,44,102,87,171,14,99,96,100,98,122,37,31,35,30,125,126,36,39,29,43,32,33,41,95,92,94,93,45,91,28,40,42,27,38)
)T1
inner join 
(
	select vattu,dvdo,vdvdo,tyle,congthuc
	from dulieudvdo 
	where tyle<>0 and dvdo<>0 and vattu<>0 and congthuc like '*'
	and vattu in (26,21,25,22,23,24,173,172,46,44,102,87,171,14,99,96,100,98,122,37,31,35,30,125,126,36,39,29,43,32,33,41,95,92,94,93,45,91,28,40,42,27,38)
)T2
on T1.vattu=T2.vattu and T1.dvdo = T2.vdvdo and T1.dvdo <> T2.dvdo
```

* update soluong= soluong2 \* tyle, dongia = psno/(soluong2 \*tyle)

```sql

declare @nghiepvu int = 101
declare @tungay datetime = '01/01/2022'
declare @denngay datetime = '12/31/2022'
update T1 set T1.soluong = T1.soluong2 * T2.tyle, T1.dongia = T1.psno/(T1.soluong2*T2.tyle)
from 
(
	select *
	from dulieuketoan
	where nghiepvu=@nghiepvu
	and tkno like '15%'
	and ngay>=@tungay and ngay<=@denngay
	and loaidl=0 
	and vattu in (26,21,25,22,23,24,173,172,46,44,102,87,171,14,99,96,100,98,122,37,31,35,30,125,126,36,39,29,43,32,33,41,95,92,94,93,45,91,28,40,42,27,38)
)T1
inner join 
(
	select vattu,dvdo,vdvdo,tyle,congthuc
	from dulieudvdo 
	where tyle<>0 and dvdo<>0 and vattu<>0 and congthuc like '*'
	and vattu in (26,21,25,22,23,24,173,172,46,44,102,87,171,14,99,96,100,98,122,37,31,35,30,125,126,36,39,29,43,32,33,41,95,92,94,93,45,91,28,40,42,27,38)
)T2
on T1.vattu=T2.vattu and T1.dvdo = T2.vdvdo and T1.dvdo <> T2.dvdo
```

## TRƯỜNG HỢP 2:

**Mô tả:** Khách hàng nhập liệu ban đầu số dư, nhập mua hàng khai báo mặt hàng theo đơn vị tính to sau đó phát hiện cần chỉnh sửa xuống đơn vị nhỏ nhất để quản lý tồn kho, ví dụ Nhập vào trước là Két/Thùng sau đó cần đổi sang là Chai, Lon

**Phương án:**

* Sao lưu dữ liệu đánh dấu thời điểm hiện tại đang bị sai Dữ liệu quy đổi
* Yêu cầu khách hàng sửa lại và thực hiện khai báo quy đổi đơn vị tính trên danh mục vật tư về đơn vị nhỏ nhất, ví dụ là Chai/Lon
* Thực hiện chạy lệnh sql để tiến hành cập nhật cho đúng số lượng 2, đơn giá 2, => Số lượng, đơn giá

```sql
--1. Update Soluong2 = soluong,dongia2 =dongia, dvdo = DuLieuDvdo=vdvdo
-- Chú ý trên bảng DuLieuDvDo thì lấy cột vDvDo, bổ sung thêm điều kiện where
Update a
set a.Soluong2=a.soluong,a.dongia2=a.dongia,a.dvdo=b.vdvdo
from DuLieuKeToan a
inner join DuLieuDvdo b
on a.Vattu=b.Vattu
where a.NghiepVu=101
--and a.Vattu=47 and b.vattu=47
```

<pre class="language-sql"><code class="lang-sql"><strong>
</strong><strong>--2. Update lại số lượng = số lượng 2* tỷ lệ, đơn giá = Tiền/(số lượng 2* tỷ lệ)
</strong><strong>-- Bổ sung thêm điều kiện where
</strong><strong>update T1
</strong>set T1.soluong = T1.soluong2 * T2.tyle, T1.dongia = case when T1.Soluong2&#x3C;>0 then T1.psno/(T1.soluong2*T2.tyle) else 1 end
From DuLieuKeToan T1
inner join DuLieuDvdo T2
on T1.Vattu = T2.Vattu
Where T1.NghiepVu=101
--and T1.Vattu=47 and T2.vattu=47
</code></pre>

* Tìm kiếm lại dữ liệu nhập mua hàng xem đã điền đúng được 2 cột Số lượng,  số lượng phụ, đơn giá, đơn giá phú là được.


# UPDATE CHITIETTHEO

* update nhanh tài khoản cần quản lý chi tiết theo.
* Ví dụ: Tài khoản 641 cần quản lý theo khoản mục

```sql
// Some code
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',KhoanMuc#1' else 'KhoanMuc#1' end)
, chitiettheotext = chitiettheotext + (case when chitiettheotext<>'' then N', Khoản mục<Bắt buộc>' else  N'Khoản mục<Bắt buộc>' end)
, modidate=getdate()
where ma like '64%'
```

```sql
// khách hàng
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',KhachHang#1' else 'KhachHang#1' end)
, chitiettheotext = chitiettheotext 
+ (case when chitiettheotext<>'' then N', Đối tượng công nợ<Bắt buộc>' else  N'Đối tượng công nợ<Bắt buộc>' end)
, modidate=getdate()
where ma like '131%' and CHARINDEX(N'khachhang',chitiettheo)=0
```

```sql
// ngân hàng
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',BankAccount#1' else 'BankAccount#1' end)
, chitiettheotext = chitiettheotext 
+ (case when chitiettheotext<>'' then N', Ngân hàng<Bắt buộc>' else  N'Ngân hàng<Bắt buộc>' end)
, modidate=getdate()
where ma like '112%' and CHARINDEX(N'BankAccount',chitiettheo)=0
```

```sql
// Kho
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',Kho#1,SanPham#1' else 'Kho#1,SanPham#1' end)
, chitiettheotext = chitiettheotext 
+ (case when chitiettheotext<>'' then N', Kho<Bắt buộc>, Sản phẩm<Bắt buộc>' else  N'Kho<Bắt buộc>, Sản phẩm<Bắt buộc>' end)
, modidate=getdate()
where ma like '15%' and ma not like '154%' and CHARINDEX(N'sanpham',chitiettheo)=0
```

```sql
// Tài sản
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',TaiSan#1' else 'TaiSan#1' end)
, chitiettheotext = chitiettheotext 
+ (case when chitiettheotext<>'' then N', Tài sản<Bắt buộc>' else  N'Tài sản<Bắt buộc>' end)
, modidate=getdate()
where ma like '21%' and CHARINDEX(N'taisan',chitiettheo)=0
```

```sql
// phân bổ
update taikhoan 
set chitiettheo=chitiettheo+ (case when chitiettheo<>'' then ',PhanBo#1' else 'PhanBo#1' end)
, chitiettheotext = chitiettheotext 
+ (case when chitiettheotext<>'' then N', Chi phí chờ phân bổ<Bắt buộc>' else  N'Chi phí chờ phân bổ<Bắt buộc>' end)
, modidate=getdate()
where ma like '242%' and CHARINDEX(N'phanbo',chitiettheo)=0
```


# UPDATE CNHD BẢNG DULIEUTAIKHOAN

* Sang bản cus, có thể tài khoản công nợ mở tiểu khoản
* một số bản cũ chưa khai báo
* Tài khoản 131

```sql
--tài khoản 131
-- sửa lại groupBy cho phù hợp 
  --1.1 kiểm tra
  select ma
 ,'khachhang,sohd,serihd,mauhd,nhanvien,vung,phongban,phanxuong' as  [FieldGroupBy]
 , 'Max(case when vr.benno=1 then vr.ngayhd else ''01/01/1900'' end) as ngayhd' as [FieldAdd]
 ,'ngayhd,sohd' as [FieldOrderBy]
  from taikhoan
  where ma like '131%' and cap=0
  --1.2 insert
 insert into dulieutaikhoan (taikhoan,[FieldGroupBy],[FieldAdd],[FieldOrderBy],donvi)
 select ma
 ,'khachhang,sohd,serihd,mauhd,nhanvien,vung,phongban,phanxuong' as  [FieldGroupBy]
 , 'Max(case when vr.benno=1 then vr.ngayhd else ''01/01/1900'' end) as ngayhd' as [FieldAdd]
 ,'ngayhd,sohd' as [FieldOrderBy]
 ,1 as donvi
  from taikhoan
where ma like '131%' and cap=0
```

* Tài khoản 331

```sql
--tài khoản 331
-- sửa lại groupBy cho phù hợp  
  --1.1 kiểm tra
  select ma
 ,'khachhang,sohd,serihd,mauhd,nhanvien,vung,phongban,phanxuong' as  [FieldGroupBy]
 , 'Max(case when vr.benno=0 then vr.ngayhd else ''01/01/1900'' end) as ngayhd' as [FieldAdd]
 ,'ngayhd,sohd' as [FieldOrderBy]
  from taikhoan
  where ma like '331%' and cap=0
  --1.2 insert
 insert into dulieutaikhoan (taikhoan,[FieldGroupBy],[FieldAdd],[FieldOrderBy],donvi)
 select ma
 ,'khachhang,sohd,serihd,mauhd,nhanvien,vung,phongban,phanxuong' as  [FieldGroupBy]
 , 'Max(case when vr.benno=0 then vr.ngayhd else ''01/01/1900'' end) as ngayhd' as [FieldAdd]
 ,'ngayhd,sohd' as [FieldOrderBy]
 ,1 as donvi
  from taikhoan
where ma like '331%' and cap=0
```


# CODE PROC CỘT

* Lấy dữ liệu cột danh mục gán ra bên ngoài

```csharp
// Some code
int index = myform.grid.RowEdited;
		DataTable dt = myform.grid.dvSource.Table;
		DataRow drTuDiem = myform.grid.GetCurrentRowComboBoxCell(index, dt.Columns["TuDiem"].ColumnName);
		DataRow drDenDiem = myform.grid.GetCurrentRowComboBoxCell(index, dt.Columns["DenDiem"].ColumnName);
		string ten = drTuDiem["ten"].ToString()+" - "+drDenDiem["ten"].ToString();
		myform.grid.gridView1.SetRowCellValue(index, dt.Columns["ten"].ColumnName, ten);
```


# DỮ LIỆU KẾ TOÁN


# CONVERT CỘT ẢO SANG CỘT THẬT

Chuyển cột 'sohopdong' từ cột ảo sang cột thật 'chuongtrinh'

```sql
Update DuLieuKeToan set chuongTrinh = SoHopDong
from
(
select CASE WHEN MoreValue='' or CHARINDEX('>>>SoHopDong:',Morevalue,0)<=0 THEN N'' ELSE SUBSTRING(MoreValue+'>>>',charindex('>>>SoHopDong:',MoreValue,0)+LEN('>>>SoHopDong:'), charindex('>>>',MoreValue+'>>>',charindex('>>>SoHopDong:',MoreValue,0)+1)-charindex('>>>SoHopDong:',MoreValue,0)-LEN('>>>SoHopDong:')) END AS SoHopDong
,identityKey,ChuongTrinh
from dulieuketoan where nghiepvu in (1000003,1000004,1000005)
and MoreValue<>'' and MoreValue like '%sohopdong%'
)temp where temp.identityKey = DuLieuKeToan.identityKey
```


# CẬP NHẬT DIỄN GIẢI BÁN HÀNG

## Ý nghĩa

* Người dùng muốn trường diễn giải được cập nhật lại theo format = "Bán hàng cho @khachhang.ten theo hoá đơn @sohd"
* Nghiepvu
  * thông tư 88: 301, 1079
  * thông tư 200, 133:&#x20;

## Hướng dẫn

```sql
-- code cho bản 88
-- update diengiai của dòng chính và dòng phụ
update T1 set T1.diengiai = 
case when t1.psco<>0 then substring(T1.diengiai,1, charindex(']',T1.diengiai)) + N' Bán hàng cho '+ t2.ten + N' theo hoá đơn '+ t1.sohd
else N'Bán hàng cho '+ t2.ten + N' theo hoá đơn '+ t1.sohd end
from
(
select * from dulieuketoan 
where nghiepvu in (301,1079)
)T1 inner join 
(select * from khachhang where khachhang>0)T2
on T1.khachhang = T2.khachhang
```


# LƯƠNG


# CÁC LỖI THƯỜNG GẶP

## 1. Hạch toán lương sinh ra bút toán có giá trị trong khoảng \[1,12]&#x20;

* giá trị \[1,12] là tháng xử lý
* do khai báo tài khoản hạch toán default có giá trị nên sinh bút toán hạch toán, với giá trị là tháng ( cột tháng trên bảng lương)

```sql
//Câu lệnh kiểm tra giá trị
select identitykey,tenhienthi,ten,fielddefault,* from LuongConfig where nhomluong = 3 and fieldDefault<>''

/// select xong, nhìn dòng đầu tiên, thường là cột tháng, ko có tên hiển thị lấy giá trị cột identityKey để where update

update luongconfig set fieldDefault='', fieldDefault2='',fieldDefault3='',fieldDefault4='',fieldDefault5='',modidate=getdate()
where identitykey=93933
```


# LẤY HẠCH TOÁN LƯƠNG ĐỂ CẬP NHẬT

* Một số bản cài bị sai hạch toán tài khoản
* cần cập nhật lại tài khoản hạch toán mà không phải cập nhật lại toàn bộ bảng luongconfig

```sql
// Some code
Declare @query nvarchar(Max) = N''
select @query += 'Update LuongConfig set ModiDate=getdate(), FieldDefault = N'''+  REPLACE(FieldDefault, CHAR(13), '')+''',FieldDefault2 = N'''+REPLACE(FieldDefault2, CHAR(13), '')+''',FieldDefault3 = N'''+REPLACE(FieldDefault3, CHAR(13), '')+''',FieldDefault4 = N'''+REPLACE(FieldDefault4, CHAR(13), '')+''',FieldDefault5 =N'''+REPLACE(FieldDefault5, CHAR(13), '')+''' where NhomLuong=3 and ten = N'''+ten+''''+Char(10)+Char(13) from luongconfig where NhomLuong = 3 and (FieldDefault<>'' or FieldDefault2<>'' or FieldDefault3<>'' or FieldDefault4<>'' or FieldDefault5<>'')
select @query
```


# NGHIỆP VỤ


# CODE Ô DANH MỤC FORM CHUNG

* object danh mục/ kiểm tra danh mục có cột A không?

```sql
// kiểm tra danh mục khachhang có cột 'mdvqhns' không
// nếu có, thì gán giá trị sang object 
if(myform.txtKhachHangMa.CurrentRow.Table.Columns.Contains("MDVQHNS"))
				myform.txtMDVQHNS.Text = myform.txtKhachHangMa.CurrentRow["MDVQHNS"].ToString();
```


# THÊM OBJECT NHẬP LIỆU

## YÊU CẦU

* Thêm 01 object text trên form chung nhập liệu

## HƯỚNG DẪN

* Thêm cột vào cấu trúc dữ liệu
* Thêm brow cho bảng nhập liệu
* Vào nghiệp vụ/ 01 nghiệp vụ cụ thể/ cấu hình/ chọn "Trường thêm"
* Thoát phần mềm ra vào lại
* Vào nghiệp vụ/ 01 nghiệp vụ cụ thể/ cấu hình/ chọn "Thông tin chung" để ẩn hiện object lên form chung
* Nếu độ rộng objec chưa phù hợp, thì chuột phải vào object và sửa lại độ rộng
* Cập nhật sang khách hàng: form0002.form0002; control mới thêm (nếu có code, còn không thì không cần) và nghiepvu (ẩn hiện trên nghiệp vụ)


# TEMP NOTE PHÂN TÍCH

## Ý nghĩa chức năng <a href="#y-nghia-chuc-nang" id="y-nghia-chuc-nang"></a>

* temp dùng để lưu vết cấu trúc, nguyên tắc tạo báo cáo
* khi sửa đổi bổ sung thì nên chèn dòng và mô tả lý do tại sao

## Code tạo temp Phân tích

```sql
declare @nghiepvu int = 1034
select N'.I. Mô tả nội dung nghiệp vụ'+ char(13)
+ char(13) + N'.II. Chi tiết cấu hinh nghiệp vụ'+ char(13)
from nghiepvu where nghiepvu=@nghiepvu

union
select N'1. ID + Tên Nghiệp vụ:' 
+ char(13) + space(5) + convert(nvarchar(10),nghiepvu) +' - '+ vietnam
from nghiepvu where nghiepvu=@nghiepvu

union
select N'2. ẩn hiện chung:' 
+ char(13) + space(5)+ nghiepvu.FieldShow
from nghiepvu where nghiepvu=@nghiepvu

union
select N'3. ẩn hiện chi tiết:' 
+ char(13) + space(5) + nghiepvu.FieldDetail
from nghiepvu where nghiepvu=@nghiepvu

union
select N'4. ẩn hiện nút:' 
+ char(13) + space(5)+ case when AnHienNut<>'' then nghiepvu.AnHienNut else '-null' end
from nghiepvu where nghiepvu=@nghiepvu

union
select N'5. Đặt mặc định:' 
+ char(13) + space(5) + case when [SetDefault]<>'' then nghiepvu.[SetDefault] else '-null' end
from nghiepvu where nghiepvu=@nghiepvu

union
select N'6. Trường kiểm tra:' 
+ char(13) + space(5) + case when FieldKtra<>'' then nghiepvu.FieldKtra else '-null' end
from nghiepvu where nghiepvu=@nghiepvu

union
select N'7. Trường không được trống:' 
+ char(13) + space(5) + case when FieldNotEmpty<>'' then nghiepvu.FieldNotEmpty else '-null' end
from nghiepvu where nghiepvu=@nghiepvu

union
select N'8. Trường để lại sau khi lưu:' 
+ char(13) + space(5) + case when DeLai<>'' then nghiepvu.DeLai else '-null' end
from nghiepvu where nghiepvu=@nghiepvu

union
select N'9. Trường readonly:' 
+ char(13) + space(5)+ case when FReadOnly<>'' then nghiepvu.FReadOnly else '-null' end
from nghiepvu where nghiepvu=@nghiepvu
```

## Code so sánh ẩn hiện của form chung giữa 2 bản&#x20;

* Copy text ẩn hiện của bản số 1 và gán vào biến @fieldShowSosanh
* Chạy câu lệnh dưới để nhìn thấy sự khác biệt

```sql
-- Gán lại giá trị cho biến @fieldShowSosanh 
declare @fieldShowSosanh nvarchar(2000) = 'Ngay,0#ChungTu,1#PtVAT,5#SoHD,3#NgayHD,4#OngBa,6#'
declare @fieldShowHientai nvarchar(2000) =''
-- sửa lại giá trị nghiệp vụ
declare @nghiepvu int = 301
set @fieldShowHientai = (select fieldshow from nghiepvu where nghiepvu=@nghiepvu)

select T1.tencot as tencotHientai, T2.tencot as tencotSosanh from
(
select *
, substring(SplitItem,1, CHARINDEX(',', splititem)-1) as tencot
from [dbo].[SplitString](@fieldShowHientai, '#')
)T1 full outer join 
(
select *
, substring(SplitItem,1, CHARINDEX(',', splititem)-1) as tencot
from [dbo].[SplitString](@fieldShowSosanh, '#')
)T2
on T1.tencot =t2.tencot

```

## Code so sánh ẩn hiện của lưới chi tiết giữa 2 bản

* Copy text ẩn hiện của bản bản số 1 và gán vào biến @detailSosanh
* chạy câu lệnh dưới để nhìn thấy sự khác biệt ẩn hiện cột

```sql
-- gán lại giá trị cho biến @detailSosanh
declare @detailSosanh nvarchar(2000) = 'TkCo(388)TaiKhoan,ma,ma#soLuong(391)#donGia(392)#soInvoice(100)'
declare @detailHientai nvarchar(2000) =''
-- sửa lại giá trị biến nghiệp vụ
declare @nghiepvu int = 301

set @detailHientai = (select fielddetail from nghiepvu where nghiepvu=@nghiepvu)
select T1.tencot as tencotHientai, T2.tencot as tencotSosanh from
(
select *
, substring(SplitItem, 1, CHARINDEX('(', splititem)-1) as fieldName
, substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) )  as sourceName
, case when  substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) ) <>''
	then substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) ) 
	else substring(SplitItem, 1, CHARINDEX('(', splititem)-1)
	end as tencot
from [dbo].[SplitString](@detailHientai, '#')
)T1 full outer join 
(
select *
, substring(SplitItem, 1, CHARINDEX('(', splititem)-1) as fieldName
, substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) ) as sourceName
, case when  substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) ) <>''
	then substring(SplitItem, CHARINDEX(')', splititem) + 1, LEN(splitItem) - CHARINDEX(')', splititem) ) 
	else substring(SplitItem, 1, CHARINDEX('(', splititem)-1)
	end as tencot
from [dbo].[SplitString](@detailSosanh, '#')


)T2
on T1.tencot =t2.tencot

```


# CODE BROW CỘT

* Gán dữ liệu
* Lấy giá trị trên lưới
* Lấy giá trị trên form
* Lưu ý trường hợp copy paste nhiều dòng

```csharp
// tạo đối tượng dtg là lưới nhập liệu, để gọi cho thuận tiện
_DataGridView dtg = myform.DTG0001 as _DataGridView;
// lấy dòng hiện thời
DataRow drfocus = myform.DTG0001.CurrentRow;
// nếu copy paste thì lấy dòng đang edit
if(myform.DTG0001.RowEdited>= 0 && myform.DTG0001.RowEdited< myform.DTG0001.gridView1.RowCount)
	drfocus = myform.DTG0001.GetDataRow(myform.DTG0001.RowEdited);
// lấy danh mục của cột danh mục. thì lấy dòng index là dòng đang sửa
// với  myform.DTG0001.RowEdited là dòng đang sửa. 
// nếu CurrentRow thì sẽ là dòng tích chuột ban đầu, nếu copy paste nhiều dòng sẽ ko đúng
DataRow drKhachhang = dtg.GetCurrentRowComboBoxCell(  myform.DTG0001.RowEdited, "khachhang");	

if(drfocus["nhomvat"].ToString() == "6") // không đủ điều kiện khấu trừ
	{
	//Nếu không có cột tkno2 ở dưới lưới thì thông báo
		if(!myform.DTG0001.ContainsColumn("TkNo2"))
		{
		//
			_Func.ThongBaoLoi("Giao diện không có cột [Tk thuế] để ghi nhận sang tài khoản chi phí. Hãy báo lại Dtech thiết lập.");
			return;
		}

		if(drfocus.Table.Columns.Contains("TkNo2"))		
		{
			if(myform.NghiepVu.ToString() == "1047" || myform.NghiepVu.ToString() == "1035")
				{
					if(myform.DTG0001.ContainsColumn("TkCo"))//tk nợ dưới lưới
					drfocus["TkNo2"] = drfocus["TkCo"];
					else if(myform.txtTkhoanCo.Visible)//tk nợ trên form
					drfocus["TkNo2"] = myform.txtTkhoanCo.Value;
				}
				else
				{
					if(myform.DTG0001.ContainsColumn("TkNo"))//tk nợ dưới lưới
					drfocus["TkNo2"] = drfocus["TkNo"];
					else if(myform.txtTkhoanNo.Visible)//tk nợ trên form
					drfocus["TkNo2"] = myform.txtTkhoanNo.Value;
				}
				if(string.IsNullOrWhiteSpace(drfocus["TkNo2"].ToString()))
					_Func.ThongBaoLoi("Với hóa đơn không đủ điều kiện khấu trừ, bạn cần chọn thông tin cột [Tk thuế] là tài khoản chi phí");
			}
	}
	else
	{
		if(drfocus.Table.Columns.Contains("TkNo2"))
		{
			drfocus["TkNo2"] = "";
		}
	}
```

## lấy dòng danh mục của id đang chọn

```csharp
// lấy dòng danh mục hiện thời của id đang chọn trên giao diện lưới
_DataGridView dtg = myform.DTG0001 as _DataGridView;
DataRow drvattu = dtg.GetCurrentRowComboBoxCell(dtg.CurrentRowIndex, "VatTu");

// lấy dòng danh mục hiện thời của id đang chọn trên giao diện chung
if(myform.txtKhachHangMa.CurrentRow.Table.Columns.Contains("nhanvienGiamsat"))
		myform.txtnhanvienma.Value = myform.txtKhachHangMa.CurrentRow["nhanvienGiamsat"];

// kiểm tra cột có tồn tại dưới lưới
if(dtg.ContainsColumn("Kho"))
{
}
```


# CODE NÚT CHỨC NĂNG GÁN DỮ LIỆU XUỐNG LƯỚI

## Yêu cầu

* Tạo nút chức năng trên lưới
* Code nút gán dữ liệu nhiều dòng xuống lưới

## Hướng dẫn thao tác

* Chuột phải vào 1 object có sẵn của hàng nút chức năng trên lưới/ chọn nhân đôi
* chuột phải properties/ sửa name, text
* code chức năng. ở dưới là 2 ví dụ

### Ví dụ 1: code nút nhập số tiền CIF rồi phân bổ xuống lưới

```csharp
/*
		- phân bổ giá cif: bản chất là phí vận chuyển + bảo hiểm do người bán cung cấp. nên cần phân bổ để tính thuế nhập khẩu. 
		- bấm vào nút, hiển thị ô nhập tiền (format kiểu vnd)
		- bấm chấp nhận, thì phân bổ xuống cột cif ở dưới lưới. Theo tỷ lệ: cif_tong * psno / sum(psno) ở dưới lưới.
			+ lưu ý làm tròn số tiền lần cuối cùng.
		- tính lại psnoTinhthueHQ
			+ sau khi soluong*dongia*tygiaHQ
			+ kiểm tra xem hình như còn chi phí phân bổ trước hải quan. nếu có thì cif sau cả đoạn này
			+ cộng thêm cif vừa được phân bổ vào.
		- tính lại các cột liên quan
			+ các loại thuế phía sau thuexnk
		*/
		try
		{
		bool confirm = false; 
		decimal cif = 0;
		//tính tổng lại giá trị cif đã bổ nếu có
		// DataRow dr = _Func.GetFormValues(new Size(300, 400), _Func.DichNN("Nhập số dòng thêm"), 1, out confirm, "N", "Số dòng:#3#0#10");
		if(myform.DTG0001.dvSource.Table.Columns.Contains("CIF"))
		{
			cif = Convert.ToDecimal(myform.DTG0001.dvSource.Table.Compute("Sum(CIF)",""));
			DataRow dr1 = _Func.GetFormValues(new Size(300, 400), "Phân bổ giá CIF", 1, out confirm , "N"
		, "Tiền cif bổ: #18#0#"+ cif.ToString().Trim());
			if(confirm)
			{
				decimal cifbo = Convert.ToDecimal(dr1[0]);
				_Func.EditTable(myform.DTG0001.dvSource.Table,"CIF",0,"");
				if(cifbo >0)
				{
					decimal tongtieuthuc = 0;
					object resul = myform.DTG0001.dvSource.Table.Compute("Sum(PsNo)","PsNo>0");
					if(resul != DBNull.Value)
						tongtieuthuc = Convert.ToDecimal(resul);
					if(tongtieuthuc<=0)
					{
						_Func.ThongBaoLoi("Phí cif bổ theo tiền nên vui lòng nhập tiền của các dòng chi tiết trước khi bổ.");
						return;
					}
					DataRow[] drbo = myform.DTG0001.dvSource.Table.Select("PsNo>0");
					decimal conlai = cifbo;
					for(int i =0 ;i< drbo.Length; i++)
					{
						if(i== drbo.Length-1)
							drbo[i]["CIF"] = conlai;
						else
						{
							decimal gtbo = Math.Round(cifbo* Convert.ToDecimal(drbo[i]["PsNo"])/tongtieuthuc, 0, MidpointRounding.AwayFromZero);
							drbo[i]["CIF"] = gtbo;
							conlai -= gtbo;
						}
					}
				}
				//Chạy lại tính thuexnk cộng thêm phí CIf
				myform.CodeChungNL("ptthuexnk",true);
			}

		}
		
		}catch(Exception ex){MessageBox.Show(ex.Message);}
```

### Ví dụ 2: code nút Xuất vật tư theo báo cáo

```csharp
/*
		- XuatNguyenlieu_NWC: mẫu báo cáo sử dụng
		- chạy báo cáo: tungay,denngay= dauthang,cuoithang của vr.ngay trên giao diện
		- hiển thị brow bảng kết quả. cho phép người dùng chọn hoặc bỏ (dòng, toàn bộ bảng)
		- gán toàn bộ dữ liệu xuống dưới. khớp tên thì gán xuống. (báo cáo để các cột cần gán khớp với tên cấu trúc cột)
		*/
		DateTime tungay = new DateTime(myform.txtNgay.Value.Year, 1, 1);
		DateTime denngay = new DateTime(myform.txtNgay.Value.Year, 1, 1).AddYears(1).AddDays(-1);
		DataTable dtbrow = null;
		DataTable dtDLBC = _Func.ChayBaoCao(0, "XuatNguyenlieu_NWC", "DauBC=0", "", tungay, denngay, out dtbrow, myform);
		if(dtDLBC != null)
		{
			//Thêm cột ok để lựa chọn
			if(!dtDLBC.Columns.Contains("ok"))
			{
				dtDLBC.Columns.Add(new DataColumn() { ColumnName = "ok", DataType = typeof(int), 	DefaultValue = 0 });
				if(!dtDLBC.Columns.Contains("ColorRow"))
					dtDLBC.Columns.Add(new DataColumn() { ColumnName = "ColorRow", DataType = typeof(int), 	DefaultValue = 0 });
				dtDLBC.Columns["ColorRow"].Expression = "ok*2";
				//Thêm brow cột ok
				if(dtbrow != null && dtbrow.Rows.Count>0)
				{
					DataRow drbr = dtbrow.NewRow();
					drbr.ItemArray = dtbrow.Rows[0].ItemArray;
					drbr["Stt"] = 0.01;
					drbr["FieldName"] = "ok";
					drbr["VietNam"] = "ok";
					drbr["Type"] = "N";
					drbr["FromList"] = "0\n1";
					drbr["MultiRow"] = 1;
					dtbrow.Rows.Add(drbr);
				}

				_Func.EditTable(dtbrow, "Width", 80, "");
			}

			ViewTableConfig vtc = new ViewTableConfig();
			vtc.AllowEdit = true;
			vtc.Title = "Lựa chọn dữ liệu";
			vtc.ShowEmptyTable = true;
			vtc.CloseWhenDoubleClick = true;
			vtc.CloseWhenEnter = true;
			vtc.CloseWhenEscape = true;
			vtc.dtviewconfig = dtbrow;
			vtc.AutoSizeColumn = true;
			vtc.ButtonOnView = new string[] { "F7", "F4", "F12", "F5", "F3" };
			_Func.ViewTable(dtDLBC, vtc);
			if(dtDLBC.Select("ok=1").Length>0)
			{
				myform.DTG0001.dvSource.Table.Rows.Clear();
				foreach(DataRow dr in dtDLBC.Select("ok=1"))
				{
					DataRow drnew = myform.DTG0001.dvSource.Table.NewRow();
					//gán mặc định của nghiệp vụ khi thêm dòng mới
					myform.SetDefaultValueToRow(drnew);
					foreach(DataColumn col in dtDLBC.Columns)
					{
						if(drnew.Table.Columns.Contains(col.ColumnName) && dr[col.ColumnName] != DBNull.Value)
							drnew[col.ColumnName] = dr[col.ColumnName];
					}

					drnew.Table.Rows.Add(drnew);
				}

				myform.DTG0001.dvSource.Table.AcceptChanges();
				//Đẩy hiển thị các trường trên form
				if(dtDLBC.Columns.Contains("Kho"))
					myform.txtKhoMa.Value = Convert.ToInt32(dtDLBC.Select("ok=1")[0]["Kho"]);
			}
		}
		else
			_Func.ThongBao("Không có dữ liệu cần xuất. Hãy kiểm tra lại");
```


# PHIẾU IN NHẬP LIỆU


# cột ảo

```csharp
// Some code
		string fieldSeleThem = "benno,ngay,psno,ngoaite,tkno,tkco,loaitien,ongba,khthue,khachhang_ten,ngayhd,ptvat,dvdo,soluong2,MoreValue"; // liệt kê các trường thêm để truyền tham số ngoài các trường lựa chọn trong RPT file nếu có
		string ssele = _Func.GetStringSeleFromXReport(rpt, fieldSeleThem, swhere);
		string cotao = _Func.GetFieldFromMoreValue("thanhtien") +","+_Func.GetFieldFromMoreValue("tiengiamtru");
		ssele = ssele.Replace("DuLieuKeToan.[MoreValue]", cotao);
		DataTable dt = _Func.DBSelect(ssele);
		
```


# IN MẪU DẠNG GROUP

## YÊU CẦU

* Trên dữ liệu, các dòng dữ liệu thuộc 01 nhóm GroupIn
* Khi view in thì 01 nhóm GroupIn sẽ in thành 1 bộ chứng từ riêng
* Ví dụ:
  * Sổ chi tiết vật tư in liên tục: 01 vật tư khi in là 01 bộ chứng từ riêng biệt

## HƯỚNG DẪN

### Yêu cầu dữ liệu

* Có 01 cột dùng để xử lý in theo nhóm. Gọi là GroupIn
* Sắp xêp tuần tự theo GroupIn
* Trong GroupIn, sắp xếp theo 1 tiêu chí khác, để đảm bảo các lần in khác nhau đều được sắp xếp theo cùng 1 nguyên tắc

### Xử lý mẫu in

### 1. Tạo GroupHeader1 (Level0) để vẽ tiêu đề cột

* Thêm 01 groupHeader&#x20;

  <figure><img src="/files/rj419TVkduXFFWseGpbD" alt=""><figcaption><p>Tạo 01 groupHeader</p></figcaption></figure>

* Cấu hình group cho band vừa tạo&#x20;

  <figure><img src="/files/YaeozD3Uiz2Xn3Wgx3Ou" alt=""><figcaption><p>Cấu hình groupFields</p></figcaption></figure>

### 2. Tạo GroupHeader2 (Level1) để vẽ nội dung pageHeader

* Thêm 01 groupHeader&#x20;

  <figure><img src="/files/rj419TVkduXFFWseGpbD" alt=""><figcaption><p>Tạo groupHeader </p></figcaption></figure>

* Cấu hình group cho band vừa tạo&#x20;

  <figure><img src="/files/YaeozD3Uiz2Xn3Wgx3Ou" alt=""><figcaption><p>cấu hình groupFields</p></figcaption></figure>

### 3. Tạo GroupFooter để vẽ nội dung sum tổng cuối cùng của bảng

* Tạo groupFooter&#x20;

  <figure><img src="/files/7xrf6N0MhgsngEpVVqUL" alt=""><figcaption><p>Tạo 01 groupFooter - tạo nội dung sum tổng kết sau detail</p></figcaption></figure>
* Cấu hình ngắt trang giữa các group: Nếu không cấu hình thì các group sẽ in liên tục trên cùng 1 tờ giấy, mong muốn là kết thúc 1 group thì ngắt trang để in group mới&#x20;

  <figure><img src="/files/HNAWpFOpPh8fI17Xasx1" alt=""><figcaption><p>Cấu hình groupFooter - Ngắt trang</p></figcaption></figure>

### 4. Tạo detail - vẽ nộ dung lưới chi tiết

* Giữ nguyên detail&#x20;

### 5. Xử lý sum cột của detail trong mẫu in group

* Nếu không cấu hình, thì sum(cột) sẽ là sum(all dòng) của bảng dữ liệu thay vì là của các dòng trong group&#x20;

  <figure><img src="/files/wIRLcbxT0kAyLZwnqwx4" alt=""><figcaption><p>Cấu hình groupFooter - tính sum(cột) các dòng thuộc group</p></figcaption></figure>

* Xử lý đọc tiền ra chữ. Do không thể truyền thông qua biến parameter nên cần xử lý lại dataSource của phiếu in. Thêm cột docsorachu và gán nội dung vào để sử dụng trong mẫu in

```csharp
// Some code
/*
- do mẫu in theo group(kho) nên cần xử lý lại tienhang, docsorachu theo từng kho
- group kho
- sum tienhang theo kho, docsorachu theo kho
*/
if(!dt.Columns.Contains("tienhang"))
	dt.Columns.Add(new DataColumn() { ColumnName = "tienhang", DataType = typeof(int), DefaultValue = 0 });
if(!dt.Columns.Contains("docsorachu"))
	dt.Columns.Add(new DataColumn() { ColumnName = "docsorachu", DataType = typeof(string), DefaultValue = "" });
string sWhere = "";
decimal TienHang = 0, TienHangNgoaiTe = 0;
DataView dtDL = new DataView(dt);
DataTable dtKho = dtDL.ToTable(true, new string[] { "kho" });
if(_Vari.Developer || _Vari.Customer)
	{
		dtKho.TableName = "";
		_Func.ViewTable(dtKho);
	}
			
if(dtKho != null && dtKho.Rows.Count != 0)
	{
		for (int i = 0; i < dtKho.Rows.Count; i++)
			{
				swhere = "tkco like '15%' and khutrung<>3 and kho="+ dtKho.Rows[i]["kho"].ToString(); // nếu phiếu xuất thì đổi điều kiện
				//_Func.TextEditer(swhere);
				decimal.TryParse(dt.Compute("Sum(psno)", swhere).ToString(), out TienHang);
				//MessageBox.Show(TienHang.ToString());
				_Func.EditTable(dt, "tienhang", TienHang, "kho="+ dtKho.Rows[i]["kho"].ToString(), false);
				_Func.EditTable(dt, "docsorachu", formreport.BangChu(TienHang.ToString(), loaitien) , "kho="+ dtKho.Rows[i]["kho"].ToString(), false);
			}
	}
```


# Merger các ô của cột trên các dòng giống nhau

## Mô tả

* Ở bảng dữ liệu chi tiết detail, khách hàng muốn merge các ô theo 1 tiêu chí nào đó
* Hình ảnh minh hoạ:&#x20;

  <figure><img src="/files/gS3aiCrgqT3gdRpAjmtn" alt=""><figcaption><p>Mong muốn merge các ô giống nhau vào</p></figcaption></figure>

## Hướng dẫn

### Nguyên tắc

* Xác định tiêu chí để merger
* Nếu tiêu chưa sẵn có trên dữ liệu thì cần xử lý lại data truyền vào mẫu in
* Khi merge nên chọn merge theo:
  * Value
  * Tag

### Hướng dẫn

* Merge các ô detail theo cột stt được xử lý ở tiền xử lý: các dòng có stt giống nhau thì được merge

* Cấu hình merger: merge theo tag&#x20;

  <figure><img src="/files/3XWLErpJrC75u9tJ6HS8" alt=""><figcaption><p>Cấu hình xử lý trùng dữ liệu: Kiểu merge (gộp) và dựa vào tag (stt)</p></figcaption></figure>

* Cấu hình lại giá trị tag để phục vụ việc cấu hình merge theo tag: với tag là cột stt&#x20;

  <figure><img src="/files/Faa8R2smCKZmtnkC3Z5F" alt=""><figcaption><p>Khai báo tag là cột stt ( cột stt được xử lý ở tiền xử lý - các dòng giống nhau thì stt giống nhau)</p></figcaption></figure>

* Kết quả sau khi cấu hình&#x20;

  <figure><img src="/files/gS3aiCrgqT3gdRpAjmtn" alt=""><figcaption><p>Kết quả sau khi cấu hình merge theo tag (stt)</p></figcaption></figure>


# CÂU LỆNH FORMAT PHIẾU IN

## 1. Số lượng

* Số lượng ở cột

```csharp
// số lượng ở cột
Replace(Replace(Replace(FormatString('{0:#,0.00;;0.00}',[SoLuong]), ',', ' '),'.',','),' ','.')
```

* Số lượng ở sum cột

```csharp
// Số lượng ở sum cột
Replace(Replace(Replace(FormatString('{0:#,#.00}',sumSum([SoLuong])), ',', ' '),'.',','),' ','.')
```

* Số lượng ở báo cáo

```csharp
// xử lý được: (0,95 thay vì ,95) && (0.00=>trắng)
{0:#,0.00;(#,0.00); }

//Nó được chia làm 3 phần ngăn cách bởi dấu chấm phẩy ;
// tương ứng với: [Số dương] ; [Số âm] ; [Số không]
/*
1. Cấu trúc tổng quát
{0: Phần_Dương ; Phần_Âm ; Phần_Số_Không }

Phần 1: #,0.00 (Dành cho số dương)
#: Ký tự đại diện cho chữ số (Digit placeholder). 
Nếu số không có chữ số ở vị trí đó thì sẽ để trống (không hiện số 0 vô nghĩa ở đầu).
,: Dấu ngăn cách hàng nghìn (Thousand separator).
0.00: Buộc hiển thị ít nhất một chữ số trước dấu phẩy và luôn luôn hiện 2 chữ số thập phân.
Ví dụ: 1234.5 -> 1,234.50

Phần 2: (#,0.00) (Dành cho số âm)
Dấu ngoặc đơn (...): Định dạng này thường dùng trong kế toán tài chính. Thay vì hiện dấu trừ -, số âm sẽ được bao lại trong dấu ngoặc.
Quy tắc hiển thị số bên trong giống hệt phần số dương.
Ví dụ: -1234.5 -> (1,234.50)

Phần 3:   (Dành cho số 0)
Khoảng trắng: Đây là một thủ thuật rất hay trong báo cáo.
Nếu giá trị bằng 0, chương trình sẽ không hiển thị gì cả 
(chỉ hiện một khoảng trắng). 
Điều này giúp báo cáo trông sạch sẽ hơn, người xem không bị rối mắt bởi quá nhiều số 0.
*/
```

## 2. Đơn giá

```
// Đơn giá cột
Replace(Replace(Replace(FormatString('{0:#,#.00}',[DonGia]), ',', ' '),'.',','),' ','.')
```

## 3. Thành tiền việt nam

```
// Thành tiền cột
Replace(Replace(Replace(FormatString('{0:#,#}',[PsNo]), ',', ' '),'.',','),' ','.')
// Thành tiền ở sum cột
Replace(Replace(Replace(FormatString('{0:#,#}',sumSum([PsNo])), ',', ' '),'.',','),' ','.')
```

## 4.KIỂU NGÀY

```
FormatString('{0:dd/MM/yyyy}', [Hansudung])

Trim([VatTu_Ten])+Char(13)+'- Lô sản xuất: '+[losanxuat]+char(13)+'- Hạn dùng: '+ Iif(GetYear([Hansudung])<>1900, FormatString('{0:dd/MM/yyyy}', [Hansudung]) ,'')
```

```sql
Trim([VatTu_Ten])+NewLine()+ ' - Lô sản xuất: ' + Trim([Losanxuat])+     NewLine()+ ' - Hạn sử dụng: '+ Iif(GetYear([Hansudung])<>1900, FormatString('{0:dd/MM/yyyy}',[Hansudung]),' /  /' )
```

```
// công thức object phiếu in
Iif(GetYear([NgayHD])<>1900,[NgayHD] ,'' )
// format string của object để hiển thị kiểu ngay dạng dd/MM/yyyy
{0:dd/MM/yyyy}
// format string của object để hiện thị kiểu thời gian Giờ:Phút
{0:HH:mm}
// format datetime đầy đủ
{0:MM/dd/yyyy h:mm tt}
// ghi chú; 
Nếu dùng Expression: FormatString('{0:HH:mm}', [NgayChungTu])
Nếu dùng Text Format String: {0:HH:mm}

```

<figure><img src="/files/SgbRk4s1Xd0CSJBTRhWH" alt=""><figcaption></figcaption></figure>

## 5. Format nhiều loại font chữ trong 1 đoạn text&#x20;

* Với label thì không xử lý được
* Chuyển sang Richtext
* Richtext thì sau khi gõ nội dung gồm: Text + Tên trường + tên biến thì có thể format
  * Bôi đen nội dung và format font đoạn cần xử lý
  * richtext chỉ sử dụng text, cột dữ liệu, biến. Không áp dụng công thức, + chuỗi


# Lặp lại tiêu đề in + logo... ở trang 2

## Yêu cầu chức năng

* Trang n+1 sẽ lặp lại:
  * Logo, địa chỉ công ty: dạng page header
  * Tiêu đề mẫu in
  * Tiêu đề cột
* Trang n+1 sẽ không lặp lại
  * Nội dung ở dưới tiêu đề in&#x20;

    <figure><img src="/files/MPx3S4ojFMKHIE40PdBM" alt=""><figcaption><p>Yêu cầu mẫu in</p></figcaption></figure>

## Hướng dẫn thao tác

### 1. Nguyên tắc chung

* Nội dung lặp lại ở đầu trang: đưa vào pageHeader
* Nội dung trong pageHeader mà muốn có 2 nhóm: 01 nhóm chỉ xuất hiện ở trang 01. nhóm khác thì hiển thị lặp lại ở trang n+1 thì sử dụng thêm sub-band của pageHeader

### 2. Thao tác

<figure><img src="/files/s3g8Wp0Q2A1QogZDPlKY" alt=""><figcaption><p>Cấu hình thêm sub-band và đặt ẩn hiện cho sub-band chi tiết (sub-band1)</p></figcaption></figure>


# Lặp lại tiêu đề in

* Người dùng muốn lặp lại tiêu đề cột ở các trang

<figure><img src="/files/DMkLBUpMlBJrSQMk6auF" alt=""><figcaption></figcaption></figure>


# XỬ LÝ CHÂN KÝ SANG TRANG

## VẤN ĐỀ

* Khi in ấn, phần tổng kết và chân ký bị sang trang mà không có nội dung của detail đi cùng
* detail toàn bộ nằm ở trang trước đó
* Mong muốn
  * Chân ký phải có liên quan tới nội dung detail

## GIẢI PHÁP

* Do report footer không có thuộc tính keepTogether nên không thể gắn detail và footer cùng nhau
* Sử dụng GroupFooter thay thế cho ReportFooter với thuộc tính:
  * GroupUnion: WithLastDetail (gắn với dòng cuối của detail)
  * KeepTogether: Check true

## HƯỚNG DẪN

* Chuột phải vào reportFooter, chọn inserBand/ Chọn GroupHeader và GroupFooter&#x20;

  <figure><img src="/files/uHOtKDN2WfvqFRDhNefB" alt=""><figcaption><p>Add thêm GroupFooter</p></figcaption></figure>
* Cấu hình cho GroupFooter 2 thuộc tính:
  * GroupUnion: WithLastDetail&#x20;
  * KeepTogether: Check true
* Các khoảng trắng thừa kéo sát lại&#x20;

  <figure><img src="/files/oY6yq75LlhD1McoT84FL" alt=""><figcaption><p>Cấu hình </p></figcaption></figure>


# TÌM KIẾM DỮ LIỆU


# CHỌN LOAIDL= ALL

* Muốn chọn điều kiện loaidl là tất cả thay vì chọn từng loại dữ liệu
* Chạy code tạo id = tất cả và copy exe mới nhất (thoát ra vào lại)

```
-- copy code sql để tạo dòng loaidl=tất cả
if(not exists(select 1 from LoaiDL where LoaiDL =-1))
	insert into LoaiDL(LoaiDL,Ten,ModiDate,CreateDate) Values(-1,N'<Tất cả>',GETDATE(),GETDATE())
	
```


# KẾT NỐI HTKK


# MAPING TỪ 1 BÁO CÁO

## Ý NGHĨA CHỨC NĂNG

* Maping 1 xml có form cố định từ báo cáo cố định (báo cáo dạng Fix)

## HƯỚNG DẪN SỬ DỤNG

### 1. XÁC ĐỊNH THÔNG TIN

* Xác định file xml + báo cáo ghép vào
* Xác định key xml và key báo cáo
  * xml thường lấy key là "path": là thẻ xml&#x20;
  * key báo cáo: báo cáo dạng fix thường có 1 cột là id dòng. Ví dụ: mã số, stt, thuyết minh
* Tên file xml khi sinh ra sau khi xử lý
  * Thường sẽ là tên file xml gốc làm temp + chèn thêm text thông tin: Tháng, năm, cơ quan quản lý thuế... tuỳ vào giao diện htkk
  * Kỹ thuật thường sẽ tạo htkk tay và xem nguyên tắc tạo tên cho file, từ đó xác định được nội dung cần chèn thêm, vị trí chèn

### 2. XỬ LÝ

* Tạo dòng quản lý trong htkk api
* Maping giữa xml và báo cáo&#x20;

  <figure><img src="/files/zjVPpnmU0NYQjreplN1u" alt=""><figcaption><p>Maping xml </p></figcaption></figure>


# MAPING TỪ NHIỀU BÁO CÁO


# BÁO CÁO


# CÔNG THỨC

## 1. công thức cột gọi tên danh mục liên kết

danh mục tài sản, có cột phân xưởng.&#x20;

Trên báo cáo tài sản, cần lấy ra thông tin tên cột phân xưởng

```sql
-- Giá trị, kiểu cộng Max()
/*joindmgocPhanXuong_Taisan_PhanXuongend*/PhanXuong.Ten as PhanxuongTen
-- điều kiện: để trống
-- gộp thêm: không. 

---- danh mục Đối tượng
/*joindmgocNhomkhachhang_Khachhang_Nhomkhachhangend*/Nhomkhachhang.Ten as NhomkhachhangTen

---- danh mục Vật tư
/*joindmgocNhomVattu_Vattu_Nhomvattuend*/Nhomvattu.Ten as NhomvattuTen

---- danh mục phân bổ chi phí trả trước
/*joindmgocPhanxuong_Phanbo_Phanxuongend*/Phanxuong.Ten as PhanxuongTen
/*joindmgocPhongban_Phanbo_Phongbanend*/Phongban.Ten as PhongbanTen
/*joindmgocVuviec_Phanbo_Vuviecend*/Vuviec.Ten as VuviecTen
/*joindmgocCongtrinh_Phanbo_Congtrinhend*/Congtrinh.Ten as CongtrinhTen
/*joindmgocKhoanmuc_Phanbo_Khoanmucend*/Khoanmuc.Ten as KhoanmucTen

---- danh mục Tài sản
/*joindmgocPhanxuong_Taisan_Phanxuongend*/Phanxuong.Ten as PhanxuongTen
/*joindmgocPhongban_Taisan_Phongbanend*/Phongban.Ten as PhongbanTen
/*joindmgocVuviec_Taisan_Vuviecend*/Vuviec.Ten as VuviecTen
/*joindmgocCongtrinh_Taisan_Congtrinhend*/Congtrinh.Ten as CongtrinhTen
/*joindmgocKhoanmuc_Taisan_Khoanmucend*/Khoanmuc.Ten as KhoanmucTen
/*joindmgocLoaitaisan_Taisan_Loaitaisanend*/Loaitaisan.Ten as LoaitaisanTen
```

## 2. Công thức hàm \_fun.EditTable khi chia 0

Trường hợp áp dụng công thức A/B thì cần xử lý vấn đề chia 0

```sql
-- Some code
_Func.EditTable(dtDL, "ketqua","iif(B=0,0,A/iif(B=0,1,B))","1=1",true);
```

## 3. Công thức hàm \_fun.EditTable khi muốn round(0)

Trường hợp áp dụng công thức round(A/B,0)

```sql
-- Some code
_Func.EditTable(dtDL,"ketqua","Convert( iif(B=0,0,A/iif(B=0,1,B)),'System.Int32')","1=1",true);
```

## 4. Công thức hàm \_fun.EditTable khi muốn convert(string)

```
// Some code
_Func.EditTable(dtDL, "vtrangthaixulyten", dvTrangthai[i]["ten"].ToString(), "Convert(trangthaixuly, 'System.String')='"+ dvTrangthai[i]["trangThaiXuLy"].ToString()+"'", false);
```

## 5. Công thức groupby

```
// Some code
//DataTable dtKhoNhomkho = _Func.GroupBy(dtDL, cacCotGroup, doiVitriCacCotGroup, cacCotTinhtoan, kieuCong, capindam, strAdd ,where);
DataTable dtKhoNhomkho = _Func.GroupBy(dtDL, "khoTen,nhomkhoTen,kho,nhomkho", "", "", "", "1,1", "", "");
```

## 6. Công thức tạo table group từ view

```
// Some code
DataView dvDL = new DataView(dtDL);
DataTable dtGop = dvDL.ToTable(true, "kho", "nhomkho");
```

## 7. Format kiểu số lượng trong mẫu in báo cáo

```
// trong báo cáo, chỉ cần đặt công thức là tên cột
// format thì đặt ở format string của object
// summury: none
// 1. format kiểu số lượng
{0:#,0.00;(#,0.00); }
// 2. format kiểu tiền
{0:#,#;(#,#);#}
// 3. format đơn giá
{0:#,#.00;(#,#.00);#}
```

## 8. hàm chuyển từ ngày-đến ngày thành text ngaythangnam

```
// Some code
// sử dụng hàm: formreport.ThangQuyNamText(DateTime tungay, DateTime denngay, string ngonngu)
string ngayThangNam = formreport.ThangQuyNamText(Convert.ToDateTime(formreport.TuNgay), formreport.DenNgay, "vietnam");
rpt.Parameters["NgayThangNam"].Value = ngayThangNam.ToString();

```

## 9. Hàm convert datetime công thức cột

```
// cột báo cáo, cần add kiểu thời gian mặc định
// nếu để '01/01/1900' thì sẽ hiển thị xấu. muốn để trống
// sửa cột giá trị, để như sau.
Convert(datetime,'01/01/1900') as Ngay
```

### 10. Lấy text tháng, quý, năm từ giao diện báo cáo

* Muốn lấy text thể hiện thời gian ở điều kiện xem báo cáo: tháng, quý, năm

```csharp
//-- nếu ở phiếu in thì là formreport
//-- nếu ở trước tất cả thì là formbaocao
string ngayLap = "";
		if(formreport.lblTuNgay.Text.Trim().ToUpper() == "NĂM")
		{
			ngayLap = "Năm " +formreport.TuNgay.Year.ToString();
		}
		if(formreport.lblTuNgay.Text.Trim().ToUpper() == "THÁNG")
		{
			ngayLap = "Tháng " + formreport.TuNgay.Month.ToString() + "/" + formreport.TuNgay.Year.ToString();
		}
		else if(formreport.lblTuNgay.Text.Trim().ToUpper() == "QUÝ")
		{
			ngayLap = "Quý " + formreport.txtQuy.Text + "/" + formreport.TuNgay.Year.ToString();
		}
		
		else if(formreport.lblTuNgay.Text.Trim().ToUpper() == "TỪ")
		{
			if(formreport.TuNgay.Month == formreport.DenNgay.Month)
				ngayLap = "Tháng " + formreport.TuNgay.Month.ToString() + "/" + formreport.TuNgay.Year.ToString();
			else if(formreport.DenNgay.Month - formreport.TuNgay.Month == 3)
					ngayLap = "Quý " + formreport.txtQuy.Text + "/" + formreport.TuNgay.Year.ToString();
			else
				ngayLap = "Từ - " + formreport.TuNgay.ToString("dd/MM/yyyy")+ "    Đến - "+ formreport.DenNgay.ToString("dd/MM/yyyy");
				
		}
```


# CÔNG THỨC SQL THƯỜNG DÙN

### 1.  Chuyển kiểu ngày là số sang string 2 ký tự

```
-- sử dụng format(@number, '00')
select format(day('01/05/2026'),'00')
-- chuyển kiểu số thành ký tự
SELECT CAST(1029 AS VARCHAR(10)) AS KetQua;
SELECT CONVERT(VARCHAR(10), 1029) AS KetQua;
SELECT CONVERT(VARCHAR(10), TenCotSo) AS KetQua FROM TenBang;
```


# BÁO CÁO DYNAMIC ĐỘNG

## 1. TIỀN XỬ LÝ

```csharp
// Some code

public DataTable DMain(DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		//Viết code dưới dòng này(chỉnh sửa cấu trúc hiển thị báo cáo)
		/*
		- xử lý vấn đề bên nợ 154: đối ứng với 62,154 trong kỳ
		- 01 tkdoiung, có 2 nhóm chính: đích danh và phân bổ
		*/
		string nhomcot = "";
		nhomcot = drReport["nhomcot"].ToString(); // lấy nhóm cột gõ tay nếu có
		// nhomcot(col1,col2)0#
		try {			
			#region: xử lý bên có 154

			if(dtTaocot != null && dtTaocot.Rows.Count != 0)
			{
				for (int i = 0; i < dtTaocot.Rows.Count; i++)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = dtTaocot.Rows[i]["tkco"].ToString().Trim();
					dr["giatri"] = "case when vr.benno=0 then vr.psco else 0 end as TKC_"+ dtTaocot.Rows[i]["tkco"].ToString();
					dr["dieukien"] = "vr.tkco like '" + dtTaocot.Rows[i]["tkco"].ToString()+"' and trongky()";
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}
			}
			#endregion: xử lý bên có 154
		}
		
		catch(Exception ex) { MessageBox.Show(ex.Message); }
		drReport["nhomcot"] = nhomcot; // gán lại nhóm cột mới
		return dtColumns;
	}
	
public string dynamicTen(string strTen)
	{
		// strTen = "Tháng " + strTen;
		return strTen;
	}

	// hàm tính giá trị
	public string dynamicGiaTri(string strGiaTri)
	{
		// strGiaTri = "case when KhoanmucTien.tinhchat=1 or vr.khoanmucTien=0 then VR.PSNO - VR.PSCO else VR.PSCO - VR.PSNO end as TH_"+ strGiaTri.Trim();
		return strGiaTri;
	}

	// hàm tính điều kiện
	public string dynamicDieuKien(string strDieuKien)
	{
		// int thang = Convert.ToInt32(strDieuKien.Split('_')[0].ToString());
		// int nam = Convert.ToInt32(strDieuKien.Split('_')[1].ToString());
		// strDieuKien = "ngay>='"+ new DateTime(nam, thang , 01).ToString("MM/dd/yyyy")+"'";
		// strDieuKien += " and ngay<='"+ new DateTime(nam, thang , 01).AddMonths(1).AddDays(-1).ToString("MM/dd/yyyy")+"'";
		return strDieuKien;
	}
	
	public string dynamicTen1(string strTen)
	{
		strTen = "Tháng 1" ;
		return strTen;
	}

	// hàm tính giá trị
	public string dynamicGiaTri1(string strGiaTri)
	{
		strGiaTri = "0";
		return strGiaTri;
	}

	// hàm tính điều kiện
	public string dynamicDieuKien1(string strDieuKien)
	{
		strDieuKien = "1=1";
		return strDieuKien;
	}
```

## 2. XỬ LÝ CÁC CỘT TRONG CÔNG THỨC - ẨN HIỆN, THỨ TỰ...

```csharp
// thêm cột khonghien. để ẩn hiện cột dynamic
if(!_Vari.FormChinh.dtDynamicReportColumn.Columns.Contains("khonghien"))
			_Vari.FormChinh.dtDynamicReportColumn.Columns.Add("khonghien", typeof(int));
// thêm cột stt, sắp xếp thứ tự cột dynamic			
if(!_Vari.FormChinh.dtDynamicReportColumn.Columns.Contains("stt"))
			_Vari.FormChinh.dtDynamicReportColumn.Columns.Add("stt", typeof(decimal));			
```

## 3. TRƯỚC TẤT CẢ - KQKD KHOẢN MỤC + PHÒNG BAN

```csharp
// Some code
public DateTime TuNgay, DenNgay;
	public DataTable dtBaocaoPhu;
	public string nhomcot;
	public DataTable dtnhom = new DataTable();// bảng lưu dữ liệu phục vụ tính nhóm cột
	public DataTable DMain(DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		// //Viết code dưới dòng này(chỉnh sửa cấu trúc hiển thị báo cáo)
		TuNgay = formBaoCao.TuNgay;
		DenNgay = formBaoCao.DenNgay;
		string where = formBaoCao.GetWhereCondition(false);//.Replace("where", "").Replace("WHERE", "");
		dtBaocaoPhu = formBaoCao.ChayBaoCao(0, "BCP_KQKD_B", "Daubc=0", where, formBaoCao.TuNgay, formBaoCao.DenNgay);
		formBaoCao.AddTable("BaoCaoPhu", dtBaocaoPhu);
		// nhomcot = "Chứng từ(chungtu,ngaychungtu)0#"+ nhomcot ;
		// drReport["nhomcot"] = nhomcot;

		#region: Tính toán nhóm cột cho báo cáo bsc par
		dtnhom.Columns.Add("From", typeof(string));
		dtnhom.Columns.Add("To", typeof(string));
		dtnhom.Columns.Add("TenNhom", typeof(string));
		formBaoCao.Tag = dtnhom;// lưu lại bảng dtnhom trên form báo cáo
		#endregion: Tính toán nhóm cột cho báo cáo bsc par
		return dtColumns;
	}

	// hàm tính giá trị
	public string dynamicRowGiaTri(string strGiaTri)
	{
		// MessageBox.Show("gt: "+ strGiaTri);
		return strGiaTri;
	}

	// 24217
	// 24219
	public string dynamicCol(string strGT, string ctCotTruoc, string tencottruoc, string tencotnay, DataRow drReportFix)
	{
		// strGT là giá trị cột giá trị của dòng trong dtDynamicReportColumn
		// ctCotTruoc là giá trị công thức cột trước cột đang động cột
		// tencottruoc là columnName cột trước cột tự động
		// tencotnay là cột hiện tại đang tự động tính nhằm replace các biểu thức tính sau cho chính xác tên cột mình cần xử lý
		// drReportFix là dòng dữ liệu trong báo cáo fix trường hợp không lấy cột liền trước có thể gọi các cột liền trước để lấy công thức cần dùng
		DataView dvBaocaophu = new DataView(dtBaocaoPhu);

		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			// DateTime tungay = formBaoCao.TuNgay;
			// DateTime denngay = formBaoCao.DenNgay;
			int sothang = (DenNgay.Year*12 + DenNgay.Month)-(TuNgay.Year*12 + TuNgay.Month)+1;
			DataTable dtPhongban = _Func.GetTable("phongban").Copy();
			DataView dvPhongban = new DataView(dtPhongban);
			DataRow dr;
			string nhomcotthang = "";// lưu text tiêu đề nhóm cột vào dòng đầu tiên bảng nhomcot
			for (int i = 0; i < sothang; i++)
			{
				dvBaocaophu.RowFilter = "thang =" + (TuNgay.AddMonths(i).Year*12 + TuNgay.AddMonths(i).Month).ToString(); // filter những phòng ban của tháng đang duyệt
				if(dvBaocaophu.Count != 0)
				{
					nhomcot += "Tháng_"+ TuNgay.AddMonths(i).Month.ToString()+"_"+ TuNgay.AddMonths(i).Year.ToString()+"(";
					DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "phongban" });
					if(dtBaocaophu0.Rows.Count != 0)
					{
						for (int iij = 0; iij < dtBaocaophu0.Rows.Count; iij++)
						{
							dvPhongban.RowFilter = "phongban = " + dtBaocaophu0.Rows[iij]["phongban"].ToString()+ (dtPhongban.Columns.Contains("donvi")?" and donvi="+ _Vari.Donvi:"");
							dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
							//dr["ten"] = dvPhongban[0]["ten"].ToString()+" Tháng_" + TuNgay.AddMonths(i).Month.ToString()+"_"+ TuNgay.AddMonths(i).Year.ToString();// điền tên cần hiển thị thêm trong report fix
							dr["ten"] = (string.IsNullOrWhiteSpace(dvPhongban[0]["ten"].ToString())? "Trống phòng ban" : dvPhongban[0]["ten"].ToString());// điền tên cần hiển thị thêm trong report fix
							dr["giatri"] = dtBaocaophu0.Rows[iij]["phongban"].ToString() +"_"+ (TuNgay.AddMonths(i).Year*12 + TuNgay.AddMonths(i).Month).ToString();// điền giá trị dùng để truyền đầu vào hàm dynamicCol để xử lý
							_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
						}
					}
				}

				nhomcotthang+= "Tháng "+ TuNgay.AddMonths(i).ToString("MM/yyyy").Trim()+"#";
			}

			// //_Func.TextEditer("nhomcotthang: "+nhomcotthang);
			// this.dtnhom.Rows.Add("", "", nhomcotthang);
			// // MessageBox.Show("1: "+ strGT +"#"+ctCotTruoc+"#"+tencottruoc);
		}

		// //MessageBox.Show("1: "+ strGT +"#"+ctCotTruoc+"#"+tencottruoc);
		// if(this.dtnhom.Rows.Count == 0 ||(this.dtnhom.Rows[this.dtnhom.Rows.Count-1]["from"].ToString().Trim()+"_"+this.dtnhom.Rows[this.dtnhom.Rows.Count-1]["to"].ToString().Trim() != tencottruoc.Trim() +"_"+tencotnay.Trim()))
		// this.dtnhom.Rows.Add(tencottruoc, tencotnay, "");
		if(string.IsNullOrWhiteSpace(strGT))
			return ctCotTruoc;
		else
			return ctCotTruoc.Replace(tencottruoc, tencotnay).Replace(",1)".Replace(" ", ""), ","+ (strGT.Split('_').Length!= 0? strGT.Split('_')[1].ToString():strGT) +")".Replace(" ", ""))
														.Replace(",-3)".Replace(" ", ""), ","+ (strGT.Split('_').Length!= 0? strGT.Split('_')[1].ToString():strGT) +")".Replace(" ", ""))
														.Replace(",-1".Replace(" ", ""), ","+ (strGT.Split('_').Length!= 0? strGT.Split('_')[0].ToString():strGT) +"".Replace(" ", ""))
														;
	}

	public string dynamicRowTenCpDinhPhi(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "tkno like '64%' or tkno like '635%' or tkno like '811%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc" });
			DataTable dtBaocaophu1 = dvBaocaophu.ToTable(true, new string[] { "tkno" });
			string sTaikhoan = string.Join("_", dtBaocaophu1.Rows.OfType<DataRow>().Select(r => r[0].ToString()));
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));
			DataRow dr;
			string dkKhoanmucMa = "DP";
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				// dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and ma like '" + dkKhoanmucMa + "%'";
				dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and dinhphi=1";
				if(dvkhoanmuc.Count != 0)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = new string(' ', 10) +" - " +dvkhoanmuc[0]["Ten"];
					dr["giatri"] = "pscdCPKM(" + sTaikhoan +",911,-1,"+ dvkhoanmuc[0]["KhoanMuc"].ToString()+",-3)";
					// pscd(641,911,-phongban-1, -khoanmuc-2,-thang-3) pscdCPBH
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}
			}

			// điền 1 dòng trống khi không có dòng dữ liệu nào của báo cáo này
			if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = "";
				dr["giatri"] = "";
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}
	
		public string dynamicRowTenCpBienPhiNhanvien(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "tkno like '64%' or tkno like '635%' or tkno like '811%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc" });
			DataTable dtBaocaophu1 = dvBaocaophu.ToTable(true, new string[] { "tkno" });
			string sTaikhoan = string.Join("_", dtBaocaophu1.Rows.OfType<DataRow>().Select(r => r[0].ToString()));
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));
			string dkKhoanmucMa = "BP01";
			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				// dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and ma like '" + dkKhoanmucMa +"%'";
				dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and dinhphi=2";
				if(dvkhoanmuc.Count != 0)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = new string(' ', 10) +" - " +dvkhoanmuc[0]["Ten"];
					dr["giatri"] = "pscdCPKM(" + sTaikhoan +",911,-1,"+ dvkhoanmuc[0]["KhoanMuc"].ToString()+",-3)";
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}
			}

			// điền 1 dòng trống khi không có dòng dữ liệu nào của báo cáo này
			if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = "";
				dr["giatri"] = "";
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	public string dynamicRowTenCpBienPhiMuaNgoai(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "tkno like '64%' or tkno like '635%' or tkno like '811%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc" });
			DataTable dtBaocaophu1 = dvBaocaophu.ToTable(true, new string[] { "tkno" });
			string sTaikhoan = string.Join("_", dtBaocaophu1.Rows.OfType<DataRow>().Select(r => r[0].ToString()));
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));
			string dkKhoanmucMa = "BP02";
			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				// dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and ma like '" + dkKhoanmucMa +"%'";
				dvkhoanmuc.RowFilter = "khoanmuc="+ dtBaocaophu0.Rows[i]["khoanmuc"].ToString()+" and dinhphi=0";
				if(dvkhoanmuc.Count != 0)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = new string(' ', 10) +" - " +dvkhoanmuc[0]["Ten"];
					dr["giatri"] = "pscdCPKM(" + sTaikhoan +",911,-1,"+ dvkhoanmuc[0]["KhoanMuc"].ToString()+",-3)";
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}
			}

			// điền 1 dòng trống khi không có dòng dữ liệu nào của báo cáo này
			if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = "";
				dr["giatri"] = "";
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}
```

## 4. HẬU XỬ LÝ - KQKD KHOẢN MỤC + PHÒNG BAN

```csharp

public DataTable dtbccon;
	public DataTable DMain(DataTable dtDL, string ssele, DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		#region: Tính toán nhóm cột cho báo cáo bsc par
		// tính toán nhóm cột cho báo cáo bsc par
		if(formBaoCao.Tag!= null & formBaoCao.Tag.GetType() == typeof(DataTable))
		{
			try {
				DataTable dtnhomcot = formBaoCao.Tag as DataTable;
				if(dtnhomcot.Rows.Count>1 && !string.IsNullOrWhiteSpace(dtnhomcot.Rows[0]["TenNhom"].ToString()))
				{
					string[] artennhom = dtnhomcot.Rows[0]["TenNhom"].ToString().Split('#');
					StringBuilder strnhomcot = new StringBuilder();
					string sfrom = dtnhomcot.Rows[1]["from"].ToString().Trim(), sto = dtnhomcot.Rows[1]["to"].ToString().Trim(), sbd = dtnhomcot.Rows[1]["to"].ToString().Trim();
					int nhom = 0;
					DataView dvcol = new DataView(dtColumns);
					for(int i = 1; i< dtnhomcot.Rows.Count; i++)
					{
						// sang nhóm khác
					if(dtnhomcot.Rows[i]["from"].ToString().Trim()!= sfrom || i == dtnhomcot.Rows.Count-1)
						{
							if(i == dtnhomcot.Rows.Count-1)
								sto = dtnhomcot.Rows[i]["to"].ToString().Trim();
							string tennhom = "";
							if(nhom< artennhom.Length)
								tennhom = artennhom[nhom];
							nhom+= 1;
							int sttbd = 0, sttkt = 0;
							dvcol.RowFilter = "GiaTri = 'ReportFix."+ sbd +"'";
							if(dvcol.Count>0)
								sttbd = Convert.ToInt16(dvcol[0]["stt"]);
							dvcol.RowFilter = "GiaTri = 'ReportFix."+ sto +"'";
							if(dvcol.Count>0)
								sttkt = Convert.ToInt16(dvcol[0]["stt"]);
							if(sttkt>0)
								strnhomcot.Append(tennhom+"("+sttbd+","+sttkt +")0#");
							sfrom = dtnhomcot.Rows[i]["from"].ToString().Trim();
							sto = sbd = dtnhomcot.Rows[i]["to"].ToString().Trim();
						}
						else sto = dtnhomcot.Rows[i]["to"].ToString().Trim();
					}

					drReport["nhomcot"] = strnhomcot.ToString();
				}
			}
			catch(Exception ex) { MessageBox.Show(ex.Message); }
		}

		#endregion: Tính toán nhóm cột cho báo cáo bsc par
		//Viết code dưới dòng này(chỉnh sửa dữ liệu báo cáo)
		// #region trường hợp chạy báo cáo FIX trên sql sẽ chuyển hóa các hàm công thức cột FIX sang sum giá trị cột tr trong báo cáo con tương ứng. Ví dụ
		// formBaoCao.dtfuncFixReport.Rows.Add("dndau(", "dndau(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr2-tr3 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dcdau(", "dcdau(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr3-tr2 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dn(", "dn(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr4-tr5 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dc(", "dc(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr5-tr4 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dndault(", "dndault(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr2 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dcdault(", "dcdault(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr3 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dnlt(", "dnlt(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr4 else 0 end) ");
		// formBaoCao.dtfuncFixReport.Rows.Add("dclt(", "dclt(@tkhoan)", " Sum( case when  tr1 like '{0}%' then tr5 else 0 end) ");
		// #endregion
		#region trường hợp chạy báo cáo FIX dưới local thì thực hiện chạy báo cáo con rùi add vào dữ liệu báo cáo sau đó tính toán dựa vào bảng đó
		//MessageBox.Show("chạy báo cáo con");
		string where = formBaoCao.GetWhereCondition(false);//.Replace("where", "").Replace("WHERE", "");
		dtbccon = formBaoCao.ChayBaoCao(0, "BCP_KQKD_B", "Daubc=0", where, formBaoCao.TuNgay, formBaoCao.DenNgay);

		formBaoCao.AddTable("BaoCaoCon", dtbccon);
		// thêm cột để quản lý việc sử dụng các tài khoản trong báo con
		if(dtbccon!= null && dtbccon.Rows.Count>0)
		{
			DataColumn dcdu = new DataColumn();
			dcdu.ColumnName = "langoi";// lần gọi số dư
			dcdu.DataType = typeof(int);
			dcdu.DefaultValue = 0;
			dtbccon.Columns.Add(dcdu);
			DataColumn dcnolt = new DataColumn();
			dcnolt.ColumnName = "langoinolt";// lần gọi số dư nợ lưỡng tính
			dcnolt.DataType = typeof(int);
			dcnolt.DefaultValue = 0;
			dtbccon.Columns.Add(dcnolt);
			DataColumn dccolt = new DataColumn();
			dccolt.ColumnName = "langoicolt";// lần gọi số dư có lưỡng tính
			dccolt.DataType = typeof(int);
			dccolt.DefaultValue = 0;
			dtbccon.Columns.Add(dccolt);
		}

		//formBaoCao.ChayBaoCao(154,"","",);
		#endregion
	return dtDL;
	}

	///
public object psDoanhthu(string tkno, int phongban, int thang)
	{
		//psc(511,1). lấy ps có 
		decimal result0 = 0, result1 = 0;
		if(thang == -3) //// bắt chéo tài khoản với cột. Tổng cộng
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkco").StartsWith(tkno.Trim()))
												 && ! r.Field<string>("Tkno").StartsWith("521") && ! r.Field<string>("Tkno").StartsWith("911")
												)
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkco").StartsWith(tkno.Trim()))
												 && ! r.Field<string>("Tkno").StartsWith("521") && ! r.Field<string>("Tkno").StartsWith("911")
												)
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		}
		else
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkco").StartsWith(tkno.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												&& ! r.Field<string>("Tkno").StartsWith("521") && ! r.Field<string>("Tkno").StartsWith("911")
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkco").StartsWith(tkno.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												&& ! r.Field<string>("Tkno").StartsWith("521") && ! r.Field<string>("Tkno").StartsWith("911")
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);									
		}

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;

		return result0-result1;
	}

	//====
public object psnd(string tkno, string tkco, int phongban, int thang)
	{
		decimal result0 = 0, result1 = 0;
		if(thang == -3) /// kỳ này //// bắt chéo tài khoản với cột
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		}
		else
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		}

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1;
	}

	// pscd(511, 52, 1)
	//====
public object pscd(string tkno, string tkco, int phongban, int thang)
	{
		decimal result0 = 0, result1 = 0;
		if(thang == -3) /// kỳ này //// bắt chéo tài khoản với cột
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
		}
		else
		{
			result0 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("phongban").Equals(phongban)
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
		}

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1;
	}


	public object pscdCPKM(string tkno, string tkco, int phongban, int khoanmuc, int thang)
	{
		decimal result0 = 0, result1 = 0;
		int ngaydau = 0, ngaycuoi = 0;
		List<string> lstTkno = tkno.Split('_').ToList();

		if(thang ==-3) /// kỳ này //// bắt chéo tài khoản với cột
		{
			result0 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("khoanmuc").Equals(khoanmuc)
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("khoanmuc").Equals(khoanmuc)
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
		}
		else
		{
			result0 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("Phongban").Equals(phongban)
												&& r.Field<int>("khoanmuc").Equals(khoanmuc)
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			// result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
			result1 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("Phongban").Equals(phongban)
												&& r.Field<int>("khoanmuc").Equals(khoanmuc)
												&& r.Field<int>("thang").Equals(thang)
			//&& r.Field<int>("thang") <= ngaycuoi
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
		}

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1;
	}

	public object pscdFilter(string tkno, string tkco, int phongban, string dkKhoanmuc, int thang)
	{
		string cmm = "select stuff( (select ','+ convert(nvarchar,KhoanMuc) from dbo.KhoanMuc WHERE "+ dkKhoanmuc+" for xml path('')),1,1,'')";
		object sKhoanmuc = _Func.DBEXEC(cmm, true);
		//MessageBox.Show("sKhoanmuc:\r"+sKhoanmuc.ToString());
		// List<string> lstKhoanmuc = sKhoanmuc.ToString().Split(',').ToList();

		List<int> lstKhoanmuc = sKhoanmuc.ToString().Split(',').Select(Int32.Parse).ToList();
		decimal result0 = 0, result1 = 0;
		int iThang = 0, iNam = 0;
		int ngaydau = 0, ngaycuoi = 0;
		List<string> lstTkno = tkno.Split('_').ToList();
		string sWhereTkno = "";
		for (int i = 0; i < lstTkno.Count; i++)
		{
			sWhereTkno += " ma like '" + lstTkno[i].ToString()+"%'" + (i == (lstTkno.Count-1)?"":" or ");
		}

		//MessageBox.Show(sWhereTkno);
		cmm = "select stuff( (select '_'+ ma from dbo.taikhoan WHERE "+ sWhereTkno+" for xml path('')),1,1,'')";
		object sTkno = _Func.DBEXEC(cmm, true);
		lstTkno = sTkno.ToString().Split('_').ToList();

		if(thang == -3) /// kỳ này //// bắt chéo tài khoản với cột
		{
			result0 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			result1 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
			//MessageBox.Show(result0.ToString()+"#"+result1.ToString());
		}
		else
		{
			result0 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("Phongban").Equals(phongban)
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
			// result1 = dtbccon.AsEnumerable().Where(r => (r.Field<string>("Tkno").StartsWith(tkno.Trim())
			result1 = dtbccon.AsEnumerable().Where(r => (lstTkno.Contains(r.Field<string>("Tkno").Trim())
												&& r.Field<string>("tkco").StartsWith(tkco.Trim())
												&& r.Field<int>("Phongban").Equals(phongban)
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& r.Field<int>("thang").Equals(thang)
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);
		}

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1;
	}
	
```


# dynamic cột là tháng

## 1. Trước tất cả

```csharp
public DataTable DMain(DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		//Viết code dưới dòng này(chỉnh sửa cấu trúc hiển thị báo cáo)
		DateTime ngay0 = formBaoCao.TuNgay;
		DateTime ngay1 = formBaoCao.TuNgay;
		DateTime ngay2 = formBaoCao.DenNgay;
		int sothang = ngay2.Year*12 + ngay2.Month - ngay1.Year*12 - ngay1.Month + 1;
		DataRow dr;

		//
		for (int i = 0; i < sothang; i++)
		{
			ngay0 = ngay1.AddMonths(i);
			//MessageBox.Show("ngay0="+ngay0.ToString("dd/MM/yyyy"));
			dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
			dr["ten"] = ngay0.Month.ToString() +"_"+ ngay0.Year.ToString();
			dr["giatri"] = ngay0.Month.ToString() +"_"+ ngay0.Year.ToString();
			dr["dieukien"] = ngay0.Month.ToString() +"_"+ ngay0.Year.ToString();
			_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			
			
		}

		
       

		return dtColumns;
	}

	public string dynamicTen(string strTen)
	{
		strTen = "Tháng " + strTen;
		return strTen;
	}
	

	// hàm tính giá trị
	public string dynamicGiaTri(string strGiaTri)
	{
		strGiaTri = "(Vr.psno - vr.psco) as TH_"+ strGiaTri.Trim();
		return strGiaTri;
	}
	

	// hàm tính điều kiện
	public string dynamicDieuKien(string strDieuKien)
	{
		int thang = Convert.ToInt32(strDieuKien.Split('_')[0].ToString());
		int nam = Convert.ToInt32(strDieuKien.Split('_')[1].ToString());
		strDieuKien = "ngay>='"+ new DateTime(nam, thang , 01).ToString("MM/dd/yyyy")+"'";
		strDieuKien += " and ngay<='"+ new DateTime(nam, thang , 01).AddMonths(1).AddDays(-1).ToString("MM/dd/yyyy")+"'";
		return strDieuKien;
	}
```


# VIEW CÁC BẢNG KIỂM TRA DYNAMIC CỘT FIX

### 1. View bảng dinamic để kiểm tra

* code ở mục "Trước tất cả"
* Hàm dynamicCol
* Đoạn if(\_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)

```csharp
// Some code
public string dynamicCol(string strGT, string ctCotTruoc, string tencottruoc, string tencotnay, DataRow drReportFix)
	{
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			// tính toán cột dynamic, chèn vào bảng dtDynamicReportColumn
			
			
			// view bảng kiểm tra
			_Func.ViewTable(_Vari.FormChinh.dtDynamicReportColumn);
		}
	}
```

### 2. View bảng dtColumn sau khi đã chèn đầy đủ cột và xem công thức

* View code ở "Hậu xử lý"

```csharp

public DataTable DMain(DataTable dtDL, string ssele, DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		if(_Vari.Developer)
		{
			MessageBox.Show("dtDL");
			_Func.ViewTable(dtDL);
		}
		// chạy báo cáo phụ để phục vụ hàm phía dưới
		// chạy các lệnh tính toán khác
		return dtDL;
	}
```


# DYNAMIC - COL - ROW BC FIX

## Ý NGHĨA CHỨC NĂNG

* Báo cáo fix. Tạo dynamic dòng và cột
* Nhóm cột&#x20;
* dòng tổng cộng có điều kiện dạng where thay vì theo id&#x20;

  <figure><img src="/files/2mnHtfsTGJ9pVLtY2CxC" alt=""><figcaption><p>Mẫu báo cáo</p></figcaption></figure>

  <figure><img src="/files/Mnkls5bh9TBQruYJ4EFi" alt=""><figcaption><p>Công thức báo cáo</p></figcaption></figure>

## NGUYÊN TẮC CHUNG

* Báo cáo tích vào:&#x20;
  * Fix
  * Dynamic column
* code trước tất cả
  * Tính toán ra dynamicRow
  * Tính toán ra dynamicCol
* code Hậu xử lý
  * Viết các hàm tính toán của dòng chi tiết
    * Ví dụ psNoKhoanmuc(idKhoanmuc, thang)
  * Viết các hàm tính toán của dòng tổng
    * Ví dụ psNoKhoanmuc\_sum(dieukienWhereKhoanmuc, thang)
* Nếu có nhiều dynamicRow thì số lượng tham số thay đổi theo cột nên đồng bộ với nhau giữa các hàm. Ví dụ,&#x20;
  * psNoKhoanmuc(tkno,tkco,-khoanmuc,-bophan,-thang)
  * psNoDoanhthu(tkno,tkco,-khoanmuc,-bophan,-thang)

## TRƯỚC TẤT CẢ

* Hàm chính:
  * Tạo báo cáo phụ, phục vụ tính toán cho các hàm dynamicRow và dynamicCol phía dưới
* Viết các hàm dynamicRow
  * Sinh ra các dòng tự động: Cột chỉ tiêu và cột công thức
  * Cột công thức, thì thay đổi tham số tương ứng với việc chèn dòng. Ví dụ, chèn dòng theo khoanmuc, thì công thức sẽ thay đổi theo tham số khoanmuc
* Viết hàm dynamicCol
  * Sinh ra các cột tự động
  * Nhân bản công thức cột đầu tiên. Sau đó, thay đổi tham số cho cột vừa nhân bản. Ví dụ, chèn cột theo tháng, bộ phận thì công thức cần thay đổi tham số tháng, bộ phận
  * Xử lý nhóm cột nếu có
* Code của bài toán trên như sau:&#x20;

```csharp
public DateTime TuNgay, DenNgay;
	public DataTable dtBaocaoPhu;
	#region phục vụ tính nhóm cột bscpar
	// liệt kê các nhóm cột theo cú pháp 'Tên nhóm cột([Tên cột bắt đầu],[Tên cột kết thúc])0#'
	public DataRow drReport0;
	public string tencot = "";
	public dynamic f25;
	#endregion phục vụ tính nhóm cột bscpar
	public DataTable DMain(DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		// //Viết code dưới dòng này(chỉnh sửa cấu trúc hiển thị báo cáo)
		#region phục vụ tính nhóm cột bscpar
		drReport0 = drReport;
		f25 = formBaoCao;
		#endregion phục vụ tính nhóm cột bscpar
		#region trường hợp chạy báo cáo FIX dưới local thì thực hiện chạy báo cáo con rùi add vào dữ liệu báo cáo sau đó tính toán dựa vào bảng đó

		DateTime daunam = new DateTime();
		DateTime tungay = formBaoCao.TuNgay;
		if(Convert.ToInt16(_Vari.DrCus["thangtaichinh"]) - tungay.Month <= 0)
		{
			daunam = new DateTime(tungay.Year, Convert.ToInt16(_Vari.DrCus["thangtaichinh"]), 01);
		}
		else
		{
			daunam = new DateTime(tungay.Year - 1, Convert.ToInt16(_Vari.DrCus["thangtaichinh"]), 01);
		}

		#endregion: Tính thời gian đầu năm
		TuNgay = formBaoCao.TuNgay;
		DenNgay = formBaoCao.DenNgay;
		string where = formBaoCao.GetWhereCondition(false);//.Replace("where", "").Replace("WHERE", "");
		dtBaocaoPhu = formBaoCao.ChayBaoCao(0, "BCP_KQKD_HPLUX", "Daubc=0", where, formBaoCao.TuNgay , formBaoCao.DenNgay);
		formBaoCao.AddTable("BaoCaoPhu", dtBaocaoPhu);

		return dtColumns;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_LoaiDoanhThu(string strTen)
	{
		/*
		- hàm có nhiệm vụ sinh các dòng tự động
		- các dòng sinh tự động, bằng cách chèn dòng vào dtDynamicReportColumn[ten,giatri]
		- dtDynamicReportColumn[ten,giatri]: tên là nội dung hiển thị của cột chỉ tiêu (cột chứa tên hàm này dynamicRow_LoaiDoanhThu)
			- giatri là tên hàm được viết ở code Hauxuly. và thay đổi tham số tương ứng với tham số sinh dòng tự động
			- ví dụ, hàm có tên là psCoLoaiDoanhThu(tkno,tkco,loaiDoanthu,khoanmuc,bophan,thang)
			- trong đó, việc sinh dòng tự động đang xét theo yếu tố khoanmuc, nên hàm trên sẽ được thay đổi theo từng dòng ở tham số khoanmuc
				-  psCoLoaiDoanhThu(tkno,tkco,loaiDoanthu,@khoanmuc,bophan,thang)
		*/
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "tkno like '51%' or tkno like '711%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "loaidoanhthu", "loaidoanhthuMa", "loaidoanhthuTen" });

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["loaidoanhthuTen"].ToString();
				// điền công thức cho cột tổng đầu tiên.// 
				dr["giatri"] = "psCoLoaiDoanhThu(511_515_711,911,"+ dtBaocaophu0.Rows[i]["loaidoanhthu"]+",-2,-3,-4)";

				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_GiaVon(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "tkno like '632%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc", "khoanmucMa", "khoanmucTen" });
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["khoanmucTen"].ToString();
				// điền công thức cho cột tổng đầu tiên.// 
				dr["giatri"] = "psNoKhoanmuc(632,911,-1,"+ dtBaocaophu0.Rows[i]["khoanmuc"]+",-3,-4)";

				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_NhanCong(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "(tkno like '64%' or tkno like '81%') and khoanmucMa like '02.01%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc", "khoanmucMa", "khoanmucTen" });
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["khoanmucTen"].ToString();
				// điền công thức cho cột tổng đầu tiên.// 
				dr["giatri"] = "psNoKhoanmuc(64_81,911,-1,"+ dtBaocaophu0.Rows[i]["khoanmuc"]+",-3,-4)";

				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_HoaHong(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "(tkno like '64%' or tkno like '81%') and khoanmucMa like '02.02%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc", "khoanmucMa", "khoanmucTen" });
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["khoanmucTen"].ToString();
				// điền công thức cho cột tổng đầu tiên.// 
				dr["giatri"] = "psNoKhoanmuc(64_81,911,-1,"+ dtBaocaophu0.Rows[i]["khoanmuc"]+",-3,-4)";

				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_MatBang(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "(tkno like '64%' or tkno like '81%') and khoanmucMa like '02.03%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc", "khoanmucMa", "khoanmucTen" });
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["khoanmucTen"].ToString();
				// điền công thức cho cột tổng đầu tiên.// 
				dr["giatri"] = "psNoKhoanmuc(64_81,911,-1,"+ dtBaocaophu0.Rows[i]["khoanmuc"]+",-3,-4)";

				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	// -1, -2, -3 -4: loại doanh thu -1, khoản mục-2, bộ phận-3, tháng -4
	public string dynamicRow_Khac(string strTen)
	{
		// tính toán lần đầu tiên bảng cần par
		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			DataView dvBaocaophu = new DataView(dtBaocaoPhu);
			dvBaocaophu.RowFilter = "(tkno like '64%' or tkno like '81%') and khoanmucMa like '02.04%'";
			DataTable dtBaocaophu0 = dvBaocaophu.ToTable(true, new string[] { "khoanmuc", "khoanmucMa", "khoanmucTen" });
			DataView dvkhoanmuc = new DataView(_Func.GetTable("KhoanMuc"));

			DataRow dr;
			//// nếu dvBaocaophu không có dòng nào thì sao.
			if(dtBaocaophu0.Rows.Count != 0)
			{
				for (int i = 0; i < dtBaocaophu0.Rows.Count; i++)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = new string(' ', 5) +" - " +dtBaocaophu0.Rows[i]["khoanmucTen"].ToString();
					// điền công thức cho cột tổng đầu tiên.// 
					dr["giatri"] = "psNoKhoanmuc(64_81,911,-1,"+ dtBaocaophu0.Rows[i]["khoanmuc"]+",-3,-4)";

					// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}
			}
			else
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = new string(' ', 5) +" - Chi phí khác";
				dr["giatri"] = "psNoKhoanmuc(64_81,911,-1,0,-3,-4)";
				// psNoKhoanmuc(staikhoan-"632_641_642", tkDoiung-"911", loaidoanhthu - 1,khoanmuc-2, bophan-3, thang-4);
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}
		}

		return strTen;
	}

	public string dynamicRowGiaTri(string strGiaTri)
	{
		return strGiaTri;
	}

	public string dynamicCol(string strGT, string ctCotTruoc, string tencottruoc, string tencotnay, DataRow drReportFix)
	{
		// strGT là giá trị cột giá trị của dòng trong dtDynamicReportColumn
		// ctCotTruoc là giá trị công thức cột trước cột đang động cột
		// tencottruoc là columnName cột trước cột tự động
		// tencotnay là cột hiện tại đang tự động tính nhằm replace các biểu thức tính sau cho chính xác tên cột mình cần xử lý
		// drReportFix là dòng dữ liệu trong báo cáo fix trường hợp không lấy cột liền trước có thể gọi các cột liền trước để lấy công thức cần dùng

		/*
		- hàm có nhiệm vụ sinh các cột tự động và nhóm cột
		- cột sinh tự động, bằng cách chèn dòng vào dtDynamicReportColumn[ten,giatri]
		- dtDynamicReportColumn[ten,giatri]: tên là tên cột hiển thị. giatri là điều kiện để xử lý lại công thức của cột vừa nhân bản lên
			- giatri thường có dạng: @kyhieudau_@thamsoThaydoi01_@thamsoThaydoi02
		
		*/
		
		DataView dvBaocaophu = new DataView(dtBaocaoPhu);
		int sothang = (DenNgay.Year*12 + DenNgay.Month)-(TuNgay.Year*12 + TuNgay.Month)+1;
		int iThang = 0;
		DataRow dr;

		if(_Vari.FormChinh.dtDynamicReportColumn.Rows.Count<= 0)
		{
			/*
			- duyệt qua số tháng đề chèn cột tháng.
				- trong 01 tháng
						- duyệt qua tiếp danh mục bộ phận để tạo cột bộ phận của tháng
						- thêm mặc định cột tổng cộng của tháng vào
			- hết duyệt tháng, thì chèn thêm tổng cộng cả năm vào
				- duyệt danh mục bộ phận để tạo cột bộ phận của năm
				- thêm mặc định cột tổng cộng của năm vào
			- nguyên tắc dynamicCol
				- nhân bản công thức cột đầu tiên lên cho cột hiện tại
				- sửa lại công thức của cột hiện tại, thay thế các tham số tương ứng với việc chạy cột.
				- ở ví dụ này, thì việc chạy tương ứng với tháng và bộ phận. nên ta sẽ thay đổi tham số tháng và bộ phận
				- theo quy ước của hàm dynamicRow(tkno,tkco,loaiDoanhthu=-1,khoanmuc =-2,bophan=-3,thang=-4) thì 2 tham số đó tương ứng như sau[thang=-4][bophan=-3]
				- từ đó, ta sẽ ra được đoạn return
					return ctCotTruoc.Replace(tencottruoc, tencotnay)
					.Replace(",-3", strGT.Split('_').Length != 0 ? ","+strGT.Split('_')[2].ToString() : ",-3") // bộ phận 
					.Replace(",-4", strGT.Split('_').Length != 0 ? ","+strGT.Split('_')[1].ToString() : ",-4"); // tháng
				- trong đó;
					strGT là giá trị của bảng dtDynamicReportColumn.col["giatri"]
					col["giatri"] là do ta gán vào dtDynamic theo 1 format quy ước nào đó. ví dụ: Thang_@thang_@bophan; tongThang_@thang_@bophan
					như trên, quy ước là ký hiệu đầu + @thang + @bophan
					sau đó, tại công thức return, ta xử lý thay đổi các tham số -3 và -4 của công thức gốc, bằng strGT (hay là col["giatri"]) ở text:kyhieu_@thang_@bophan
			
			*/
			
			#region:
			DataTable dtBophan = _Func.GetTable("BoPhan");
			DataView dvBP = new DataView(dtBophan);
			dvBP.RowFilter = "BoPhan>0";
			dvBP.Sort = "Ma";
			string tencot = tencotnay;
			for (int i = 0; i < sothang; i++)
			{
				iThang = TuNgay.AddMonths(i).Year*12 + TuNgay.AddMonths(i).Month;
				for(int b = 0; b< dvBP.Count; b++)
				{
					dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
					dr["ten"] = dvBP[b]["ten"].ToString();
					//dr["ten"] = "Tháng_" + TuNgay.AddMonths(i).Month.ToString() + "/" + TuNgay.AddMonths(i).Year.ToString()+" "+ dvBP[b]["Ma"].ToString();
					// //dr["giatri"] = "TH_" + TuNgay.AddMonths(i).Month.ToString() + "_" + TuNgay.AddMonths(i).Year.ToString();// điền giá trị dùng để truyền đầu vào hàm dynamicCol để xử lý
					dr["giatri"] = "thang_" + iThang.ToString()+"_"+dvBP[b]["BoPhan"].ToString();
					_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				}

				//Thêm cột Tổng cộng của từng tháng
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = "Tổng cộng";
				//dr["ten"] = " Tổng tháng_" + TuNgay.AddMonths(i).Month.ToString() + "/" + TuNgay.AddMonths(i).Year.ToString()+" ";
				// //dr["giatri"] = "TH_" + TuNgay.AddMonths(i).Month.ToString() + "_" + TuNgay.AddMonths(i).Year.ToString();// điền giá trị dùng để truyền đầu vào hàm dynamicCol để xử lý
				dr["giatri"] = "tongthang_" + iThang.ToString()+"_-3";
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
				#region phục vụ tính nhóm cột bscpar. Tính nhóm cột hiện tại và lưu vết cuột cuối cùng của nhóm hiện tại
				drReport0["NhomCot"] = drReport0["NhomCot"].ToString().Trim()+"Tháng " + TuNgay.AddMonths(i).Month.ToString().Trim()+"("+ tencot + ",".Trim()+ f25.NextNameColumn(tencot, dvBP.Count) +")0#";
				tencot = f25.NextNameColumn(tencot, dvBP.Count+1);
				#endregion
			}

			// tạo cột cho bộ phận, tổng cả năm
			for(int b = 0; b< dvBP.Count; b++)
			{
				dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
				dr["ten"] = dvBP[b]["ten"].ToString();
				dr["giatri"] = "Nam_-1_"+dvBP[b]["BoPhan"].ToString();
				_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);
			}

			// tạo cột cho tổng cả năm
			dr = _Vari.FormChinh.dtDynamicReportColumn.NewRow();
			dr["ten"] = "Tổng cộng";
			dr["giatri"] = "tongNam_-1_-2";
			_Vari.FormChinh.dtDynamicReportColumn.Rows.Add(dr);

			/// tạo nhóm cột theo nguyên tắc: nhomcot = nhomcot + "tổng cộng (" + tencot + "," + f25.NextNameColumn(tencot, @soluongCot) +")0#";
			/// với @soluongCot là số lượng cột ở giữa cột bắt đầu và cột kết thúc của nhóm cột hiện tại
			drReport0["NhomCot"] = drReport0["NhomCot"].ToString().Trim()+"Tổng cộng (" + tencot + ",".Trim()+ f25.NextNameColumn(tencot, 2) +")0#";
			// _Func.TextEditer("Nhomcot: /r"+drReport0["NhomCot"].ToString());
			#endregion
		}

		if(string.IsNullOrWhiteSpace(strGT))
			return ctCotTruoc;
		else
		{
			//return ctCotTruoc;
			return ctCotTruoc.Replace(tencottruoc, tencotnay)
			.Replace(",-3", strGT.Split('_').Length != 0 ? ","+strGT.Split('_')[2].ToString() : ",-3") // bộ phận 
			.Replace(",-4", strGT.Split('_').Length != 0 ? ","+strGT.Split('_')[1].ToString() : ",-4"); // tháng
		}
	}
```

## HẬU XỬ LÝ

* Hàm chính:
  * chạy báo cáo phụ để tạo ra nguồn cho các hàm phía dưới phục vụ tính toán
* Code các hàm phục vụ công thức cột (hoặc gán trong trước tất cả)

```csharp
public DataTable dtbccon;
	public DataTable DMain(DataTable dtDL, string ssele, DataTable dtColumns, DataRow drReport, FOR0025 formBaoCao)
	{
		// if(_Vari.Developer)
		// {
		// MessageBox.Show("dtDL");
		// _Func.ViewTable(dtDL);
		// }
		#region trường hợp chạy báo cáo FIX dưới local thì thực hiện chạy báo cáo con rùi add vào dữ liệu báo cáo sau đó tính toán dựa vào bảng đó
		string where = formBaoCao.GetWhereCondition(false);//.Replace("where", "").Replace("WHERE", "");
		dtbccon = formBaoCao.ChayBaoCao(0, "BCP_KQKD_HPLUX", "Daubc=0", where, formBaoCao.TuNgay , formBaoCao.DenNgay);
		// dtbccon.TableName = "";
		// _Func.ViewTable(dtbccon);
		#endregion

		return dtDL;
	}

	// loaidoanhthu-1, khoanmuc-2,bophan-3,thang-4
	public object psCoLoaiDoanhThu(string dkTaikhoan, string tkDoiung, int loaidoanhthu, int khoanmuc, int bophan, int thang)
	{
		/*
		- dkTaikhoan: là truyền 1 danh sách tài khoản, phân tách bằng dấu _ ; Ví dụ; 511_515_711
			- câu lệnh where tài khoản: sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
		- loaidoanhthu,khoanmuc, bophan, thang: nếu muốn loại trừ khi tính toán thì điền số âm. 
			- mặc định tuần tự là: -1,-2,-3,-4
			- quan trọng: vì phải xử lý nhân bản cột lên. nên thứ tự phải đúng và phải đồng bộ giữa các hàm
			- câu lệnh where idBophan:  && (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
				- nếu tham số bophan < 0 thì coi như thỏa mãn toàn bộ
				- còn không thì (bophan>=0) thì where cột dữ liệu có id thỏa mãn: r.Field<int>("bophan").Equals(bophan)
				
		*/
		decimal result0 = 0, result1 = 0;
		#region: tính ra listTkNo
		List<string> lstTkno = new List<string>();
		lstTkno = dkTaikhoan.Split('_').ToList();
		string sWhereTkno = "";
		for (int i = 0; i < lstTkno.Count; i++)
		{
			sWhereTkno += " ma like '" + lstTkno[i].ToString()+"%'" + (i == (lstTkno.Count-1)?"":" or ");
		}

		string cmm = "select stuff( (select '_'+ ma from dbo.taikhoan WHERE "+ sWhereTkno +" for xml path('')),1,1,'')";
		string sTkno = "_"+_Func.DBEXEC(cmm, true).ToString()+"_";
		#endregion: tính ra listTkNo

		result0 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu)) // && lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		result1 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu)) //&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result1 - result0; // no - co ; do sau khi ket chuyen thi but toan la 511/911
	}

	public object psNoKhoanmuc(string dkTaikhoan, string tkDoiung, int loaidoanhthu, int khoanmuc, int bophan, int thang)
	{
		decimal result0 = 0, result1 = 0;

		#region: tính ra listTkNo
		List<string> lstTkno = new List<string>();
		lstTkno = dkTaikhoan.Split('_').ToList();
		string sWhereTkno = "";
		for (int i = 0; i < lstTkno.Count; i++)
		{
			sWhereTkno += " ma like '" + lstTkno[i].ToString()+"%'" + (i == (lstTkno.Count-1)?"":" or ");
		}

		string cmm = "select stuff( (select '_'+ ma from dbo.taikhoan WHERE "+ sWhereTkno +" for xml path('')),1,1,'')";
		string sTkno = "_"+_Func.DBEXEC(cmm, true).ToString()+"_";
		#endregion: tính ra listTkNo

		result0 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu))
												&& (khoanmuc<0 || r.Field<int>("khoanmuc").Equals(khoanmuc))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		result1 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu))
												&& (khoanmuc<0 || r.Field<int>("khoanmuc").Equals(khoanmuc))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1; /// lay co - no; do sau khi ket chuyen thi but toan la 911/632
	}

	public object psNoKhoanmuc_where(string dkTaikhoan, string tkDoiung, int loaidoanhthu, string khoanmuc, int bophan, int thang)
	{
		/*
		- dkTaikhoan: là truyền 1 danh sách tài khoản, phân tách bằng dấu _ ; Ví dụ; 511_515_711
			- câu lệnh where tài khoản: sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
		- loaidoanhthu,khoanmuc, bophan, thang: nếu muốn loại trừ khi tính toán thì điền số âm. 
			- mặc định tuần tự là: -1,-2,-3,-4
			- quan trọng: vì phải xử lý nhân bản cột lên. nên thứ tự phải đúng và phải đồng bộ giữa các hàm
			- câu lệnh where idBophan:  && (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
				- nếu tham số bophan < 0 thì coi như thỏa mãn toàn bộ
				- còn không thì (bophan>=0) thì where cột dữ liệu có id thỏa mãn: r.Field<int>("bophan").Equals(bophan)
			- câu lệnh where dk_khoanmuc: && lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
				- từ dk_khoanmuc, tìm ra list danh sách id_khoanmuc => gán vào lstKhoanmuc
				- sử dụng điều kiện trên để lọc ra các dòng dữ liệu có khoanmuc thuộc listKhoanmuc
		*/
		
		decimal result0 = 0, result1 = 0;

		#region: tính ra listTkNo
		List<string> lstTkno = new List<string>();
		lstTkno = dkTaikhoan.Split('_').ToList();
		string sWhereTkno = "";
		for (int i = 0; i < lstTkno.Count; i++)
		{
			sWhereTkno += " ma like '" + lstTkno[i].ToString()+"%'" + (i == (lstTkno.Count-1)?"":" or ");
		}

		string cmm = "select stuff( (select '_'+ ma from dbo.taikhoan WHERE "+ sWhereTkno +" for xml path('')),1,1,'')";
		string sTkno = "_"+_Func.DBEXEC(cmm, true).ToString()+"_";
		#endregion: tính ra listTkNo

		#region: tính ra lstKhoanmuc
		string cmmKhoanmuc = "select stuff( (select ','+ convert(nvarchar,KhoanMuc) from dbo.KhoanMuc WHERE "+ khoanmuc +" for xml path('')),1,1,'')";
		object sKhoanmuc = _Func.DBEXEC(cmmKhoanmuc, true);
		List<int> lstKhoanmuc = sKhoanmuc.ToString().Split(',').Select(Int32.Parse).ToList();

		#endregion: tính ra lstKhoanmuc

		result0 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu))
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("pscoKynay") ?? 0);
		result1 = dtbccon.AsEnumerable().Where(r => (
												sTkno.ToString().Contains("_"+ r.Field<string>("TKNO").Trim() +"_")
												&& r.Field<string>("tkco").StartsWith(tkDoiung.Trim())
												&& (loaidoanhthu<0 || r.Field<int>("loaidoanhthu").Equals(loaidoanhthu))
												&& lstKhoanmuc.Contains(r.Field<int>("khoanmuc"))
												&& (bophan < 0 || r.Field<int>("bophan").Equals(bophan))
												&& (thang<0 || r.Field<int>("thang").Equals(thang))
												))
												.Sum(r => r.Field<decimal?>("psnoKynay") ?? 0);

		if(result0 == null)result0 = 0;
		if(result1 == null)result1 = 0;
		return result0 - result1; /// lay co - no; do sau khi ket chuyen thi but toan la 911/632
	}
```




---

[Next Page](/llms-full.txt/1)

