diff options
| -rw-r--r-- | addons/purchase_stock/__manifest__.py | 2 | ||||
| -rw-r--r-- | addons/purchase_stock/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/purchase_stock/models/purchase.py | 1 | ||||
| -rw-r--r-- | addons/purchase_stock/models/res_partner.py | 36 | ||||
| -rw-r--r-- | addons/purchase_stock/report/__init__.py | 1 | ||||
| -rw-r--r-- | addons/purchase_stock/report/vendor_delay_report.py | 73 | ||||
| -rw-r--r-- | addons/purchase_stock/report/vendor_delay_report.xml | 37 | ||||
| -rw-r--r-- | addons/purchase_stock/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/purchase_stock/views/purchase_views.xml | 13 | ||||
| -rw-r--r-- | addons/purchase_stock/views/res_partner_views.xml | 27 |
10 files changed, 193 insertions, 0 deletions
diff --git a/addons/purchase_stock/__manifest__.py b/addons/purchase_stock/__manifest__.py index 471374708a5..ae43f2adab8 100644 --- a/addons/purchase_stock/__manifest__.py +++ b/addons/purchase_stock/__manifest__.py @@ -14,10 +14,12 @@ 'data/purchase_stock_data.xml', 'data/mail_data.xml', 'views/assets.xml', + 'report/vendor_delay_report.xml', 'views/purchase_views.xml', 'views/stock_views.xml', 'views/stock_rule_views.xml', 'views/res_config_settings_views.xml', + 'views/res_partner_views.xml', 'views/stock_production_lot_views.xml', 'report/purchase_report_views.xml', 'report/purchase_report_templates.xml', diff --git a/addons/purchase_stock/models/__init__.py b/addons/purchase_stock/models/__init__.py index d822fea458a..85cc204cb90 100644 --- a/addons/purchase_stock/models/__init__.py +++ b/addons/purchase_stock/models/__init__.py @@ -6,5 +6,6 @@ from . import product from . import purchase from . import res_company from . import res_config_settings +from . import res_partner from . import stock from . import stock_rule diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index bfff1567238..5ff7c7f67fd 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -29,6 +29,7 @@ class PurchaseOrder(models.Model): is_shipped = fields.Boolean(compute="_compute_is_shipped") effective_date = fields.Datetime("Effective Date", compute='_compute_effective_date', store=True, copy=False, help="Completion date of the first receipt order.") + on_time_rate = fields.Float(related='partner_id.on_time_rate') @api.depends('order_line.move_ids.returned_move_ids', 'order_line.move_ids.state', diff --git a/addons/purchase_stock/models/res_partner.py b/addons/purchase_stock/models/res_partner.py new file mode 100644 index 00000000000..10289cde11c --- /dev/null +++ b/addons/purchase_stock/models/res_partner.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import timedelta + +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + purchase_line_ids = fields.One2many('purchase.order.line', 'partner_id', string="Purchase Lines") + on_time_rate = fields.Float( + "On-Time Delivery Rate", compute='_compute_on_time_rate', + help="Over the past 12 months, number of products received on time / total number of ordered products.") + + @api.depends('purchase_line_ids') + def _compute_on_time_rate(self): + order_lines = self.env['purchase.order.line'].search([ + ('partner_id', 'in', self.ids), + ('date_order', '>', fields.Date.today() - timedelta(365)), + ('qty_received', '!=', 0), + ]).filtered(lambda l: l.product_id.product_tmpl_id.type != 'service' and l.order_id.state in ['done', 'purchase']) + partner_dict = {} + for line in order_lines: + on_time, ordered = partner_dict.get(line.partner_id, (0, 0)) + ordered += line.product_uom_qty + on_time += sum(line.mapped('move_ids').filtered(lambda m: m.state == 'done' and m.date <= m.purchase_line_id.date_planned).mapped('quantity_done')) + partner_dict[line.partner_id] = (on_time, ordered) + seen_partner = self.env['res.partner'] + for partner, numbers in partner_dict.items(): + seen_partner |= partner + on_time, ordered = numbers + partner.on_time_rate = on_time / ordered * 100 if ordered else 100 + (self - seen_partner).on_time_rate = 100 + diff --git a/addons/purchase_stock/report/__init__.py b/addons/purchase_stock/report/__init__.py index 129d295b8be..dfdf3818fdb 100644 --- a/addons/purchase_stock/report/__init__.py +++ b/addons/purchase_stock/report/__init__.py @@ -3,3 +3,4 @@ from . import purchase_report from . import report_stock_rule +from . import vendor_delay_report diff --git a/addons/purchase_stock/report/vendor_delay_report.py b/addons/purchase_stock/report/vendor_delay_report.py new file mode 100644 index 00000000000..25cfaf6c25a --- /dev/null +++ b/addons/purchase_stock/report/vendor_delay_report.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, tools +from odoo.exceptions import UserError +from odoo.osv.expression import expression + + +class VendorDelayReport(models.Model): + _name = "vendor.delay.report" + _description = "Vendor Delay Report" + _auto = False + + partner_id = fields.Many2one('res.partner', 'Vendor', readonly=True) + product_id = fields.Many2one('product.product', 'Product', readonly=True) + category_id = fields.Many2one('product.category', 'Product Category', readonly=True) + date = fields.Datetime('Effective Date', readonly=True) + qty_total = fields.Float('Total Quantity', readonly=True) + qty_on_time = fields.Float('On-Time Quantity', readonly=True) + on_time_rate = fields.Float('On-Time Delivery Rate', readonly=True) + + def init(self): + tools.drop_view_if_exists(self.env.cr, 'vendor_delay_report') + self.env.cr.execute(""" +CREATE OR replace VIEW vendor_delay_report AS( +SELECT m.id AS id, + m.date AS date, + m.purchase_line_id AS purchase_line_id, + m.product_id AS product_id, + Min(pc.id) AS category_id, + Min(po.partner_id) AS partner_id, + Sum(pol.product_uom_qty) AS qty_total, + Sum(CASE + WHEN pol.date_planned >= m.date THEN ml.qty_done + ELSE 0 + END) AS qty_on_time +FROM stock_move m + JOIN stock_move_line ml + ON m.id = ml.move_id + JOIN purchase_order_line pol + ON pol.id = m.purchase_line_id + JOIN purchase_order po + ON po.id = pol.order_id + JOIN product_product p + ON p.id = m.product_id + JOIN product_template pt + ON pt.id = p.product_tmpl_id + JOIN product_category pc + ON pc.id = pt.categ_id +WHERE m.state = 'done' +GROUP BY m.id +)""") + + @api.model + def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): + if 'on_time_rate' not in fields: + res = super().read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) + return res + + fields.remove('on_time_rate') + if 'qty_total' not in fields: + fields.append('qty_total') + if 'qty_on_time' not in fields: + fields.append('qty_on_time') + res = super().read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) + for group in res: + if group['qty_total'] == 0: + on_time_rate = 100 + else: + on_time_rate = group['qty_on_time'] / group['qty_total'] * 100 + group.update({'on_time_rate': on_time_rate}) + + return res diff --git a/addons/purchase_stock/report/vendor_delay_report.xml b/addons/purchase_stock/report/vendor_delay_report.xml new file mode 100644 index 00000000000..7391d5eade7 --- /dev/null +++ b/addons/purchase_stock/report/vendor_delay_report.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record id="vendor_delay_report_filter" model="ir.ui.view"> + <field name="name">vendor.delay.report.search</field> + <field name="model">vendor.delay.report</field> + <field name="arch" type="xml"> + <search string="On-time Delivery"> + <field name="partner_id"/> + <field name="product_id"/> + <filter string="Effective Date Last Year" name="later_than_a_year_ago" domain="[('date', '>=', ((context_today()-relativedelta(years=1)).strftime('%Y-%m-%d')))]"/> + </search> + </field> + </record> + + <record id="vendor_delay_report_view_graph" model="ir.ui.view"> + <field name="name">vendor.delay.report.view.graph</field> + <field name="model">vendor.delay.report</field> + <field name="arch" type="xml"> + <graph string="On-Time Delivery" type="bar"> + <field name="product_id" type="row"/> + <field name="on_time_rate" type="measure"/> + </graph> + </field> + </record> + + <record id="action_purchase_vendor_delay_report" model="ir.actions.act_window"> + <field name="name">On-time Delivery</field> + <field name="res_model">vendor.delay.report</field> + <field name="view_mode">graph</field> + <field name="search_view_id" ref="vendor_delay_report_filter"/> + <field name="help">Vendor On-time Delivery analysis</field> + <field name="target">current</field> + <field name="context">{'search_default_later_than_a_year_ago':1}</field> + </record> + + <menuitem id="delay_report" name="Vendor Delay" parent="purchase.menu_purchase_root" sequence="99" groups="purchase.group_purchase_manager" action="action_purchase_vendor_delay_report"/> +</odoo> diff --git a/addons/purchase_stock/security/ir.model.access.csv b/addons/purchase_stock/security/ir.model.access.csv index 12c826e863b..faf2bb933ac 100644 --- a/addons/purchase_stock/security/ir.model.access.csv +++ b/addons/purchase_stock/security/ir.model.access.csv @@ -11,3 +11,5 @@ access_stock_picking_purchase_user_manager,stock.picking,stock.model_stock_picki access_stock_move_purchase_user_manager,stock.move,stock.model_stock_move,purchase.group_purchase_manager,1,1,1,1 access_stock_warehouse_orderpoint_manager,stock.warehouse.orderpoint,stock.model_stock_warehouse_orderpoint,purchase.group_purchase_manager,1,0,0,0 access_stock_warehouse_orderpoint_user,stock.warehouse.orderpoint,stock.model_stock_warehouse_orderpoint,purchase.group_purchase_user,1,0,0,0 +access_report_purchase_order,vendor.delay.report,model_vendor_delay_report,purchase.group_purchase_manager,1,0,0,0 +access_report_purchase_order_user,vendor.delay.report user,model_vendor_delay_report,purchase.group_purchase_user,1,0,0,0 diff --git a/addons/purchase_stock/views/purchase_views.xml b/addons/purchase_stock/views/purchase_views.xml index 9c239bc4ce7..981802114c5 100644 --- a/addons/purchase_stock/views/purchase_views.xml +++ b/addons/purchase_stock/views/purchase_views.xml @@ -18,6 +18,19 @@ <field name="picking_ids" invisible="1"/> </button> </xpath> + <xpath expr="//field[@name='partner_id']" position="replace"> + <label for="partner_id"/> + <div class="o_row"> + <field name="partner_id" widget="res_partner_many2one" context="{'res_partner_search_mode': 'supplier', 'show_vat': True}" + placeholder="Name, TIN, Email, or Reference" + /> + <div class="oe_edit_only"> + <button name="%(action_purchase_vendor_delay_report)d" type="action" class="oe_stat_button" context="{'search_default_partner_id': partner_id}"> + <field name="on_time_rate" class="text-muted" widget='percentpie'/> + </button> + </div> + </div> + </xpath> <xpath expr="//field[@name='currency_id']" position="after"> <field name="is_shipped" invisible="1"/> </xpath> diff --git a/addons/purchase_stock/views/res_partner_views.xml b/addons/purchase_stock/views/res_partner_views.xml new file mode 100644 index 00000000000..88bad643ea3 --- /dev/null +++ b/addons/purchase_stock/views/res_partner_views.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + + <record id="res_partner_view_purchase_buttons_inherit" model="ir.ui.view"> + <field name="name">res.partner.purchase.stock.form.inherit</field> + <field name="model">res.partner</field> + <field name="inherit_id" ref="purchase.res_partner_view_purchase_buttons"/> + <field name="arch" type="xml"> + <xpath expr="//field[@name='purchase_order_count']/.." position="after"> + <button class="oe_stat_button" name="%(action_purchase_vendor_delay_report)d" type="action" + groups="purchase.group_purchase_user" + icon="fa-truck" + context="{'search_default_partner_id': id}"> + <div class="o_form_field o_stat_info"> + <div class="o_row"> + <span class="o_stat_value"> + <field string="On-time Rate" name="on_time_rate" widget="integer"/> + </span> + <span class="o_stat_value">%</span> + </div> + <span class="o_stat_text">On-time Rate</span> + </div> + </button> + </xpath> + </field> + </record> +</odoo> |
