From 3751379f1e9a4c215fb6eb898b4ccc67659b9ace Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Tue, 10 May 2022 21:51:50 +0700 Subject: initial commit 2 --- addons/account/report/__init__.py | 6 + .../report/account_hash_integrity_templates.py | 22 +++ .../report/account_hash_integrity_templates.xml | 106 +++++++++++++ addons/account/report/account_invoice_report.py | 168 +++++++++++++++++++++ .../account/report/account_invoice_report_view.xml | 132 ++++++++++++++++ addons/account/report/account_journal.py | 121 +++++++++++++++ 6 files changed, 555 insertions(+) create mode 100644 addons/account/report/__init__.py create mode 100644 addons/account/report/account_hash_integrity_templates.py create mode 100644 addons/account/report/account_hash_integrity_templates.xml create mode 100644 addons/account/report/account_invoice_report.py create mode 100644 addons/account/report/account_invoice_report_view.xml create mode 100644 addons/account/report/account_journal.py (limited to 'addons/account/report') diff --git a/addons/account/report/__init__.py b/addons/account/report/__init__.py new file mode 100644 index 00000000..7c196646 --- /dev/null +++ b/addons/account/report/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import account_invoice_report +from . import account_journal +from . import account_hash_integrity_templates diff --git a/addons/account/report/account_hash_integrity_templates.py b/addons/account/report/account_hash_integrity_templates.py new file mode 100644 index 00000000..fe246841 --- /dev/null +++ b/addons/account/report/account_hash_integrity_templates.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class ReportAccountHashIntegrity(models.AbstractModel): + _name = 'report.account.report_hash_integrity' + _description = 'Get hash integrity result as PDF.' + + @api.model + def _get_report_values(self, docids, data=None): + if data: + data.update(self.env.company._check_hash_integrity()) + else: + data = self.env.company._check_hash_integrity() + return { + 'doc_ids' : docids, + 'doc_model' : self.env['res.company'], + 'data' : data, + 'docs' : self.env['res.company'].browse(self.env.company.id), + } diff --git a/addons/account/report/account_hash_integrity_templates.xml b/addons/account/report/account_hash_integrity_templates.xml new file mode 100644 index 00000000..14716749 --- /dev/null +++ b/addons/account/report/account_hash_integrity_templates.xml @@ -0,0 +1,106 @@ + + + + + + diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py new file mode 100644 index 00000000..05be7856 --- /dev/null +++ b/addons/account/report/account_invoice_report.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api + +from functools import lru_cache + + +class AccountInvoiceReport(models.Model): + _name = "account.invoice.report" + _description = "Invoices Statistics" + _auto = False + _rec_name = 'invoice_date' + _order = 'invoice_date desc' + + # ==== Invoice fields ==== + move_id = fields.Many2one('account.move', readonly=True) + journal_id = fields.Many2one('account.journal', string='Journal', readonly=True) + company_id = fields.Many2one('res.company', string='Company', readonly=True) + company_currency_id = fields.Many2one('res.currency', string='Company Currency', readonly=True) + partner_id = fields.Many2one('res.partner', string='Partner', readonly=True) + commercial_partner_id = fields.Many2one('res.partner', string='Partner Company', help="Commercial Entity") + country_id = fields.Many2one('res.country', string="Country") + invoice_user_id = fields.Many2one('res.users', string='Salesperson', readonly=True) + move_type = fields.Selection([ + ('out_invoice', 'Customer Invoice'), + ('in_invoice', 'Vendor Bill'), + ('out_refund', 'Customer Credit Note'), + ('in_refund', 'Vendor Credit Note'), + ], readonly=True) + state = fields.Selection([ + ('draft', 'Draft'), + ('posted', 'Open'), + ('cancel', 'Cancelled') + ], string='Invoice Status', readonly=True) + payment_state = fields.Selection(selection=[ + ('not_paid', 'Not Paid'), + ('in_payment', 'In Payment'), + ('paid', 'paid') + ], string='Payment Status', readonly=True) + fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', readonly=True) + invoice_date = fields.Date(readonly=True, string="Invoice Date") + + # ==== Invoice line fields ==== + quantity = fields.Float(string='Product Quantity', readonly=True) + product_id = fields.Many2one('product.product', string='Product', readonly=True) + product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure', readonly=True) + product_categ_id = fields.Many2one('product.category', string='Product Category', readonly=True) + invoice_date_due = fields.Date(string='Due Date', readonly=True) + account_id = fields.Many2one('account.account', string='Revenue/Expense Account', readonly=True, domain=[('deprecated', '=', False)]) + analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', groups="analytic.group_analytic_accounting") + price_subtotal = fields.Float(string='Untaxed Total', readonly=True) + price_average = fields.Float(string='Average Price', readonly=True, group_operator="avg") + + _depends = { + 'account.move': [ + 'name', 'state', 'move_type', 'partner_id', 'invoice_user_id', 'fiscal_position_id', + 'invoice_date', 'invoice_date_due', 'invoice_payment_term_id', 'partner_bank_id', + ], + 'account.move.line': [ + 'quantity', 'price_subtotal', 'amount_residual', 'balance', 'amount_currency', + 'move_id', 'product_id', 'product_uom_id', 'account_id', 'analytic_account_id', + 'journal_id', 'company_id', 'currency_id', 'partner_id', + ], + 'product.product': ['product_tmpl_id'], + 'product.template': ['categ_id'], + 'uom.uom': ['category_id', 'factor', 'name', 'uom_type'], + 'res.currency.rate': ['currency_id', 'name'], + 'res.partner': ['country_id'], + } + + @property + def _table_query(self): + return '%s %s %s' % (self._select(), self._from(), self._where()) + + @api.model + def _select(self): + return ''' + SELECT + line.id, + line.move_id, + line.product_id, + line.account_id, + line.analytic_account_id, + line.journal_id, + line.company_id, + line.company_currency_id, + line.partner_id AS commercial_partner_id, + move.state, + move.move_type, + move.partner_id, + move.invoice_user_id, + move.fiscal_position_id, + move.payment_state, + move.invoice_date, + move.invoice_date_due, + uom_template.id AS product_uom_id, + template.categ_id AS product_categ_id, + line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END) + AS quantity, + -line.balance * currency_table.rate AS price_subtotal, + -COALESCE( + -- Average line price + (line.balance / NULLIF(line.quantity, 0.0)) + -- convert to template uom + * (NULLIF(COALESCE(uom_line.factor, 1), 0.0) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)), + 0.0) * currency_table.rate AS price_average, + COALESCE(partner.country_id, commercial_partner.country_id) AS country_id + ''' + + @api.model + def _from(self): + return ''' + FROM account_move_line line + LEFT JOIN res_partner partner ON partner.id = line.partner_id + LEFT JOIN product_product product ON product.id = line.product_id + LEFT JOIN account_account account ON account.id = line.account_id + LEFT JOIN account_account_type user_type ON user_type.id = account.user_type_id + LEFT JOIN product_template template ON template.id = product.product_tmpl_id + LEFT JOIN uom_uom uom_line ON uom_line.id = line.product_uom_id + LEFT JOIN uom_uom uom_template ON uom_template.id = template.uom_id + INNER JOIN account_move move ON move.id = line.move_id + LEFT JOIN res_partner commercial_partner ON commercial_partner.id = move.commercial_partner_id + JOIN {currency_table} ON currency_table.company_id = line.company_id + '''.format( + currency_table=self.env['res.currency']._get_query_currency_table({'multi_company': True, 'date': {'date_to': fields.Date.today()}}), + ) + + @api.model + def _where(self): + return ''' + WHERE move.move_type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') + AND line.account_id IS NOT NULL + AND NOT line.exclude_from_invoice_tab + ''' + + +class ReportInvoiceWithoutPayment(models.AbstractModel): + _name = 'report.account.report_invoice' + _description = 'Account report without payment lines' + + @api.model + def _get_report_values(self, docids, data=None): + docs = self.env['account.move'].browse(docids) + + qr_code_urls = {} + for invoice in docs: + if invoice.display_qr_code: + new_code_url = invoice.generate_qr_code() + if new_code_url: + qr_code_urls[invoice.id] = new_code_url + + return { + 'doc_ids': docids, + 'doc_model': 'account.move', + 'docs': docs, + 'qr_code_urls': qr_code_urls, + } + +class ReportInvoiceWithPayment(models.AbstractModel): + _name = 'report.account.report_invoice_with_payments' + _description = 'Account report with payment lines' + _inherit = 'report.account.report_invoice' + + @api.model + def _get_report_values(self, docids, data=None): + rslt = super()._get_report_values(docids, data) + rslt['report_type'] = data.get('report_type') if data else '' + return rslt diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml new file mode 100644 index 00000000..9bfaaa19 --- /dev/null +++ b/addons/account/report/account_invoice_report_view.xml @@ -0,0 +1,132 @@ + + + + + account.invoice.report.pivot + account.invoice.report + + + + + + + + + + + account.invoice.report.graph + account.invoice.report + + + + + + + + + + + By Salespersons + account.invoice.report + [] + + {'group_by': ['invoice_date:month', 'invoice_user_id']} + + + By Product + account.invoice.report + [] + + {'group_by': ['invoice_date:month', 'product_id'], 'set_visible':True, 'residual_invisible':True} + + + By Product Category + account.invoice.report + [] + + {'group_by': ['invoice_date:month', 'product_categ_id'], 'residual_invisible':True} + + + By Credit Note + account.invoice.report + [('move_type', '=', 'out_refund')] + + {'group_by': ['invoice_date:month', 'invoice_user_id']} + + + By Country + account.invoice.report + [] + + {'group_by': ['invoice_date:month', 'country_id']} + + + + account.invoice.report.search + account.invoice.report + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + account.invoice.report.search_analytic_accounting + account.invoice.report + + + + + + + + + + + Invoices Analysis + account.invoice.report + graph,pivot + {'search_default_current':1, 'search_default_supplier': 1, 'group_by':['invoice_date'], 'group_by_no_leaf':1} + + From this report, you can have an overview of the amount invoiced from your vendors. The search tool can also be used to personalise your Invoices reports and so, match this analysis to your needs. + + + Invoices Analysis + account.invoice.report + graph,pivot + {'search_default_current':1, 'search_default_customer': 1, 'group_by':['invoice_date'], 'group_by_no_leaf':1} + + From this report, you can have an overview of the amount invoiced to your customers. The search tool can also be used to personalise your Invoices reports and so, match this analysis to your needs. + + + + diff --git a/addons/account/report/account_journal.py b/addons/account/report/account_journal.py new file mode 100644 index 00000000..a327ef76 --- /dev/null +++ b/addons/account/report/account_journal.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +import time +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class ReportJournal(models.AbstractModel): + _name = 'report.account.report_journal' + _description = 'Account Journal Report' + + def lines(self, target_move, journal_ids, sort_selection, data): + if isinstance(journal_ids, int): + journal_ids = [journal_ids] + + move_state = ['draft', 'posted'] + if target_move == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_ids)] + query_get_clause[2] + query = 'SELECT "account_move_line".id FROM ' + query_get_clause[0] + ', account_move am, account_account acc WHERE "account_move_line".account_id = acc.id AND "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ORDER BY ' + if sort_selection == 'date': + query += '"account_move_line".date' + else: + query += 'am.name' + query += ', "account_move_line".move_id, acc.code' + self.env.cr.execute(query, tuple(params)) + ids = (x[0] for x in self.env.cr.fetchall()) + return self.env['account.move.line'].browse(ids) + + def _sum_debit(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2] + self.env.cr.execute('SELECT SUM(debit) FROM ' + query_get_clause[0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ', + tuple(params)) + return self.env.cr.fetchone()[0] or 0.0 + + def _sum_credit(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2] + self.env.cr.execute('SELECT SUM(credit) FROM ' + query_get_clause[0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' ', + tuple(params)) + return self.env.cr.fetchone()[0] or 0.0 + + def _get_taxes(self, data, journal_id): + move_state = ['draft', 'posted'] + if data['form'].get('target_move', 'all') == 'posted': + move_state = ['posted'] + + query_get_clause = self._get_query_get_clause(data) + params = [tuple(move_state), tuple(journal_id.ids)] + query_get_clause[2] + query = """ + SELECT rel.account_tax_id, SUM("account_move_line".balance) AS base_amount + FROM account_move_line_account_tax_rel rel, """ + query_get_clause[0] + """ + LEFT JOIN account_move am ON "account_move_line".move_id = am.id + WHERE "account_move_line".id = rel.account_move_line_id + AND am.state IN %s + AND "account_move_line".journal_id IN %s + AND """ + query_get_clause[1] + """ + GROUP BY rel.account_tax_id""" + self.env.cr.execute(query, tuple(params)) + ids = [] + base_amounts = {} + for row in self.env.cr.fetchall(): + ids.append(row[0]) + base_amounts[row[0]] = row[1] + + + res = {} + for tax in self.env['account.tax'].browse(ids): + self.env.cr.execute('SELECT sum(debit - credit) FROM ' + query_get_clause[0] + ', account_move am ' + 'WHERE "account_move_line".move_id=am.id AND am.state IN %s AND "account_move_line".journal_id IN %s AND ' + query_get_clause[1] + ' AND tax_line_id = %s', + tuple(params + [tax.id])) + res[tax] = { + 'base_amount': base_amounts[tax.id], + 'tax_amount': self.env.cr.fetchone()[0] or 0.0, + } + if journal_id.type == 'sale': + #sales operation are credits + res[tax]['base_amount'] = res[tax]['base_amount'] * -1 + res[tax]['tax_amount'] = res[tax]['tax_amount'] * -1 + return res + + def _get_query_get_clause(self, data): + return self.env['account.move.line'].with_context(data['form'].get('used_context', {}))._query_get() + + @api.model + def _get_report_values(self, docids, data=None): + if not data.get('form'): + raise UserError(_("Form content is missing, this report cannot be printed.")) + + target_move = data['form'].get('target_move', 'all') + sort_selection = data['form'].get('sort_selection', 'date') + + res = {} + for journal in data['form']['journal_ids']: + res[journal] = self.with_context(data['form'].get('used_context', {})).lines(target_move, journal, sort_selection, data) + return { + 'doc_ids': data['form']['journal_ids'], + 'doc_model': self.env['account.journal'], + 'data': data, + 'docs': self.env['account.journal'].browse(data['form']['journal_ids']), + 'time': time, + 'lines': res, + 'sum_credit': self._sum_credit, + 'sum_debit': self._sum_debit, + 'get_taxes': self._get_taxes, + 'company_id': self.env['res.company'].browse( + data['form']['company_id'][0]), + } -- cgit v1.2.3