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)
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 Needs Resubmitting
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)

[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)

[ADD] translation

29. By Holger Brunn (Therp)

[ADD] handle refunds with cash discounts

Revision history for this message
Erwin van der Ploeg (BAS Solutions) (erwin-bassolutions-deactivatedaccount) wrote :

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.

Revision history for this message
Erwin van der Ploeg (BAS Solutions) (erwin-bassolutions-deactivatedaccount) wrote :

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)

[FIX] wrong numbers in example

Revision history for this message
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.

Revision history for this message
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: Needs Resubmitting

Unmerged revisions

30. By Holger Brunn (Therp)

[FIX] wrong numbers in example

29. By Holger Brunn (Therp)

[ADD] handle refunds with cash discounts

28. By Holger Brunn (Therp)

[ADD] translation

27. By Holger Brunn (Therp)

[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)

[ADD] account_cash_discount

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'account_cash_discount'
=== added file 'account_cash_discount/__init__.py'
--- account_cash_discount/__init__.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/__init__.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,25 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6# Copyright (C) 2010-2012 Camptocamp Austria (<http://www.camptocamp.at>)
7# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23import model
24import wizard
25# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
026
=== added file 'account_cash_discount/__openerp__.py'
--- account_cash_discount/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/__openerp__.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,84 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6# Copyright (C) 2010-2012 Camptocamp Austria (<http://www.camptocamp.at>)
7# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24
25{
26 'name': 'Cash discount',
27 'version': '0.9',
28 'category': 'Accounting & Finance',
29 'description': """
30Cash Discount (Austria and Germany style)
31=========================================
32
33Usage
34-----
35
36Define your discounts as part of your payment terms. Keep in mind that other
37computation lines than 'balance' won't make much sense, so only fill in one
38computation line of type 'balance' indicating your payment date.
39
40When paying your invoice, fill in the exact amount for the discounted invoice.
41If the payment date and the amount matches a discount, the invoice will be
42marked as paid and some correction move lines will be created to reflect the
43invoice being paid with a discount.
44
45If you manually reconcile move lines, you'll be offered a button to book the
46writeoff amount as cash discount if a matching cash discount can be found.
47
48Example
49-------
50
51On 01/20/2014, you charged EUR 80 to a customer, with 25% tax, totalling in
52EUR 100. Further, you set up a payment term for this invoice that includes
535% discount if paid within a week.
54
55If you fill in EUR 95 and payment date 01/22/2014 when paying the invoice, the
56following happens:
57
58- the invoice is marked as paid
59- on the invoice's 'Other info' tag, you'll find a field 'Cash discount
60 correction' with the following lines:
61
62 - EUR 1 as debit on your tax account
63 - EUR 4 as debit on your income account
64 - EUR 5 as credit on your customer's account
65
66TODO
67----
68
69- cash discounts don't show up on invoice report
70- multi currency not tested
71- no automatic reconciliation
72""",
73 'author': 'Therp BV',
74 'depends': [ 'account_voucher' ],
75 'data': [
76 'view/account_move_line_reconcile.xml',
77 'view/account_payment_term.xml',
78 'view/account_invoice.xml',
79 'security/ir.model.access.csv',
80 ],
81 'installable': True,
82 'auto_install': False,
83}
84# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
085
=== added directory 'account_cash_discount/i18n'
=== added file 'account_cash_discount/i18n/account_cash_discount.pot'
--- account_cash_discount/i18n/account_cash_discount.pot 1970-01-01 00:00:00 +0000
+++ account_cash_discount/i18n/account_cash_discount.pot 2014-05-26 08:47:02 +0000
@@ -0,0 +1,142 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * account_cash_discount
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-02-18 15:41+0000\n"
10"PO-Revision-Date: 2014-02-18 15:41+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: account_cash_discount
19#: code:addons/account_cash_discount/model/account_invoice.py:130
20#: code:addons/account_cash_discount/model/account_invoice.py:144
21#, python-format
22msgid "Cash discount writeoff"
23msgstr ""
24
25#. module: account_cash_discount
26#: view:account.invoice:0
27msgid "Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab"
28msgstr ""
29
30#. module: account_cash_discount
31#: field:account.payment.term.cash.discount,allowed_deviation:0
32msgid "Allowed deviation"
33msgstr ""
34
35#. module: account_cash_discount
36#: help:account.payment.term.cash.discount,discount_expense_account_id:0
37msgid "This account will be used to post the cash discount expense"
38msgstr ""
39
40#. module: account_cash_discount
41#: field:account.move.line.reconcile,invoice_has_cash_discount:0
42msgid "Cash discount on invoice"
43msgstr ""
44
45#. module: account_cash_discount
46#: model:ir.model,name:account_cash_discount.model_account_move_line_reconcile
47msgid "Account move line reconcile"
48msgstr ""
49
50#. module: account_cash_discount
51#: code:addons/account_cash_discount/model/account_payment_term.py:45
52#, python-format
53msgid "When working with cash discounts, you can only have one computation line of type \"balance\"!"
54msgstr ""
55
56#. module: account_cash_discount
57#: field:account.payment.term.cash.discount,discount_income_account_id:0
58msgid "Discount Income Account"
59msgstr ""
60
61#. module: account_cash_discount
62#: help:account.payment.term.cash.discount,discount_income_account_id:0
63msgid "This account will be used to post the cash discount income"
64msgstr ""
65
66#. module: account_cash_discount
67#: field:account.payment.term.cash.discount,discount:0
68msgid "Discount"
69msgstr ""
70
71#. module: account_cash_discount
72#: field:account.invoice,cash_discount_move_id:0
73msgid "Cash discount correction move"
74msgstr ""
75
76#. module: account_cash_discount
77#: view:account.move.line.reconcile:0
78msgid "Book difference as cash discount"
79msgstr ""
80
81#. module: account_cash_discount
82#: field:account.payment.term.cash.discount,payment_term_id:0
83msgid "Payment term"
84msgstr ""
85
86#. module: account_cash_discount
87#: field:account.payment.term.cash.discount,days:0
88msgid "Days"
89msgstr ""
90
91#. module: account_cash_discount
92#: code:addons/account_cash_discount/model/account_payment_term.py:45
93#, python-format
94msgid "Error"
95msgstr ""
96
97#. module: account_cash_discount
98#: field:account.payment.term.cash.discount,discount_expense_account_id:0
99msgid "Discount Expense Account"
100msgstr ""
101
102#. module: account_cash_discount
103#: field:account.payment.term,cash_discount_ids:0
104#: model:ir.model,name:account_cash_discount.model_account_payment_term_cash_discount
105msgid "Cash discount"
106msgstr ""
107
108#. module: account_cash_discount
109#: view:account.move.line.reconcile:0
110msgid "Reconcile"
111msgstr ""
112
113#. module: account_cash_discount
114#: model:ir.model,name:account_cash_discount.model_account_voucher
115msgid "Accounting Voucher"
116msgstr ""
117
118#. module: account_cash_discount
119#: view:account.payment.term:0
120#: model:ir.model,name:account_cash_discount.model_account_payment_term
121msgid "Payment Term"
122msgstr ""
123
124#. module: account_cash_discount
125#: help:account.payment.term.cash.discount,allowed_deviation:0
126msgid "The amount a payment can deviate from the computed amount and still be accepted as payment within this discount.\n"
127"Think of rounding errors as use case for this"
128msgstr ""
129
130#. module: account_cash_discount
131#: code:addons/account_cash_discount/model/account_invoice.py:64
132#: code:addons/account_cash_discount/model/account_invoice.py:188
133#, python-format
134msgid "Cash discount for %s"
135msgstr ""
136
137#. module: account_cash_discount
138#: field:account.move.line.reconcile,invoice_id:0
139#: model:ir.model,name:account_cash_discount.model_account_invoice
140msgid "Invoice"
141msgstr ""
142
0143
=== added file 'account_cash_discount/i18n/de.po'
--- account_cash_discount/i18n/de.po 1970-01-01 00:00:00 +0000
+++ account_cash_discount/i18n/de.po 2014-05-26 08:47:02 +0000
@@ -0,0 +1,133 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * account_cash_discount
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-02-18 15:41+0000\n"
10"PO-Revision-Date: 2014-02-18 15:41+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: account_cash_discount
19#: code:addons/account_cash_discount/model/account_invoice.py:130
20#: code:addons/account_cash_discount/model/account_invoice.py:144
21#, python-format
22msgid "Cash discount writeoff"
23msgstr "Skontoabschreibung"
24
25#. module: account_cash_discount
26#: view:account.invoice:0
27msgid "Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab"
28msgstr "Achtung: Diese Rechnung wurde unter Einbehalt von Skonto bezahlt. Die ensprechenden Buchungen finden sich im 'Weitere Info'-Reiter."
29
30#. module: account_cash_discount
31#: field:account.payment.term.cash.discount,allowed_deviation:0
32msgid "Allowed deviation"
33msgstr "Erlaubte Abweichung"
34
35#. module: account_cash_discount
36#: help:account.payment.term.cash.discount,discount_expense_account_id:0
37msgid "This account will be used to post the cash discount expense"
38msgstr "Skontoausgaben werden auf dieses Konto gebucht"
39
40#. module: account_cash_discount
41#: field:account.move.line.reconcile,invoice_has_cash_discount:0
42msgid "Cash discount on invoice"
43msgstr "Rechnung mit Skonto"
44
45#. module: account_cash_discount
46#: model:ir.model,name:account_cash_discount.model_account_move_line_reconcile
47msgid "Account move line reconcile"
48msgstr "Buchen OP Ausgleich"
49
50#. module: account_cash_discount
51#: code:addons/account_cash_discount/model/account_payment_term.py:45
52#, python-format
53msgid "When working with cash discounts, you can only have one computation line of type \"balance\"!"
54msgstr "Skontobezahlungen können nur eine Berechnung vom Typ \"Saldo\" beinhalten!"
55
56#. module: account_cash_discount
57#: field:account.payment.term.cash.discount,discount_income_account_id:0
58msgid "Discount Income Account"
59msgstr "Skonto-Erlöskonto"
60
61#. module: account_cash_discount
62#: help:account.payment.term.cash.discount,discount_income_account_id:0
63msgid "This account will be used to post the cash discount income"
64msgstr "Skontoerlöse werden auf dieses Konto gebucht"
65
66#. module: account_cash_discount
67#: field:account.payment.term.cash.discount,discount:0
68msgid "Discount"
69msgstr "Skonto"
70
71#. module: account_cash_discount
72#: field:account.invoice,cash_discount_move_id:0
73msgid "Cash discount correction move"
74msgstr "Skonto-Buchungssatz"
75
76#. module: account_cash_discount
77#: view:account.move.line.reconcile:0
78msgid "Book difference as cash discount"
79msgstr "Differenz als Skonto buchen"
80
81#. module: account_cash_discount
82#: field:account.payment.term.cash.discount,payment_term_id:0
83msgid "Payment term"
84msgstr "Zahlungsbedingung"
85
86#. module: account_cash_discount
87#: field:account.payment.term.cash.discount,days:0
88msgid "Days"
89msgstr "Tage"
90
91#. module: account_cash_discount
92#: code:addons/account_cash_discount/model/account_payment_term.py:45
93#, python-format
94msgid "Error"
95msgstr "Fehler"
96
97#. module: account_cash_discount
98#: field:account.payment.term.cash.discount,discount_expense_account_id:0
99msgid "Discount Expense Account"
100msgstr "Skonto-Aufwandskonto"
101
102#. module: account_cash_discount
103#: field:account.payment.term,cash_discount_ids:0
104#: model:ir.model,name:account_cash_discount.model_account_payment_term_cash_discount
105msgid "Cash discount"
106msgstr "Skonto"
107
108#. module: account_cash_discount
109#: view:account.payment.term:0
110#: model:ir.model,name:account_cash_discount.model_account_payment_term
111msgid "Payment Term"
112msgstr "Zahlungsbedingung"
113
114#. module: account_cash_discount
115#: help:account.payment.term.cash.discount,allowed_deviation:0
116msgid "The amount a payment can deviate from the computed amount and still be accepted as payment within this discount.\n"
117"Think of rounding errors as use case for this"
118msgstr "Der Betrag um den eine Zahlung vom berechneten Betrag abweichen darf um noch als Zahlung innerhalb des Skontos angesehen zu werden.\n"
119"Dies vereinfact die Handhabung von Rundungsunterschieden."
120
121#. module: account_cash_discount
122#: code:addons/account_cash_discount/model/account_invoice.py:64
123#: code:addons/account_cash_discount/model/account_invoice.py:188
124#, python-format
125msgid "Cash discount for %s"
126msgstr "Skonto für %s"
127
128#. module: account_cash_discount
129#: field:account.move.line.reconcile,invoice_id:0
130#: model:ir.model,name:account_cash_discount.model_account_invoice
131msgid "Invoice"
132msgstr "Rechnung"
133
0134
=== added directory 'account_cash_discount/model'
=== added file 'account_cash_discount/model/__init__.py'
--- account_cash_discount/model/__init__.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/model/__init__.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,24 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 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##############################################################################
21import account_voucher
22import account_invoice
23import account_payment_term
24import account_payment_term_cash_discount
025
=== added file 'account_cash_discount/model/account_invoice.py'
--- account_cash_discount/model/account_invoice.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/model/account_invoice.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,251 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 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##############################################################################
21import datetime
22from openerp.osv import orm, fields
23from openerp.tools.translate import _
24from openerp.tools import float_round, float_is_zero, float_compare,\
25 DEFAULT_SERVER_DATE_FORMAT
26
27
28class AccountInvoice(orm.Model):
29 _inherit = 'account.invoice'
30
31 _columns = {
32 'cash_discount_move_id': fields.many2one(
33 'account.move', string='Cash discount correction move'),
34 }
35
36 def create_cash_discount_move_lines(
37 self, cr, uid, ids, discount, payment_move_lines, date=None,
38 post_move=True, context=None):
39 '''create correction entries for invoices eligible to a cash discount,
40 given by the appropriate payment.term.line
41
42 :param discount: the discount
43 :type discount: browse_record('payment.term.line')
44 :param payment_move_lines: the payment move lines to create correction
45 entries for
46 :type payment_move_lines: browse_record_list('account.move.line')
47 :param date: the date to post the move to
48 :type date: string
49
50 :returns: list of created account.move.lines' ids
51 '''
52 if date is None:
53 date = datetime.datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT)
54
55 account_move_line = self.pool.get('account.move.line')
56 account_move = self.pool.get('account.move')
57 precision = self.pool.get('decimal.precision').precision_get(
58 cr, uid, 'Account')
59 result = []
60 for this in self.browse(cr, uid, ids, context=context):
61 discount_move_id = account_move.create(
62 cr, uid,
63 {
64 'name': _('Cash discount for %s') % this.move_id.name,
65 'period_id': self.pool.get('account.period').find(
66 cr, uid, dt=date, context=context)[0],
67 'journal_id': this.journal_id.id,
68 'date': date,
69 },
70 context=context)
71
72 #prepare to correct potential rounding errors
73 total_debit_payment = total_credit_payment = 0
74 for payment_move_line in payment_move_lines:
75 total_debit_payment += payment_move_line.debit
76 total_credit_payment += payment_move_line.credit
77 total_debit_invoice = total_credit_invoice = 0
78 #create correction entries
79 total_debit_correction = total_credit_correction = 0
80 for move_line in this.move_id.line_id:
81 line_data = self.create_cash_discount_move_line_dict(
82 discount_move_id, move_line, discount, precision)
83 line_id = account_move_line.create(cr, uid, line_data,
84 context=context)
85 result.append(line_id)
86 total_debit_correction += line_data['debit']
87 total_credit_correction += line_data['credit']
88 total_debit_invoice += move_line.debit
89 total_credit_invoice += move_line.credit
90
91 #add rounding error to a matching correction entry
92 if not float_is_zero(
93 total_debit_correction - total_credit_correction,
94 precision):
95 error_credit = max(0, float_round(
96 total_debit_correction - total_credit_correction,
97 precision))
98 error_debit = max(0, float_round(
99 total_credit_correction - total_debit_correction,
100 precision))
101
102 for correction_line in account_move_line.browse(
103 cr, uid, result, context=context):
104 if correction_line.credit and error_credit:
105 correction_line.write(
106 {'credit': correction_line.credit + error_credit})
107 total_credit_correction += error_credit
108 error_credit = 0
109 if correction_line.debit and error_debit:
110 correction_line.write(
111 {'debit': correction_line.debit + error_debit})
112 total_debit_correction += error_debit
113 error_debit = 0
114 if not error_credit and not error_debit:
115 break
116
117 #write off differences within deviation margin
118 writeoff_debit = total_debit_correction + total_debit_payment\
119 - total_debit_invoice
120 writeoff_credit = total_credit_correction + total_credit_payment\
121 - total_credit_invoice
122
123 if float_compare(writeoff_debit, writeoff_credit, precision) == 0\
124 and float_compare(abs(writeoff_debit),
125 discount.allowed_deviation,
126 precision) == -1:
127 line_id = account_move_line.create(
128 cr, uid,
129 {
130 'name': _('Cash discount writeoff'),
131 'debit': abs(writeoff_debit),
132 'move_id': discount_move_id,
133 'partner_id': this.partner_id.id,
134 'account_id': this.account_id.id
135 if writeoff_debit > 0
136 else discount.discount_expense_account_id.id,
137 },
138 context=context)
139 result.append(line_id)
140
141 line_id = account_move_line.create(
142 cr, uid,
143 {
144 'name': _('Cash discount writeoff'),
145 'credit': abs(writeoff_credit),
146 'move_id': discount_move_id,
147 'partner_id': this.partner_id.id,
148 'account_id': this.account_id.id
149 if writeoff_credit < 0
150 else discount.discount_income_account_id.id,
151 },
152 context=context)
153 result.append(line_id)
154
155 this.write({'cash_discount_move_id': discount_move_id})
156 if post_move:
157 account_move.post(cr, uid, [discount_move_id], context=context)
158 return result
159
160 def create_cash_discount_move_line_dict(self, move_id, move_line,
161 discount, precision):
162 '''return a dict suitable to create a correction entry for specified
163 cash discount
164
165 :param move_id: the move to append correction entries to
166 :type move: int
167 :param move_line: the move to append correction entries to
168 :type move_line: browse_record('account.move')
169 :param discount: the discount
170 :type discount: browse_record('payment.term.line')
171
172 :returns: dict that can be fed to account_move_line.create
173 '''
174
175 account_id = move_line.account_id.id
176 if move_line.account_id.id in [
177 l.account_id.id for l in move_line.invoice.invoice_line]:
178 if move_line.credit:
179 account_id = discount.discount_expense_account_id.id\
180 or account_id
181 if move_line.debit:
182 account_id = discount.discount_income_account_id.id\
183 or account_id
184
185 data = self.pool.get('account.move.line').copy_data(
186 move_line._cr, move_line._uid, move_line.id,
187 default={
188 'name': _('Cash discount for %s') % move_line.name,
189 'debit': float_round(
190 move_line.credit * discount.discount / 100,
191 precision),
192 'credit': float_round(
193 move_line.debit * discount.discount / 100,
194 precision),
195 'tax_amount': float_round(
196 -move_line.tax_amount * discount.discount / 100,
197 precision),
198 'amount_currency': float_round(
199 -move_line.amount_currency * discount.discount / 100,
200 precision),
201 'move_id': move_id,
202 'account_id': account_id,
203 },
204 context=move_line._context)
205 return data
206
207 def copy_data(self, cr, uid, id, default=None, context=None):
208 '''reset cash_discount_move_id'''
209 if default is None:
210 default = {}
211 default.setdefault('cash_discount_move_id', False)
212
213 return super(AccountInvoice, self).copy_data(
214 cr, uid, id, default=default, context=None)
215
216 def get_matching_cash_discount(self, cr, uid, ids, amount,
217 payment_date=None, context=None):
218 '''return a cash discount that matches the amount paid and payment
219 date'''
220 result = False
221 for this in self.browse(cr, uid, ids, context=context):
222 if not this.payment_term or\
223 not this.payment_term.cash_discount_ids:
224 continue
225 for discount in this.payment_term.cash_discount_ids:
226 if discount.matches(amount, this.date_invoice,
227 this.amount_total,
228 payment_date=payment_date):
229 return discount
230 return result
231
232 def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None,
233 description=None, journal_id=None, context=None):
234 result = super(AccountInvoice, self)._prepare_refund(
235 cr, uid, invoice, date=date, period_id=period_id,
236 description=description, journal_id=journal_id, context=context)
237 if invoice.cash_discount_move_id:
238 result['payment_term'] = False
239 for discount_move in invoice.cash_discount_move_id.line_id:
240 if not discount_move.account_id == invoice.account_id:
241 continue
242 result['invoice_line'].append(
243 (0, 0,
244 {
245 'name': discount_move.name,
246 'account_id': discount_move.account_id.id,
247 'price_unit': -(discount_move.credit or
248 -discount_move.debit),
249 }))
250
251 return result
0252
=== added file 'account_cash_discount/model/account_payment_term.py'
--- account_cash_discount/model/account_payment_term.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/model/account_payment_term.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,59 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6# Copyright (C) 2012-2012 Camptocamp Austria (<http://www.camptocamp.at>)
7# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23from openerp.osv import fields, orm
24from openerp.tools.translate import _
25
26
27class AccountPaymentTerm(orm.Model):
28 _inherit = "account.payment.term"
29 _columns = {
30 'cash_discount_ids': fields.one2many(
31 'account.payment.term.cash.discount', 'payment_term_id',
32 'Cash discount'),
33 }
34
35 def _check_validity(self, cr, uid, ids, context=None):
36 for this in self.browse(
37 cr, uid, ids if isinstance(ids, list) else [ids],
38 context=context):
39 if not this.cash_discount_ids:
40 continue
41 for payment_term_line in this.line_ids:
42 if payment_term_line.value == 'balance':
43 continue
44 raise orm.except_orm(
45 _('Error'), _('When working with cash discounts, you can '
46 'only have one computation line of type '
47 '"balance"!'))
48
49 def create(self, cr, uid, vals, context=None):
50 result = super(AccountPaymentTerm, self).create(
51 cr, uid, vals, context=context)
52 self._check_validity(cr, uid, result, context=context)
53 return result
54
55 def write(self, cr, uid, ids, vals, context=None):
56 result = super(AccountPaymentTerm, self).write(
57 cr, uid, ids, vals, context=context)
58 self._check_validity(cr, uid, ids, context=context)
59 return result
060
=== added file 'account_cash_discount/model/account_payment_term_cash_discount.py'
--- account_cash_discount/model/account_payment_term_cash_discount.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/model/account_payment_term_cash_discount.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,87 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 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##############################################################################
21import datetime
22from openerp.osv.orm import Model
23from openerp.osv import fields
24from openerp.tools import float_compare, DEFAULT_SERVER_DATE_FORMAT
25
26class AccountPaymentTermCashDiscount(Model):
27 _name = 'account.payment.term.cash.discount'
28 _description= 'Cash discount'
29 _rec_name = 'days'
30 _order = 'days'
31
32 _columns = {
33 'payment_term_id': fields.many2one(
34 'account.payment.term', 'Payment term', required=True),
35 'days': fields.integer('Days', required=True),
36 'discount': fields.float('Discount', required=True),
37 'allowed_deviation': fields.float(
38 'Allowed deviation',
39 help="The amount a payment can deviate from the computed amount "
40 "and still be accepted as payment within this discount.\n"
41 "Think of rounding errors as use case for this"),
42 'discount_income_account_id': fields.many2one(
43 'account.account', string='Discount Income Account',
44 help="This account will be used to post the cash discount income"),
45 'discount_expense_account_id': fields.many2one(
46 'account.account',
47 string='Discount Expense Account',
48 help="This account will be used to post the cash discount expense")
49 }
50
51 def matches(self, cr, uid, ids, amount, invoice_date, invoice_amount,
52 payment_date=None, context=None):
53 '''determine if an amount paid at a certain date matches an invoiced
54 amount from a certain date
55
56 :param amount: the amount paid
57 :type amount: float
58 :param invoice_date: the invoice's date
59 :type invoice_date: string
60 :param invoice_amount: the invoiced amount
61 :type invoice_amount: float
62 :param payment_date: the date of payment, today if None
63 :type payment_date: string
64 '''
65 precision = self.pool.get('decimal.precision').precision_get(
66 cr, uid, 'Account')
67
68 if not payment_date:
69 payment_date = datetime.datetime.now().strftime(
70 DEFAULT_SERVER_DATE_FORMAT)
71
72 for this in self.browse(cr, uid, ids, context=context):
73 date = (
74 datetime.datetime.strptime(
75 invoice_date, DEFAULT_SERVER_DATE_FORMAT) +
76 datetime.timedelta(days=this.days))\
77 .strftime(DEFAULT_SERVER_DATE_FORMAT)
78
79 return date >= payment_date and float_compare(
80 amount,
81 (1 - this.discount / 100) * invoice_amount
82 - this.allowed_deviation,
83 precision) == 1 and float_compare(
84 amount,
85 (1 - this.discount / 100) * invoice_amount
86 + this.allowed_deviation,
87 precision) == -1
088
=== added file 'account_cash_discount/model/account_voucher.py'
--- account_cash_discount/model/account_voucher.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/model/account_voucher.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,75 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6# Copyright (C) 2012-2012 Camptocamp Austria (<http://www.camptocamp.at>)
7# Copyright (C) 2014 Therp BV (<http://www.therp.nl>)
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU Affero General Public License as
11# published by the Free Software Foundation, either version 3 of the
12# License, or (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22##############################################################################
23
24from openerp.osv import fields, orm
25
26
27class AccountVoucher(orm.Model):
28 _inherit = 'account.voucher'
29
30 def voucher_move_line_create(
31 self, cr, uid, voucher_id, line_total, move_id, company_currency,
32 current_currency, context=None):
33 total, ids_list = super(AccountVoucher, self).voucher_move_line_create(
34 cr, uid, voucher_id, line_total, move_id, company_currency,
35 current_currency)
36 '''add correction entries to payment if the payment amount matches a
37 cash discount amount and is in time for that'''
38
39 account_move_line = self.pool.get('account.move.line')
40
41 precision = self.pool.get('decimal.precision').precision_get(
42 cr, uid, 'Account')
43 voucher = self.browse(cr, uid, voucher_id, context=context)
44 move = self.pool.get('account.move').browse(
45 cr, uid, move_id, context=context)
46
47 for ids in ids_list:
48 move_lines = account_move_line.browse(cr, uid, ids,
49 context=context)
50 for move_line in move_lines:
51 #only act on move liness with invoices whose payment term is a
52 #cash discount
53 if move_line.invoice and move_line.invoice.payment_term and\
54 move_line.invoice.payment_term.cash_discount_ids:
55
56 #find a cash discount that matches current payment
57 discount = move_line.invoice.get_matching_cash_discount(
58 voucher.amount, payment_date=voucher.date)
59 if not discount:
60 continue
61
62 discount_move_line_ids = move_line.invoice\
63 .create_cash_discount_move_lines(
64 payment_move_lines=move.line_id,
65 discount=discount, date=move.date)
66
67 #put correction entries into list with matching
68 #original ones to have them reconciled at once
69 for discount_move_line in account_move_line.browse(
70 cr, uid, discount_move_line_ids, context=context):
71 if discount_move_line.account_id == \
72 move_line.account_id:
73 ids.append(discount_move_line.id)
74
75 return total, ids_list
076
=== added directory 'account_cash_discount/security'
=== added file 'account_cash_discount/security/ir.model.access.csv'
--- account_cash_discount/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ account_cash_discount/security/ir.model.access.csv 2014-05-26 08:47:02 +0000
@@ -0,0 +1,3 @@
1"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
2crud_cash_discount_line,"CRUD cash discount",model_account_payment_term_cash_discount,account.group_account_manager,1,1,1,1
3r_cash_discount_line,"R cash discount",model_account_payment_term_cash_discount,account.group_account_user,1,0,0,0
04
=== added directory 'account_cash_discount/view'
=== added file 'account_cash_discount/view/account_invoice.xml'
--- account_cash_discount/view/account_invoice.xml 1970-01-01 00:00:00 +0000
+++ account_cash_discount/view/account_invoice.xml 2014-05-26 08:47:02 +0000
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="invoice_form" model="ir.ui.view">
5 <field name="model">account.invoice</field>
6 <field name="inherit_id" ref="account.invoice_form" />
7 <field name="arch" type="xml">
8 <data>
9 <field name="move_id" position="after">
10 <field name="cash_discount_move_id"
11 groups="account.group_account_user"
12 readonly="1"
13 attrs="{'invisible': [('cash_discount_move_id', '=', False)]}"/>
14 </field>
15 <field name="residual" position="after">
16 <div attrs="{'invisible': [('cash_discount_move_id', '=', False)]}" colspan="2">
17 Note: This invoice was paid with a cash discount as detailed in the cash discount correction move on the 'Other info' tab
18 </div>
19 </field>
20 </data>
21 </field>
22 </record>
23 </data>
24</openerp>
025
=== added file 'account_cash_discount/view/account_move_line_reconcile.xml'
--- account_cash_discount/view/account_move_line_reconcile.xml 1970-01-01 00:00:00 +0000
+++ account_cash_discount/view/account_move_line_reconcile.xml 2014-05-26 08:47:02 +0000
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<openerp>
3 <data>
4 <record id="view_account_move_line_reconcile_full" model="ir.ui.view">
5 <field name="model">account.move.line.reconcile</field>
6 <field name="inherit_id" ref="account.view_account_move_line_reconcile_full" />
7 <field name="arch" type="xml">
8 <data>
9 <footer position="before">
10 <group attrs="{'invisible': [('invoice_has_cash_discount', '=', False)]}">
11 <field name="invoice_id" readonly="1" />
12 <field name="invoice_has_cash_discount" invisible="1" />
13 </group>
14 </footer>
15 <button string="Reconcile" position="after">
16 <button string="Book difference as cash discount"
17 type="object"
18 name="reconcile_with_cash_discount"
19 attrs="{'invisible': [('invoice_has_cash_discount', '=', False)]}"
20 />
21 </button>
22 </data>
23 </field>
24 </record>
25 </data>
26</openerp>
027
=== added file 'account_cash_discount/view/account_payment_term.xml'
--- account_cash_discount/view/account_payment_term.xml 1970-01-01 00:00:00 +0000
+++ account_cash_discount/view/account_payment_term.xml 2014-05-26 08:47:02 +0000
@@ -0,0 +1,49 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="view_payment_term_disc_tree" model="ir.ui.view">
6 <field name="name">account.payment.term.disc.tree</field>
7 <field name="model">account.payment.term</field>
8 <field name="type">tree</field>
9 <field name="arch" type="xml">
10 <tree string="Payment Term">
11 <field name="name"/>
12 <field name="active"/>
13 </tree>
14 </field>
15 </record>
16
17
18 <record id="view_payment_term_disc_form" model="ir.ui.view">
19 <field name="name">account.payment.term.disc.form</field>
20 <field name="model">account.payment.term</field>
21 <field name="inherit_id" ref="account.view_payment_term_form"/>
22 <field name="type">form</field>
23 <field name="arch" type="xml">
24 <field name="line_ids" position="after">
25 <div class="oe_horizontal_separator oe_clear"><label for="cash_discount_ids" /></div>
26 <field name="cash_discount_ids">
27 <tree>
28 <field name="days" />
29 <field name="discount" />
30 <field name="allowed_deviation" />
31 </tree>
32 <form>
33 <group>
34 <field name="days" />
35 <field name="discount" />
36 <field name="allowed_deviation" />
37 </group>
38 <group>
39 <field name="discount_expense_account_id" />
40 <field name="discount_income_account_id" />
41 </group>
42 </form>
43 </field>
44 </field>
45 </field>
46 </record>
47 </data>
48</openerp>
49
050
=== added directory 'account_cash_discount/wizard'
=== added file 'account_cash_discount/wizard/__init__.py'
--- account_cash_discount/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/wizard/__init__.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,22 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 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##############################################################################
21import account_move_line_reconcile
22import account_move_line_reconcile
023
=== added file 'account_cash_discount/wizard/account_move_line_reconcile.py'
--- account_cash_discount/wizard/account_move_line_reconcile.py 1970-01-01 00:00:00 +0000
+++ account_cash_discount/wizard/account_move_line_reconcile.py 2014-05-26 08:47:02 +0000
@@ -0,0 +1,76 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# This module copyright (C) 2014 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##############################################################################
21from openerp.osv.orm import Model
22from openerp.osv import fields
23
24class AccountMoveLineReconcile(Model):
25 _inherit = 'account.move.line.reconcile'
26
27 _columns = {
28 'invoice_id': fields.many2one('account.invoice', 'Invoice'),
29 'invoice_has_cash_discount': fields.boolean('Cash discount on invoice')
30 }
31
32 def default_get(self, cr, uid, fields, context=None):
33 result = super(AccountMoveLineReconcile, self).default_get(
34 cr, uid, fields, context=context)
35 for move_line in self.pool.get('account.move.line').browse(
36 cr, uid, context.get('active_ids', []), context=context):
37 if move_line.invoice:
38 if move_line.invoice.id != result.get('invoice_id'):
39 result['invoice_id'] = move_line.invoice.id
40 result['invoice_has_cash_discount'] = bool(
41 move_line.invoice.get_matching_cash_discount(
42 result.get('credit')) or
43 move_line.invoice.get_matching_cash_discount(
44 result.get('debit')))
45 else:
46 result['invoice_id'] = False
47 result['invoice_has_cash_discount'] = False
48 return result
49
50 def reconcile_with_cash_discount(self, cr, uid, ids, context=None):
51 account_move_line = self.pool.get('account.move.line')
52 for this in self.browse(cr, uid, ids, context=context):
53 discount = this.invoice_id.get_matching_cash_discount(this.credit)\
54 or this.invoice_id.get_matching_cash_discount(this.debit)
55 if not discount:
56 continue
57 payment_move_lines = [
58 l for ml in account_move_line.browse(
59 cr, uid, context.get('active_ids', []),
60 context=context)
61 for l in ml.move_id.line_id
62 if not l.invoice]
63 correction_move_line_ids = this.invoice_id\
64 .create_cash_discount_move_lines(discount,
65 payment_move_lines)
66 reconcile_ids = context.get('active_ids', [])
67 for correction_move_line in account_move_line.browse(
68 cr, uid, correction_move_line_ids, context=context):
69 for payment_move_line in payment_move_lines:
70 if payment_move_line.account_id ==\
71 correction_move_line.account_id:
72 reconcile_ids.append(correction_move_line.id)
73 break
74 return self.trans_rec_reconcile_full(
75 cr, uid, [this.id], dict(context, active_ids=reconcile_ids))
76 pass

Subscribers

People subscribed via source and target branches