Joan Sisquella
5 years ago
46 changed files with 5475 additions and 7967 deletions
-
10account_financial_report/__manifest__.py
-
22account_financial_report/models/account_group.py
-
5account_financial_report/readme/CONTRIBUTORS.rst
-
1account_financial_report/report/__init__.py
-
23account_financial_report/report/abstract_report.py
-
188account_financial_report/report/abstract_report_xlsx.py
-
972account_financial_report/report/aged_partner_balance.py
-
181account_financial_report/report/aged_partner_balance_xlsx.py
-
2353account_financial_report/report/general_ledger.py
-
176account_financial_report/report/general_ledger_xlsx.py
-
1168account_financial_report/report/journal_ledger.py
-
112account_financial_report/report/journal_ledger_xlsx.py
-
1209account_financial_report/report/open_items.py
-
121account_financial_report/report/open_items_xlsx.py
-
438account_financial_report/report/templates/aged_partner_balance.xml
-
347account_financial_report/report/templates/general_ledger.xml
-
110account_financial_report/report/templates/journal_ledger.xml
-
106account_financial_report/report/templates/open_items.xml
-
1031account_financial_report/report/templates/trial_balance.xml
-
172account_financial_report/report/templates/vat_report.xml
-
1191account_financial_report/report/trial_balance.py
-
110account_financial_report/report/trial_balance_xlsx.py
-
526account_financial_report/report/vat_report.py
-
38account_financial_report/report/vat_report_xlsx.py
-
144account_financial_report/reports.xml
-
4account_financial_report/tests/__init__.py
-
399account_financial_report/tests/abstract_test.py
-
78account_financial_report/tests/abstract_test_foreign_currency.py
-
75account_financial_report/tests/abstract_test_tax_report.py
-
41account_financial_report/tests/test_aged_partner_balance.py
-
561account_financial_report/tests/test_general_ledger.py
-
206account_financial_report/tests/test_journal_ledger.py
-
37account_financial_report/tests/test_open_items.py
-
649account_financial_report/tests/test_trial_balance.py
-
243account_financial_report/tests/test_vat_report.py
-
16account_financial_report/view/report_template.xml
-
49account_financial_report/wizard/aged_partner_balance_wizard.py
-
4account_financial_report/wizard/aged_partner_balance_wizard_view.xml
-
72account_financial_report/wizard/general_ledger_wizard.py
-
79account_financial_report/wizard/journal_ledger_wizard.py
-
53account_financial_report/wizard/open_items_wizard.py
-
1account_financial_report/wizard/open_items_wizard_view.xml
-
67account_financial_report/wizard/trial_balance_wizard.py
-
2account_financial_report/wizard/trial_balance_wizard_view.xml
-
36account_financial_report/wizard/vat_report_wizard.py
-
2requirements.txt
@ -1,23 +0,0 @@ |
|||
# Copyright 2018 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models |
|||
from psycopg2.extensions import AsIs |
|||
|
|||
|
|||
class AbstractReport(models.AbstractModel): |
|||
_name = 'account_financial_report_abstract' |
|||
_description = 'Abstract Report' |
|||
|
|||
def _transient_clean_rows_older_than(self, seconds): |
|||
assert self._transient, \ |
|||
"Model %s is not transient, it cannot be vacuumed!" % self._name |
|||
# Never delete rows used in last 5 minutes |
|||
seconds = max(seconds, 300) |
|||
query = ( |
|||
"DELETE FROM %s" |
|||
" WHERE COALESCE(" |
|||
"write_date, create_date, (now() at time zone 'UTC'))" |
|||
"::timestamp < ((now() at time zone 'UTC') - interval %s)" |
|||
) |
|||
self.env.cr.execute(query, (AsIs(self._table), "%s seconds" % seconds)) |
@ -1,633 +1,375 @@ |
|||
# © 2016 Julien Coux (Camptocamp) |
|||
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import models, fields, api |
|||
from odoo import models, api |
|||
from odoo.tools import float_is_zero |
|||
from datetime import date, datetime, timedelta |
|||
import pandas as pd |
|||
|
|||
|
|||
class AgedPartnerBalanceReport(models.TransientModel): |
|||
""" Here, we just define class fields. |
|||
For methods, go more bottom at this file. |
|||
|
|||
The class hierarchy is : |
|||
* AgedPartnerBalanceReport |
|||
** AgedPartnerBalanceReportAccount |
|||
*** AgedPartnerBalanceReportPartner |
|||
**** AgedPartnerBalanceReportLine |
|||
**** AgedPartnerBalanceReportMoveLine |
|||
If "show_move_line_details" is selected |
|||
""" |
|||
|
|||
_name = 'report_aged_partner_balance' |
|||
_inherit = 'account_financial_report_abstract' |
|||
|
|||
# Filters fields, used for data computation |
|||
date_at = fields.Date() |
|||
only_posted_moves = fields.Boolean() |
|||
company_id = fields.Many2one(comodel_name='res.company') |
|||
filter_account_ids = fields.Many2many(comodel_name='account.account') |
|||
filter_partner_ids = fields.Many2many(comodel_name='res.partner') |
|||
show_move_line_details = fields.Boolean() |
|||
|
|||
# Open Items Report Data fields, used as base for compute the data reports |
|||
open_items_id = fields.Many2one(comodel_name='report_open_items') |
|||
|
|||
# Data fields, used to browse report data |
|||
account_ids = fields.One2many( |
|||
comodel_name='report_aged_partner_balance_account', |
|||
inverse_name='report_id' |
|||
) |
|||
|
|||
|
|||
class AgedPartnerBalanceReportAccount(models.TransientModel): |
|||
_name = 'report_aged_partner_balance_account' |
|||
_inherit = 'account_financial_report_abstract' |
|||
_order = 'code ASC' |
|||
|
|||
report_id = fields.Many2one( |
|||
comodel_name='report_aged_partner_balance', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used to keep link with real object |
|||
account_id = fields.Many2one( |
|||
'account.account', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used for report display |
|||
code = fields.Char() |
|||
name = fields.Char() |
|||
|
|||
cumul_amount_residual = fields.Float(digits=(16, 2)) |
|||
cumul_current = fields.Float(digits=(16, 2)) |
|||
cumul_age_30_days = fields.Float(digits=(16, 2)) |
|||
cumul_age_60_days = fields.Float(digits=(16, 2)) |
|||
cumul_age_90_days = fields.Float(digits=(16, 2)) |
|||
cumul_age_120_days = fields.Float(digits=(16, 2)) |
|||
cumul_older = fields.Float(digits=(16, 2)) |
|||
|
|||
percent_current = fields.Float(digits=(16, 2)) |
|||
percent_age_30_days = fields.Float(digits=(16, 2)) |
|||
percent_age_60_days = fields.Float(digits=(16, 2)) |
|||
percent_age_90_days = fields.Float(digits=(16, 2)) |
|||
percent_age_120_days = fields.Float(digits=(16, 2)) |
|||
percent_older = fields.Float(digits=(16, 2)) |
|||
|
|||
# Data fields, used to browse report data |
|||
partner_ids = fields.One2many( |
|||
comodel_name='report_aged_partner_balance_partner', |
|||
inverse_name='report_account_id' |
|||
) |
|||
|
|||
|
|||
class AgedPartnerBalanceReportPartner(models.TransientModel): |
|||
_name = 'report_aged_partner_balance_partner' |
|||
_inherit = 'account_financial_report_abstract' |
|||
|
|||
report_account_id = fields.Many2one( |
|||
comodel_name='report_aged_partner_balance_account', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used to keep link with real object |
|||
partner_id = fields.Many2one( |
|||
'res.partner', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used for report display |
|||
name = fields.Char() |
|||
|
|||
# Data fields, used to browse report data |
|||
move_line_ids = fields.One2many( |
|||
comodel_name='report_aged_partner_balance_move_line', |
|||
inverse_name='report_partner_id' |
|||
) |
|||
line_ids = fields.One2many( |
|||
comodel_name='report_aged_partner_balance_line', |
|||
inverse_name='report_partner_id' |
|||
) |
|||
class AgedPartnerBalanceReport(models.AbstractModel): |
|||
_name = 'report.account_financial_report.aged_partner_balance' |
|||
_description = "Aged Partner Balance Report" |
|||
|
|||
@api.model |
|||
def _generate_order_by(self, order_spec, query): |
|||
"""Custom order to display "No partner allocated" at last position.""" |
|||
return """ |
|||
ORDER BY |
|||
CASE |
|||
WHEN |
|||
"report_aged_partner_balance_partner"."partner_id" IS NOT NULL |
|||
THEN 0 |
|||
ELSE 1 |
|||
END, |
|||
"report_aged_partner_balance_partner"."name" |
|||
""" |
|||
|
|||
|
|||
class AgedPartnerBalanceReportLine(models.TransientModel): |
|||
_name = 'report_aged_partner_balance_line' |
|||
_inherit = 'account_financial_report_abstract' |
|||
|
|||
report_partner_id = fields.Many2one( |
|||
comodel_name='report_aged_partner_balance_partner', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used for report display |
|||
partner = fields.Char() |
|||
amount_residual = fields.Float(digits=(16, 2)) |
|||
current = fields.Float(digits=(16, 2)) |
|||
age_30_days = fields.Float(digits=(16, 2)) |
|||
age_60_days = fields.Float(digits=(16, 2)) |
|||
age_90_days = fields.Float(digits=(16, 2)) |
|||
age_120_days = fields.Float(digits=(16, 2)) |
|||
older = fields.Float(digits=(16, 2)) |
|||
|
|||
|
|||
class AgedPartnerBalanceReportMoveLine(models.TransientModel): |
|||
_name = 'report_aged_partner_balance_move_line' |
|||
_inherit = 'account_financial_report_abstract' |
|||
|
|||
report_partner_id = fields.Many2one( |
|||
comodel_name='report_aged_partner_balance_partner', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used to keep link with real object |
|||
move_line_id = fields.Many2one('account.move.line') |
|||
|
|||
# Data fields, used for report display |
|||
date = fields.Date() |
|||
date_due = fields.Date() |
|||
entry = fields.Char() |
|||
journal = fields.Char() |
|||
account = fields.Char() |
|||
partner = fields.Char() |
|||
label = fields.Char() |
|||
def _initialize_account(self, ag_pb_data, acc_id): |
|||
ag_pb_data[acc_id] = {} |
|||
ag_pb_data[acc_id]['id'] = acc_id |
|||
ag_pb_data[acc_id]['residual'] = 0.0 |
|||
ag_pb_data[acc_id]['current'] = 0.0 |
|||
ag_pb_data[acc_id]['30_days'] = 0.0 |
|||
ag_pb_data[acc_id]['60_days'] = 0.0 |
|||
ag_pb_data[acc_id]['90_days'] = 0.0 |
|||
ag_pb_data[acc_id]['120_days'] = 0.0 |
|||
ag_pb_data[acc_id]['older'] = 0.0 |
|||
return ag_pb_data |
|||
|
|||
amount_residual = fields.Float(digits=(16, 2)) |
|||
current = fields.Float(digits=(16, 2)) |
|||
age_30_days = fields.Float(digits=(16, 2)) |
|||
age_60_days = fields.Float(digits=(16, 2)) |
|||
age_90_days = fields.Float(digits=(16, 2)) |
|||
age_120_days = fields.Float(digits=(16, 2)) |
|||
older = fields.Float(digits=(16, 2)) |
|||
|
|||
|
|||
class AgedPartnerBalanceReportCompute(models.TransientModel): |
|||
""" Here, we just define methods. |
|||
For class fields, go more top at this file. |
|||
""" |
|||
@api.model |
|||
def _initialize_partner(self, ag_pb_data, acc_id, prt_id): |
|||
ag_pb_data[acc_id][prt_id] = {} |
|||
ag_pb_data[acc_id][prt_id]['id'] = acc_id |
|||
ag_pb_data[acc_id][prt_id]['residual'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['current'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['30_days'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['60_days'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['90_days'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['120_days'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['older'] = 0.0 |
|||
ag_pb_data[acc_id][prt_id]['move_lines'] = [] |
|||
return ag_pb_data |
|||
|
|||
def _get_journals_data(self, journals_ids): |
|||
journals = self.env['account.journal'].browse(journals_ids) |
|||
journals_data = {} |
|||
for journal in journals: |
|||
journals_data.update({journal.id: {'id': journal.id, |
|||
'code': journal.code}}) |
|||
return journals_data |
|||
|
|||
def _get_accounts_data(self, accounts_ids): |
|||
accounts = self.env['account.account'].browse(accounts_ids) |
|||
accounts_data = {} |
|||
for account in accounts: |
|||
accounts_data.update({account.id: {'id': account.id, |
|||
'code': account.code, |
|||
'name': account.name}}) |
|||
return accounts_data |
|||
|
|||
_inherit = 'report_aged_partner_balance' |
|||
@api.model |
|||
def _get_move_lines_domain(self, company_id, account_ids, partner_ids, |
|||
only_posted_moves): |
|||
domain = [('account_id', 'in', account_ids), |
|||
('company_id', '=', company_id), |
|||
('reconciled', '=', False)] |
|||
if partner_ids: |
|||
domain += [('partner_id', 'in', partner_ids)] |
|||
if only_posted_moves: |
|||
domain += [('move_id.state', '=', 'posted')] |
|||
return domain |
|||
|
|||
@api.multi |
|||
def print_report(self, report_type): |
|||
self.ensure_one() |
|||
if report_type == 'xlsx': |
|||
report_name = 'a_f_r.report_aged_partner_balance_xlsx' |
|||
@api.model |
|||
def _calculate_amounts(self, ag_pb_data, acc_id, prt_id, residual, |
|||
due_date, date_at_object): |
|||
ag_pb_data[acc_id]['residual'] += residual |
|||
ag_pb_data[acc_id][prt_id]['residual'] += residual |
|||
today = date_at_object |
|||
if not due_date or today <= due_date: |
|||
ag_pb_data[acc_id]['current'] += residual |
|||
ag_pb_data[acc_id][prt_id]['current'] += residual |
|||
elif today <= due_date + timedelta(days=30): |
|||
ag_pb_data[acc_id]['30_days'] += residual |
|||
ag_pb_data[acc_id][prt_id]['30_days'] += residual |
|||
elif today <= due_date + timedelta(days=60): |
|||
ag_pb_data[acc_id]['60_days'] += residual |
|||
ag_pb_data[acc_id][prt_id]['60_days'] += residual |
|||
elif today <= due_date + timedelta(days=90): |
|||
ag_pb_data[acc_id]['90_days'] += residual |
|||
ag_pb_data[acc_id][prt_id]['90_days'] += residual |
|||
elif today <= due_date + timedelta(days=120): |
|||
ag_pb_data[acc_id]['120_days'] += residual |
|||
ag_pb_data[acc_id][prt_id]['120_days'] += residual |
|||
else: |
|||
report_name = 'account_financial_report.' \ |
|||
'report_aged_partner_balance_qweb' |
|||
report = self.env['ir.actions.report'].search( |
|||
[('report_name', '=', report_name), |
|||
('report_type', '=', report_type)], limit=1) |
|||
return report.report_action(self, config=False) |
|||
|
|||
def _get_html(self): |
|||
result = {} |
|||
rcontext = {} |
|||
context = dict(self.env.context) |
|||
report = self.browse(context.get('active_id')) |
|||
if report: |
|||
rcontext['o'] = report |
|||
result['html'] = self.env.ref( |
|||
'account_financial_report.report_aged_partner_balance').render( |
|||
rcontext) |
|||
return result |
|||
ag_pb_data[acc_id]['older'] += residual |
|||
ag_pb_data[acc_id][prt_id]['older'] += residual |
|||
return ag_pb_data |
|||
|
|||
def _get_account_partial_reconciled(self, company_id, date_at_object): |
|||
domain = [('max_date', '>=', date_at_object), |
|||
('company_id', '=', company_id)] |
|||
fields = ['debit_move_id', 'credit_move_id', 'amount'] |
|||
accounts_partial_reconcile = \ |
|||
self.env['account.partial.reconcile'].search_read( |
|||
domain=domain, |
|||
fields=fields |
|||
) |
|||
debit_amount = {} |
|||
credit_amount = {} |
|||
for account_partial_reconcile_data in accounts_partial_reconcile: |
|||
debit_move_id = account_partial_reconcile_data['debit_move_id'][0] |
|||
credit_move_id = account_partial_reconcile_data['credit_move_id'][0] |
|||
if debit_move_id not in debit_amount.keys(): |
|||
debit_amount[debit_move_id] = 0.0 |
|||
debit_amount[debit_move_id] += \ |
|||
account_partial_reconcile_data['amount'] |
|||
if credit_move_id not in credit_amount.keys(): |
|||
credit_amount[credit_move_id] = 0.0 |
|||
credit_amount[credit_move_id] += \ |
|||
account_partial_reconcile_data['amount'] |
|||
account_partial_reconcile_data.update({ |
|||
'debit_move_id': debit_move_id, |
|||
'credit_move_id': credit_move_id, |
|||
}) |
|||
return accounts_partial_reconcile, debit_amount, credit_amount |
|||
|
|||
@api.model |
|||
def get_html(self, given_context=None): |
|||
return self._get_html() |
|||
|
|||
def _prepare_report_open_items(self): |
|||
self.ensure_one() |
|||
return { |
|||
'date_at': self.date_at, |
|||
'only_posted_moves': self.only_posted_moves, |
|||
'company_id': self.company_id.id, |
|||
'filter_account_ids': [(6, 0, self.filter_account_ids.ids)], |
|||
'filter_partner_ids': [(6, 0, self.filter_partner_ids.ids)], |
|||
} |
|||
|
|||
@api.multi |
|||
def compute_data_for_report(self): |
|||
self.ensure_one() |
|||
# Compute Open Items Report Data. |
|||
# The data of Aged Partner Balance Report |
|||
# are based on Open Items Report data. |
|||
model = self.env['report_open_items'] |
|||
self.open_items_id = model.create(self._prepare_report_open_items()) |
|||
self.open_items_id.compute_data_for_report() |
|||
|
|||
# Compute report data |
|||
self._inject_account_values() |
|||
self._inject_partner_values() |
|||
self._inject_line_values() |
|||
self._inject_line_values(only_empty_partner_line=True) |
|||
if self.show_move_line_details: |
|||
self._inject_move_line_values() |
|||
self._inject_move_line_values(only_empty_partner_line=True) |
|||
self._compute_accounts_cumul() |
|||
# Refresh cache because all data are computed with SQL requests |
|||
self.invalidate_cache() |
|||
|
|||
def _inject_account_values(self): |
|||
"""Inject report values for report_aged_partner_balance_account""" |
|||
query_inject_account = """ |
|||
INSERT INTO |
|||
report_aged_partner_balance_account |
|||
( |
|||
report_id, |
|||
create_uid, |
|||
create_date, |
|||
account_id, |
|||
code, |
|||
name |
|||
) |
|||
SELECT |
|||
%s AS report_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
rao.account_id, |
|||
rao.code, |
|||
rao.name |
|||
FROM |
|||
report_open_items_account rao |
|||
WHERE |
|||
rao.report_id = %s |
|||
""" |
|||
query_inject_account_params = ( |
|||
self.id, |
|||
self.env.uid, |
|||
self.open_items_id.id, |
|||
def _get_new_move_lines_domain(self, new_ml_ids, account_ids, company_id, |
|||
partner_ids, only_posted_moves): |
|||
domain = [('account_id', 'in', account_ids), |
|||
('company_id', '=', company_id), |
|||
('id', 'in', new_ml_ids)] |
|||
if partner_ids: |
|||
domain += [('partner_id', 'in', partner_ids)] |
|||
if only_posted_moves: |
|||
domain += [('move_id.state', '=', 'posted')] |
|||
return domain |
|||
|
|||
def _recalculate_move_lines(self, move_lines, debit_ids, credit_ids, |
|||
debit_amount, credit_amount, ml_ids, |
|||
account_ids, company_id, partner_ids, |
|||
only_posted_moves): |
|||
reconciled_ids = list(debit_ids) + list(credit_ids) |
|||
new_ml_ids = [] |
|||
for reconciled_id in reconciled_ids: |
|||
if reconciled_id not in ml_ids and reconciled_id not in new_ml_ids: |
|||
new_ml_ids += [reconciled_id] |
|||
new_domain = self._get_new_move_lines_domain(new_ml_ids, account_ids, |
|||
company_id, partner_ids, |
|||
only_posted_moves) |
|||
ml_fields = [ |
|||
'id', 'name', 'date', 'move_id', 'journal_id', 'account_id', |
|||
'partner_id', 'amount_residual', 'date_maturity', 'ref', |
|||
'reconciled'] |
|||
new_move_lines = self.env['account.move.line'].search_read( |
|||
domain=new_domain, fields=ml_fields |
|||
) |
|||
self.env.cr.execute(query_inject_account, query_inject_account_params) |
|||
|
|||
def _inject_partner_values(self): |
|||
"""Inject report values for report_aged_partner_balance_partner""" |
|||
query_inject_partner = """ |
|||
INSERT INTO |
|||
report_aged_partner_balance_partner |
|||
( |
|||
report_account_id, |
|||
create_uid, |
|||
create_date, |
|||
partner_id, |
|||
name |
|||
) |
|||
SELECT |
|||
ra.id AS report_account_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
rpo.partner_id, |
|||
rpo.name |
|||
FROM |
|||
report_open_items_partner rpo |
|||
INNER JOIN |
|||
report_open_items_account rao ON rpo.report_account_id = rao.id |
|||
INNER JOIN |
|||
report_aged_partner_balance_account ra ON rao.code = ra.code |
|||
WHERE |
|||
rao.report_id = %s |
|||
AND ra.report_id = %s |
|||
""" |
|||
query_inject_partner_params = ( |
|||
self.env.uid, |
|||
self.open_items_id.id, |
|||
self.id, |
|||
move_lines = move_lines + new_move_lines |
|||
for move_line in move_lines: |
|||
ml_id = move_line['id'] |
|||
if ml_id in debit_ids: |
|||
move_line['amount_residual'] += debit_amount[ml_id] |
|||
if ml_id in credit_ids: |
|||
move_line['amount_residual'] -= credit_amount[ml_id] |
|||
return move_lines |
|||
|
|||
def _get_move_lines_data( |
|||
self, company_id, account_ids, partner_ids, date_at_object, |
|||
only_posted_moves, show_move_line_details): |
|||
domain = self._get_move_lines_domain(company_id, account_ids, |
|||
partner_ids, only_posted_moves) |
|||
ml_fields = [ |
|||
'id', 'name', 'date', 'move_id', 'journal_id', 'account_id', |
|||
'partner_id', 'amount_residual', 'date_maturity', 'ref', |
|||
'reconciled'] |
|||
move_lines = self.env['account.move.line'].search_read( |
|||
domain=domain, fields=ml_fields |
|||
) |
|||
self.env.cr.execute(query_inject_partner, query_inject_partner_params) |
|||
|
|||
def _inject_line_values(self, only_empty_partner_line=False): |
|||
""" Inject report values for report_aged_partner_balance_line. |
|||
ml_ids = set(pd.DataFrame(move_lines).id.to_list()) |
|||
journals_ids = set() |
|||
partners_ids = set() |
|||
partners_data = {} |
|||
ag_pb_data = {} |
|||
if date_at_object < date.today(): |
|||
acc_partial_rec, debit_amount, credit_amount = \ |
|||
self._get_account_partial_reconciled(company_id, date_at_object) |
|||
if acc_partial_rec: |
|||
acc_partial_rec_data = pd.DataFrame(acc_partial_rec) |
|||
debit_ids = set(acc_partial_rec_data.debit_move_id.to_list()) |
|||
credit_ids = set(acc_partial_rec_data.credit_move_id.to_list()) |
|||
move_lines = self._recalculate_move_lines( |
|||
move_lines, debit_ids, credit_ids, |
|||
debit_amount, credit_amount, ml_ids, account_ids, |
|||
company_id, partner_ids, only_posted_moves |
|||
) |
|||
moves_lines_to_remove = [] |
|||
for move_line in move_lines: |
|||
if move_line['date'] > date_at_object or \ |
|||
float_is_zero(move_line['amount_residual'], |
|||
precision_digits=2): |
|||
moves_lines_to_remove.append(move_line) |
|||
if len(moves_lines_to_remove) > 0: |
|||
for move_line_to_remove in moves_lines_to_remove: |
|||
move_lines.remove(move_line_to_remove) |
|||
for move_line in move_lines: |
|||
journals_ids.add(move_line['journal_id'][0]) |
|||
acc_id = move_line['account_id'][0] |
|||
if move_line['partner_id']: |
|||
prt_id = move_line['partner_id'][0] |
|||
prt_name = move_line['partner_id'][1] |
|||
else: |
|||
prt_id = 0 |
|||
prt_name = "" |
|||
if prt_id not in partners_ids: |
|||
partners_data.update({ |
|||
prt_id: {'id': prt_id, 'name': prt_name} |
|||
}) |
|||
partners_ids.add(prt_id) |
|||
if acc_id not in ag_pb_data.keys(): |
|||
ag_pb_data = self._initialize_account(ag_pb_data, acc_id) |
|||
if prt_id not in ag_pb_data[acc_id]: |
|||
ag_pb_data = self._initialize_partner(ag_pb_data, acc_id, |
|||
prt_id) |
|||
move_line_data = {} |
|||
if show_move_line_details: |
|||
move_line_data.update({ |
|||
'date': move_line['date'], |
|||
'entry': move_line['move_id'][1], |
|||
'jnl_id': move_line['journal_id'][0], |
|||
'acc_id': acc_id, |
|||
'partner': prt_name, |
|||
'ref': move_line['ref'], |
|||
'due_date': move_line['date_maturity'], |
|||
'residual': move_line['amount_residual'], |
|||
}) |
|||
ag_pb_data[acc_id][prt_id]['move_lines'].append(move_line_data) |
|||
ag_pb_data = self._calculate_amounts( |
|||
ag_pb_data, acc_id, prt_id, move_line['amount_residual'], |
|||
move_line['date_maturity'], date_at_object) |
|||
journals_data = self._get_journals_data(list(journals_ids)) |
|||
accounts_data = self._get_accounts_data(ag_pb_data.keys()) |
|||
return ag_pb_data, accounts_data, partners_data, journals_data |
|||
|
|||
The "only_empty_partner_line" value is used |
|||
to compute data without partner. |
|||
""" |
|||
query_inject_line = """ |
|||
WITH |
|||
date_range AS |
|||
( |
|||
SELECT |
|||
DATE %s AS date_current, |
|||
DATE %s - INTEGER '30' AS date_less_30_days, |
|||
DATE %s - INTEGER '60' AS date_less_60_days, |
|||
DATE %s - INTEGER '90' AS date_less_90_days, |
|||
DATE %s - INTEGER '120' AS date_less_120_days |
|||
) |
|||
INSERT INTO |
|||
report_aged_partner_balance_line |
|||
( |
|||
report_partner_id, |
|||
create_uid, |
|||
create_date, |
|||
partner, |
|||
amount_residual, |
|||
current, |
|||
age_30_days, |
|||
age_60_days, |
|||
age_90_days, |
|||
age_120_days, |
|||
older |
|||
) |
|||
SELECT |
|||
rp.id AS report_partner_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
rp.name, |
|||
SUM(rlo.amount_residual) AS amount_residual, |
|||
SUM( |
|||
CASE |
|||
WHEN rlo.date_due >= date_range.date_current |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS current, |
|||
SUM( |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_30_days |
|||
AND rlo.date_due < date_range.date_current |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS age_30_days, |
|||
SUM( |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_60_days |
|||
AND rlo.date_due < date_range.date_less_30_days |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS age_60_days, |
|||
SUM( |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_90_days |
|||
AND rlo.date_due < date_range.date_less_60_days |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS age_90_days, |
|||
SUM( |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_120_days |
|||
AND rlo.date_due < date_range.date_less_90_days |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS age_120_days, |
|||
SUM( |
|||
CASE |
|||
WHEN rlo.date_due < date_range.date_less_120_days |
|||
THEN rlo.amount_residual |
|||
END |
|||
) AS older |
|||
FROM |
|||
date_range, |
|||
report_open_items_move_line rlo |
|||
INNER JOIN |
|||
report_open_items_partner rpo ON rlo.report_partner_id = rpo.id |
|||
INNER JOIN |
|||
report_open_items_account rao ON rpo.report_account_id = rao.id |
|||
INNER JOIN |
|||
report_aged_partner_balance_account ra ON rao.code = ra.code |
|||
INNER JOIN |
|||
report_aged_partner_balance_partner rp |
|||
ON |
|||
ra.id = rp.report_account_id |
|||
""" |
|||
if not only_empty_partner_line: |
|||
query_inject_line += """ |
|||
AND rpo.partner_id = rp.partner_id |
|||
""" |
|||
elif only_empty_partner_line: |
|||
query_inject_line += """ |
|||
AND rpo.partner_id IS NULL |
|||
AND rp.partner_id IS NULL |
|||
""" |
|||
query_inject_line += """ |
|||
WHERE |
|||
rao.report_id = %s |
|||
AND ra.report_id = %s |
|||
GROUP BY |
|||
rp.id |
|||
""" |
|||
query_inject_line_params = (self.date_at,) * 5 |
|||
query_inject_line_params += ( |
|||
self.env.uid, |
|||
self.open_items_id.id, |
|||
self.id, |
|||
) |
|||
self.env.cr.execute(query_inject_line, query_inject_line_params) |
|||
|
|||
def _inject_move_line_values(self, only_empty_partner_line=False): |
|||
""" Inject report values for report_aged_partner_balance_move_line |
|||
@api.model |
|||
def _compute_maturity_date(self, ml, date_at_object): |
|||
ml.update({ |
|||
'current': 0.0, |
|||
'30_days': 0.0, |
|||
'60_days': 0.0, |
|||
'90_days': 0.0, |
|||
'120_days': 0.0, |
|||
'older': 0.0, |
|||
}) |
|||
due_date = ml['due_date'] |
|||
amount = ml['residual'] |
|||
today = date_at_object |
|||
if not due_date or today <= due_date: |
|||
ml['current'] += amount |
|||
elif today <= due_date + timedelta(days=30): |
|||
ml['30_days'] += amount |
|||
elif today <= due_date + timedelta(days=60): |
|||
ml['60_days'] += amount |
|||
elif today <= due_date + timedelta(days=90): |
|||
ml['90_days'] += amount |
|||
elif today <= due_date + timedelta(days=120): |
|||
ml['120_days'] += amount |
|||
else: |
|||
ml['older'] += amount |
|||
|
|||
def _create_account_list( |
|||
self, ag_pb_data, accounts_data, partners_data, journals_data, |
|||
show_move_line_details, date_at_oject): |
|||
aged_partner_data = [] |
|||
for account in accounts_data.values(): |
|||
acc_id = account['id'] |
|||
account.update({ |
|||
'residual': ag_pb_data[acc_id]['residual'], |
|||
'current': ag_pb_data[acc_id]['current'], |
|||
'30_days': ag_pb_data[acc_id]['30_days'], |
|||
'60_days': ag_pb_data[acc_id]['60_days'], |
|||
'90_days': ag_pb_data[acc_id]['90_days'], |
|||
'120_days': ag_pb_data[acc_id]['120_days'], |
|||
'older': ag_pb_data[acc_id]['older'], |
|||
'partners': [], |
|||
}) |
|||
for prt_id in ag_pb_data[acc_id]: |
|||
if isinstance(prt_id, int): |
|||
partner = { |
|||
'name': partners_data[prt_id]['name'], |
|||
'residual': ag_pb_data[acc_id][prt_id]['residual'], |
|||
'current': ag_pb_data[acc_id][prt_id]['current'], |
|||
'30_days': ag_pb_data[acc_id][prt_id]['30_days'], |
|||
'60_days': ag_pb_data[acc_id][prt_id]['60_days'], |
|||
'90_days': ag_pb_data[acc_id][prt_id]['90_days'], |
|||
'120_days': ag_pb_data[acc_id][prt_id]['120_days'], |
|||
'older': ag_pb_data[acc_id][prt_id]['older'], |
|||
} |
|||
if show_move_line_details: |
|||
move_lines = [] |
|||
for ml in ag_pb_data[acc_id][prt_id]['move_lines']: |
|||
ml.update({ |
|||
'journal': journals_data[ml['jnl_id']]['code'], |
|||
'account': accounts_data[ml['acc_id']]['code'], |
|||
}) |
|||
self._compute_maturity_date(ml, date_at_oject) |
|||
move_lines.append(ml) |
|||
partner.update({ |
|||
'move_lines': move_lines |
|||
}) |
|||
account['partners'].append(partner) |
|||
aged_partner_data.append(account) |
|||
return aged_partner_data |
|||
|
|||
The "only_empty_partner_line" value is used |
|||
to compute data without partner. |
|||
""" |
|||
query_inject_move_line = """ |
|||
WITH |
|||
date_range AS |
|||
( |
|||
SELECT |
|||
DATE %s AS date_current, |
|||
DATE %s - INTEGER '30' AS date_less_30_days, |
|||
DATE %s - INTEGER '60' AS date_less_60_days, |
|||
DATE %s - INTEGER '90' AS date_less_90_days, |
|||
DATE %s - INTEGER '120' AS date_less_120_days |
|||
) |
|||
INSERT INTO |
|||
report_aged_partner_balance_move_line |
|||
( |
|||
report_partner_id, |
|||
create_uid, |
|||
create_date, |
|||
move_line_id, |
|||
date, |
|||
date_due, |
|||
entry, |
|||
journal, |
|||
account, |
|||
partner, |
|||
label, |
|||
amount_residual, |
|||
current, |
|||
age_30_days, |
|||
age_60_days, |
|||
age_90_days, |
|||
age_120_days, |
|||
older |
|||
) |
|||
SELECT |
|||
rp.id AS report_partner_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
rlo.move_line_id, |
|||
rlo.date, |
|||
rlo.date_due, |
|||
rlo.entry, |
|||
rlo.journal, |
|||
rlo.account, |
|||
rlo.partner, |
|||
rlo.label, |
|||
rlo.amount_residual AS amount_residual, |
|||
CASE |
|||
WHEN rlo.date_due >= date_range.date_current |
|||
THEN rlo.amount_residual |
|||
END AS current, |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_30_days |
|||
AND rlo.date_due < date_range.date_current |
|||
THEN rlo.amount_residual |
|||
END AS age_30_days, |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_60_days |
|||
AND rlo.date_due < date_range.date_less_30_days |
|||
THEN rlo.amount_residual |
|||
END AS age_60_days, |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_90_days |
|||
AND rlo.date_due < date_range.date_less_60_days |
|||
THEN rlo.amount_residual |
|||
END AS age_90_days, |
|||
CASE |
|||
WHEN |
|||
rlo.date_due >= date_range.date_less_120_days |
|||
AND rlo.date_due < date_range.date_less_90_days |
|||
THEN rlo.amount_residual |
|||
END AS age_120_days, |
|||
CASE |
|||
WHEN rlo.date_due < date_range.date_less_120_days |
|||
THEN rlo.amount_residual |
|||
END AS older |
|||
FROM |
|||
date_range, |
|||
report_open_items_move_line rlo |
|||
INNER JOIN |
|||
report_open_items_partner rpo ON rlo.report_partner_id = rpo.id |
|||
INNER JOIN |
|||
report_open_items_account rao ON rpo.report_account_id = rao.id |
|||
INNER JOIN |
|||
report_aged_partner_balance_account ra ON rao.code = ra.code |
|||
INNER JOIN |
|||
report_aged_partner_balance_partner rp |
|||
ON |
|||
ra.id = rp.report_account_id |
|||
""" |
|||
if not only_empty_partner_line: |
|||
query_inject_move_line += """ |
|||
AND rpo.partner_id = rp.partner_id |
|||
""" |
|||
elif only_empty_partner_line: |
|||
query_inject_move_line += """ |
|||
AND rpo.partner_id IS NULL |
|||
AND rp.partner_id IS NULL |
|||
""" |
|||
query_inject_move_line += """ |
|||
WHERE |
|||
rao.report_id = %s |
|||
AND ra.report_id = %s |
|||
""" |
|||
query_inject_move_line_params = (self.date_at,) * 5 |
|||
query_inject_move_line_params += ( |
|||
self.env.uid, |
|||
self.open_items_id.id, |
|||
self.id, |
|||
) |
|||
self.env.cr.execute(query_inject_move_line, |
|||
query_inject_move_line_params) |
|||
@api.model |
|||
def _calculate_percent(self, aged_partner_data): |
|||
for account in aged_partner_data: |
|||
if abs(account['residual']) > 0.01: |
|||
total = account['residual'] |
|||
account.update({ |
|||
'percent_current': abs( |
|||
round((account['current'] / total) * 100, 2)), |
|||
'percent_30_days': abs( |
|||
round((account['30_days'] / total) * 100, |
|||
2)), |
|||
'percent_60_days': abs( |
|||
round((account['60_days'] / total) * 100, |
|||
2)), |
|||
'percent_90_days': abs( |
|||
round((account['90_days'] / total) * 100, |
|||
2)), |
|||
'percent_120_days': abs( |
|||
round((account['120_days'] / total) * 100, |
|||
2)), |
|||
'percent_older': abs( |
|||
round((account['older'] / total) * 100, 2)), |
|||
}) |
|||
else: |
|||
account.update({ |
|||
'percent_current': 0.0, |
|||
'percent_30_days': 0.0, |
|||
'percent_60_days': 0.0, |
|||
'percent_90_days': 0.0, |
|||
'percent_120_days': 0.0, |
|||
'percent_older': 0.0, |
|||
}) |
|||
return aged_partner_data |
|||
|
|||
def _compute_accounts_cumul(self): |
|||
""" Compute cumulative amount for |
|||
report_aged_partner_balance_account. |
|||
""" |
|||
query_compute_accounts_cumul = """ |
|||
WITH |
|||
cumuls AS |
|||
( |
|||
SELECT |
|||
ra.id AS report_account_id, |
|||
SUM(rl.amount_residual) AS cumul_amount_residual, |
|||
SUM(rl.current) AS cumul_current, |
|||
SUM(rl.age_30_days) AS cumul_age_30_days, |
|||
SUM(rl.age_60_days) AS cumul_age_60_days, |
|||
SUM(rl.age_90_days) AS cumul_age_90_days, |
|||
SUM(rl.age_120_days) AS cumul_age_120_days, |
|||
SUM(rl.older) AS cumul_older |
|||
FROM |
|||
report_aged_partner_balance_line rl |
|||
INNER JOIN |
|||
report_aged_partner_balance_partner rp |
|||
ON rl.report_partner_id = rp.id |
|||
INNER JOIN |
|||
report_aged_partner_balance_account ra |
|||
ON rp.report_account_id = ra.id |
|||
WHERE |
|||
ra.report_id = %s |
|||
GROUP BY |
|||
ra.id |
|||
) |
|||
UPDATE |
|||
report_aged_partner_balance_account |
|||
SET |
|||
cumul_amount_residual = c.cumul_amount_residual, |
|||
cumul_current = c.cumul_current, |
|||
cumul_age_30_days = c.cumul_age_30_days, |
|||
cumul_age_60_days = c.cumul_age_60_days, |
|||
cumul_age_90_days = c.cumul_age_90_days, |
|||
cumul_age_120_days = c.cumul_age_120_days, |
|||
cumul_older = c.cumul_older, |
|||
percent_current = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_current / c.cumul_amount_residual |
|||
END, |
|||
percent_age_30_days = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_age_30_days / c.cumul_amount_residual |
|||
END, |
|||
percent_age_60_days = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_age_60_days / c.cumul_amount_residual |
|||
END, |
|||
percent_age_90_days = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_age_90_days / c.cumul_amount_residual |
|||
END, |
|||
percent_age_120_days = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_age_120_days / c.cumul_amount_residual |
|||
END, |
|||
percent_older = |
|||
CASE |
|||
WHEN c.cumul_amount_residual != 0 |
|||
THEN 100 * c.cumul_older / c.cumul_amount_residual |
|||
END |
|||
FROM |
|||
cumuls c |
|||
WHERE |
|||
id = c.report_account_id |
|||
""" |
|||
params_compute_accounts_cumul = (self.id,) |
|||
self.env.cr.execute(query_compute_accounts_cumul, |
|||
params_compute_accounts_cumul) |
|||
@api.multi |
|||
def _get_report_values(self, docids, data): |
|||
wizard_id = data['wizard_id'] |
|||
company = self.env['res.company'].browse(data['company_id']) |
|||
company_id = data['company_id'] |
|||
account_ids = data['account_ids'] |
|||
partner_ids = data['partner_ids'] |
|||
date_at = data['date_at'] |
|||
date_at_object = datetime.strptime(date_at, '%Y-%m-%d').date() |
|||
only_posted_moves = data['only_posted_moves'] |
|||
show_move_line_details = data['show_move_line_details'] |
|||
ag_pb_data, accounts_data, partners_data, \ |
|||
journals_data = self._get_move_lines_data( |
|||
company_id, account_ids, partner_ids, date_at_object, |
|||
only_posted_moves, show_move_line_details) |
|||
aged_partner_data = self._create_account_list( |
|||
ag_pb_data, accounts_data, partners_data, journals_data, |
|||
show_move_line_details, date_at_object) |
|||
aged_partner_data = self._calculate_percent(aged_partner_data) |
|||
return { |
|||
'doc_ids': [wizard_id], |
|||
'doc_model': 'open.items.report.wizard', |
|||
'docs': self.env['open.items.report.wizard'].browse(wizard_id), |
|||
'company_name': company.display_name, |
|||
'currency_name': company.currency_id.name, |
|||
'date_at': date_at, |
|||
'only_posted_moves': only_posted_moves, |
|||
'aged_partner_balance': aged_partner_data, |
|||
'show_move_lines_details': show_move_line_details, |
|||
} |
2353
account_financial_report/report/general_ledger.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1168
account_financial_report/report/journal_ledger.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1209
account_financial_report/report/open_items.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1031
account_financial_report/report/templates/trial_balance.xml
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1191
account_financial_report/report/trial_balance.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,356 +1,190 @@ |
|||
# Copyright 2018 Forest and Biomass Romania |
|||
# Copyright 2020 ForgeFlow S.L. (https://www.forgeflow.com) |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from odoo import api, fields, models |
|||
from odoo import models, api |
|||
|
|||
|
|||
class VATReport(models.TransientModel): |
|||
_name = "report_vat_report" |
|||
_inherit = 'account_financial_report_abstract' |
|||
""" Here, we just define class fields. |
|||
For methods, go more bottom at this file. |
|||
class VATReport(models.AbstractModel): |
|||
_name = 'report.account_financial_report.vat_report' |
|||
_description = "VAT Report" |
|||
|
|||
The class hierarchy is : |
|||
* VATReport |
|||
** VATReportTaxTags |
|||
*** VATReportTax |
|||
""" |
|||
|
|||
# Filters fields, used for data computation |
|||
company_id = fields.Many2one(comodel_name='res.company') |
|||
date_from = fields.Date() |
|||
date_to = fields.Date() |
|||
based_on = fields.Selection([('taxtags', 'Tax Tags'), |
|||
('taxgroups', 'Tax Groups')], |
|||
string='Based On', |
|||
required=True, |
|||
default='taxtags') |
|||
tax_detail = fields.Boolean('Tax Detail') |
|||
|
|||
# Data fields, used to browse report data |
|||
taxtags_ids = fields.One2many( |
|||
comodel_name='report_vat_report_taxtag', |
|||
inverse_name='report_id' |
|||
) |
|||
|
|||
|
|||
class VATReportTaxTags(models.TransientModel): |
|||
_name = 'report_vat_report_taxtag' |
|||
_inherit = 'account_financial_report_abstract' |
|||
_order = 'code ASC' |
|||
|
|||
report_id = fields.Many2one( |
|||
comodel_name='report_vat_report', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used to keep link with real object |
|||
taxtag_id = fields.Many2one( |
|||
'account.account.tag', |
|||
index=True |
|||
) |
|||
taxgroup_id = fields.Many2one( |
|||
'account.tax.group', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used for report display |
|||
code = fields.Char() |
|||
name = fields.Char() |
|||
net = fields.Float(digits=(16, 2)) |
|||
tax = fields.Float(digits=(16, 2)) |
|||
|
|||
# Data fields, used to browse report data |
|||
tax_ids = fields.One2many( |
|||
comodel_name='report_vat_report_tax', |
|||
inverse_name='report_tax_id', |
|||
string='Taxes' |
|||
) |
|||
|
|||
|
|||
class VATReportTax(models.TransientModel): |
|||
_name = 'report_vat_report_tax' |
|||
_inherit = 'account_financial_report_abstract' |
|||
_order = 'name ASC' |
|||
|
|||
report_tax_id = fields.Many2one( |
|||
comodel_name='report_vat_report_taxtag', |
|||
ondelete='cascade', |
|||
index=True |
|||
) |
|||
|
|||
# Data fields, used to keep link with real object |
|||
tax_id = fields.Many2one( |
|||
'account.tax', |
|||
index=True, |
|||
string='Tax ID', |
|||
) |
|||
|
|||
# Data fields, used for report display |
|||
code = fields.Char() |
|||
name = fields.Char() |
|||
net = fields.Float(digits=(16, 2)) |
|||
tax = fields.Float(digits=(16, 2)) |
|||
|
|||
|
|||
class VATReportCompute(models.TransientModel): |
|||
""" Here, we just define methods. |
|||
For class fields, go more top at this file. |
|||
""" |
|||
|
|||
_inherit = 'report_vat_report' |
|||
|
|||
@api.multi |
|||
def print_report(self, report_type='qweb'): |
|||
self.ensure_one() |
|||
if report_type == 'xlsx': |
|||
report_name = 'a_f_r.report_vat_report_xlsx' |
|||
else: |
|||
report_name = 'account_financial_report.report_vat_report_qweb' |
|||
context = dict(self.env.context) |
|||
action = self.env['ir.actions.report'].search( |
|||
[('report_name', '=', report_name), |
|||
('report_type', '=', report_type)], limit=1) |
|||
return action.with_context(context).report_action(self, config=False) |
|||
|
|||
def _get_html(self): |
|||
result = {} |
|||
rcontext = {} |
|||
context = dict(self.env.context) |
|||
report = self.browse(context.get('active_id')) |
|||
if report: |
|||
rcontext['o'] = report |
|||
result['html'] = self.env.ref( |
|||
'account_financial_report.report_vat_report').render( |
|||
rcontext) |
|||
return result |
|||
def _get_tax_data(self, tax_ids): |
|||
taxes = self.env['account.tax'].browse(tax_ids) |
|||
tax_data = {} |
|||
for tax in taxes: |
|||
tax_data.update({ |
|||
tax.id: { |
|||
'id': tax.id, |
|||
'name': tax.name, |
|||
'tax_group_id': tax.tax_group_id.id, |
|||
'tags_ids': tax.tag_ids.ids |
|||
} |
|||
}) |
|||
return tax_data |
|||
|
|||
@api.model |
|||
def get_html(self, given_context=None): |
|||
return self.with_context(given_context)._get_html() |
|||
|
|||
@api.multi |
|||
def compute_data_for_report(self): |
|||
self.ensure_one() |
|||
# Compute report data |
|||
if self.based_on == 'taxtags': |
|||
self._inject_taxtags_values() |
|||
self._inject_tax_taxtags_values() |
|||
elif self.based_on == 'taxgroups': |
|||
self._inject_taxgroups_values() |
|||
self._inject_tax_taxgroups_values() |
|||
# Refresh cache because all data are computed with SQL requests |
|||
self.refresh() |
|||
|
|||
def _inject_taxtags_values(self): |
|||
"""Inject report values for report_vat_report_taxtags.""" |
|||
query_inject_taxtags = """ |
|||
WITH |
|||
taxtags AS |
|||
(SELECT coalesce(regexp_replace(tag.name, |
|||
'[^0-9\\.]+', '', 'g'), ' ') AS code, |
|||
tag.name, tag.id, |
|||
coalesce(sum(movetax.tax_base_amount), 0.00) AS net, |
|||
coalesce(sum(movetax.balance), 0.00) AS tax |
|||
FROM |
|||
account_account_tag AS tag |
|||
INNER JOIN account_tax_account_tag AS taxtag |
|||
ON tag.id = taxtag.account_account_tag_id |
|||
INNER JOIN account_tax AS tax |
|||
ON tax.id = taxtag.account_tax_id |
|||
INNER JOIN account_move_line AS movetax |
|||
ON movetax.tax_line_id = tax.id |
|||
INNER JOIN account_move AS move |
|||
ON move.id = movetax.move_id |
|||
WHERE tag.id is not null AND movetax.tax_exigible |
|||
AND move.company_id = %s AND move.date >= %s |
|||
AND move.date <= %s AND move.state = 'posted' |
|||
GROUP BY tag.id |
|||
ORDER BY code, tag.name |
|||
def _get_vat_report_domain(self, company_id, date_from, date_to): |
|||
domain = [('company_id', '=', company_id), |
|||
('date', '>=', date_from), |
|||
('date', '<', date_to), |
|||
('tax_line_id', '!=', False), |
|||
('tax_exigible', '=', True)] |
|||
return domain |
|||
|
|||
def _get_vat_report_data(self, company_id, date_from, date_to): |
|||
domain = self._get_vat_report_domain(company_id, date_from, date_to) |
|||
ml_fields = ['id', 'tax_base_amount', 'balance', 'tax_line_id', |
|||
'analytic_tag_ids'] |
|||
tax_move_lines = self.env['account.move.line'].search_read( |
|||
domain=domain, |
|||
fields=ml_fields, |
|||
) |
|||
INSERT INTO |
|||
report_vat_report_taxtag |
|||
( |
|||
report_id, |
|||
create_uid, |
|||
create_date, |
|||
taxtag_id, |
|||
code, |
|||
name, |
|||
net, tax |
|||
) |
|||
SELECT |
|||
%s AS report_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
tag.id, |
|||
tag.code, |
|||
tag.name, |
|||
abs(tag.net), |
|||
abs(tag.tax) |
|||
FROM |
|||
taxtags tag |
|||
""" |
|||
query_inject_taxtags_params = (self.company_id.id, self.date_from, |
|||
self.date_to, self.id, self.env.uid) |
|||
self.env.cr.execute(query_inject_taxtags, query_inject_taxtags_params) |
|||
vat_data = {} |
|||
tax_ids = set() |
|||
for tax_move_line in tax_move_lines: |
|||
tax_ml_id = tax_move_line['id'] |
|||
vat_data[tax_ml_id] = {} |
|||
vat_data[tax_ml_id].update({ |
|||
'id': tax_ml_id, |
|||
'net': tax_move_line['tax_base_amount'], |
|||
'tax': tax_move_line['balance'] if tax_move_line[ |
|||
'balance'] > 0 else (-1) * tax_move_line['balance'], |
|||
'tax_line_id': tax_move_line['tax_line_id'], |
|||
}) |
|||
tax_ids.add(tax_move_line['tax_line_id'][0]) |
|||
tax_data = self._get_tax_data(tax_ids) |
|||
return vat_data, tax_data |
|||
|
|||
def _get_tax_group_data(self, tax_group_ids): |
|||
tax_groups = self.env['account.tax.group'].browse(tax_group_ids) |
|||
tax_group_data = {} |
|||
for tax_group in tax_groups: |
|||
tax_group_data.update({ |
|||
tax_group.id: { |
|||
'id': tax_group.id, |
|||
'name': tax_group.name, |
|||
'code': str(tax_group.sequence), |
|||
} |
|||
}) |
|||
return tax_group_data |
|||
|
|||
def _get_vat_report_group_data(self, vat_report_data, tax_data, tax_detail): |
|||
vat_report = {} |
|||
for tax_move_line in vat_report_data.values(): |
|||
tax_id = tax_move_line['tax_line_id'][0] |
|||
tax_group_id = tax_data[tax_id]['tax_group_id'] |
|||
if tax_group_id not in vat_report.keys(): |
|||
vat_report[tax_group_id] = {} |
|||
vat_report[tax_group_id]['net'] = 0.0 |
|||
vat_report[tax_group_id]['tax'] = 0.0 |
|||
vat_report[tax_group_id][tax_id] = tax_data[tax_id] |
|||
vat_report[tax_group_id][tax_id].update( |
|||
{'net': 0.0, 'tax': 0.0} |
|||
) |
|||
else: |
|||
if tax_id not in vat_report[tax_group_id].keys(): |
|||
vat_report[tax_group_id][tax_id] = tax_data[tax_id] |
|||
vat_report[tax_group_id][tax_id].update( |
|||
{'net': 0.0, 'tax': 0.0} |
|||
) |
|||
vat_report[tax_group_id]['net'] += tax_move_line['net'] |
|||
vat_report[tax_group_id]['tax'] += tax_move_line['tax'] |
|||
vat_report[tax_group_id][tax_id]['net'] += tax_move_line['net'] |
|||
vat_report[tax_group_id][tax_id]['tax'] += tax_move_line['tax'] |
|||
tax_group_data = self._get_tax_group_data(vat_report.keys()) |
|||
vat_report_list = [] |
|||
for tax_group_id in vat_report.keys(): |
|||
vat_report[tax_group_id]['name'] = tax_group_data[ |
|||
tax_group_id]['name'] |
|||
vat_report[tax_group_id]['code'] = tax_group_data[ |
|||
tax_group_id]['code'] |
|||
if tax_detail: |
|||
vat_report[tax_group_id]['taxes'] = [] |
|||
for tax_id in vat_report[tax_group_id]: |
|||
if isinstance(tax_id, int): |
|||
vat_report[tax_group_id]['taxes'].append( |
|||
vat_report[tax_group_id][tax_id] |
|||
) |
|||
vat_report_list.append(vat_report[tax_group_id]) |
|||
return vat_report_list |
|||
|
|||
def _get_tags_data(self, tags_ids): |
|||
tags = self.env['account.account.tag'].browse(tags_ids) |
|||
tags_data = {} |
|||
for tag in tags: |
|||
tags_data.update({tag.id: { |
|||
'code': "", |
|||
'name': tag.name} |
|||
}) |
|||
return tags_data |
|||
|
|||
def _get_vat_report_tag_data(self, vat_report_data, tax_data, tax_detail): |
|||
vat_report = {} |
|||
for tax_move_line in vat_report_data.values(): |
|||
tax_id = tax_move_line['tax_line_id'][0] |
|||
tags_ids = tax_data[tax_id]['tags_ids'] |
|||
if tags_ids: |
|||
for tag_id in tags_ids: |
|||
if tag_id not in vat_report.keys(): |
|||
vat_report[tag_id] = {} |
|||
vat_report[tag_id]['net'] = 0.0 |
|||
vat_report[tag_id]['tax'] = 0.0 |
|||
vat_report[tag_id][tax_id] = tax_data[tax_id] |
|||
vat_report[tag_id][tax_id].update( |
|||
{'net': 0.0, 'tax': 0.0} |
|||
) |
|||
else: |
|||
if tax_id not in vat_report[tag_id].keys(): |
|||
vat_report[tag_id][tax_id] = tax_data[tax_id] |
|||
vat_report[tag_id][tax_id].update( |
|||
{'net': 0.0, 'tax': 0.0} |
|||
) |
|||
vat_report[tag_id][tax_id]['net'] += tax_move_line['net'] |
|||
vat_report[tag_id][tax_id]['tax'] += tax_move_line['tax'] |
|||
vat_report[tag_id]['net'] += tax_move_line['net'] |
|||
vat_report[tag_id]['tax'] += tax_move_line['tax'] |
|||
tags_data = self._get_tags_data(vat_report.keys()) |
|||
vat_report_list = [] |
|||
for tag_id in vat_report.keys(): |
|||
vat_report[tag_id]['name'] = tags_data[tag_id]['name'] |
|||
vat_report[tag_id]['code'] = tags_data[tag_id]['code'] |
|||
if tax_detail: |
|||
vat_report[tag_id]['taxes'] = [] |
|||
for tax_id in vat_report[tag_id]: |
|||
if isinstance(tax_id, int): |
|||
vat_report[tag_id]['taxes'].append( |
|||
vat_report[tag_id][tax_id] |
|||
) |
|||
vat_report_list.append(vat_report[tag_id]) |
|||
return vat_report_list |
|||
|
|||
def _inject_taxgroups_values(self): |
|||
"""Inject report values for report_vat_report_taxtags.""" |
|||
query_inject_taxgroups = """ |
|||
WITH |
|||
taxgroups AS |
|||
(SELECT coalesce(taxgroup.sequence, 0) AS code, |
|||
taxgroup.name, taxgroup.id, |
|||
coalesce(sum(movetax.tax_base_amount), 0.00) AS net, |
|||
coalesce(sum(movetax.balance), 0.00) AS tax |
|||
FROM |
|||
account_tax_group AS taxgroup |
|||
INNER JOIN account_tax AS tax |
|||
ON tax.tax_group_id = taxgroup.id |
|||
INNER JOIN account_move_line AS movetax |
|||
ON movetax.tax_line_id = tax.id |
|||
INNER JOIN account_move AS move |
|||
ON move.id = movetax.move_id |
|||
WHERE taxgroup.id is not null AND movetax.tax_exigible |
|||
AND move.company_id = %s AND move.date >= %s |
|||
AND move.date <= %s AND move.state = 'posted' |
|||
GROUP BY taxgroup.id |
|||
ORDER BY code, taxgroup.name |
|||
) |
|||
INSERT INTO |
|||
report_vat_report_taxtag |
|||
( |
|||
report_id, |
|||
create_uid, |
|||
create_date, |
|||
taxgroup_id, |
|||
code, |
|||
name, |
|||
net, tax |
|||
) |
|||
SELECT |
|||
%s AS report_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
groups.id, |
|||
groups.code, |
|||
groups.name, |
|||
abs(groups.net), |
|||
abs(groups.tax) |
|||
FROM |
|||
taxgroups groups |
|||
""" |
|||
query_inject_taxgroups_params = (self.company_id.id, self.date_from, |
|||
self.date_to, self.id, self.env.uid) |
|||
self.env.cr.execute(query_inject_taxgroups, |
|||
query_inject_taxgroups_params) |
|||
|
|||
def _inject_tax_taxtags_values(self): |
|||
""" Inject report values for report_vat_report_tax. """ |
|||
# pylint: disable=sql-injection |
|||
query_inject_tax = """ |
|||
WITH |
|||
taxtags_tax AS |
|||
( |
|||
SELECT |
|||
tag.id AS report_tax_id, ' ' AS code, |
|||
tax.name, tax.id, |
|||
coalesce(sum(movetax.tax_base_amount), 0.00) AS net, |
|||
coalesce(sum(movetax.balance), 0.00) AS tax |
|||
FROM |
|||
report_vat_report_taxtag AS tag |
|||
INNER JOIN account_tax_account_tag AS taxtag |
|||
ON tag.taxtag_id = taxtag.account_account_tag_id |
|||
INNER JOIN account_tax AS tax |
|||
ON tax.id = taxtag.account_tax_id |
|||
INNER JOIN account_move_line AS movetax |
|||
ON movetax.tax_line_id = tax.id |
|||
INNER JOIN account_move AS move |
|||
ON move.id = movetax.move_id |
|||
WHERE tag.id is not null AND movetax.tax_exigible |
|||
AND tag.report_id = %s AND move.company_id = %s |
|||
AND move.date >= %s AND move.date <= %s |
|||
AND move.state = 'posted' |
|||
GROUP BY tag.id, tax.id |
|||
ORDER BY tax.name |
|||
) |
|||
INSERT INTO |
|||
report_vat_report_tax |
|||
( |
|||
report_tax_id, |
|||
create_uid, |
|||
create_date, |
|||
tax_id, |
|||
name, |
|||
net, |
|||
tax |
|||
) |
|||
SELECT |
|||
tt.report_tax_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
tt.id, |
|||
tt.name, |
|||
abs(tt.net), |
|||
abs(tt.tax) |
|||
FROM |
|||
taxtags_tax tt |
|||
""" |
|||
query_inject_tax_params = (self.id, self.company_id.id, self.date_from, |
|||
self.date_to, self.env.uid) |
|||
self.env.cr.execute(query_inject_tax, query_inject_tax_params) |
|||
|
|||
def _inject_tax_taxgroups_values(self): |
|||
""" Inject report values for report_vat_report_tax. """ |
|||
# pylint: disable=sql-injection |
|||
query_inject_tax = """ |
|||
WITH |
|||
taxtags_tax AS |
|||
( |
|||
SELECT |
|||
taxtag.id AS report_tax_id, ' ' AS code, |
|||
tax.name, tax.id, |
|||
coalesce(sum(movetax.tax_base_amount), 0.00) AS net, |
|||
coalesce(sum(movetax.balance), 0.00) AS tax |
|||
FROM |
|||
report_vat_report_taxtag AS taxtag |
|||
INNER JOIN account_tax AS tax |
|||
ON tax.tax_group_id = taxtag.taxgroup_id |
|||
INNER JOIN account_move_line AS movetax |
|||
ON movetax.tax_line_id = tax.id |
|||
INNER JOIN account_move AS move |
|||
ON move.id = movetax.move_id |
|||
WHERE taxtag.id is not null AND movetax.tax_exigible |
|||
AND taxtag.report_id = %s AND move.company_id = %s |
|||
AND move.date >= %s AND move.date <= %s |
|||
AND move.state = 'posted' |
|||
GROUP BY taxtag.id, tax.id |
|||
ORDER BY tax.name |
|||
) |
|||
INSERT INTO |
|||
report_vat_report_tax |
|||
( |
|||
report_tax_id, |
|||
create_uid, |
|||
create_date, |
|||
tax_id, |
|||
name, |
|||
net, |
|||
tax |
|||
) |
|||
SELECT |
|||
tt.report_tax_id, |
|||
%s AS create_uid, |
|||
NOW() AS create_date, |
|||
tt.id, |
|||
tt.name, |
|||
abs(tt.net), |
|||
abs(tt.tax) |
|||
FROM |
|||
taxtags_tax tt |
|||
""" |
|||
query_inject_tax_params = (self.id, self.company_id.id, self.date_from, |
|||
self.date_to, self.env.uid) |
|||
self.env.cr.execute(query_inject_tax, query_inject_tax_params) |
|||
@api.multi |
|||
def _get_report_values(self, docids, data): |
|||
wizard_id = data['wizard_id'] |
|||
company = self.env['res.company'].browse(data['company_id']) |
|||
company_id = data['company_id'] |
|||
date_from = data['date_from'] |
|||
date_to = data['date_to'] |
|||
based_on = data['based_on'] |
|||
tax_detail = data['tax_detail'] |
|||
vat_report_data, tax_data = self._get_vat_report_data( |
|||
company_id, date_from, date_to) |
|||
if based_on == 'taxgroups': |
|||
vat_report = self._get_vat_report_group_data( |
|||
vat_report_data, tax_data, tax_detail) |
|||
else: |
|||
vat_report = self._get_vat_report_tag_data( |
|||
vat_report_data, tax_data, tax_detail) |
|||
return { |
|||
'doc_ids': [wizard_id], |
|||
'doc_model': 'open.items.report.wizard', |
|||
'docs': self.env['open.items.report.wizard'].browse(wizard_id), |
|||
'company_name': company.display_name, |
|||
'currency_name': company.currency_id.name, |
|||
'date_to': data['date_to'], |
|||
'date_from': data['date_from'], |
|||
'based_on': data['based_on'], |
|||
'tax_detail': data['tax_detail'], |
|||
'vat_report': vat_report, |
|||
} |
@ -1,399 +0,0 @@ |
|||
# Author: Julien Coux |
|||
# Copyright 2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import logging |
|||
|
|||
from odoo.tests import common |
|||
from odoo.tools import test_reports |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class AbstractTest(common.TransactionCase): |
|||
"""Common technical tests for all reports.""" |
|||
at_install = False |
|||
post_install = True |
|||
|
|||
accounts = {} |
|||
|
|||
def with_context(self, *args, **kwargs): |
|||
context = dict(args[0] if args else self.env.context, **kwargs) |
|||
self.env = self.env(context=context) |
|||
return self |
|||
|
|||
def _chart_template_create(self): |
|||
transfer_account_id = self.env['account.account.template'].create({ |
|||
'code': '000', |
|||
'name': 'Liquidity Transfers', |
|||
'reconcile': True, |
|||
'user_type_id': self.ref( |
|||
"account.data_account_type_current_assets"), |
|||
}) |
|||
self.chart = self.env['account.chart.template'].create({ |
|||
'name': 'Test COA', |
|||
'code_digits': 4, |
|||
'bank_account_code_prefix': 1014, |
|||
'cash_account_code_prefix': 1014, |
|||
'currency_id': self.ref('base.USD'), |
|||
'transfer_account_code_prefix': '000', |
|||
}) |
|||
transfer_account_id.update({ |
|||
'chart_template_id': self.chart.id, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': transfer_account_id.id, |
|||
'model': transfer_account_id._name, |
|||
'name': 'Liquidity Transfers', |
|||
}) |
|||
act = self.env['account.account.template'].create({ |
|||
'code': '001', |
|||
'name': 'Expenses', |
|||
'user_type_id': self.ref("account.data_account_type_expenses"), |
|||
'chart_template_id': self.chart.id, |
|||
'reconcile': True, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': act.id, |
|||
'model': act._name, |
|||
'name': 'expenses', |
|||
}) |
|||
act = self.env['account.account.template'].create({ |
|||
'code': '002', |
|||
'name': 'Product Sales', |
|||
'user_type_id': self.ref("account.data_account_type_revenue"), |
|||
'chart_template_id': self.chart.id, |
|||
'reconcile': True, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': act.id, |
|||
'model': act._name, |
|||
'name': 'sales', |
|||
}) |
|||
act = self.env['account.account.template'].create({ |
|||
'code': '003', |
|||
'name': 'Account Receivable', |
|||
'user_type_id': self.ref("account.data_account_type_receivable"), |
|||
'chart_template_id': self.chart.id, |
|||
'reconcile': True, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': act.id, |
|||
'model': act._name, |
|||
'name': 'receivable', |
|||
}) |
|||
act = self.env['account.account.template'].create({ |
|||
'code': '004', |
|||
'name': 'Account Payable', |
|||
'user_type_id': self.ref("account.data_account_type_payable"), |
|||
'chart_template_id': self.chart.id, |
|||
'reconcile': True, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': act.id, |
|||
'model': act._name, |
|||
'name': 'payable', |
|||
}) |
|||
|
|||
def _add_chart_of_accounts(self): |
|||
self.company = self.env['res.company'].create({ |
|||
'name': 'Spanish test company', |
|||
}) |
|||
self.env.ref('base.group_multi_company').write({ |
|||
'users': [(4, self.env.uid)], |
|||
}) |
|||
self.env.user.write({ |
|||
'company_ids': [(4, self.company.id)], |
|||
'company_id': self.company.id, |
|||
}) |
|||
self.with_context( |
|||
company_id=self.company.id, force_company=self.company.id) |
|||
self.chart.try_loading_for_current_company() |
|||
self.revenue = self.env['account.account'].search( |
|||
[('user_type_id', '=', self.ref( |
|||
"account.data_account_type_revenue"))], limit=1) |
|||
self.expense = self.env['account.account'].search( |
|||
[('user_type_id', '=', self.ref( |
|||
"account.data_account_type_expenses"))], limit=1) |
|||
self.receivable = self.env['account.account'].search( |
|||
[('user_type_id', '=', self.ref( |
|||
"account.data_account_type_receivable"))], limit=1) |
|||
self.payable = self.env['account.account'].search( |
|||
[('user_type_id', '=', self.ref( |
|||
"account.data_account_type_payable"))], limit=1) |
|||
return True |
|||
|
|||
def _journals_create(self): |
|||
self.journal_sale = self.env['account.journal'].create({ |
|||
'company_id': self.company.id, |
|||
'name': 'Test journal for sale', |
|||
'type': 'sale', |
|||
'code': 'TSALE', |
|||
'default_debit_account_id': self.revenue.id, |
|||
'default_credit_account_id': self.revenue.id, |
|||
}) |
|||
self.journal_purchase = self.env['account.journal'].create({ |
|||
'company_id': self.company.id, |
|||
'name': 'Test journal for purchase', |
|||
'type': 'purchase', |
|||
'code': 'TPUR', |
|||
'default_debit_account_id': self.expense.id, |
|||
'default_credit_account_id': self.expense.id, |
|||
}) |
|||
return True |
|||
|
|||
def _invoice_create(self): |
|||
self.partner = self.env['res.partner'].create({ |
|||
'name': 'Test partner', |
|||
'company_id': self.company.id, |
|||
'property_account_receivable_id': self.receivable.id, |
|||
'property_account_payable_id': self.payable.id, |
|||
}) |
|||
|
|||
# customer invoice |
|||
customer_invoice_lines = [(0, False, { |
|||
'name': 'Test description #1', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 1.0, |
|||
'price_unit': 100.0, |
|||
}), (0, False, { |
|||
'name': 'Test description #2', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 2.0, |
|||
'price_unit': 25.0, |
|||
})] |
|||
self.invoice_out = self.env['account.invoice'].create({ |
|||
'partner_id': self.partner.id, |
|||
'type': 'out_invoice', |
|||
'invoice_line_ids': customer_invoice_lines, |
|||
'account_id': self.partner.property_account_receivable_id.id, |
|||
'journal_id': self.journal_sale.id, |
|||
}) |
|||
self.invoice_out.action_invoice_open() |
|||
|
|||
# vendor bill |
|||
vendor_invoice_lines = [(0, False, { |
|||
'name': 'Test description #1', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 1.0, |
|||
'price_unit': 100.0, |
|||
}), (0, False, { |
|||
'name': 'Test description #2', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 2.0, |
|||
'price_unit': 25.0, |
|||
})] |
|||
self.invoice_in = self.env['account.invoice'].create({ |
|||
'partner_id': self.partner.id, |
|||
'type': 'in_invoice', |
|||
'invoice_line_ids': vendor_invoice_lines, |
|||
'account_id': self.partner.property_account_payable_id.id, |
|||
'journal_id': self.journal_purchase.id, |
|||
}) |
|||
self.invoice_in.action_invoice_open() |
|||
|
|||
def setUp(self): |
|||
super(AbstractTest, self).setUp() |
|||
|
|||
self.with_context() |
|||
self._chart_template_create() |
|||
self._add_chart_of_accounts() |
|||
self._journals_create() |
|||
self._invoice_create() |
|||
|
|||
self.model = self._getReportModel() |
|||
|
|||
self.qweb_report_name = self._getQwebReportName() |
|||
self.xlsx_report_name = self._getXlsxReportName() |
|||
self.xlsx_action_name = self._getXlsxReportActionName() |
|||
|
|||
self.report_title = self._getReportTitle() |
|||
|
|||
self.base_filters = self._getBaseFilters() |
|||
self.additional_filters = self._getAdditionalFiltersToBeTested() |
|||
|
|||
self.report = self.model.create(self.base_filters) |
|||
self.report.compute_data_for_report() |
|||
|
|||
def test_html(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.qweb_report_name, |
|||
[self.report.id], |
|||
report_type='qweb-html') |
|||
|
|||
def test_qweb(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.qweb_report_name, |
|||
[self.report.id], |
|||
report_type='qweb-pdf') |
|||
|
|||
def test_xlsx(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.xlsx_report_name, |
|||
[self.report.id], |
|||
report_type='xlsx') |
|||
|
|||
def test_print(self): |
|||
self.report.print_report('qweb') |
|||
self.report.print_report('xlsx') |
|||
|
|||
def test_02_generation_report_html(self): |
|||
"""Check if report HTML is correctly generated""" |
|||
|
|||
# Check if returned report action is correct |
|||
report_type = 'qweb-html' |
|||
report_action = self.report.print_report(report_type) |
|||
self.assertDictContainsSubset( |
|||
{ |
|||
'type': 'ir.actions.report', |
|||
'report_name': self.qweb_report_name, |
|||
'report_type': 'qweb-html', |
|||
}, |
|||
report_action |
|||
) |
|||
|
|||
# Check if report template is correct |
|||
report = self.env['ir.actions.report'].search( |
|||
[('report_name', '=', self.qweb_report_name), |
|||
('report_type', '=', report_type)], limit=1) |
|||
self.assertEqual(report.report_type, 'qweb-html') |
|||
|
|||
rep = report.render(self.report.ids, {}) |
|||
|
|||
self.assertTrue(self.report_title.encode('utf8') in rep[0]) |
|||
self.assertTrue( |
|||
self.report.account_ids[0].name.encode('utf8') in rep[0] |
|||
) |
|||
|
|||
def test_04_compute_data(self): |
|||
"""Check that the SQL queries work with all filters options""" |
|||
|
|||
for filters in [{}] + self.additional_filters: |
|||
current_filter = self.base_filters.copy() |
|||
current_filter.update(filters) |
|||
|
|||
report = self.model.create(current_filter) |
|||
report.compute_data_for_report() |
|||
|
|||
self.assertGreaterEqual(len(report.account_ids), 1) |
|||
|
|||
# Same filters with only one account |
|||
current_filter = self.base_filters.copy() |
|||
current_filter.update(filters) |
|||
report_accounts = report.account_ids.filtered('account_id') |
|||
current_filter.update({ |
|||
'filter_account_ids': |
|||
[(6, 0, report_accounts[0].account_id.ids)], |
|||
}) |
|||
|
|||
report2 = self.model.create(current_filter) |
|||
report2.compute_data_for_report() |
|||
|
|||
self.assertEqual(len(report2.account_ids), 1) |
|||
self.assertEqual(report2.account_ids.name, |
|||
report_accounts[0].name) |
|||
|
|||
if self._partner_test_is_possible(filters): |
|||
# Same filters with only one partner |
|||
report_partner_ids = report.account_ids.mapped('partner_ids') |
|||
partner_ids = report_partner_ids.mapped('partner_id') |
|||
|
|||
current_filter = self.base_filters.copy() |
|||
current_filter.update(filters) |
|||
current_filter.update({ |
|||
'filter_partner_ids': [(6, 0, partner_ids[0].ids)], |
|||
}) |
|||
|
|||
report3 = self.model.create(current_filter) |
|||
report3.compute_data_for_report() |
|||
|
|||
self.assertGreaterEqual(len(report3.account_ids), 1) |
|||
|
|||
report_partner_ids3 = report3.account_ids.mapped('partner_ids') |
|||
partner_ids3 = report_partner_ids3.mapped('partner_id') |
|||
|
|||
self.assertEqual(len(partner_ids3), 1) |
|||
self.assertEqual( |
|||
partner_ids3.name, |
|||
partner_ids[0].name |
|||
) |
|||
|
|||
# Same filters with only one partner and one account |
|||
report_partner_ids = report3.account_ids.mapped('partner_ids') |
|||
report_account_id = report_partner_ids.filtered( |
|||
lambda p: p.partner_id |
|||
)[0].report_account_id |
|||
|
|||
current_filter = self.base_filters.copy() |
|||
current_filter.update(filters) |
|||
current_filter.update({ |
|||
'filter_account_ids': |
|||
[(6, 0, report_account_id.account_id.ids)], |
|||
'filter_partner_ids': [(6, 0, partner_ids[0].ids)], |
|||
}) |
|||
|
|||
report4 = self.model.create(current_filter) |
|||
report4.compute_data_for_report() |
|||
|
|||
self.assertEqual(len(report4.account_ids), 1) |
|||
self.assertEqual(report4.account_ids.name, |
|||
report_account_id.account_id.name) |
|||
|
|||
report_partner_ids4 = report4.account_ids.mapped('partner_ids') |
|||
partner_ids4 = report_partner_ids4.mapped('partner_id') |
|||
|
|||
self.assertEqual(len(partner_ids4), 1) |
|||
self.assertEqual( |
|||
partner_ids4.name, |
|||
partner_ids[0].name |
|||
) |
|||
|
|||
def _partner_test_is_possible(self, filters): |
|||
""" |
|||
:return: |
|||
a boolean to indicate if partner test is possible |
|||
with current filters |
|||
""" |
|||
return True |
|||
|
|||
def _getReportModel(self): |
|||
""" |
|||
:return: the report model name |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getQwebReportName(self): |
|||
""" |
|||
:return: the qweb report name |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getXlsxReportName(self): |
|||
""" |
|||
:return: the xlsx report name |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getXlsxReportActionName(self): |
|||
""" |
|||
:return: the xlsx report action name |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getReportTitle(self): |
|||
""" |
|||
:return: the report title displayed into the report |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getBaseFilters(self): |
|||
""" |
|||
:return: the minimum required filters to generate report |
|||
""" |
|||
raise NotImplementedError() |
|||
|
|||
def _getAdditionalFiltersToBeTested(self): |
|||
""" |
|||
:return: the additional filters to generate report variants |
|||
""" |
|||
raise NotImplementedError() |
@ -1,78 +0,0 @@ |
|||
# Copyright 2018 Forest and Biomass Romania |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import logging |
|||
from . import abstract_test |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class AbstractTestForeignCurrency(abstract_test.AbstractTest): |
|||
"""Common technical tests for all reports.""" |
|||
|
|||
def _chart_template_create(self): |
|||
super(AbstractTestForeignCurrency, self)._chart_template_create() |
|||
# Account for foreign payments |
|||
self.account_type_other = self.env['account.account.type'].create( |
|||
{'name': 'foreign expenses', |
|||
'type': 'other', |
|||
}) |
|||
act = self.env['account.account.template'].create({ |
|||
'code': '0012', |
|||
'name': 'Foreign Expenses', |
|||
'user_type_id': self.account_type_other.id, |
|||
'chart_template_id': self.chart.id, |
|||
'currency_id': self.env.ref('base.EUR').id, |
|||
}) |
|||
self.env['ir.model.data'].create({ |
|||
'res_id': act.id, |
|||
'model': act._name, |
|||
'name': 'foreign expenses', |
|||
}) |
|||
return True |
|||
|
|||
def _add_chart_of_accounts(self): |
|||
super(AbstractTestForeignCurrency, self)._add_chart_of_accounts() |
|||
self.foreign_expense = self.env['account.account'].search( |
|||
[('currency_id', '=', self.env.ref('base.EUR').id)], limit=1) |
|||
self.foreign_currency_id = self.foreign_expense.currency_id |
|||
return True |
|||
|
|||
def _journals_create(self): |
|||
super(AbstractTestForeignCurrency, self)._journals_create() |
|||
self.journal_foreign_purchases = self.env['account.journal'].create({ |
|||
'company_id': self.company.id, |
|||
'name': 'Test journal for purchase', |
|||
'type': 'purchase', |
|||
'code': 'TFORPUR', |
|||
'default_debit_account_id': self.foreign_expense.id, |
|||
'default_credit_account_id': self.foreign_expense.id, |
|||
'currency_id': self.foreign_currency_id.id, |
|||
}) |
|||
return True |
|||
|
|||
def _invoice_create(self): |
|||
super(AbstractTestForeignCurrency, self)._invoice_create() |
|||
# vendor bill foreign currency |
|||
foreign_vendor_invoice_lines = [(0, False, { |
|||
'name': 'Test description #1', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 1.0, |
|||
'price_unit': 100.0, |
|||
'currency_id': self.foreign_currency_id.id, |
|||
}), (0, False, { |
|||
'name': 'Test description #2', |
|||
'account_id': self.revenue.id, |
|||
'quantity': 2.0, |
|||
'price_unit': 25.0, |
|||
'currency_id': self.foreign_currency_id.id, |
|||
})] |
|||
self.foreign_invoice_in = self.env['account.invoice'].create({ |
|||
'partner_id': self.partner.id, |
|||
'type': 'in_invoice', |
|||
'invoice_line_ids': foreign_vendor_invoice_lines, |
|||
'account_id': self.partner.property_account_payable_id.id, |
|||
'journal_id': self.journal_foreign_purchases.id, |
|||
}) |
|||
self.foreign_invoice_in.action_invoice_open() |
|||
return True |
@ -1,75 +0,0 @@ |
|||
# Copyright 2018 Forest and Biomass Romania |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
import logging |
|||
from odoo.tests.common import TransactionCase |
|||
from odoo.tools import test_reports |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class AbstractTest(TransactionCase): |
|||
"""Common technical tests for all reports.""" |
|||
|
|||
def setUp(cls): |
|||
super(AbstractTest, cls).setUp() |
|||
|
|||
cls.model = cls._getReportModel() |
|||
|
|||
cls.qweb_report_name = cls._getQwebReportName() |
|||
cls.xlsx_report_name = cls._getXlsxReportName() |
|||
cls.xlsx_action_name = cls._getXlsxReportActionName() |
|||
|
|||
cls.report_title = cls._getReportTitle() |
|||
|
|||
cls.base_filters = cls._getBaseFilters() |
|||
|
|||
cls.report = cls.model.create(cls.base_filters) |
|||
cls.report.compute_data_for_report() |
|||
|
|||
def test_html(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.qweb_report_name, |
|||
[self.report.id], |
|||
report_type='qweb-html') |
|||
|
|||
def test_qweb(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.qweb_report_name, |
|||
[self.report.id], |
|||
report_type='qweb-pdf') |
|||
|
|||
def test_xlsx(self): |
|||
test_reports.try_report(self.env.cr, self.env.uid, |
|||
self.xlsx_report_name, |
|||
[self.report.id], |
|||
report_type='xlsx') |
|||
|
|||
def test_print(self): |
|||
self.report.print_report('qweb') |
|||
self.report.print_report('xlsx') |
|||
|
|||
def test_generation_report_html(self): |
|||
"""Check if report HTML is correctly generated""" |
|||
|
|||
# Check if returned report action is correct |
|||
report_type = 'qweb-html' |
|||
report_action = self.report.print_report(report_type) |
|||
self.assertDictContainsSubset( |
|||
{ |
|||
'type': 'ir.actions.report', |
|||
'report_name': self.qweb_report_name, |
|||
'report_type': 'qweb-html', |
|||
}, |
|||
report_action |
|||
) |
|||
|
|||
# Check if report template is correct |
|||
report = self.env['ir.actions.report'].search( |
|||
[('report_name', '=', self.qweb_report_name), |
|||
('report_type', '=', report_type)], limit=1) |
|||
self.assertEqual(report.report_type, 'qweb-html') |
|||
|
|||
rep = report.render(self.report.ids, {}) |
|||
|
|||
self.assertTrue(self.report_title.encode('utf8') in rep[0]) |
@ -1,41 +0,0 @@ |
|||
# Author: Julien Coux |
|||
# Copyright 2016 Camptocamp SA |
|||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
|
|||
from datetime import date |
|||
from . import abstract_test |
|||
|
|||
|
|||
class TestAgedPartnerBalance(abstract_test.AbstractTest): |
|||
""" |
|||
Technical tests for Aged Partner Balance Report. |
|||
""" |
|||
|
|||
def _getReportModel(self): |
|||
return self.env['report_aged_partner_balance'] |
|||
|
|||
def _getQwebReportName(self): |
|||
return 'account_financial_report.report_aged_partner_balance_qweb' |
|||
|
|||
def _getXlsxReportName(self): |
|||
return 'a_f_r.report_aged_partner_balance_xlsx' |
|||
|
|||
def _getXlsxReportActionName(self): |
|||
return 'account_financial_report.' \ |
|||
'action_report_aged_partner_balance_xlsx' |
|||
|
|||
def _getReportTitle(self): |
|||
return 'Odoo' |
|||
|
|||
def _getBaseFilters(self): |
|||
return { |
|||
'date_at': date(date.today().year, 12, 31), |
|||
'company_id': self.company.id, |
|||
} |
|||
|
|||
def _getAdditionalFiltersToBeTested(self): |
|||
return [ |
|||
{'only_posted_moves': True}, |
|||
{'show_move_line_details': True}, |
|||
{'only_posted_moves': True, 'show_move_line_details': True}, |
|||
] |
@ -0,0 +1,2 @@ |
|||
natsort |
|||
pandas |
Write
Preview
Loading…
Cancel
Save
Reference in new issue