Merge lp:~camptocamp/account-invoicing/7.0-account_invoice_zero into lp:~account-core-editors/account-invoicing/7.0

Proposed by Guewen Baconnier @ Camptocamp
Status: Merged
Approved by: Yannick Vaucher @ Camptocamp
Approved revision: 42
Merged at revision: 42
Proposed branch: lp:~camptocamp/account-invoicing/7.0-account_invoice_zero
Merge into: lp:~account-core-editors/account-invoicing/7.0
Diff against target: 215 lines (+189/-0)
5 files modified
account_invoice_zero/__init__.py (+3/-0)
account_invoice_zero/__openerp__.py (+52/-0)
account_invoice_zero/account_invoice.py (+50/-0)
account_invoice_zero/test/account_invoice_no_zero_open.yml (+40/-0)
account_invoice_zero/test/account_invoice_zero_paid.yml (+44/-0)
To merge this branch: bzr merge lp:~camptocamp/account-invoicing/7.0-account_invoice_zero
Reviewer Review Type Date Requested Status
Pedro Manuel Baeza code review Approve
Vincent Renaville@camptocamp (community) Approve
Yannick Vaucher @ Camptocamp code review Approve
Review via email: mp+210215@code.launchpad.net

Commit message

add account_invoice_zero: automatically set invoices with a zero amount to 'paid'

Description of the change

Invoices with a amount of 0 are automatically set as paid.

When an invoice has an amount of 0, OpenERP still generates a
receivable/payable move line with a 0 balance. The invoice stays as
open even if there is nothing to pay. The user has 2 ways to set the
invoice as paid: create a payment of 0 and reconcile the line with the
payment or reconcile the receivable/payable move line with itself.
This module takes the latter approach and will directly set the invoice
as paid once it is opened.

To post a comment you must log in.
Revision history for this message
Vincent Renaville@camptocamp (vrenaville-c2c) wrote :

Hello,

Juste question at line 109

I don't know if it's better to use float_compare method with precision

prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
not float_compare(debit, credit, precision_digits=prec)

Vincent

review: Needs Information
42. By Guewen Baconnier @ Camptocamp

use float comparisons

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

> Hello,
>
> Juste question at line 109
>
> I don't know if it's better to use float_compare method with precision
>
> prec = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
> not float_compare(debit, credit, precision_digits=prec)
>
> Vincent

Thanks for the review. I made the change, but used float_is_zero.

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Guewen, I'm curious about the use case for having 0 amount invoices.

review: Needs Information
Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

Pedro,

This can be the case when a customer order a product and has a reduction that makes it at 0.

The Sale Order will generate an invoice even if the price is 0.

Revision history for this message
Yannick Vaucher @ Camptocamp (yvaucher-c2c) wrote :

LGTM

review: Approve (code review)
Revision history for this message
Vincent Renaville@camptocamp (vrenaville-c2c) wrote :

LGTM

review: Approve
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Guewen, thanks for the clarification. Here in Spain that operation is forbidden by law (because you need at least declare taxes for the amount).

Regards.

review: Approve (code review)
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

> Hi, Guewen, thanks for the clarification. Here in Spain that operation is
> forbidden by law (because you need at least declare taxes for the amount).
>
> Regards.

Interesting.

For my curiosity, if you have a sale line with 100€ taxes included and you add a second line with a rebate of -100€ taxes include too what happens? Can't happen so?

Here, the total amount is 0, the invoice still have move lines for the taxes but they are balanced by the rebate.

Example: ("TVA" are the taxes, "Bons de rabais" are discounts)
http://postimg.org/image/95bb6lgl1/
http://postimg.org/image/95bb6lgl1/

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

Twice the same image, here is the second one with the move details.
http://postimg.org/image/k9bx94w17/

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

Hi, Guewen, the case you told it's also forbidden. Let me explain: the only official mechanism for compensating invoices is via refunds. You don't need to make cash flows, but you need to have both documents. That is theoretically. In practice, if you have an amount to compensate (a potential refund) that is lower than the amount of the invoice, it's a common and tolerated practise to discount it from the invoice, but never if the amount is equal.

I hope I explain it well.

Regards.

Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote :

On 03/14/2014 11:06 AM, Pedro Manuel Baeza wrote:
> Hi, Guewen, the case you told it's also forbidden. Let me explain: the only official mechanism for compensating invoices is via refunds. You don't need to make cash flows, but you need to have both documents. That is theoretically. In practice, if you have an amount to compensate (a potential refund) that is lower than the amount of the invoice, it's a common and tolerated practise to discount it from the invoice, but never if the amount is equal.
>
> I hope I explain it well.
>
> Regards.
>

Good to know!
Thanks for the explanation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'account_invoice_zero'
2=== added file 'account_invoice_zero/__init__.py'
3--- account_invoice_zero/__init__.py 1970-01-01 00:00:00 +0000
4+++ account_invoice_zero/__init__.py 2014-03-10 14:45:38 +0000
5@@ -0,0 +1,3 @@
6+# -*- coding: utf-8 -*-
7+
8+from . import account_invoice
9
10=== added file 'account_invoice_zero/__openerp__.py'
11--- account_invoice_zero/__openerp__.py 1970-01-01 00:00:00 +0000
12+++ account_invoice_zero/__openerp__.py 2014-03-10 14:45:38 +0000
13@@ -0,0 +1,52 @@
14+# -*- coding: utf-8 -*-
15+##############################################################################
16+#
17+# Author: Guewen Baconnier
18+# Copyright 2014 Camptocamp SA
19+#
20+# This program is free software: you can redistribute it and/or modify
21+# it under the terms of the GNU Affero General Public License as
22+# published by the Free Software Foundation, either version 3 of the
23+# License, or (at your option) any later version.
24+#
25+# This program is distributed in the hope that it will be useful,
26+# but WITHOUT ANY WARRANTY; without even the implied warranty of
27+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28+# GNU Affero General Public License for more details.
29+#
30+# You should have received a copy of the GNU Affero General Public License
31+# along with this program. If not, see <http://www.gnu.org/licenses/>.
32+#
33+##############################################################################
34+
35+{'name': 'Account Invoice Zero',
36+ 'version': '1.0',
37+ 'author': 'Camptocamp',
38+ 'maintainer': 'Camptocamp',
39+ 'license': 'AGPL-3',
40+ 'category': 'Accounting & Finance',
41+ 'depends': ['account',
42+ ],
43+ 'description': """
44+Account Invoice Zero
45+====================
46+
47+Invoices with a amount of 0 are automatically set as paid.
48+
49+When an invoice has an amount of 0, OpenERP still generates a
50+receivable/payable move line with a 0 balance. The invoice stays as
51+open even if there is nothing to pay. The user has 2 ways to set the
52+invoice as paid: create a payment of 0 and reconcile the line with the
53+payment or reconcile the receivable/payable move line with itself.
54+This module takes the latter approach and will directly set the invoice
55+as paid once it is opened.
56+
57+ """,
58+ 'website': 'http://www.camptocamp.com',
59+ 'data': [],
60+ 'test': ['test/account_invoice_zero_paid.yml',
61+ 'test/account_invoice_no_zero_open.yml',
62+ ],
63+ 'installable': True,
64+ 'auto_install': False,
65+}
66
67=== added file 'account_invoice_zero/account_invoice.py'
68--- account_invoice_zero/account_invoice.py 1970-01-01 00:00:00 +0000
69+++ account_invoice_zero/account_invoice.py 2014-03-10 14:45:38 +0000
70@@ -0,0 +1,50 @@
71+# -*- coding: utf-8 -*-
72+##############################################################################
73+#
74+# Author: Guewen Baconnier
75+# Copyright 2014 Camptocamp SA
76+#
77+# This program is free software: you can redistribute it and/or modify
78+# it under the terms of the GNU Affero General Public License as
79+# published by the Free Software Foundation, either version 3 of the
80+# License, or (at your option) any later version.
81+#
82+# This program is distributed in the hope that it will be useful,
83+# but WITHOUT ANY WARRANTY; without even the implied warranty of
84+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85+# GNU Affero General Public License for more details.
86+#
87+# You should have received a copy of the GNU Affero General Public License
88+# along with this program. If not, see <http://www.gnu.org/licenses/>.
89+#
90+##############################################################################
91+
92+from functools import partial
93+
94+from openerp.osv import orm
95+from openerp.tools.float_utils import float_is_zero
96+
97+
98+class account_invoice(orm.Model):
99+ _inherit = 'account.invoice'
100+
101+ def invoice_validate(self, cr, uid, ids, context=None):
102+ result = super(account_invoice, self).invoice_validate(
103+ cr, uid, ids, context=context)
104+ dp_obj = self.pool['decimal.precision']
105+ precision = dp_obj.precision_get(cr, uid, 'Account')
106+ is_zero = partial(float_is_zero, precision_digits=precision)
107+ for invoice in self.browse(cr, uid, ids, context=context):
108+ if is_zero(invoice.amount_total):
109+ account = invoice.account_id.id
110+ # search the payable / receivable lines
111+ lines = [line for line in invoice.move_id.line_id
112+ if line.account_id.id == account]
113+ # reconcile the lines with a zero balance
114+ if is_zero(sum(line.debit - line.credit for line in lines)):
115+ move_line_obj = self.pool['account.move.line']
116+ move_line_obj.reconcile(cr, uid,
117+ [line.id for line in lines],
118+ context=context)
119+ return result
120+
121
122=== added directory 'account_invoice_zero/test'
123=== added file 'account_invoice_zero/test/account_invoice_no_zero_open.yml'
124--- account_invoice_zero/test/account_invoice_no_zero_open.yml 1970-01-01 00:00:00 +0000
125+++ account_invoice_zero/test/account_invoice_no_zero_open.yml 2014-03-10 14:45:38 +0000
126@@ -0,0 +1,40 @@
127+-
128+ In order to test that the invoices with an amount still behave normally, I create on invoice
129+-
130+ !record {model: account.invoice, id: account_invoice_state}:
131+ account_id: account.a_recv
132+ company_id: base.main_company
133+ currency_id: base.EUR
134+ invoice_line:
135+ - account_id: account.a_sale
136+ name: '[PCSC234] PC Assemble SC234'
137+ price_unit: 450.0
138+ quantity: 1.0
139+ product_id: product.product_product_3
140+ uos_id: product.product_uom_unit
141+ journal_id: account.bank_journal
142+ partner_id: base.res_partner_12
143+ reference_type: none
144+-
145+ I check that Initially customer invoice state is "Draft"
146+-
147+ !assert {model: account.invoice, id: account_invoice_state}:
148+ - state == 'draft'
149+-
150+ I called the "Confirm Draft Invoices" wizard
151+-
152+ !record {model: account.invoice.confirm, id: account_invoice_confirm_0}:
153+ {}
154+-
155+ I clicked on Confirm Invoices Button
156+-
157+ !python {model: account.invoice.confirm}: |
158+ self.invoice_confirm(cr, uid, [ref("account_invoice_confirm_0")], {"lang": 'en_US',
159+ "tz": False, "active_model": "account.invoice", "active_ids": [ref("account_invoice_state")],
160+ "type": "out_invoice", "active_id": ref("account_invoice_state"), })
161+-
162+ I check that customer invoice state is "Open"
163+-
164+ !assert {model: account.invoice, id: account_invoice_state}:
165+ - state == 'open'
166+
167
168=== added file 'account_invoice_zero/test/account_invoice_zero_paid.yml'
169--- account_invoice_zero/test/account_invoice_zero_paid.yml 1970-01-01 00:00:00 +0000
170+++ account_invoice_zero/test/account_invoice_zero_paid.yml 2014-03-10 14:45:38 +0000
171@@ -0,0 +1,44 @@
172+-
173+ In order to test that an invoice with a zero amount is directly paid, I create an invoice
174+-
175+ !record {model: account.invoice, id: account_invoice_zero_paid}:
176+ account_id: account.a_recv
177+ company_id: base.main_company
178+ currency_id: base.EUR
179+ invoice_line:
180+ - account_id: account.a_sale
181+ name: '[PCSC234] PC Assemble SC234'
182+ price_unit: 120.0
183+ quantity: 1.0
184+ product_id: product.product_product_3
185+ uos_id: product.product_uom_unit
186+ - account_id: account.a_sale
187+ name: discount
188+ price_unit: -120.0
189+ quantity: 1.0
190+ uos_id: product.product_uom_unit
191+ journal_id: account.bank_journal
192+ partner_id: base.res_partner_12
193+ reference_type: none
194+-
195+ I check that Initially customer invoice state is "Draft"
196+-
197+ !assert {model: account.invoice, id: account_invoice_zero_paid}:
198+ - state == 'draft'
199+-
200+ I called the "Confirm Draft Invoices" wizard
201+-
202+ !record {model: account.invoice.confirm, id: account_invoice_confirm_0}:
203+ {}
204+-
205+ I clicked on Confirm Invoices Button
206+-
207+ !python {model: account.invoice.confirm}: |
208+ self.invoice_confirm(cr, uid, [ref("account_invoice_confirm_0")], {"lang": 'en_US',
209+ "tz": False, "active_model": "account.invoice", "active_ids": [ref("account_invoice_zero_paid")],
210+ "type": "out_invoice", "active_id": ref("account_invoice_zero_paid"), })
211+-
212+ I check that customer invoice state is "Paid"
213+-
214+ !assert {model: account.invoice, id: account_invoice_zero_paid}:
215+ - state == 'paid'

Subscribers

People subscribed via source and target branches