Bản địa hóa kế toán

Cảnh báo

Bài hướng dẫn này yêu cầu bạn có kiến thức về cách xây dựng một module trong Odoo (xem Server framework 101).

Quy trình cài đặt

Khi cài đặt module account, module bản địa hóa tương ứng với mã quốc gia của công ty sẽ được cài đặt tự động. Trong trường hợp chưa thiết lập mã quốc gia hoặc không tìm thấy module bản địa hóa phù hợp, module bản địa hóa l10n_generic_coa (Mỹ) sẽ được cài đặt theo mặc định. Xem post init hook để biết chi tiết.

Ví dụ, l10n_ch sẽ được cài đặt nếu công ty có quốc gia là Switzerland.

Xây dựng một module bản địa hóa

Cấu trúc của một module l10n_XX cơ bản có thể được mô tả bằng file __manifest__.py sau:

{
    "name": "COUNTRY - Accounting",
    "version": "1.0.0",
    "category": "Accounting/Localizations/Account Charts",
    "license": "LGPL-3",
    "depends": [
        "account",
    ],
    "data": [
        "data/other_data.xml",
        "views/xxxmodel_views.xml",
    ],
    "demo": [
        "demo/demo_company.xml",
    ]
}

Worktree của bạn sẽ trông như thế này

l10n_xx
├── data
│   ├── template
│   │   ├── account.account-xx.csv
│   │   ├── account.group-xx.csv
│   │   └── account.tax.group-xx.csv
│   └── other_data.xml
├── views
│   └── xxxmodel_views.xml
├── demo
│   └── demo_company.xml
├── models
│   ├── template_xx.py
│   └── __init__.py
├── __init__.py
└── __manifest__.py

Trong file đầu tiên models/template_xx.py, chúng ta thiết lập tên cho hệ thống tài khoản (chart of accounts) cùng với một số field cơ bản.

Hệ thống tài khoản (Chart of Accounts)

Tag tài khoản

Tag là một cách để sắp xếp các tài khoản. Ví dụ, giả sử bạn muốn tạo một báo cáo tài chính có nhiều dòng nhưng không tìm ra quy tắc nào để phân bổ các tài khoản theo code của chúng. Giải pháp là sử dụng tag, mỗi tag ứng với một dòng báo cáo, để lọc các tài khoản theo ý muốn.

Đặt các tag vào file data/account_account_tag_data.xml.

Tài khoản

Hiển nhiên, Chart of Accounts không thể tồn tại nếu không có Accounts. Bạn cần khai báo chúng trong file data/account.account.template.csv.

Cảnh báo

  • Tránh sử dụng account_type là asset_cash! Thực tế, các tài khoản ngân hàng & tiền mặt được tạo trực tiếp khi cài đặt module bản địa hóa và sau đó được liên kết với một account.journal.

  • Chỉ cần một tài khoản kiểu payable/receivable là đủ cho trường hợp thông thường. Tuy nhiên, chúng ta cũng cần định nghĩa một tài khoản receivable cho PoS. (được liên kết trong CoA)

  • Đừng tạo quá nhiều tài khoản: 200-300 là đủ. Nhưng quan trọng hơn, chúng ta cố gắng tìm ra sự cân bằng hợp lý để CoA sau này chỉ cần điều chỉnh tối thiểu cho phần lớn các công ty.

Nhóm tài khoản

Nhóm tài khoản cho phép mô tả cấu trúc phân cấp của hệ thống tài khoản. Bộ lọc cần được kích hoạt trong báo cáo, và khi bạn mở rộng xuống các bút toán sổ nhật ký, nó sẽ hiển thị các tài khoản cha.

Nó hoạt động dựa trên tiền tố start/end, vì vậy mỗi tài khoản có mã bắt đầu bằng một giá trị nằm giữa start và end sẽ có account.group này làm nhóm cha. Hơn nữa, các nhóm tài khoản cũng có thể có một nhóm tài khoản cha để tạo thành cấu trúc phân cấp.

Thuế

Để thêm thuế, trước tiên bạn cần khai báo các nhóm thuế. Thông thường bạn chỉ cần một nhóm thuế cho mỗi mức thuế suất, ngoại trừ mức 0% vì bạn thường cần phân biệt giữa các loại thuế miễn thuế, 0%, không chịu thuế, v.v. Model này chỉ có hai field bắt buộc: name và country. Tạo file data/template/account.tax.group-xx.csv và liệt kê các nhóm.

Giờ bạn có thể thêm các loại thuế thông qua file data/template/account.tax-xx.csv. Thuế mua/bán đầu tiên bạn định nghĩa cũng sẽ trở thành thuế mua/bán mặc định cho các sản phẩm của bạn.

Báo cáo thuế

Enterprise feature

Báo cáo thuế được khai báo trong ứng dụng Invoicing (account), nhưng báo cáo này chỉ có thể truy cập được khi ứng dụng Accounting (account_accountant) được cài đặt.

Trong phần trước, bạn đã thấy các field invoice_repartition_line_ids hoặc refund_repartition_line_ids và có lẽ chưa hiểu gì về chúng cả. Tin tốt là: bạn không phải người duy nhất chưa hiểu. Tin xấu là: bạn vẫn phải tự tìm hiểu một chút. Chủ đề này khá phức tạp. Thật vậy:

digraph foo {
  graph [
    newrank=true,
    overlap=false,
  ];
  node [
    fontname="Ubuntu"
    fontsize=10,
    style="filled,setlinewidth(6)",
    shape=box,
    height=0.1,
    width=0.1,
  ];
  edge [
    fontsize=8,
  ];
  res_country[label="res.country", fillcolor=white, penwidth=1];
  subgraph cluster_invoice {
    style = filled;
    label = "Invoices";
    color = lightyellow;
    node [style=filled, color=white];
    edge [fontsize=8,];
    account_move_line[label="account.move.line"]
    account_tax[label="account.tax"]
    account_tax_repartition_line[label="account.tax.repartition.line"];
    account_account_tag[label="account.account.tag"];
    account_move_line -> account_tax [label="tax_ids | tax_line_ids"];
    account_move_line -> account_tax_repartition_line [label="tax_repartition_line_id"];
    account_move_line -> account_account_tag [label="tag_ids"];
    account_tax_repartition_line -> account_account_tag [label="tag_ids"];
    account_tax -> account_tax_repartition_line [label="1 for base, 1..* for tax amount"];
  }
  subgraph cluster_reporting {
    style = filled;
    label = "Reporting";
    color = mistyrose;
    node [style=filled, color=white];
    edge [fontsize=8,];
    account_tax_report [label="account.report"];
    account_tax_report_line [label="account.report.line"];
    account_report_expression [label="account.report.expression"];
    account_tax_report -> account_tax_report_line [label="0..*"]
  }
  subgraph cluster_templates {
    style = filled;
    label = "Templates";
    color = lightblue;
    node [style=filled, color=white];
    edge [fontsize=8,];
    account_tax_template[label="account.tax.template"];
    account_tax_repartition_line_template[label="account.tax.repartition.line.template"];
    account_tax_template -> account_tax_repartition_line_template [label="1 for base, 1..* for tax amount"];
  }
  {
    rank=same;
    account_move_line;
    account_tax_report;
  }
  {
    rank=same;
    account_tax;
    account_tax_repartition_line;
    account_account_tag;
    res_country;
  }
  {
    rank=same;
    account_report_expression;
    account_tax_template;
    account_tax_repartition_line_template;
  }
  account_tax -> account_tax_template [label="    Creates when\n    installing CoA", dir=back];
  account_tax_repartition_line -> account_tax_repartition_line_template[label="    Creates when\n   installing CoA", dir=back];
  account_tax_repartition_line_template -> account_account_tag [label="tag_ids"];
  account_tax_report_line -> account_report_expression [label="0..*"];
  account_tax_report_line -> account_tax_report_line [label="children_ids"];
  account_report_expression -> account_account_tag [label="Engine tax_tags 1..*"];
  account_tax_report -> res_country [label="0..1"];
  account_account_tag -> res_country [label="0..1"];
}

Nói một cách đơn giản, trong tax template, bạn chỉ định trong các dòng phân bổ (repartition) của hóa đơn/hoàn tiền xem phần cơ sở tính thuế hay một tỷ lệ phần trăm của thuế cần được báo cáo vào dòng báo cáo nào (thông qua các field minus/plus_report_line_ids). Điều này cũng sẽ rõ ràng hơn khi bạn kiểm tra cấu hình thuế trong giao diện Odoo (hoặc xem tài liệu Tài liệu tham khảo Tax, Tài liệu tham khảo Tax Repartition).

Vì vậy, sau khi đã cấu hình thuế đúng cách, bạn chỉ cần thêm file data/account_tax_report_data.xml với một bản ghi cho account.report của bạn. Để nó được xem là một báo cáo thuế, bạn cần cung cấp cho nó root_report_id phù hợp.

<odoo>
    <record id="tax_report" model="account.report">
        <field name="name">Tax Report</field>
        <field name="root_report_id" ref="account.generic_tax_report"/>
        <field name="country_id" ref="base.XX"/>
    </record>

    ...
</odoo>

... theo sau là khai báo các dòng của nó, dưới dạng các bản ghi account.report.line.

Vị trí tài khóa (Fiscal positions)

Khai báo các vị trí tài khóa trong file data/template/account.fiscal.position-xx.csv.

Các bước cuối cùng

Cuối cùng, bạn có thể thêm một công ty demo để việc bản địa hóa có thể dễ dàng được kiểm thử trong chế độ demo.

Báo cáo kế toán

Enterprise feature

Xem thêm

Báo cáo

Các báo cáo kế toán nên được thêm thông qua một module riêng l10n_XX_reports, module này nên được đưa vào enterprise repository.

File __manifest__.py cơ bản cho một module như vậy trông như sau:

{
    "name": "COUNTRY - Accounting Reports",
    "category": "Accounting/Localizations/Reporting",
    "version": "1.0.0",
    "license": "OEEL-1",
    "depends": [
        "l10n_XX", "account_reports"
    ],
    "data": [
        "data/balance_sheet.xml",
        "data/profit_and_loss.xml",
    ],
    "auto_install": True,
}

Tổng quan chức năng về các báo cáo tài chính có tại đây: Báo cáo.

Một số ví dụ tốt:

Bạn có thể xem ý nghĩa của các field tại đây:

Nếu bạn đã gán root_report_id cho báo cáo của mình, báo cáo đó giờ sẽ có mặt trong bộ chọn biến thể (variant selector) của nó. Nếu không, bạn vẫn cần thêm một mục menu cho nó. Một mục menu mặc định có thể được tạo từ form view của báo cáo bằng cách nhấp vào Actions ‣ Create Menu Item. Sau đó bạn cần làm mới trang để thấy nó. Ngoài ra, để tạo một phần riêng cho một báo cáo hoàn toàn mới trong menu Reporting, bạn cần tạo một bản ghi ir.ui.menu mới (thường nằm trong module l10n_XX chính) và một ir.actions.client mới (thường nằm trong file XML báo cáo mới) gọi đến account.report với report id mới. Sau đó, đặt menu mới này làm field parent_id trong action model.