Restore proprietary modules to tracking for private self-hosted Gitea repo
@@ -6,20 +6,5 @@ log/odoo.log
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# Modul OPL-1/Proprietary - DMCA risk - jangan di-commit ke public repo
|
||||
account_dynamic_reports/
|
||||
account_dynamic_reports_jabung/
|
||||
bi_project_issue_sequence/
|
||||
bi_project_task_sequence/
|
||||
bi_project_template/
|
||||
foss_project_tags/
|
||||
journal_sequence/
|
||||
ks_list_view_manager/
|
||||
kw_project_assign_wizard/
|
||||
kw_project_description/
|
||||
st_recurring_document_project_task/
|
||||
transport_management/
|
||||
ts_financial_statements/
|
||||
|
||||
# Backup bundle
|
||||
backup-all.bundle
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
# Dokumentasi Laporan Keuangan FASPE / SAK EP
|
||||
## Modul: `account_dynamic_reports` — Versi 14.2.0
|
||||
|
||||
---
|
||||
|
||||
## Daftar Isi
|
||||
|
||||
1. [Latar Belakang](#1-latar-belakang)
|
||||
2. [Ruang Lingkup Implementasi](#2-ruang-lingkup-implementasi)
|
||||
3. [Arsitektur Teknis](#3-arsitektur-teknis)
|
||||
4. [Model Data](#4-model-data)
|
||||
5. [Logika Bisnis & Alur Kalkulasi](#5-logika-bisnis--alur-kalkulasi)
|
||||
6. [Validasi dan Aturan Bisnis](#6-validasi-dan-aturan-bisnis)
|
||||
7. [Panduan Penggunaan (User Guide)](#7-panduan-penggunaan-user-guide)
|
||||
8. [Konfigurasi Prasyarat](#8-konfigurasi-prasyarat)
|
||||
9. [Hak Akses (Security)](#9-hak-akses-security)
|
||||
10. [Referensi File Kode](#10-referensi-file-kode)
|
||||
11. [Troubleshooting](#11-troubleshooting)
|
||||
12. [Changelog](#12-changelog)
|
||||
|
||||
---
|
||||
|
||||
## 1. Latar Belakang
|
||||
|
||||
### 1.1 Apa itu FASPE / SAK EP?
|
||||
|
||||
**FASPE** *(Financial Accounting Standards for Private Entities)* adalah versi bahasa Inggris dari **SAK EP** *(Standar Akuntansi Keuangan Entitas Privat)* — standar pelaporan keuangan yang diterbitkan oleh Ikatan Akuntan Indonesia (IAI) khusus untuk entitas privat (bukan entitas publik/Tbk).
|
||||
|
||||
SAK EP merupakan penyederhanaan dari SAK berbasis IFRS full, dirancang agar entitas non-publik dapat menyusun laporan keuangan yang informatif namun tidak terlalu kompleks. Standar ini menggantikan SAK ETAP (Entitas Tanpa Akuntabilitas Publik) secara bertahap.
|
||||
|
||||
### 1.2 Relevansi dalam Konteks Multi-Company
|
||||
|
||||
Dalam grup usaha yang terdiri dari beberapa entitas hukum (misalnya induk dan anak perusahaan), laporan keuangan konsolidasi diperlukan untuk memberikan gambaran posisi keuangan grup secara keseluruhan. Proses konsolidasi memerlukan:
|
||||
|
||||
- **Agregasi saldo** dari seluruh company dalam grup
|
||||
- **Eliminasi transaksi intra-grup** (piutang/utang antar company, investasi, dll.) agar tidak terjadi penghitungan ganda
|
||||
- **Penyajian bersih** atas posisi Aset, Liabilitas, dan Ekuitas setelah eliminasi
|
||||
|
||||
### 1.3 Fitur yang Dibangun
|
||||
|
||||
Implementasi ini menambahkan menu **Laporan Keuangan FASPE** yang berdiri sendiri di dalam modul `account_dynamic_reports` Odoo 14, terpisah dari laporan keuangan yang sudah ada (General Ledger, Trial Balance, dll.).
|
||||
|
||||
Fitur utama:
|
||||
- Pilih rentang periode bebas
|
||||
- Pilih 2 atau lebih company yang akan dikonsolidasi
|
||||
- Tentukan akun eliminasi per company secara spesifik
|
||||
- Tampilkan laporan neraca konsolidasi dengan kolom: Sebelum Eliminasi, Eliminasi, Setelah Eliminasi
|
||||
- Validasi otomatis (minimal 2 company, kesamaan mata uang, konsistensi tanggal)
|
||||
- Baris *check* otomatis: Aset − (Liabilitas + Ekuitas) harus = 0
|
||||
|
||||
---
|
||||
|
||||
## 2. Ruang Lingkup Implementasi
|
||||
|
||||
### 2.1 Yang Termasuk dalam Scope
|
||||
|
||||
| Komponen | Keterangan |
|
||||
|---|---|
|
||||
| Laporan Neraca Konsolidasi | Aset, Liabilitas, Ekuitas per akun, dikonsolidasikan dari semua company terpilih |
|
||||
| Eliminasi Intra-Grup | Per company, per akun. Saldo akun yang dieliminasi dikurangkan dari total konsolidasi |
|
||||
| Validasi input | Minimal 2 company, tanggal konsisten, mata uang sama |
|
||||
| Hasil read-only | Halaman laporan tidak bisa diedit/dihapus → hanya dibaca |
|
||||
| Balance check | Baris check otomatis menampilkan selisih Aset − (Liabilitas + Ekuitas) |
|
||||
|
||||
### 2.2 Yang Tidak Termasuk dalam Scope (di luar versi ini)
|
||||
|
||||
| Komponen | Keterangan |
|
||||
|---|---|
|
||||
| Laporan Laba Rugi Konsolidasi | Belum ada; hanya neraca (Balance Sheet) |
|
||||
| Ekspor PDF / XLSX | Belum diimplementasikan untuk FASPE |
|
||||
| Laporan Arus Kas Konsolidasi | Belum ada |
|
||||
| Jurnal eliminasi otomatis | Hanya kalkulasi di laporan saja, tidak membuat journal entries |
|
||||
| Multi-currency conversion | Tidak ada; semua company harus pakai mata uang sama |
|
||||
|
||||
---
|
||||
|
||||
## 3. Arsitektur Teknis
|
||||
|
||||
### 3.1 Pola 3-Layer Report
|
||||
|
||||
Mengikuti pola yang sama dengan semua report eksisting di modul `account_dynamic_reports`:
|
||||
|
||||
```
|
||||
[Menu Item]
|
||||
↓ trigger action
|
||||
[ir.actions.act_window] (target="new")
|
||||
↓ buka popup wizard
|
||||
[Wizard Form — ins.faspe.consolidated.wizard]
|
||||
↓ klik "Tampilkan Laporan"
|
||||
↓ action_generate_report() → create ins.faspe.consolidated.report
|
||||
[Report Form — ins.faspe.consolidated.report] (target="current", read-only)
|
||||
```
|
||||
|
||||
### 3.2 Diagram Alur Eksekusi
|
||||
|
||||
```
|
||||
User klik "Laporan Keuangan FASPE"
|
||||
│
|
||||
▼
|
||||
[Popup Wizard Form]
|
||||
- Pilih date_from, date_to
|
||||
- Pilih company_ids (min. 2)
|
||||
- Isi elimination_line_ids (opsional)
|
||||
- Klik "Tampilkan Laporan"
|
||||
│
|
||||
▼
|
||||
[action_generate_report()]
|
||||
1. _validate_input() → cek rules bisnis
|
||||
2. _prepare_account_payload() → read_group dari account.move.line
|
||||
3. _prepare_elimination_payload() → hitung eliminasi per akun
|
||||
4. _prepare_report_lines() → bangun list baris laporan
|
||||
5. create ins.faspe.consolidated.report + line_ids
|
||||
6. return act_window → buka halaman report
|
||||
│
|
||||
▼
|
||||
[Halaman Report — Read Only]
|
||||
- Header: judul, periode, company, currency
|
||||
- Ringkasan: total per section + selisih check
|
||||
- Tabel detail: per akun, 3 kolom nominal
|
||||
```
|
||||
|
||||
### 3.3 Stack Teknis
|
||||
|
||||
| Item | Nilai |
|
||||
|---|---|
|
||||
| Platform | Odoo 14.0-20231205 |
|
||||
| Python | 3.7.7 (`C:/odoo14c/python/python.exe`) |
|
||||
| Database | PostgreSQL, db: `kanjabung_MRP` |
|
||||
| Server | `C:/odoo14c/server/odoo-bin` |
|
||||
| Config | `C:/addon14/odoo.conf` |
|
||||
| Modul | `account_dynamic_reports` |
|
||||
| Model storage | `TransientModel` (tabel sementara, auto-vacuum) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Model Data
|
||||
|
||||
### 4.1 `ins.faspe.consolidated.wizard`
|
||||
|
||||
Wizard input pengguna. Bersifat sementara (`TransientModel`).
|
||||
|
||||
| Field | Tipe | Wajib | Keterangan |
|
||||
|---|---|---|---|
|
||||
| `title` | `Char` | — | Judul laporan, auto-fill, readonly |
|
||||
| `date_from` | `Date` | Ya | Tanggal awal periode; default 1 Jan tahun berjalan |
|
||||
| `date_to` | `Date` | Ya | Tanggal akhir periode; default hari ini |
|
||||
| `target_move` | `Selection` | Ya | `posted` = hanya entri terposting; `all` = semua |
|
||||
| `company_ids` | `Many2many` → `res.company` | Ya | Company yang dikonsolidasikan (min. 2) |
|
||||
| `company_id` | `Many2one` → `res.company` | Ya | Company utama (hidden, dipakai sebagai referensi currency) |
|
||||
| `currency_id` | `Many2one` → `res.currency` | — | Terambil otomatis dari `company_id`, readonly |
|
||||
| `elimination_line_ids` | `One2many` → `ins.faspe.consolidated.elimination.line` | — | Daftar akun eliminasi per company |
|
||||
|
||||
**Methods penting:**
|
||||
- `_onchange_company_ids()` — sinkronisasi baris eliminasi ketika pilihan company berubah
|
||||
- `_validate_input()` — pengecekan rules bisnis sebelum laporan digenerate
|
||||
- `_get_move_line_domain()` — membangun domain filter untuk query ke `account.move.line`
|
||||
- `_prepare_account_payload()` — eksekusi `read_group` dan aggregasi saldo per akun
|
||||
- `_prepare_elimination_payload()` — menghitung total eliminasi per akun berdasarkan `elimination_line_ids`
|
||||
- `_prepare_report_lines()` — merakit list baris laporan dengan urutan section/akun/total/check
|
||||
- `action_generate_report()` — entry point tombol "Tampilkan Laporan"
|
||||
|
||||
---
|
||||
|
||||
### 4.2 `ins.faspe.consolidated.elimination.line`
|
||||
|
||||
Baris eliminasi intra-grup, bersifat sementara.
|
||||
|
||||
| Field | Tipe | Wajib | Keterangan |
|
||||
|---|---|---|---|
|
||||
| `wizard_id` | `Many2one` → `ins.faspe.consolidated.wizard` | Ya | Parent wizard; cascade delete |
|
||||
| `company_id` | `Many2one` → `res.company` | Ya | Company pemilik akun yang dieliminasi |
|
||||
| `account_ids` | `Many2many` → `account.account` | — | Akun yang saldo-nya akan dieliminasi dari konsolidasi |
|
||||
|
||||
**Catatan domain:** `account_ids` dibatasi hanya akun milik `company_id` dengan `internal_group` = `asset`, `liability`, atau `equity`.
|
||||
|
||||
---
|
||||
|
||||
### 4.3 `ins.faspe.consolidated.report`
|
||||
|
||||
Header laporan hasil. Dibuat otomatis oleh `action_generate_report()`.
|
||||
|
||||
| Field | Tipe | Keterangan |
|
||||
|---|---|---|
|
||||
| `title` | `Char` | Judul dari wizard |
|
||||
| `generated_at` | `Datetime` | Timestamp pembuatan laporan |
|
||||
| `date_from` / `date_to` | `Date` | Rentang periode |
|
||||
| `target_move` | `Selection` | Filter entri |
|
||||
| `company_ids` | `Many2many` → `res.company` | Daftar company yang dikonsolidasikan |
|
||||
| `company_names` | `Char` | Nama company dirangkai dengan koma (untuk display) |
|
||||
| `currency_id` | `Many2one` → `res.currency` | Mata uang laporan |
|
||||
| `elimination_total` | `Monetary` | Total nominal akun yang dieliminasi |
|
||||
| `total_assets` | `Monetary` | Total saldo Aset setelah eliminasi |
|
||||
| `total_liabilities` | `Monetary` | Total saldo Liabilitas setelah eliminasi |
|
||||
| `total_equity` | `Monetary` | Total saldo Ekuitas setelah eliminasi |
|
||||
| `balance_difference` | `Monetary` | Aset − (Liabilitas + Ekuitas); idealnya = 0 |
|
||||
| `line_ids` | `One2many` → `ins.faspe.consolidated.report.line` | Baris detail |
|
||||
|
||||
---
|
||||
|
||||
### 4.4 `ins.faspe.consolidated.report.line`
|
||||
|
||||
Baris detail laporan, terurut berdasarkan `sequence, id`.
|
||||
|
||||
| Field | Tipe | Keterangan |
|
||||
|---|---|---|
|
||||
| `report_id` | `Many2one` | Parent report; cascade delete |
|
||||
| `sequence` | `Integer` | Urutan tampil (kelipatan 10) |
|
||||
| `line_type` | `Selection` | `section` / `account` / `total` / `check` |
|
||||
| `section` | `Selection` | `asset` / `liability` / `equity` / `check` |
|
||||
| `account_code` | `Char` | Kode akun (kosong untuk section/total/check) |
|
||||
| `name` | `Char` | Nama akun atau judul section |
|
||||
| `amount_before_elimination` | `Monetary` | Saldo agregat sebelum eliminasi |
|
||||
| `elimination_amount` | `Monetary` | Besaran yang dieliminasi |
|
||||
| `amount` | `Monetary` | Saldo bersih setelah eliminasi |
|
||||
|
||||
**Aturan tampil (dekorasi XML):**
|
||||
- `line_type = 'section'` atau `'total'` → **bold** (`decoration-bf`)
|
||||
- `line_type = 'check'` → warna biru info (`decoration-info`)
|
||||
|
||||
---
|
||||
|
||||
## 5. Logika Bisnis & Alur Kalkulasi
|
||||
|
||||
### 5.1 Pengambilan Data Saldo (`_prepare_account_payload`)
|
||||
|
||||
Query ke `account.move.line` menggunakan `read_group` dengan parameter:
|
||||
|
||||
```python
|
||||
aml_obj.read_group(
|
||||
domain=[
|
||||
('company_id', 'in', self.company_ids.ids),
|
||||
('account_id.internal_group', 'in', ['asset', 'liability', 'equity']),
|
||||
('date', '>=', self.date_from),
|
||||
('date', '<=', self.date_to),
|
||||
# + ('move_id.state', '=', 'posted') jika target_move = 'posted'
|
||||
],
|
||||
fields=['balance', 'account_id', 'company_id'],
|
||||
groupby=['account_id', 'company_id'],
|
||||
lazy=False,
|
||||
)
|
||||
```
|
||||
|
||||
Hasil dikelompokkan menjadi dua struktur:
|
||||
1. **`payload`** — saldo agregat per akun (lintas semua company): `{account_id: {code, name, internal_group, balance}}`
|
||||
2. **`balance_by_company_account`** — saldo per pasangan (company, akun): `{(company_id, account_id): balance}` — dipakai untuk menghitung eliminasi yang tepat.
|
||||
|
||||
### 5.2 Kalkulasi Eliminasi (`_prepare_elimination_payload`)
|
||||
|
||||
```
|
||||
Untuk setiap elimination_line:
|
||||
Untuk setiap account dalam elimination_line.account_ids:
|
||||
elimination_by_account[account_id] += balance_by_company_account[(company_id, account_id)]
|
||||
elimination_total += jumlah tersebut
|
||||
```
|
||||
|
||||
Eliminasi bersifat **additive per akun** — jika akun yang sama muncul di dua elimination_line berbeda (company berbeda), kedua nilainya dijumlahkan.
|
||||
|
||||
### 5.3 Perakitan Baris Laporan (`_prepare_report_lines`)
|
||||
|
||||
Urutan section: **ASET → LIABILITAS → EKUITAS**
|
||||
|
||||
Untuk setiap section:
|
||||
1. Baris header section (type = `section`, bold)
|
||||
2. Baris per akun, diurutkan berdasarkan `account_code` lalu `name` (type = `account`)
|
||||
3. Baris total section (type = `total`, bold)
|
||||
|
||||
Setelah semua section:
|
||||
4. Baris check (type = `check`, warna biru):
|
||||
`CHECK ASET - (LIABILITAS + EKUITAS)` = `total_assets - (total_liabilities + total_equity)`
|
||||
|
||||
**Catatan:** Akun dengan saldo nol (setelah eliminasi) **dan** eliminasi nol akan di-skip (`continue`) — tidak dimunculkan di laporan agar tetap ringkas.
|
||||
|
||||
### 5.4 Rumus Keseimbangan Neraca
|
||||
|
||||
$$\text{balance\_difference} = \text{Total Aset} - (\text{Total Liabilitas} + \text{Total Ekuitas})$$
|
||||
|
||||
Nilai ini idealnya = **0**. Jika tidak nol, dapat mengindikasikan:
|
||||
- Ada akun yang `internal_group`-nya salah di Chart of Accounts
|
||||
- Ada eliminasi yang tidak seimbang (mengeliminasi satu sisi tanpa sisi lawan)
|
||||
- Ada jurnal yang belum di-posting (jika filter = `posted`)
|
||||
|
||||
---
|
||||
|
||||
## 6. Validasi dan Aturan Bisnis
|
||||
|
||||
Semua validasi dijalankan di method `_validate_input()` sebelum query ke database.
|
||||
|
||||
| # | Kondisi yang Dicek | Pesan Error |
|
||||
|---|---|---|
|
||||
| 1 | `len(company_ids) < 2` | "Pilih minimal 2 company untuk laporan consolidated multi company." |
|
||||
| 2 | `date_from` atau `date_to` kosong | "Tanggal Mulai dan Tanggal Akhir wajib diisi." |
|
||||
| 3 | `date_from > date_to` | "Tanggal Mulai tidak boleh lebih besar dari Tanggal Akhir." |
|
||||
| 4 | Company-company memiliki mata uang berbeda | "Semua company yang dikonsolidasikan harus menggunakan mata uang yang sama agar presisi laporan terjaga." |
|
||||
|
||||
Selain itu, setelah query jika tidak ada data:
|
||||
|
||||
| # | Kondisi | Pesan Error |
|
||||
|---|---|---|
|
||||
| 5 | `payload` kosong (tidak ada move line) | "Tidak ada data akun neraca pada rentang tanggal dan filter yang dipilih." |
|
||||
|
||||
---
|
||||
|
||||
## 7. Panduan Penggunaan (User Guide)
|
||||
|
||||
### 7.1 Mengakses Menu
|
||||
|
||||
1. Login ke Odoo sebagai user dengan role **Accounting / Accountant** atau **Accounting / Adviser**
|
||||
2. Buka modul **Accounting**
|
||||
3. Di menu laporan (biasanya **Accounting → Reports** atau sesuai konfigurasi menu `account_reports_ins`), pilih **"Laporan Keuangan FASPE"**
|
||||
|
||||
### 7.2 Mengisi Wizard Input
|
||||
|
||||
Popup wizard akan muncul dengan bagian:
|
||||
|
||||
**Bagian Periode:**
|
||||
| Field | Cara Isi |
|
||||
|---|---|
|
||||
| Tanggal Mulai | Pilih tanggal awal periode laporan |
|
||||
| Tanggal Akhir | Pilih tanggal akhir periode laporan |
|
||||
| Target Moves | Pilih `All Posted Entries` untuk hanya memuat jurnal yang sudah di-posting, atau `All Entries` untuk semua |
|
||||
|
||||
**Bagian Konsolidasi Multi Company:**
|
||||
| Field | Cara Isi |
|
||||
|---|---|
|
||||
| Companies | Pilih 2 atau lebih company yang akan dikonsolidasikan. Field ini hanya tampil jika user aktif di lingkungan multi-company (`base.group_multi_company`). |
|
||||
| Currency | Terisi otomatis dari company utama; pastikan semua company terpilih menggunakan mata uang yang sama. |
|
||||
|
||||
**Bagian Akun Eliminasi per Company:**
|
||||
|
||||
Setelah memilih company, tabel eliminasi akan terisi otomatis dengan satu baris per company. Isi kolom **Akun yang Di-eliminasi** untuk setiap company:
|
||||
- Klik pada kolom akun di baris company yang ingin diatur
|
||||
- Pilih satu atau lebih akun yang saldo-nya harus dieliminasi (misalnya: akun piutang antar company, akun investasi pada anak perusahaan)
|
||||
- Daftar akun dibatasi hanya akun kepunyaan company tersebut dengan `internal_group` = Aset/Liabilitas/Ekuitas
|
||||
|
||||
> **Catatan:** Bagian eliminasi bersifat opsional. Jika tidak diisi, laporan akan menampilkan konsolidasi tanpa eliminasi.
|
||||
|
||||
### 7.3 Membaca Halaman Laporan
|
||||
|
||||
Setelah klik **"Tampilkan Laporan"**, halaman laporan akan terbuka dengan:
|
||||
|
||||
**Header:**
|
||||
- Judul laporan
|
||||
- Tanggal dibuat (`generated_at`)
|
||||
- Filter target entries
|
||||
- Periode laporan
|
||||
- Daftar company yang dikonsolidasikan
|
||||
|
||||
**Ringkasan Konsolidasi:**
|
||||
| Field | Arti |
|
||||
|---|---|
|
||||
| Total Eliminasi | Jumlah nominal seluruh akun yang dieliminasi |
|
||||
| Total Aset | Saldo aset konsolidasi bersih setelah eliminasi |
|
||||
| Total Liabilitas | Saldo liabilitas konsolidasi bersih setelah eliminasi |
|
||||
| Total Ekuitas | Saldo ekuitas konsolidasi bersih setelah eliminasi |
|
||||
| Selisih Aset − (Liabilitas + Ekuitas) | Idealnya = 0; jika tidak nol, perlu investigasi |
|
||||
|
||||
**Tabel Detail Laporan:**
|
||||
|
||||
Tabel berisi kolom:
|
||||
- **Kode Akun** — kode akun Chart of Accounts
|
||||
- **Nama** — nama akun atau judul section/total
|
||||
- **Sebelum Eliminasi** — saldo agregat dari semua company sebelum eliminasi diterapkan
|
||||
- **Eliminasi** — nominal yang dieliminasi untuk akun tersebut
|
||||
- **Setelah Eliminasi** — saldo bersih: Sebelum Eliminasi − Eliminasi
|
||||
|
||||
Penyajian tabel:
|
||||
- Baris **section** (ASET, LIABILITAS, EKUITAS) dan baris **total** ditampilkan **tebal** (bold)
|
||||
- Baris **check** ditampilkan dengan warna **biru**
|
||||
- Baris akun ditampilkan normal, diurutkan berdasarkan kode akun
|
||||
|
||||
### 7.4 Menutup dan Membuat Laporan Baru
|
||||
|
||||
- Laporan bersifat **TransientModel** — data otomatis terhapus setelah sesi berakhir atau setelah vacuum oleh Odoo
|
||||
- Untuk membuat laporan baru dengan periode/company berbeda, kembali ke menu dan buka wizard kembali
|
||||
- Halaman laporan bersifat **read-only** — tidak bisa diedit atau dihapus secara manual
|
||||
|
||||
---
|
||||
|
||||
## 8. Konfigurasi Prasyarat
|
||||
|
||||
### 8.1 Multi-Company Odoo
|
||||
|
||||
Fitur ini membutuhkan setup multi-company di Odoo:
|
||||
1. Masuk ke **Settings → Companies** — tambahkan minimal 2 company
|
||||
2. Pastikan user memiliki akses ke semua company yang akan dikonsolidasikan (**Settings → Users → [user] → Companies**)
|
||||
3. Aktifkan fitur multi-company: **Settings → General Settings → Companies → Allow multi-company**
|
||||
|
||||
### 8.2 Chart of Accounts
|
||||
|
||||
Setiap company harus memiliki **Chart of Accounts** yang sudah dikonfigurasi dengan benar, khususnya:
|
||||
- Field `internal_group` pada setiap akun harus diset: `asset`, `liability`, atau `equity` (bukan `income`, `expense`, atau `off_balance`)
|
||||
- Tanpa konfigurasi ini, saldo akun tersebut tidak akan muncul di laporan FASPE
|
||||
|
||||
### 8.3 Mata Uang
|
||||
|
||||
- Semua company yang akan dikonsolidasikan **harus menggunakan mata uang yang sama**
|
||||
- Konfigurasi di: **Settings → Companies → [company] → Currency**
|
||||
- Konsolidasi multi-currency belum didukung di versi ini
|
||||
|
||||
### 8.4 Journal Entries
|
||||
|
||||
- Pastikan semua jurnal relevan sudah dalam status **Posted** jika menggunakan filter `All Posted Entries`
|
||||
- Gunakan filter `All Entries` untuk memasukkan draft entries dalam laporan (berguna untuk preview)
|
||||
|
||||
---
|
||||
|
||||
## 9. Hak Akses (Security)
|
||||
|
||||
Empat model baru terdaftar di `security/ir.model.access.csv` dengan akses penuh bagi group `account.group_account_user` (Accounting User):
|
||||
|
||||
| Access ID | Model | Group | Read | Write | Create | Delete |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `access_ins_faspe_consolidated_wizard` | `ins.faspe.consolidated.wizard` | `account.group_account_user` | 1 | 1 | 1 | 1 |
|
||||
| `access_ins_faspe_consolidated_elimination_line` | `ins.faspe.consolidated.elimination.line` | `account.group_account_user` | 1 | 1 | 1 | 1 |
|
||||
| `access_ins_faspe_consolidated_report` | `ins.faspe.consolidated.report` | `account.group_account_user` | 1 | 1 | 1 | 1 |
|
||||
| `access_ins_faspe_consolidated_report_line` | `ins.faspe.consolidated.report.line` | `account.group_account_user` | 1 | 1 | 1 | 1 |
|
||||
|
||||
Menu item `account_report_faspe_consolidated` juga dikunci dengan `groups="account.group_account_user"`, sehingga hanya akuntan yang bisa mengaksesnya.
|
||||
|
||||
---
|
||||
|
||||
## 10. Referensi File Kode
|
||||
|
||||
| File | Peran |
|
||||
|---|---|
|
||||
| `wizard/faspe_consolidated_report.py` | Core logic: 4 model TransientModel, semua method kalkulasi |
|
||||
| `wizard/faspe_consolidated_report_view.xml` | Definisi view wizard input, form laporan, action, menuitem |
|
||||
| `wizard/__init__.py` | Import `faspe_consolidated_report` |
|
||||
| `__manifest__.py` | Versi `14.2.0`; `faspe_consolidated_report_view.xml` di list `data` |
|
||||
| `security/ir.model.access.csv` | 4 baris akses untuk model baru |
|
||||
|
||||
### 10.1 Ringkasan Perubahan dari Versi 14.1.0 ke 14.2.0
|
||||
|
||||
```
|
||||
+ wizard/faspe_consolidated_report.py (BARU)
|
||||
+ wizard/faspe_consolidated_report_view.xml (BARU)
|
||||
~ wizard/__init__.py (tambah import)
|
||||
~ __manifest__.py (versi + data entry baru)
|
||||
~ security/ir.model.access.csv (4 baris baru)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Troubleshooting
|
||||
|
||||
### 11.1 "Pilih minimal 2 company..."
|
||||
|
||||
**Penyebab:** User hanya memilih 1 company (atau tidak ada) di field `Companies`.
|
||||
|
||||
**Solusi:** Pastikan minimal 2 company dipilih. Jika field `Companies` tidak tampil, user mungkin tidak diaktifkan dalam mode multi-company — aktifkan terlebih dahulu di **Settings → Users → [user] → Companies**.
|
||||
|
||||
---
|
||||
|
||||
### 11.2 "Semua company yang dikonsolidasikan harus menggunakan mata uang yang sama..."
|
||||
|
||||
**Penyebab:** Company-company yang dipilih memiliki pengaturan `Currency` berbeda.
|
||||
|
||||
**Solusi:** Samakan mata uang di **Settings → Companies → [tiap company] → Currency**. Jika membutuhkan multi-currency, fitur ini belum tersedia di versi ini.
|
||||
|
||||
---
|
||||
|
||||
### 11.3 "Tidak ada data akun neraca pada rentang tanggal..."
|
||||
|
||||
**Penyebab:** Tidak ada `account.move.line` yang memenuhi semua kondisi filter:
|
||||
- Company dalam `company_ids`
|
||||
- `account_id.internal_group` = asset/liability/equity
|
||||
- `date` dalam rentang `date_from` – `date_to`
|
||||
- (opsional) `move_id.state = posted`
|
||||
|
||||
**Solusi:**
|
||||
1. Perluas rentang tanggal
|
||||
2. Coba ganti filter ke `All Entries`
|
||||
3. Pastikan Chart of Accounts sudah ada dan transaksi sudah diinput
|
||||
|
||||
---
|
||||
|
||||
### 11.4 `balance_difference` Tidak Nol
|
||||
|
||||
**Penyebab umum:**
|
||||
- Ada akun dengan `internal_group` yang tidak tepat (misalnya akun modal di-set sebagai `asset`)
|
||||
- Eliminasi tidak seimbang (mengeliminasi satu sisi tanpa pasangannya — misalnya hanya mengeliminasi piutang antar-company tanpa mengeliminasi utang antar-company yang sesuai)
|
||||
- Ada transaksi yang belum di-posting (gunakan `All Entries` untuk cek)
|
||||
|
||||
**Langkah investigasi:**
|
||||
1. Lihat kolom **Sebelum Eliminasi** — sudah seimbang sebelum eliminasi? Jika tidak, periksa `internal_group` akun
|
||||
2. Lihat kolom **Eliminasi** — apakah eliminasi dilakukan di kedua sisi? Misalnya eliminasi piutang company A sekaligus eliminasi utang company B
|
||||
|
||||
---
|
||||
|
||||
### 11.5 Menu "Laporan Keuangan FASPE" Tidak Muncul
|
||||
|
||||
**Kemungkinan penyebab:**
|
||||
1. Modul belum di-upgrade setelah update kode
|
||||
2. User tidak memiliki group `account.group_account_user`
|
||||
3. Cache browser
|
||||
|
||||
**Solusi:**
|
||||
```powershell
|
||||
# Upgrade modul
|
||||
C:/odoo14c/python/python.exe C:/odoo14c/server/odoo-bin `
|
||||
--config=C:/addon14/odoo.conf `
|
||||
--database=kanjabung_MRP `
|
||||
--without-demo=all `
|
||||
-u account_dynamic_reports `
|
||||
--stop-after-init
|
||||
```
|
||||
Kemudian refresh browser (Ctrl+F5).
|
||||
|
||||
---
|
||||
|
||||
### 11.6 Error Saat Upgrade — `ParseError` pada XML
|
||||
|
||||
**Catatan historis:** Error ini sudah diperbaiki. Odoo 14 menolak tag `<label string="..." />` tanpa atribut `for` di dalam form view. Solusi yang diterapkan: diganti dengan `<div class="o_form_label text-muted">...</div>`.
|
||||
|
||||
---
|
||||
|
||||
## 12. Changelog
|
||||
|
||||
### Versi 14.2.0 (2026-03)
|
||||
|
||||
**Tambahan Baru:**
|
||||
- ✅ Model `ins.faspe.consolidated.wizard` — wizard input multi-company
|
||||
- ✅ Model `ins.faspe.consolidated.elimination.line` — konfigurasi eliminasi per company
|
||||
- ✅ Model `ins.faspe.consolidated.report` — header laporan hasil konsolidasi
|
||||
- ✅ Model `ins.faspe.consolidated.report.line` — baris detail laporan
|
||||
- ✅ View wizard form dengan bagian periode, multi-company, dan eliminasi
|
||||
- ✅ View laporan read-only dengan ringkasan dan tabel detail
|
||||
- ✅ Menu **Laporan Keuangan FASPE** di bawah grup laporan akuntansi (`sequence=90`)
|
||||
- ✅ Hak akses untuk 4 model baru (`account.group_account_user`)
|
||||
- ✅ Validasi bisnis: min. 2 company, mata uang sama, konsistensi tanggal
|
||||
- ✅ Baris check otomatis: Aset − (Liabilitas + Ekuitas)
|
||||
|
||||
### Versi 14.1.0 (sebelumnya)
|
||||
|
||||
- Versi dasar dengan General Ledger, Partner Ledger, Trial Balance, Partner Ageing, Financial Report
|
||||
- Penambahan field `company_ids` (Many2many) di semua wizard report lama untuk dukungan multi-company
|
||||
|
||||
---
|
||||
|
||||
*Dokumen ini dibuat berdasarkan implementasi modul `account_dynamic_reports` versi 14.2.0 pada Odoo 14.0.*
|
||||
@@ -0,0 +1,27 @@
|
||||
Odoo Proprietary License v1.0
|
||||
|
||||
This software and associated files (the "Software") may only be used (executed,
|
||||
modified, executed after modifications) if you have purchased a valid license
|
||||
from the authors, typically via Odoo Apps, or if you have received a written
|
||||
agreement from the authors of the Software (see the COPYRIGHT file).
|
||||
|
||||
You may develop Odoo modules that use the Software as a library (typically
|
||||
by depending on it, importing it and using its resources), but without copying
|
||||
any source code or material from the Software. You may distribute those
|
||||
modules under the license of your choice, provided that this license is
|
||||
compatible with the terms of the Odoo Proprietary License (For example:
|
||||
LGPL, MIT, or proprietary licenses similar to this one).
|
||||
|
||||
It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
or modified copies of the Software.
|
||||
|
||||
The above copyright notice and this permission notice must be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,4 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import report
|
||||
from . import controllers
|
||||
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name' : 'All in one Dynamic Financial Reports v14',
|
||||
'version' : '14.2.1',
|
||||
'summary': "General Ledger Trial Balance Ageing Balance Sheet Profit and Loss Cash Flow Dynamic",
|
||||
'sequence': 15,
|
||||
'description': """
|
||||
Odoo 14 Full Accouning, Odoo 14 All in one Accouning, PDF Reports, XLSX Reports,
|
||||
Dynamic View, Drill down, Clickable, Pdf and Xlsx package, Odoo 14 Accounting,
|
||||
Full Account Reports, Complete Accounting Reports, Financial Report for Odoo 13,
|
||||
Financial Reports, Excel reports, Financial Reports in Excel, Ageing Report,
|
||||
General Ledger, Partner Ledger, Trial Balance, Balance Sheet, Profit and Loss,
|
||||
Financial Report Kit, Cash Flow Statements, Cash Flow Report, Cash flow, Dynamic reports,
|
||||
Dynamic accounting, Dynamic financial
|
||||
""",
|
||||
'category': 'Accounting/Accounting',
|
||||
"price": 100,
|
||||
'author': 'Pycus',
|
||||
'maintainer': 'Pycus Technologies',
|
||||
'website': '',
|
||||
'images': ['static/description/banner.gif'],
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/data_financial_report.xml',
|
||||
'data/data_financial_report_fix.xml',
|
||||
|
||||
'views/assets.xml',
|
||||
'views/views.xml',
|
||||
'views/res_company_view.xml',
|
||||
|
||||
'views/general_ledger_view.xml',
|
||||
'views/partner_ledger_view.xml',
|
||||
'views/trial_balance_view.xml',
|
||||
'views/partner_ageing_view.xml',
|
||||
'views/financial_report_view.xml',
|
||||
|
||||
'wizard/general_ledger_view.xml',
|
||||
'wizard/partner_ledger_view.xml',
|
||||
'wizard/trial_balance_view.xml',
|
||||
'wizard/partner_ageing_view.xml',
|
||||
'wizard/financial_report_view.xml',
|
||||
'wizard/faspe_consolidated_report_view.xml',
|
||||
],
|
||||
'demo': [],
|
||||
'license': 'OPL-1',
|
||||
'qweb': ['static/src/xml/view.xml'],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
from . import main
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
from odoo import http
|
||||
from odoo.http import content_disposition, request
|
||||
from odoo.addons.web.controllers.main import _serialize_exception
|
||||
from odoo.tools import html_escape
|
||||
|
||||
|
||||
class XLSXReportController(http.Controller):
|
||||
|
||||
@http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False)
|
||||
def get_report_xlsx(self, model, options, output_format, token, report_name, **kw):
|
||||
uid = request.session.uid
|
||||
report_obj = request.env[model].with_user(uid)
|
||||
options = json.loads(options)
|
||||
try:
|
||||
if output_format == 'xlsx':
|
||||
response = request.make_response(
|
||||
None,
|
||||
headers=[
|
||||
('Content-Type', 'application/vnd.ms-excel'),
|
||||
('Content-Disposition', content_disposition(report_name + '.xlsx'))
|
||||
]
|
||||
)
|
||||
report_obj.get_xlsx_report(options, response)
|
||||
response.set_cookie('fileToken', token)
|
||||
return response
|
||||
except Exception as e:
|
||||
se = _serialize_exception(e)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': 'Odoo Server Error',
|
||||
'data': se
|
||||
}
|
||||
return request.make_response(html_escape(json.dumps(error)))
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Financial Reports -->
|
||||
<record id="ins_account_financial_report_profitandloss0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Profit and Loss</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="sign">-1</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Income</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_gross_profit0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Gross Profit</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_operating_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Operating Income</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_gross_profit0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_revenue'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_cost_of_revenue0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Cost of Revenue</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_gross_profit0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_direct_costs'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_other_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Other Income</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_other_income'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_expense0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Expense</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_expenses')), (4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_balancesheet0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">BALANCE SHEET</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_assets0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">ASSETS</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_receivable')), (4,ref('account.data_account_type_liquidity')), (4,ref('account.data_account_type_current_assets')), (4,ref('account.data_account_type_non_current_assets')), (4,ref('account.data_account_type_prepayments')), (4,ref('account.data_account_type_fixed_assets'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_liabilitysum0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">LIABILITIES</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_liability0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Liability</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_liabilitysum0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_payable')), (4,ref('account.data_account_type_current_liabilities')), (4,ref('account.data_account_type_non_current_liabilities'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_equitysum0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">EQUITY</field>
|
||||
<field name="sequence">300</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_retained_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Retained Earnings</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_equitysum0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_equity'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Unallocated Earnings</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_equitysum0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Unallocated Earnings</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Earnings</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_current_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_other_income')),
|
||||
(4,ref('account.data_account_type_revenue')),
|
||||
(4,ref('account.data_account_type_expenses')),
|
||||
(4,ref('account.data_account_type_direct_costs')),
|
||||
(4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_allocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Allocated Earnings</field>
|
||||
<field name="sign">-1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_current_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_unaffected_earnings'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_previous_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Previous Unallocated Earnings</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">initial_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_unaffected_earnings')),
|
||||
(4,ref('account.data_account_type_other_income')),
|
||||
(4,ref('account.data_account_type_revenue')),
|
||||
(4,ref('account.data_account_type_expenses')),
|
||||
(4,ref('account.data_account_type_direct_costs')),
|
||||
(4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_cash_flow0" model="ins.account.financial.report">
|
||||
<field name="name">Cash Flow Statement</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="display_detail">no_detail</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_operation0" model="ins.account.financial.report">
|
||||
<field name="name">Operations</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_investing_activity0" model="ins.account.financial.report">
|
||||
<field name="name">Investing Activities</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_financing_activity1" model="ins.account.financial.report">
|
||||
<field name="name">Financing Activities</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="ins_cash_in_operation_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_operation_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_cash_in_investing_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_investing_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_cash_in_financial_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_financial_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<function model="ins.account.financial.report"
|
||||
name="ensure_balance_sheet_asset_account_types"/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
from . import res_company
|
||||
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
strict_range = fields.Boolean(string='Use Strict Range', default=True,
|
||||
help='Use this if you want to show TB with retained earnings section')
|
||||
bucket_1 = fields.Integer(string='Bucket 1', required=True, default=10)
|
||||
bucket_2 = fields.Integer(string='Bucket 2', required=True, default=30)
|
||||
bucket_3 = fields.Integer(string='Bucket 3', required=True, default=60)
|
||||
bucket_4 = fields.Integer(string='Bucket 4', required=True, default=90)
|
||||
date_range = fields.Selection(
|
||||
[('today', 'Today'),
|
||||
('this_week', 'This Week'),
|
||||
('this_month', 'This Month'),
|
||||
('this_quarter', 'This Quarter'),
|
||||
('this_financial_year', 'This financial Year'),
|
||||
('yesterday', 'Yesterday'),
|
||||
('last_week', 'Last Week'),
|
||||
('last_month', 'Last Month'),
|
||||
('last_quarter', 'Last Quarter'),
|
||||
('last_financial_year', 'Last Financial Year')],
|
||||
string='Default Date Range', default='this_financial_year', required=True
|
||||
)
|
||||
financial_year = fields.Selection([
|
||||
('april_march','1 April to 31 March'),
|
||||
('july_june','1 july to 30 June'),
|
||||
('january_december','1 Jan to 31 Dec')
|
||||
], string='Financial Year', default='january_december', required=True)
|
||||
|
||||
|
||||
class ResCurrency(models.Model):
|
||||
_inherit = 'res.currency'
|
||||
|
||||
excel_format = fields.Char(string='Excel format', default='_ * #,##0.00_) ;_ * - #,##0.00_) ;_ * "-"??_) ;_ @_ ', required=True)
|
||||
|
||||
class ins_account_financial_report(models.Model):
|
||||
_name = "ins.account.financial.report"
|
||||
_description = "Account Report"
|
||||
|
||||
@api.model
|
||||
def ensure_balance_sheet_asset_account_types(self):
|
||||
report = self.env.ref(
|
||||
'account_dynamic_reports.ins_account_financial_report_assets0',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
if not report:
|
||||
return False
|
||||
xmlids = [
|
||||
'account.data_account_type_receivable',
|
||||
'account.data_account_type_liquidity',
|
||||
'account.data_account_type_current_assets',
|
||||
'account.data_account_type_non_current_assets',
|
||||
'account.data_account_type_prepayments',
|
||||
'account.data_account_type_fixed_assets',
|
||||
]
|
||||
account_type_ids = []
|
||||
for xmlid in xmlids:
|
||||
account_type = self.env.ref(xmlid, raise_if_not_found=False)
|
||||
if account_type:
|
||||
account_type_ids.append(account_type.id)
|
||||
missing_ids = [
|
||||
account_type_id
|
||||
for account_type_id in account_type_ids
|
||||
if account_type_id not in report.account_type_ids.ids
|
||||
]
|
||||
if missing_ids:
|
||||
report.write({
|
||||
'account_type_ids': [(4, account_type_id) for account_type_id in missing_ids]
|
||||
})
|
||||
return True
|
||||
|
||||
@api.depends('parent_id', 'parent_id.level')
|
||||
def _get_level(self):
|
||||
'''Returns a dictionary with key=the ID of a record and value = the level of this
|
||||
record in the tree structure.'''
|
||||
for report in self:
|
||||
level = 0
|
||||
if report.parent_id:
|
||||
level = report.parent_id.level + 1
|
||||
report.level = level
|
||||
|
||||
def _get_children_by_order(self, strict_range):
|
||||
'''returns a recordset of all the children computed recursively, and sorted by sequence. Ready for the printing'''
|
||||
res = self
|
||||
children = self.search([('parent_id', 'in', self.ids)], order='sequence ASC')
|
||||
if children:
|
||||
for child in children:
|
||||
res += child._get_children_by_order(strict_range)
|
||||
if not strict_range:
|
||||
res -= self.env.ref('account_dynamic_reports.ins_account_financial_report_unallocated_earnings0')
|
||||
res -= self.env.ref('account_dynamic_reports.ins_account_financial_report_equitysum0')
|
||||
return res
|
||||
|
||||
name = fields.Char('Report Name', required=True, translate=True)
|
||||
parent_id = fields.Many2one('ins.account.financial.report', 'Parent')
|
||||
children_ids = fields.One2many('ins.account.financial.report', 'parent_id', 'Account Report')
|
||||
sequence = fields.Integer('Sequence')
|
||||
level = fields.Integer(compute='_get_level', string='Level', store=True)
|
||||
type = fields.Selection([
|
||||
('sum', 'View'),
|
||||
('accounts', 'Accounts'),
|
||||
('account_type', 'Account Type'),
|
||||
('account_report', 'Report Value'),
|
||||
], 'Type', default='sum')
|
||||
account_ids = fields.Many2many('account.account', 'ins_account_account_financial_report', 'report_line_id', 'account_id', 'Accounts')
|
||||
account_report_id = fields.Many2one('ins.account.financial.report', 'Report Value')
|
||||
account_type_ids = fields.Many2many('account.account.type', 'ins_account_account_financial_report_type', 'report_id', 'account_type_id', 'Account Types')
|
||||
sign = fields.Selection([('-1', 'Reverse balance sign'), ('1', 'Preserve balance sign')], 'Sign on Reports', required=True, default='1',
|
||||
help='For accounts that are typically more debited than credited and that you would like to print as negative amounts in your reports, you should reverse the sign of the balance; e.g.: Expense account. The same applies for accounts that are typically more credited than debited and that you would like to print as positive amounts in your reports; e.g.: Income account.')
|
||||
range_selection = fields.Selection([
|
||||
('from_the_beginning', 'From the Beginning'),
|
||||
('current_date_range', 'Based on Current Date Range'),
|
||||
('initial_date_range', 'Based on Initial Date Range')],
|
||||
help='"From the beginning" will select all the entries before and on the date range selected.'
|
||||
'"Based on Current Date Range" will select all the entries strictly on the date range selected'
|
||||
'"Based on Initial Date Range" will select only the initial balance for the selected date range',
|
||||
string='Custom Date Range')
|
||||
display_detail = fields.Selection([
|
||||
('no_detail', 'No detail'),
|
||||
('detail_flat', 'Display children flat'),
|
||||
('detail_with_hierarchy', 'Display children with hierarchy')
|
||||
], 'Display details', default='detail_flat')
|
||||
style_overwrite = fields.Selection([
|
||||
('0', 'Automatic formatting'),
|
||||
('1', 'Main Title 1 (bold, underlined)'),
|
||||
('2', 'Title 2 (bold)'),
|
||||
('3', 'Title 3 (bold, smaller)'),
|
||||
('4', 'Normal Text'),
|
||||
('5', 'Italic Text (smaller)'),
|
||||
('6', 'Smallest Text'),
|
||||
], 'Financial Report Style', default='0',
|
||||
help="You can set up here the format you want this record to be displayed. If you leave the automatic formatting, it will be computed based on the financial reports hierarchy (auto-computed field 'level').")
|
||||
|
||||
|
||||
class AccountAccount(models.Model):
|
||||
_inherit = 'account.account'
|
||||
|
||||
def get_cashflow_domain(self):
|
||||
cash_flow_id = self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0')
|
||||
if cash_flow_id:
|
||||
return [('parent_id.id', '=', cash_flow_id.id)]
|
||||
|
||||
cash_flow_category = fields.Many2one('ins.account.financial.report', string="Cash Flow type", domain=get_cashflow_domain)
|
||||
|
||||
@api.onchange('cash_flow_category')
|
||||
def onchange_cash_flow_category(self):
|
||||
# Add account to cash flow record to account_ids
|
||||
if self._origin and self._origin.id:
|
||||
self.cash_flow_category.write({'account_ids': [(4, self._origin.id)]})
|
||||
self.env.ref(
|
||||
'account_dynamic_reports.ins_account_financial_report_cash_flow0').write(
|
||||
{'account_ids': [(4, self._origin.id)]})
|
||||
# Remove account from previous category
|
||||
# In case of changing/ removing category
|
||||
if self._origin.cash_flow_category:
|
||||
self._origin.cash_flow_category.write({'account_ids': [(3, self._origin.id)]})
|
||||
self.env.ref(
|
||||
'account_dynamic_reports.ins_account_financial_report_cash_flow0').write(
|
||||
{'account_ids': [(3, self._origin.id)]})
|
||||
@@ -0,0 +1,10 @@
|
||||
from . import report_general_ledger
|
||||
#from . import report_general_ledger_xlsx
|
||||
from . import report_partner_ledger
|
||||
#from . import report_partner_ledger_xlsx
|
||||
from . import report_trial_balance
|
||||
#from . import report_trial_balance_xlsx
|
||||
from . import report_partner_ageing
|
||||
#from . import report_partner_ageing_xlsx
|
||||
from . import report_financial_report
|
||||
#from . import report_financial_report_xlsx
|
||||
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class FinancialReportPdf(models.AbstractModel):
|
||||
""" Abstract model for generating PDF report value and send to template common for P and L and Balance Sheet"""
|
||||
|
||||
_name = 'report.account_dynamic_reports.ins_report_financial'
|
||||
_description = 'Financial Report'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
""" Provide report values to template """
|
||||
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({
|
||||
'data': data.get('js_data'),
|
||||
'report_lines': data['js_data']['report_lines'],
|
||||
'account_report': data['js_data']['form']['account_report_id'][1],
|
||||
'currency': data['js_data']['currency'],
|
||||
})
|
||||
return data
|
||||
|
||||
ctx = {
|
||||
'data': data,
|
||||
'report_lines': data['report_lines'],
|
||||
'account_report': data['form']['account_report_id'][1],
|
||||
'currency': data['currency'],
|
||||
}
|
||||
return ctx
|
||||
@@ -0,0 +1,274 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsFinancialReportXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.ins_financial_report_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'bottom': False
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
#'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_string = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_string_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['date_from'] and filter['form']['date_from'].strftime('%Y-%m-%d'))
|
||||
if filter['form'].get('date_from'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
# Date to
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['date_to'] and filter['form']['date_to'].strftime('%Y-%m-%d'))
|
||||
if filter['form'].get('date_to'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
analytics = ', '.join(filter['form'].get('selected_analytics', []))
|
||||
if analytics:
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, analytics,
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
if filter['form']['enable_filter']:
|
||||
|
||||
# Compariosn Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Comparison Date from'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['comparison_context']['date_from'] and filter['form']['comparison_context']['date_from'].strftime('%Y-%m-%d'))
|
||||
if filter['form']['comparison_context'].get('date_from'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
# Compariosn Date to
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Comparison Date to'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['comparison_context']['date_to'] and filter['form']['comparison_context']['date_to'].strftime('%Y-%m-%d'))
|
||||
if filter['form']['comparison_context'].get('date_to'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
|
||||
def prepare_report_contents(self, data):
|
||||
self.row_pos += 3
|
||||
|
||||
if data['form']['debit_credit'] == 1:
|
||||
|
||||
self.sheet.set_column(0, 0, 90)
|
||||
self.sheet.set_column(1, 1, 15)
|
||||
self.sheet.set_column(2, 3, 15)
|
||||
self.sheet.set_column(3, 3, 15)
|
||||
|
||||
self.sheet.write_string(self.row_pos, 0, _('Name'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
self.row_pos += 1
|
||||
self.row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = self.line_header_string
|
||||
tmp_style_num = self.line_header
|
||||
else:
|
||||
tmp_style_str = self.line_header_string_bold
|
||||
tmp_style_num = self.line_header_bold
|
||||
self.sheet.write_string(self.row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('debit')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 2, float(a.get('credit')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 3, float(a.get('balance')), tmp_style_num)
|
||||
|
||||
if data['form']['debit_credit'] != 1:
|
||||
|
||||
self.sheet.set_column(0, 0, 105)
|
||||
self.sheet.set_column(1, 1, 15)
|
||||
self.sheet.set_column(2, 2, 15)
|
||||
|
||||
self.sheet.write_string(self.row_pos, 0, _('Name'),
|
||||
self.format_header)
|
||||
if data['form']['enable_filter']:
|
||||
self.sheet.write_string(self.row_pos, 1, data['form']['label_filter'],
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.write_string(self.row_pos, 1, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
self.row_pos += 1
|
||||
self.row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = self.line_header_string
|
||||
tmp_style_num = self.line_header
|
||||
else:
|
||||
tmp_style_str = self.line_header_string_bold
|
||||
tmp_style_num = self.line_header_bold
|
||||
self.sheet.write_string(self.row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
if data['form']['enable_filter']:
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('balance_cmp')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 2, float(a.get('balance')), tmp_style_num)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('balance')), tmp_style_num)
|
||||
|
||||
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_bold.num_format = currency_id.excel_format
|
||||
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
if not record:
|
||||
return False
|
||||
data = record.get_report_values()
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet(data['form']['account_report_id'][1])
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
|
||||
self.sheet_2.set_column(0, 0, 25)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
#self.sheet.protect()
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
self.sheet.merge_range(0, 0, 0, 3, data['form']['account_report_id'][1] +' - '+data['form']['company_name'], self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
|
||||
#Filter section
|
||||
self.prepare_report_filters(data)
|
||||
# Content section
|
||||
self.prepare_report_contents(data)
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportGeneralLedger(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.general_ledger'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ledger_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,360 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsGeneralLedgerXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.ins_general_ledger_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
'border': False
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
#'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'border': True,
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': True,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_left = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
#'top': True,
|
||||
#'bottom': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
#'top': True,
|
||||
#'bottom': True,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
})
|
||||
self.line_header_light_initial = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_ending = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_from']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_to']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Target moves'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['target_moves'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Display accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['display_accounts'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Sort by'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['sort_accounts_by'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Initial Balance'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['initial_balance'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
|
||||
# Journals
|
||||
self.row_pos_2 += 2
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Journals'),
|
||||
self.format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, j_list,
|
||||
self.content_header)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('accounts')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Account Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Account Tags'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('account_tags')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Analytic Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytics')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Analytic Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Tags'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytic_tags')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, acc_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if filter.get('include_details', False):
|
||||
self.sheet.write_string(self.row_pos, 0, _('Date'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('JRNL'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Partner'),
|
||||
self.format_header)
|
||||
# self.sheet.write_string(self.row_pos, 3, _('Ref'),
|
||||
# self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Move'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 4, _('Entry Label'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 1, _('Code'), self.format_header)
|
||||
self.sheet.merge_range(self.row_pos, 2, self.row_pos, 4, _('Account'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
if acc_lines:
|
||||
for line in acc_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, ' ' + acc_lines[line].get('code') + ' - ' + acc_lines[line].get('name'), self.line_header_left)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')), self.line_header)
|
||||
|
||||
if filter.get('include_details', False):
|
||||
|
||||
count, offset, sub_lines = self.record.build_detailed_move_lines(offset=0, account=line,
|
||||
fetch_range=1000000)
|
||||
|
||||
for sub_line in sub_lines:
|
||||
if sub_line.get('move_name') == 'Initial Balance':
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_initial)
|
||||
elif sub_line.get('move_name') not in ['Initial Balance','Ending Balance']:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_datetime(self.row_pos, 0, self.convert_to_date(sub_line.get('ldate')),
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 1, sub_line.get('lcode'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('partner_name') or '',
|
||||
self.line_header_light)
|
||||
# self.sheet.write_string(self.row_pos, 3, sub_line.get('lref') or '',
|
||||
# self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('move_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('lname') or '',
|
||||
self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5,
|
||||
float(sub_line.get('debit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6,
|
||||
float(sub_line.get('credit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 7,
|
||||
float(sub_line.get('balance')),self.line_header_light)
|
||||
else: # Ending Balance
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_ending)
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_initial.num_format = currency_id.excel_format
|
||||
self.line_header_light_ending.num_format = currency_id.excel_format
|
||||
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('General Ledger')
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 12)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 2, 30)
|
||||
self.sheet.set_column(3, 3, 18)
|
||||
self.sheet.set_column(4, 4, 30)
|
||||
self.sheet.set_column(5, 5, 10)
|
||||
self.sheet.set_column(6, 6, 10)
|
||||
self.sheet.set_column(7, 7, 10)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 8, 'General Ledger'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, account_lines = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, account_lines, filters)
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportPartnerAgeing(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.partner_ageing'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ageing_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
'Period_Dict': data.get('js_data')[2],
|
||||
'Period_List': data.get('js_data')[3]
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,298 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api,_
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsPartnerAgeingXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.ins_partner_ageing_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 14,
|
||||
'font':'Arial'
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
#'border': True
|
||||
})
|
||||
self.format_header_period = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'left': True,
|
||||
'right': True,
|
||||
# 'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
#'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_total = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'border': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_period = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.line_header_light_period = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('As on Date'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['as_on_date']) or ''),
|
||||
self.content_header_date)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Partner Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partner Tag'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('categories')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, period_dict, period_list, ageing_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if self.record.include_details:
|
||||
self.sheet.write_string(self.row_pos, 0, _('Entry #'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('Due Date'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Journal'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Account'), self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, _('Partner'),
|
||||
self.format_header)
|
||||
k = 4
|
||||
for period in period_list:
|
||||
self.sheet.write_string(self.row_pos, k, str(period),
|
||||
self.format_header_period)
|
||||
k += 1
|
||||
self.sheet.write_string(self.row_pos, k, _('Total'),
|
||||
self.format_header_period)
|
||||
|
||||
|
||||
if ageing_lines:
|
||||
for line in ageing_lines:
|
||||
|
||||
# Dummy vacant lines
|
||||
self.row_pos += 1
|
||||
report_last_column = 4 + len(period_list)
|
||||
for column in range(4, report_last_column + 1):
|
||||
self.sheet.write_string(self.row_pos, column, '', self.line_header_light_period)
|
||||
|
||||
self.row_pos += 1
|
||||
if line != 'Total':
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, ageing_lines[line].get('partner_name'), self.line_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, _('Total'),self.line_header_total)
|
||||
k = 4
|
||||
|
||||
for period in period_list:
|
||||
if line != 'Total':
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line][period],self.line_header)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line][period], self.line_header_total)
|
||||
k += 1
|
||||
if line != 'Total':
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line]['total'], self.line_header)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line]['total'], self.line_header_total)
|
||||
|
||||
if self.record.include_details:
|
||||
if line != 'Total':
|
||||
count, offset, sub_lines, period_list = self.record.process_detailed_data(partner=line, fetch_range=1000000)
|
||||
for sub_line in sub_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 0, sub_line.get('move_name') or '',
|
||||
self.line_header_light)
|
||||
date = self.convert_to_date(sub_line.get('date_maturity') and sub_line.get('date_maturity').strftime('%Y-%m-%d') or sub_line.get('date').strftime('%Y-%m-%d'))
|
||||
self.sheet.write_datetime(self.row_pos, 1, date,
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('journal_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('account_name') or '',
|
||||
self.line_header_light)
|
||||
detail_column = 4
|
||||
detail_total = 0.0
|
||||
for period_index in period_dict:
|
||||
amount = float(sub_line.get('range_%s' % period_index) or 0.0)
|
||||
self.sheet.write_number(self.row_pos, detail_column, amount, self.line_header_light_period)
|
||||
detail_total += amount
|
||||
detail_column += 1
|
||||
self.sheet.write_number(self.row_pos, detail_column, detail_total, self.line_header_light_period)
|
||||
|
||||
|
||||
|
||||
self.row_pos += 1
|
||||
k = 4
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_period.num_format = currency_id.excel_format
|
||||
self.line_header_total.num_format = currency_id.excel_format
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('Partner Ageing')
|
||||
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 15)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 3, 15)
|
||||
self.sheet.set_column(3, 3, 15)
|
||||
self.sheet.set_column(4, 4, 15)
|
||||
self.sheet.set_column(5, 5, 15)
|
||||
self.sheet.set_column(6, 6, 15)
|
||||
self.sheet.set_column(7, 7, 15)
|
||||
self.sheet.set_column(8, 8, 15)
|
||||
self.sheet.set_column(9, 9, 15)
|
||||
self.sheet.set_column(10, 10, 15)
|
||||
self.sheet.set_column(11, 11, 15)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
self.record = record
|
||||
|
||||
self.sheet.set_zoom(75)
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 11, 'Partner Ageing'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, ageing_lines, period_dict, period_list = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, period_dict, period_list, ageing_lines, filters)
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportPartnerLedger(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.partner_ledger'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ledger_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,328 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsPartnerLedgerXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.ins_partner_ledger_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
'border': False
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
#'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'border': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': True,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'text_wrap': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light_initial = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'bottom': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_ending = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_from']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_to']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Target moves'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['target_moves'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Display accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['display_accounts'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Reconciled'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['reconciled'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Initial Balance'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['initial_balance'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
|
||||
# Journals
|
||||
self.row_pos_2 += 2
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Journals'),
|
||||
self.format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, j_list,
|
||||
self.content_header)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Partner Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partner Tag'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('categories')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('accounts')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, acc_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if filter.get('include_details', False):
|
||||
self.sheet.write_string(self.row_pos, 0, _('Date'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('JRNL'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Partner'),
|
||||
self.format_header)
|
||||
# self.sheet.write_string(self.row_pos, 3, _('Ref'),
|
||||
# self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Move'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 4, _('Entry Label'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, _('Partner'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
if acc_lines:
|
||||
for line in acc_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, acc_lines[line].get('name'), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')), self.line_header)
|
||||
|
||||
if filter.get('include_details', False):
|
||||
|
||||
count, offset, sub_lines = self.record.build_detailed_move_lines(offset=0, partner=line,
|
||||
fetch_range=1000000)
|
||||
|
||||
for sub_line in sub_lines:
|
||||
if sub_line.get('move_name') == 'Initial Balance':
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_initial)
|
||||
elif sub_line.get('move_name') not in ['Initial Balance','Ending Balance']:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_datetime(self.row_pos, 0, self.convert_to_date(sub_line.get('ldate')),
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 1, sub_line.get('lcode'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('account_name') or '',
|
||||
self.line_header_light)
|
||||
# self.sheet.write_string(self.row_pos, 3, sub_line.get('lref') or '',
|
||||
# self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('move_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('lname') or '',
|
||||
self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5,
|
||||
float(sub_line.get('debit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6,
|
||||
float(sub_line.get('credit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 7,
|
||||
float(sub_line.get('balance')),self.line_header_light)
|
||||
else: # Ending Balance
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_ending)
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_initial.num_format = currency_id.excel_format
|
||||
self.line_header_light_ending.num_format = currency_id.excel_format
|
||||
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('Partner Ledger')
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 12)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 2, 30)
|
||||
self.sheet.set_column(3, 3, 18)
|
||||
self.sheet.set_column(4, 4, 30)
|
||||
self.sheet.set_column(5, 5, 10)
|
||||
self.sheet.set_column(6, 6, 10)
|
||||
self.sheet.set_column(7, 7, 10)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 8, 'Partner Ledger'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, account_lines = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, account_lines, filters)
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportTrialBalance(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.trial_balance'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ledger_data': data.get('js_data')[1],
|
||||
'Retained': data.get('js_data')[2],
|
||||
'Subtotal': data.get('js_data')[3],
|
||||
'Filters': data.get('js_data')[0],
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,349 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api,_
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsTrialBalanceXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports.ins_trial_balance_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
#'border': True
|
||||
})
|
||||
self.format_merged_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'right': True,
|
||||
'left': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.format_merged_header_without_border = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_total = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_left = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_left_total = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light_total = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_light_left = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_highlight = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, data, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_from']) or ''),
|
||||
self.line_header_light_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_to']) or ''),
|
||||
self.line_header_light_date)
|
||||
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Display accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['display_accounts'],
|
||||
self.content_header)
|
||||
|
||||
# Journals
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Journals'),
|
||||
self.format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, j_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytics')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
# self.row_pos_2 += 1
|
||||
# self.sheet_2.write_string(self.row_pos_2, 0, _('Accounts'),
|
||||
# self.format_header)
|
||||
# a_list = ', '.join([lt or '' for lt in filter.get('accounts')])
|
||||
# self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
# self.content_header)
|
||||
|
||||
# Branches
|
||||
# self.row_pos_2 += 1
|
||||
# self.sheet_2.write_string(self.row_pos_2, 0, _('Branch'),
|
||||
# self.format_header)
|
||||
# a_list = ', '.join([lt or '' for lt in filter.get('operating_location_ids')])
|
||||
# self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
# self.content_header)
|
||||
|
||||
def prepare_report_contents(self, acc_lines, retained, subtotal, filter):
|
||||
|
||||
self.row_pos += 3
|
||||
self.sheet.merge_range(self.row_pos, 1, self.row_pos, 3, 'Initial Balance', self.format_merged_header)
|
||||
|
||||
self.sheet.write_datetime(self.row_pos, 4, self.convert_to_date(filter.get('date_from')),
|
||||
self.format_merged_header_without_border)
|
||||
self.sheet.write_string(self.row_pos, 5, _(' To '),
|
||||
self.format_merged_header_without_border)
|
||||
self.sheet.write_datetime(self.row_pos, 6, self.convert_to_date(filter.get('date_to')),
|
||||
self.format_merged_header_without_border)
|
||||
|
||||
self.sheet.merge_range(self.row_pos, 7, self.row_pos, 9, 'Ending Balance', self.format_merged_header)
|
||||
|
||||
self.row_pos += 1
|
||||
|
||||
self.sheet.write_string(self.row_pos, 0, _('Account'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Balance'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 4, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Balance'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 8, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 9, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
if acc_lines:
|
||||
if not filter.get('show_hierarchy'):
|
||||
for line in acc_lines: # Normal lines
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 0, acc_lines[line].get('code') + ' ' +acc_lines[line].get('name'), self.line_header_light_left)
|
||||
self.sheet.write_number(self.row_pos, 1, float(acc_lines[line].get('initial_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 2, float(acc_lines[line].get('initial_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 3, float(acc_lines[line].get('initial_balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 4, float(acc_lines[line].get('debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('ending_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 8, float(acc_lines[line].get('ending_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 9, float(acc_lines[line].get('ending_balance')), self.line_header_highlight)
|
||||
else:
|
||||
for line in acc_lines: # Normal lines
|
||||
self.row_pos += 1
|
||||
blank_space = ' ' * len(line.get('indent_list'))
|
||||
if line.get('dummy'):
|
||||
self.sheet.write_string(self.row_pos, 0, blank_space + line.get('code'),
|
||||
self.line_header_light_left)
|
||||
else:
|
||||
self.sheet.write_string(self.row_pos, 0, blank_space + line.get('code') + ' ' + line.get('name'),
|
||||
self.line_header_light_left)
|
||||
self.sheet.write_number(self.row_pos, 1, float(line.get('initial_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 2, float(line.get('initial_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 3, float(line.get('initial_balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 4, float(line.get('debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5, float(line.get('credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6, float(line.get('balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 7, float(line.get('ending_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 8, float(line.get('ending_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 9, float(line.get('ending_balance')), self.line_header_highlight)
|
||||
|
||||
|
||||
if filter.get('strict_range'):
|
||||
# Retained Earnings line
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 0, ' ' + retained['RETAINED'].get('name'), self.line_header_light_left)
|
||||
self.sheet.write_number(self.row_pos, 1, float(retained['RETAINED'].get('initial_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 2, float(retained['RETAINED'].get('initial_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 3, float(retained['RETAINED'].get('initial_balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 4, float(retained['RETAINED'].get('debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5, float(retained['RETAINED'].get('credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6, float(retained['RETAINED'].get('balance')), self.line_header_highlight)
|
||||
self.sheet.write_number(self.row_pos, 7, float(retained['RETAINED'].get('ending_debit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 8, float(retained['RETAINED'].get('ending_credit')), self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 9, float(retained['RETAINED'].get('ending_balance')), self.line_header_highlight)
|
||||
# Sub total line
|
||||
self.row_pos += 2
|
||||
self.sheet.write_string(self.row_pos, 0, subtotal['SUBTOTAL'].get('code') + ' ' + subtotal['SUBTOTAL'].get('name'), self.line_header_left_total)
|
||||
self.sheet.write_number(self.row_pos, 1,float(subtotal['SUBTOTAL'].get('initial_debit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 2, float(subtotal['SUBTOTAL'].get('initial_credit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 3, float(subtotal['SUBTOTAL'].get('initial_balance')), self.line_header_total)
|
||||
self.sheet.write_number(self.row_pos, 4, float(subtotal['SUBTOTAL'].get('debit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 5, float(subtotal['SUBTOTAL'].get('credit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 6, float(subtotal['SUBTOTAL'].get('balance')), self.line_header_total)
|
||||
self.sheet.write_number(self.row_pos, 7, float(subtotal['SUBTOTAL'].get('ending_debit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 8, float(subtotal['SUBTOTAL'].get('ending_credit')), self.line_header_light_total)
|
||||
self.sheet.write_number(self.row_pos, 9, float(subtotal['SUBTOTAL'].get('ending_balance')), self.line_header_total)
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
|
||||
self.line_header_highlight.num_format = currency_id.excel_format
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.format_merged_header_without_border.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
self.sheet = workbook.add_worksheet('General Ledger')
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 30)
|
||||
self.sheet.set_column(1, 1, 15)
|
||||
self.sheet.set_column(2, 2, 15)
|
||||
self.sheet.set_column(3, 3, 15)
|
||||
self.sheet.set_column(4, 4, 15)
|
||||
self.sheet.set_column(5, 5, 15)
|
||||
self.sheet.set_column(6, 6, 15)
|
||||
self.sheet.set_column(7, 7, 15)
|
||||
self.sheet.set_column(8, 8, 15)
|
||||
self.sheet.set_column(9, 9, 15)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(5, 0)
|
||||
|
||||
self.sheet.set_zoom(80)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code', '=', lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 10, 'Trial Balance'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, account_lines, retained, subtotal = record.get_report_datas()
|
||||
|
||||
# Filter section
|
||||
self.prepare_report_filters(data, filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(account_lines, retained, subtotal, filters)
|
||||
@@ -0,0 +1,11 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_ins_account_financial_report,ins.account.financial.report,model_ins_account_financial_report,account.group_account_user,1,1,1,1
|
||||
access_ins_general_ledger,ins.general.ledger,model_ins_general_ledger,account.group_account_user,1,1,1,1
|
||||
access_ins_financial_report,ins.financial.report,model_ins_financial_report,account.group_account_user,1,1,1,1
|
||||
access_ins_partner_ageing,ins.partner.ageing,model_ins_partner_ageing,account.group_account_user,1,1,1,1
|
||||
access_ins_partner_ledger,ins.partner.ledger,model_ins_partner_ledger,account.group_account_user,1,1,1,1
|
||||
access_ins_trial_balance,ins.trial.balance,model_ins_trial_balance,account.group_account_user,1,1,1,1
|
||||
access_ins_faspe_consolidated_wizard,ins.faspe.consolidated.wizard,model_ins_faspe_consolidated_wizard,account.group_account_user,1,1,1,1
|
||||
access_ins_faspe_consolidated_elimination_line,ins.faspe.consolidated.elimination.line,model_ins_faspe_consolidated_elimination_line,account.group_account_user,1,1,1,1
|
||||
access_ins_faspe_consolidated_report,ins.faspe.consolidated.report,model_ins_faspe_consolidated_report,account.group_account_user,1,1,1,1
|
||||
access_ins_faspe_consolidated_report_line,ins.faspe.consolidated.report.line,model_ins_faspe_consolidated_report_line,account.group_account_user,1,1,1,1
|
||||
|
|
After Width: | Height: | Size: 6.4 MiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 7.4 MiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 6.4 MiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 8.9 MiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 6.6 MiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 6.4 MiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,493 @@
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span6">
|
||||
<h2 class="oe_slogan" style="color:#875A7B;">Financial Reports</h2>
|
||||
<h3 class="oe_slogan">Odoo 14 Dynamic Financial Reports for Community Edition</h3>
|
||||
</div>
|
||||
<div class="oe_span6">
|
||||
<!-- <div class="oe_row_img oe_centered">-->
|
||||
<!-- <img alt="Icon" class="oe_picture" src="icon.png"/>-->
|
||||
<!-- </div>-->
|
||||
<div class="alert alert-info oe_mt32" style="padding:0.3em 0.6em; font-size: 150%;">
|
||||
<ul class="list-unstyled">
|
||||
<li>General Ledger</li> <li>Trial Balance</li> <li>Ageing Report</li>
|
||||
<li>Partner Ledger</li> <li>Profit and Loss</li> <li>Balance Sheet</li> <li>Cash Flow Statements</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h4 class="oe_slogan"><b>Features</b></h4>
|
||||
<div class="alert alert-info oe_mt32" style="padding:0.3em 0.6em; font-size: 150%;">
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
User friendly view for reports without download it.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
Interactive filters to compare and process.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
Easy to debug interaction between reports will make accountant life easy.
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
Reports output option with beautiful Xlsx and Pdf formats
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
Drill down option enabled in reports to journal entry level
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-check-square-o text-primary"></i>
|
||||
Company level configuration will make different report option based on company
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
|
||||
<!--<div class="row"-->
|
||||
<!-- style="margin:75px 0;position: relative;color: #000;background-position: center;background: #ffffff;border-bottom: 1px solid #e4e4e4; padding-bottom: 30px;">-->
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">General Ledger</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
GL Report welcomes you with an interactive panel with useful filters.
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journals
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Accounts
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Account Tags
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Analytic Account
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Analytic Tags
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Extra options with initial balance and detailed sub line options
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/GL.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- ============= PL ============== -->
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Partner Ledger</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Account Type (Receivable Only / Payable Only)
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Reconciled Status
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journals
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Accounts
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Partners
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Partner Tag
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Extra options with initial balance and detailed sub line options
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/PL.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<!-- ============= TB ============== -->
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Trial Balance</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journals
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Analytic Account
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/TB.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- ============= AGE ============== -->
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Ageing Report</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Account Type (Receivable Only / Payable Only)
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Partner Type (Customer Only / Supplier Only)
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Partners
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Partner Tag
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Extra options to include sub lines
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/AGE.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- ============= P and L ============== -->
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Profit and Loss</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Comparison Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journal
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Debit/Credit Option
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/PRO.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<!-- ============= Balance Sheet ============== -->
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Balance Sheet</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Comparison Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journal
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Debit/Credit Option
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/BS.gif" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<!-- ============= Cash Flow ============== -->
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Cash Flow Statement</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Available filters are
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Custom Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Comparison Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Journal
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Debit/Credit Option
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Excel and Pdf export options
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
Extra wizard menu for generating reports.
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/CASH_2.png" class="img-responsive" alt="">
|
||||
<img style="max-width:100%" src="images/CASH.png" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<!-- ============= Company Master ============== -->
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Backend Configuration</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 19px;line-height: 50px; background-color: #3a34380d;padding-left: 20px;border-radius: 7px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 22px; margin-right: 6px; " alt="check">
|
||||
You can configure reports on company master as defaults and also on the fly.
|
||||
<ul>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Default Date Range
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Default Ageing Buckets
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Strict range option for TB. Useful in case of auto closing financials
|
||||
</li>
|
||||
<li class="mb8" style="font-family: Roboto;color: #000;list-style-type: square;font-size: 15px;line-height: 15px;padding-left: 10px;list-style: none;">
|
||||
<img src="images/bullet_green.png" style=" width: 15px; margin-right: 3px; " alt="check">
|
||||
Default financial year of the company (July-June/April-March/January-December)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<img style="max-width:100%" src="images/company_master.png" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Pdf and Xlsx Reports</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d67a22;width: 5%;">
|
||||
|
||||
<p>General Ledger - Pdf</p>
|
||||
<img style="max-width:100%" src="images/GL_pdf.png" class="img-responsive" alt="">
|
||||
<p>General Ledger - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/GL_xlsx.png" class="img-responsive" alt="">
|
||||
<p>Partner Ledger - Pdf</p>
|
||||
<img style="max-width:100%" src="images/PL_pdf.png" class="img-responsive" alt="">
|
||||
<p>Partner Ledger - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/PL_xlsx.png" class="img-responsive" alt="">
|
||||
<p>Trial Balance - Pdf</p>
|
||||
<img style="max-width:100%" src="images/TB_pdf.png" class="img-responsive" alt="">
|
||||
<p>Trial Balance - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/TB_xlsx.png" class="img-responsive" alt="">
|
||||
<p>Trial Balance - Pdf-With Hierarchy</p>
|
||||
<img style="max-width:100%" src="images/TB_pdf_2.png" class="img-responsive" alt="">
|
||||
<p>Trial Balance - Xlsx-With Hierarchy</p>
|
||||
<img style="max-width:100%" src="images/TB_xlsx_2.png" class="img-responsive" alt="">
|
||||
<p>Partner Ageing - Pdf</p>
|
||||
<img style="max-width:100%" src="images/AGE_pdf.png" class="img-responsive" alt="">
|
||||
<p>Partner Ageing - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/AGE_xlsx.png" class="img-responsive" alt="">
|
||||
<p>P and L - Pdf</p>
|
||||
<img style="max-width:100%" src="images/PRO_pdf.png" class="img-responsive" alt="">
|
||||
<p>P and L - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/PRO_xlsx.png" class="img-responsive" alt="">
|
||||
<p>Balance Sheet - Pdf</p>
|
||||
<img style="max-width:100%" src="images/BS_pdf.png" class="img-responsive" alt="">
|
||||
<p>Balance Sheet - Xlsx</p>
|
||||
<img style="max-width:100%" src="images/BS_xlsx.png" class="img-responsive" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="oe_container">
|
||||
<div class="oe_row oe_spaced">
|
||||
<div class="oe_span12">
|
||||
<section class="oe_container" style="padding: 0% 0% 6% 0%;">
|
||||
<center>
|
||||
<div class="col-md-12" style="margin: auto !important;
|
||||
width: 70%;
|
||||
padding: 30px;">
|
||||
<h2 style="font-weight: 600;text-align: center;width: 100%;">Any Queries?</h2>
|
||||
<hr style="margin-top: 0px;margin-bottom: 2%;border: 0;text-align: center;border-top: 3px solid #d25c22;width: 5%;">
|
||||
|
||||
<h4 style="font-size:16px;"> For any help on this module or demo please contact us. </h4>
|
||||
<div class="col-md-12" style="float:left; padding:20px;">
|
||||
<h4><i class="fa fa-envelope"></i>Email us </h4>
|
||||
<p>pycustech@gmail.com</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</center>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,48 @@
|
||||
odoo.define('account_dynamic_reports.action_manager', function (require) {
|
||||
"use strict";
|
||||
/**
|
||||
* The purpose of this file is to add the actions of type
|
||||
* 'xlsx' to the ActionManager.
|
||||
*/
|
||||
|
||||
var ActionManager = require('web.ActionManager');
|
||||
var framework = require('web.framework');
|
||||
var session = require('web.session');
|
||||
|
||||
|
||||
ActionManager.include({
|
||||
|
||||
/**
|
||||
* Executes actions of type 'ir.actions.report'.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} action the description of the action to execute
|
||||
* @param {Object} options @see doAction for details
|
||||
* @returns {Promise} resolved when the action has been executed
|
||||
*/
|
||||
_executexlsxReportDownloadAction: function (action) {
|
||||
framework.blockUI();
|
||||
var def = $.Deferred();
|
||||
session.get_file({
|
||||
url: '/xlsx_reports',
|
||||
data: action.data,
|
||||
success: def.resolve.bind(def),
|
||||
complete: framework.unblockUI,
|
||||
});
|
||||
return def;
|
||||
},
|
||||
/**
|
||||
* Overrides to handle the 'ir.actions.report' actions.
|
||||
*
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
_executeReportAction: function (action, options) {
|
||||
if (action.report_type === 'xlsx') {
|
||||
return this._executexlsxReportDownloadAction(action, options);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,682 @@
|
||||
#loader {
|
||||
animation: loader 5s cubic-bezier(.8,0,.2,1) infinite;
|
||||
height: 40px;
|
||||
width: 41px;
|
||||
position: absolute;
|
||||
top:calc(50% - 20px);
|
||||
left:calc(50% - 20px);
|
||||
visibility: hidden;
|
||||
background-color: #c5a6c7;
|
||||
}
|
||||
@keyframes loader {
|
||||
90% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(180deg); }
|
||||
}
|
||||
#top {
|
||||
animation: top 5s linear infinite;
|
||||
border-top: 20px solid #fff;
|
||||
border-right: 20px solid transparent;
|
||||
border-left: 20px solid transparent;
|
||||
height: 0px;
|
||||
width: 1px;
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
@keyframes top {
|
||||
90% { transform: scale(0); }
|
||||
100% { transform: scale(0);}
|
||||
}
|
||||
#bottom {
|
||||
animation: bottom 5s linear infinite;
|
||||
border-right: 20px solid transparent;
|
||||
border-bottom: 20px solid #fff;
|
||||
border-left: 20px solid transparent;
|
||||
height: 0px;
|
||||
width: 1px;
|
||||
transform: scale(0);
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
@keyframes bottom {
|
||||
10% { transform: scale(0); }
|
||||
90% { transform: scale(1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
#line {
|
||||
animation: line 5s linear infinite;
|
||||
border-left: 1px dotted #fff;
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
}
|
||||
@keyframes line {
|
||||
10% { height: 20px; }
|
||||
100% { height: 20px; }
|
||||
}
|
||||
|
||||
/* --------- Loader ends ----------------*/
|
||||
|
||||
.py-main-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
margin: 0;
|
||||
font-family: "Roboto", "Odoo Unicode Support Noto", sans-serif;
|
||||
font-size: 1.08333333rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #666666;
|
||||
text-align: left;
|
||||
background-color: #f1e4e4;
|
||||
|
||||
}
|
||||
.py-filter-container {
|
||||
|
||||
}
|
||||
.py-control-panel {
|
||||
border-bottom: 1px solid #cccccc;
|
||||
padding-top: 10px;
|
||||
padding-right: 16px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 16px;
|
||||
background-color: white;
|
||||
}
|
||||
.py-data-container {
|
||||
flex: 1 1 100%;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.py-data-summary {
|
||||
width: 100%;
|
||||
background-color: inherit;
|
||||
text-align: right;
|
||||
font-size: inherit;
|
||||
font-family: inherit
|
||||
}
|
||||
|
||||
.py-data-summary table{
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
font-weight: bold;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.py-breadcrumb {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.py-breadcrumb li {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.py-control-div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.py-control-panel .py_cntrl_left {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.py-control-panel .py_cntrl_right {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.py-control-panel .py-ctrl-buttons {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.py-btn-primary{
|
||||
color: #FFFFFF;
|
||||
background-color: #00A09D;
|
||||
border-color: #00A09D;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.py-btn-secondary{
|
||||
color: #FFFFFF;
|
||||
background-color: #00A0AD;
|
||||
border-color: #00A09D;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.py-search-buttons{
|
||||
display: block;
|
||||
margin: auto 0px;
|
||||
}
|
||||
|
||||
.py-search-btn-date,
|
||||
.py-search-type,
|
||||
.py-search-partner-type,
|
||||
.py-search-partner-tags,
|
||||
.py-search-reconciled-filter,
|
||||
.py-search-date-filter,
|
||||
.py-search-journals,
|
||||
.py-search-partners,
|
||||
.py-search-accounts,
|
||||
.py-search-accounts-tag,
|
||||
.py-search-analytics,
|
||||
.py-search-analytic-tag,
|
||||
.py-search-extra{
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.py-filters-menu{
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown-item{
|
||||
display: block;
|
||||
width: 100%;
|
||||
clear: both;
|
||||
font-weight: 400;
|
||||
color: rgb(102, 102, 102);
|
||||
text-align: inherit;
|
||||
white-space: nowrap;
|
||||
background-color: transparent;
|
||||
padding: 0.25rem 1.5rem;
|
||||
border-width: 0px;
|
||||
border-style: initial;
|
||||
border-color: initial;
|
||||
border-image: initial;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.py-data-container{
|
||||
background-color: white;
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
/* ============= Table for data =============== */
|
||||
|
||||
.system-data-table{
|
||||
background-color: white;
|
||||
padding: 0px 10px 0px 10px;
|
||||
margin-top: 50px;
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
|
||||
a{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 20px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
th:first-child{
|
||||
padding-left: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
.py-total-line{
|
||||
background-color: #e6e6e6;
|
||||
border-bottom: 2px solid #a9a9a9;
|
||||
border-top: 2px solid #a9a9a9;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
text-align: right;
|
||||
padding-right:15px
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.py-mline{
|
||||
border-bottom: 1px solid #bbb;
|
||||
border-top: 1px solid #bbb;
|
||||
background-color: #e6e6e6;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
padding-left:10px;
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
|
||||
ul{
|
||||
li{
|
||||
display: inline;
|
||||
a {
|
||||
border: 1px solid #a2a2bb;
|
||||
border-radius: 3px;
|
||||
margin: 0px 3px 0px 3px;
|
||||
padding: 0px 2px 0px 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
.py-mline-data-table{
|
||||
width:97%;
|
||||
margin: 0px 15px 0px 15px;
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr{
|
||||
a{
|
||||
white-space: nowrap;
|
||||
}
|
||||
td{
|
||||
vertical-align: top;
|
||||
}
|
||||
td:first-child{
|
||||
width:8%;
|
||||
}
|
||||
td:nth-last-child(2){ // balance
|
||||
width:8%;
|
||||
}
|
||||
td:nth-last-child(3){ // cerdit
|
||||
width:8%;
|
||||
}
|
||||
td:nth-last-child(4){ // debit
|
||||
width:8%;
|
||||
}
|
||||
td:nth-child(2){ // Jrnl
|
||||
width:8%;
|
||||
}
|
||||
td:nth-child(3){ // Partner
|
||||
width:20%;
|
||||
}
|
||||
td:last-child{
|
||||
width:8%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
};
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
tr:hover{
|
||||
//background-color:#00ede8;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.view-source{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.py-mline-sub{
|
||||
.view-source:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --------------------------- Ageing Report ------------------------ //
|
||||
|
||||
.system-data-table-age{
|
||||
background-color: white;
|
||||
padding: 0px 10px 0px 10px;
|
||||
margin-top: 50px;
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
|
||||
a{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 20px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
th:first-child{
|
||||
padding-left: 5%;
|
||||
}
|
||||
}
|
||||
|
||||
.py-total-line{
|
||||
background-color: #e6e6e6;
|
||||
border-bottom: 2px solid #a9a9a9;
|
||||
border-top: 2px solid #a9a9a9;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
text-align: right;
|
||||
padding-right:15px
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.py-mline{
|
||||
border-bottom: 1px solid #bbb;
|
||||
border-top: 1px solid #bbb;
|
||||
background-color: #e6e6e6;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
padding-left:10px;
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
|
||||
ul{
|
||||
li{
|
||||
display: inline;
|
||||
a {
|
||||
border: 1px solid #a2a2bb;
|
||||
border-radius: 3px;
|
||||
margin: 0px 3px 0px 3px;
|
||||
padding: 0px 2px 0px 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
.py-mline-data-table{
|
||||
width:97%;
|
||||
margin: 0px 15px 0px 15px;
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr{
|
||||
a{
|
||||
white-space: nowrap;
|
||||
}
|
||||
td{
|
||||
vertical-align: top;
|
||||
}
|
||||
td:first-child{ // Entry label
|
||||
width:8%;
|
||||
}
|
||||
td:nth-child(2){ // Due Date
|
||||
text-align: center;
|
||||
}
|
||||
td:nth-child(3){ // Journal
|
||||
width:15%;
|
||||
}
|
||||
td:nth-child(3){ // Account
|
||||
width:15%;
|
||||
}
|
||||
td:last-child{
|
||||
width:8%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
};
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
tr:hover{
|
||||
//background-color:#00ede8;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.view-source{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.py-mline-sub{
|
||||
.view-source:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --------------------------- Trial Balance ------------------------ //
|
||||
|
||||
.system-data-table-tb{
|
||||
background-color: white;
|
||||
padding: 0px 10px 0px 10px;
|
||||
margin-top: 50px;
|
||||
color: #666666;
|
||||
|
||||
a{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
thead > tr:first-child{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
thead > tr:nth-child(2){
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 20px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr:last-child{
|
||||
background-color: #dcdcdc;
|
||||
}
|
||||
|
||||
.py-mline{
|
||||
border-bottom: 1px solid #bbb;
|
||||
background-color: white;
|
||||
//border-top: 2px solid #bbb;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
padding-left:20px;
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.bld{
|
||||
font-weight: bold;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
|
||||
.py-mline-data-table{
|
||||
width:97%;
|
||||
margin: 0px 15px 0px 15px;
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
};
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
tr:hover{
|
||||
background-color:#00ede8;
|
||||
}
|
||||
}
|
||||
|
||||
.view-source{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.py-mline-sub{
|
||||
|
||||
.view-source:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --------------------------- Financial Reports ------------------------ //
|
||||
.system-data-table-fr{
|
||||
background-color: white;
|
||||
padding: 0px 10px 0px 10px;
|
||||
margin-top: 50px;
|
||||
color: #666666;
|
||||
|
||||
a{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 20px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
tr:hover{
|
||||
background-color:#00fffa;
|
||||
}
|
||||
|
||||
.py-mline{
|
||||
border-bottom: 1px solid #bbb;
|
||||
background-color: white;
|
||||
//border-top: 2px solid #bbb;
|
||||
color: #666666;
|
||||
td:first-child{
|
||||
padding-left:20px;
|
||||
}
|
||||
td > span{
|
||||
margin-left: 15px;
|
||||
}
|
||||
.bld{
|
||||
font-weight: bold;
|
||||
}
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
td:last-child{
|
||||
padding-right: 20px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
};
|
||||
|
||||
.py-mline-data-table{
|
||||
width:97%;
|
||||
margin: 0px 15px 0px 15px;
|
||||
thead > tr{
|
||||
th{
|
||||
padding-left: 15px;
|
||||
}
|
||||
th:last-child{
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
||||
.amt-head{
|
||||
text-align: right;
|
||||
};
|
||||
.amt{
|
||||
text-align: right;
|
||||
}
|
||||
tr:hover{
|
||||
background-color:#00ede8;
|
||||
}
|
||||
}
|
||||
|
||||
.view-source{
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.py-mline-sub{
|
||||
.view-source:hover{
|
||||
cursor:pointer;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_backend" name="account_dynamic_reports assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/account_dynamic_reports/static/src/scss/dynamic_common_style.scss" />
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -0,0 +1,214 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<template id="ins_report_financial">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<t t-set="data_report_margin_top" t-value="12"/>
|
||||
<t t-set="data_report_header_spacing" t-value="9"/>
|
||||
<t t-set="data_report_dpi" t-value="110"/>
|
||||
<div class="page">
|
||||
<style>
|
||||
.pims_report_content{
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
}
|
||||
|
||||
.pims_report_line_table {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.pims_report_summary_table {
|
||||
width:100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pims_report_line_main_tr {
|
||||
font-weight:bold;
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
border-bottom:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_initial_tr {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.pims_report_line_subtotal {
|
||||
font-weight:bold;
|
||||
background-color: white;
|
||||
border:1px solid grey;
|
||||
}
|
||||
</style>
|
||||
<h3><span t-esc="res_company.name"/>: <t t-esc="data['form']['account_report_id'][1]"/> </h3>
|
||||
|
||||
<div class="row mt32 pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Target Moves:</strong>
|
||||
<p>
|
||||
<span t-if="data['form']['target_move'] == 'all'">All Entries</span>
|
||||
<span t-if="data['form']['target_move'] == 'posted'">All Posted Entries</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date From:</strong>
|
||||
<p t-esc="data['form']['date_from']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date To:</strong>
|
||||
<p t-esc="data['form']['date_to']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
</div>
|
||||
<t t-if="data['form'].get('selected_analytics')">
|
||||
<div class="row mt16 pims_report_content">
|
||||
<div class="col-12">
|
||||
<strong>Analytic Accounts:</strong>
|
||||
<p t-esc="', '.join(data['form']['selected_analytics'])"></p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="data['form']['enable_filter']">
|
||||
<div class="row mt32 pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Comparison Date From:</strong>
|
||||
<p t-esc="data['form']['comparison_context']['date_from']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Comparison Date To:</strong>
|
||||
<p t-esc="data['form']['comparison_context']['date_to']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<br></br><br></br>
|
||||
|
||||
<table class="pims_report_line_table" t-if="data['form']['debit_credit'] == 1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="text-right">Debit</th>
|
||||
<th class="text-right">Credit</th>
|
||||
<th class="text-right">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="pims_report_line_main_tr" t-foreach="report_lines" t-as="a">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..' * a.get('level', 0)"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<t t-if="a.get('debit')">
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('debit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-right">-</td></t>
|
||||
<t t-if="a.get('credit')">
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('credit')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-right">-</td></t>
|
||||
<t t-if="a.get('balance')">
|
||||
<td class="text-right" style="white-space: text-nowrap;">
|
||||
<span t-att-style="style" t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-right">-</td></t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="pims_report_line_table"
|
||||
t-if="not data['form']['debit_credit']">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<t t-if="data['form']['enable_filter']">
|
||||
<th class="text-right"><t t-esc="data['form']['label_filter']"/></th>
|
||||
</t>
|
||||
<th class="text-right">Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="pims_report_line_main_tr" t-foreach="report_lines" t-as="a">
|
||||
<t t-if="a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
<t t-if="not a.get('level') > 3">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
|
||||
<td>
|
||||
<span style="color: white;" t-esc="'..' * a.get('level', 0)"/>
|
||||
<span t-att-style="style" t-esc="a.get('name')"/>
|
||||
</td>
|
||||
<t t-if="data['form']['enable_filter']">
|
||||
<t t-if="a.get('balance_cmp')">
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="a.get('balance_cmp')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-right">-</td></t>
|
||||
</t>
|
||||
<t t-if="a.get('balance')">
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="a.get('balance')"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-right">-</td></t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<t t-if="data['initial_balance'] or data['current_balance'] or data['ending_balance']">
|
||||
<br></br>
|
||||
<div class="row">
|
||||
<div class="col-8"></div>
|
||||
<div class="col-4">
|
||||
<table class="pims_report_summary_table">
|
||||
<tr>
|
||||
<td>Initial Cash Balance</td>
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="data['initial_balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Cash Balance</td>
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="data['current_balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Net Cash Balance</td>
|
||||
<td class="text-right">
|
||||
<span t-att-style="style" t-esc="data['ending_balance']"
|
||||
t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<report
|
||||
id="ins_financial_report_pdf"
|
||||
model="ins.financial.report"
|
||||
string="Financial reports"
|
||||
report_type="qweb-pdf"
|
||||
name="account_dynamic_reports.ins_report_financial"
|
||||
file="account_dynamic_reports.ins_report_financial"
|
||||
/>
|
||||
|
||||
</odoo>
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="account_dynamic_reports.general_ledger">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
<style>
|
||||
.pims_report_content{
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
}
|
||||
|
||||
.pims_report_line_table {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.pims_report_line_main_tr {
|
||||
font-weight:bold;
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
border-bottom:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_initial_tr {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.pims_report_line_data_tr {
|
||||
font-style:inherit;
|
||||
}
|
||||
|
||||
.pims_report_line_subtotal {
|
||||
font-weight:bold;
|
||||
background-color: white;
|
||||
border:1px solid grey;
|
||||
}
|
||||
</style>
|
||||
<h3><span t-esc="res_company.name"/>: General ledger</h3>
|
||||
<div>
|
||||
|
||||
<div class="row mt32 pims_report_content">
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Display Account</strong>
|
||||
<p t-esc="Filters['display_accounts']"></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date From:</strong>
|
||||
<p t-esc="Filters['date_from']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date To:</strong>
|
||||
<p t-esc="Filters['date_to']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Analytic Tag:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['analytic_tags'] ])"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pims_report_content">
|
||||
|
||||
<div class="col-3">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['journals'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Accounts:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['accounts'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Partners:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['partners'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Analytic:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['analytics'] ])"/>
|
||||
</div>
|
||||
</div>
|
||||
<br></br><br></br>
|
||||
<table class="pims_report_line_table pims_report_content">
|
||||
<thead>
|
||||
<t t-if="not Filters['include_details']">
|
||||
<tr class="text-center">
|
||||
<th colspan="5" class="text-center">Partner</th>
|
||||
<th style="text-align:right;padding-bottom:10px;">Debit</th>
|
||||
<th style="text-align:right">Credit</th>
|
||||
<th style="text-align:right">Balance</th>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="Filters['include_details']">
|
||||
<tr class="text-center">
|
||||
<th style="text-align:left;">Date</th>
|
||||
<th style="text-align:left;">Journal</th>
|
||||
<th style="text-align:left;">Partner</th>
|
||||
<th style="text-align:left;">Move</th>
|
||||
<th style="text-align:left;">Reference</th>
|
||||
<th style="text-align:right;padding-bottom:10px;">Debit</th>
|
||||
<th style="text-align:right">Credit</th>
|
||||
<th style="text-align:right">Balance</th>
|
||||
</tr>
|
||||
</t>
|
||||
</thead>
|
||||
<t t-foreach="Ledger_data" t-as="line">
|
||||
<tr class="pims_report_line_main_tr">
|
||||
<td colspan="5">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="Ledger_data[line]['name']"/>
|
||||
</td>
|
||||
<t t-if="Ledger_data[line]['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
|
||||
<t t-foreach="Ledger_data[line]['lines']" t-as="sub_line">
|
||||
<t t-if="sub_line['initial_bal']">
|
||||
<tr class="pims_report_line_initial_tr">
|
||||
<td colspan="5" class="text-right">
|
||||
<span>Initial Balance</span>
|
||||
</td>
|
||||
<t t-if="sub_line['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="Filters['include_details']">
|
||||
<t t-if="not sub_line['initial_bal'] and not sub_line['ending_bal']">
|
||||
<tr class="pims_report_line_data_tr">
|
||||
<td><t t-esc="sub_line.get('ldate')" t-options='{"widget": "date"}'/></td>
|
||||
<td><t t-esc="sub_line.get('lcode')"/></td>
|
||||
<td><t t-esc="sub_line.get('partner_name')"/></td>
|
||||
<td><t t-esc="sub_line.get('move_name')"/></td>
|
||||
<td style="width:30%;"><t t-esc="sub_line.get('lname')"/></td>
|
||||
<t t-if="sub_line.get('debit')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('debit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line.get('credit')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('credit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line.get('balance')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('balance')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="sub_line['ending_bal']">
|
||||
<tr class="pims_report_line_initial_tr">
|
||||
<td colspan="5" class="text-right">
|
||||
<span>Ending Balance</span>
|
||||
</td>
|
||||
<t t-if="sub_line['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
<br></br>
|
||||
<p style="text-align:center"> *** END OF DOCUMENT ***</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<report id="action_print_general_ledger"
|
||||
model="ins.general.ledger"
|
||||
report_type="qweb-pdf"
|
||||
menu="False"
|
||||
string="General Ledger"
|
||||
name="account_dynamic_reports.general_ledger"
|
||||
file="account_dynamic_reports.general_ledger"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="account_dynamic_reports.partner_ageing">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
<style>
|
||||
.pims_report_content{
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
}
|
||||
|
||||
.pims_report_line_table {
|
||||
width:100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.pims_report_line_table thead{
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
border-bottom:2px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_main_tr {
|
||||
font-weight:bold;
|
||||
--background-color: cyan;
|
||||
border-bottom:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_initial_tr {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.pims_report_line_subtotal {
|
||||
font-weight:bold;
|
||||
background-color: white;
|
||||
--border:1px solid grey;
|
||||
}
|
||||
</style>
|
||||
<h3><span t-esc="res_company.name"/>: Partner Ageing</h3>
|
||||
<div>
|
||||
|
||||
<div class="row pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>As on Date:</strong>
|
||||
<p t-esc="Filters['as_on_date']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<strong>Partners:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['partners'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Partner Tags:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['categories'] ])"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br></br>
|
||||
|
||||
<table class="pims_report_line_table pims_report_content">
|
||||
<thead>
|
||||
<tr class="text-center">
|
||||
<th>Partner</th>
|
||||
<t t-foreach="Period_List" t-as="period">
|
||||
<th style="text-align:right;margin-bottom:10px;"><t t-esc="period"/></th>
|
||||
</t>
|
||||
<th style="text-align:right">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<t t-foreach="Ageing_data" t-as="line">
|
||||
|
||||
<t t-if="line == 'Total'">
|
||||
<t t-set="style" t-value="'font-weight: bold;'"/>
|
||||
</t>
|
||||
<t else="">
|
||||
<t t-set="style" t-value="'font-weight: normal;'"/>
|
||||
</t>
|
||||
|
||||
<tr class="pims_report_line_main_tr" t-att-style="style">
|
||||
<td>
|
||||
<t t-if="line == 'Total'">
|
||||
<span>Total</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="Ageing_data[line]['partner_name']"/>
|
||||
</t>
|
||||
</td>
|
||||
<t t-foreach="Period_List" t-as="period">
|
||||
<t t-if="Ageing_data[line][period]">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ageing_data[line][period]" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</t>
|
||||
<t t-if="Ageing_data[line]['total']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ageing_data[line]['total']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
<br></br>
|
||||
<p style="text-align:center"> *** END OF DOCUMENT ***</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<report id="action_print_partner_ageing"
|
||||
model="ins.partner.ageing"
|
||||
report_type="qweb-pdf"
|
||||
menu="False"
|
||||
string="Partner Ageing"
|
||||
name="account_dynamic_reports.partner_ageing"
|
||||
file="account_dynamic_reports.partner_ageing"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<template id="account_dynamic_reports.partner_ledger">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
<style>
|
||||
.pims_report_content{
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
}
|
||||
|
||||
.pims_report_line_table {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.pims_report_line_main_tr {
|
||||
font-weight:bold;
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
border-bottom:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_initial_tr {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.pims_report_line_data_tr {
|
||||
font-style:inherit;
|
||||
}
|
||||
|
||||
.pims_report_line_subtotal {
|
||||
font-weight:bold;
|
||||
background-color: white;
|
||||
border:1px solid grey;
|
||||
}
|
||||
</style>
|
||||
<h3><span t-esc="res_company.name"/>: Partner ledger</h3>
|
||||
<div>
|
||||
|
||||
<div class="row mt32 pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['journals'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Display Account</strong>
|
||||
<p t-esc="Filters['display_accounts']"></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Target Moves:</strong>
|
||||
<p t-esc="Filters['target_moves']"></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Reconciled:</strong>
|
||||
<p t-esc="Filters['reconciled']"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Date From:</strong>
|
||||
<p t-esc="Filters['date_from']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date To:</strong>
|
||||
<p t-esc="Filters['date_to']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Accounts:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['accounts'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Partner Tags:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['categories'] ])"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br></br><br></br>
|
||||
<table class="pims_report_line_table pims_report_content">
|
||||
<thead>
|
||||
<t t-if="not Filters['include_details']">
|
||||
<tr class="text-center">
|
||||
<th colspan="5" class="text-center">Partner</th>
|
||||
<th style="text-align:right;padding-bottom:10px;">Debit</th>
|
||||
<th style="text-align:right">Credit</th>
|
||||
<th style="text-align:right">Balance</th>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="Filters['include_details']">
|
||||
<tr class="text-center">
|
||||
<th style="text-align:left;">Date</th>
|
||||
<th style="text-align:left;">Journal</th>
|
||||
<th style="text-align:left;">Account</th>
|
||||
<th style="text-align:left;">Move</th>
|
||||
<th style="text-align:left;">Reference</th>
|
||||
<th style="text-align:right;padding-bottom:10px;">Debit</th>
|
||||
<th style="text-align:right">Credit</th>
|
||||
<th style="text-align:right">Balance</th>
|
||||
</tr>
|
||||
</t>
|
||||
</thead>
|
||||
<t t-foreach="Ledger_data" t-as="line">
|
||||
<tr class="pims_report_line_main_tr">
|
||||
<td colspan="5">
|
||||
<span style="color: white;" t-esc="'..'"/>
|
||||
<span t-esc="Ledger_data[line]['name']"/>
|
||||
</td>
|
||||
<t t-if="Ledger_data[line]['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Ledger_data[line]['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
|
||||
<t t-foreach="Ledger_data[line]['lines']" t-as="sub_line">
|
||||
<t t-if="sub_line['initial_bal']">
|
||||
<tr class="pims_report_line_initial_tr">
|
||||
<td colspan="5" class="text-right">
|
||||
<span>Initial Balance</span>
|
||||
</td>
|
||||
<t t-if="sub_line['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="Filters['include_details']">
|
||||
<t t-if="not sub_line['initial_bal'] and not sub_line['ending_bal']">
|
||||
<tr class="pims_report_line_data_tr">
|
||||
<td><t t-esc="sub_line.get('ldate')" t-options='{"widget": "date"}'/></td>
|
||||
<td><t t-esc="sub_line.get('lcode')"/></td>
|
||||
<td><t t-esc="sub_line.get('account_name')"/></td>
|
||||
<td><t t-esc="sub_line.get('move_name')"/></td>
|
||||
<td style="width:30%;"><t t-esc="sub_line.get('lname')"/></td>
|
||||
<t t-if="sub_line.get('debit')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('debit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line.get('credit')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('credit')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line.get('balance')">
|
||||
<td class="text-right"><t t-esc="sub_line.get('balance')" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/></td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="sub_line['ending_bal']">
|
||||
<tr class="pims_report_line_initial_tr">
|
||||
<td colspan="5" class="text-right">
|
||||
<span>Ending Balance</span>
|
||||
</td>
|
||||
<t t-if="sub_line['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="sub_line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="sub_line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
<br></br>
|
||||
<p style="text-align:center"> *** END OF DOCUMENT ***</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<report id="action_print_partner_ledger"
|
||||
model="ins.partner.ledger"
|
||||
report_type="qweb-pdf"
|
||||
menu="False"
|
||||
string="Partner Ledger"
|
||||
name="account_dynamic_reports.partner_ledger"
|
||||
file="account_dynamic_reports.partner_ledger"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_res_company_form" model="ir.ui.view">
|
||||
<field name="name">res.company.form.inherit</field>
|
||||
<field name="model">res.company</field>
|
||||
<field name="priority">2</field>
|
||||
<field name="inherit_id" ref="base.view_company_form"/>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
<xpath expr="//notebook/page[1]" position="inside">
|
||||
<group string="Dynamic Reports">
|
||||
<group>
|
||||
<field name="strict_range" groups="base.group_system" invisible="1"/>
|
||||
<field name="date_range" groups="base.group_system"/>
|
||||
<field name="financial_year" groups="base.group_system"/>
|
||||
</group>
|
||||
<group col="5">
|
||||
<label for="bucket_4" string="Ageing Bucket" groups="base.group_system"/>
|
||||
<field name="bucket_1" class="oe_inline" nolabel="1" groups="base.group_system"/>
|
||||
<field name="bucket_2" class="oe_inline" nolabel="1" groups="base.group_system"/>
|
||||
<field name="bucket_3" class="oe_inline" nolabel="1" groups="base.group_system"/>
|
||||
<field name="bucket_4" class="oe_inline" nolabel="1" groups="base.group_system"/>
|
||||
</group>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_res_currency_specific_form">
|
||||
<field name="name">res.currency.form.inherit</field>
|
||||
<field name="inherit_id" ref="base.view_currency_form"/>
|
||||
<field name="model">res.currency</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='active']" position="after">
|
||||
<field name="excel_format"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_account" model="ir.ui.view">
|
||||
<field name="name">Account Inherit</field>
|
||||
<field name="model">account.account</field>
|
||||
<field name="inherit_id" ref="account.view_account_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//field[@name='user_type_id']" position="after">
|
||||
<field name="cash_flow_category"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,338 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<odoo>
|
||||
<template id="account_dynamic_reports.trial_balance">
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.internal_layout">
|
||||
<div class="page">
|
||||
<div class="oe_structure"/>
|
||||
<style>
|
||||
.pims_report_content{
|
||||
font-size: 14px;
|
||||
font-family" 'Arial';
|
||||
}
|
||||
|
||||
.pims_report_line_table {
|
||||
width:100%;
|
||||
--table-layout: fixed;
|
||||
--word-break: break-all;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.pims_report_line_main_tr {
|
||||
font-weight:bold;
|
||||
background-color: cyan;
|
||||
border:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_initial_tr {
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.pims_report_line_subtotal {
|
||||
font-weight:bold;
|
||||
background-color: white;
|
||||
border-top:2px solid grey;
|
||||
}
|
||||
|
||||
<!-- For TB Only -->
|
||||
|
||||
.pims_report_line_tb {
|
||||
border:1px solid grey;
|
||||
}
|
||||
|
||||
.pims_report_line_tb td {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pims_report_line_tb td+td {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.pims_tb_head {
|
||||
font-weight:bold;
|
||||
background-color: cyan;
|
||||
border:1px solid grey;
|
||||
text-align:right;
|
||||
}
|
||||
</style>
|
||||
<h3><span t-esc="res_company.name"/>: Trial Balance</h3>
|
||||
<div>
|
||||
|
||||
<div class="row pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Display Account</strong>
|
||||
<p t-esc="Filters.get('display_accounts')"></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Journals:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['journals'] ])"/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date From:</strong>
|
||||
<p t-esc="Filters['date_from']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<strong>Date To:</strong>
|
||||
<p t-esc="Filters['date_to']" t-options='{"widget": "date"}'></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row pims_report_content">
|
||||
<div class="col-3">
|
||||
<strong>Analytic:</strong>
|
||||
<p t-esc="', '.join([ lt or '' for lt in Filters['analytics'] ])"/>
|
||||
</div>
|
||||
</div>
|
||||
<br></br>
|
||||
<table class="pims_report_line_table pims_report_content">
|
||||
<thead>
|
||||
<!-- <tr class="text-center pims_tb_head">-->
|
||||
<!-- <th colspan="1"></th>-->
|
||||
<!-- <th colspan="1">initial Balance</th>-->
|
||||
<!-- <th colspan="3">Current Balance</th>-->
|
||||
<!-- <th colspan="1">Ending Balance</th>-->
|
||||
<!-- </tr>-->
|
||||
<tr class="text-center pims_tb_head">
|
||||
<th></th>
|
||||
<!-- <th style="text-align:right;">Debit</th>-->
|
||||
<!-- <th style="text-align:right;">Credit</th>-->
|
||||
<th style="text-align:right;padding-bottom:10px;">Initial Balance</th>
|
||||
<th style="text-align:right;">Debit</th>
|
||||
<th style="text-align:right;">Credit</th>
|
||||
<th style="text-align:right;">Balance</th>
|
||||
<!-- <th style="text-align:right;">Debit</th>-->
|
||||
<!-- <th style="text-align:right;">Credit</th>-->
|
||||
<th style="text-align:right;">Ending Balance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
||||
<t t-if="not Filters['show_hierarchy']">
|
||||
<t t-foreach="Ledger_data" t-as="line">
|
||||
<tr class="pims_report_line_tb"> <!-- Normal lines -->
|
||||
<td>
|
||||
<span><t t-esc="Ledger_data[line]['code']"/> - <t t-esc="Ledger_data[line]['name']"/></span>
|
||||
</td>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Ledger_data[line]['initial_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Ledger_data[line]['initial_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Ledger_data[line]['initial_balance']">
|
||||
<td class="text-right" style="width:10%;">
|
||||
<span t-esc="Ledger_data[line]['initial_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['debit']">
|
||||
<td class="text-right" style="width:10%;">
|
||||
<span t-esc="Ledger_data[line]['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['credit']">
|
||||
<td class="text-right" style="width:10%;">
|
||||
<span t-esc="Ledger_data[line]['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Ledger_data[line]['balance']">
|
||||
<td class="text-right" style="width:10%;">
|
||||
<span t-esc="Ledger_data[line]['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Ledger_data[line]['ending_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Ledger_data[line]['ending_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Ledger_data[line]['ending_balance']">
|
||||
<td class="text-right" style="width:10%;">
|
||||
<span t-esc="Ledger_data[line]['ending_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<td></td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-if="Filters['show_hierarchy']">
|
||||
<t t-foreach="Ledger_data" t-as="line">
|
||||
|
||||
<t t-if="line['parent'] != '0'"><t t-set="tr_style" t-value="'border-bottom:1px solid grey;'"/></t>
|
||||
<t t-if="line['parent'] == '0'"><t t-set="tr_style" t-value="'border-bottom:2px solid black;font-weight:bold'"/></t>
|
||||
|
||||
|
||||
<tr class="pims_report_line_tb" t-att-style="tr_style"> <!-- Normal lines -->
|
||||
<td colspan="1">
|
||||
|
||||
<t t-foreach="line['indent_list']" t-as="i">
|
||||
<span>&nbsp;</span>
|
||||
<span>&nbsp;</span>
|
||||
<span>&nbsp;</span>
|
||||
</t>
|
||||
|
||||
<t t-if="line['dummy']">
|
||||
<t t-esc="line['code']"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-esc="line['code']"/>
|
||||
-
|
||||
<t t-esc="line['name']"/>
|
||||
</t>
|
||||
|
||||
</td>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="line['initial_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="line['initial_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="line['initial_balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="line['initial_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="line['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="line['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="line['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="line['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="line['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="line['ending_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="line['ending_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="line['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="line['ending_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<td></td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
</t>
|
||||
|
||||
|
||||
<t t-if="Filters['strict_range']">
|
||||
<t t-foreach="Retained" t-as="line"> <!-- Retained -->
|
||||
<tr class="pims_report_line_tb" style="font-weight:bold;">
|
||||
<td colspan="1">
|
||||
<span><t t-esc="Retained[line]['code']"/> <t t-esc="Retained[line]['name']"/></span>
|
||||
</td>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Retained[line]['initial_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Retained[line]['initial_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Retained[line]['initial_balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Retained[line]['initial_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Retained[line]['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Retained[line]['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Retained[line]['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Retained[line]['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Retained[line]['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Retained[line]['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Retained[line]['ending_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Retained[line]['ending_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Retained[line]['ending_balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Retained[line]['ending_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<td></td>
|
||||
</tr>
|
||||
</t>
|
||||
</t>
|
||||
<t t-foreach="Subtotal" t-as="line"> <!-- Subtotal -->
|
||||
<tr class="pims_report_line_subtotal">
|
||||
<td colspan="1">
|
||||
<span><t t-esc="Subtotal[line]['code']"/> <t t-esc="Subtotal[line]['name']"/></span>
|
||||
</td>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Subtotal[line]['initial_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Subtotal[line]['initial_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Subtotal[line]['initial_balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Subtotal[line]['initial_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Subtotal[line]['debit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Subtotal[line]['debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Subtotal[line]['credit']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Subtotal[line]['credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<t t-if="Subtotal[line]['balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Subtotal[line]['balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Subtotal[line]['ending_debit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<!-- <td class="text-right">-->
|
||||
<!-- <span t-esc="Subtotal[line]['ending_credit']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>-->
|
||||
<!-- </td>-->
|
||||
<t t-if="Subtotal[line]['ending_balance']">
|
||||
<td class="text-right">
|
||||
<span t-esc="Subtotal[line]['ending_balance']" t-options="{'widget': 'monetary', 'display_currency': res_company.currency_id}"/>
|
||||
</td>
|
||||
</t><t t-else=""><td class="text-center">-</td></t>
|
||||
<td></td>
|
||||
</tr>
|
||||
</t>
|
||||
</table>
|
||||
</div>
|
||||
<br></br>
|
||||
<p style="text-align:center"> *** END OF DOCUMENT ***</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<report id="action_print_trial_balance"
|
||||
model="ins.trial.balance"
|
||||
report_type="qweb-pdf"
|
||||
menu="False"
|
||||
string="Trial Balance"
|
||||
name="account_dynamic_reports.trial_balance"
|
||||
file="account_dynamic_reports.trial_balance"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="assets_backend" name="account_dynamic_reports assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/account_dynamic_reports/static/src/scss/dynamic_common_style.scss"/>
|
||||
<script type="text/javascript" src="/account_dynamic_reports/static/src/js/select2.full.min.js"/>
|
||||
<script type="text/javascript" src="/account_dynamic_reports/static/src/js/script.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/account_dynamic_reports/static/src/js/action_manager.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<menuitem id="account_reports_ins" sequence="10"
|
||||
name="Dynamic Reports(Wiz)" parent="account.menu_finance_reports"/>
|
||||
|
||||
<menuitem id="account_reports_ins_wiz" sequence="15"
|
||||
name="Accounting Reports" parent="account.menu_finance_reports"/>
|
||||
|
||||
<!-- ============ Financial Report =============== -->
|
||||
|
||||
<record id="view_ins_account_financial_report_form" model="ir.ui.view">
|
||||
<field name="name">account.financial.report.form</field>
|
||||
<field name="model">ins.account.financial.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Account Report">
|
||||
<group col="4">
|
||||
<field name="name"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="sequence"/>
|
||||
<field name="type"/>
|
||||
<field name="sign"/>
|
||||
<field name="range_selection" attrs="{'required':[('type','in',['accounts','account_type'])]}"/>
|
||||
<field name="style_overwrite" invisible="1"/>
|
||||
</group>
|
||||
<notebook
|
||||
attrs="{'invisible': [('type','not in',['accounts','account_type', 'account_report'])]}">
|
||||
<page string="Report">
|
||||
<group>
|
||||
<field name="display_detail"
|
||||
attrs="{'invisible': [('type','not in',['accounts','account_type'])]}"/>
|
||||
<field name="account_report_id"
|
||||
attrs="{'invisible': [('type', '!=', 'account_report')]}"/>
|
||||
</group>
|
||||
<field name="account_ids"
|
||||
attrs="{'invisible': [('type', '!=', 'accounts')]}"/>
|
||||
<field name="account_type_ids"
|
||||
attrs="{'invisible': [('type', '!=', 'account_type')]}"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_ins_account_financial_report_tree" model="ir.ui.view">
|
||||
<field name="name">account.financial.report.tree</field>
|
||||
<field name="model">ins.account.financial.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Account Report">
|
||||
<field name="name"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="type"/>
|
||||
<field name="account_report_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_ins_account_financial_report_search" model="ir.ui.view">
|
||||
<field name="name">account.financial.report.search</field>
|
||||
<field name="model">ins.account.financial.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Account Report">
|
||||
<field name="name" string="Account Report"/>
|
||||
<field name="type"/>
|
||||
<field name="account_report_id"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Parent Report"
|
||||
name="filter_parent_rep"
|
||||
domain=""
|
||||
context="{'group_by':'parent_id'}"/>
|
||||
<filter string="Report Type"
|
||||
name="filter_rep_type"
|
||||
domain="[]"
|
||||
context="{'group_by':'type'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_account_financial_report_tree"
|
||||
model="ir.actions.act_window">
|
||||
<field name="name">Financial Reports</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ins.account.financial.report</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id"
|
||||
ref="view_ins_account_financial_report_search"/>
|
||||
<field name="view_id" ref="view_ins_account_financial_report_tree"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="ins_account_financial_report"
|
||||
name="Financial Reports"
|
||||
action="action_ins_account_financial_report_tree"
|
||||
sequence="500"
|
||||
parent="account.account_account_menu"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,6 @@
|
||||
from . import general_ledger
|
||||
from . import partner_ledger
|
||||
from . import trial_balance
|
||||
from . import partner_ageing
|
||||
from . import financial_report
|
||||
from . import faspe_consolidated_report
|
||||
@@ -0,0 +1,372 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import date
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsFaspeConsolidatedWizard(models.TransientModel):
|
||||
_name = 'ins.faspe.consolidated.wizard'
|
||||
_description = 'FASPE Consolidated Multi Company Wizard'
|
||||
|
||||
title = fields.Char(
|
||||
string='Judul',
|
||||
default='Laporan Keuangan Financial Accounting Standards for Private Entitie(FASPE)',
|
||||
readonly=True,
|
||||
)
|
||||
date_from = fields.Date(
|
||||
string='Tanggal Mulai',
|
||||
required=True,
|
||||
default=lambda self: date(fields.Date.context_today(self).year, 1, 1),
|
||||
)
|
||||
date_to = fields.Date(
|
||||
string='Tanggal Akhir',
|
||||
required=True,
|
||||
default=lambda self: fields.Date.context_today(self),
|
||||
)
|
||||
target_move = fields.Selection(
|
||||
[('posted', 'All Posted Entries'), ('all', 'All Entries')],
|
||||
string='Target Moves',
|
||||
required=True,
|
||||
default='posted',
|
||||
)
|
||||
company_ids = fields.Many2many(
|
||||
'res.company',
|
||||
'ins_faspe_consolidated_wizard_res_company_rel',
|
||||
'ins_faspe_consolidated_wizard_id',
|
||||
'res_company_id',
|
||||
string='Companies',
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company Utama',
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency',
|
||||
string='Currency',
|
||||
related='company_id.currency_id',
|
||||
readonly=True,
|
||||
)
|
||||
elimination_line_ids = fields.One2many(
|
||||
'ins.faspe.consolidated.elimination.line',
|
||||
'wizard_id',
|
||||
string='Akun Eliminasi per Company',
|
||||
)
|
||||
|
||||
@api.onchange('company_ids')
|
||||
def _onchange_company_ids(self):
|
||||
if not self.company_ids:
|
||||
self.company_id = False
|
||||
self.elimination_line_ids = [(5, 0, 0)]
|
||||
return
|
||||
|
||||
if self.company_id not in self.company_ids:
|
||||
self.company_id = self.company_ids[0]
|
||||
|
||||
existing_by_company = {line.company_id.id: line for line in self.elimination_line_ids if line.company_id}
|
||||
commands = []
|
||||
for company in self.company_ids:
|
||||
line = existing_by_company.get(company.id)
|
||||
if line:
|
||||
commands.append((0, 0, {
|
||||
'company_id': company.id,
|
||||
'account_ids': [(6, 0, line.account_ids.ids)],
|
||||
}))
|
||||
else:
|
||||
commands.append((0, 0, {'company_id': company.id}))
|
||||
|
||||
self.elimination_line_ids = commands
|
||||
|
||||
def _validate_input(self):
|
||||
self.ensure_one()
|
||||
|
||||
if len(self.company_ids) < 2:
|
||||
raise UserError(_('Pilih minimal 2 company untuk laporan consolidated multi company.'))
|
||||
|
||||
if not self.date_from or not self.date_to:
|
||||
raise UserError(_('Tanggal Mulai dan Tanggal Akhir wajib diisi.'))
|
||||
|
||||
if self.date_from > self.date_to:
|
||||
raise UserError(_('Tanggal Mulai tidak boleh lebih besar dari Tanggal Akhir.'))
|
||||
|
||||
currencies = self.company_ids.mapped('currency_id')
|
||||
if len(currencies) > 1:
|
||||
raise UserError(_(
|
||||
'Semua company yang dikonsolidasikan harus menggunakan mata uang yang sama agar presisi laporan terjaga.'
|
||||
))
|
||||
|
||||
def _get_move_line_domain(self):
|
||||
self.ensure_one()
|
||||
domain = [
|
||||
('company_id', 'in', self.company_ids.ids),
|
||||
('account_id.internal_group', 'in', ['asset', 'liability', 'equity']),
|
||||
('date', '>=', self.date_from),
|
||||
('date', '<=', self.date_to),
|
||||
]
|
||||
if self.target_move == 'posted':
|
||||
domain.append(('move_id.state', '=', 'posted'))
|
||||
return domain
|
||||
|
||||
def _prepare_account_payload(self):
|
||||
self.ensure_one()
|
||||
aml_obj = self.env['account.move.line'].sudo()
|
||||
grouped = aml_obj.read_group(
|
||||
domain=self._get_move_line_domain(),
|
||||
fields=['balance', 'account_id', 'company_id'],
|
||||
groupby=['account_id', 'company_id'],
|
||||
lazy=False,
|
||||
)
|
||||
|
||||
account_ids = [row['account_id'][0] for row in grouped if row.get('account_id')]
|
||||
account_map = {account.id: account for account in self.env['account.account'].browse(account_ids)}
|
||||
|
||||
payload = {}
|
||||
balance_by_company_account = {}
|
||||
for row in grouped:
|
||||
if not row.get('account_id') or not row.get('company_id'):
|
||||
continue
|
||||
|
||||
account_id = row['account_id'][0]
|
||||
company_id = row['company_id'][0]
|
||||
balance = row.get('balance', 0.0)
|
||||
account = account_map.get(account_id)
|
||||
if not account:
|
||||
continue
|
||||
|
||||
balance_by_company_account[(company_id, account_id)] = balance
|
||||
if account_id not in payload:
|
||||
payload[account_id] = {
|
||||
'code': account.code,
|
||||
'name': account.name,
|
||||
'internal_group': account.internal_group,
|
||||
'balance': 0.0,
|
||||
}
|
||||
payload[account_id]['balance'] += balance
|
||||
|
||||
return payload, balance_by_company_account
|
||||
|
||||
def _prepare_elimination_payload(self, balance_by_company_account):
|
||||
self.ensure_one()
|
||||
elimination_by_account = defaultdict(float)
|
||||
elimination_total = 0.0
|
||||
|
||||
for elimination_line in self.elimination_line_ids:
|
||||
if not elimination_line.company_id:
|
||||
continue
|
||||
for account in elimination_line.account_ids:
|
||||
amount = balance_by_company_account.get((elimination_line.company_id.id, account.id), 0.0)
|
||||
elimination_by_account[account.id] += amount
|
||||
elimination_total += amount
|
||||
|
||||
return elimination_by_account, elimination_total
|
||||
|
||||
def _prepare_report_lines(self, payload, elimination_by_account):
|
||||
self.ensure_one()
|
||||
currency = self.currency_id
|
||||
|
||||
section_titles = {
|
||||
'asset': 'ASET',
|
||||
'liability': 'LIABILITAS',
|
||||
'equity': 'EKUITAS',
|
||||
}
|
||||
section_order = ['asset', 'liability', 'equity']
|
||||
|
||||
section_data = defaultdict(list)
|
||||
for account_id, values in payload.items():
|
||||
section_key = values.get('internal_group')
|
||||
if section_key not in section_titles:
|
||||
continue
|
||||
elimination_amount = elimination_by_account.get(account_id, 0.0)
|
||||
final_amount = values.get('balance', 0.0) - elimination_amount
|
||||
if currency.is_zero(final_amount) and currency.is_zero(elimination_amount):
|
||||
continue
|
||||
|
||||
section_data[section_key].append({
|
||||
'name': values.get('name'),
|
||||
'account_code': values.get('code'),
|
||||
'amount_before_elimination': values.get('balance', 0.0),
|
||||
'elimination_amount': elimination_amount,
|
||||
'amount': final_amount,
|
||||
})
|
||||
|
||||
line_vals = []
|
||||
totals = {'asset': 0.0, 'liability': 0.0, 'equity': 0.0}
|
||||
sequence = 10
|
||||
|
||||
for section_key in section_order:
|
||||
section_lines = sorted(
|
||||
section_data.get(section_key, []),
|
||||
key=lambda item: (item.get('account_code') or '', item.get('name') or ''),
|
||||
)
|
||||
if not section_lines:
|
||||
continue
|
||||
|
||||
line_vals.append({
|
||||
'sequence': sequence,
|
||||
'line_type': 'section',
|
||||
'name': section_titles[section_key],
|
||||
'section': section_key,
|
||||
})
|
||||
sequence += 10
|
||||
|
||||
for section_line in section_lines:
|
||||
totals[section_key] += section_line['amount']
|
||||
line_vals.append({
|
||||
'sequence': sequence,
|
||||
'line_type': 'account',
|
||||
'name': section_line['name'],
|
||||
'account_code': section_line['account_code'],
|
||||
'section': section_key,
|
||||
'amount_before_elimination': section_line['amount_before_elimination'],
|
||||
'elimination_amount': section_line['elimination_amount'],
|
||||
'amount': section_line['amount'],
|
||||
})
|
||||
sequence += 10
|
||||
|
||||
line_vals.append({
|
||||
'sequence': sequence,
|
||||
'line_type': 'total',
|
||||
'name': 'TOTAL %s' % section_titles[section_key],
|
||||
'section': section_key,
|
||||
'amount_before_elimination': sum(line['amount_before_elimination'] for line in section_lines),
|
||||
'elimination_amount': sum(line['elimination_amount'] for line in section_lines),
|
||||
'amount': totals[section_key],
|
||||
})
|
||||
sequence += 10
|
||||
|
||||
balance_difference = totals['asset'] - (totals['liability'] + totals['equity'])
|
||||
line_vals.append({
|
||||
'sequence': sequence,
|
||||
'line_type': 'check',
|
||||
'name': 'CHECK ASET - (LIABILITAS + EKUITAS)',
|
||||
'section': 'check',
|
||||
'amount': balance_difference,
|
||||
})
|
||||
|
||||
return line_vals, totals, balance_difference
|
||||
|
||||
def action_generate_report(self):
|
||||
self.ensure_one()
|
||||
self._validate_input()
|
||||
|
||||
payload, balance_by_company_account = self._prepare_account_payload()
|
||||
if not payload:
|
||||
raise UserError(_('Tidak ada data akun neraca pada rentang tanggal dan filter yang dipilih.'))
|
||||
|
||||
elimination_by_account, elimination_total = self._prepare_elimination_payload(balance_by_company_account)
|
||||
line_vals, totals, balance_difference = self._prepare_report_lines(payload, elimination_by_account)
|
||||
|
||||
report = self.env['ins.faspe.consolidated.report'].create({
|
||||
'title': self.title,
|
||||
'date_from': self.date_from,
|
||||
'date_to': self.date_to,
|
||||
'target_move': self.target_move,
|
||||
'company_ids': [(6, 0, self.company_ids.ids)],
|
||||
'company_names': ', '.join(self.company_ids.mapped('name')),
|
||||
'currency_id': self.currency_id.id,
|
||||
'elimination_total': elimination_total,
|
||||
'total_assets': totals['asset'],
|
||||
'total_liabilities': totals['liability'],
|
||||
'total_equity': totals['equity'],
|
||||
'balance_difference': balance_difference,
|
||||
})
|
||||
|
||||
line_commands = [(0, 0, line) for line in line_vals]
|
||||
report.write({'line_ids': line_commands})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': self.title,
|
||||
'res_model': 'ins.faspe.consolidated.report',
|
||||
'view_mode': 'form',
|
||||
'view_id': self.env.ref('account_dynamic_reports.ins_faspe_consolidated_report_form').id,
|
||||
'res_id': report.id,
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
|
||||
class InsFaspeConsolidatedEliminationLine(models.TransientModel):
|
||||
_name = 'ins.faspe.consolidated.elimination.line'
|
||||
_description = 'FASPE Consolidated Elimination Line'
|
||||
|
||||
wizard_id = fields.Many2one('ins.faspe.consolidated.wizard', required=True, ondelete='cascade')
|
||||
company_id = fields.Many2one('res.company', string='Company', required=True)
|
||||
account_ids = fields.Many2many(
|
||||
'account.account',
|
||||
'ins_faspe_consol_elim_line_account_rel',
|
||||
'elimination_line_id',
|
||||
'account_id',
|
||||
string='Akun yang Di-eliminasi',
|
||||
)
|
||||
|
||||
@api.onchange('company_id')
|
||||
def _onchange_company_id(self):
|
||||
domain = [('internal_group', 'in', ['asset', 'liability', 'equity'])]
|
||||
if self.company_id:
|
||||
domain.append(('company_id', '=', self.company_id.id))
|
||||
return {'domain': {'account_ids': domain}}
|
||||
|
||||
|
||||
class InsFaspeConsolidatedReport(models.TransientModel):
|
||||
_name = 'ins.faspe.consolidated.report'
|
||||
_description = 'FASPE Consolidated Report'
|
||||
|
||||
title = fields.Char(string='Judul', required=True)
|
||||
generated_at = fields.Datetime(string='Dibuat Pada', default=fields.Datetime.now, readonly=True)
|
||||
date_from = fields.Date(string='Tanggal Mulai', required=True, readonly=True)
|
||||
date_to = fields.Date(string='Tanggal Akhir', required=True, readonly=True)
|
||||
target_move = fields.Selection(
|
||||
[('posted', 'All Posted Entries'), ('all', 'All Entries')],
|
||||
string='Target Moves',
|
||||
readonly=True,
|
||||
)
|
||||
company_ids = fields.Many2many(
|
||||
'res.company',
|
||||
'ins_faspe_consolidated_report_res_company_rel',
|
||||
'ins_faspe_consolidated_report_id',
|
||||
'res_company_id',
|
||||
string='Companies',
|
||||
readonly=True,
|
||||
)
|
||||
company_names = fields.Char(string='Company Consolidated', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', required=True, readonly=True)
|
||||
elimination_total = fields.Monetary(string='Total Eliminasi', currency_field='currency_id', readonly=True)
|
||||
total_assets = fields.Monetary(string='Total Aset', currency_field='currency_id', readonly=True)
|
||||
total_liabilities = fields.Monetary(string='Total Liabilitas', currency_field='currency_id', readonly=True)
|
||||
total_equity = fields.Monetary(string='Total Ekuitas', currency_field='currency_id', readonly=True)
|
||||
balance_difference = fields.Monetary(
|
||||
string='Selisih Aset - (Liabilitas + Ekuitas)',
|
||||
currency_field='currency_id',
|
||||
readonly=True,
|
||||
)
|
||||
line_ids = fields.One2many('ins.faspe.consolidated.report.line', 'report_id', string='Baris Laporan', readonly=True)
|
||||
|
||||
|
||||
class InsFaspeConsolidatedReportLine(models.TransientModel):
|
||||
_name = 'ins.faspe.consolidated.report.line'
|
||||
_description = 'FASPE Consolidated Report Line'
|
||||
_order = 'sequence, id'
|
||||
|
||||
report_id = fields.Many2one('ins.faspe.consolidated.report', required=True, ondelete='cascade')
|
||||
sequence = fields.Integer(default=10)
|
||||
line_type = fields.Selection(
|
||||
[('section', 'Section'), ('account', 'Account'), ('total', 'Total'), ('check', 'Check')],
|
||||
string='Tipe Baris',
|
||||
required=True,
|
||||
)
|
||||
section = fields.Selection(
|
||||
[('asset', 'Aset'), ('liability', 'Liabilitas'), ('equity', 'Ekuitas'), ('check', 'Check')],
|
||||
string='Section',
|
||||
)
|
||||
name = fields.Char(string='Nama', required=True)
|
||||
account_code = fields.Char(string='Kode Akun')
|
||||
amount_before_elimination = fields.Monetary(string='Sebelum Eliminasi', currency_field='currency_id')
|
||||
elimination_amount = fields.Monetary(string='Eliminasi', currency_field='currency_id')
|
||||
amount = fields.Monetary(string='Setelah Eliminasi', currency_field='currency_id')
|
||||
currency_id = fields.Many2one(related='report_id.currency_id', readonly=True)
|
||||
@@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="ins_faspe_consolidated_wizard_form" model="ir.ui.view">
|
||||
<field name="name">ins.faspe.consolidated.wizard.form</field>
|
||||
<field name="model">ins.faspe.consolidated.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Laporan Keuangan Financial Accounting Standards for Private Entitie(FASPE)">
|
||||
<group>
|
||||
<group string="Periode">
|
||||
<field name="date_from" required="1"/>
|
||||
<field name="date_to" required="1"/>
|
||||
<field name="target_move" widget="radio"/>
|
||||
</group>
|
||||
<group string="Konsolidasi Multi Company">
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"
|
||||
options="{'no_create_edit': True, 'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="currency_id" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group string="Akun Eliminasi per Company">
|
||||
<field name="elimination_line_ids" nolabel="1">
|
||||
<tree editable="bottom">
|
||||
<field name="company_id"
|
||||
options="{'no_create_edit': True, 'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="account_ids" widget="many2many_tags"
|
||||
domain="[('company_id', '=', company_id), ('internal_group', 'in', ['asset', 'liability', 'equity'])]"
|
||||
options="{'no_create_edit': True, 'no_create': True, 'no_quick_create': True}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
|
||||
<div class="o_form_label text-muted">
|
||||
Pilih akun yang harus dieliminasi di masing-masing company sebelum laporan consolidated ditampilkan.
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button name="action_generate_report" type="object" string="Tampilkan Laporan" class="btn-primary"/>
|
||||
<button string="Discard" special="cancel" class="btn-secondary"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_faspe_consolidated_report_line_tree" model="ir.ui.view">
|
||||
<field name="name">ins.faspe.consolidated.report.line.tree</field>
|
||||
<field name="model">ins.faspe.consolidated.report.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree create="false" delete="false"
|
||||
decoration-bf="line_type in ('section', 'total')"
|
||||
decoration-info="line_type == 'check'">
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="line_type" invisible="1"/>
|
||||
<field name="section" invisible="1"/>
|
||||
<field name="account_code"/>
|
||||
<field name="name"/>
|
||||
<field name="amount_before_elimination" sum="Sebelum Eliminasi"/>
|
||||
<field name="elimination_amount" sum="Eliminasi"/>
|
||||
<field name="amount" sum="Setelah Eliminasi"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_faspe_consolidated_report_form" model="ir.ui.view">
|
||||
<field name="name">ins.faspe.consolidated.report.form</field>
|
||||
<field name="model">ins.faspe.consolidated.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Laporan Keuangan Financial Accounting Standards for Private Entitie(FASPE)" create="false" delete="false" edit="false">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="title" readonly="1"/>
|
||||
<field name="generated_at" readonly="1"/>
|
||||
<field name="target_move" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_from" readonly="1"/>
|
||||
<field name="date_to" readonly="1"/>
|
||||
<field name="company_names" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group string="Ringkasan Konsolidasi">
|
||||
<field name="elimination_total" readonly="1"/>
|
||||
<field name="total_assets" readonly="1"/>
|
||||
<field name="total_liabilities" readonly="1"/>
|
||||
<field name="total_equity" readonly="1"/>
|
||||
<field name="balance_difference" readonly="1"/>
|
||||
</group>
|
||||
|
||||
<group string="Detail Laporan">
|
||||
<field name="line_ids" nolabel="1" readonly="1">
|
||||
<tree create="false" delete="false"
|
||||
decoration-bf="line_type in ('section', 'total')"
|
||||
decoration-info="line_type == 'check'">
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="line_type" invisible="1"/>
|
||||
<field name="section" invisible="1"/>
|
||||
<field name="account_code"/>
|
||||
<field name="name"/>
|
||||
<field name="amount_before_elimination" sum="Sebelum Eliminasi"/>
|
||||
<field name="elimination_amount" sum="Eliminasi"/>
|
||||
<field name="amount" sum="Setelah Eliminasi"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_faspe_consolidated_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Laporan Keuangan Financial Accounting Standards for Private Entitie(FASPE)</field>
|
||||
<field name="res_model">ins.faspe.consolidated.wizard</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_faspe_consolidated_wizard_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_faspe_consolidated"
|
||||
name="Laporan Keuangan FASPE"
|
||||
parent="account_reports_ins"
|
||||
action="action_ins_faspe_consolidated_wizard"
|
||||
sequence="90"
|
||||
groups="account.group_account_user"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,859 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, models, fields, _
|
||||
import re
|
||||
|
||||
from datetime import datetime, timedelta, date
|
||||
import calendar
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.exceptions import UserError
|
||||
import json
|
||||
import io
|
||||
from odoo.tools import date_utils
|
||||
|
||||
try:
|
||||
from odoo.tools.misc import xlsxwriter
|
||||
except ImportError:
|
||||
import xlsxwriter
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
|
||||
class InsFinancialReport(models.TransientModel):
|
||||
_name = "ins.financial.report"
|
||||
_description = "Financial Reports"
|
||||
|
||||
def _get_report_company(self):
|
||||
self.ensure_one()
|
||||
return self.company_id or self.env.company
|
||||
|
||||
def _get_report_companies(self):
|
||||
self.ensure_one()
|
||||
return self.company_ids or self.company_id or self.env.company
|
||||
|
||||
def _get_report_company_ids(self):
|
||||
self.ensure_one()
|
||||
return self._get_report_companies().ids or [self.env.company.id]
|
||||
|
||||
def _get_company_display_name(self):
|
||||
self.ensure_one()
|
||||
return ', '.join(self._get_report_companies().mapped('name'))
|
||||
|
||||
def _get_available_companies(self):
|
||||
return self.env.user.company_ids
|
||||
|
||||
@api.onchange('company_id', 'company_ids')
|
||||
def _onchange_company_id(self):
|
||||
companies = self.company_ids or self.company_id
|
||||
if self.company_ids:
|
||||
self.company_id = self.company_ids[0]
|
||||
elif self.company_id:
|
||||
self.company_ids = [(6, 0, [self.company_id.id])]
|
||||
if companies:
|
||||
self.journal_ids = self.env['account.journal'].search(
|
||||
[('company_id', 'in', companies.ids)])
|
||||
else:
|
||||
self.journal_ids = self.env['account.journal'].search([])
|
||||
|
||||
@api.onchange('date_range', 'financial_year')
|
||||
def onchange_date_range(self):
|
||||
if self.date_range:
|
||||
date = datetime.today()
|
||||
if self.date_range == 'today':
|
||||
self.date_from = date.strftime("%Y-%m-%d")
|
||||
self.date_to = date.strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_week':
|
||||
day_today = date - timedelta(days=date.weekday())
|
||||
self.date_from = (day_today - timedelta(days=date.weekday())).strftime("%Y-%m-%d")
|
||||
self.date_to = (day_today + timedelta(days=6)).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_month':
|
||||
self.date_from = datetime(date.year, date.month, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, date.month, calendar.mdays[date.month]).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_quarter':
|
||||
if int((date.month - 1) / 3) == 0: # First quarter
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, calendar.mdays[3]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 1: # Second quarter
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, calendar.mdays[6]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 2: # Third quarter
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 9, calendar.mdays[9]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 3: # Fourth quarter
|
||||
self.date_from = datetime(date.year, 10, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, calendar.mdays[12]).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_financial_year':
|
||||
if self.financial_year == 'january_december':
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'april_march':
|
||||
if date.month < 4:
|
||||
self.date_from = datetime(date.year - 1, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, 31).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 3, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'july_june':
|
||||
if date.month < 7:
|
||||
self.date_from = datetime(date.year - 1, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, 30).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 6, 30).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(days=1))
|
||||
if self.date_range == 'yesterday':
|
||||
self.date_from = date.strftime("%Y-%m-%d")
|
||||
self.date_to = date.strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(days=7))
|
||||
if self.date_range == 'last_week':
|
||||
day_today = date - timedelta(days=date.weekday())
|
||||
self.date_from = (day_today - timedelta(days=date.weekday())).strftime("%Y-%m-%d")
|
||||
self.date_to = (day_today + timedelta(days=6)).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(months=1))
|
||||
if self.date_range == 'last_month':
|
||||
self.date_from = datetime(date.year, date.month, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, date.month, calendar.mdays[date.month]).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(months=3))
|
||||
if self.date_range == 'last_quarter':
|
||||
if int((date.month - 1) / 3) == 0: # First quarter
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, calendar.mdays[3]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 1: # Second quarter
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, calendar.mdays[6]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 2: # Third quarter
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 9, calendar.mdays[9]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 3: # Fourth quarter
|
||||
self.date_from = datetime(date.year, 10, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, calendar.mdays[12]).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(years=1))
|
||||
if self.date_range == 'last_financial_year':
|
||||
if self.financial_year == 'january_december':
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'april_march':
|
||||
if date.month < 4:
|
||||
self.date_from = datetime(date.year - 1, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, 31).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 3, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'july_june':
|
||||
if date.month < 7:
|
||||
self.date_from = datetime(date.year - 1, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, 30).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 6, 30).strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
def _compute_account_balance(self, accounts, report):
|
||||
""" compute the balance, debit and credit for the provided accounts
|
||||
"""
|
||||
mapping = {
|
||||
'balance': "COALESCE(SUM(debit),0) - COALESCE(SUM(credit), 0) as balance",
|
||||
'debit': "COALESCE(SUM(debit), 0) as debit",
|
||||
'credit': "COALESCE(SUM(credit), 0) as credit",
|
||||
}
|
||||
|
||||
res = {}
|
||||
for account in accounts:
|
||||
res[account.id] = dict.fromkeys(mapping, 0.0)
|
||||
if accounts:
|
||||
if self.account_report_id != \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0') and self.strict_range:
|
||||
|
||||
context = dict(self._context, strict_range=True)
|
||||
# Validation
|
||||
if report.type in ['accounts','account_type'] and not report.range_selection:
|
||||
raise UserError(_('Please choose "Custom Date Range" for the report head %s')%(report.name))
|
||||
|
||||
if report.type in ['accounts','account_type'] and report.range_selection == 'from_the_beginning':
|
||||
context.update({'strict_range': False})
|
||||
# For equity
|
||||
if report.type in ['accounts','account_type'] and report.range_selection == 'current_date_range':
|
||||
if self.date_to and self.date_from:
|
||||
context.update({'strict_range': True, 'initial_bal': False, 'date_from': self.date_from,'date_to': self.date_to})
|
||||
else:
|
||||
raise UserError(_('From date and To date are mandatory to generate this report'))
|
||||
if report.type in ['accounts','account_type'] and report.range_selection == 'initial_date_range':
|
||||
if self.date_from:
|
||||
context.update({'strict_range': True, 'initial_bal': True, 'date_from': self.date_from,'date_to': False})
|
||||
else:
|
||||
raise UserError(_('From date is mandatory to generate this report'))
|
||||
tables, where_clause, where_params = self.env['account.move.line'].with_context(context)._query_get()
|
||||
else:
|
||||
tables, where_clause, where_params = self.env['account.move.line']._query_get()
|
||||
tables = tables.replace('"', '') if tables else "account_move_line"
|
||||
wheres = [""]
|
||||
if where_clause.strip():
|
||||
wheres.append(where_clause.strip())
|
||||
filters = " AND ".join(wheres)
|
||||
request = "SELECT account_id as id, " + ', '.join(mapping.values()) + \
|
||||
" FROM " + tables + \
|
||||
" WHERE account_id IN %s " \
|
||||
+ filters + \
|
||||
" GROUP BY account_id"
|
||||
params = (tuple(accounts._ids),) + tuple(where_params)
|
||||
self.env.cr.execute(request, params)
|
||||
for row in self.env.cr.dictfetchall():
|
||||
res[row['id']] = row
|
||||
return res
|
||||
|
||||
def _compute_report_balance(self, reports):
|
||||
'''returns a dictionary with key=the ID of a record and value=the credit, debit and balance amount
|
||||
computed for this record. If the record is of type :
|
||||
'accounts' : it's the sum of the linked accounts
|
||||
'account_type' : it's the sum of leaf accoutns with such an account_type
|
||||
'account_report' : it's the amount of the related report
|
||||
'sum' : it's the sum of the children of this record (aka a 'view' record)'''
|
||||
res = {}
|
||||
fields = ['credit', 'debit', 'balance']
|
||||
for report in reports:
|
||||
if report.id in res:
|
||||
continue
|
||||
res[report.id] = dict((fn, 0.0) for fn in fields)
|
||||
if report.type == 'accounts':
|
||||
# it's the sum of the linked accounts
|
||||
if self.account_report_id != \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
res[report.id]['account'] = self._compute_account_balance(report.account_ids, report)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
else:
|
||||
res2 = self._compute_report_balance(report.parent_id)
|
||||
for key, value in res2.items():
|
||||
if report in [self.env.ref('account_dynamic_reports.ins_cash_in_operation_1'),
|
||||
self.env.ref('account_dynamic_reports.ins_cash_in_investing_1'),
|
||||
self.env.ref('account_dynamic_reports.ins_cash_in_financial_1')]:
|
||||
res[report.id]['debit'] += value['debit']
|
||||
res[report.id]['balance'] += value['debit']
|
||||
else:
|
||||
res[report.id]['credit'] += value['credit']
|
||||
res[report.id]['balance'] += -(value['credit'])
|
||||
elif report.type == 'account_type':
|
||||
# it's the sum the leaf accounts with such an account type
|
||||
if self.account_report_id != \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
accounts = self.env['account.account'].search([('user_type_id', 'in', report.account_type_ids.ids)])
|
||||
res[report.id]['account'] = self._compute_account_balance(accounts, report)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
else:
|
||||
accounts = self.env['account.account'].search(
|
||||
[('user_type_id', 'in', report.account_type_ids.ids)])
|
||||
res[report.id]['account'] = self._compute_account_balance(
|
||||
accounts, report)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
elif report.type == 'account_report' and report.account_report_id:
|
||||
# it's the amount of the linked report
|
||||
if self.account_report_id != \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
res2 = self._compute_report_balance(report.account_report_id)
|
||||
for key, value in res2.items():
|
||||
for field in fields:
|
||||
res[report.id][field] += value[field]
|
||||
else:
|
||||
res[report.id]['account'] = self._compute_account_balance(
|
||||
report.account_ids, report)
|
||||
for value in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += value.get(field)
|
||||
elif report.type == 'sum':
|
||||
# it's the sum of the children of this account.report
|
||||
if self.account_report_id != \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
res2 = self._compute_report_balance(report.children_ids)
|
||||
for key, value in res2.items():
|
||||
for field in fields:
|
||||
res[report.id][field] += value[field]
|
||||
else:
|
||||
accounts = report.account_ids
|
||||
if report == self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
accounts = self.env['account.account'].search([('company_id', 'in', self._get_report_company_ids()),
|
||||
('cash_flow_category', 'not in', [0])])
|
||||
res[report.id]['account'] = self._compute_account_balance(accounts, report)
|
||||
for values in res[report.id]['account'].values():
|
||||
for field in fields:
|
||||
res[report.id][field] += values.get(field)
|
||||
return res
|
||||
|
||||
def get_account_lines(self, data):
|
||||
lines = []
|
||||
initial_balance = 0.0
|
||||
current_balance = 0.0
|
||||
ending_balance = 0.0
|
||||
account_report = self.account_report_id
|
||||
child_reports = account_report._get_children_by_order(strict_range = self.strict_range)
|
||||
res = self.with_context(data.get('used_context'))._compute_report_balance(child_reports)
|
||||
if self.account_report_id == \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
if not data.get('used_context').get('date_from',False):
|
||||
raise UserError(_('Start date is mandatory!'))
|
||||
cashflow_context = data.get('used_context')
|
||||
initial_to = fields.Date.from_string(data.get('used_context').get('date_from')) - timedelta(days=1)
|
||||
cashflow_context.update({'date_from': False, 'date_to': fields.Date.to_string(initial_to)})
|
||||
initial_balance = self.with_context(cashflow_context)._compute_report_balance(child_reports). \
|
||||
get(self.account_report_id.id)['balance']
|
||||
current_balance = res.get(self.account_report_id.id)['balance']
|
||||
ending_balance = initial_balance + current_balance
|
||||
if data['enable_filter']:
|
||||
comparison_res = self.with_context(data.get('comparison_context'))._compute_report_balance(child_reports)
|
||||
for report_id, value in comparison_res.items():
|
||||
res[report_id]['comp_bal'] = value['balance']
|
||||
report_acc = res[report_id].get('account')
|
||||
if report_acc:
|
||||
for account_id, val in comparison_res[report_id].get('account').items():
|
||||
report_acc[account_id]['comp_bal'] = val['balance']
|
||||
|
||||
for report in child_reports:
|
||||
company_id = self._get_report_company()
|
||||
currency_id = company_id.currency_id
|
||||
vals = {
|
||||
'name': report.name,
|
||||
'balance': res[report.id]['balance'] * int(report.sign),
|
||||
'parent': report.parent_id.id if report.parent_id.type in ['accounts','account_type'] else 0,
|
||||
'self_id': report.id,
|
||||
'type': 'report',
|
||||
'style_type': 'main',
|
||||
'precision': currency_id.decimal_places,
|
||||
'symbol': currency_id.symbol,
|
||||
'position': currency_id.position,
|
||||
'list_len': [a for a in range(0,report.level)],
|
||||
'level': report.level,
|
||||
'company_currency_id': currency_id.id,
|
||||
'account_type': report.type or False, #used to underline the financial report balances
|
||||
'fin_report_type': report.type,
|
||||
'display_detail': report.display_detail
|
||||
}
|
||||
if data['debit_credit']:
|
||||
vals['debit'] = res[report.id]['debit']
|
||||
vals['credit'] = res[report.id]['credit']
|
||||
|
||||
if data['enable_filter']:
|
||||
vals['balance_cmp'] = res[report.id]['comp_bal'] * int(report.sign)
|
||||
|
||||
lines.append(vals)
|
||||
if report.display_detail == 'no_detail':
|
||||
continue
|
||||
|
||||
if res[report.id].get('account'):
|
||||
sub_lines = []
|
||||
for account_id, value in res[report.id]['account'].items():
|
||||
flag = False
|
||||
account = self.env['account.account'].browse(account_id)
|
||||
vals = {
|
||||
'account': account.id,
|
||||
'name': account.code + ' ' + account.name,
|
||||
'balance': value['balance'] * int(report.sign) or 0.0,
|
||||
'type': 'account',
|
||||
'parent': report.id if report.type in ['accounts','account_type'] else 0,
|
||||
'self_id': 50,
|
||||
'style_type': 'sub',
|
||||
'precision': currency_id.decimal_places,
|
||||
'symbol': currency_id.symbol,
|
||||
'position': currency_id.position,
|
||||
'list_len':[a for a in range(0,report.display_detail == 'detail_with_hierarchy' and 4)],
|
||||
'level': 4,
|
||||
'company_currency_id': currency_id.id,
|
||||
'account_type': account.internal_type,
|
||||
'fin_report_type': report.type,
|
||||
'display_detail': report.display_detail
|
||||
}
|
||||
if data['debit_credit']:
|
||||
vals['debit'] = value['debit']
|
||||
vals['credit'] = value['credit']
|
||||
if not currency_id.is_zero(vals['debit']) or not currency_id.is_zero(vals['credit']):
|
||||
flag = True
|
||||
if not currency_id.is_zero(vals['balance']):
|
||||
flag = True
|
||||
if data['enable_filter']:
|
||||
vals['balance_cmp'] = value['comp_bal'] * int(report.sign)
|
||||
if not currency_id.is_zero(vals['balance_cmp']):
|
||||
flag = True
|
||||
if flag:
|
||||
sub_lines.append(vals)
|
||||
lines += sorted(sub_lines, key=lambda sub_line: sub_line['name'])
|
||||
return lines, initial_balance, current_balance, ending_balance
|
||||
|
||||
def get_report_values(self):
|
||||
self.ensure_one()
|
||||
|
||||
self.onchange_date_range()
|
||||
|
||||
company = self._get_report_company()
|
||||
company_ids = self._get_report_company_ids()
|
||||
company_domain = [('company_id', 'in', company_ids)]
|
||||
|
||||
journal_ids = self.env['account.journal'].search(company_domain)
|
||||
analytics = self.env['account.analytic.account'].search(company_domain)
|
||||
analytic_tags = self.env['account.analytic.tag'].sudo().search(
|
||||
['|', ('company_id', 'in', company_ids), ('company_id', '=', False)])
|
||||
companies = self._get_available_companies()
|
||||
|
||||
data = dict()
|
||||
data['ids'] = self.env.context.get('active_ids', [])
|
||||
data['model'] = self.env.context.get('active_model', 'ir.ui.menu')
|
||||
data['form'] = self.read(
|
||||
['date_from', 'enable_filter', 'debit_credit', 'date_to', 'date_range',
|
||||
'account_report_id', 'target_move', 'view_format', 'journal_ids',
|
||||
'analytic_ids', 'analytic_tag_ids', 'strict_range',
|
||||
'company_id', 'company_ids', 'enable_filter', 'date_from_cmp', 'date_to_cmp', 'label_filter', 'filter_cmp'])[0]
|
||||
data['form'].update({'journals_list': [(j.id, j.name) for j in journal_ids]})
|
||||
data['form'].update({'analytics_list': [(j.id, j.name) for j in analytics]})
|
||||
data['form'].update({'analytic_tag_list': [(j.id, j.name) for j in analytic_tags]})
|
||||
data['form'].update({'companies_list': [(c.id, c.name) for c in companies]})
|
||||
data['form'].update({'company_name': self._get_company_display_name()})
|
||||
data['form'].update({'company_ids': company_ids})
|
||||
data['form'].update({'selected_analytics': self.analytic_ids.mapped('name')})
|
||||
data['form'].update({'selected_analytic_tags': self.analytic_tag_ids.mapped('name')})
|
||||
|
||||
if self.enable_filter:
|
||||
data['form']['debit_credit'] = False
|
||||
|
||||
date_from, date_to = False, False
|
||||
used_context = {}
|
||||
used_context['date_from'] = self.date_from or False
|
||||
used_context['date_to'] = self.date_to or False
|
||||
|
||||
used_context['strict_range'] = True
|
||||
used_context['company_id'] = company.id
|
||||
used_context['allowed_company_ids'] = company_ids
|
||||
used_context['company_ids'] = company_ids
|
||||
|
||||
used_context['journal_ids'] = self.journal_ids.ids
|
||||
used_context['analytic_account_ids'] = self.analytic_ids
|
||||
used_context['analytic_tag_ids'] = self.analytic_tag_ids
|
||||
used_context['state'] = data['form'].get('target_move', '')
|
||||
data['form']['used_context'] = used_context
|
||||
|
||||
comparison_context = {}
|
||||
comparison_context['strict_range'] = True
|
||||
comparison_context['company_id'] = company.id
|
||||
comparison_context['allowed_company_ids'] = company_ids
|
||||
comparison_context['company_ids'] = company_ids
|
||||
|
||||
comparison_context['journal_ids'] = self.journal_ids.ids
|
||||
comparison_context['analytic_account_ids'] = self.analytic_ids
|
||||
comparison_context['analytic_tag_ids'] = self.analytic_tag_ids
|
||||
if self.filter_cmp == 'filter_date':
|
||||
comparison_context['date_to'] = self.date_to_cmp or ''
|
||||
comparison_context['date_from'] = self.date_from_cmp or ''
|
||||
else:
|
||||
comparison_context['date_to'] = False
|
||||
comparison_context['date_from'] = False
|
||||
comparison_context['state'] = self.target_move or ''
|
||||
data['form']['comparison_context'] = comparison_context
|
||||
|
||||
report_lines, initial_balance, current_balance, ending_balance = self.get_account_lines(data.get('form'))
|
||||
data['currency'] = company.currency_id.id
|
||||
data['report_lines'] = report_lines
|
||||
data['initial_balance'] = initial_balance or 0.0
|
||||
data['current_balance'] = current_balance or 0.0
|
||||
data['ending_balance'] = ending_balance or 0.0
|
||||
if self.account_report_id == \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_cash_flow0'):
|
||||
data['form']['rtype'] = 'CASH'
|
||||
elif self.account_report_id == \
|
||||
self.env.ref('account_dynamic_reports.ins_account_financial_report_profitandloss0'):
|
||||
data['form']['rtype'] = 'PANDL'
|
||||
else:
|
||||
if self.strict_range:
|
||||
data['form']['rtype'] = 'OTHER'
|
||||
else:
|
||||
data['form']['rtype'] = 'PANDL'
|
||||
return data
|
||||
|
||||
@api.model
|
||||
def _get_default_report_id(self):
|
||||
if self.env.context.get('report_name', False):
|
||||
return self.env.context.get('report_name', False)
|
||||
return self.env.ref('account_dynamic_reports.ins_account_financial_report_profitandloss0').id
|
||||
|
||||
@api.model
|
||||
def _get_default_date_range(self):
|
||||
return self.env.company.date_range
|
||||
|
||||
@api.depends('account_report_id')
|
||||
def name_get(self):
|
||||
res = []
|
||||
for record in self:
|
||||
name = record.account_report_id.name or 'Financial Report'
|
||||
res.append((record.id, name))
|
||||
return res
|
||||
|
||||
financial_year = fields.Selection(
|
||||
[('april_march', '1 April to 31 March'),
|
||||
('july_june', '1 july to 30 June'),
|
||||
('january_december', '1 Jan to 31 Dec')],
|
||||
string='Financial Year', default=lambda self: self.env.company.financial_year, required=True)
|
||||
|
||||
date_range = fields.Selection(
|
||||
[('today', 'Today'),
|
||||
('this_week', 'This Week'),
|
||||
('this_month', 'This Month'),
|
||||
('this_quarter', 'This Quarter'),
|
||||
('this_financial_year', 'This financial Year'),
|
||||
('yesterday', 'Yesterday'),
|
||||
('last_week', 'Last Week'),
|
||||
('last_month', 'Last Month'),
|
||||
('last_quarter', 'Last Quarter'),
|
||||
('last_financial_year', 'Last Financial Year')],
|
||||
string='Date Range', default=_get_default_date_range
|
||||
)
|
||||
view_format = fields.Selection([
|
||||
('vertical', 'Vertical'),
|
||||
('horizontal', 'Horizontal')],
|
||||
default='vertical',
|
||||
string="Format")
|
||||
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company)
|
||||
company_ids = fields.Many2many(
|
||||
'res.company',
|
||||
'ins_financial_report_res_company_rel',
|
||||
'ins_financial_report_id',
|
||||
'res_company_id',
|
||||
string='Companies',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
strict_range = fields.Boolean(
|
||||
string='Strict Range',
|
||||
default=lambda self: self.env.company.strict_range)
|
||||
journal_ids = fields.Many2many('account.journal', string='Journals', required=True,
|
||||
default=lambda self: self.env['account.journal'].search(
|
||||
[('company_id', '=', self.company_id.id)]))
|
||||
date_from = fields.Date(string='Start Date')
|
||||
date_to = fields.Date(string='End Date')
|
||||
target_move = fields.Selection([('posted', 'All Posted Entries'),
|
||||
('all', 'All Entries'),
|
||||
], string='Target Moves', required=True, default='posted')
|
||||
|
||||
enable_filter = fields.Boolean(
|
||||
string='Enable Comparison',
|
||||
default=False)
|
||||
account_report_id = fields.Many2one(
|
||||
'ins.account.financial.report',
|
||||
string='Account Reports',
|
||||
required=True, default=_get_default_report_id)
|
||||
|
||||
debit_credit = fields.Boolean(
|
||||
string='Display Debit/Credit Columns',
|
||||
default=True,
|
||||
help="Help to identify debit and credit with balance line for better understanding.")
|
||||
analytic_ids = fields.Many2many(
|
||||
'account.analytic.account', string='Analytic Accounts'
|
||||
)
|
||||
analytic_tag_ids = fields.Many2many(
|
||||
'account.analytic.tag', string='Analytic Tags'
|
||||
)
|
||||
date_from_cmp = fields.Date(string='Start Date')
|
||||
date_to_cmp = fields.Date(string='End Date')
|
||||
filter_cmp = fields.Selection([('filter_no', 'No Filters'), ('filter_date', 'Date')], string='Filter by',
|
||||
required=True, default='filter_date')
|
||||
label_filter = fields.Char(string='Column Label', default='Comparison Period',
|
||||
help="This label will be displayed on report to show the balance computed for the given comparison filter.")
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
ret = super(InsFinancialReport, self).create(vals)
|
||||
return ret
|
||||
|
||||
def write(self, vals):
|
||||
|
||||
if vals.get('date_range'):
|
||||
vals.update({'date_from': False, 'date_to': False})
|
||||
if vals.get('date_from') or vals.get('date_to'):
|
||||
vals.update({'date_range': False})
|
||||
|
||||
if vals.get('journal_ids'):
|
||||
vals.update({'journal_ids': vals.get('journal_ids')})
|
||||
if vals.get('journal_ids') == []:
|
||||
vals.update({'journal_ids': [(5,)]})
|
||||
|
||||
if vals.get('analytic_ids'):
|
||||
vals.update({'analytic_ids': vals.get('analytic_ids')})
|
||||
if vals.get('analytic_ids') == []:
|
||||
vals.update({'analytic_ids': [(5,)]})
|
||||
|
||||
if vals.get('analytic_tag_ids'):
|
||||
vals.update({'analytic_tag_ids': vals.get('analytic_tag_ids')})
|
||||
if vals.get('analytic_tag_ids') == []:
|
||||
vals.update({'analytic_tag_ids': [(5,)]})
|
||||
|
||||
if vals.get('company_ids'):
|
||||
company_ids = vals.get('company_ids')
|
||||
if isinstance(company_ids, list) and company_ids and isinstance(company_ids[0], int):
|
||||
vals.update({'company_ids': [(6, 0, company_ids)], 'company_id': company_ids[0]})
|
||||
if vals.get('company_ids') == []:
|
||||
vals.update({'company_ids': [(5,)]})
|
||||
if vals.get('company_id') and not vals.get('company_ids'):
|
||||
vals.update({'company_ids': [(6, 0, [vals.get('company_id')])]})
|
||||
|
||||
ret = super(InsFinancialReport, self).write(vals)
|
||||
return ret
|
||||
|
||||
def action_pdf(self):
|
||||
''' Button function for Pdf '''
|
||||
data = self.get_report_values()
|
||||
return self.env.ref(
|
||||
'account_dynamic_reports.ins_financial_report_pdf').report_action(self,
|
||||
data)
|
||||
|
||||
def action_xlsx(self):
|
||||
''' Button function for Xlsx '''
|
||||
|
||||
data = self.read()
|
||||
date_from = fields.Date.from_string(self.date_from).strftime(
|
||||
self.env['res.lang'].search([('code', '=', self.env.user.lang)])[0].date_format)
|
||||
date_to = fields.Date.from_string(self.date_to).strftime(
|
||||
self.env['res.lang'].search([('code', '=', self.env.user.lang)])[0].date_format)
|
||||
report = self.account_report_id.name
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.report',
|
||||
'data': {'model': 'ins.financial.report',
|
||||
'options': json.dumps(data[0], default=date_utils.json_default),
|
||||
'output_format': 'xlsx',
|
||||
'report_name': '%s - %s / %s' %(report, date_from , date_to),
|
||||
},
|
||||
'report_type': 'xlsx'
|
||||
}
|
||||
|
||||
def get_xlsx_report(self, data, response):
|
||||
|
||||
# Initialize
|
||||
#############################################################
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet(data['account_report_id'][1])
|
||||
sheet.set_zoom(95)
|
||||
sheet2 = workbook.add_worksheet('Filters')
|
||||
sheet2.protect()
|
||||
|
||||
# Get record and data
|
||||
record = self.env['ins.financial.report'].browse(data.get('id', [])) or False
|
||||
data = record.get_report_values()
|
||||
|
||||
# Formats
|
||||
############################################################
|
||||
sheet2.set_column(0, 0, 25)
|
||||
sheet2.set_column(1, 1, 25)
|
||||
sheet2.set_column(2, 2, 25)
|
||||
sheet2.set_column(3, 3, 25)
|
||||
sheet2.set_column(4, 4, 25)
|
||||
sheet2.set_column(5, 5, 25)
|
||||
sheet2.set_column(6, 6, 25)
|
||||
sheet.freeze_panes(4, 0)
|
||||
sheet.screen_gridlines = False
|
||||
sheet2.screen_gridlines = False
|
||||
sheet2.protect()
|
||||
|
||||
format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
})
|
||||
format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'bottom': False
|
||||
})
|
||||
content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
# 'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
line_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': False
|
||||
})
|
||||
line_header_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
line_header_string = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': False
|
||||
})
|
||||
line_header_string_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
lang = self.env.user.lang
|
||||
lang_id = self.env['res.lang'].search([('code', '=', lang)])[0]
|
||||
currency_id = self.env.user.company_id.currency_id
|
||||
line_header.num_format = currency_id.excel_format
|
||||
line_header_bold.num_format = currency_id.excel_format
|
||||
content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
# Write data
|
||||
################################################################
|
||||
row_pos_2 = 0
|
||||
row_pos = 0
|
||||
sheet2.write(row_pos_2, 0, _('Date from'), format_header)
|
||||
datestring = fields.Date.from_string(str(data['form']['date_from'] and data['form']['date_from'])).strftime(lang_id.date_format)
|
||||
if data['form'].get('date_from'):
|
||||
sheet2.write(row_pos_2, 1, datestring, content_header_date)
|
||||
row_pos_2 += 1
|
||||
# Date to
|
||||
sheet2.write(row_pos_2, 0, _('Date to'), format_header)
|
||||
datestring = fields.Date.from_string(str(data['form']['date_to'] and data['form']['date_to'])).strftime(
|
||||
lang_id.date_format)
|
||||
if data['form'].get('date_to'):
|
||||
sheet2.write(row_pos_2, 1, datestring,
|
||||
content_header_date)
|
||||
row_pos_2 += 1
|
||||
if data['form']['enable_filter']:
|
||||
# Compariosn Date from
|
||||
sheet2.write(row_pos_2, 0, _('Comparison Date from'),
|
||||
format_header)
|
||||
datestring = fields.Date.from_string(str(data['form']['comparison_context']['date_from'] and data['form']['comparison_context'][
|
||||
'date_from'].strftime('%Y-%m-%d')))
|
||||
if data['form']['comparison_context'].get('date_from'):
|
||||
sheet2.write(row_pos_2, 1, datestring,
|
||||
content_header_date)
|
||||
row_pos_2 += 1
|
||||
# Compariosn Date to
|
||||
sheet2.write(row_pos_2, 0, _('Comparison Date to'),
|
||||
format_header)
|
||||
datestring = fields.Date.from_string(
|
||||
str(data['form']['comparison_context']['date_to'] and data['form']['comparison_context'][
|
||||
'date_to'].strftime('%Y-%m-%d')))
|
||||
if data['form']['comparison_context'].get('date_to'):
|
||||
sheet2.write(row_pos_2, 1, datestring,
|
||||
content_header_date)
|
||||
|
||||
# Write Ledger details
|
||||
row_pos += 3
|
||||
if data['form']['debit_credit'] == 1:
|
||||
|
||||
sheet.set_column(0, 0, 90)
|
||||
sheet.set_column(1, 1, 15)
|
||||
sheet.set_column(2, 3, 15)
|
||||
sheet.set_column(3, 3, 15)
|
||||
|
||||
sheet.write(row_pos, 0, _('Name'), format_header)
|
||||
sheet.write(row_pos, 1, _('Debit'), format_header)
|
||||
sheet.write(row_pos, 2, _('Credit'), format_header)
|
||||
sheet.write(row_pos, 3, _('Balance'), format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
row_pos += 1
|
||||
row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = line_header_string
|
||||
tmp_style_num = line_header
|
||||
else:
|
||||
tmp_style_str = line_header_string_bold
|
||||
tmp_style_num = line_header_bold
|
||||
sheet.write(row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
sheet.write(row_pos, 1, float(a.get('debit')), tmp_style_num)
|
||||
sheet.write(row_pos, 2, float(a.get('credit')), tmp_style_num)
|
||||
sheet.write(row_pos, 3, float(a.get('balance')), tmp_style_num)
|
||||
|
||||
if data['form']['debit_credit'] != 1:
|
||||
|
||||
sheet.set_column(0, 0, 105)
|
||||
sheet.set_column(1, 1, 15)
|
||||
sheet.set_column(2, 2, 15)
|
||||
|
||||
sheet.write(row_pos, 0, _('Name'), format_header)
|
||||
if data['form']['enable_filter']:
|
||||
sheet.write(row_pos, 1, data['form']['label_filter'], format_header)
|
||||
sheet.write(row_pos, 2, _('Balance'), format_header)
|
||||
else:
|
||||
sheet.write(row_pos, 1, _('Balance'), format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
row_pos += 1
|
||||
row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = line_header_string
|
||||
tmp_style_num = line_header
|
||||
else:
|
||||
tmp_style_str = line_header_string_bold
|
||||
tmp_style_num = line_header_bold
|
||||
sheet.write(row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
if data['form']['enable_filter']:
|
||||
sheet.write(row_pos, 1, float(a.get('balance_cmp')), tmp_style_num)
|
||||
sheet.write(row_pos, 2, float(a.get('balance')), tmp_style_num)
|
||||
else:
|
||||
sheet.write(row_pos, 1, float(a.get('balance')), tmp_style_num)
|
||||
if data.get('initial_balance') or data.get('current_balance') or data.get('ending_balance'):
|
||||
row_pos += 2
|
||||
sheet.merge_range(row_pos, 1, row_pos, 2, 'Initial Cash Balance', tmp_style_num)
|
||||
sheet.write(row_pos, 3, float(data.get('initial_balance')), tmp_style_num)
|
||||
row_pos += 1
|
||||
sheet.merge_range(row_pos, 1, row_pos, 2, 'Current Cash Balance', tmp_style_num)
|
||||
sheet.write(row_pos, 3, float(data.get('current_balance')), tmp_style_num)
|
||||
row_pos += 1
|
||||
sheet.merge_range(row_pos, 1, row_pos, 2, 'Net Cash Balance', tmp_style_num)
|
||||
sheet.write(row_pos, 3, float(data.get('ending_balance')), tmp_style_num)
|
||||
|
||||
# Close and return
|
||||
#################################################################
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
def action_view(self):
|
||||
res = {
|
||||
'type': 'ir.actions.client',
|
||||
'name': 'FR View',
|
||||
'tag': 'dynamic.fr',
|
||||
'context': {'wizard_id': self.id,
|
||||
'account_report_id': self.account_report_id.id}
|
||||
}
|
||||
return res
|
||||
@@ -0,0 +1,154 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Financial report common form view -->
|
||||
<record id="ins_financial_report_wiz_modified" model="ir.ui.view">
|
||||
<field name="name">ins.financial.report.extended.wiz</field>
|
||||
<field name="model">ins.financial.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<group string="Date">
|
||||
<field name="date_range"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="financial_year" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="target_move" widget="radio"/>
|
||||
<field name="view_format" widget="radio" invisible="1"/>
|
||||
<field name="enable_filter" invisible="1"/>
|
||||
<field name="debit_credit"/>
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="account_report_id" required="1" force_save="1"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group>
|
||||
<field name="enable_filter"/>
|
||||
<field name="date_from_cmp" attrs="{'invisible':[('enable_filter','=',False)]}"/>
|
||||
|
||||
</group>
|
||||
<group>
|
||||
<field name="label_filter" attrs="{'required':[('enable_filter','=',True)],
|
||||
'invisible':[('enable_filter','=',False)]}"/>
|
||||
<field name="filter_cmp" invisible="1"/>
|
||||
<field name="date_to_cmp" attrs="{'invisible':[('enable_filter','=',False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="journal_ids" widget="many2many_tags"
|
||||
domain="['|',('company_id','in',company_ids),('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="analytic_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags"
|
||||
domain="['|',('company_id','in',company_ids),('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</group>
|
||||
|
||||
<footer>
|
||||
<button string="PDF" name="action_pdf" type="object"
|
||||
class="btn-primary"/>
|
||||
<button string="XLSX" name="action_xlsx" type="object"
|
||||
class="btn-primary"/>
|
||||
<button string="VIEW" name="action_view" type="object"
|
||||
class="btn-primary"/>
|
||||
<button string="Discard" class="btn-secondary"
|
||||
special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Action for profit and loss -->
|
||||
<record id="action_ins_profit_and_loss_report" model="ir.actions.act_window">
|
||||
<field name="name">Profit and Loss</field>
|
||||
<field name="res_model">ins.financial.report</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_financial_report_wiz_modified"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context"
|
||||
eval="{'default_account_report_id':ref('account_dynamic_reports.ins_account_financial_report_profitandloss0')}"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_pandl" sequence="50" action="action_ins_profit_and_loss_report"
|
||||
name="Profit and Loss" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<!-- Action for balance sheet -->
|
||||
<record id="action_ins_balance_sheet_report" model="ir.actions.act_window">
|
||||
<field name="name">Balance Sheet</field>
|
||||
<field name="res_model">ins.financial.report</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_financial_report_wiz_modified"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context"
|
||||
eval="{'default_account_report_id':ref('account_dynamic_reports.ins_account_financial_report_balancesheet0')}"/>
|
||||
</record>
|
||||
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_financial_report_xlsx"-->
|
||||
<!-- model="ins.financial.report"-->
|
||||
<!-- string="Financial Report"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="account_dynamic_reports.ins_financial_report_xlsx"-->
|
||||
<!-- file="account_dynamic_reports.ins_financial_report_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'FinancialReport - %s/%s' % (object.date_from,object.date_to)"-->
|
||||
<!-- />-->
|
||||
|
||||
<menuitem id="account_report_bl" sequence="60" action="action_ins_balance_sheet_report"
|
||||
name="Balance Sheet" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<!-- Action for Cash Flow -->
|
||||
<record id="action_ins_cash_flow_report" model="ir.actions.act_window">
|
||||
<field name="name">Cash Flow Report</field>
|
||||
<field name="res_model">ins.financial.report</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_financial_report_wiz_modified"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context"
|
||||
eval="{'default_account_report_id':ref('account_dynamic_reports.ins_account_financial_report_cash_flow0')}"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_cashflow" sequence="70" action="action_ins_cash_flow_report"
|
||||
name="Cash Flow Report" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<record id="action_dynamic_allinone_pl_report" model="ir.actions.client">
|
||||
<field name="name">Profit and Loss</field>
|
||||
<field name="tag">dynamic.fr</field>
|
||||
<field name="context"
|
||||
eval="{'account_report_id':ref('account_dynamic_reports.ins_account_financial_report_profitandloss0')}"/>
|
||||
</record>
|
||||
|
||||
<record id="action_dynamic_allinone_bs_report" model="ir.actions.client">
|
||||
<field name="name">Balance Sheet</field>
|
||||
<field name="tag">dynamic.fr</field>
|
||||
<field name="context"
|
||||
eval="{'account_report_id':ref('account_dynamic_reports.ins_account_financial_report_balancesheet0')}"/>
|
||||
</record>
|
||||
|
||||
<record id="action_dynamic_allinone_cf_report" model="ir.actions.client">
|
||||
<field name="name">Cash Flow</field>
|
||||
<field name="tag">dynamic.fr</field>
|
||||
<field name="context"
|
||||
eval="{'account_report_id':ref('account_dynamic_reports.ins_account_financial_report_cash_flow0')}"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_pl_wiz" sequence="60" action="action_dynamic_allinone_pl_report"
|
||||
name="Profit and Loss" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_bs_wiz" sequence="70" action="action_dynamic_allinone_bs_report"
|
||||
name="Balance Sheet" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_cf_wiz" sequence="80" action="action_dynamic_allinone_cf_report"
|
||||
name="Cash Flow" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,105 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="ins_general_ledger_wizard" model="ir.ui.view">
|
||||
<field name="name">ins.general.ledger.view</field>
|
||||
<field name="model">ins.general.ledger</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group col="4">
|
||||
<field name="date_range"/>
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="target_moves" widget="radio"/>
|
||||
<field name="sort_accounts_by" widget="radio"/>
|
||||
<field name="display_accounts" widget="radio"/>
|
||||
<field name="initial_balance"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="include_details" help="It will show detailed lines in reports"/>
|
||||
<field name="financial_year" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="account_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="account_tag_ids" widget="many2many_tags"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="journal_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="partner_ids" widget="many2many_tags"
|
||||
domain="[('parent_id','=', False),'|',('company_id','in',company_ids),('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="analytic_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags"
|
||||
domain="['|',('company_id','in',company_ids),('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_pdf" type="object" string="PDF" class="oe_highlight"/>
|
||||
<button name="action_xlsx" type="object" string="XLSX" class="oe_highlight"/>
|
||||
<button name="action_view" type="object" string="VIEW" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_general_ledger_wizard" model="ir.actions.act_window">
|
||||
<field name="name">General Ledger</field>
|
||||
<field name="res_model">ins.general.ledger</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_general_ledger_wizard"/>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="account_dynamic_reports.model_ins_general_ledger" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_general_ledger_xlsx"-->
|
||||
<!-- model="ins.general.ledger"-->
|
||||
<!-- string="General Ledger"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="account_dynamic_reports.ins_general_ledger_xlsx"-->
|
||||
<!-- file="account_dynamic_reports.ins_general_ledger_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'GeneralLedger - %s/%s' % (object.date_from,object.date_to)"-->
|
||||
<!-- />-->
|
||||
|
||||
<record id="action_dynamic_allinone_gl_report" model="ir.actions.client">
|
||||
<field name="name">General Ledger Report</field>
|
||||
<field name="tag">dynamic.gl</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_gl" sequence="10" action="action_ins_general_ledger_wizard"
|
||||
name="General Ledger" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_gl_wiz" sequence="10" action="action_dynamic_allinone_gl_report"
|
||||
name="General Ledger" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
<!-- Xlsx Action -->
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_general_ledger_xlsx"-->
|
||||
<!-- model="ins.general.ledger"-->
|
||||
<!-- string="General Ledger"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="dynamic_xlsx.ins_general_ledger_xlsx"-->
|
||||
<!-- file="dynamic_xlsx.ins_general_ledger_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'GeneralLedger - %s/%s' % (object.date_from,object.date_to)"-->
|
||||
<!-- />-->
|
||||
|
||||
<record id="action_ins_general_ledger_xlsx" model="ir.actions.act_window">
|
||||
<field name="name">Excel</field>
|
||||
<field name="res_model">ins.general.ledger</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,883 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from datetime import datetime, timedelta, date
|
||||
import calendar
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
import json
|
||||
import io
|
||||
from odoo.tools import date_utils
|
||||
|
||||
try:
|
||||
from odoo.tools.misc import xlsxwriter
|
||||
except ImportError:
|
||||
import xlsxwriter
|
||||
|
||||
FETCH_RANGE = 2500
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsPartnerAgeing(models.TransientModel):
|
||||
_name = "ins.partner.ageing"
|
||||
|
||||
def _get_report_company(self):
|
||||
self.ensure_one()
|
||||
return self.company_id or self.env.company
|
||||
|
||||
def _get_report_companies(self):
|
||||
self.ensure_one()
|
||||
return self.company_ids or self.company_id or self.env.company
|
||||
|
||||
def _get_report_company_ids(self):
|
||||
self.ensure_one()
|
||||
return self._get_report_companies().ids or [self.env.company.id]
|
||||
|
||||
def _get_company_display_name(self):
|
||||
self.ensure_one()
|
||||
return ', '.join(self._get_report_companies().mapped('name'))
|
||||
|
||||
def _get_available_companies(self):
|
||||
return self.env.user.company_ids
|
||||
|
||||
@api.onchange('partner_type', 'company_id', 'company_ids')
|
||||
def onchange_partner_type(self):
|
||||
self.partner_ids = [(5,)]
|
||||
company_ids = self.company_ids.ids or (self.company_id and [self.company_id.id]) or [self.env.company.id]
|
||||
if self.company_ids:
|
||||
self.company_id = self.company_ids[0]
|
||||
elif self.company_id:
|
||||
self.company_ids = [(6, 0, [self.company_id.id])]
|
||||
if self.partner_type:
|
||||
if self.partner_type == 'customer':
|
||||
partner_company_domain = [('parent_id', '=', False),
|
||||
('customer_rank', '>', 0),
|
||||
'|',
|
||||
('company_id', 'in', company_ids),
|
||||
('company_id', '=', False)]
|
||||
|
||||
self.partner_ids |= self.env['res.partner'].search(partner_company_domain)
|
||||
if self.partner_type == 'supplier':
|
||||
partner_company_domain = [('parent_id', '=', False),
|
||||
('supplier_rank', '>', 0),
|
||||
'|',
|
||||
('company_id', 'in', company_ids),
|
||||
('company_id', '=', False)]
|
||||
|
||||
self.partner_ids |= self.env['res.partner'].search(partner_company_domain)
|
||||
|
||||
def name_get(self):
|
||||
res = []
|
||||
for record in self:
|
||||
res.append((record.id, 'Ageing'))
|
||||
return res
|
||||
|
||||
as_on_date = fields.Date(string='As on date', required=True, default=fields.Date.today())
|
||||
bucket_1 = fields.Integer(string='Bucket 1', required=True, default=lambda self:self.env.company.bucket_1)
|
||||
bucket_2 = fields.Integer(string='Bucket 2', required=True, default=lambda self:self.env.company.bucket_2)
|
||||
bucket_3 = fields.Integer(string='Bucket 3', required=True, default=lambda self:self.env.company.bucket_3)
|
||||
bucket_4 = fields.Integer(string='Bucket 4', required=True, default=lambda self:self.env.company.bucket_4)
|
||||
include_details = fields.Boolean(string='Include Details', default=True)
|
||||
type = fields.Selection([('receivable','Receivable Accounts Only'),
|
||||
('payable','Payable Accounts Only')], string='Type')
|
||||
partner_type = fields.Selection([('customer', 'Customer Only'),
|
||||
('supplier', 'Supplier Only')], string='Partner Type')
|
||||
|
||||
partner_ids = fields.Many2many(
|
||||
'res.partner', required=False
|
||||
)
|
||||
partner_category_ids = fields.Many2many(
|
||||
'res.partner.category', string='Partner Tag',
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company',
|
||||
default=lambda self: self.env.company
|
||||
)
|
||||
company_ids = fields.Many2many(
|
||||
'res.company',
|
||||
'ins_partner_ageing_res_company_rel',
|
||||
'ins_partner_ageing_id',
|
||||
'res_company_id',
|
||||
string='Companies',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
|
||||
def write(self, vals):
|
||||
if not vals.get('partner_ids'):
|
||||
vals.update({
|
||||
'partner_ids': [(5, 0, 0)]
|
||||
})
|
||||
|
||||
if vals.get('partner_category_ids'):
|
||||
vals.update({'partner_category_ids': vals.get('partner_category_ids')})
|
||||
if vals.get('partner_category_ids') == []:
|
||||
vals.update({'partner_category_ids': [(5,)]})
|
||||
|
||||
if vals.get('company_ids'):
|
||||
company_ids = vals.get('company_ids')
|
||||
if isinstance(company_ids, list) and company_ids and isinstance(company_ids[0], int):
|
||||
vals.update({'company_ids': [(6, 0, company_ids)], 'company_id': company_ids[0]})
|
||||
if vals.get('company_ids') == []:
|
||||
vals.update({'company_ids': [(5,)]})
|
||||
if vals.get('company_id') and not vals.get('company_ids'):
|
||||
vals.update({'company_ids': [(6, 0, [vals.get('company_id')])]})
|
||||
|
||||
ret = super(InsPartnerAgeing, self).write(vals)
|
||||
return ret
|
||||
|
||||
def validate_data(self):
|
||||
if not (self.bucket_1 < self.bucket_2 and self.bucket_2 < self.bucket_3 and self.bucket_3 < self.bucket_4):
|
||||
raise ValidationError(_('"Bucket order must be ascending"'))
|
||||
return True
|
||||
|
||||
def get_filters(self, default_filters={}):
|
||||
|
||||
company = self._get_report_company()
|
||||
company_ids = self._get_report_company_ids()
|
||||
partner_company_domain = [('parent_id','=', False),
|
||||
'|',
|
||||
('customer_rank', '>', 0),
|
||||
('supplier_rank', '>', 0),
|
||||
'|',
|
||||
('company_id', 'in', company_ids),
|
||||
('company_id', '=', False)]
|
||||
companies = self._get_available_companies()
|
||||
|
||||
partners = self.partner_ids if self.partner_ids else self.env['res.partner'].search(partner_company_domain)
|
||||
categories = self.partner_category_ids if self.partner_category_ids else self.env['res.partner.category'].search([])
|
||||
|
||||
filter_dict = {
|
||||
'partner_ids': self.partner_ids.ids,
|
||||
'partner_category_ids': self.partner_category_ids.ids,
|
||||
'company_ids': company_ids,
|
||||
'company_id': self.company_id and self.company_id.id or False,
|
||||
'as_on_date': self.as_on_date,
|
||||
'type': self.type,
|
||||
'partner_type': self.partner_type,
|
||||
'bucket_1': self.bucket_1,
|
||||
'bucket_2': self.bucket_2,
|
||||
'bucket_3': self.bucket_3,
|
||||
'bucket_4': self.bucket_4,
|
||||
'include_details': self.include_details,
|
||||
|
||||
'partners_list': [(p.id, p.name) for p in partners],
|
||||
'category_list': [(c.id, c.name) for c in categories],
|
||||
'company_name': self._get_company_display_name(),
|
||||
'companies_list': [(c.id, c.name) for c in companies],
|
||||
}
|
||||
filter_dict.update(default_filters)
|
||||
return filter_dict
|
||||
|
||||
def process_filters(self):
|
||||
''' To show on report headers'''
|
||||
|
||||
data = self.get_filters(default_filters={})
|
||||
|
||||
filters = {}
|
||||
|
||||
filters['bucket_1'] = data.get('bucket_1')
|
||||
filters['bucket_2'] = data.get('bucket_2')
|
||||
filters['bucket_3'] = data.get('bucket_3')
|
||||
filters['bucket_4'] = data.get('bucket_4')
|
||||
|
||||
if data.get('partner_ids', []):
|
||||
filters['partners'] = self.env['res.partner'].browse(data.get('partner_ids', [])).mapped('name')
|
||||
else:
|
||||
filters['partners'] = ['All']
|
||||
|
||||
if data.get('as_on_date', False):
|
||||
filters['as_on_date'] = data.get('as_on_date')
|
||||
|
||||
if data.get('company_id'):
|
||||
filters['company_id'] = data.get('company_id')
|
||||
else:
|
||||
filters['company_id'] = ''
|
||||
filters['company_ids'] = data.get('company_ids', [])
|
||||
|
||||
if data.get('type'):
|
||||
filters['type'] = data.get('type')
|
||||
|
||||
if data.get('partner_type'):
|
||||
filters['partner_type'] = data.get('partner_type')
|
||||
|
||||
if data.get('partner_category_ids', []):
|
||||
filters['categories'] = self.env['res.partner.category'].browse(data.get('partner_category_ids', [])).mapped('name')
|
||||
else:
|
||||
filters['categories'] = ['All']
|
||||
|
||||
if data.get('include_details'):
|
||||
filters['include_details'] = True
|
||||
else:
|
||||
filters['include_details'] = False
|
||||
|
||||
filters['partners_list'] = data.get('partners_list')
|
||||
filters['category_list'] = data.get('category_list')
|
||||
filters['company_name'] = data.get('company_name')
|
||||
filters['companies_list'] = data.get('companies_list')
|
||||
|
||||
return filters
|
||||
|
||||
def prepare_bucket_list(self):
|
||||
periods = {}
|
||||
date_from = self.as_on_date
|
||||
date_from = fields.Date.from_string(date_from)
|
||||
|
||||
lang = self.env.user.lang
|
||||
language_id = self.env['res.lang'].search([('code', '=', lang)])[0]
|
||||
|
||||
bucket_list = [self.bucket_1, self.bucket_2, self.bucket_3, self.bucket_4]
|
||||
|
||||
start = False
|
||||
stop = date_from
|
||||
name = 'Not Due'
|
||||
periods[0] = {
|
||||
'bucket': 'As on',
|
||||
'name': name,
|
||||
'start': '',
|
||||
'stop': stop.strftime('%Y-%m-%d'),
|
||||
}
|
||||
|
||||
stop = date_from
|
||||
final_date = False
|
||||
for i in range(len(bucket_list)):
|
||||
start = stop - relativedelta(days=1)
|
||||
stop = start - relativedelta(days=bucket_list[i])
|
||||
name = '0 - ' + str(bucket_list[0]) if i==0 else str(str(bucket_list[i-1] + 1)) + ' - ' + str(bucket_list[i])
|
||||
final_date = stop
|
||||
periods[i+1] = {
|
||||
'bucket': bucket_list[i],
|
||||
'name': name,
|
||||
'start': start.strftime('%Y-%m-%d'),
|
||||
'stop': stop.strftime('%Y-%m-%d'),
|
||||
}
|
||||
|
||||
start = final_date -relativedelta(days=1)
|
||||
stop = ''
|
||||
name = str(self.bucket_4) + ' +'
|
||||
|
||||
periods[len(bucket_list) + 1] = {
|
||||
'bucket': 'Above',
|
||||
'name': name,
|
||||
'start': start.strftime('%Y-%m-%d'),
|
||||
'stop': '',
|
||||
}
|
||||
return periods
|
||||
|
||||
def process_detailed_data(self, offset=0, partner=0, fetch_range=FETCH_RANGE):
|
||||
'''
|
||||
|
||||
It is used for showing detailed move lines as sub lines. It is defered loading compatable
|
||||
:param offset: It is nothing but page numbers. Multiply with fetch_range to get final range
|
||||
:param partner: Integer - Partner
|
||||
:param fetch_range: Global Variable. Can be altered from calling model
|
||||
:return: count(int-Total rows without offset), offset(integer), move_lines(list of dict)
|
||||
'''
|
||||
as_on_date = self.as_on_date
|
||||
period_dict = self.prepare_bucket_list()
|
||||
period_list = [period_dict[a]['name'] for a in period_dict]
|
||||
company_ids = self._get_report_company_ids()
|
||||
|
||||
type = ('receivable','payable')
|
||||
if self.type:
|
||||
type = tuple([self.type,'none'])
|
||||
|
||||
offset = offset * fetch_range
|
||||
count = 0
|
||||
|
||||
if partner:
|
||||
|
||||
|
||||
sql = """
|
||||
SELECT COUNT(*)
|
||||
FROM
|
||||
account_move_line AS l
|
||||
LEFT JOIN
|
||||
account_move AS m ON m.id = l.move_id
|
||||
LEFT JOIN
|
||||
account_account AS a ON a.id = l.account_id
|
||||
LEFT JOIN
|
||||
account_account_type AS ty ON a.user_type_id = ty.id
|
||||
LEFT JOIN
|
||||
account_journal AS j ON l.journal_id = j.id
|
||||
WHERE
|
||||
l.balance <> 0
|
||||
AND m.state = 'posted'
|
||||
AND ty.type IN %s
|
||||
AND l.partner_id = %s
|
||||
AND l.date <= '%s'
|
||||
AND l.company_id IN %s
|
||||
""" % (type, partner, as_on_date, tuple(company_ids) + tuple([0]))
|
||||
self.env.cr.execute(sql)
|
||||
count = self.env.cr.fetchone()[0]
|
||||
|
||||
SELECT = """SELECT m.name AS move_name,
|
||||
m.id AS move_id,
|
||||
l.date AS date,
|
||||
l.date_maturity AS date_maturity,
|
||||
j.name AS journal_name,
|
||||
cc.id AS company_currency_id,
|
||||
a.name AS account_name, """
|
||||
|
||||
for period in period_dict:
|
||||
if period_dict[period].get('start') and period_dict[period].get('stop'):
|
||||
SELECT += """ CASE
|
||||
WHEN
|
||||
COALESCE(l.date_maturity,l.date) >= '%s' AND
|
||||
COALESCE(l.date_maturity,l.date) <= '%s'
|
||||
THEN
|
||||
sum(l.balance) +
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE credit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
) -
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE debit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
)
|
||||
ELSE
|
||||
0
|
||||
END AS %s,"""%(period_dict[period].get('stop'),
|
||||
period_dict[period].get('start'),
|
||||
as_on_date,
|
||||
as_on_date,
|
||||
'range_'+str(period),
|
||||
)
|
||||
elif not period_dict[period].get('start'):
|
||||
SELECT += """ CASE
|
||||
WHEN
|
||||
COALESCE(l.date_maturity,l.date) >= '%s'
|
||||
THEN
|
||||
sum(
|
||||
l.balance
|
||||
) +
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE credit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
) -
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE debit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
)
|
||||
ELSE
|
||||
0
|
||||
END AS %s,"""%(period_dict[period].get('stop'), as_on_date, as_on_date, 'range_'+str(period))
|
||||
else:
|
||||
SELECT += """ CASE
|
||||
WHEN
|
||||
COALESCE(l.date_maturity,l.date) <= '%s'
|
||||
THEN
|
||||
sum(
|
||||
l.balance
|
||||
) +
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE credit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
) -
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE debit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
)
|
||||
ELSE
|
||||
0
|
||||
END AS %s """%(period_dict[period].get('start'), as_on_date, as_on_date ,'range_'+str(period))
|
||||
|
||||
sql = """
|
||||
FROM
|
||||
account_move_line AS l
|
||||
LEFT JOIN
|
||||
account_move AS m ON m.id = l.move_id
|
||||
LEFT JOIN
|
||||
account_account AS a ON a.id = l.account_id
|
||||
LEFT JOIN
|
||||
account_account_type AS ty ON a.user_type_id = ty.id
|
||||
LEFT JOIN
|
||||
account_journal AS j ON l.journal_id = j.id
|
||||
LEFT JOIN
|
||||
res_currency AS cc ON l.company_currency_id = cc.id
|
||||
WHERE
|
||||
l.balance <> 0
|
||||
AND m.state = 'posted'
|
||||
AND ty.type IN %s
|
||||
AND l.partner_id = %s
|
||||
AND l.date <= '%s'
|
||||
AND l.company_id IN %s
|
||||
GROUP BY
|
||||
l.date, l.date_maturity, m.id, m.name, j.name, a.name, cc.id
|
||||
OFFSET %s ROWS
|
||||
FETCH FIRST %s ROWS ONLY
|
||||
"""%(type, partner, as_on_date, tuple(company_ids) + tuple([0]), offset, fetch_range)
|
||||
self.env.cr.execute(SELECT + sql)
|
||||
final_list = self.env.cr.dictfetchall() or 0.0
|
||||
move_lines = []
|
||||
range_keys = ['range_%s' % period for period in period_dict]
|
||||
for m in final_list:
|
||||
if any(m.get(range_key) for range_key in range_keys):
|
||||
move_lines.append(m)
|
||||
|
||||
if move_lines:
|
||||
return count, offset, move_lines, period_list
|
||||
else:
|
||||
return 0, 0, [], []
|
||||
|
||||
def process_data(self):
|
||||
''' Query Start Here
|
||||
['partner_id':
|
||||
{'0-30':0.0,
|
||||
'30-60':0.0,
|
||||
'60-90':0.0,
|
||||
'90-120':0.0,
|
||||
'>120':0.0,
|
||||
'as_on_date_amount': 0.0,
|
||||
'total': 0.0}]
|
||||
1. Prepare bucket range list from bucket values
|
||||
2. Fetch partner_ids and loop through bucket range for values
|
||||
'''
|
||||
period_dict = self.prepare_bucket_list()
|
||||
|
||||
company = self._get_report_company()
|
||||
company_ids = self._get_report_company_ids()
|
||||
domain = ['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]
|
||||
if self.partner_type == 'customer':
|
||||
domain.append(('customer_rank','>',0))
|
||||
if self.partner_type == 'supplier':
|
||||
domain.append(('supplier_rank','>',0))
|
||||
|
||||
if self.partner_category_ids:
|
||||
domain.append(('category_id','in',self.partner_category_ids.ids))
|
||||
|
||||
partner_ids = self.partner_ids or self.env['res.partner'].search(domain)
|
||||
as_on_date = self.as_on_date
|
||||
company_currency_id = company.currency_id.id
|
||||
|
||||
type = ('receivable', 'payable')
|
||||
if self.type:
|
||||
type = tuple([self.type,'none'])
|
||||
|
||||
partner_dict = {}
|
||||
for partner in partner_ids:
|
||||
partner_dict.update({partner.id:{}})
|
||||
|
||||
partner_dict.update({'Total': {}})
|
||||
for period in period_dict:
|
||||
partner_dict['Total'].update({period_dict[period]['name']: 0.0})
|
||||
partner_dict['Total'].update({'total': 0.0, 'partner_name': 'ZZZZZZZZZ'})
|
||||
partner_dict['Total'].update({'company_currency_id': company_currency_id})
|
||||
|
||||
for partner in partner_ids:
|
||||
partner_dict[partner.id].update({'partner_name':partner.name})
|
||||
total_balance = 0.0
|
||||
|
||||
sql = """
|
||||
SELECT
|
||||
COUNT(*) AS count
|
||||
FROM
|
||||
account_move_line AS l
|
||||
LEFT JOIN
|
||||
account_move AS m ON m.id = l.move_id
|
||||
LEFT JOIN
|
||||
account_account AS a ON a.id = l.account_id
|
||||
LEFT JOIN
|
||||
account_account_type AS ty ON a.user_type_id = ty.id
|
||||
WHERE
|
||||
l.balance <> 0
|
||||
AND m.state = 'posted'
|
||||
AND ty.type IN %s
|
||||
AND l.partner_id = %s
|
||||
AND l.date <= '%s'
|
||||
AND l.company_id IN %s
|
||||
"""%(type, partner.id, as_on_date, tuple(company_ids) + tuple([0]))
|
||||
self.env.cr.execute(sql)
|
||||
fetch_dict = self.env.cr.dictfetchone() or 0.0
|
||||
count = fetch_dict.get('count') or 0.0
|
||||
|
||||
if count:
|
||||
for period in period_dict:
|
||||
|
||||
where = " AND l.date <= '%s' AND l.partner_id = %s AND COALESCE(l.date_maturity,l.date) "%(as_on_date, partner.id)
|
||||
if period_dict[period].get('start') and period_dict[period].get('stop'):
|
||||
where += " BETWEEN '%s' AND '%s'" % (period_dict[period].get('stop'), period_dict[period].get('start'))
|
||||
elif not period_dict[period].get('start'): # ie just
|
||||
where += " >= '%s'" % (period_dict[period].get('stop'))
|
||||
else:
|
||||
where += " <= '%s'" % (period_dict[period].get('start'))
|
||||
|
||||
sql = """
|
||||
SELECT
|
||||
sum(
|
||||
l.balance
|
||||
) AS balance,
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE credit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
) AS sum_debit,
|
||||
sum(
|
||||
COALESCE(
|
||||
(SELECT
|
||||
SUM(amount)
|
||||
FROM account_partial_reconcile
|
||||
WHERE debit_move_id = l.id AND max_date <= '%s'), 0
|
||||
)
|
||||
) AS sum_credit
|
||||
FROM
|
||||
account_move_line AS l
|
||||
LEFT JOIN
|
||||
account_move AS m ON m.id = l.move_id
|
||||
LEFT JOIN
|
||||
account_account AS a ON a.id = l.account_id
|
||||
LEFT JOIN
|
||||
account_account_type AS ty ON a.user_type_id = ty.id
|
||||
WHERE
|
||||
l.balance <> 0
|
||||
AND m.state = 'posted'
|
||||
AND ty.type IN %s
|
||||
AND l.company_id IN %s
|
||||
"""%(as_on_date, as_on_date, type, tuple(company_ids) + tuple([0]))
|
||||
amount = 0.0
|
||||
self.env.cr.execute(sql + where)
|
||||
fetch_dict = self.env.cr.dictfetchall() or 0.0
|
||||
|
||||
if not fetch_dict[0].get('balance'):
|
||||
amount = 0.0
|
||||
else:
|
||||
amount = fetch_dict[0]['balance'] + fetch_dict[0]['sum_debit'] - fetch_dict[0]['sum_credit']
|
||||
total_balance += amount
|
||||
|
||||
partner_dict[partner.id].update({period_dict[period]['name']:amount})
|
||||
partner_dict['Total'][period_dict[period]['name']] += amount
|
||||
partner_dict[partner.id].update({'count': count})
|
||||
partner_dict[partner.id].update({'pages': self.get_page_list(count)})
|
||||
partner_dict[partner.id].update({'single_page': True if count <= FETCH_RANGE else False})
|
||||
partner_dict[partner.id].update({'total': total_balance})
|
||||
partner_dict['Total']['total'] += total_balance
|
||||
partner_dict[partner.id].update({'company_currency_id': company_currency_id})
|
||||
partner_dict['Total'].update({'company_currency_id': company_currency_id})
|
||||
else:
|
||||
partner_dict.pop(partner.id, None)
|
||||
return period_dict, partner_dict
|
||||
|
||||
def get_page_list(self, total_count):
|
||||
'''
|
||||
Helper function to get list of pages from total_count
|
||||
:param total_count: integer
|
||||
:return: list(pages) eg. [1,2,3,4,5,6,7 ....]
|
||||
'''
|
||||
page_count = int(total_count / FETCH_RANGE)
|
||||
if total_count % FETCH_RANGE:
|
||||
page_count += 1
|
||||
return [i+1 for i in range(0, int(page_count))] or []
|
||||
|
||||
def get_report_datas(self, default_filters={}):
|
||||
'''
|
||||
Main method for pdf, xlsx and js calls
|
||||
:param default_filters: Use this while calling from other methods. Just a dict
|
||||
:return: All the datas for GL
|
||||
'''
|
||||
if self.validate_data():
|
||||
filters = self.process_filters()
|
||||
period_dict, ageing_lines = self.process_data()
|
||||
period_list = [period_dict[a]['name'] for a in period_dict]
|
||||
return filters, ageing_lines, period_dict, period_list
|
||||
|
||||
def action_pdf(self):
|
||||
filters, ageing_lines, period_dict, period_list = self.get_report_datas()
|
||||
return self.env.ref(
|
||||
'account_dynamic_reports'
|
||||
'.action_print_partner_ageing').with_context(landscape=True).report_action(
|
||||
self, data={'Ageing_data': ageing_lines,
|
||||
'Filters': filters,
|
||||
'Period_Dict': period_dict,
|
||||
'Period_List': period_list
|
||||
})
|
||||
|
||||
def action_xlsx(self):
|
||||
''' Button function for Xlsx '''
|
||||
|
||||
data = self.read()
|
||||
as_on_date = fields.Date.from_string(self.as_on_date).strftime(
|
||||
self.env['res.lang'].search([('code', '=', self.env.user.lang)])[0].date_format)
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.report',
|
||||
'data': {'model': 'ins.partner.ageing',
|
||||
'options': json.dumps(data[0], default=date_utils.json_default),
|
||||
'output_format': 'xlsx',
|
||||
'report_name': 'Ageing as On - %s' % (as_on_date),
|
||||
},
|
||||
'report_type': 'xlsx'
|
||||
}
|
||||
|
||||
def get_xlsx_report(self, data, response):
|
||||
|
||||
# Initialize
|
||||
#############################################################
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet('Partner Ageing')
|
||||
sheet.set_zoom(95)
|
||||
sheet_2 = workbook.add_worksheet('Filters')
|
||||
sheet_2.protect()
|
||||
|
||||
# Get record and data
|
||||
record = self.env['ins.partner.ageing'].browse(data.get('id', [])) or False
|
||||
filter, ageing_lines, period_dict, period_list = record.get_report_datas()
|
||||
|
||||
# Formats
|
||||
############################################################
|
||||
sheet.set_column(0, 0, 15)
|
||||
sheet.set_column(1, 1, 12)
|
||||
sheet.set_column(2, 3, 15)
|
||||
sheet.set_column(3, 3, 15)
|
||||
for column in range(4, 11):
|
||||
sheet.set_column(column, column, 15)
|
||||
|
||||
sheet_2.set_column(0, 0, 35)
|
||||
sheet_2.set_column(1, 1, 25)
|
||||
sheet_2.set_column(2, 2, 25)
|
||||
sheet_2.set_column(3, 3, 25)
|
||||
sheet_2.set_column(4, 4, 25)
|
||||
sheet_2.set_column(5, 5, 25)
|
||||
sheet_2.set_column(6, 6, 25)
|
||||
|
||||
sheet.freeze_panes(4, 0)
|
||||
sheet.screen_gridlines = False
|
||||
sheet_2.screen_gridlines = False
|
||||
sheet_2.protect()
|
||||
sheet.set_zoom(75)
|
||||
|
||||
format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 14,
|
||||
'font': 'Arial'
|
||||
})
|
||||
format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
# 'border': True
|
||||
})
|
||||
format_header_period = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'left': True,
|
||||
'right': True,
|
||||
# 'border': True
|
||||
})
|
||||
content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
})
|
||||
content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
# 'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
line_header = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
line_header_total = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'border': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
line_header_period = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
line_header_light_period = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
})
|
||||
|
||||
lang = self.env.user.lang
|
||||
lang_id = self.env['res.lang'].search([('code', '=', lang)])[0]
|
||||
currency_id = self.env.user.company_id.currency_id
|
||||
line_header.num_format = currency_id.excel_format
|
||||
line_header_light.num_format = currency_id.excel_format
|
||||
line_header_light_period.num_format = currency_id.excel_format
|
||||
line_header_total.num_format = currency_id.excel_format
|
||||
line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
# Write data
|
||||
################################################################
|
||||
row_pos_2 = 0
|
||||
row_pos = 0
|
||||
report_last_column = 4 + len(period_list)
|
||||
sheet.merge_range(0, 0, 0, report_last_column, 'Partner Ageing' + ' - ' + filter['company_name'], format_title)
|
||||
|
||||
# Write filters
|
||||
row_pos_2 += 2
|
||||
sheet_2.write(row_pos_2, 0, _('As on Date'), format_header)
|
||||
datestring = fields.Date.from_string(str(filter['as_on_date'])).strftime(lang_id.date_format)
|
||||
sheet_2.write(row_pos_2, 1, datestring or '', content_header_date)
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Partners'), format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
sheet_2.write(row_pos_2, 1, p_list, content_header)
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Partner Tag'), format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('categories')])
|
||||
sheet_2.write(row_pos_2, 1, p_list, content_header)
|
||||
|
||||
# Write Ledger details
|
||||
row_pos += 3
|
||||
if record.include_details:
|
||||
sheet.write(row_pos, 0, _('Entry #'), format_header)
|
||||
sheet.write(row_pos, 1, _('Due Date'), format_header)
|
||||
sheet.write(row_pos, 2, _('Journal'), format_header)
|
||||
sheet.write(row_pos, 3, _('Account'), format_header)
|
||||
else:
|
||||
sheet.merge_range(row_pos, 0, row_pos, 3, _('Partner'),
|
||||
format_header)
|
||||
k = 4
|
||||
for period in period_list:
|
||||
sheet.write(row_pos, k, str(period),
|
||||
format_header_period)
|
||||
k += 1
|
||||
sheet.write(row_pos, k, _('Total'),
|
||||
format_header_period)
|
||||
if ageing_lines:
|
||||
for line in ageing_lines:
|
||||
# Dummy vacant lines
|
||||
row_pos += 1
|
||||
for column in range(4, report_last_column + 1):
|
||||
sheet.write(row_pos, column, '', line_header_light_period)
|
||||
row_pos += 1
|
||||
if line != 'Total':
|
||||
sheet.merge_range(row_pos, 0, row_pos, 3, ageing_lines[line].get('partner_name'), line_header)
|
||||
else:
|
||||
sheet.merge_range(row_pos, 0, row_pos, 3, _('Total'),line_header_total)
|
||||
k = 4
|
||||
for period in period_list:
|
||||
if line != 'Total':
|
||||
sheet.write(row_pos, k, ageing_lines[line][period],line_header)
|
||||
else:
|
||||
sheet.write(row_pos, k, ageing_lines[line][period], line_header_total)
|
||||
k += 1
|
||||
if line != 'Total':
|
||||
sheet.write(row_pos, k, ageing_lines[line]['total'], line_header)
|
||||
else:
|
||||
sheet.write(row_pos, k, ageing_lines[line]['total'], line_header_total)
|
||||
if record.include_details:
|
||||
if line != 'Total':
|
||||
count, offset, sub_lines, period_list = record.process_detailed_data(partner=line, fetch_range=1000000)
|
||||
for sub_line in sub_lines:
|
||||
row_pos += 1
|
||||
sheet.write(row_pos, 0, sub_line.get('move_name') or '',
|
||||
line_header_light)
|
||||
datestring = fields.Date.from_string(str(sub_line.get('date_maturity') or sub_line.get('date'))).strftime(
|
||||
lang_id.date_format)
|
||||
sheet.write(row_pos, 1, datestring, line_header_light_date)
|
||||
sheet.write(row_pos, 2, sub_line.get('journal_name'), line_header_light)
|
||||
sheet.write(row_pos, 3, sub_line.get('account_name') or '', line_header_light)
|
||||
detail_column = 4
|
||||
detail_total = 0.0
|
||||
for period_index in period_dict:
|
||||
amount = float(sub_line.get('range_%s' % period_index) or 0.0)
|
||||
sheet.write(row_pos, detail_column, amount, line_header_light_period)
|
||||
detail_total += amount
|
||||
detail_column += 1
|
||||
sheet.write(row_pos, detail_column, detail_total, line_header_light_period)
|
||||
row_pos += 1
|
||||
k = 4
|
||||
|
||||
# Close and return
|
||||
#################################################################
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
def action_view(self):
|
||||
res = {
|
||||
'type': 'ir.actions.client',
|
||||
'name': 'Ageing View',
|
||||
'tag': 'dynamic.pa',
|
||||
'context': {'wizard_id': self.id}
|
||||
}
|
||||
return res
|
||||
@@ -0,0 +1,75 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="ins_partner_ageing_wizard" model="ir.ui.view">
|
||||
<field name="name">ins.partner.ageing.view</field>
|
||||
<field name="model">ins.partner.ageing</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group col="4">
|
||||
<field name="as_on_date"/>
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="type"/>
|
||||
<field name="partner_type"/>
|
||||
<field name="include_details"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="partner_ids" widget="many2many_tags"
|
||||
domain="[('parent_id','=', False),
|
||||
'|',('company_id','in',company_ids), ('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="partner_category_ids" widget="many2many_tags"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</group>
|
||||
<group col="4">
|
||||
<field name="bucket_1" class="oe_inline" nolabel="1"/>
|
||||
<field name="bucket_2" class="oe_inline" nolabel="1"/>
|
||||
<field name="bucket_3" class="oe_inline" nolabel="1"/>
|
||||
<field name="bucket_4" class="oe_inline" nolabel="1"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_pdf" type="object" string="PDF" class="oe_highlight"/>
|
||||
<button name="action_xlsx" type="object" string="XLSX" class="oe_highlight"/>
|
||||
<button name="action_view" type="object" string="VIEW" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_partner_ageing_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Partner Ageing</field>
|
||||
<field name="res_model">ins.partner.ageing</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_partner_ageing_wizard"/>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="account_dynamic_reports.model_ins_partner_ageing" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_partner_ageing_xlsx"-->
|
||||
<!-- model="ins.partner.ageing"-->
|
||||
<!-- string="Partner Ageing"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="account_dynamic_reports.ins_partner_ageing_xlsx"-->
|
||||
<!-- file="account_dynamic_reports.ins_partner_ageing_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'Ageing - %s' % (object.as_on_date)"-->
|
||||
<!-- />-->
|
||||
|
||||
<record id="action_dynamic_allinone_pa_report" model="ir.actions.client">
|
||||
<field name="name">Partner Ageing Report</field>
|
||||
<field name="tag">dynamic.pa</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_pa" sequence="40" action="action_ins_partner_ageing_wizard"
|
||||
name="Partner Ageing" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_pa_wiz" sequence="40" action="action_dynamic_allinone_pa_report"
|
||||
name="Partner Ageing" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,83 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="ins_partner_ledger_wizard" model="ir.ui.view">
|
||||
<field name="name">ins.partner.ledger.view</field>
|
||||
<field name="model">ins.partner.ledger</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group col="4">
|
||||
<field name="date_range"/>
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="target_moves" widget="radio"/>
|
||||
<field name="reconciled" widget="radio"/>
|
||||
<field name="display_accounts" widget="radio"/>
|
||||
<field name="type" widget="radio"/>
|
||||
<field name="initial_balance"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="include_details" help="It will show detailed lines in reports"/>
|
||||
<field name="balance_less_than_zero"/>
|
||||
<field name="balance_greater_than_zero"/>
|
||||
<field name="financial_year" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="account_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="journal_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="partner_category_ids" widget="many2many_tags"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="partner_ids" widget="many2many_tags"
|
||||
domain="[('parent_id','=', False),
|
||||
'|',('company_id','in',company_ids),('company_id','=',False)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_pdf" type="object" string="PDF" class="oe_highlight"/>
|
||||
<button name="action_xlsx" type="object" string="XLSX" class="oe_highlight"/>
|
||||
<button name="action_view" type="object" string="VIEW" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_partner_ledger_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Partner Ledger</field>
|
||||
<field name="res_model">ins.partner.ledger</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_partner_ledger_wizard"/>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="account_dynamic_reports.model_ins_partner_ledger" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_partner_ledger_xlsx"-->
|
||||
<!-- model="ins.partner.ledger"-->
|
||||
<!-- string="Partner Ledger"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="account_dynamic_reports.ins_partner_ledger_xlsx"-->
|
||||
<!-- file="account_dynamic_reports.ins_partner_ledger_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'PartnerLedger - %s/%s' % (object.date_from,object.date_to)"-->
|
||||
<!-- />-->
|
||||
|
||||
<record id="action_dynamic_allinone_plg_report" model="ir.actions.client">
|
||||
<field name="name">Partner Ledger Report</field>
|
||||
<field name="tag">dynamic.pl</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_plg" sequence="20" action="action_ins_partner_ledger_wizard"
|
||||
name="Partner Ledger" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_plg_wiz" sequence="20" action="action_dynamic_allinone_plg_report"
|
||||
name="Partner Ledger" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,955 @@
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from datetime import datetime, timedelta, date
|
||||
import calendar
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
from operator import itemgetter
|
||||
import json
|
||||
import io
|
||||
from odoo.tools import date_utils
|
||||
|
||||
try:
|
||||
from odoo.tools.misc import xlsxwriter
|
||||
except ImportError:
|
||||
import xlsxwriter
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
|
||||
class InsTrialBalance(models.TransientModel):
|
||||
_name = "ins.trial.balance"
|
||||
|
||||
def _get_report_company(self):
|
||||
self.ensure_one()
|
||||
return self.company_id or self.env.company
|
||||
|
||||
def _get_report_companies(self):
|
||||
self.ensure_one()
|
||||
return self.company_ids or self.company_id or self.env.company
|
||||
|
||||
def _get_report_company_ids(self):
|
||||
self.ensure_one()
|
||||
return self._get_report_companies().ids or [self.env.company.id]
|
||||
|
||||
def _get_company_display_name(self):
|
||||
self.ensure_one()
|
||||
return ', '.join(self._get_report_companies().mapped('name'))
|
||||
|
||||
def _get_available_companies(self):
|
||||
return self.env.user.company_ids
|
||||
|
||||
def _get_journals(self):
|
||||
return self.env['account.journal'].search([])
|
||||
|
||||
@api.onchange('date_range', 'financial_year')
|
||||
def onchange_date_range(self):
|
||||
if self.date_range:
|
||||
date = datetime.today()
|
||||
if self.date_range == 'today':
|
||||
self.date_from = date.strftime("%Y-%m-%d")
|
||||
self.date_to = date.strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_week':
|
||||
day_today = date - timedelta(days=date.weekday())
|
||||
self.date_from = (day_today - timedelta(days=date.weekday())).strftime("%Y-%m-%d")
|
||||
self.date_to = (day_today + timedelta(days=6)).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_month':
|
||||
self.date_from = datetime(date.year, date.month, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, date.month, calendar.mdays[date.month]).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_quarter':
|
||||
if int((date.month - 1) / 3) == 0: # First quarter
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, calendar.mdays[3]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 1: # Second quarter
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, calendar.mdays[6]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 2: # Third quarter
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 9, calendar.mdays[9]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 3: # Fourth quarter
|
||||
self.date_from = datetime(date.year, 10, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, calendar.mdays[12]).strftime("%Y-%m-%d")
|
||||
if self.date_range == 'this_financial_year':
|
||||
if self.financial_year == 'january_december':
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'april_march':
|
||||
if date.month < 4:
|
||||
self.date_from = datetime(date.year - 1, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, 31).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 3, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'july_june':
|
||||
if date.month < 7:
|
||||
self.date_from = datetime(date.year - 1, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, 30).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 6, 30).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(days=1))
|
||||
if self.date_range == 'yesterday':
|
||||
self.date_from = date.strftime("%Y-%m-%d")
|
||||
self.date_to = date.strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(days=7))
|
||||
if self.date_range == 'last_week':
|
||||
day_today = date - timedelta(days=date.weekday())
|
||||
self.date_from = (day_today - timedelta(days=date.weekday())).strftime("%Y-%m-%d")
|
||||
self.date_to = (day_today + timedelta(days=6)).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(months=1))
|
||||
if self.date_range == 'last_month':
|
||||
self.date_from = datetime(date.year, date.month, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, date.month, calendar.mdays[date.month]).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(months=3))
|
||||
if self.date_range == 'last_quarter':
|
||||
if int((date.month - 1) / 3) == 0: # First quarter
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, calendar.mdays[3]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 1: # Second quarter
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, calendar.mdays[6]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 2: # Third quarter
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 9, calendar.mdays[9]).strftime("%Y-%m-%d")
|
||||
if int((date.month - 1) / 3) == 3: # Fourth quarter
|
||||
self.date_from = datetime(date.year, 10, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, calendar.mdays[12]).strftime("%Y-%m-%d")
|
||||
date = (datetime.now() - relativedelta(years=1))
|
||||
if self.date_range == 'last_financial_year':
|
||||
if self.financial_year == 'january_december':
|
||||
self.date_from = datetime(date.year, 1, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 12, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'april_march':
|
||||
if date.month < 4:
|
||||
self.date_from = datetime(date.year - 1, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 3, 31).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 4, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 3, 31).strftime("%Y-%m-%d")
|
||||
if self.financial_year == 'july_june':
|
||||
if date.month < 7:
|
||||
self.date_from = datetime(date.year - 1, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year, 6, 30).strftime("%Y-%m-%d")
|
||||
else:
|
||||
self.date_from = datetime(date.year, 7, 1).strftime("%Y-%m-%d")
|
||||
self.date_to = datetime(date.year + 1, 6, 30).strftime("%Y-%m-%d")
|
||||
|
||||
@api.model
|
||||
def _get_default_date_range(self):
|
||||
return self.env.company.date_range
|
||||
|
||||
def name_get(self):
|
||||
res = []
|
||||
for record in self:
|
||||
res.append((record.id, 'Trial Balance'))
|
||||
return res
|
||||
|
||||
financial_year = fields.Selection(
|
||||
[('april_march', '1 April to 31 March'),
|
||||
('july_june', '1 july to 30 June'),
|
||||
('january_december', '1 Jan to 31 Dec')],
|
||||
string='Financial Year', default=lambda self: self.env.company.financial_year, required=True)
|
||||
|
||||
date_range = fields.Selection(
|
||||
[('today', 'Today'),
|
||||
('this_week', 'This Week'),
|
||||
('this_month', 'This Month'),
|
||||
('this_quarter', 'This Quarter'),
|
||||
('this_financial_year', 'This financial Year'),
|
||||
('yesterday', 'Yesterday'),
|
||||
('last_week', 'Last Week'),
|
||||
('last_month', 'Last Month'),
|
||||
('last_quarter', 'Last Quarter'),
|
||||
('last_financial_year', 'Last Financial Year')],
|
||||
string='Date Range', default=_get_default_date_range
|
||||
)
|
||||
strict_range = fields.Boolean(
|
||||
string='Strict Range',
|
||||
default=lambda self: self.env.company.strict_range
|
||||
)
|
||||
show_hierarchy = fields.Boolean(
|
||||
string='Show hierarchy'
|
||||
)
|
||||
target_moves = fields.Selection(
|
||||
[('all_entries', 'All entries'),
|
||||
('posted_only', 'Posted Only')], string='Target Moves',
|
||||
default='posted_only', required=True
|
||||
)
|
||||
display_accounts = fields.Selection(
|
||||
[('all', 'All'),
|
||||
('balance_not_zero', 'With balance not zero')], string='Display accounts',
|
||||
default='balance_not_zero', required=True
|
||||
)
|
||||
date_from = fields.Date(
|
||||
string='Start date',
|
||||
)
|
||||
date_to = fields.Date(
|
||||
string='End date',
|
||||
)
|
||||
account_ids = fields.Many2many(
|
||||
'account.account', string='Accounts'
|
||||
)
|
||||
analytic_ids = fields.Many2many(
|
||||
'account.analytic.account', string='Analytic Accounts'
|
||||
)
|
||||
journal_ids = fields.Many2many(
|
||||
'account.journal', string='Journals',
|
||||
default=_get_journals
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company',
|
||||
default=lambda self: self.env.company
|
||||
)
|
||||
company_ids = fields.Many2many(
|
||||
'res.company',
|
||||
'ins_trial_balance_res_company_rel',
|
||||
'ins_trial_balance_id',
|
||||
'res_company_id',
|
||||
string='Companies',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
|
||||
def write(self, vals):
|
||||
|
||||
if vals.get('date_range'):
|
||||
vals.update({'date_from': False, 'date_to': False})
|
||||
if vals.get('date_from') and vals.get('date_to'):
|
||||
vals.update({'date_range': False})
|
||||
|
||||
if vals.get('journal_ids'):
|
||||
vals.update({'journal_ids': vals.get('journal_ids')})
|
||||
if vals.get('journal_ids') == []:
|
||||
vals.update({'journal_ids': [(5,)]})
|
||||
|
||||
if vals.get('account_ids'):
|
||||
vals.update({'account_ids': vals.get('account_ids')})
|
||||
if vals.get('account_ids') == []:
|
||||
vals.update({'account_ids': [(5,)]})
|
||||
|
||||
if vals.get('analytic_ids'):
|
||||
vals.update({'analytic_ids': vals.get('analytic_ids')})
|
||||
if vals.get('analytic_ids') == []:
|
||||
vals.update({'analytic_ids': [(5,)]})
|
||||
|
||||
if vals.get('company_ids'):
|
||||
company_ids = vals.get('company_ids')
|
||||
if isinstance(company_ids, list) and company_ids and isinstance(company_ids[0], int):
|
||||
vals.update({'company_ids': [(6, 0, company_ids)], 'company_id': company_ids[0]})
|
||||
if vals.get('company_ids') == []:
|
||||
vals.update({'company_ids': [(5,)]})
|
||||
if vals.get('company_id') and not vals.get('company_ids'):
|
||||
vals.update({'company_ids': [(6, 0, [vals.get('company_id')])]})
|
||||
|
||||
ret = super(InsTrialBalance, self).write(vals)
|
||||
return ret
|
||||
|
||||
def validate_data(self):
|
||||
if self.date_from > self.date_to:
|
||||
raise ValidationError(_('"Date from" must be less than or equal to "Date to"'))
|
||||
return True
|
||||
|
||||
def process_filters(self, data):
|
||||
''' To show on report headers'''
|
||||
filters = {}
|
||||
|
||||
if data.get('date_from') > data.get('date_to'):
|
||||
raise ValidationError(_('From date must not be less than to date'))
|
||||
|
||||
if not data.get('date_from') or not data.get('date_to'):
|
||||
raise ValidationError(_('From date and To dates are mandatory for this report'))
|
||||
|
||||
if data.get('journal_ids',[]):
|
||||
filters['journals'] = self.env['account.journal'].browse(data.get('journal_ids',[])).mapped('code')
|
||||
else:
|
||||
filters['journals'] = ''
|
||||
|
||||
if data.get('account_ids',[]):
|
||||
filters['accounts'] = self.env['account.account'].browse(data.get('account_ids',[])).mapped('code')
|
||||
else:
|
||||
filters['accounts'] = ''
|
||||
|
||||
if data.get('analytic_ids', []):
|
||||
filters['analytics'] = self.env['account.analytic.account'].browse(data.get('analytic_ids', [])).mapped(
|
||||
'name')
|
||||
else:
|
||||
filters['analytics'] = ['All']
|
||||
|
||||
if data.get('display_accounts') == 'all':
|
||||
filters['display_accounts'] = 'All'
|
||||
else:
|
||||
filters['display_accounts'] = 'With balance not zero'
|
||||
|
||||
if data.get('date_from',False):
|
||||
filters['date_from'] = data.get('date_from')
|
||||
if data.get('date_to', False):
|
||||
filters['date_to'] = data.get('date_to')
|
||||
|
||||
if data.get('show_hierarchy', False):
|
||||
filters['show_hierarchy'] = True
|
||||
else:
|
||||
filters['show_hierarchy'] = False
|
||||
|
||||
if data.get('strict_range', False):
|
||||
filters['strict_range'] = True
|
||||
else:
|
||||
filters['strict_range'] = False
|
||||
|
||||
filters['journals_list'] = data.get('journals_list')
|
||||
filters['accounts_list'] = data.get('accounts_list')
|
||||
filters['analytics_list'] = data.get('analytics_list')
|
||||
filters['company_name'] = data.get('company_name')
|
||||
filters['company_id'] = data.get('company_id')
|
||||
filters['company_ids'] = data.get('company_ids', [])
|
||||
filters['companies_list'] = data.get('companies_list')
|
||||
|
||||
return filters
|
||||
|
||||
def prepare_hierarchy(self, move_lines):
|
||||
'''
|
||||
It will process the move lines as per the hierarchy.
|
||||
:param move_lines: list of dict
|
||||
:return: list of dict with hierarchy levels
|
||||
'''
|
||||
|
||||
def prepare_tmp(id=False, code=False, indent_list=[], parent=[]):
|
||||
return {
|
||||
'id': id,
|
||||
'code': code,
|
||||
'initial_debit': 0,
|
||||
'initial_credit': 0,
|
||||
'initial_balance': 0,
|
||||
'debit': 0,
|
||||
'credit': 0,
|
||||
'balance': 0,
|
||||
'ending_debit': 0,
|
||||
'ending_credit': 0,
|
||||
'ending_balance': 0,
|
||||
'dummy': True,
|
||||
'indent_list': indent_list,
|
||||
'len': len(indent_list) or 1,
|
||||
'parent': ' a'.join(['0'] + parent)
|
||||
}
|
||||
|
||||
if move_lines:
|
||||
hirarchy_list = []
|
||||
parent_1 = []
|
||||
parent_2 = []
|
||||
parent_3 = []
|
||||
for line in move_lines:
|
||||
|
||||
q = move_lines[line]
|
||||
tmp = q.copy()
|
||||
tmp.update(prepare_tmp(id=str(tmp['id']) + 'z1',
|
||||
code=str(tmp['code'])[0],
|
||||
indent_list=[1],
|
||||
parent=[]))
|
||||
if tmp['code'] not in [k['code'] for k in hirarchy_list]:
|
||||
hirarchy_list.append(tmp)
|
||||
parent_1 = [tmp['id']]
|
||||
|
||||
tmp = q.copy()
|
||||
tmp.update(prepare_tmp(id=str(tmp['id']) + 'z2',
|
||||
code=str(tmp['code'])[:2],
|
||||
indent_list=[1,2],
|
||||
parent=parent_1))
|
||||
if tmp['code'] not in [k['code'] for k in hirarchy_list]:
|
||||
hirarchy_list.append(tmp)
|
||||
parent_2 = [tmp['id']]
|
||||
|
||||
tmp = q.copy()
|
||||
tmp.update(prepare_tmp(id=str(tmp['id']) + 'z3',
|
||||
code=str(tmp['code'])[:3],
|
||||
indent_list=[1, 2, 3],
|
||||
parent=parent_1 + parent_2))
|
||||
|
||||
if tmp['code'] not in [k['code'] for k in hirarchy_list]:
|
||||
hirarchy_list.append(tmp)
|
||||
parent_3 = [tmp['id']]
|
||||
final_parent = ['0'] + parent_1 + parent_2 + parent_3
|
||||
tmp = q.copy()
|
||||
tmp.update({'code': str(tmp['code']), 'parent': ' a'.join(final_parent), 'dummy': False, 'indent_list': [1, 2, 3, 4],})
|
||||
hirarchy_list.append(tmp)
|
||||
|
||||
for line in move_lines:
|
||||
q = move_lines[line]
|
||||
for l in hirarchy_list:
|
||||
if str(q['code'])[0] == l['code'] or \
|
||||
str(q['code'])[:2] == l['code'] or \
|
||||
str(q['code'])[:3] == l['code']:
|
||||
l['initial_debit'] += q['initial_debit']
|
||||
l['initial_credit'] += q['initial_credit']
|
||||
l['initial_balance'] += q['initial_balance']
|
||||
l['debit'] += q['debit']
|
||||
l['credit'] += q['credit']
|
||||
l['balance'] += q['balance']
|
||||
l['ending_debit'] += q['ending_debit']
|
||||
l['ending_credit'] += q['ending_credit']
|
||||
l['ending_balance'] += q['ending_balance']
|
||||
|
||||
return sorted(hirarchy_list, key=itemgetter('code'))
|
||||
return []
|
||||
|
||||
def process_data(self, data):
|
||||
if data:
|
||||
cr = self.env.cr
|
||||
WHERE = '(1=1)'
|
||||
|
||||
if data.get('journal_ids',[]):
|
||||
WHERE += ' AND j.id IN %s' % str(tuple(data.get('journal_ids'))+tuple([0]))
|
||||
|
||||
if data.get('account_ids',[]):
|
||||
WHERE += ' AND a.id IN %s' % str(tuple(data.get('account_ids'))+tuple([0]))
|
||||
|
||||
if data.get('analytic_ids', []):
|
||||
WHERE += ' AND anl.id IN %s' % str(tuple(data.get('analytic_ids')) + tuple([0]))
|
||||
if data.get('company_ids', []):
|
||||
WHERE += ' AND l.company_id IN %s' % str(tuple(data.get('company_ids')) + tuple([0]))
|
||||
elif data.get('company_id', False):
|
||||
WHERE += ' AND l.company_id = %s' % data.get('company_id')
|
||||
|
||||
if data.get('target_moves') == 'posted_only':
|
||||
WHERE += " AND m.state = 'posted'"
|
||||
|
||||
if data.get('account_ids'):
|
||||
account_ids = self.env['account.account'].browse(data.get('account_ids'))
|
||||
else:
|
||||
account_ids = self.env['account.account'].search([('company_id', 'in', data.get('company_ids') or [data.get('company_id')])])
|
||||
company_currency_id = self._get_report_company().currency_id
|
||||
|
||||
move_lines = {x.code: {'name':x.name,'code':x.code,'id':x.id,
|
||||
'initial_debit':0.0, 'initial_credit':0.0,'initial_balance':0.0,
|
||||
'debit':0.0, 'credit':0.0, 'balance':0.0,
|
||||
'ending_credit':0.0, 'ending_debit':0.0, 'ending_balance':0.0,
|
||||
'company_currency_id': company_currency_id.id} for x in account_ids} # base for accounts to display
|
||||
retained = {}
|
||||
retained_earnings = 0.0
|
||||
retained_credit = 0.0
|
||||
retained_debit = 0.0
|
||||
total_deb = 0.0
|
||||
total_cre = 0.0
|
||||
total_bln = 0.0
|
||||
total_init_deb = 0.0
|
||||
total_init_cre = 0.0
|
||||
total_init_bal = 0.0
|
||||
total_end_deb = 0.0
|
||||
total_end_cre = 0.0
|
||||
total_end_bal = 0.0
|
||||
for account in account_ids:
|
||||
currency = account.company_id.currency_id or company_currency_id
|
||||
WHERE_INIT = WHERE + " AND l.date < '%s'" % data.get('date_from')
|
||||
WHERE_INIT += " AND l.account_id = %s" % account.id
|
||||
init_blns = 0.0
|
||||
deb = 0.0
|
||||
cre = 0.0
|
||||
end_blns = 0.0
|
||||
end_cr = 0.0
|
||||
end_dr = 0.0
|
||||
sql = ('''
|
||||
SELECT
|
||||
COALESCE(SUM(l.debit),0) AS initial_debit,
|
||||
COALESCE(SUM(l.credit),0) AS initial_credit,
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit),0) AS initial_balance
|
||||
FROM account_move_line l
|
||||
JOIN account_move m ON (l.move_id=m.id)
|
||||
JOIN account_account a ON (l.account_id=a.id)
|
||||
LEFT JOIN account_analytic_account anl ON (l.analytic_account_id=anl.id)
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)
|
||||
JOIN account_journal j ON (l.journal_id=j.id)
|
||||
WHERE %s
|
||||
''')%WHERE_INIT
|
||||
cr.execute(sql)
|
||||
init_blns = cr.dictfetchone()
|
||||
|
||||
move_lines[account.code]['initial_balance'] = init_blns['initial_balance']
|
||||
move_lines[account.code]['initial_debit'] = init_blns['initial_debit']
|
||||
move_lines[account.code]['initial_credit'] = init_blns['initial_credit']
|
||||
|
||||
if account.user_type_id.include_initial_balance and self.strict_range:
|
||||
move_lines[account.code]['initial_balance'] = 0.0
|
||||
move_lines[account.code]['initial_debit'] = 0.0
|
||||
move_lines[account.code]['initial_credit'] = 0.0
|
||||
if self.strict_range and account.user_type_id != self.env.ref('account.data_unaffected_earnings'):
|
||||
retained_earnings += init_blns['initial_balance']
|
||||
retained_credit += init_blns['initial_credit']
|
||||
retained_debit += init_blns['initial_debit']
|
||||
total_init_deb += init_blns['initial_debit']
|
||||
total_init_cre += init_blns['initial_credit']
|
||||
total_init_bal += init_blns['initial_balance']
|
||||
WHERE_CURRENT = WHERE + " AND l.date >= '%s'" % data.get('date_from') + " AND l.date <= '%s'" % data.get('date_to')
|
||||
WHERE_CURRENT += " AND a.id = %s" % account.id
|
||||
sql = ('''
|
||||
SELECT
|
||||
COALESCE(SUM(l.debit),0) AS debit,
|
||||
COALESCE(SUM(l.credit),0) AS credit,
|
||||
COALESCE(SUM(l.debit),0) - COALESCE(SUM(l.credit),0) AS balance
|
||||
FROM account_move_line l
|
||||
JOIN account_move m ON (l.move_id=m.id)
|
||||
JOIN account_account a ON (l.account_id=a.id)
|
||||
LEFT JOIN account_analytic_account anl ON (l.analytic_account_id=anl.id)
|
||||
LEFT JOIN res_currency c ON (l.currency_id=c.id)
|
||||
LEFT JOIN res_partner p ON (l.partner_id=p.id)
|
||||
JOIN account_journal j ON (l.journal_id=j.id)
|
||||
WHERE %s
|
||||
''') % WHERE_CURRENT
|
||||
cr.execute(sql)
|
||||
op = cr.dictfetchone()
|
||||
deb = op['debit']
|
||||
cre = op['credit']
|
||||
bln = op['balance']
|
||||
move_lines[account.code]['debit'] = deb
|
||||
move_lines[account.code]['credit'] = cre
|
||||
move_lines[account.code]['balance'] = bln
|
||||
|
||||
end_blns = init_blns['initial_balance'] + bln
|
||||
end_cr = init_blns['initial_credit'] + cre
|
||||
end_dr = init_blns['initial_debit'] + deb
|
||||
|
||||
move_lines[account.code]['ending_balance'] = end_blns
|
||||
move_lines[account.code]['ending_credit'] = end_cr
|
||||
move_lines[account.code]['ending_debit'] = end_dr
|
||||
|
||||
if data.get('display_accounts') == 'balance_not_zero':
|
||||
if end_blns: # debit or credit exist
|
||||
total_deb += deb
|
||||
total_cre += cre
|
||||
total_bln += bln
|
||||
elif bln:
|
||||
continue
|
||||
else:
|
||||
move_lines.pop(account.code)
|
||||
else:
|
||||
total_deb += deb
|
||||
total_cre += cre
|
||||
total_bln += bln
|
||||
|
||||
if self.strict_range:
|
||||
retained = {'RETAINED': {'name':'Unallocated Earnings','code':'','id':'RET',
|
||||
'initial_credit':company_currency_id.round(retained_credit),
|
||||
'initial_debit':company_currency_id.round(retained_debit),
|
||||
'initial_balance':company_currency_id.round(retained_earnings),
|
||||
'credit':0.0, 'debit':0.0, 'balance': 0.0,
|
||||
'ending_credit':company_currency_id.round(retained_credit),
|
||||
'ending_debit':company_currency_id.round(retained_debit),
|
||||
'ending_balance':company_currency_id.round(retained_earnings),
|
||||
'company_currency_id': company_currency_id.id}}
|
||||
subtotal = {'SUBTOTAL': {
|
||||
'name': 'Total',
|
||||
'code': '',
|
||||
'id': 'SUB',
|
||||
'initial_credit': company_currency_id.round(total_init_cre),
|
||||
'initial_debit':company_currency_id.round(total_init_deb),
|
||||
'initial_balance':company_currency_id.round(total_init_bal),
|
||||
'credit': company_currency_id.round(total_cre),
|
||||
'debit':company_currency_id.round(total_deb),
|
||||
'balance':company_currency_id.round(total_bln),
|
||||
'ending_credit': company_currency_id.round(total_init_cre + total_cre),
|
||||
'ending_debit': company_currency_id.round(total_init_deb + total_deb),
|
||||
'ending_balance': company_currency_id.round(total_init_bal + total_bln),
|
||||
'company_currency_id': company_currency_id.id}}
|
||||
|
||||
if self.show_hierarchy:
|
||||
|
||||
move_lines = self.prepare_hierarchy(move_lines)
|
||||
return [move_lines, retained, subtotal]
|
||||
|
||||
def get_filters(self, default_filters={}):
|
||||
|
||||
self.onchange_date_range()
|
||||
|
||||
company = self._get_report_company()
|
||||
company_ids = self._get_report_company_ids()
|
||||
company_domain = [('company_id', 'in', company_ids)]
|
||||
companies = self._get_available_companies()
|
||||
|
||||
journals = self.journal_ids if self.journal_ids else self.env['account.journal'].search(company_domain)
|
||||
accounts = self.account_ids if self.account_ids else self.env['account.account'].search(company_domain)
|
||||
analytics = self.analytic_ids if self.analytic_ids else self.env['account.analytic.account'].search(company_domain)
|
||||
|
||||
filter_dict = {
|
||||
'journal_ids': self.journal_ids.ids,
|
||||
'account_ids': self.account_ids.ids,
|
||||
'analytic_ids': self.analytic_ids.ids,
|
||||
'company_ids': company_ids,
|
||||
'company_id': self.company_id and self.company_id.id or False,
|
||||
'date_from': self.date_from,
|
||||
'date_to': self.date_to,
|
||||
'display_accounts': self.display_accounts,
|
||||
'show_hierarchy': self.show_hierarchy,
|
||||
'strict_range': self.strict_range,
|
||||
'target_moves': self.target_moves,
|
||||
|
||||
'journals_list': [(j.id, j.name) for j in journals],
|
||||
'accounts_list': [(a.id, a.name) for a in accounts],
|
||||
'analytics_list': [(anl.id, anl.name) for anl in analytics],
|
||||
'company_name': self._get_company_display_name(),
|
||||
'companies_list': [(c.id, c.name) for c in companies],
|
||||
}
|
||||
filter_dict.update(default_filters)
|
||||
return filter_dict
|
||||
|
||||
def get_report_datas(self, default_filters={}):
|
||||
'''
|
||||
Main method for pdf, xlsx and js calls
|
||||
:param default_filters: Use this while calling from other methods. Just a dict
|
||||
:return: All the datas for GL
|
||||
'''
|
||||
if self.validate_data():
|
||||
data = self.get_filters(default_filters)
|
||||
filters = self.process_filters(data)
|
||||
account_lines, retained, subtotal = self.process_data(data)
|
||||
return filters, account_lines, retained, subtotal
|
||||
|
||||
def action_pdf(self):
|
||||
filters, account_lines, retained, subtotal = self.get_report_datas()
|
||||
return self.env.ref(
|
||||
'account_dynamic_reports'
|
||||
'.action_print_trial_balance').with_context(landscape=True).report_action(
|
||||
self, data={'Ledger_data':account_lines,
|
||||
'Retained':retained,
|
||||
'Subtotal':subtotal,
|
||||
'Filters':filters
|
||||
})
|
||||
|
||||
def action_xlsx(self):
|
||||
''' Button function for Xlsx '''
|
||||
|
||||
data = self.read()
|
||||
date_from = fields.Date.from_string(self.date_from).strftime(
|
||||
self.env['res.lang'].search([('code', '=', self.env.user.lang)])[0].date_format)
|
||||
date_to = fields.Date.from_string(self.date_to).strftime(
|
||||
self.env['res.lang'].search([('code', '=', self.env.user.lang)])[0].date_format)
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.report',
|
||||
'data': {'model': 'ins.trial.balance',
|
||||
'options': json.dumps(data[0], default=date_utils.json_default),
|
||||
'output_format': 'xlsx',
|
||||
'report_name': 'Trial Balance - %s / %s' % (date_from, date_to),
|
||||
},
|
||||
'report_type': 'xlsx'
|
||||
}
|
||||
|
||||
def get_xlsx_report(self, data, response):
|
||||
|
||||
# Initialize
|
||||
#############################################################
|
||||
output = io.BytesIO()
|
||||
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
|
||||
sheet = workbook.add_worksheet('Trial Balance')
|
||||
sheet.set_zoom(95)
|
||||
sheet_2 = workbook.add_worksheet('Filters')
|
||||
sheet_2.protect()
|
||||
|
||||
# Get record and data
|
||||
record = self.env['ins.trial.balance'].browse(data.get('id', [])) or False
|
||||
filter, account_lines, retained, subtotal = record.get_report_datas()
|
||||
|
||||
# Formats
|
||||
############################################################
|
||||
sheet.set_column(0, 0, 30)
|
||||
sheet.set_column(1, 1, 15)
|
||||
sheet.set_column(2, 2, 15)
|
||||
sheet.set_column(3, 3, 15)
|
||||
sheet.set_column(4, 4, 15)
|
||||
sheet.set_column(5, 5, 15)
|
||||
sheet.set_column(6, 6, 15)
|
||||
sheet.set_column(7, 7, 15)
|
||||
sheet.set_column(8, 8, 15)
|
||||
sheet.set_column(9, 9, 15)
|
||||
|
||||
sheet_2.set_column(0, 0, 35)
|
||||
sheet_2.set_column(1, 1, 25)
|
||||
sheet_2.set_column(2, 2, 25)
|
||||
sheet_2.set_column(3, 3, 25)
|
||||
sheet_2.set_column(4, 4, 25)
|
||||
sheet_2.set_column(5, 5, 25)
|
||||
sheet_2.set_column(6, 6, 25)
|
||||
|
||||
sheet.freeze_panes(5, 0)
|
||||
|
||||
sheet.set_zoom(80)
|
||||
|
||||
sheet.screen_gridlines = False
|
||||
sheet_2.screen_gridlines = False
|
||||
sheet_2.protect()
|
||||
|
||||
format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
})
|
||||
format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
# 'border': True
|
||||
})
|
||||
format_merged_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'right': True,
|
||||
'left': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
format_merged_header_without_border = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header_total = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
line_header_left = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header_left_total = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header_light_total = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
})
|
||||
line_header_light_left = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header_highlight = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
})
|
||||
line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
|
||||
lang = self.env.user.lang
|
||||
lang_id = self.env['res.lang'].search([('code', '=', lang)])[0]
|
||||
currency_id = self.env.user.company_id.currency_id
|
||||
line_header.num_format = currency_id.excel_format
|
||||
line_header_light.num_format = currency_id.excel_format
|
||||
line_header_highlight.num_format = currency_id.excel_format
|
||||
line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
format_merged_header_without_border.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
# Write data
|
||||
################################################################
|
||||
row_pos_2 = 0
|
||||
row_pos = 0
|
||||
sheet.merge_range(0, 0, 0, 10, 'Trial Balance' + ' - ' + filter['company_name'], format_title)
|
||||
|
||||
# Write filters
|
||||
row_pos_2 += 2
|
||||
sheet_2.write(row_pos_2, 0, _('Date from'), format_header)
|
||||
datestring = fields.Date.from_string(str(filter['date_from'])).strftime(lang_id.date_format)
|
||||
sheet_2.write(row_pos_2, 1, datestring or '', line_header_light_date)
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Date to'), format_header)
|
||||
datestring = fields.Date.from_string(str(filter['date_to'])).strftime(lang_id.date_format)
|
||||
sheet_2.write(row_pos_2, 1, datestring or '', line_header_light_date)
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Display accounts'), format_header)
|
||||
sheet_2.write(row_pos_2, 1, filter['display_accounts'], content_header)
|
||||
# Journals
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Journals'), format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
sheet_2.write(row_pos_2, 1, j_list, content_header)
|
||||
# Accounts
|
||||
row_pos_2 += 1
|
||||
sheet_2.write(row_pos_2, 0, _('Analytic Accounts'), format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytics')])
|
||||
sheet_2.write(row_pos_2, 1, a_list, content_header)
|
||||
|
||||
# Write Ledger details
|
||||
row_pos += 3
|
||||
sheet.merge_range(row_pos, 1, row_pos, 3, 'Initial Balance', format_merged_header)
|
||||
datestring = fields.Date.from_string(str(filter.get('date_from'))).strftime(lang_id.date_format)
|
||||
sheet.write(row_pos, 4, datestring, format_merged_header_without_border)
|
||||
sheet.write(row_pos, 5, _(' To '), format_merged_header_without_border)
|
||||
datestring = fields.Date.from_string(str(filter.get('date_to'))).strftime(lang_id.date_format)
|
||||
sheet.write(row_pos, 6, datestring, format_merged_header_without_border)
|
||||
sheet.merge_range(row_pos, 7, row_pos, 9, 'Ending Balance', format_merged_header)
|
||||
row_pos += 1
|
||||
sheet.write(row_pos, 0, _('Account'), format_header)
|
||||
sheet.write(row_pos, 1, _('Debit'), format_header)
|
||||
sheet.write(row_pos, 2, _('Credit'), format_header)
|
||||
sheet.write(row_pos, 3, _('Balance'), format_header)
|
||||
sheet.write(row_pos, 4, _('Debit'), format_header)
|
||||
sheet.write(row_pos, 5, _('Credit'), format_header)
|
||||
sheet.write(row_pos, 6, _('Balance'), format_header)
|
||||
sheet.write(row_pos, 7, _('Debit'), format_header)
|
||||
sheet.write(row_pos, 8, _('Credit'), format_header)
|
||||
sheet.write(row_pos, 9, _('Balance'), format_header)
|
||||
|
||||
if account_lines:
|
||||
if not filter.get('show_hierarchy'):
|
||||
for line in account_lines: # Normal lines
|
||||
row_pos += 1
|
||||
sheet.write(row_pos, 0,
|
||||
account_lines[line].get('code') + ' ' + account_lines[line].get('name'),
|
||||
line_header_light_left)
|
||||
sheet.write(row_pos, 1, float(account_lines[line].get('initial_debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 2, float(account_lines[line].get('initial_credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 3, float(account_lines[line].get('initial_balance')),
|
||||
line_header_highlight)
|
||||
sheet.write(row_pos, 4, float(account_lines[line].get('debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 5, float(account_lines[line].get('credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 6, float(account_lines[line].get('balance')),
|
||||
line_header_highlight)
|
||||
sheet.write(row_pos, 7, float(account_lines[line].get('ending_debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 8, float(account_lines[line].get('ending_credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 9, float(account_lines[line].get('ending_balance')),
|
||||
line_header_highlight)
|
||||
else:
|
||||
for line in account_lines: # Normal lines
|
||||
row_pos += 1
|
||||
blank_space = ' ' * len(line.get('indent_list'))
|
||||
if line.get('dummy'):
|
||||
sheet.write(row_pos, 0, blank_space + line.get('code'),
|
||||
line_header_light_left)
|
||||
else:
|
||||
sheet.write(row_pos, 0,
|
||||
blank_space + line.get('code') + ' ' + line.get('name'),
|
||||
line_header_light_left)
|
||||
sheet.write(row_pos, 1, float(line.get('initial_debit')), line_header_light)
|
||||
sheet.write(row_pos, 2, float(line.get('initial_credit')), line_header_light)
|
||||
sheet.write(row_pos, 3, float(line.get('initial_balance')),
|
||||
line_header_highlight)
|
||||
sheet.write(row_pos, 4, float(line.get('debit')), line_header_light)
|
||||
sheet.write(row_pos, 5, float(line.get('credit')), line_header_light)
|
||||
sheet.write(row_pos, 6, float(line.get('balance')), line_header_highlight)
|
||||
sheet.write(row_pos, 7, float(line.get('ending_debit')), line_header_light)
|
||||
sheet.write(row_pos, 8, float(line.get('ending_credit')), line_header_light)
|
||||
sheet.write(row_pos, 9, float(line.get('ending_balance')),
|
||||
line_header_highlight)
|
||||
|
||||
if filter.get('strict_range'):
|
||||
# Retained Earnings line
|
||||
row_pos += 1
|
||||
sheet.write(row_pos, 0, ' ' + retained['RETAINED'].get('name'),
|
||||
line_header_light_left)
|
||||
sheet.write(row_pos, 1, float(retained['RETAINED'].get('initial_debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 2, float(retained['RETAINED'].get('initial_credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 3, float(retained['RETAINED'].get('initial_balance')),
|
||||
line_header_highlight)
|
||||
sheet.write(row_pos, 4, float(retained['RETAINED'].get('debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 5, float(retained['RETAINED'].get('credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 6, float(retained['RETAINED'].get('balance')),
|
||||
line_header_highlight)
|
||||
sheet.write(row_pos, 7, float(retained['RETAINED'].get('ending_debit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 8, float(retained['RETAINED'].get('ending_credit')),
|
||||
line_header_light)
|
||||
sheet.write(row_pos, 9, float(retained['RETAINED'].get('ending_balance')),
|
||||
line_header_highlight)
|
||||
# Sub total line
|
||||
row_pos += 2
|
||||
sheet.write(row_pos, 0,
|
||||
subtotal['SUBTOTAL'].get('code') + ' ' + subtotal['SUBTOTAL'].get('name'),
|
||||
line_header_left_total)
|
||||
sheet.write(row_pos, 1, float(subtotal['SUBTOTAL'].get('initial_debit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 2, float(subtotal['SUBTOTAL'].get('initial_credit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 3, float(subtotal['SUBTOTAL'].get('initial_balance')),
|
||||
line_header_total)
|
||||
sheet.write(row_pos, 4, float(subtotal['SUBTOTAL'].get('debit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 5, float(subtotal['SUBTOTAL'].get('credit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 6, float(subtotal['SUBTOTAL'].get('balance')), line_header_total)
|
||||
sheet.write(row_pos, 7, float(subtotal['SUBTOTAL'].get('ending_debit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 8, float(subtotal['SUBTOTAL'].get('ending_credit')),
|
||||
line_header_light_total)
|
||||
sheet.write(row_pos, 9, float(subtotal['SUBTOTAL'].get('ending_balance')),
|
||||
line_header_total)
|
||||
|
||||
# Close and return
|
||||
#################################################################
|
||||
workbook.close()
|
||||
output.seek(0)
|
||||
response.stream.write(output.read())
|
||||
output.close()
|
||||
|
||||
def action_view(self):
|
||||
res = {
|
||||
'type': 'ir.actions.client',
|
||||
'name': 'TB View',
|
||||
'tag': 'dynamic.tb',
|
||||
'context': {'wizard_id': self.id}
|
||||
}
|
||||
return res
|
||||
@@ -0,0 +1,77 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="ins_trial_balance_wizard" model="ir.ui.view">
|
||||
<field name="name">ins.trial.balance.view</field>
|
||||
<field name="model">ins.trial.balance</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group col="4">
|
||||
<field name="date_range"/>
|
||||
<field name="company_ids" widget="many2many_tags" groups="base.group_multi_company"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="target_moves" widget="radio"/>
|
||||
<field name="display_accounts" widget="radio"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="show_hierarchy" invisible="0"/>
|
||||
<field name="financial_year" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="account_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="journal_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="analytic_ids" widget="many2many_tags"
|
||||
domain="[('company_id','in',company_ids)]"
|
||||
options="{'no_create_edit': True,'no_create': True, 'no_quick_create': True}"/>
|
||||
<field name="strict_range" help="If checked the accounts will act strict to the date range
|
||||
else it will consider initial balance to account"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_pdf" type="object" string="PDF" class="oe_highlight"/>
|
||||
<button name="action_xlsx" type="object" string="XLSX" class="oe_highlight"/>
|
||||
<button name="action_view" type="object" string="VIEW" class="oe_highlight"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ins_trial_balance_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Trial Balance</field>
|
||||
<field name="res_model">ins.trial.balance</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="ins_trial_balance_wizard"/>
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="account_dynamic_reports.model_ins_trial_balance" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<!-- <report-->
|
||||
<!-- id="action_ins_trial_balance_xlsx"-->
|
||||
<!-- model="ins.trial.balance"-->
|
||||
<!-- string="Trial Balance"-->
|
||||
<!-- report_type="xlsx"-->
|
||||
<!-- name="account_dynamic_reports.ins_trial_balance_xlsx"-->
|
||||
<!-- file="account_dynamic_reports.ins_trial_balance_xlsx"-->
|
||||
<!-- attachment_use="False"-->
|
||||
<!-- print_report_name="'TrialBalance - %s/%s' % (object.date_from,object.date_to)"-->
|
||||
<!-- />-->
|
||||
|
||||
<record id="action_dynamic_allinone_tb_report" model="ir.actions.client">
|
||||
<field name="name">Trial Balance Report</field>
|
||||
<field name="tag">dynamic.tb</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="account_report_tb" sequence="30" action="action_ins_trial_balance_wizard"
|
||||
name="Trial Balance" parent="account_reports_ins" groups="account.group_account_user"/>
|
||||
|
||||
<menuitem id="account_report_tb_wiz" sequence="30" action="action_dynamic_allinone_tb_report"
|
||||
name="Trial Balance" parent="account_reports_ins_wiz" groups="account.group_account_user"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,27 @@
|
||||
Odoo Proprietary License v1.0
|
||||
|
||||
This software and associated files (the "Software") may only be used (executed,
|
||||
modified, executed after modifications) if you have purchased a valid license
|
||||
from the authors, typically via Odoo Apps, or if you have received a written
|
||||
agreement from the authors of the Software (see the COPYRIGHT file).
|
||||
|
||||
You may develop Odoo modules that use the Software as a library (typically
|
||||
by depending on it, importing it and using its resources), but without copying
|
||||
any source code or material from the Software. You may distribute those
|
||||
modules under the license of your choice, provided that this license is
|
||||
compatible with the terms of the Odoo Proprietary License (For example:
|
||||
LGPL, MIT, or proprietary licenses similar to this one).
|
||||
|
||||
It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
||||
or modified copies of the Software.
|
||||
|
||||
The above copyright notice and this permission notice must be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,4 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import report
|
||||
from . import controllers
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name' : 'All in one Dynamic Financial Reports v14',
|
||||
'version' : '14.2.1',
|
||||
'summary': "General Ledger Trial Balance Ageing Balance Sheet Profit and Loss Cash Flow Dynamic",
|
||||
'sequence': 15,
|
||||
'description': """
|
||||
Odoo 14 Full Accouning, Odoo 14 All in one Accouning, PDF Reports, XLSX Reports,
|
||||
Dynamic View, Drill down, Clickable, Pdf and Xlsx package, Odoo 14 Accounting,
|
||||
Full Account Reports, Complete Accounting Reports, Financial Report for Odoo 13,
|
||||
Financial Reports, Excel reports, Financial Reports in Excel, Ageing Report,
|
||||
General Ledger, Partner Ledger, Trial Balance, Balance Sheet, Profit and Loss,
|
||||
Financial Report Kit, Cash Flow Statements, Cash Flow Report, Cash flow, Dynamic reports,
|
||||
Dynamic accounting, Dynamic financial
|
||||
""",
|
||||
'category': 'Accounting/Accounting',
|
||||
"price": 100,
|
||||
'author': 'Pycus',
|
||||
'maintainer': 'Pycus Technologies',
|
||||
'website': '',
|
||||
'images': ['static/description/banner.gif'],
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/data_financial_report.xml',
|
||||
'data/data_financial_report_fix.xml',
|
||||
|
||||
'views/assets.xml',
|
||||
'views/views.xml',
|
||||
'views/res_company_view.xml',
|
||||
|
||||
'views/general_ledger_view.xml',
|
||||
'views/partner_ledger_view.xml',
|
||||
'views/trial_balance_view.xml',
|
||||
'views/partner_ageing_view.xml',
|
||||
'views/financial_report_view.xml',
|
||||
|
||||
'wizard/general_ledger_view.xml',
|
||||
'wizard/partner_ledger_view.xml',
|
||||
'wizard/trial_balance_view.xml',
|
||||
'wizard/partner_ageing_view.xml',
|
||||
'wizard/financial_report_view.xml',
|
||||
],
|
||||
'demo': [],
|
||||
'license': 'OPL-1',
|
||||
'qweb': ['static/src/xml/view.xml'],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
from . import main
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
from odoo import http
|
||||
from odoo.http import content_disposition, request
|
||||
from odoo.addons.web.controllers.main import _serialize_exception
|
||||
from odoo.tools import html_escape
|
||||
|
||||
|
||||
class XLSXReportController(http.Controller):
|
||||
|
||||
@http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False)
|
||||
def get_report_xlsx(self, model, options, output_format, token, report_name, **kw):
|
||||
uid = request.session.uid
|
||||
report_obj = request.env[model].with_user(uid)
|
||||
options = json.loads(options)
|
||||
try:
|
||||
if output_format == 'xlsx':
|
||||
response = request.make_response(
|
||||
None,
|
||||
headers=[
|
||||
('Content-Type', 'application/vnd.ms-excel'),
|
||||
('Content-Disposition', content_disposition(report_name + '.xlsx'))
|
||||
]
|
||||
)
|
||||
report_obj.get_xlsx_report(options, response)
|
||||
response.set_cookie('fileToken', token)
|
||||
return response
|
||||
except Exception as e:
|
||||
se = _serialize_exception(e)
|
||||
error = {
|
||||
'code': 200,
|
||||
'message': 'Odoo Server Error',
|
||||
'data': se
|
||||
}
|
||||
return request.make_response(html_escape(json.dumps(error)))
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Financial Reports -->
|
||||
<record id="ins_account_financial_report_profitandloss0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Profit and Loss</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="sign">-1</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Income</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_gross_profit0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Gross Profit</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_operating_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Operating Income</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_gross_profit0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_revenue'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_cost_of_revenue0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Cost of Revenue</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_gross_profit0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_direct_costs'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_other_income0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Other Income</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_income0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_other_income'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_expense0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Expense</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_profitandloss0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_expenses')), (4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_balancesheet0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">BALANCE SHEET</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_assets0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">ASSETS</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_receivable')), (4,ref('account.data_account_type_liquidity')), (4,ref('account.data_account_type_current_assets')), (4,ref('account.data_account_type_non_current_assets')), (4,ref('account.data_account_type_prepayments')), (4,ref('account.data_account_type_fixed_assets'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_liabilitysum0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">LIABILITIES</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_liability0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Liability</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_liabilitysum0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_payable')), (4,ref('account.data_account_type_current_liabilities')), (4,ref('account.data_account_type_non_current_liabilities'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_equitysum0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">EQUITY</field>
|
||||
<field name="sequence">300</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_balancesheet0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_retained_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Retained Earnings</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_equitysum0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">from_the_beginning</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_equity'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Unallocated Earnings</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_equitysum0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Unallocated Earnings</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Earnings</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_current_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_account_type_other_income')),
|
||||
(4,ref('account.data_account_type_revenue')),
|
||||
(4,ref('account.data_account_type_expenses')),
|
||||
(4,ref('account.data_account_type_direct_costs')),
|
||||
(4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_current_allocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Current Allocated Earnings</field>
|
||||
<field name="sign">-1</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_current_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_unaffected_earnings'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_previous_unallocated_earnings0"
|
||||
model="ins.account.financial.report">
|
||||
<field name="name">Previous Unallocated Earnings</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="parent_id"
|
||||
ref="ins_account_financial_report_unallocated_earnings0"/>
|
||||
<field name="display_detail">no_detail</field>
|
||||
<field name="type">account_type</field>
|
||||
<field name="range_selection">initial_date_range</field>
|
||||
<field name="account_type_ids"
|
||||
eval="[(4,ref('account.data_unaffected_earnings')),
|
||||
(4,ref('account.data_account_type_other_income')),
|
||||
(4,ref('account.data_account_type_revenue')),
|
||||
(4,ref('account.data_account_type_expenses')),
|
||||
(4,ref('account.data_account_type_direct_costs')),
|
||||
(4,ref('account.data_account_type_depreciation'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_cash_flow0" model="ins.account.financial.report">
|
||||
<field name="name">Cash Flow Statement</field>
|
||||
<field name="type">sum</field>
|
||||
<field name="display_detail">no_detail</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_operation0" model="ins.account.financial.report">
|
||||
<field name="name">Operations</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_investing_activity0" model="ins.account.financial.report">
|
||||
<field name="name">Investing Activities</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_account_financial_report_financing_activity1" model="ins.account.financial.report">
|
||||
<field name="name">Financing Activities</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_cash_flow0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">sum</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="ins_cash_in_operation_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_operation_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="sequence">2</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_operation0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_cash_in_investing_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_investing_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_investing_activity0"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
<record id="ins_cash_in_financial_1" model="ins.account.financial.report">
|
||||
<field name="name">Cash In</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
<record id="ins_cash_out_financial_2" model="ins.account.financial.report">
|
||||
<field name="name">Cash Out</field>
|
||||
<field name="parent_id" ref="ins_account_financial_report_financing_activity1"/>
|
||||
<field name="display_detail">detail_with_hierarchy</field>
|
||||
<field name="type">accounts</field>
|
||||
<field name="range_selection">current_date_range</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<function model="ins.account.financial.report"
|
||||
name="ensure_balance_sheet_asset_account_types"/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
from . import res_company
|
||||
@@ -0,0 +1,171 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = 'res.company'
|
||||
|
||||
strict_range = fields.Boolean(string='Use Strict Range', default=True,
|
||||
help='Use this if you want to show TB with retained earnings section')
|
||||
bucket_1 = fields.Integer(string='Bucket 1', required=True, default=30)
|
||||
bucket_2 = fields.Integer(string='Bucket 2', required=True, default=60)
|
||||
bucket_3 = fields.Integer(string='Bucket 3', required=True, default=90)
|
||||
bucket_4 = fields.Integer(string='Bucket 4', required=True, default=120)
|
||||
bucket_5 = fields.Integer(string='Bucket 5', required=True, default=180)
|
||||
date_range = fields.Selection(
|
||||
[('today', 'Today'),
|
||||
('this_week', 'This Week'),
|
||||
('this_month', 'This Month'),
|
||||
('this_quarter', 'This Quarter'),
|
||||
('this_financial_year', 'This financial Year'),
|
||||
('yesterday', 'Yesterday'),
|
||||
('last_week', 'Last Week'),
|
||||
('last_month', 'Last Month'),
|
||||
('last_quarter', 'Last Quarter'),
|
||||
('last_financial_year', 'Last Financial Year')],
|
||||
string='Default Date Range', default='this_financial_year', required=True
|
||||
)
|
||||
financial_year = fields.Selection([
|
||||
('april_march','1 April to 31 March'),
|
||||
('july_june','1 july to 30 June'),
|
||||
('january_december','1 Jan to 31 Dec')
|
||||
], string='Financial Year', default='january_december', required=True)
|
||||
|
||||
|
||||
class ResCurrency(models.Model):
|
||||
_inherit = 'res.currency'
|
||||
|
||||
excel_format = fields.Char(string='Excel format', default='_ * #,##0.00_) ;_ * - #,##0.00_) ;_ * "-"??_) ;_ @_ ', required=True)
|
||||
|
||||
class ins_account_financial_report(models.Model):
|
||||
_name = "ins.account.financial.report"
|
||||
_description = "Account Report"
|
||||
|
||||
@api.model
|
||||
def ensure_balance_sheet_asset_account_types(self):
|
||||
report = self.env.ref(
|
||||
'account_dynamic_reports_jabung.ins_account_financial_report_assets0',
|
||||
raise_if_not_found=False,
|
||||
)
|
||||
if not report:
|
||||
return False
|
||||
xmlids = [
|
||||
'account.data_account_type_receivable',
|
||||
'account.data_account_type_liquidity',
|
||||
'account.data_account_type_current_assets',
|
||||
'account.data_account_type_non_current_assets',
|
||||
'account.data_account_type_prepayments',
|
||||
'account.data_account_type_fixed_assets',
|
||||
]
|
||||
account_type_ids = []
|
||||
for xmlid in xmlids:
|
||||
account_type = self.env.ref(xmlid, raise_if_not_found=False)
|
||||
if account_type:
|
||||
account_type_ids.append(account_type.id)
|
||||
missing_ids = [
|
||||
account_type_id
|
||||
for account_type_id in account_type_ids
|
||||
if account_type_id not in report.account_type_ids.ids
|
||||
]
|
||||
if missing_ids:
|
||||
report.write({
|
||||
'account_type_ids': [(4, account_type_id) for account_type_id in missing_ids]
|
||||
})
|
||||
return True
|
||||
|
||||
@api.depends('parent_id', 'parent_id.level')
|
||||
def _get_level(self):
|
||||
'''Returns a dictionary with key=the ID of a record and value = the level of this
|
||||
record in the tree structure.'''
|
||||
for report in self:
|
||||
level = 0
|
||||
if report.parent_id:
|
||||
level = report.parent_id.level + 1
|
||||
report.level = level
|
||||
|
||||
def _get_children_by_order(self, strict_range):
|
||||
'''returns a recordset of all the children computed recursively, and sorted by sequence. Ready for the printing'''
|
||||
res = self
|
||||
children = self.search([('parent_id', 'in', self.ids)], order='sequence ASC')
|
||||
if children:
|
||||
for child in children:
|
||||
res += child._get_children_by_order(strict_range)
|
||||
if not strict_range:
|
||||
res -= self.env.ref('account_dynamic_reports_jabung.ins_account_financial_report_unallocated_earnings0')
|
||||
res -= self.env.ref('account_dynamic_reports_jabung.ins_account_financial_report_equitysum0')
|
||||
return res
|
||||
|
||||
name = fields.Char('Report Name', required=True, translate=True)
|
||||
parent_id = fields.Many2one('ins.account.financial.report', 'Parent')
|
||||
children_ids = fields.One2many('ins.account.financial.report', 'parent_id', 'Account Report')
|
||||
sequence = fields.Integer('Sequence')
|
||||
level = fields.Integer(compute='_get_level', string='Level', store=True)
|
||||
type = fields.Selection([
|
||||
('sum', 'View'),
|
||||
('accounts', 'Accounts'),
|
||||
('account_type', 'Account Type'),
|
||||
('account_report', 'Report Value'),
|
||||
], 'Type', default='sum')
|
||||
account_ids = fields.Many2many('account.account', 'ins_account_account_financial_report', 'report_line_id', 'account_id', 'Accounts')
|
||||
account_report_id = fields.Many2one('ins.account.financial.report', 'Report Value')
|
||||
account_type_ids = fields.Many2many('account.account.type', 'ins_account_account_financial_report_type', 'report_id', 'account_type_id', 'Account Types')
|
||||
sign = fields.Selection([('-1', 'Reverse balance sign'), ('1', 'Preserve balance sign')], 'Sign on Reports', required=True, default='1',
|
||||
help='For accounts that are typically more debited than credited and that you would like to print as negative amounts in your reports, you should reverse the sign of the balance; e.g.: Expense account. The same applies for accounts that are typically more credited than debited and that you would like to print as positive amounts in your reports; e.g.: Income account.')
|
||||
range_selection = fields.Selection([
|
||||
('from_the_beginning', 'From the Beginning'),
|
||||
('current_date_range', 'Based on Current Date Range'),
|
||||
('initial_date_range', 'Based on Initial Date Range')],
|
||||
help='"From the beginning" will select all the entries before and on the date range selected.'
|
||||
'"Based on Current Date Range" will select all the entries strictly on the date range selected'
|
||||
'"Based on Initial Date Range" will select only the initial balance for the selected date range',
|
||||
string='Custom Date Range')
|
||||
display_detail = fields.Selection([
|
||||
('no_detail', 'No detail'),
|
||||
('detail_flat', 'Display children flat'),
|
||||
('detail_with_hierarchy', 'Display children with hierarchy')
|
||||
], 'Display details', default='detail_flat')
|
||||
style_overwrite = fields.Selection([
|
||||
('0', 'Automatic formatting'),
|
||||
('1', 'Main Title 1 (bold, underlined)'),
|
||||
('2', 'Title 2 (bold)'),
|
||||
('3', 'Title 3 (bold, smaller)'),
|
||||
('4', 'Normal Text'),
|
||||
('5', 'Italic Text (smaller)'),
|
||||
('6', 'Smallest Text'),
|
||||
], 'Financial Report Style', default='0',
|
||||
help="You can set up here the format you want this record to be displayed. If you leave the automatic formatting, it will be computed based on the financial reports hierarchy (auto-computed field 'level').")
|
||||
|
||||
|
||||
class AccountAccount(models.Model):
|
||||
_inherit = 'account.account'
|
||||
|
||||
def get_cashflow_domain(self):
|
||||
cash_flow_id = self.env.ref('account_dynamic_reports_jabung.ins_account_financial_report_cash_flow0')
|
||||
if cash_flow_id:
|
||||
return [('parent_id.id', '=', cash_flow_id.id)]
|
||||
|
||||
cash_flow_category = fields.Many2one('ins.account.financial.report', string="Cash Flow type", domain=get_cashflow_domain)
|
||||
|
||||
@api.onchange('cash_flow_category')
|
||||
def onchange_cash_flow_category(self):
|
||||
# Add account to cash flow record to account_ids
|
||||
if self._origin and self._origin.id:
|
||||
self.cash_flow_category.write({'account_ids': [(4, self._origin.id)]})
|
||||
self.env.ref(
|
||||
'account_dynamic_reports_jabung.ins_account_financial_report_cash_flow0').write(
|
||||
{'account_ids': [(4, self._origin.id)]})
|
||||
# Remove account from previous category
|
||||
# In case of changing/ removing category
|
||||
if self._origin.cash_flow_category:
|
||||
self._origin.cash_flow_category.write({'account_ids': [(3, self._origin.id)]})
|
||||
self.env.ref(
|
||||
'account_dynamic_reports_jabung.ins_account_financial_report_cash_flow0').write(
|
||||
{'account_ids': [(3, self._origin.id)]})
|
||||
|
||||
|
||||
class CommonXlsxOut(models.TransientModel):
|
||||
_name = 'common.xlsx.out'
|
||||
|
||||
filedata = fields.Binary('Download file', readonly=True)
|
||||
filename = fields.Char('Filename', size=64, readonly=True)
|
||||
@@ -0,0 +1,10 @@
|
||||
from . import report_general_ledger
|
||||
#from . import report_general_ledger_xlsx
|
||||
from . import report_partner_ledger
|
||||
#from . import report_partner_ledger_xlsx
|
||||
from . import report_trial_balance
|
||||
#from . import report_trial_balance_xlsx
|
||||
from . import report_partner_ageing
|
||||
#from . import report_partner_ageing_xlsx
|
||||
from . import report_financial_report
|
||||
#from . import report_financial_report_xlsx
|
||||
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class FinancialReportPdf(models.AbstractModel):
|
||||
""" Abstract model for generating PDF report value and send to template common for P and L and Balance Sheet"""
|
||||
|
||||
_name = 'report.account_dynamic_reports_jabung.ins_report_financial'
|
||||
_description = 'Financial Report'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
""" Provide report values to template """
|
||||
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({
|
||||
'data': data.get('js_data'),
|
||||
'report_lines': data['js_data']['report_lines'],
|
||||
'account_report': data['js_data']['form']['account_report_id'][1],
|
||||
'currency': data['js_data']['currency'],
|
||||
})
|
||||
return data
|
||||
|
||||
ctx = {
|
||||
'data': data,
|
||||
'report_lines': data['report_lines'],
|
||||
'account_report': data['form']['account_report_id'][1],
|
||||
'currency': data['currency'],
|
||||
}
|
||||
return ctx
|
||||
@@ -0,0 +1,275 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsFinancialReportXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.ins_financial_report_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'bottom': False
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
#'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'right',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_string = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
self.line_header_string_bold = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'font': 'Arial',
|
||||
'bottom': True
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['date_from'] and filter['form']['date_from'].strftime('%Y-%m-%d'))
|
||||
if filter['form'].get('date_from'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
# Date to
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['date_to'] and filter['form']['date_to'].strftime('%Y-%m-%d'))
|
||||
if filter['form'].get('date_to'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
analytics = ', '.join(filter['form'].get('selected_analytics', []))
|
||||
if analytics:
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, analytics,
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
if filter['form']['enable_filter']:
|
||||
|
||||
# Compariosn Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Comparison Date from'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['comparison_context']['date_from'] and filter['form']['comparison_context']['date_from'].strftime('%Y-%m-%d'))
|
||||
if filter['form']['comparison_context'].get('date_from'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
# Compariosn Date to
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Comparison Date to'),
|
||||
self.format_header)
|
||||
date = self.convert_to_date(
|
||||
filter['form']['comparison_context']['date_to'] and filter['form']['comparison_context']['date_to'].strftime('%Y-%m-%d'))
|
||||
if filter['form']['comparison_context'].get('date_to'):
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, date,
|
||||
self.content_header_date)
|
||||
|
||||
def prepare_report_contents(self, data):
|
||||
self.row_pos += 3
|
||||
|
||||
if data['form']['debit_credit'] == 1:
|
||||
|
||||
self.sheet.set_column(0, 0, 90)
|
||||
self.sheet.set_column(1, 1, 15)
|
||||
self.sheet.set_column(2, 3, 15)
|
||||
self.sheet.set_column(3, 3, 15)
|
||||
|
||||
self.sheet.write_string(self.row_pos, 0, _('Name'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
self.row_pos += 1
|
||||
self.row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = self.line_header_string
|
||||
tmp_style_num = self.line_header
|
||||
else:
|
||||
tmp_style_str = self.line_header_string_bold
|
||||
tmp_style_num = self.line_header_bold
|
||||
self.sheet.write_string(self.row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('debit')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 2, float(a.get('credit')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 3, float(a.get('balance')), tmp_style_num)
|
||||
|
||||
if data['form']['debit_credit'] != 1:
|
||||
|
||||
self.sheet.set_column(0, 0, 105)
|
||||
self.sheet.set_column(1, 1, 15)
|
||||
self.sheet.set_column(2, 2, 15)
|
||||
|
||||
self.sheet.write_string(self.row_pos, 0, _('Name'),
|
||||
self.format_header)
|
||||
if data['form']['enable_filter']:
|
||||
self.sheet.write_string(self.row_pos, 1, data['form']['label_filter'],
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.write_string(self.row_pos, 1, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
for a in data['report_lines']:
|
||||
if a['level'] == 2:
|
||||
self.row_pos += 1
|
||||
self.row_pos += 1
|
||||
if a.get('account', False):
|
||||
tmp_style_str = self.line_header_string
|
||||
tmp_style_num = self.line_header
|
||||
else:
|
||||
tmp_style_str = self.line_header_string_bold
|
||||
tmp_style_num = self.line_header_bold
|
||||
self.sheet.write_string(self.row_pos, 0, ' ' * len(a.get('list_len', [])) + a.get('name'),
|
||||
tmp_style_str)
|
||||
if data['form']['enable_filter']:
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('balance_cmp')), tmp_style_num)
|
||||
self.sheet.write_number(self.row_pos, 2, float(a.get('balance')), tmp_style_num)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, 1, float(a.get('balance')), tmp_style_num)
|
||||
|
||||
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_bold.num_format = currency_id.excel_format
|
||||
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
if not record:
|
||||
return False
|
||||
data = record.get_report_values()
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet(data['form']['account_report_id'][1])
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
|
||||
self.sheet_2.set_column(0, 0, 25)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
#self.sheet.protect()
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
self.sheet.merge_range(0, 0, 0, 3, data['form']['account_report_id'][1] +' - '+data['form']['company_name'], self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
|
||||
#Filter section
|
||||
self.prepare_report_filters(data)
|
||||
# Content section
|
||||
self.prepare_report_contents(data)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportGeneralLedger(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.general_ledger'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ledger_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,361 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsGeneralLedgerXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.ins_general_ledger_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
'border': False
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
#'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'border': True,
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': True,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_left = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'left',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
#'top': True,
|
||||
#'bottom': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
#'top': True,
|
||||
#'bottom': True,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
})
|
||||
self.line_header_light_initial = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'bottom': True,
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_ending = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
'valign': 'top'
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_from']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_to']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Target moves'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['target_moves'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Display accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['display_accounts'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Sort by'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['sort_accounts_by'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Initial Balance'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['initial_balance'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
|
||||
# Journals
|
||||
self.row_pos_2 += 2
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Journals'),
|
||||
self.format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, j_list,
|
||||
self.content_header)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('accounts')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Account Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Account Tags'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('account_tags')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Analytic Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytics')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
# Analytic Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Analytic Tags'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('analytic_tags')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, acc_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if filter.get('include_details', False):
|
||||
self.sheet.write_string(self.row_pos, 0, _('Date'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('JRNL'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Partner'),
|
||||
self.format_header)
|
||||
# self.sheet.write_string(self.row_pos, 3, _('Ref'),
|
||||
# self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Move'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 4, _('Entry Label'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 1, _('Code'), self.format_header)
|
||||
self.sheet.merge_range(self.row_pos, 2, self.row_pos, 4, _('Account'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
if acc_lines:
|
||||
for line in acc_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, ' ' + acc_lines[line].get('code') + ' - ' + acc_lines[line].get('name'), self.line_header_left)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')), self.line_header)
|
||||
|
||||
if filter.get('include_details', False):
|
||||
|
||||
count, offset, sub_lines = self.record.build_detailed_move_lines(offset=0, account=line,
|
||||
fetch_range=1000000)
|
||||
|
||||
for sub_line in sub_lines:
|
||||
if sub_line.get('move_name') == 'Initial Balance':
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_initial)
|
||||
elif sub_line.get('move_name') not in ['Initial Balance','Ending Balance']:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_datetime(self.row_pos, 0, self.convert_to_date(sub_line.get('ldate')),
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 1, sub_line.get('lcode'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('partner_name') or '',
|
||||
self.line_header_light)
|
||||
# self.sheet.write_string(self.row_pos, 3, sub_line.get('lref') or '',
|
||||
# self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('move_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('lname') or '',
|
||||
self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5,
|
||||
float(sub_line.get('debit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6,
|
||||
float(sub_line.get('credit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 7,
|
||||
float(sub_line.get('balance')),self.line_header_light)
|
||||
else: # Ending Balance
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_ending)
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_initial.num_format = currency_id.excel_format
|
||||
self.line_header_light_ending.num_format = currency_id.excel_format
|
||||
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('General Ledger')
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 12)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 2, 30)
|
||||
self.sheet.set_column(3, 3, 18)
|
||||
self.sheet.set_column(4, 4, 30)
|
||||
self.sheet.set_column(5, 5, 10)
|
||||
self.sheet.set_column(6, 6, 10)
|
||||
self.sheet.set_column(7, 7, 10)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 8, 'General Ledger'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, account_lines = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, account_lines, filters)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportPartnerAgeing(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.partner_ageing'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ageing_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
'Period_Dict': data.get('js_data')[2],
|
||||
'Period_List': data.get('js_data')[3]
|
||||
})
|
||||
return data
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api,_
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsPartnerAgeingXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.ins_partner_ageing_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 14,
|
||||
'font':'Arial'
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
#'border': True
|
||||
})
|
||||
self.format_header_period = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
'left': True,
|
||||
'right': True,
|
||||
# 'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial'
|
||||
#'num_format': 'dd/mm/yyyy',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_total = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'border': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_period = workbook.add_format({
|
||||
'font_size': 11,
|
||||
'align': 'center',
|
||||
'bold': True,
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial'
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.line_header_light_period = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'left': True,
|
||||
'right': True,
|
||||
'font': 'Arial',
|
||||
'text_wrap': True,
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': False,
|
||||
'font': 'Arial',
|
||||
'align': 'center',
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('As on Date'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['as_on_date']) or ''),
|
||||
self.content_header_date)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Partner Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partner Tag'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('categories')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, period_dict, period_list, ageing_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if self.record.include_details:
|
||||
self.sheet.write_string(self.row_pos, 0, _('Entry #'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('Due Date'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Journal'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Account'), self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, _('Partner'),
|
||||
self.format_header)
|
||||
k = 4
|
||||
for period in period_list:
|
||||
self.sheet.write_string(self.row_pos, k, str(period),
|
||||
self.format_header_period)
|
||||
k += 1
|
||||
self.sheet.write_string(self.row_pos, k, _('Total'),
|
||||
self.format_header_period)
|
||||
|
||||
|
||||
if ageing_lines:
|
||||
for line in ageing_lines:
|
||||
|
||||
# Dummy vacant lines
|
||||
self.row_pos += 1
|
||||
report_last_column = 4 + len(period_list)
|
||||
for column in range(4, report_last_column + 1):
|
||||
self.sheet.write_string(self.row_pos, column, '', self.line_header_light_period)
|
||||
|
||||
self.row_pos += 1
|
||||
if line != 'Total':
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, ageing_lines[line].get('partner_name'), self.line_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 3, _('Total'),self.line_header_total)
|
||||
k = 4
|
||||
|
||||
for period in period_list:
|
||||
if line != 'Total':
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line][period],self.line_header)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line][period], self.line_header_total)
|
||||
k += 1
|
||||
if line != 'Total':
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line]['total'], self.line_header)
|
||||
else:
|
||||
self.sheet.write_number(self.row_pos, k, ageing_lines[line]['total'], self.line_header_total)
|
||||
|
||||
if self.record.include_details:
|
||||
if line != 'Total':
|
||||
count, offset, sub_lines, period_list = self.record.process_detailed_data(partner=line, fetch_range=1000000)
|
||||
for sub_line in sub_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 0, sub_line.get('move_name') or '',
|
||||
self.line_header_light)
|
||||
date = self.convert_to_date(sub_line.get('date_maturity') and sub_line.get('date_maturity').strftime('%Y-%m-%d') or sub_line.get('date').strftime('%Y-%m-%d'))
|
||||
self.sheet.write_datetime(self.row_pos, 1, date,
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('journal_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('account_name') or '',
|
||||
self.line_header_light)
|
||||
detail_column = 4
|
||||
detail_total = 0.0
|
||||
for period_index in period_dict:
|
||||
amount = float(sub_line.get('range_%s' % period_index) or 0.0)
|
||||
self.sheet.write_number(self.row_pos, detail_column, amount, self.line_header_light_period)
|
||||
detail_total += amount
|
||||
detail_column += 1
|
||||
self.sheet.write_number(self.row_pos, detail_column, detail_total, self.line_header_light_period)
|
||||
|
||||
|
||||
|
||||
self.row_pos += 1
|
||||
k = 4
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_period.num_format = currency_id.excel_format
|
||||
self.line_header_total.num_format = currency_id.excel_format
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('Partner Ageing')
|
||||
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 15)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 3, 15)
|
||||
self.sheet.set_column(3, 3, 15)
|
||||
self.sheet.set_column(4, 4, 15)
|
||||
self.sheet.set_column(5, 5, 15)
|
||||
self.sheet.set_column(6, 6, 15)
|
||||
self.sheet.set_column(7, 7, 15)
|
||||
self.sheet.set_column(8, 8, 15)
|
||||
self.sheet.set_column(9, 9, 15)
|
||||
self.sheet.set_column(10, 10, 15)
|
||||
self.sheet.set_column(11, 11, 15)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
self.record = record
|
||||
|
||||
self.sheet.set_zoom(75)
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 11, 'Partner Ageing'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, ageing_lines, period_dict, period_list = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, period_dict, period_list, ageing_lines, filters)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
from odoo import api, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class InsReportPartnerLedger(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.partner_ledger'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
|
||||
# If it is a call from Js window
|
||||
if self.env.context.get('from_js'):
|
||||
if data.get('js_data'):
|
||||
data.update({'Ledger_data': data.get('js_data')[1],
|
||||
'Filters': data.get('js_data')[0],
|
||||
})
|
||||
return data
|
||||
@@ -0,0 +1,329 @@
|
||||
# _*_ coding: utf-8
|
||||
from odoo import models, fields, api, _
|
||||
|
||||
from datetime import datetime
|
||||
try:
|
||||
from odoo.addons.report_xlsx.report.report_xlsx import ReportXlsx
|
||||
from xlsxwriter.utility import xl_rowcol_to_cell
|
||||
except ImportError:
|
||||
ReportXlsx = object
|
||||
|
||||
DATE_DICT = {
|
||||
'%m/%d/%Y' : 'mm/dd/yyyy',
|
||||
'%Y/%m/%d' : 'yyyy/mm/dd',
|
||||
'%m/%d/%y' : 'mm/dd/yy',
|
||||
'%d/%m/%Y' : 'dd/mm/yyyy',
|
||||
'%d/%m/%y' : 'dd/mm/yy',
|
||||
'%d-%m-%Y' : 'dd-mm-yyyy',
|
||||
'%d-%m-%y' : 'dd-mm-yy',
|
||||
'%m-%d-%Y' : 'mm-dd-yyyy',
|
||||
'%m-%d-%y' : 'mm-dd-yy',
|
||||
'%Y-%m-%d' : 'yyyy-mm-dd',
|
||||
'%f/%e/%Y' : 'm/d/yyyy',
|
||||
'%f/%e/%y' : 'm/d/yy',
|
||||
'%e/%f/%Y' : 'd/m/yyyy',
|
||||
'%e/%f/%y' : 'd/m/yy',
|
||||
'%f-%e-%Y' : 'm-d-yyyy',
|
||||
'%f-%e-%y' : 'm-d-yy',
|
||||
'%e-%f-%Y' : 'd-m-yyyy',
|
||||
'%e-%f-%y' : 'd-m-yy'
|
||||
}
|
||||
|
||||
class InsPartnerLedgerXlsx(models.AbstractModel):
|
||||
_name = 'report.account_dynamic_reports_jabung.ins_partner_ledger_xlsx'
|
||||
_inherit = 'report.report_xlsx.abstract'
|
||||
|
||||
def _define_formats(self, workbook):
|
||||
""" Add cell formats to current workbook.
|
||||
Available formats:
|
||||
* format_title
|
||||
* format_header
|
||||
"""
|
||||
self.format_title = workbook.add_format({
|
||||
'bold': True,
|
||||
'align': 'center',
|
||||
'font_size': 12,
|
||||
'font': 'Arial',
|
||||
'border': False
|
||||
})
|
||||
self.format_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
#'border': True
|
||||
})
|
||||
self.content_header = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'border': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.content_header_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'border': True,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header = workbook.add_format({
|
||||
'bold': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'bottom': True,
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'text_wrap': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_date = workbook.add_format({
|
||||
'bold': False,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'font': 'Arial',
|
||||
})
|
||||
self.line_header_light_initial = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'bottom': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
self.line_header_light_ending = workbook.add_format({
|
||||
'italic': True,
|
||||
'font_size': 10,
|
||||
'align': 'center',
|
||||
'top': True,
|
||||
'font': 'Arial',
|
||||
'valign': 'top'
|
||||
})
|
||||
|
||||
def prepare_report_filters(self, filter):
|
||||
"""It is writing under second page"""
|
||||
self.row_pos_2 += 2
|
||||
if filter:
|
||||
# Date from
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date from'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_from']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Date to'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_datetime(self.row_pos_2, 1, self.convert_to_date(str(filter['date_to']) or ''),
|
||||
self.content_header_date)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Target moves'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['target_moves'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Display accounts'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['display_accounts'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Reconciled'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['reconciled'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Initial Balance'),
|
||||
self.format_header)
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, filter['initial_balance'],
|
||||
self.content_header)
|
||||
self.row_pos_2 += 1
|
||||
|
||||
# Journals
|
||||
self.row_pos_2 += 2
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Journals'),
|
||||
self.format_header)
|
||||
j_list = ', '.join([lt or '' for lt in filter.get('journals')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, j_list,
|
||||
self.content_header)
|
||||
|
||||
# Partners
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partners'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('partners')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Partner Tags
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Partner Tag'),
|
||||
self.format_header)
|
||||
p_list = ', '.join([lt or '' for lt in filter.get('categories')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, p_list,
|
||||
self.content_header)
|
||||
|
||||
# Accounts
|
||||
self.row_pos_2 += 1
|
||||
self.sheet_2.write_string(self.row_pos_2, 0, _('Accounts'),
|
||||
self.format_header)
|
||||
a_list = ', '.join([lt or '' for lt in filter.get('accounts')])
|
||||
self.sheet_2.write_string(self.row_pos_2, 1, a_list,
|
||||
self.content_header)
|
||||
|
||||
def prepare_report_contents(self, data, acc_lines, filter):
|
||||
data = data[0]
|
||||
self.row_pos += 3
|
||||
|
||||
if filter.get('include_details', False):
|
||||
self.sheet.write_string(self.row_pos, 0, _('Date'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 1, _('JRNL'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 2, _('Partner'),
|
||||
self.format_header)
|
||||
# self.sheet.write_string(self.row_pos, 3, _('Ref'),
|
||||
# self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 3, _('Move'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 4, _('Entry Label'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
else:
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, _('Partner'), self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 5, _('Debit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 6, _('Credit'),
|
||||
self.format_header)
|
||||
self.sheet.write_string(self.row_pos, 7, _('Balance'),
|
||||
self.format_header)
|
||||
|
||||
if acc_lines:
|
||||
for line in acc_lines:
|
||||
self.row_pos += 1
|
||||
self.sheet.merge_range(self.row_pos, 0, self.row_pos, 4, acc_lines[line].get('name'), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')), self.line_header)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')), self.line_header)
|
||||
|
||||
if filter.get('include_details', False):
|
||||
|
||||
count, offset, sub_lines = self.record.build_detailed_move_lines(offset=0, partner=line,
|
||||
fetch_range=1000000)
|
||||
|
||||
for sub_line in sub_lines:
|
||||
if sub_line.get('move_name') == 'Initial Balance':
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_initial)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_initial)
|
||||
elif sub_line.get('move_name') not in ['Initial Balance','Ending Balance']:
|
||||
self.row_pos += 1
|
||||
self.sheet.write_datetime(self.row_pos, 0, self.convert_to_date(sub_line.get('ldate')),
|
||||
self.line_header_light_date)
|
||||
self.sheet.write_string(self.row_pos, 1, sub_line.get('lcode'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 2, sub_line.get('account_name') or '',
|
||||
self.line_header_light)
|
||||
# self.sheet.write_string(self.row_pos, 3, sub_line.get('lref') or '',
|
||||
# self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 3, sub_line.get('move_name'),
|
||||
self.line_header_light)
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('lname') or '',
|
||||
self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 5,
|
||||
float(sub_line.get('debit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 6,
|
||||
float(sub_line.get('credit')),self.line_header_light)
|
||||
self.sheet.write_number(self.row_pos, 7,
|
||||
float(sub_line.get('balance')),self.line_header_light)
|
||||
else: # Ending Balance
|
||||
self.row_pos += 1
|
||||
self.sheet.write_string(self.row_pos, 4, sub_line.get('move_name'),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 5, float(acc_lines[line].get('debit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 6, float(acc_lines[line].get('credit')),
|
||||
self.line_header_light_ending)
|
||||
self.sheet.write_number(self.row_pos, 7, float(acc_lines[line].get('balance')),
|
||||
self.line_header_light_ending)
|
||||
|
||||
def _format_float_and_dates(self, currency_id, lang_id):
|
||||
|
||||
self.line_header.num_format = currency_id.excel_format
|
||||
self.line_header_light.num_format = currency_id.excel_format
|
||||
self.line_header_light_initial.num_format = currency_id.excel_format
|
||||
self.line_header_light_ending.num_format = currency_id.excel_format
|
||||
|
||||
|
||||
self.line_header_light_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
self.content_header_date.num_format = DATE_DICT.get(lang_id.date_format, 'dd/mm/yyyy')
|
||||
|
||||
def convert_to_date(self, datestring=False):
|
||||
if datestring:
|
||||
datestring = fields.Date.from_string(datestring).strftime(self.language_id.date_format)
|
||||
return datetime.strptime(datestring, self.language_id.date_format)
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_xlsx_report(self, workbook, data, record):
|
||||
|
||||
self._define_formats(workbook)
|
||||
self.row_pos = 0
|
||||
self.row_pos_2 = 0
|
||||
|
||||
self.record = record # Wizard object
|
||||
|
||||
self.sheet = workbook.add_worksheet('Partner Ledger')
|
||||
self.sheet_2 = workbook.add_worksheet('Filters')
|
||||
self.sheet.set_column(0, 0, 12)
|
||||
self.sheet.set_column(1, 1, 12)
|
||||
self.sheet.set_column(2, 2, 30)
|
||||
self.sheet.set_column(3, 3, 18)
|
||||
self.sheet.set_column(4, 4, 30)
|
||||
self.sheet.set_column(5, 5, 10)
|
||||
self.sheet.set_column(6, 6, 10)
|
||||
self.sheet.set_column(7, 7, 10)
|
||||
|
||||
self.sheet_2.set_column(0, 0, 35)
|
||||
self.sheet_2.set_column(1, 1, 25)
|
||||
self.sheet_2.set_column(2, 2, 25)
|
||||
self.sheet_2.set_column(3, 3, 25)
|
||||
self.sheet_2.set_column(4, 4, 25)
|
||||
self.sheet_2.set_column(5, 5, 25)
|
||||
self.sheet_2.set_column(6, 6, 25)
|
||||
|
||||
self.sheet.freeze_panes(4, 0)
|
||||
|
||||
self.sheet.screen_gridlines = False
|
||||
self.sheet_2.screen_gridlines = False
|
||||
self.sheet_2.protect()
|
||||
|
||||
# For Formating purpose
|
||||
lang = self.env.user.lang
|
||||
self.language_id = self.env['res.lang'].search([('code','=',lang)])[0]
|
||||
self._format_float_and_dates(self.env.user.company_id.currency_id, self.language_id)
|
||||
|
||||
if record:
|
||||
data = record.read()
|
||||
self.sheet.merge_range(0, 0, 0, 8, 'Partner Ledger'+' - '+record._get_company_display_name(), self.format_title)
|
||||
self.dateformat = self.env.user.lang
|
||||
filters, account_lines = record.get_report_datas()
|
||||
# Filter section
|
||||
self.prepare_report_filters(filters)
|
||||
# Content section
|
||||
self.prepare_report_contents(data, account_lines, filters)
|
||||
|
||||