Merge lp:~therp-nl/account-invoicing/7.0-account_cash_discount into lp:~account-core-editors/account-invoicing/7.0

Proposed by Holger Brunn (Therp) on 2014-01-27
Status: Needs review
Proposed branch: lp:~therp-nl/account-invoicing/7.0-account_cash_discount
Merge into: lp:~account-core-editors/account-invoicing/7.0
Diff against target: 1160 lines (+1080/-0)
15 files modified
account_cash_discount/__init__.py (+25/-0)
account_cash_discount/__openerp__.py (+84/-0)
account_cash_discount/i18n/account_cash_discount.pot (+142/-0)
account_cash_discount/i18n/de.po (+133/-0)
account_cash_discount/model/__init__.py (+24/-0)
account_cash_discount/model/account_invoice.py (+251/-0)
account_cash_discount/model/account_payment_term.py (+59/-0)
account_cash_discount/model/account_payment_term_cash_discount.py (+87/-0)
account_cash_discount/model/account_voucher.py (+75/-0)
account_cash_discount/security/ir.model.access.csv (+3/-0)
account_cash_discount/view/account_invoice.xml (+24/-0)
account_cash_discount/view/account_move_line_reconcile.xml (+26/-0)
account_cash_discount/view/account_payment_term.xml (+49/-0)
account_cash_discount/wizard/__init__.py (+22/-0)
account_cash_discount/wizard/account_move_line_reconcile.py (+76/-0)
To merge this branch: bzr merge lp:~therp-nl/account-invoicing/7.0-account_cash_discount
Reviewer Review Type Date Requested Status
Pedro Manuel Baeza 2014-01-27 Resubmit on 2014-08-13
Review via email: mp+203359@code.launchpad.net

Description of the change

This started as a port of http://bazaar.launchpad.net/~camptocamp/c2c-rd-addons/7.0/files/head:/account_cash_discount_61

I've been unhappy with the way the original module did its work (too much SQL, deleting moves and recreating them), so by now, it's actually a rewrite.

To post a comment you must log in.
27. By Holger Brunn (Therp) on 2014-02-18

[ADD] cope with user configurable deviations from exact discount price
[ADD] use discount_{expense,income}_account_id
[RFR] make discount_{expense,income}_account_id regular fields

28. By Holger Brunn (Therp) on 2014-02-18

[ADD] translation

29. By Holger Brunn (Therp) on 2014-02-19

[ADD] handle refunds with cash discounts

Hi Holger, good work. Question. Is there any integration between this cash discount and payment orders? When i make a payment order on time then discount can be extracted. When im passed my discount days it can't. Curious if this exists or is in planning. Thanks.

Hi Holger. I did some testing on the sales site and it looks ok
- First of all you example is not correct, You say: "On 01/20/2014, you charged EUR 80 to a customer, with 20% tax, totalling in EUR 100". If the amount is 80 and tax is 20%, then the total is 96,-
- Second. In the Netherlands it is not allowed to make a correction on the tax. If you want to correct tax, you have to provide the customer with a new invoice.

Example: 100 + 20% vat = 120 (early payment discount = 5%). The to pay amount = (100-5%) + 20 = 115.

30. By Holger Brunn (Therp) on 2014-05-26

[FIX] wrong numbers in example

Holger Brunn (Therp) (hbrunn) wrote :

Erwin, thanks a lot for having a look!

I corrected the example.

Support for payment orders will happen sooner or later, but currently I think it will be rather later than sooner.

This module is meant to deal with the situation in Austria and Germany, there you are legally required to jump through this hoops. For the Dutch situation, a simple write-off is enough I think.

Pedro Manuel Baeza (pedro.baeza) wrote :

This project is now hosted on https://github.com/OCA/account-invoicing. Please move your proposal there. This guide may help you https://github.com/OCA/maintainers-tools/wiki/How-to-move-a-Merge-Proposal-to-GitHub

review: Resubmit

Unmerged revisions

30. By Holger Brunn (Therp) on 2014-05-26

[FIX] wrong numbers in example

29. By Holger Brunn (Therp) on 2014-02-19

[ADD] handle refunds with cash discounts

28. By Holger Brunn (Therp) on 2014-02-18

[ADD] translation

27. By Holger Brunn (Therp) on 2014-02-18

[ADD] cope with user configurable deviations from exact discount price
[ADD] use discount_{expense,income}_account_id
[RFR] make discount_{expense,income}_account_id regular fields

26. By Holger Brunn (Therp) on 2014-01-27

[ADD] account_cash_discount

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'account_cash_discount'
2=== added file 'account_cash_discount/__init__.py'
3--- account_cash_discount/__init__.py 1970-01-01 00:00:00 +0000
4+++ account_cash_discount/__init__.py 2014-05-26 08:47:02 +0000
5@@ -0,0 +1,25 @@
6+# -*- coding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
11+# Copyright (C) 2010-2012 Camptocamp Austria (<http://www.camptocamp.at>)
12+# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
13+#
14+# This program is free software: you can redistribute it and/or modify
15+# it under the terms of the GNU Affero General Public License as
16+# published by the Free Software Foundation, either version 3 of the
17+# License, or (at your option) any later version.
18+#
19+# This program is distributed in the hope that it will be useful,
20+# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+# GNU Affero General Public License for more details.
23+#
24+# You should have received a copy of the GNU Affero General Public License
25+# along with this program. If not, see <http://www.gnu.org/licenses/>.
26+#
27+##############################################################################
28+import model
29+import wizard
30+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
31
32=== added file 'account_cash_discount/__openerp__.py'
33--- account_cash_discount/__openerp__.py 1970-01-01 00:00:00 +0000
34+++ account_cash_discount/__openerp__.py 2014-05-26 08:47:02 +0000
35@@ -0,0 +1,84 @@
36+# -*- coding: utf-8 -*-
37+##############################################################################
38+#
39+# OpenERP, Open Source Management Solution
40+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
41+# Copyright (C) 2010-2012 Camptocamp Austria (<http://www.camptocamp.at>)
42+# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
43+#
44+# This program is free software: you can redistribute it and/or modify
45+# it under the terms of the GNU Affero General Public License as
46+# published by the Free Software Foundation, either version 3 of the
47+# License, or (at your option) any later version.
48+#
49+# This program is distributed in the hope that it will be useful,
50+# but WITHOUT ANY WARRANTY; without even the implied warranty of
51+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52+# GNU Affero General Public License for more details.
53+#
54+# You should have received a copy of the GNU Affero General Public License
55+# along with this program. If not, see <http://www.gnu.org/licenses/>.
56+#
57+##############################################################################
58+
59+
60+{
61+ 'name': 'Cash discount',
62+ 'version': '0.9',
63+ 'category': 'Accounting & Finance',
64+ 'description': """
65+Cash Discount (Austria and Germany style)
66+=========================================
67+
68+Usage
69+-----
70+
71+Define your discounts as part of your payment terms. Keep in mind that other
72+computation lines than 'balance' won't make much sense, so only fill in one
73+computation line of type 'balance' indicating your payment date.
74+
75+When paying your invoice, fill in the exact amount for the discounted invoice.
76+If the payment date and the amount matches a discount, the invoice will be
77+marked as paid and some correction move lines will be created to reflect the
78+invoice being paid with a discount.
79+
80+If you manually reconcile move lines, you'll be offered a button to book the
81+writeoff amount as cash discount if a matching cash discount can be found.
82+
83+Example
84+-------
85+
86+On 01/20/2014, you charged EUR 80 to a customer, with 25% tax, totalling in
87+EUR 100. Further, you set up a payment term for this invoice that includes
88+5% discount if paid within a week.
89+
90+If you fill in EUR 95 and payment date 01/22/2014 when paying the invoice, the
91+following happens:
92+
93+- the invoice is marked as paid
94+- on the invoice's 'Other info' tag, you'll find a field 'Cash discount
95+ correction' with the following lines:
96+
97+ - EUR 1 as debit on your tax account
98+ - EUR 4 as debit on your income account
99+ - EUR 5 as credit on your customer's account
100+
101+TODO
102+----
103+
104+- cash discounts don't show up on invoice report
105+- multi currency not tested
106+- no automatic reconciliation
107+""",
108+ 'author': 'Therp BV',
109+ 'depends': [ 'account_voucher' ],
110+ 'data': [
111+ 'view/account_move_line_reconcile.xml',
112+ 'view/account_payment_term.xml',
113+ 'view/account_invoice.xml',
114+ 'security/ir.model.access.csv',
115+ ],
116+ 'installable': True,
117+ 'auto_install': False,
118+}
119+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
120
121=== added directory 'account_cash_discount/i18n'
122=== added file 'account_cash_discount/i18n/account_cash_discount.pot'
123--- account_cash_discount/i18n/account_cash_discount.pot 1970-01-01 00:00:00 +0000
124+++ account_cash_discount/i18n/account_cash_discount.pot 2014-05-26 08:47:02 +0000
125@@ -0,0 +1,142 @@
126+# Translation of OpenERP Server.
127+# This file contains the translation of the following modules:
128+# * account_cash_discount
129+#
130+msgid ""
131+msgstr ""
132+"Project-Id-Version: OpenERP Server 7.0\n"
133+"Report-Msgid-Bugs-To: \n"
134+"POT-Creation-Date: 2014-02-18 15:41+0000\n"
135+"PO-Revision-Date: 2014-02-18 15:41+0000\n"
136+"Last-Translator: <>\n"
137+"Language-Team: \n"
138+"MIME-Version: 1.0\n"
139+"Content-Type: text/plain; charset=UTF-8\n"
140+"Content-Transfer-Encoding: \n"
141+"Plural-Forms: \n"
142+
143+#. module: account_cash_discount
144+#: code:addons/account_cash_discount/model/account_invoice.py:130
145+#: code:addons/account_cash_discount/model/account_invoice.py:144
146+#, python-format
147+msgid "Cash discount writeoff"
148+msgstr ""
149+
150+#. module: account_cash_discount
151+#: view:account.invoice:0
152+msgid "Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab"
153+msgstr ""
154+
155+#. module: account_cash_discount
156+#: field:account.payment.term.cash.discount,allowed_deviation:0
157+msgid "Allowed deviation"
158+msgstr ""
159+
160+#. module: account_cash_discount
161+#: help:account.payment.term.cash.discount,discount_expense_account_id:0
162+msgid "This account will be used to post the cash discount expense"
163+msgstr ""
164+
165+#. module: account_cash_discount
166+#: field:account.move.line.reconcile,invoice_has_cash_discount:0
167+msgid "Cash discount on invoice"
168+msgstr ""
169+
170+#. module: account_cash_discount
171+#: model:ir.model,name:account_cash_discount.model_account_move_line_reconcile
172+msgid "Account move line reconcile"
173+msgstr ""
174+
175+#. module: account_cash_discount
176+#: code:addons/account_cash_discount/model/account_payment_term.py:45
177+#, python-format
178+msgid "When working with cash discounts, you can only have one computation line of type \"balance\"!"
179+msgstr ""
180+
181+#. module: account_cash_discount
182+#: field:account.payment.term.cash.discount,discount_income_account_id:0
183+msgid "Discount Income Account"
184+msgstr ""
185+
186+#. module: account_cash_discount
187+#: help:account.payment.term.cash.discount,discount_income_account_id:0
188+msgid "This account will be used to post the cash discount income"
189+msgstr ""
190+
191+#. module: account_cash_discount
192+#: field:account.payment.term.cash.discount,discount:0
193+msgid "Discount"
194+msgstr ""
195+
196+#. module: account_cash_discount
197+#: field:account.invoice,cash_discount_move_id:0
198+msgid "Cash discount correction move"
199+msgstr ""
200+
201+#. module: account_cash_discount
202+#: view:account.move.line.reconcile:0
203+msgid "Book difference as cash discount"
204+msgstr ""
205+
206+#. module: account_cash_discount
207+#: field:account.payment.term.cash.discount,payment_term_id:0
208+msgid "Payment term"
209+msgstr ""
210+
211+#. module: account_cash_discount
212+#: field:account.payment.term.cash.discount,days:0
213+msgid "Days"
214+msgstr ""
215+
216+#. module: account_cash_discount
217+#: code:addons/account_cash_discount/model/account_payment_term.py:45
218+#, python-format
219+msgid "Error"
220+msgstr ""
221+
222+#. module: account_cash_discount
223+#: field:account.payment.term.cash.discount,discount_expense_account_id:0
224+msgid "Discount Expense Account"
225+msgstr ""
226+
227+#. module: account_cash_discount
228+#: field:account.payment.term,cash_discount_ids:0
229+#: model:ir.model,name:account_cash_discount.model_account_payment_term_cash_discount
230+msgid "Cash discount"
231+msgstr ""
232+
233+#. module: account_cash_discount
234+#: view:account.move.line.reconcile:0
235+msgid "Reconcile"
236+msgstr ""
237+
238+#. module: account_cash_discount
239+#: model:ir.model,name:account_cash_discount.model_account_voucher
240+msgid "Accounting Voucher"
241+msgstr ""
242+
243+#. module: account_cash_discount
244+#: view:account.payment.term:0
245+#: model:ir.model,name:account_cash_discount.model_account_payment_term
246+msgid "Payment Term"
247+msgstr ""
248+
249+#. module: account_cash_discount
250+#: help:account.payment.term.cash.discount,allowed_deviation:0
251+msgid "The amount a payment can deviate from the computed amount and still be accepted as payment within this discount.\n"
252+"Think of rounding errors as use case for this"
253+msgstr ""
254+
255+#. module: account_cash_discount
256+#: code:addons/account_cash_discount/model/account_invoice.py:64
257+#: code:addons/account_cash_discount/model/account_invoice.py:188
258+#, python-format
259+msgid "Cash discount for %s"
260+msgstr ""
261+
262+#. module: account_cash_discount
263+#: field:account.move.line.reconcile,invoice_id:0
264+#: model:ir.model,name:account_cash_discount.model_account_invoice
265+msgid "Invoice"
266+msgstr ""
267+
268
269=== added file 'account_cash_discount/i18n/de.po'
270--- account_cash_discount/i18n/de.po 1970-01-01 00:00:00 +0000
271+++ account_cash_discount/i18n/de.po 2014-05-26 08:47:02 +0000
272@@ -0,0 +1,133 @@
273+# Translation of OpenERP Server.
274+# This file contains the translation of the following modules:
275+# * account_cash_discount
276+#
277+msgid ""
278+msgstr ""
279+"Project-Id-Version: OpenERP Server 7.0\n"
280+"Report-Msgid-Bugs-To: \n"
281+"POT-Creation-Date: 2014-02-18 15:41+0000\n"
282+"PO-Revision-Date: 2014-02-18 15:41+0000\n"
283+"Last-Translator: <>\n"
284+"Language-Team: \n"
285+"MIME-Version: 1.0\n"
286+"Content-Type: text/plain; charset=UTF-8\n"
287+"Content-Transfer-Encoding: \n"
288+"Plural-Forms: \n"
289+
290+#. module: account_cash_discount
291+#: code:addons/account_cash_discount/model/account_invoice.py:130
292+#: code:addons/account_cash_discount/model/account_invoice.py:144
293+#, python-format
294+msgid "Cash discount writeoff"
295+msgstr "Skontoabschreibung"
296+
297+#. module: account_cash_discount
298+#: view:account.invoice:0
299+msgid "Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab"
300+msgstr "Achtung: Diese Rechnung wurde unter Einbehalt von Skonto bezahlt. Die ensprechenden Buchungen finden sich im 'Weitere Info'-Reiter."
301+
302+#. module: account_cash_discount
303+#: field:account.payment.term.cash.discount,allowed_deviation:0
304+msgid "Allowed deviation"
305+msgstr "Erlaubte Abweichung"
306+
307+#. module: account_cash_discount
308+#: help:account.payment.term.cash.discount,discount_expense_account_id:0
309+msgid "This account will be used to post the cash discount expense"
310+msgstr "Skontoausgaben werden auf dieses Konto gebucht"
311+
312+#. module: account_cash_discount
313+#: field:account.move.line.reconcile,invoice_has_cash_discount:0
314+msgid "Cash discount on invoice"
315+msgstr "Rechnung mit Skonto"
316+
317+#. module: account_cash_discount
318+#: model:ir.model,name:account_cash_discount.model_account_move_line_reconcile
319+msgid "Account move line reconcile"
320+msgstr "Buchen OP Ausgleich"
321+
322+#. module: account_cash_discount
323+#: code:addons/account_cash_discount/model/account_payment_term.py:45
324+#, python-format
325+msgid "When working with cash discounts, you can only have one computation line of type \"balance\"!"
326+msgstr "Skontobezahlungen können nur eine Berechnung vom Typ \"Saldo\" beinhalten!"
327+
328+#. module: account_cash_discount
329+#: field:account.payment.term.cash.discount,discount_income_account_id:0
330+msgid "Discount Income Account"
331+msgstr "Skonto-Erlöskonto"
332+
333+#. module: account_cash_discount
334+#: help:account.payment.term.cash.discount,discount_income_account_id:0
335+msgid "This account will be used to post the cash discount income"
336+msgstr "Skontoerlöse werden auf dieses Konto gebucht"
337+
338+#. module: account_cash_discount
339+#: field:account.payment.term.cash.discount,discount:0
340+msgid "Discount"
341+msgstr "Skonto"
342+
343+#. module: account_cash_discount
344+#: field:account.invoice,cash_discount_move_id:0
345+msgid "Cash discount correction move"
346+msgstr "Skonto-Buchungssatz"
347+
348+#. module: account_cash_discount
349+#: view:account.move.line.reconcile:0
350+msgid "Book difference as cash discount"
351+msgstr "Differenz als Skonto buchen"
352+
353+#. module: account_cash_discount
354+#: field:account.payment.term.cash.discount,payment_term_id:0
355+msgid "Payment term"
356+msgstr "Zahlungsbedingung"
357+
358+#. module: account_cash_discount
359+#: field:account.payment.term.cash.discount,days:0
360+msgid "Days"
361+msgstr "Tage"
362+
363+#. module: account_cash_discount
364+#: code:addons/account_cash_discount/model/account_payment_term.py:45
365+#, python-format
366+msgid "Error"
367+msgstr "Fehler"
368+
369+#. module: account_cash_discount
370+#: field:account.payment.term.cash.discount,discount_expense_account_id:0
371+msgid "Discount Expense Account"
372+msgstr "Skonto-Aufwandskonto"
373+
374+#. module: account_cash_discount
375+#: field:account.payment.term,cash_discount_ids:0
376+#: model:ir.model,name:account_cash_discount.model_account_payment_term_cash_discount
377+msgid "Cash discount"
378+msgstr "Skonto"
379+
380+#. module: account_cash_discount
381+#: view:account.payment.term:0
382+#: model:ir.model,name:account_cash_discount.model_account_payment_term
383+msgid "Payment Term"
384+msgstr "Zahlungsbedingung"
385+
386+#. module: account_cash_discount
387+#: help:account.payment.term.cash.discount,allowed_deviation:0
388+msgid "The amount a payment can deviate from the computed amount and still be accepted as payment within this discount.\n"
389+"Think of rounding errors as use case for this"
390+msgstr "Der Betrag um den eine Zahlung vom berechneten Betrag abweichen darf um noch als Zahlung innerhalb des Skontos angesehen zu werden.\n"
391+"Dies vereinfact die Handhabung von Rundungsunterschieden."
392+
393+#. module: account_cash_discount
394+#: code:addons/account_cash_discount/model/account_invoice.py:64
395+#: code:addons/account_cash_discount/model/account_invoice.py:188
396+#, python-format
397+msgid "Cash discount for %s"
398+msgstr "Skonto für %s"
399+
400+#. module: account_cash_discount
401+#: field:account.move.line.reconcile,invoice_id:0
402+#: model:ir.model,name:account_cash_discount.model_account_invoice
403+msgid "Invoice"
404+msgstr "Rechnung"
405+
406
407=== added directory 'account_cash_discount/model'
408=== added file 'account_cash_discount/model/__init__.py'
409--- account_cash_discount/model/__init__.py 1970-01-01 00:00:00 +0000
410+++ account_cash_discount/model/__init__.py 2014-05-26 08:47:02 +0000
411@@ -0,0 +1,24 @@
412+# -*- coding: utf-8 -*-
413+##############################################################################
414+#
415+# OpenERP, Open Source Management Solution
416+# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
417+#
418+# This program is free software: you can redistribute it and/or modify
419+# it under the terms of the GNU Affero General Public License as
420+# published by the Free Software Foundation, either version 3 of the
421+# License, or (at your option) any later version.
422+#
423+# This program is distributed in the hope that it will be useful,
424+# but WITHOUT ANY WARRANTY; without even the implied warranty of
425+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
426+# GNU Affero General Public License for more details.
427+#
428+# You should have received a copy of the GNU Affero General Public License
429+# along with this program. If not, see <http://www.gnu.org/licenses/>.
430+#
431+##############################################################################
432+import account_voucher
433+import account_invoice
434+import account_payment_term
435+import account_payment_term_cash_discount
436
437=== added file 'account_cash_discount/model/account_invoice.py'
438--- account_cash_discount/model/account_invoice.py 1970-01-01 00:00:00 +0000
439+++ account_cash_discount/model/account_invoice.py 2014-05-26 08:47:02 +0000
440@@ -0,0 +1,251 @@
441+# -*- coding: utf-8 -*-
442+##############################################################################
443+#
444+# OpenERP, Open Source Management Solution
445+# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
446+#
447+# This program is free software: you can redistribute it and/or modify
448+# it under the terms of the GNU Affero General Public License as
449+# published by the Free Software Foundation, either version 3 of the
450+# License, or (at your option) any later version.
451+#
452+# This program is distributed in the hope that it will be useful,
453+# but WITHOUT ANY WARRANTY; without even the implied warranty of
454+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
455+# GNU Affero General Public License for more details.
456+#
457+# You should have received a copy of the GNU Affero General Public License
458+# along with this program. If not, see <http://www.gnu.org/licenses/>.
459+#
460+##############################################################################
461+import datetime
462+from openerp.osv import orm, fields
463+from openerp.tools.translate import _
464+from openerp.tools import float_round, float_is_zero, float_compare,\
465+ DEFAULT_SERVER_DATE_FORMAT
466+
467+
468+class AccountInvoice(orm.Model):
469+ _inherit = 'account.invoice'
470+
471+ _columns = {
472+ 'cash_discount_move_id': fields.many2one(
473+ 'account.move', string='Cash discount correction move'),
474+ }
475+
476+ def create_cash_discount_move_lines(
477+ self, cr, uid, ids, discount, payment_move_lines, date=None,
478+ post_move=True, context=None):
479+ '''create correction entries for invoices eligible to a cash discount,
480+ given by the appropriate payment.term.line
481+
482+ :param discount: the discount
483+ :type discount: browse_record('payment.term.line')
484+ :param payment_move_lines: the payment move lines to create correction
485+ entries for
486+ :type payment_move_lines: browse_record_list('account.move.line')
487+ :param date: the date to post the move to
488+ :type date: string
489+
490+ :returns: list of created account.move.lines' ids
491+ '''
492+ if date is None:
493+ date = datetime.datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT)
494+
495+ account_move_line = self.pool.get('account.move.line')
496+ account_move = self.pool.get('account.move')
497+ precision = self.pool.get('decimal.precision').precision_get(
498+ cr, uid, 'Account')
499+ result = []
500+ for this in self.browse(cr, uid, ids, context=context):
501+ discount_move_id = account_move.create(
502+ cr, uid,
503+ {
504+ 'name': _('Cash discount for %s') % this.move_id.name,
505+ 'period_id': self.pool.get('account.period').find(
506+ cr, uid, dt=date, context=context)[0],
507+ 'journal_id': this.journal_id.id,
508+ 'date': date,
509+ },
510+ context=context)
511+
512+ #prepare to correct potential rounding errors
513+ total_debit_payment = total_credit_payment = 0
514+ for payment_move_line in payment_move_lines:
515+ total_debit_payment += payment_move_line.debit
516+ total_credit_payment += payment_move_line.credit
517+ total_debit_invoice = total_credit_invoice = 0
518+ #create correction entries
519+ total_debit_correction = total_credit_correction = 0
520+ for move_line in this.move_id.line_id:
521+ line_data = self.create_cash_discount_move_line_dict(
522+ discount_move_id, move_line, discount, precision)
523+ line_id = account_move_line.create(cr, uid, line_data,
524+ context=context)
525+ result.append(line_id)
526+ total_debit_correction += line_data['debit']
527+ total_credit_correction += line_data['credit']
528+ total_debit_invoice += move_line.debit
529+ total_credit_invoice += move_line.credit
530+
531+ #add rounding error to a matching correction entry
532+ if not float_is_zero(
533+ total_debit_correction - total_credit_correction,
534+ precision):
535+ error_credit = max(0, float_round(
536+ total_debit_correction - total_credit_correction,
537+ precision))
538+ error_debit = max(0, float_round(
539+ total_credit_correction - total_debit_correction,
540+ precision))
541+
542+ for correction_line in account_move_line.browse(
543+ cr, uid, result, context=context):
544+ if correction_line.credit and error_credit:
545+ correction_line.write(
546+ {'credit': correction_line.credit + error_credit})
547+ total_credit_correction += error_credit
548+ error_credit = 0
549+ if correction_line.debit and error_debit:
550+ correction_line.write(
551+ {'debit': correction_line.debit + error_debit})
552+ total_debit_correction += error_debit
553+ error_debit = 0
554+ if not error_credit and not error_debit:
555+ break
556+
557+ #write off differences within deviation margin
558+ writeoff_debit = total_debit_correction + total_debit_payment\
559+ - total_debit_invoice
560+ writeoff_credit = total_credit_correction + total_credit_payment\
561+ - total_credit_invoice
562+
563+ if float_compare(writeoff_debit, writeoff_credit, precision) == 0\
564+ and float_compare(abs(writeoff_debit),
565+ discount.allowed_deviation,
566+ precision) == -1:
567+ line_id = account_move_line.create(
568+ cr, uid,
569+ {
570+ 'name': _('Cash discount writeoff'),
571+ 'debit': abs(writeoff_debit),
572+ 'move_id': discount_move_id,
573+ 'partner_id': this.partner_id.id,
574+ 'account_id': this.account_id.id
575+ if writeoff_debit > 0
576+ else discount.discount_expense_account_id.id,
577+ },
578+ context=context)
579+ result.append(line_id)
580+
581+ line_id = account_move_line.create(
582+ cr, uid,
583+ {
584+ 'name': _('Cash discount writeoff'),
585+ 'credit': abs(writeoff_credit),
586+ 'move_id': discount_move_id,
587+ 'partner_id': this.partner_id.id,
588+ 'account_id': this.account_id.id
589+ if writeoff_credit < 0
590+ else discount.discount_income_account_id.id,
591+ },
592+ context=context)
593+ result.append(line_id)
594+
595+ this.write({'cash_discount_move_id': discount_move_id})
596+ if post_move:
597+ account_move.post(cr, uid, [discount_move_id], context=context)
598+ return result
599+
600+ def create_cash_discount_move_line_dict(self, move_id, move_line,
601+ discount, precision):
602+ '''return a dict suitable to create a correction entry for specified
603+ cash discount
604+
605+ :param move_id: the move to append correction entries to
606+ :type move: int
607+ :param move_line: the move to append correction entries to
608+ :type move_line: browse_record('account.move')
609+ :param discount: the discount
610+ :type discount: browse_record('payment.term.line')
611+
612+ :returns: dict that can be fed to account_move_line.create
613+ '''
614+
615+ account_id = move_line.account_id.id
616+ if move_line.account_id.id in [
617+ l.account_id.id for l in move_line.invoice.invoice_line]:
618+ if move_line.credit:
619+ account_id = discount.discount_expense_account_id.id\
620+ or account_id
621+ if move_line.debit:
622+ account_id = discount.discount_income_account_id.id\
623+ or account_id
624+
625+ data = self.pool.get('account.move.line').copy_data(
626+ move_line._cr, move_line._uid, move_line.id,
627+ default={
628+ 'name': _('Cash discount for %s') % move_line.name,
629+ 'debit': float_round(
630+ move_line.credit * discount.discount / 100,
631+ precision),
632+ 'credit': float_round(
633+ move_line.debit * discount.discount / 100,
634+ precision),
635+ 'tax_amount': float_round(
636+ -move_line.tax_amount * discount.discount / 100,
637+ precision),
638+ 'amount_currency': float_round(
639+ -move_line.amount_currency * discount.discount / 100,
640+ precision),
641+ 'move_id': move_id,
642+ 'account_id': account_id,
643+ },
644+ context=move_line._context)
645+ return data
646+
647+ def copy_data(self, cr, uid, id, default=None, context=None):
648+ '''reset cash_discount_move_id'''
649+ if default is None:
650+ default = {}
651+ default.setdefault('cash_discount_move_id', False)
652+
653+ return super(AccountInvoice, self).copy_data(
654+ cr, uid, id, default=default, context=None)
655+
656+ def get_matching_cash_discount(self, cr, uid, ids, amount,
657+ payment_date=None, context=None):
658+ '''return a cash discount that matches the amount paid and payment
659+ date'''
660+ result = False
661+ for this in self.browse(cr, uid, ids, context=context):
662+ if not this.payment_term or\
663+ not this.payment_term.cash_discount_ids:
664+ continue
665+ for discount in this.payment_term.cash_discount_ids:
666+ if discount.matches(amount, this.date_invoice,
667+ this.amount_total,
668+ payment_date=payment_date):
669+ return discount
670+ return result
671+
672+ def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None,
673+ description=None, journal_id=None, context=None):
674+ result = super(AccountInvoice, self)._prepare_refund(
675+ cr, uid, invoice, date=date, period_id=period_id,
676+ description=description, journal_id=journal_id, context=context)
677+ if invoice.cash_discount_move_id:
678+ result['payment_term'] = False
679+ for discount_move in invoice.cash_discount_move_id.line_id:
680+ if not discount_move.account_id == invoice.account_id:
681+ continue
682+ result['invoice_line'].append(
683+ (0, 0,
684+ {
685+ 'name': discount_move.name,
686+ 'account_id': discount_move.account_id.id,
687+ 'price_unit': -(discount_move.credit or
688+ -discount_move.debit),
689+ }))
690+
691+ return result
692
693=== added file 'account_cash_discount/model/account_payment_term.py'
694--- account_cash_discount/model/account_payment_term.py 1970-01-01 00:00:00 +0000
695+++ account_cash_discount/model/account_payment_term.py 2014-05-26 08:47:02 +0000
696@@ -0,0 +1,59 @@
697+# -*- coding: utf-8 -*-
698+##############################################################################
699+#
700+# OpenERP, Open Source Management Solution
701+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
702+# Copyright (C) 2012-2012 Camptocamp Austria (<http://www.camptocamp.at>)
703+# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
704+#
705+# This program is free software: you can redistribute it and/or modify
706+# it under the terms of the GNU Affero General Public License as
707+# published by the Free Software Foundation, either version 3 of the
708+# License, or (at your option) any later version.
709+#
710+# This program is distributed in the hope that it will be useful,
711+# but WITHOUT ANY WARRANTY; without even the implied warranty of
712+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
713+# GNU Affero General Public License for more details.
714+#
715+# You should have received a copy of the GNU Affero General Public License
716+# along with this program. If not, see <http://www.gnu.org/licenses/>.
717+#
718+##############################################################################
719+from openerp.osv import fields, orm
720+from openerp.tools.translate import _
721+
722+
723+class AccountPaymentTerm(orm.Model):
724+ _inherit = "account.payment.term"
725+ _columns = {
726+ 'cash_discount_ids': fields.one2many(
727+ 'account.payment.term.cash.discount', 'payment_term_id',
728+ 'Cash discount'),
729+ }
730+
731+ def _check_validity(self, cr, uid, ids, context=None):
732+ for this in self.browse(
733+ cr, uid, ids if isinstance(ids, list) else [ids],
734+ context=context):
735+ if not this.cash_discount_ids:
736+ continue
737+ for payment_term_line in this.line_ids:
738+ if payment_term_line.value == 'balance':
739+ continue
740+ raise orm.except_orm(
741+ _('Error'), _('When working with cash discounts, you can '
742+ 'only have one computation line of type '
743+ '"balance"!'))
744+
745+ def create(self, cr, uid, vals, context=None):
746+ result = super(AccountPaymentTerm, self).create(
747+ cr, uid, vals, context=context)
748+ self._check_validity(cr, uid, result, context=context)
749+ return result
750+
751+ def write(self, cr, uid, ids, vals, context=None):
752+ result = super(AccountPaymentTerm, self).write(
753+ cr, uid, ids, vals, context=context)
754+ self._check_validity(cr, uid, ids, context=context)
755+ return result
756
757=== added file 'account_cash_discount/model/account_payment_term_cash_discount.py'
758--- account_cash_discount/model/account_payment_term_cash_discount.py 1970-01-01 00:00:00 +0000
759+++ account_cash_discount/model/account_payment_term_cash_discount.py 2014-05-26 08:47:02 +0000
760@@ -0,0 +1,87 @@
761+# -*- coding: utf-8 -*-
762+##############################################################################
763+#
764+# OpenERP, Open Source Management Solution
765+# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
766+#
767+# This program is free software: you can redistribute it and/or modify
768+# it under the terms of the GNU Affero General Public License as
769+# published by the Free Software Foundation, either version 3 of the
770+# License, or (at your option) any later version.
771+#
772+# This program is distributed in the hope that it will be useful,
773+# but WITHOUT ANY WARRANTY; without even the implied warranty of
774+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
775+# GNU Affero General Public License for more details.
776+#
777+# You should have received a copy of the GNU Affero General Public License
778+# along with this program. If not, see <http://www.gnu.org/licenses/>.
779+#
780+##############################################################################
781+import datetime
782+from openerp.osv.orm import Model
783+from openerp.osv import fields
784+from openerp.tools import float_compare, DEFAULT_SERVER_DATE_FORMAT
785+
786+class AccountPaymentTermCashDiscount(Model):
787+ _name = 'account.payment.term.cash.discount'
788+ _description= 'Cash discount'
789+ _rec_name = 'days'
790+ _order = 'days'
791+
792+ _columns = {
793+ 'payment_term_id': fields.many2one(
794+ 'account.payment.term', 'Payment term', required=True),
795+ 'days': fields.integer('Days', required=True),
796+ 'discount': fields.float('Discount', required=True),
797+ 'allowed_deviation': fields.float(
798+ 'Allowed deviation',
799+ help="The amount a payment can deviate from the computed amount "
800+ "and still be accepted as payment within this discount.\n"
801+ "Think of rounding errors as use case for this"),
802+ 'discount_income_account_id': fields.many2one(
803+ 'account.account', string='Discount Income Account',
804+ help="This account will be used to post the cash discount income"),
805+ 'discount_expense_account_id': fields.many2one(
806+ 'account.account',
807+ string='Discount Expense Account',
808+ help="This account will be used to post the cash discount expense")
809+ }
810+
811+ def matches(self, cr, uid, ids, amount, invoice_date, invoice_amount,
812+ payment_date=None, context=None):
813+ '''determine if an amount paid at a certain date matches an invoiced
814+ amount from a certain date
815+
816+ :param amount: the amount paid
817+ :type amount: float
818+ :param invoice_date: the invoice's date
819+ :type invoice_date: string
820+ :param invoice_amount: the invoiced amount
821+ :type invoice_amount: float
822+ :param payment_date: the date of payment, today if None
823+ :type payment_date: string
824+ '''
825+ precision = self.pool.get('decimal.precision').precision_get(
826+ cr, uid, 'Account')
827+
828+ if not payment_date:
829+ payment_date = datetime.datetime.now().strftime(
830+ DEFAULT_SERVER_DATE_FORMAT)
831+
832+ for this in self.browse(cr, uid, ids, context=context):
833+ date = (
834+ datetime.datetime.strptime(
835+ invoice_date, DEFAULT_SERVER_DATE_FORMAT) +
836+ datetime.timedelta(days=this.days))\
837+ .strftime(DEFAULT_SERVER_DATE_FORMAT)
838+
839+ return date >= payment_date and float_compare(
840+ amount,
841+ (1 - this.discount / 100) * invoice_amount
842+ - this.allowed_deviation,
843+ precision) == 1 and float_compare(
844+ amount,
845+ (1 - this.discount / 100) * invoice_amount
846+ + this.allowed_deviation,
847+ precision) == -1
848
849=== added file 'account_cash_discount/model/account_voucher.py'
850--- account_cash_discount/model/account_voucher.py 1970-01-01 00:00:00 +0000
851+++ account_cash_discount/model/account_voucher.py 2014-05-26 08:47:02 +0000
852@@ -0,0 +1,75 @@
853+# -*- coding: utf-8 -*-
854+##############################################################################
855+#
856+# OpenERP, Open Source Management Solution
857+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
858+# Copyright (C) 2012-2012 Camptocamp Austria (<http://www.camptocamp.at>)
859+# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
860+#
861+# This program is free software: you can redistribute it and/or modify
862+# it under the terms of the GNU Affero General Public License as
863+# published by the Free Software Foundation, either version 3 of the
864+# License, or (at your option) any later version.
865+#
866+# This program is distributed in the hope that it will be useful,
867+# but WITHOUT ANY WARRANTY; without even the implied warranty of
868+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
869+# GNU Affero General Public License for more details.
870+#
871+# You should have received a copy of the GNU Affero General Public License
872+# along with this program. If not, see <http://www.gnu.org/licenses/>.
873+#
874+##############################################################################
875+
876+from openerp.osv import fields, orm
877+
878+
879+class AccountVoucher(orm.Model):
880+ _inherit = 'account.voucher'
881+
882+ def voucher_move_line_create(
883+ self, cr, uid, voucher_id, line_total, move_id, company_currency,
884+ current_currency, context=None):
885+ total, ids_list = super(AccountVoucher, self).voucher_move_line_create(
886+ cr, uid, voucher_id, line_total, move_id, company_currency,
887+ current_currency)
888+ '''add correction entries to payment if the payment amount matches a
889+ cash discount amount and is in time for that'''
890+
891+ account_move_line = self.pool.get('account.move.line')
892+
893+ precision = self.pool.get('decimal.precision').precision_get(
894+ cr, uid, 'Account')
895+ voucher = self.browse(cr, uid, voucher_id, context=context)
896+ move = self.pool.get('account.move').browse(
897+ cr, uid, move_id, context=context)
898+
899+ for ids in ids_list:
900+ move_lines = account_move_line.browse(cr, uid, ids,
901+ context=context)
902+ for move_line in move_lines:
903+ #only act on move liness with invoices whose payment term is a
904+ #cash discount
905+ if move_line.invoice and move_line.invoice.payment_term and\
906+ move_line.invoice.payment_term.cash_discount_ids:
907+
908+ #find a cash discount that matches current payment
909+ discount = move_line.invoice.get_matching_cash_discount(
910+ voucher.amount, payment_date=voucher.date)
911+ if not discount:
912+ continue
913+
914+ discount_move_line_ids = move_line.invoice\
915+ .create_cash_discount_move_lines(
916+ payment_move_lines=move.line_id,
917+ discount=discount, date=move.date)
918+
919+ #put correction entries into list with matching
920+ #original ones to have them reconciled at once
921+ for discount_move_line in account_move_line.browse(
922+ cr, uid, discount_move_line_ids, context=context):
923+ if discount_move_line.account_id == \
924+ move_line.account_id:
925+ ids.append(discount_move_line.id)
926+
927+ return total, ids_list
928
929=== added directory 'account_cash_discount/security'
930=== added file 'account_cash_discount/security/ir.model.access.csv'
931--- account_cash_discount/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
932+++ account_cash_discount/security/ir.model.access.csv 2014-05-26 08:47:02 +0000
933@@ -0,0 +1,3 @@
934+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
935+crud_cash_discount_line,"CRUD cash discount",model_account_payment_term_cash_discount,account.group_account_manager,1,1,1,1
936+r_cash_discount_line,"R cash discount",model_account_payment_term_cash_discount,account.group_account_user,1,0,0,0
937
938=== added directory 'account_cash_discount/view'
939=== added file 'account_cash_discount/view/account_invoice.xml'
940--- account_cash_discount/view/account_invoice.xml 1970-01-01 00:00:00 +0000
941+++ account_cash_discount/view/account_invoice.xml 2014-05-26 08:47:02 +0000
942@@ -0,0 +1,24 @@
943+<?xml version="1.0" encoding="UTF-8"?>
944+<openerp>
945+ <data>
946+ <record id="invoice_form" model="ir.ui.view">
947+ <field name="model">account.invoice</field>
948+ <field name="inherit_id" ref="account.invoice_form" />
949+ <field name="arch" type="xml">
950+ <data>
951+ <field name="move_id" position="after">
952+ <field name="cash_discount_move_id"
953+ groups="account.group_account_user"
954+ readonly="1"
955+ attrs="{'invisible': [('cash_discount_move_id', '=', False)]}"/>
956+ </field>
957+ <field name="residual" position="after">
958+ <div attrs="{'invisible': [('cash_discount_move_id', '=', False)]}" colspan="2">
959+ Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab
960+ </div>
961+ </field>
962+ </data>
963+ </field>
964+ </record>
965+ </data>
966+</openerp>
967
968=== added file 'account_cash_discount/view/account_move_line_reconcile.xml'
969--- account_cash_discount/view/account_move_line_reconcile.xml 1970-01-01 00:00:00 +0000
970+++ account_cash_discount/view/account_move_line_reconcile.xml 2014-05-26 08:47:02 +0000
971@@ -0,0 +1,26 @@
972+<?xml version="1.0" encoding="UTF-8"?>
973+<openerp>
974+ <data>
975+ <record id="view_account_move_line_reconcile_full" model="ir.ui.view">
976+ <field name="model">account.move.line.reconcile</field>
977+ <field name="inherit_id" ref="account.view_account_move_line_reconcile_full" />
978+ <field name="arch" type="xml">
979+ <data>
980+ <footer position="before">
981+ <group attrs="{'invisible': [('invoice_has_cash_discount', '=', False)]}">
982+ <field name="invoice_id" readonly="1" />
983+ <field name="invoice_has_cash_discount" invisible="1" />
984+ </group>
985+ </footer>
986+ <button string="Reconcile" position="after">
987+ <button string="Book difference as cash discount"
988+ type="object"
989+ name="reconcile_with_cash_discount"
990+ attrs="{'invisible': [('invoice_has_cash_discount', '=', False)]}"
991+ />
992+ </button>
993+ </data>
994+ </field>
995+ </record>
996+ </data>
997+</openerp>
998
999=== added file 'account_cash_discount/view/account_payment_term.xml'
1000--- account_cash_discount/view/account_payment_term.xml 1970-01-01 00:00:00 +0000
1001+++ account_cash_discount/view/account_payment_term.xml 2014-05-26 08:47:02 +0000
1002@@ -0,0 +1,49 @@
1003+<?xml version="1.0" encoding="utf-8"?>
1004+<openerp>
1005+ <data>
1006+
1007+ <record id="view_payment_term_disc_tree" model="ir.ui.view">
1008+ <field name="name">account.payment.term.disc.tree</field>
1009+ <field name="model">account.payment.term</field>
1010+ <field name="type">tree</field>
1011+ <field name="arch" type="xml">
1012+ <tree string="Payment Term">
1013+ <field name="name"/>
1014+ <field name="active"/>
1015+ </tree>
1016+ </field>
1017+ </record>
1018+
1019+
1020+ <record id="view_payment_term_disc_form" model="ir.ui.view">
1021+ <field name="name">account.payment.term.disc.form</field>
1022+ <field name="model">account.payment.term</field>
1023+ <field name="inherit_id" ref="account.view_payment_term_form"/>
1024+ <field name="type">form</field>
1025+ <field name="arch" type="xml">
1026+ <field name="line_ids" position="after">
1027+ <div class="oe_horizontal_separator oe_clear"><label for="cash_discount_ids" /></div>
1028+ <field name="cash_discount_ids">
1029+ <tree>
1030+ <field name="days" />
1031+ <field name="discount" />
1032+ <field name="allowed_deviation" />
1033+ </tree>
1034+ <form>
1035+ <group>
1036+ <field name="days" />
1037+ <field name="discount" />
1038+ <field name="allowed_deviation" />
1039+ </group>
1040+ <group>
1041+ <field name="discount_expense_account_id" />
1042+ <field name="discount_income_account_id" />
1043+ </group>
1044+ </form>
1045+ </field>
1046+ </field>
1047+ </field>
1048+ </record>
1049+ </data>
1050+</openerp>
1051+
1052
1053=== added directory 'account_cash_discount/wizard'
1054=== added file 'account_cash_discount/wizard/__init__.py'
1055--- account_cash_discount/wizard/__init__.py 1970-01-01 00:00:00 +0000
1056+++ account_cash_discount/wizard/__init__.py 2014-05-26 08:47:02 +0000
1057@@ -0,0 +1,22 @@
1058+# -*- coding: utf-8 -*-
1059+##############################################################################
1060+#
1061+# OpenERP, Open Source Management Solution
1062+# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
1063+#
1064+# This program is free software: you can redistribute it and/or modify
1065+# it under the terms of the GNU Affero General Public License as
1066+# published by the Free Software Foundation, either version 3 of the
1067+# License, or (at your option) any later version.
1068+#
1069+# This program is distributed in the hope that it will be useful,
1070+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1071+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1072+# GNU Affero General Public License for more details.
1073+#
1074+# You should have received a copy of the GNU Affero General Public License
1075+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1076+#
1077+##############################################################################
1078+import account_move_line_reconcile
1079+import account_move_line_reconcile
1080
1081=== added file 'account_cash_discount/wizard/account_move_line_reconcile.py'
1082--- account_cash_discount/wizard/account_move_line_reconcile.py 1970-01-01 00:00:00 +0000
1083+++ account_cash_discount/wizard/account_move_line_reconcile.py 2014-05-26 08:47:02 +0000
1084@@ -0,0 +1,76 @@
1085+# -*- coding: utf-8 -*-
1086+##############################################################################
1087+#
1088+# OpenERP, Open Source Management Solution
1089+# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
1090+#
1091+# This program is free software: you can redistribute it and/or modify
1092+# it under the terms of the GNU Affero General Public License as
1093+# published by the Free Software Foundation, either version 3 of the
1094+# License, or (at your option) any later version.
1095+#
1096+# This program is distributed in the hope that it will be useful,
1097+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1098+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1099+# GNU Affero General Public License for more details.
1100+#
1101+# You should have received a copy of the GNU Affero General Public License
1102+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1103+#
1104+##############################################################################
1105+from openerp.osv.orm import Model
1106+from openerp.osv import fields
1107+
1108+class AccountMoveLineReconcile(Model):
1109+ _inherit = 'account.move.line.reconcile'
1110+
1111+ _columns = {
1112+ 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
1113+ 'invoice_has_cash_discount': fields.boolean('Cash discount on invoice')
1114+ }
1115+
1116+ def default_get(self, cr, uid, fields, context=None):
1117+ result = super(AccountMoveLineReconcile, self).default_get(
1118+ cr, uid, fields, context=context)
1119+ for move_line in self.pool.get('account.move.line').browse(
1120+ cr, uid, context.get('active_ids', []), context=context):
1121+ if move_line.invoice:
1122+ if move_line.invoice.id != result.get('invoice_id'):
1123+ result['invoice_id'] = move_line.invoice.id
1124+ result['invoice_has_cash_discount'] = bool(
1125+ move_line.invoice.get_matching_cash_discount(
1126+ result.get('credit')) or
1127+ move_line.invoice.get_matching_cash_discount(
1128+ result.get('debit')))
1129+ else:
1130+ result['invoice_id'] = False
1131+ result['invoice_has_cash_discount'] = False
1132+ return result
1133+
1134+ def reconcile_with_cash_discount(self, cr, uid, ids, context=None):
1135+ account_move_line = self.pool.get('account.move.line')
1136+ for this in self.browse(cr, uid, ids, context=context):
1137+ discount = this.invoice_id.get_matching_cash_discount(this.credit)\
1138+ or this.invoice_id.get_matching_cash_discount(this.debit)
1139+ if not discount:
1140+ continue
1141+ payment_move_lines = [
1142+ l for ml in account_move_line.browse(
1143+ cr, uid, context.get('active_ids', []),
1144+ context=context)
1145+ for l in ml.move_id.line_id
1146+ if not l.invoice]
1147+ correction_move_line_ids = this.invoice_id\
1148+ .create_cash_discount_move_lines(discount,
1149+ payment_move_lines)
1150+ reconcile_ids = context.get('active_ids', [])
1151+ for correction_move_line in account_move_line.browse(
1152+ cr, uid, correction_move_line_ids, context=context):
1153+ for payment_move_line in payment_move_lines:
1154+ if payment_move_line.account_id ==\
1155+ correction_move_line.account_id:
1156+ reconcile_ids.append(correction_move_line.id)
1157+ break
1158+ return self.trans_rec_reconcile_full(
1159+ cr, uid, [this.id], dict(context, active_ids=reconcile_ids))
1160+ pass

Subscribers

People subscribed via source and target branches