mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[ADD] product_cores_report: for Odoo 13
This commit is contained in:
3
product_cores_report/__init__.py
Normal file
3
product_cores_report/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import models
|
||||||
21
product_cores_report/__manifest__.py
Normal file
21
product_cores_report/__manifest__.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Product Core Reporting',
|
||||||
|
'version': '13.0.1.0.0',
|
||||||
|
'category': 'Account',
|
||||||
|
'author': 'Hibou Corp.',
|
||||||
|
'license': 'OPL-1',
|
||||||
|
'website': 'https://hibou.io/',
|
||||||
|
'depends': [
|
||||||
|
'account_reports',
|
||||||
|
'product_cores',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/account_views.xml',
|
||||||
|
'views/report_templates.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': True,
|
||||||
|
'application': False,
|
||||||
|
}
|
||||||
3
product_cores_report/models/__init__.py
Normal file
3
product_cores_report/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from . import product_core_aged
|
||||||
211
product_cores_report/models/product_core_aged.py
Normal file
211
product_cores_report/models/product_core_aged.py
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import models, api, fields, _
|
||||||
|
from odoo.tools.misc import format_date
|
||||||
|
|
||||||
|
|
||||||
|
class ProductCoreAgedReport(models.AbstractModel):
|
||||||
|
_name = 'product.core.aged.report'
|
||||||
|
_description = 'Aged Product Cores'
|
||||||
|
_inherit = 'account.report'
|
||||||
|
|
||||||
|
filter_date = {'mode': 'single', 'filter': 'today'}
|
||||||
|
filter_unfold_all = False
|
||||||
|
filter_partner = True
|
||||||
|
order_selected_column = {'default': 0}
|
||||||
|
|
||||||
|
def _get_columns_name(self, options):
|
||||||
|
columns = [
|
||||||
|
{},
|
||||||
|
{'name': _('Partner'), 'class': '', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _('Date'), 'class': 'date', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _('Exp. Date'), 'class': 'date', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _('Account'), 'class': '', 'style': 'text-align:center; white-space:nowrap;'},
|
||||||
|
{'name': _("As of: %s") % format_date(self.env, options['date']['date_to']), 'class': 'number sortable', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _("Qty."), 'class': 'number sortable', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _("Expired"), 'class': 'number sortable', 'style': 'white-space:nowrap;'},
|
||||||
|
{'name': _("Exp. Qty."), 'class': 'number sortable', 'style': 'white-space:nowrap;'},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def _get_templates(self):
|
||||||
|
templates = super(ProductCoreAgedReport, self)._get_templates()
|
||||||
|
templates['main_template'] = 'product_cores_report.template_product_core_aged_report'
|
||||||
|
templates['line_template'] = 'product_cores_report.template_product_core_aged_line_report'
|
||||||
|
return templates
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_lines(self, options, line_id=None):
|
||||||
|
# We may need to reverse the sign at some point in the future
|
||||||
|
sign = 1.0
|
||||||
|
ctx = self._context
|
||||||
|
lines = []
|
||||||
|
cr = self.env.cr
|
||||||
|
user_company = self.env.company
|
||||||
|
company_ids = self._context.get('company_ids') or [user_company.id]
|
||||||
|
|
||||||
|
# expand line, filter just for this product
|
||||||
|
product = False
|
||||||
|
if line_id and 'product_' in line_id:
|
||||||
|
# we only want to fetch data about this product because we are expanding a line
|
||||||
|
product_id_str = line_id.split('_')[1]
|
||||||
|
if product_id_str.isnumeric():
|
||||||
|
product = self.env['product.product'].browse(int(product_id_str))
|
||||||
|
|
||||||
|
date_from = fields.Date.from_string(self._context['date_to'])
|
||||||
|
|
||||||
|
# put in to constants CTE for easier query
|
||||||
|
arg_list = (date_from, )
|
||||||
|
|
||||||
|
# product filtering
|
||||||
|
product_clause = ''
|
||||||
|
if product:
|
||||||
|
product_clause = 'AND (pp.id IN %s)'
|
||||||
|
arg_list += (tuple(product.ids), )
|
||||||
|
|
||||||
|
# partner filtering
|
||||||
|
partner_clause = ''
|
||||||
|
if 'partner_ids' in ctx:
|
||||||
|
if ctx['partner_ids']:
|
||||||
|
partner_clause = 'AND (l.partner_id IN %s)'
|
||||||
|
arg_list += (tuple(ctx['partner_ids'].ids),)
|
||||||
|
else:
|
||||||
|
partner_clause = 'AND l.partner_id IS NULL'
|
||||||
|
if ctx.get('partner_categories'):
|
||||||
|
partner_clause += 'AND (l.partner_id IN %s)'
|
||||||
|
partner_ids = self.env['res.partner'].search([('category_id', 'in', ctx['partner_categories'].ids)]).ids
|
||||||
|
arg_list += (tuple(partner_ids or [0]),)
|
||||||
|
|
||||||
|
query = '''
|
||||||
|
WITH
|
||||||
|
constants (date_from) AS (VALUES (%s)),
|
||||||
|
product_cores AS (
|
||||||
|
SELECT pp.id AS id, pt.product_core_validity AS product_core_validity, pt.name AS product_name
|
||||||
|
FROM product_template AS pt
|
||||||
|
INNER JOIN product_product AS pp ON pp.product_tmpl_id = pt.id
|
||||||
|
WHERE pt.core_ok = true
|
||||||
|
AND pt.type = 'service'
|
||||||
|
''' + product_clause + '''
|
||||||
|
)
|
||||||
|
SELECT pc.id AS product_id,
|
||||||
|
MAX(pc.product_name) AS product_name,
|
||||||
|
MAX(UPPER(pc.product_name)) AS UPNAME,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) < (SELECT date_from FROM constants) AND l.debit != 0 THEN l.quantity
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) < (SELECT date_from FROM constants) THEN -l.quantity
|
||||||
|
ELSE 0.0 END) AS total_expired_qty,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) < (SELECT date_from FROM constants) THEN l.debit
|
||||||
|
ELSE 0.0 END) AS total_expired_debit,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) < (SELECT date_from FROM constants) THEN l.credit
|
||||||
|
ELSE 0.0 END) AS total_expired_credit,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants) AND l.debit != 0 THEN l.quantity
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants) THEN -l.quantity
|
||||||
|
ELSE 0.0 END) AS total_qty,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants) THEN l.debit
|
||||||
|
ELSE 0.0 END) AS total_debit,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants) THEN l.credit
|
||||||
|
ELSE 0.0 END) AS total_credit,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants) THEN l.credit
|
||||||
|
ELSE 0.0 END) AS total_credit,
|
||||||
|
array_agg(l.id) FILTER (WHERE COALESCE(l.date_maturity, l.date) >= (SELECT date_from FROM constants)) AS aml_ids
|
||||||
|
FROM account_move_line AS l
|
||||||
|
INNER JOIN product_cores AS pc ON l.product_id = pc.id
|
||||||
|
WHERE (l.date <= (SELECT date_from FROM constants))
|
||||||
|
''' + partner_clause + '''
|
||||||
|
AND (l.company_id IN %s)
|
||||||
|
GROUP BY pc.id
|
||||||
|
ORDER BY UPNAME ASC
|
||||||
|
'''
|
||||||
|
arg_list += (tuple(company_ids), )
|
||||||
|
cr.execute(query, arg_list)
|
||||||
|
|
||||||
|
totals = {'total': 0.0, 'total_qty': 0.0, 'total_expired': 0.0, 'total_expired_qty': 0.0}
|
||||||
|
product_cores = cr.dictfetchall()
|
||||||
|
for product_core in product_cores:
|
||||||
|
ref = 'product_%s' % (product_core['product_id'], )
|
||||||
|
product_total = product_core['total_debit'] - product_core['total_credit']
|
||||||
|
product_total_qty = product_core['total_qty']
|
||||||
|
product_expired_total = product_core['total_expired_debit'] - product_core['total_expired_credit']
|
||||||
|
product_expired_total_qty = product_core['total_expired_qty']
|
||||||
|
totals['total'] += product_total
|
||||||
|
totals['total_qty'] += product_total_qty
|
||||||
|
totals['total_expired'] += product_expired_total
|
||||||
|
totals['total_expired_qty'] += product_expired_total_qty
|
||||||
|
vals = {
|
||||||
|
'id': ref,
|
||||||
|
'name': product_core['product_name'],
|
||||||
|
'level': 2,
|
||||||
|
'columns': [{'name': ''}] * 4 +
|
||||||
|
[{'name': self.format_value(sign * product_total), 'no_format': sign * product_total},
|
||||||
|
{'name': sign * product_total_qty, 'no_format': sign * product_total_qty},
|
||||||
|
{'name': self.format_value(sign * product_expired_total), 'no_format': sign * product_expired_total},
|
||||||
|
{'name': sign * product_expired_total_qty, 'no_format': sign * product_expired_total_qty},
|
||||||
|
],
|
||||||
|
'unfoldable': True,
|
||||||
|
'unfolded': ref in options.get('unfolded_lines'),
|
||||||
|
}
|
||||||
|
lines.append(vals)
|
||||||
|
if ref in options.get('unfolded_lines'):
|
||||||
|
amls = self.env['account.move.line'].browse(product_core['aml_ids'])
|
||||||
|
for aml in amls:
|
||||||
|
if aml.move_id.is_purchase_document():
|
||||||
|
caret_type = 'account.invoice.in'
|
||||||
|
elif aml.move_id.is_sale_document():
|
||||||
|
caret_type = 'account.invoice.out'
|
||||||
|
elif aml.payment_id:
|
||||||
|
caret_type = 'account.payment'
|
||||||
|
else:
|
||||||
|
caret_type = 'account.move'
|
||||||
|
|
||||||
|
expires = aml.date_maturity if aml.date_maturity else aml.date
|
||||||
|
expired = expires < date_from
|
||||||
|
amount = aml.debit - aml.credit
|
||||||
|
amount_not_expired = amount if not expired else 0.0
|
||||||
|
amount_expired = amount if expired else 0.0
|
||||||
|
qty = aml.quantity if aml.debit else -aml.quantity
|
||||||
|
qty_not_expired = qty if not expired else 0.0
|
||||||
|
qty_expired = qty if expired else 0.0
|
||||||
|
|
||||||
|
vals = {
|
||||||
|
'id': aml.id,
|
||||||
|
'name': aml.move_id.name,
|
||||||
|
'class': 'date',
|
||||||
|
'caret_options': caret_type,
|
||||||
|
'level': 4,
|
||||||
|
'parent_id': ref,
|
||||||
|
'columns': [{'name': v} for v in [aml.partner_id.display_name or 'Undefined', format_date(self.env, aml.date), format_date(self.env, expires), aml.account_id.display_name]] +
|
||||||
|
[{'name': self.format_value(sign * amount_not_expired, blank_if_zero=True), 'no_format': sign * amount_not_expired},
|
||||||
|
{'name': sign * qty_not_expired or '', 'no_format': sign * qty_not_expired},
|
||||||
|
{'name': self.format_value(sign * amount_expired, blank_if_zero=True), 'no_format': sign * amount_expired},
|
||||||
|
{'name': sign * qty_expired or '', 'no_format': sign * qty_expired},
|
||||||
|
],
|
||||||
|
'action_context': {
|
||||||
|
'default_type': aml.move_id.type,
|
||||||
|
'default_journal_id': aml.move_id.journal_id.id,
|
||||||
|
},
|
||||||
|
'title_hover': self._format_aml_name(aml.name, aml.ref, aml.move_id.name),
|
||||||
|
}
|
||||||
|
lines.append(vals)
|
||||||
|
|
||||||
|
if not line_id:
|
||||||
|
total_line = {
|
||||||
|
'id': 0,
|
||||||
|
'name': _('Total'),
|
||||||
|
'class': 'total',
|
||||||
|
'level': 2,
|
||||||
|
'columns': [{'name': ''}] * 4 +
|
||||||
|
[{'name': self.format_value(sign * totals['total']), 'no_format': sign * totals['total']},
|
||||||
|
{'name': sign * totals['total_qty'], 'no_format': sign * totals['total_qty']},
|
||||||
|
{'name': self.format_value(sign * totals['total_expired']), 'no_format': sign * totals['total_expired']},
|
||||||
|
{'name': sign * totals['total_expired_qty'], 'no_format': sign * totals['total_expired_qty']},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
lines.append(total_line)
|
||||||
|
|
||||||
|
return lines
|
||||||
13
product_cores_report/views/account_views.xml
Normal file
13
product_cores_report/views/account_views.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="action_product_core_aged_report" model="ir.actions.client">
|
||||||
|
<field name="name">Product Cores</field>
|
||||||
|
<field name="tag">account_report</field>
|
||||||
|
<field name="context" eval="{'model': 'product.core.aged.report'}" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_action_product_core_aged_report" name="Product Cores"
|
||||||
|
action="action_product_core_aged_report" parent="account.account_reports_management_menu"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
12
product_cores_report/views/report_templates.xml
Normal file
12
product_cores_report/views/report_templates.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<template id="template_product_core_aged_report" inherit_id="account_reports.main_template" primary="True">
|
||||||
|
<xpath expr="//table" position="attributes">
|
||||||
|
<attribute name="class">o_account_reports_table table-striped table-hover</attribute>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="template_product_core_aged_line_report" inherit_id="account_reports.line_template" primary="True" />
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user