Skip to content

Commit 8acb6c2

Browse files
MiquelRForgeFlowsantostelmo
authored andcommitted
[ADD] purchase_open_qty
1 parent 23d6d8b commit 8acb6c2

File tree

10 files changed

+326
-0
lines changed

10 files changed

+326
-0
lines changed

purchase_open_qty/README.rst

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
2+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
3+
:alt: License: AGPL-3
4+
5+
=================
6+
Purchase Open Qty
7+
=================
8+
9+
This module allows purchase users to identify what are the purchase orders
10+
that currently have quantities pending to invoice or to receive.
11+
12+
Usage
13+
=====
14+
15+
To use this module, you need to go to Purchase Orders and select the new filters in the search.
16+
17+
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
18+
:alt: Try me on Runbot
19+
:target: https://runbot.odoo-community.org/runbot/142/9.0
20+
21+
Bug Tracker
22+
===========
23+
24+
Bugs are tracked on `GitHub Issues
25+
<https://github.com/OCA/purchase-workflow/issues>`_. In case of trouble, please
26+
check there if your issue has already been reported. If you spotted it first,
27+
help us smash it by providing detailed and welcomed feedback.
28+
29+
Credits
30+
=======
31+
32+
Images
33+
------
34+
35+
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
36+
37+
Contributors
38+
------------
39+
40+
* Miquel Raïch <miquel.raich@eficent.com>
41+
42+
Maintainer
43+
----------
44+
45+
.. image:: https://odoo-community.org/logo.png
46+
:alt: Odoo Community Association
47+
:target: https://odoo-community.org
48+
49+
This module is maintained by the OCA.
50+
51+
OCA, or the Odoo Community Association, is a nonprofit organization whose
52+
mission is to support the collaborative development of Odoo features and
53+
promote its widespread use.
54+
55+
To contribute to this module, please visit https://odoo-community.org.

purchase_open_qty/__init__.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# (http://www.eficent.com)
4+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
5+
6+
from . import models
7+
from .init_hook import pre_init_hook

purchase_open_qty/__openerp__.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# (http://www.eficent.com)
4+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
5+
6+
{
7+
"name": "Purchase Open Qty",
8+
"summary": "Allows to identify the purchase orders that have quantities "
9+
"pending to invoice or to receive.",
10+
"version": "9.0.1.0.0",
11+
"author": "Eficent, "
12+
"Odoo Community Association (OCA)",
13+
"website": "https://github.com/OCA/purchase-workflow",
14+
"category": "Purchases",
15+
"depends": ["purchase"],
16+
"data": [
17+
'views/purchase_view.xml',
18+
],
19+
'pre_init_hook': 'pre_init_hook',
20+
"license": "AGPL-3",
21+
"installable": True,
22+
"application": False,
23+
}

purchase_open_qty/init_hook.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# (http://www.eficent.com)
4+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
5+
6+
import logging
7+
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def pre_init_hook(cr):
13+
"""
14+
The objective of this hook is to speed up the installation
15+
of the module on an existing Odoo instance.
16+
"""
17+
store_field_qty_to_receive_and_invoice(cr)
18+
19+
20+
def store_field_qty_to_receive_and_invoice(cr):
21+
22+
cr.execute("""SELECT column_name
23+
FROM information_schema.columns
24+
WHERE table_name='purchase_order_line' AND
25+
column_name='qty_to_receive'""")
26+
if not cr.fetchone():
27+
logger.info('Creating field qty_to_receive on purchase_order_line')
28+
cr.execute(
29+
"""
30+
ALTER TABLE purchase_order_line ADD COLUMN qty_to_receive float;
31+
COMMENT ON COLUMN purchase_order_line.qty_to_receive IS
32+
'Qty to Receive';
33+
""")
34+
35+
cr.execute("""SELECT column_name
36+
FROM information_schema.columns
37+
WHERE table_name='purchase_order_line' AND
38+
column_name='qty_to_invoice'""")
39+
if not cr.fetchone():
40+
logger.info('Creating field qty_to_invoice on purchase_order_line')
41+
cr.execute(
42+
"""
43+
ALTER TABLE purchase_order_line ADD COLUMN qty_to_invoice float;
44+
COMMENT ON COLUMN purchase_order_line.qty_to_invoice IS
45+
'Qty to Bill';
46+
""")
47+
48+
logger.info('Computing values for fields qty_to_receive and qty_to_invoice'
49+
' on purchase_order_line')
50+
cr.execute(
51+
"""
52+
UPDATE purchase_order_line pol
53+
SET qty_to_receive = pol.product_qty - pol.qty_received,
54+
qty_to_invoice = pol.product_qty - pol.qty_invoiced
55+
"""
56+
)

purchase_open_qty/models/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# (http://www.eficent.com)
4+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
5+
6+
from . import purchase_order
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# (http://www.eficent.com)
4+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
5+
6+
from openerp import api, fields, models
7+
import openerp.addons.decimal_precision as dp
8+
9+
10+
class PurchaseOrderLine(models.Model):
11+
_inherit = 'purchase.order.line'
12+
13+
@api.depends('product_qty', 'qty_invoiced')
14+
def _compute_qty_to_invoice(self):
15+
for line in self:
16+
line.qty_to_invoice = line.product_qty - line.qty_invoiced
17+
18+
@api.depends('order_id.state', 'move_ids.state', 'product_qty')
19+
def _compute_qty_received(self):
20+
super(PurchaseOrderLine, self)._compute_qty_received()
21+
for line in self:
22+
line.qty_to_receive = line.product_qty - line.qty_received
23+
24+
qty_to_invoice = fields.Float(compute='_compute_qty_to_invoice',
25+
digits=dp.get_precision(
26+
'Product Unit of Measure'),
27+
copy=False,
28+
string="Qty to Bill", store=True)
29+
qty_to_receive = fields.Float(compute='_compute_qty_received',
30+
digits=dp.get_precision(
31+
'Product Unit of Measure'),
32+
copy=False,
33+
string="Qty to Receive", store=True)
34+
35+
36+
class PurchaseOrder(models.Model):
37+
_inherit = 'purchase.order'
38+
39+
def _compute_qty_to_invoice(self):
40+
for po in self:
41+
po.qty_to_invoice = sum(po.mapped('order_line.qty_to_invoice'))
42+
43+
def _compute_qty_to_receive(self):
44+
for po in self:
45+
po.qty_to_receive = sum(po.mapped('order_line.qty_to_receive'))
46+
47+
@api.model
48+
def _search_qty_to_invoice(self, operator, value):
49+
po_line_obj = self.env['purchase.order.line']
50+
res = []
51+
po_lines = po_line_obj.search(
52+
[('qty_to_invoice', operator, value)])
53+
order_ids = po_lines.mapped('order_id')
54+
res.append(('id', 'in', order_ids.ids))
55+
return res
56+
57+
@api.model
58+
def _search_qty_to_receive(self, operator, value):
59+
po_line_obj = self.env['purchase.order.line']
60+
res = []
61+
po_lines = po_line_obj.search(
62+
[('qty_to_receive', operator, value)])
63+
order_ids = po_lines.mapped('order_id')
64+
res.append(('id', 'in', order_ids.ids))
65+
return res
66+
67+
qty_to_invoice = fields.Float(compute='_compute_qty_to_invoice',
68+
search='_search_qty_to_invoice',
69+
string="Qty to Bill",
70+
default=0.0)
71+
qty_to_receive = fields.Float(compute='_compute_qty_to_receive',
72+
search='_search_qty_to_receive',
73+
string="Qty to Receive",
74+
default=0.0)
9.23 KB
Loading

purchase_open_qty/tests/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from . import test_purchase_open_qty
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
3+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4+
5+
from openerp.tests.common import TransactionCase
6+
from openerp.fields import Datetime
7+
8+
9+
class TestPurchaseOpenQty(TransactionCase):
10+
def setUp(self):
11+
super(TestPurchaseOpenQty, self).setUp()
12+
self.purchase_order_model = self.env['purchase.order']
13+
purchase_order_line_model = self.env['purchase.order.line']
14+
partner_model = self.env['res.partner']
15+
prod_model = self.env['product.product']
16+
analytic_account_model = self.env['account.analytic.account']
17+
self.product_uom_model = self.env['product.uom']
18+
19+
pa_dict = {
20+
'name': 'Partner 1',
21+
'supplier': True,
22+
}
23+
self.partner = partner_model.sudo().create(pa_dict)
24+
po_dict = {
25+
'partner_id': self.partner.id,
26+
}
27+
# Purchase Order Num 1
28+
self.purchase_order_1 = self.purchase_order_model.create(po_dict)
29+
uom_id = self.product_uom_model.search([
30+
('name', '=', 'Unit(s)')])[0].id
31+
pr_dict = {
32+
'name': 'Product Test',
33+
'uom_id': uom_id,
34+
}
35+
self.product = prod_model.sudo().create(pr_dict)
36+
ac_dict = {
37+
'name': 'analytic account 1',
38+
}
39+
self.analytic_account_1 = \
40+
analytic_account_model.sudo().create(ac_dict)
41+
pl_dict1 = {
42+
'date_planned': Datetime.now(),
43+
'name': 'PO01',
44+
'order_id': self.purchase_order_1.id,
45+
'product_id': self.product.id,
46+
'product_uom': uom_id,
47+
'price_unit': 1.0,
48+
'product_qty': 5.0,
49+
'account_analytic_id': self.analytic_account_1.id,
50+
}
51+
self.purchase_order_line_1 = \
52+
purchase_order_line_model.sudo().create(pl_dict1)
53+
self.purchase_order_1.button_confirm()
54+
55+
def test_compute_qty_to_invoice_and_receive(self):
56+
self.assertEqual(self.purchase_order_line_1.qty_to_invoice, 5.0,
57+
"Expected 5 as qty_to_invoice in the PO line")
58+
self.assertEqual(self.purchase_order_line_1.qty_to_receive, 5.0,
59+
"Expected 5 as qty_to_receive in the PO line")
60+
self.assertEqual(self.purchase_order_1.qty_to_invoice, 5.0,
61+
"Expected 5 as qty_to_invoice in the PO")
62+
self.assertEqual(self.purchase_order_1.qty_to_receive, 5.0,
63+
"Expected 5 as qty_to_receive in the PO")
64+
65+
def test_search_qty_to_invoice_and_receive(self):
66+
found = self.purchase_order_model.search(
67+
['|', ('qty_to_invoice', '>', 0.0), ('qty_to_receive', '>', 0.0)])
68+
self.assertTrue(
69+
self.purchase_order_1.id in found.ids,
70+
'Expected PO %s in POs %s' % (self.purchase_order_1.id, found.ids))
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<openerp>
3+
<data>
4+
<record id="purchase_order_form" model="ir.ui.view">
5+
<field name="name">purchase.order.form -- open qty fields</field>
6+
<field name="model">purchase.order</field>
7+
<field name="inherit_id" ref="purchase.purchase_order_form"/>
8+
<field name="arch" type="xml">
9+
<xpath expr="//field[@name='order_line']/tree/field[@name='qty_received']" position="before">
10+
<field name="qty_to_receive" invisible="not context.get('show_purchase', False)"/>
11+
</xpath>
12+
<xpath expr="//field[@name='order_line']/tree/field[@name='qty_invoiced']" position="before">
13+
<field name="qty_to_invoice" invisible="not context.get('show_purchase', False)"/>
14+
</xpath>
15+
</field>
16+
</record>
17+
18+
<record id="view_purchase_order_filter" model="ir.ui.view">
19+
<field name="name">request.quotation.select -- open qty filters</field>
20+
<field name="model">purchase.order</field>
21+
<field name="inherit_id" ref="purchase.view_purchase_order_filter"/>
22+
<field name="arch" type="xml">
23+
<filter name="invoiced" position="after">
24+
<filter name="pending_qty_to_invoice" string="Pending Qty to Bill" domain="[('qty_to_invoice','>',0.0)]"/>
25+
<filter name="pending_qty_to_receive" string="Pending Qty to Receive" domain="[('qty_to_receive','>',0.0)]"/>
26+
</filter>
27+
</field>
28+
</record>
29+
</data>
30+
</openerp>

0 commit comments

Comments
 (0)