Restore proprietary modules to tracking for private self-hosted Gitea repo

This commit is contained in:
2026-04-23 08:18:07 +07:00
parent 19127702bd
commit c24fc7bc47
583 changed files with 50286 additions and 15 deletions
-15
View File
@@ -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.*
+27
View File
@@ -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.
+4
View File
@@ -0,0 +1,4 @@
from . import models
from . import wizard
from . import report
from . import controllers
+51
View File
@@ -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>
File diff suppressed because it is too large Load Diff
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ins_account_financial_report ins.account.financial.report model_ins_account_financial_report account.group_account_user 1 1 1 1
3 access_ins_general_ledger ins.general.ledger model_ins_general_ledger account.group_account_user 1 1 1 1
4 access_ins_financial_report ins.financial.report model_ins_financial_report account.group_account_user 1 1 1 1
5 access_ins_partner_ageing ins.partner.ageing model_ins_partner_ageing account.group_account_user 1 1 1 1
6 access_ins_partner_ledger ins.partner.ledger model_ins_partner_ledger account.group_account_user 1 1 1 1
7 access_ins_trial_balance ins.trial.balance model_ins_trial_balance account.group_account_user 1 1 1 1
8 access_ins_faspe_consolidated_wizard ins.faspe.consolidated.wizard model_ins_faspe_consolidated_wizard account.group_account_user 1 1 1 1
9 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
10 access_ins_faspe_consolidated_report ins.faspe.consolidated.report model_ins_faspe_consolidated_report account.group_account_user 1 1 1 1
11 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

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);
},
});
});
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -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;
}
};
}
File diff suppressed because it is too large Load Diff
+8
View File
@@ -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') &gt; 3">
<t t-set="style" t-value="'font-weight: normal;'"/>
</t>
<t t-if="not a.get('level') &gt; 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') &gt; 3">
<t t-set="style" t-value="'font-weight: normal;'"/>
</t>
<t t-if="not a.get('level') &gt; 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>&amp;nbsp;</span>
<span>&amp;nbsp;</span>
<span>&amp;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>
+108
View File
@@ -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>
File diff suppressed because it is too large Load Diff
@@ -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>
File diff suppressed because it is too large Load Diff
@@ -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>
+27
View File
@@ -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>
File diff suppressed because it is too large Load Diff
@@ -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)

Some files were not shown because too many files have changed in this diff Show More