Odoo18-Base/addons/purchase_stock/report/purchase_report.py

71 lines
3.4 KiB
Python
Raw Permalink Normal View History

2025-03-10 11:12:23 +07:00
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.osv.expression import expression
class PurchaseReport(models.Model):
_inherit = "purchase.report"
picking_type_id = fields.Many2one('stock.warehouse', 'Warehouse', readonly=True)
avg_receipt_delay = fields.Float(
'Average Receipt Delay', digits=(16, 2), readonly=True, store=False, # needs store=False to prevent showing up as a 'measure' option
help="Amount of time between expected and effective receipt date. Due to a hack needed to calculate this, \
every record will show the same average value, therefore only use this as an aggregated value with group_operator=avg")
effective_date = fields.Datetime(string="Effective Date")
def _select(self):
return super(PurchaseReport, self)._select() + ", spt.warehouse_id as picking_type_id, po.effective_date as effective_date"
def _from(self):
return super(PurchaseReport, self)._from() + " left join stock_picking_type spt on (spt.id=po.picking_type_id)"
def _group_by(self):
return super(PurchaseReport, self)._group_by() + ", spt.warehouse_id, effective_date"
@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 of PO specific date values since
the normal report query result will duplicate PO values across its PO lines during joins and
lead to incorrect aggregation values.
Only the AVG operator is supported for avg_receipt_delay.
"""
avg_receipt_delay = next((field for field in fields if re.search(r'\bavg_receipt_delay\b', field)), False)
if avg_receipt_delay:
fields.remove(avg_receipt_delay)
if any(field.split(':')[1].split('(')[0] != 'avg' for field in [avg_receipt_delay] if field):
raise UserError("Value: 'avg_receipt_delay' should only be used to show an average. If you are seeing this message then it is being accessed incorrectly.")
res = []
if fields:
res = super(PurchaseReport, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
if not res and avg_receipt_delay:
res = [{}]
if avg_receipt_delay:
query = """ SELECT AVG(receipt_delay.po_receipt_delay)::decimal(16,2) AS avg_receipt_delay
FROM (
SELECT extract(epoch from age(po.effective_date, po.date_planned))/(24*60*60) AS po_receipt_delay
FROM purchase_order po
WHERE po.id IN (
SELECT "purchase_report"."order_id" FROM %s WHERE %s)
) AS receipt_delay
"""
subdomain = domain + [('company_id', '=', self.env.company.id), ('effective_date', '!=', False)]
subtables, subwhere, subparams = expression(subdomain, self).query.get_sql()
self.env.cr.execute(query % (subtables, subwhere), subparams)
res[0].update({
'__count': 1,
avg_receipt_delay.split(':')[0]: self.env.cr.fetchall()[0][0],
})
return res