181 lines
9.1 KiB
Python
181 lines
9.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from odoo import models, fields, api
|
|
from odoo.addons.account.models.account_move import PAYMENT_STATE_SELECTION
|
|
|
|
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='Main Partner')
|
|
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=PAYMENT_STATE_SELECTION, 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)])
|
|
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")
|
|
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',
|
|
],
|
|
'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', 'standard_price'],
|
|
'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.journal_id,
|
|
line.company_id,
|
|
line.company_currency_id,
|
|
line.partner_id AS commercial_partner_id,
|
|
account.account_type AS user_type,
|
|
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,
|
|
line.price_total * (CASE WHEN move.move_type IN ('in_invoice','out_refund','in_receipt') THEN -1 ELSE 1 END)
|
|
AS price_total,
|
|
-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,
|
|
CASE
|
|
WHEN move.move_type NOT IN ('out_invoice', 'out_receipt') THEN 0.0
|
|
ELSE -line.balance * currency_table.rate - (line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0)) * product_standard_price.value_float
|
|
END
|
|
AS price_margin,
|
|
line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('out_invoice','in_refund','out_receipt') THEN -1 ELSE 1 END)
|
|
* product_standard_price.value_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 '''
|
|
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 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
|
|
LEFT JOIN ir_property product_standard_price
|
|
ON product_standard_price.res_id = CONCAT('product.product,', product.id)
|
|
AND product_standard_price.name = 'standard_price'
|
|
JOIN {currency_table} ON currency_table.company_id = line.company_id
|
|
'''.format(
|
|
currency_table=self.env['res.currency']._get_query_currency_table(self.env.companies.ids, 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 line.display_type = 'product'
|
|
'''
|
|
|
|
|
|
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(silent_errors=data['report_type'] == 'html')
|
|
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
|