Merge lp:~therp-nl/account-financial-tools/6.1-account-purchase-default into lp:~account-core-editors/account-financial-tools/6.1

Proposed by Ronald Portier (Therp)
Status: Merged
Merged at revision: 101
Proposed branch: lp:~therp-nl/account-financial-tools/6.1-account-purchase-default
Merge into: lp:~account-core-editors/account-financial-tools/6.1
Diff against target: 222 lines (+190/-0)
6 files modified
account_invoice_line_default_account/__init__.py (+10/-0)
account_invoice_line_default_account/__openerp__.py (+49/-0)
account_invoice_line_default_account/model/__init__.py (+11/-0)
account_invoice_line_default_account/model/account_invoice_line.py (+52/-0)
account_invoice_line_default_account/model/res_partner.py (+34/-0)
account_invoice_line_default_account/view/res_partner_view.xml (+34/-0)
To merge this branch: bzr merge lp:~therp-nl/account-financial-tools/6.1-account-purchase-default
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) Approve
Alexandre Fayolle - camptocamp code review, no test Approve
Review via email: mp+146082@code.launchpad.net

This proposal supersedes a proposal from 2013-02-01.

Description of the change

Resubmit based on 6.1 branch.

This branch makes it very easy to set the counterpart account to use on a supplier invoice.

For invoices created from purchase orders, or for buying products that are already in the database, this module is not usefull.

But for entering regular invoices for expenses like telecommunication bills, office supplies etc., not having to continually enter the account on which these expenses are to be booked is incredibly usefull and can save huge amounts of time.

This module offers the possibility to manually link a partner (supplier) to an expense account. But even better, it automatically "remembers" the expense account entered for a partner on an invoice - unless explicitly told not to do this.

Module might be extended to also cover manual account move entry. And maybe there is a use for having a similar function on sales invoices as well, altough I reasoned that usually you will sell product in the database, for which you can already set an income account.

To post a comment you must log in.
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Hi Ronald,

here are my nits:

l.17,42 The module name is not very clear. How about supplier_invoice_default_account?

l.48 Please set 'Therp B.V.' as author, as this prevents multiple 'Therp' entries showing up in the authors sesction of the Apps site and allows people to filter on all our addons by a single click.

l.59..62 init_xml and update_xml are deprecated. Please use the 'data' directive.

l.102 osv.osv is deprecated. Use orm.Model

l.108,109 use "context.get('partner_id')" and "context.get('type')"

l.126 typo 'in_refurd' -> 'in_refund'

l.112,130 You could consider using read() instead of browse(), as it adds less overhead and you only read a single field.

l.203,218 'data' tag at this level no longer necessary from v6.1 on.

review: Needs Fixing
Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : Posted in a previous version of this proposal

l. 53: avoid hyphenation in the module description, as you don't know how the lines will be reformatted when the description is displayed. This is true for docstrings too.

review: Needs Fixing
Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote : Posted in a previous version of this proposal

I kept the browse instead of the read. According to the famous Technical Mememto from Stephane Wirtel, read() should be used from webservices, but internally browse() should always be used.

I could not find any real performance comparison between the two (which nmight be interesting to do). For the moment I left the browse calls.

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : Posted in a previous version of this proposal

A few suggested refactorings to improve code maintainability.

line 110: you check on line 112 for the truth of partner_id, so you can safely use the default default value in the call to dict.get()

line 112-113: you can get rid of the 'and invoice_type', since you have an in valid_value check just after this which will return False if invoice_type is None.
context['partner_id']

line 116: no need for another lookup of context['partner_id']: this value is partner_id from line 110

line 117: I personally prefer the attribute access on records, as in
    if partner_obj and partner_obj.property_account_expense:

 * Is there really a case where the partner_id you got from the context can be non existing? Apart from concurrent read / deletion, this sounds far fetched, so I'd be tempted to remove the first part of the test. That remark also applies to line 134.

line 136-137:
  * you are mixing attribute style access and dict style access.
  * the "or 0" bit is not necessary : the and expression will evaluate to False and replacing False by 0 does not change the outcome of the test line 138

line 138: please use "if a != b" rather than "if not a == b"

review: Needs Fixing (code review, no test)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote : Posted in a previous version of this proposal

Ronald,

you were right about the read/browse performance. Although I get a consistent performance gain of 50% if I do a large number of reads to get a single char field from res.partner when compared to browsing the resource, reading a fields.property using read or a browse record takes almost exactly the same time.

Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote : Posted in a previous version of this proposal

Stefan, I will take your performance tests in mind when reading single character (what about float and integer, I guess will be about the same?) fields in the future.

Alexandre: thanks for your comments, with one exception I will use them to update the code.

The exception is on the need to test for partner_obj... I never want to make assumptions in my code on the correctness of calling functions. So indeed, I think it is necesarry to handle the case of a non existing partner_id. Or the case that should be even more rare that a valid partner_id will still not result in a valid partner object. For instance when the connection with the database has been lost.

The only thing is what to do about it.

In most cases I would prefer to have an assertion error. partner_obj should exist. If it does not, it is most likely due to a programmer error. In this case, retrieving a default value for a column that a user can change anyway, I don't want to blow up - confront the user with an exception - when something goes wrong.

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote : Posted in a previous version of this proposal

On ven. 01 févr. 2013 10:43:42 CET, Ronald Portier (Therp) wrote:
> Stefan, I will take your performance tests in mind when reading single character (what about float and integer, I guess will be about the same?) fields in the future.
>
> Alexandre: thanks for your comments, with one exception I will use them to update the code.
>
> The exception is on the need to test for partner_obj... I never want to make assumptions in my code on the correctness of calling functions. So indeed, I think it is necesarry to handle the case of a non existing partner_id. Or the case that should be even more rare that a valid partner_id will still not result in a valid partner object. For instance when the connection with the database has been lost.
>
> The only thing is what to do about it.
>
> In most cases I would prefer to have an assertion error. partner_obj should exist. If it does not, it is most likely due to a programmer error. In this case, retrieving a default value for a column that a user can change anyway, I don't want to blow up - confront the user with an exception - when something goes wrong.

Well... I cannot force you :-)

IMO, if you have a supposedly valid partner_id and browsing
model['res.partner'] for that id returns a browse_null instance, not
doing anything is wrong, because you are silencing a bug (you should
not have gotten that partner_id in the first place). At the very least,
this should be logged. And in this specific case, displaying the
exception to the user would be not be inappropriate (because it should
not be happening anyway). Si at the very least if you want to guard
against this, use an assert statement.

--
Alexandre Fayolle
Chef de Projet
Tel : + 33 (0)4 79 26 57 94

Camptocamp France SAS
Savoie Technolac, BP 352
73377 Le Bourget du Lac Cedex
http://www.camptocamp.com

Revision history for this message
Alexandre Fayolle - camptocamp (alexandre-fayolle-c2c) wrote :

LGTM

review: Approve (code review, no test)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'account_invoice_line_default_account'
=== added file 'account_invoice_line_default_account/__init__.py'
--- account_invoice_line_default_account/__init__.py 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/__init__.py 2013-02-01 09:54:22 +0000
@@ -0,0 +1,10 @@
1# -*- coding: UTF-8 -*-
2'''
3Created on 30 jan. 2013
4
5@author: Ronald Portier, Therp
6
7rportier@therp.nl
8http://www.therp.nl
9'''
10import model
011
=== added file 'account_invoice_line_default_account/__openerp__.py'
--- account_invoice_line_default_account/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/__openerp__.py 2013-02-01 09:54:22 +0000
@@ -0,0 +1,49 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21{
22 'name': 'Account Invoice Line Default Account',
23 'version': '6.1.r004',
24 'depends': [
25 'base',
26 'account'
27 ],
28 'author': 'Therp B.V.',
29 'category': 'Accounting',
30 'description': '''When entering purchase invoices directly, the user has
31to select an account which will be used as a counterpart in the generated
32move lines. However, each supplier will mostly be linked to one account. For
33instance when ordering paper from a supplier that deals in paper, the
34counterpart account will mostly be something like 'office expenses'.
35This module will add a default counterpart account to a partner (supplier
36only), comparable to the similiar field in product. When a supplier invoice
37is entered, withouth a product, the field from partner will be used as default.
38Also when an expense account is entered on an invoice line (not automatically
39selectd for a product), the expense account will be automatically linked to
40the partner - unless explicitly disabled in the partner record.
41''',
42 'data': [
43 'view/res_partner_view.xml'
44 ],
45 'demo_xml': [],
46 'test': [],
47 'installable': True,
48 'active': False,
49}
050
=== added directory 'account_invoice_line_default_account/model'
=== added file 'account_invoice_line_default_account/model/__init__.py'
--- account_invoice_line_default_account/model/__init__.py 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/model/__init__.py 2013-02-01 09:54:22 +0000
@@ -0,0 +1,11 @@
1# -*- coding: UTF-8 -*-
2'''
3Created on 30 jan. 2013
4
5@author: Ronald Portier, Therp
6
7rportier@therp.nl
8http://www.therp.nl
9'''
10import res_partner
11import account_invoice_line
012
=== added file 'account_invoice_line_default_account/model/account_invoice_line.py'
--- account_invoice_line_default_account/model/account_invoice_line.py 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/model/account_invoice_line.py 2013-02-01 09:54:22 +0000
@@ -0,0 +1,52 @@
1# -*- coding: UTF-8 -*-
2'''
3Created on 30 jan. 2013
4
5@author: Ronald Portier, Therp
6
7rportier@therp.nl
8http://www.therp.nl
9'''
10from openerp.osv import orm
11
12
13class account_invoice_line(orm.Model):
14 _inherit = 'account.invoice.line'
15
16 def _account_id_default(self, cr, uid, context=None):
17 if context is None:
18 context = {}
19 partner_id = context.get('partner_id')
20 invoice_type = context.get('type')
21 if partner_id and invoice_type in ['in_invoice', 'in_refund']:
22 partner_model = self.pool.get('res.partner')
23 partner_obj = partner_model.browse(
24 cr, uid, partner_id, context=context)
25 if partner_obj and partner_obj.property_account_expense:
26 return partner_obj.property_account_expense.id
27 return False
28
29 _defaults = {
30 'account_id': _account_id_default,
31 }
32
33 def onchange_account_id(
34 self, cr, uid, ids, product_id, partner_id, inv_type,
35 fposition_id, account_id):
36 if (account_id and partner_id and (not product_id)
37 and inv_type in ['in_invoice', 'in_refund']):
38 # We have an account_id, and is not from a product, so
39 # store it in partner automagically:
40 partner_model = self.pool.get('res.partner')
41 partner_obj = partner_model.browse(cr, uid, partner_id)
42 if partner_obj and partner_obj.auto_update_account_expense:
43 old_account_id = (
44 partner_obj.property_account_expense
45 and partner_obj.property_account_expense.id)
46 if account_id != old_account_id:
47 # only write when something really changed
48 vals = {'property_account_expense': account_id}
49 partner_obj.write(vals)
50 return super(account_invoice_line, self).onchange_account_id(
51 cr, uid, ids, product_id, partner_id, inv_type,
52 fposition_id, account_id)
053
=== added file 'account_invoice_line_default_account/model/res_partner.py'
--- account_invoice_line_default_account/model/res_partner.py 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/model/res_partner.py 2013-02-01 09:54:22 +0000
@@ -0,0 +1,34 @@
1# -*- coding: UTF-8 -*-
2'''
3Created on 30 jan. 2013
4
5@author: Ronald Portier, Therp
6
7rportier@therp.nl
8http://www.therp.nl
9'''
10from openerp.osv import orm
11from openerp.osv import fields
12
13
14class res_partner(orm.Model):
15 _inherit = 'res.partner'
16
17 _columns = {
18 'property_account_expense': fields.property(
19 'account.account',
20 type='many2one',
21 relation='account.account',
22 string='Default expense account',
23 view_load=True,
24 domain='''[('user_type.report_type', '=', 'expense')]''',
25 help='Default counterpart account for purchases',
26 required=False),
27 'auto_update_account_expense': fields.boolean(
28 'Save account selected on invoice',
29 help='When account selected on invoice, automatically save it'),
30 }
31
32 _defaults = {
33 'auto_update_account_expense': True,
34 }
035
=== added directory 'account_invoice_line_default_account/view'
=== added file 'account_invoice_line_default_account/view/res_partner_view.xml'
--- account_invoice_line_default_account/view/res_partner_view.xml 1970-01-01 00:00:00 +0000
+++ account_invoice_line_default_account/view/res_partner_view.xml 2013-02-01 09:54:22 +0000
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <record
5 id="view_partner_expense_property_form"
6 model="ir.ui.view">
7 <field name="name">res.partner.property.form.inherit</field>
8 <field name="model">res.partner</field>
9 <field name="type">form</field>
10 <field name="priority">2</field>
11 <field
12 name="inherit_id"
13 ref="account.view_partner_property_form" />
14 <field
15 name="arch"
16 type="xml">
17 <field
18 name="property_account_payable"
19 position="after">
20 <group
21 colspan="2"
22 col="4">
23 <field
24 name="property_account_expense"
25 groups="account.group_account_invoice" />
26 <field
27 name="auto_update_account_expense"
28 groups="account.group_account_invoice" />
29 </group>
30 </field>
31 </field>
32 </record>
33 </data>
34</openerp>

Subscribers

People subscribed via source and target branches