19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:30:07 +01:00
parent ba20ce7443
commit 768b70e05e
2357 changed files with 1057103 additions and 712486 deletions

View file

@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.tools import SQL
from odoo.tools.query import Query
from odoo.addons.account.models.account_move import PAYMENT_STATE_SELECTION
from functools import lru_cache
class AccountInvoiceReport(models.Model):
_name = "account.invoice.report"
_name = 'account.invoice.report'
_description = "Invoices Statistics"
_auto = False
_rec_name = 'invoice_date'
@ -40,39 +42,44 @@ class AccountInvoiceReport(models.Model):
# ==== 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_uom_id = fields.Many2one('uom.uom', string='Unit', 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)])
price_subtotal = fields.Float(string='Untaxed Total', readonly=True)
price_total = fields.Float(string='Total in Currency', readonly=True)
price_average = fields.Float(string='Average Price', readonly=True, group_operator="avg")
account_id = fields.Many2one('account.account', string='Revenue/Expense Account', readonly=True)
price_subtotal_currency = fields.Float(string='Untaxed Amount in Currency', readonly=True)
price_subtotal = fields.Float(string='Untaxed Amount', readonly=True)
price_total = fields.Float(string='Total', readonly=True)
price_total_currency = fields.Float(string='Total in Currency', readonly=True)
price_average = fields.Float(string='Average Price', readonly=True, aggregator="avg")
price_margin = fields.Float(string='Margin', readonly=True)
inventory_value = fields.Float(string='Inventory Value', readonly=True)
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True)
_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',
'invoice_date', 'invoice_date_due', 'invoice_payment_term_id', 'partner_bank_id', 'invoice_currency_rate',
],
'account.move.line': [
'quantity', 'price_subtotal', 'price_total', 'amount_residual', 'balance', 'amount_currency',
'move_id', 'product_id', 'product_uom_id', 'account_id',
'journal_id', 'company_id', 'currency_id', 'partner_id',
],
'product.product': ['product_tmpl_id'],
'product.product': ['product_tmpl_id', 'standard_price'],
'product.template': ['categ_id'],
'uom.uom': ['category_id', 'factor', 'name', 'uom_type'],
'uom.uom': ['factor', 'name'],
'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())
def _table_query(self) -> SQL:
return SQL('%s %s %s', self._select(), self._from(), self._where())
@api.model
def _select(self):
return '''
def _select(self) -> SQL:
return SQL(
'''
SELECT
line.id,
line.move_id,
@ -93,24 +100,39 @@ class AccountInvoiceReport(models.Model):
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)
line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(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,
line.price_subtotal * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
AS price_subtotal_currency,
-line.balance * account_currency_table.rate AS price_subtotal,
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
/ move.invoice_currency_rate
AS price_total,
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
AS price_total_currency,
-COALESCE(
-- Average line price
(line.balance / NULLIF(line.quantity, 0.0)) * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
-- 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,
/ NULLIF(COALESCE(uom_line.factor, 1), 0.0) * COALESCE(uom_template.factor, 1),
0.0) * account_currency_table.rate AS price_average,
CASE
WHEN move.move_type NOT IN ('out_invoice', 'out_receipt', 'out_refund') THEN 0.0
WHEN move.move_type = 'out_refund' THEN account_currency_table.rate * (-line.balance + (line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)) * COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float)
ELSE account_currency_table.rate * (-line.balance - (line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)) * COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float)
END
AS price_margin,
account_currency_table.rate * line.quantity * COALESCE(uom_line.factor, 1) / NULLIF(COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('out_invoice','in_refund','out_receipt') THEN -1 ELSE 1 END)
* COALESCE(product.standard_price -> line.company_id::text, to_jsonb(0.0))::float AS inventory_value,
COALESCE(partner.country_id, commercial_partner.country_id) AS country_id,
line.currency_id AS currency_id
'''
''',
)
@api.model
def _from(self):
return '''
def _from(self) -> SQL:
return SQL(
'''
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
@ -120,45 +142,33 @@ class AccountInvoiceReport(models.Model):
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()}}),
JOIN %(currency_table)s ON account_currency_table.company_id = line.company_id
''',
currency_table=self.env['res.currency']._get_simple_currency_table(self.env.companies),
)
@api.model
def _where(self):
return '''
def _where(self) -> SQL:
return SQL(
'''
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 line.display_type = 'product'
'''
''',
)
@api.model
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
"""
This is a hack to allow us to correctly calculate the average price.
"""
set_fields = set(fields)
if 'price_average:avg' in fields:
set_fields.add('quantity:sum')
set_fields.add('price_subtotal:sum')
res = super().read_group(domain, list(set_fields), groupby, offset, limit, orderby, lazy)
if 'price_average:avg' in fields:
for data in res:
data['price_average'] = data['price_subtotal'] / data['quantity'] if data['quantity'] else 0
if 'quantity:sum' not in fields:
del data['quantity']
if 'price_subtotal:sum' not in fields:
del data['price_subtotal']
return res
def _read_group_select(self, aggregate_spec: str, query: Query) -> SQL:
""" This override allows us to correctly calculate the average price of products. """
if aggregate_spec != 'price_average:avg':
return super()._read_group_select(aggregate_spec, query)
return SQL(
'COALESCE(SUM(%(f_price)s) / NULLIF(SUM(%(f_qty)s), 0.0), 0)',
f_qty=self._field_to_sql(self._table, 'quantity', query),
f_price=self._field_to_sql(self._table, 'price_subtotal', query),
)
class ReportInvoiceWithoutPayment(models.AbstractModel):
class ReportAccountReport_Invoice(models.AbstractModel):
_name = 'report.account.report_invoice'
_description = 'Account report without payment lines'
@ -180,10 +190,11 @@ class ReportInvoiceWithoutPayment(models.AbstractModel):
'qr_code_urls': qr_code_urls,
}
class ReportInvoiceWithPayment(models.AbstractModel):
class ReportAccountReport_Invoice_With_Payments(models.AbstractModel):
_name = 'report.account.report_invoice_with_payments'
_description = 'Account report with payment lines'
_inherit = 'report.account.report_invoice'
_inherit = ['report.account.report_invoice']
@api.model
def _get_report_values(self, docids, data=None):