-
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg | ||
:alt: License: AGPL-3 | ||
|
||
Purchase Stock Analysis | ||
======================= | ||
|
||
This module provides a new "Purchase Stock Analysis" report that extends the | ||
one called "Purchase Analysis" included in the standard Purchase module, adding | ||
a few metrics to assess the performance of suppliers. | ||
|
||
Usage | ||
===== | ||
|
||
This module adds a new report "Purchase Stock Analysis" under | ||
Reporting/Purchase. In addition to the information found in | ||
"Purchase Analysis", some new measures are added: | ||
|
||
* Days from initial to updated schedule | ||
|
||
* Days from initial to updated Scheduled Date of delivery | ||
|
||
* Days from the updated Scheduled Date to the actual Delivery Date | ||
|
||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas | ||
:alt: Try me on Runbot | ||
:target: https://runbot.odoo-community.org/runbot/141/8.0 | ||
|
||
For further information, please visit: | ||
|
||
* https://www.odoo.com/forum/help-1 | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/purchase-reporting/issues>`_. | ||
In case of trouble, please check there if your issue has already been reported. | ||
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback | ||
`here <https://github.com/OCA/purchase-reporting/issues/new?body=module:%20purchase_stock_analysis%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. | ||
|
||
|
||
Credits | ||
======= | ||
|
||
Contributors | ||
------------ | ||
|
||
* Leonardo Pistone <leonardo.pistone@camptocamp.com> | ||
|
||
Maintainer | ||
---------- | ||
|
||
.. image:: https://odoo-community.org/logo.png | ||
:alt: Odoo Community Association | ||
:target: https://odoo-community.org | ||
|
||
This module is maintained by the OCA. | ||
|
||
OCA, or the Odoo Community Association, is a nonprofit organization whose | ||
mission is to support the collaborative development of Odoo features and | ||
promote its widespread use. | ||
|
||
To contribute to this module, please visit http://odoo-community.org. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import model |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# -*- coding: utf-8 -*- | ||
# Author: Leonardo Pistone | ||
# Copyright 2015 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
{ | ||
'name': 'Purchase Stock Analysis', | ||
'version': '1.0', | ||
'category': 'Purchases', | ||
'author': "Camptocamp,Odoo Community Association (OCA)", | ||
'website': 'http://www.camptocamp.com', | ||
'depends': ['purchase'], | ||
'data': ['view/purchase_stock_analysis.xml'], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import purchase_stock_analysis |
181 changes: 181 additions & 0 deletions
181
purchase_stock_analysis/model/purchase_stock_analysis.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
# -*- coding: utf-8 -*- | ||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). | ||
# Copyright 2015 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
from openerp import tools, models, fields | ||
|
||
|
||
class PurchaseStockAnalysis(models.Model): | ||
_name = "purchase.stock.analysis" | ||
_description = "Purchases Stock Analysis" | ||
_auto = False | ||
|
||
date_order = fields.Datetime( | ||
readonly=True, | ||
help="Date on which this document has been created") | ||
state = fields.Selection( | ||
[('draft', 'Request for Quotation'), | ||
('confirmed', 'Waiting Supplier Ack'), | ||
('approved', 'Approved'), | ||
('except_picking', 'Shipping Exception'), | ||
('except_invoice', 'Invoice Exception'), | ||
('done', 'Done'), | ||
('cancel', 'Cancelled')], readonly=True) | ||
product_id = fields.Many2one('product.product', readonly=True) | ||
picking_type_id = fields.Many2one('stock.warehouse', readonly=True) | ||
location_id = fields.Many2one('stock.location', 'Destination', | ||
readonly=True) | ||
partner_id = fields.Many2one('res.partner', 'Supplier', readonly=True) | ||
pricelist_id = fields.Many2one('product.pricelist', 'Pricelist', | ||
readonly=True) | ||
date_approve = fields.Date('Date Approved', readonly=True) | ||
expected_date = fields.Date('Expected Date', readonly=True) | ||
validator = fields.Many2one('res.users', 'Validated By', readonly=True) | ||
product_uom = fields.Many2one('product.uom', 'Reference Unit of Measure', | ||
required=True) | ||
company_id = fields.Many2one('res.company', 'Company', readonly=True) | ||
user_id = fields.Many2one('res.users', 'Responsible', readonly=True) | ||
|
||
delay = fields.Float( | ||
'Days to Validate', digits=(16, 2), readonly=True, | ||
help='Days between the Order Date and the Date Approval') | ||
|
||
# was delay_pass in purchase analysis | ||
days_to_deliver = fields.Float( | ||
digits=(16, 2), readonly=True, | ||
help='Days between the Order Date and the initial Scheduled Date') | ||
|
||
days_initial_to_updated_schedule = fields.Float( | ||
digits=(16, 2), readonly=True, | ||
help='Days from initial to updated Scheduled Date of Delivery') | ||
|
||
days_schedule_to_actual_delivery = fields.Float( | ||
digits=(16, 2), readonly=True, | ||
help='Days from the updated Scheduled Date of Delivery and the actual' | ||
'Delivery Date') | ||
|
||
unit_quantity = fields.Integer('Unit Quantity', readonly=True) | ||
price_total = fields.Float('Total Price', readonly=True) | ||
price_average = fields.Float('Average Price', readonly=True, | ||
group_operator="avg") | ||
negociation = fields.Float('Purchase-Standard Price', readonly=True, | ||
group_operator="avg") | ||
price_standard = fields.Float(readonly=True, group_operator="sum") | ||
category_id = fields.Many2one('product.category', readonly=True) | ||
|
||
_order = 'date_order desc, price_total desc' | ||
|
||
def init(self, cr): | ||
tools.sql.drop_view_if_exists(cr, 'purchase_stock_analysis') | ||
cr.execute(""" | ||
create or replace view purchase_stock_analysis as ( | ||
WITH currency_rate (currency_id, rate, date_start, date_end) | ||
AS ( | ||
SELECT r.currency_id, r.rate, r.name AS date_start, | ||
(SELECT name FROM res_currency_rate r2 | ||
WHERE r2.name > r.name AND | ||
r2.currency_id = r.currency_id | ||
ORDER BY r2.name ASC | ||
LIMIT 1) AS date_end | ||
FROM res_currency_rate r | ||
) | ||
select | ||
min(l.id) as id, | ||
s.date_order as date_order, | ||
l.state, | ||
s.date_approve, | ||
s.minimum_planned_date as expected_date, | ||
s.dest_address_id, | ||
s.pricelist_id, | ||
s.validator, | ||
spt.warehouse_id as picking_type_id, | ||
s.partner_id as partner_id, | ||
s.create_uid as user_id, | ||
s.company_id as company_id, | ||
l.product_id, | ||
t.categ_id as category_id, | ||
t.uom_id as product_uom, | ||
s.location_id as location_id, | ||
sum(l.product_qty/u.factor*u2.factor) as unit_quantity, | ||
extract(epoch from age(s.date_approve,s.date_order)) / | ||
(24*60*60)::decimal(16,2) as delay, | ||
extract(epoch from age(l.date_planned,s.date_order)) / | ||
(24*60*60)::decimal(16,2) as days_to_deliver, | ||
extract(epoch from move.date_expected - l.date_planned) | ||
/ (24*60*60)::decimal(16,2) | ||
as days_initial_to_updated_schedule, | ||
extract(epoch from move.date - move.date_expected) | ||
/ (24*60*60)::decimal(16,2) | ||
as days_schedule_to_actual_delivery, | ||
sum(l.price_unit * cr.rate * l.product_qty)::decimal(16, 2) | ||
as price_total, | ||
avg(100.0 * (l.price_unit * cr.rate * l.product_qty) / | ||
NULLIF(ip.value_float * l.product_qty / u.factor | ||
* u2.factor, 0.0))::decimal(16,2) as negociation, | ||
sum(ip.value_float * l.product_qty/u.factor | ||
* u2.factor)::decimal(16,2) as price_standard, | ||
(sum(l.product_qty*cr.rate*l.price_unit)/ | ||
NULLIF(sum(l.product_qty/u.factor*u2.factor), | ||
0.0))::decimal(16,2) as price_average | ||
from purchase_order_line l | ||
join purchase_order s on (l.order_id=s.id) | ||
left join product_product p on (l.product_id=p.id) | ||
left join product_template t | ||
on (p.product_tmpl_id=t.id) | ||
LEFT JOIN ir_property ip ON | ||
(ip.name='standard_price' | ||
AND ip.res_id=CONCAT('product.template,',t.id) | ||
AND ip.company_id=s.company_id) | ||
left join product_uom u on (u.id=l.product_uom) | ||
left join product_uom u2 on (u2.id=t.uom_id) | ||
left join stock_picking_type spt | ||
on (spt.id=s.picking_type_id) | ||
join currency_rate cr on (cr.currency_id = s.currency_id | ||
and cr.date_start <= coalesce(s.date_order, now()) and | ||
(cr.date_end is null | ||
or cr.date_end > coalesce(s.date_order, now()))) | ||
left join stock_move move on (move.purchase_line_id=l.id) | ||
group by | ||
s.company_id, | ||
s.create_uid, | ||
s.partner_id, | ||
u.factor, | ||
s.location_id, | ||
l.price_unit, | ||
s.date_approve, | ||
l.date_planned, | ||
l.product_uom, | ||
s.minimum_planned_date, | ||
s.pricelist_id, | ||
s.validator, | ||
s.dest_address_id, | ||
l.product_id, | ||
t.categ_id, | ||
s.date_order, | ||
l.state, | ||
spt.warehouse_id, | ||
u.uom_type, | ||
u.category_id, | ||
t.uom_id, | ||
u.id, | ||
u2.factor, | ||
move.date, | ||
move.date_expected | ||
) | ||
""") |
104 changes: 104 additions & 0 deletions
104
purchase_stock_analysis/view/purchase_stock_analysis.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?xml version="1.0"?> | ||
<openerp> | ||
<data> | ||
|
||
<record model="ir.ui.view" id="view_purchase_stock_analysis_graph"> | ||
<field name="name">view_purchase_stock_analysis_graph</field> | ||
<field name="model">purchase.stock.analysis</field> | ||
<field name="arch" type="xml"> | ||
<graph string="Purchase Stock Analysis" type="pivot"> | ||
<field name="partner_id" type="row"/> | ||
<field name="date_order" interval="month" type="col"/> | ||
<field name="price_total" type="measure"/> | ||
<field name="unit_quantity" type="measure"/> | ||
<field name="price_average" type="measure"/> | ||
</graph> | ||
</field> | ||
</record> | ||
|
||
<record id="filter_purchase_stock_analysis_monthly_purchases" model="ir.filters"> | ||
<field name="name">Monthly Purchases</field> | ||
<field name="model_id">purchase.stock.analysis</field> | ||
<field name="domain">[('state','<>','cancel')]</field> | ||
<field name="user_id" eval="False"/> | ||
<field name="context">{'group_by': ['category_id'], 'col_group_by': ['date_order:month'], 'measures': ['unit_quantity']}</field> | ||
</record> | ||
|
||
<record id="filter_purchase_stock_analysis_price_per_supplier" model="ir.filters"> | ||
<field name="name">Price Per Supplier</field> | ||
<field name="model_id">purchase.stock.analysis</field> | ||
<field name="domain">[('state','<>','draft'),('state','<>','cancel')]</field> | ||
<field name="user_id" eval="False"/> | ||
<field name="context">{'group_by': ['partner_id'], 'col_group_by': ['product_id'], 'measures': ['price_average']}</field> | ||
</record> | ||
|
||
<record id="filter_days_to_deliver" model="ir.filters"> | ||
<field name="name">Days to deliver</field> | ||
<field name="model_id">purchase.stock.analysis</field> | ||
<field name="domain">[('state','<>','draft'),('state','<>','cancel')]</field> | ||
<field name="user_id" eval="False"/> | ||
<field name="context">{'group_by': ['partner_id'], 'measures': ['days_to_deliver']}</field> | ||
</record> | ||
|
||
<record id="filter_days_initial_to_updated_schedule" model="ir.filters"> | ||
<field name="name">Days initial to updated schedule</field> | ||
<field name="model_id">purchase.stock.analysis</field> | ||
<field name="domain">[('state','<>','draft'),('state','<>','cancel')]</field> | ||
<field name="user_id" eval="False"/> | ||
<field name="context">{'group_by': ['partner_id'], 'measures': ['days_initial_to_updated_schedule']}</field> | ||
</record> | ||
|
||
<record id="filter_days_schedule_to_actual_delivery" model="ir.filters"> | ||
<field name="name">Days from schedule to actual delivery</field> | ||
<field name="model_id">purchase.stock.analysis</field> | ||
<field name="domain">[('state','<>','draft'),('state','<>','cancel')]</field> | ||
<field name="user_id" eval="False"/> | ||
<field name="context">{'group_by': ['partner_id'], 'measures': ['days_schedule_to_actual_delivery']}</field> | ||
</record> | ||
|
||
<record id="view_purchase_stock_analysis_search" model="ir.ui.view"> | ||
<field name="name">view_purchase_stock_analysis_search</field> | ||
<field name="model">purchase.stock.analysis</field> | ||
<field name="arch" type="xml"> | ||
<search string="Purchase Orders"> | ||
<filter string="Quotations" name="quotes" domain="[('state','=','draft')]"/> | ||
<filter string="Orders" name="orders" domain="[('state','<>','draft'),('state','<>','cancel')]"/> | ||
<field name="partner_id"/> | ||
<field name="product_id"/> | ||
<group expand="0" string="Extended Filters"> | ||
<field name="user_id"/> | ||
<field name="validator"/> | ||
<field name="location_id"/> | ||
<field name="picking_type_id"/> | ||
<field name="company_id" groups="base.group_multi_company"/> | ||
<field name="date_order"/> | ||
<field name="date_approve"/> | ||
<field name="expected_date"/> | ||
</group> | ||
<group expand="1" string="Group By"> | ||
<filter string="Supplier" name="group_partner_id" context="{'group_by':'partner_id'}"/> | ||
<filter string="Responsible" name="Responsible" context="{'group_by':'user_id'}"/> | ||
<filter string="Category of product" name="group_category_id" context="{'group_by':'category_id'}"/> | ||
<filter string="Status" context="{'group_by':'state'}"/> | ||
<filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> | ||
<separator/> | ||
<filter string="Order Month" context="{'group_by':'date_order:month'}" help="Order of Day"/> | ||
</group> | ||
</search> | ||
</field> | ||
</record> | ||
|
||
<record id="action_purchase_stock_analysis" model="ir.actions.act_window"> | ||
<field name="name">Purchase Stock Analysis</field> | ||
<field name="res_model">purchase.stock.analysis</field> | ||
<field name="view_type">form</field> | ||
<field name="view_mode">graph</field> | ||
<field name="view_id" ref="view_purchase_stock_analysis_graph"></field> | ||
<field name="context">{'search_default_orders': 1, 'group_by_no_leaf':1,'group_by':[]}</field> | ||
<field name="help">XXX</field> | ||
</record> | ||
|
||
<menuitem id="menu_purchase_stock_analysis" action="action_purchase_stock_analysis" parent="base.next_id_73" /> | ||
|
||
</data> | ||
</openerp> |