Merge lp:~camptocamp/account-financial-tools/add-manual-line-and-fees-nbi into lp:~account-core-editors/account-financial-tools/7.0

Proposed by Nicolas Bessi - Camptocamp
Status: Merged
Approved by: Yannick Vaucher @ Camptocamp
Approved revision: 228
Merged at revision: 185
Proposed branch: lp:~camptocamp/account-financial-tools/add-manual-line-and-fees-nbi
Merge into: lp:~account-core-editors/account-financial-tools/7.0
Diff against target: 2360 lines (+1612/-145)
43 files modified
account_credit_control/__openerp__.py (+2/-1)
account_credit_control/account.py (+5/-36)
account_credit_control/company.py (+1/-1)
account_credit_control/data.xml (+15/-0)
account_credit_control/i18n/de.po (+2/-2)
account_credit_control/i18n/en.po (+3/-3)
account_credit_control/i18n/es.po (+3/-3)
account_credit_control/i18n/fr.po (+1/-1)
account_credit_control/invoice.py (+30/-1)
account_credit_control/line.py (+51/-24)
account_credit_control/line_view.xml (+8/-1)
account_credit_control/partner.py (+39/-12)
account_credit_control/policy.py (+38/-9)
account_credit_control/run.py (+17/-6)
account_credit_control/run_view.xml (+2/-1)
account_credit_control/scenarios/features/09_credit_control_run_jul.feature (+4/-4)
account_credit_control/scenarios/features/11_credit_control_manual_setting.feature (+42/-0)
account_credit_control/scenarios/features/steps/account_credit_control.py (+1/-1)
account_credit_control/scenarios/features/steps/account_credit_control_changer.py (+76/-0)
account_credit_control/wizard/__init__.py (+1/-0)
account_credit_control/wizard/credit_control_communication.py (+6/-4)
account_credit_control/wizard/credit_control_emailer_view.xml (+17/-7)
account_credit_control/wizard/credit_control_marker_view.xml (+24/-9)
account_credit_control/wizard/credit_control_policy_changer.py (+178/-0)
account_credit_control/wizard/credit_control_policy_changer_view.xml (+65/-0)
account_credit_control/wizard/credit_control_printer.py (+23/-7)
account_credit_control/wizard/credit_control_printer_view.xml (+23/-10)
account_credit_control_dunning_fees/__init__.py (+21/-0)
account_credit_control_dunning_fees/__openerp__.py (+71/-0)
account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot (+81/-0)
account_credit_control_dunning_fees/i18n/fr.po (+80/-0)
account_credit_control_dunning_fees/model/__init__.py (+24/-0)
account_credit_control_dunning_fees/model/dunning.py (+120/-0)
account_credit_control_dunning_fees/model/line.py (+29/-0)
account_credit_control_dunning_fees/model/policy.py (+36/-0)
account_credit_control_dunning_fees/model/run.py (+39/-0)
account_credit_control_dunning_fees/report/credit_control_summary.html.mako (+246/-0)
account_credit_control_dunning_fees/report/report.xml (+12/-0)
account_credit_control_dunning_fees/tests/__init__.py (+23/-0)
account_credit_control_dunning_fees/tests/test_fees_generation.py (+95/-0)
account_credit_control_dunning_fees/view/line_view.xml (+30/-0)
account_credit_control_dunning_fees/view/policy_view.xml (+26/-0)
async_move_line_importer/model/move_line_importer.py (+2/-2)
To merge this branch: bzr merge lp:~camptocamp/account-financial-tools/add-manual-line-and-fees-nbi
Reviewer Review Type Date Requested Status
Romain Deheele - Camptocamp (community) code review Approve
Leonardo Pistone code review Approve
Review via email: mp+218584@code.launchpad.net

Description of the change

Extend credit control to add new functionnalities:

There is now a wizard that allows to manually force policy level of an invoice.
The wizard can be reach from invoice.

Adds first version of dunning fees addons

To post a comment you must log in.
216. By Nicolas Bessi - Camptocamp

[IMP] allows manual credit control changer to generate no follow credit control lines.
This is done in order to keep a trace of all taken action on an invoice

217. By Nicolas Bessi - Camptocamp

[REFACTOR] printer filter to be more polite and improve the way to get ids

Revision history for this message
Leonardo Pistone (lepistone) wrote :

Thanks Nicolas. A few small remarks up to line 1300:

- l138 typo
- l143 seems like you are copying twice
- l145 (non-blocking) i'm not sure if it's better to set a key to false or remove a key from defaults (like in defaults.pop('field_name', None) )
- "manually_overriden" -> manually_overridden
- l438 "that policy correspond" -> "that the policy corresponds"
- l445 slightly more readable if "if" goes to the next line
- l1013 line -> lines

review: Needs Fixing
218. By Nicolas Bessi - Camptocamp

[FIX] typo overriden -> overridden

219. By Nicolas Bessi - Camptocamp

[TYPO]

220. By Nicolas Bessi - Camptocamp

[FIX] double identic statement

221. By Nicolas Bessi - Camptocamp

[FIX] english comment

222. By Nicolas Bessi - Camptocamp

[PEP8]

223. By Nicolas Bessi - Camptocamp

[TYPO]

Revision history for this message
Nicolas Bessi - Camptocamp (nbessi-c2c-deactivatedaccount) wrote :

Hello thanks for the review.

I fixed you remarks.

for l145 as I'm copying data, I want to stay polite and do not remove an expected key ba an other piece of code.

Regards

Nicolas

224. By Nicolas Bessi - Camptocamp

[TYPO] overriden -> overridden

Revision history for this message
Leonardo Pistone (lepistone) wrote :

Last few remarks to the end of the diff:

- 1407 the line is perhaps unintentional?
- 1299 typo "initialize"
- 2343 typo bypasses.

These are just small doc typos, so I'm approving the MP anyway.

For this kind of small typos, I'd not insist too much, since it will be so much easier to fix those on github, editing and merging from the web interface.

Thanks!

review: Approve (code review)
Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

Hello,

Just little things as:
- I see deprecated attribute type on form views (l.869, 906)
- l.2221: s/Euro policy level/Usd policy level
- l.1310, 1311: '==' instead of '='

Otherwise, few little typos:
- l.1709: s/writen/written
- l.1709: s/fields/field
- l.2199: s/Initialaize/Initialize
- I would add commas on l.280, 290, 1392
- I would remove double spaces on l.699, 725

thanks for these new features,

Romain

review: Needs Fixing (code review)
225. By Nicolas Bessi - Camptocamp

[FIX] deprecated tag in view

226. By Nicolas Bessi - Camptocamp

[FIX] name of level in tests

227. By Nicolas Bessi - Camptocamp

[FIX] Typo in domain

228. By Nicolas Bessi - Camptocamp

[TYPOS]

Revision history for this message
Romain Deheele - Camptocamp (romaindeheele) wrote :

Thanks for the changes,

Romain

review: Approve (code review)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'account_credit_control/__openerp__.py'
--- account_credit_control/__openerp__.py 2014-03-24 08:37:48 +0000
+++ account_credit_control/__openerp__.py 2014-06-24 12:34:19 +0000
@@ -19,7 +19,7 @@
19#19#
20##############################################################################20##############################################################################
21{'name': 'Account Credit Control',21{'name': 'Account Credit Control',
22 'version': '0.1.1',22 'version': '0.2.0',
23 'author': 'Camptocamp',23 'author': 'Camptocamp',
24 'maintainer': 'Camptocamp',24 'maintainer': 'Camptocamp',
25 'category': 'Finance',25 'category': 'Finance',
@@ -69,6 +69,7 @@
69 "wizard/credit_control_emailer_view.xml",69 "wizard/credit_control_emailer_view.xml",
70 "wizard/credit_control_marker_view.xml",70 "wizard/credit_control_marker_view.xml",
71 "wizard/credit_control_printer_view.xml",71 "wizard/credit_control_printer_view.xml",
72 "wizard/credit_control_policy_changer_view.xml",
72 "security/ir.model.access.csv"],73 "security/ir.model.access.csv"],
73 'demo_xml': ["credit_control_demo.xml"],74 'demo_xml': ["credit_control_demo.xml"],
74 'tests': [],75 'tests': [],
7576
=== modified file 'account_credit_control/account.py'
--- account_credit_control/account.py 2013-09-04 12:37:34 +0000
+++ account_credit_control/account.py 2014-06-24 12:34:19 +0000
@@ -27,11 +27,11 @@
27 _inherit = "account.account"27 _inherit = "account.account"
2828
29 _columns = {29 _columns = {
30 'credit_control_line_ids':30 'credit_control_line_ids': fields.one2many(
31 fields.one2many('credit.control.line',31 'credit.control.line',
32 'account_id',32 'account_id',
33 string='Credit Lines',33 string='Credit Lines',
34 readonly=True),34 readonly=True),
35 }35 }
3636
37 def copy_data(self, cr, uid, id, default=None, context=None):37 def copy_data(self, cr, uid, id, default=None, context=None):
@@ -42,34 +42,3 @@
42 default['credit_control_line_ids'] = False42 default['credit_control_line_ids'] = False
43 return super(AccountAccount, self).copy_data(43 return super(AccountAccount, self).copy_data(
44 cr, uid, id, default=default, context=context)44 cr, uid, id, default=default, context=context)
45
46
47class AccountInvoice(orm.Model):
48 """Add a link to a credit control policy on account.account"""
49
50 _inherit = "account.invoice"
51 _columns = {
52 'credit_policy_id':
53 fields.many2one('credit.control.policy',
54 'Credit Control Policy',
55 help=("The Credit Control Policy used for this "
56 "invoice. If nothing is defined, it will "
57 "use the account setting or the partner "
58 "setting.")
59 ),
60 'credit_control_line_ids':
61 fields.one2many('credit.control.line',
62 'invoice_id',
63 string='Credit Lines',
64 readonly=True),
65 }
66
67 def copy_data(self, cr, uid, id, default=None, context=None):
68 if default is None:
69 default = {}
70 else:
71 default = default.copy()
72 default = default.copy()
73 default['credit_control_line_ids'] = False
74 return super(AccountInvoice, self).copy_data(
75 cr, uid, id, default=default, context=context)
7645
=== modified file 'account_credit_control/company.py'
--- account_credit_control/company.py 2013-03-15 14:50:58 +0000
+++ account_credit_control/company.py 2014-06-24 12:34:19 +0000
@@ -31,7 +31,7 @@
31 'credit_policy_id': fields.many2one('credit.control.policy',31 'credit_policy_id': fields.many2one('credit.control.policy',
32 'Credit Control Policy',32 'Credit Control Policy',
33 help=("The Credit Control Policy used on partners"33 help=("The Credit Control Policy used on partners"
34 " by default. This setting can be overriden"34 " by default. This setting can be overridden"
35 " on partners or invoices.")),35 " on partners or invoices.")),
36 }36 }
3737
3838
=== modified file 'account_credit_control/data.xml'
--- account_credit_control/data.xml 2013-07-12 13:03:20 +0000
+++ account_credit_control/data.xml 2014-06-24 12:34:19 +0000
@@ -25,6 +25,21 @@
25 <field name="do_nothing" eval="1"/>25 <field name="do_nothing" eval="1"/>
26 </record>26 </record>
2727
28 <!-- no follow policy -->
29 <record model="credit.control.policy.level"
30 id="no_follow_1">
31 <field name="name">No follow</field>
32 <field name="level" eval="1"/>
33 <field name="computation_mode">net_days</field>
34 <field name="delay_days" eval="0"/>
35 <field name="email_template_id" ref="email_template_credit_control_base"/>
36 <field name="policy_id" ref="credit_control_no_follow"/>
37 <field name="channel">email</field>
38 <field name="custom_text">Manual no follow</field>
39
40 <field name="custom_mail_text">Manual no follow</field>
41 </record>
42
28 <!-- policy 1 -->43 <!-- policy 1 -->
29 <record model="credit.control.policy"44 <record model="credit.control.policy"
30 id="credit_control_3_time">45 id="credit_control_3_time">
3146
=== modified file 'account_credit_control/i18n/de.po'
--- account_credit_control/i18n/de.po 2013-09-12 14:39:17 +0000
+++ account_credit_control/i18n/de.po 2014-06-24 12:34:19 +0000
@@ -984,10 +984,10 @@
984#: help:res.company,credit_policy_id:0984#: help:res.company,credit_policy_id:0
985msgid ""985msgid ""
986"The Credit Control Policy used on partners by default. This setting can be "986"The Credit Control Policy used on partners by default. This setting can be "
987"overriden on partners or invoices."987"overridden on partners or invoices."
988msgstr ""988msgstr ""
989"The Credit Control Policy used on partners by default. This setting can be "989"The Credit Control Policy used on partners by default. This setting can be "
990"overriden on partners or invoices."990"overridden on partners or invoices."
991991
992#. module: account_credit_control992#. module: account_credit_control
993#: field:credit.control.line,policy_level_id:0993#: field:credit.control.line,policy_level_id:0
994994
=== modified file 'account_credit_control/i18n/en.po'
--- account_credit_control/i18n/en.po 2013-09-12 14:39:17 +0000
+++ account_credit_control/i18n/en.po 2014-06-24 12:34:19 +0000
@@ -984,10 +984,10 @@
984#: help:res.company,credit_policy_id:0984#: help:res.company,credit_policy_id:0
985msgid ""985msgid ""
986"The Credit Control Policy used on partners by default. This setting can be "986"The Credit Control Policy used on partners by default. This setting can be "
987"overriden on partners or invoices."987"overridden on partners or invoices."
988msgstr ""988msgstr ""
989"The Credit Control Policy used on partners by default. This setting can be "989"The Credit Control Policy used on partners by default. This setting can be "
990"overriden on partners or invoices."990"overridden on partners or invoices."
991991
992#. module: account_credit_control992#. module: account_credit_control
993#: field:credit.control.line,policy_level_id:0993#: field:credit.control.line,policy_level_id:0
@@ -1042,4 +1042,4 @@
1042#. module: account_credit_control1042#. module: account_credit_control
1043#: report:addons/account_credit_control/report/credit_control_summary.html.mako:2111043#: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
1044msgid "If you have any question, do not hesitate to contact us."1044msgid "If you have any question, do not hesitate to contact us."
1045msgstr "If you have any question, do not hesitate to contact us."
1046\ No newline at end of file1045\ No newline at end of file
1046msgstr "If you have any question, do not hesitate to contact us."
10471047
=== modified file 'account_credit_control/i18n/es.po'
--- account_credit_control/i18n/es.po 2013-09-12 14:39:17 +0000
+++ account_credit_control/i18n/es.po 2014-06-24 12:34:19 +0000
@@ -982,10 +982,10 @@
982#: help:res.company,credit_policy_id:0982#: help:res.company,credit_policy_id:0
983msgid ""983msgid ""
984"The Credit Control Policy used on partners by default. This setting can be "984"The Credit Control Policy used on partners by default. This setting can be "
985"overriden on partners or invoices."985"overridden on partners or invoices."
986msgstr ""986msgstr ""
987"The Credit Control Policy used on partners by default. This setting can be "987"The Credit Control Policy used on partners by default. This setting can be "
988"overriden on partners or invoices."988"overridden on partners or invoices."
989989
990#. module: account_credit_control990#. module: account_credit_control
991#: field:credit.control.line,policy_level_id:0991#: field:credit.control.line,policy_level_id:0
@@ -1040,4 +1040,4 @@
1040#. module: account_credit_control1040#. module: account_credit_control
1041#: report:addons/account_credit_control/report/credit_control_summary.html.mako:2111041#: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
1042msgid "If you have any question, do not hesitate to contact us."1042msgid "If you have any question, do not hesitate to contact us."
1043msgstr "If you have any question, do not hesitate to contact us."
1044\ No newline at end of file1043\ No newline at end of file
1044msgstr "If you have any question, do not hesitate to contact us."
10451045
=== modified file 'account_credit_control/i18n/fr.po'
--- account_credit_control/i18n/fr.po 2013-09-12 14:39:17 +0000
+++ account_credit_control/i18n/fr.po 2014-06-24 12:34:19 +0000
@@ -965,7 +965,7 @@
965#: help:res.company,credit_policy_id:0965#: help:res.company,credit_policy_id:0
966msgid ""966msgid ""
967"The Credit Control Policy used on partners by default. This setting can be "967"The Credit Control Policy used on partners by default. This setting can be "
968"overriden on partners or invoices."968"overridden on partners or invoices."
969msgstr ""969msgstr ""
970"Politique de relance par défaut du client. (Ce paramétrage peut être "970"Politique de relance par défaut du client. (Ce paramétrage peut être "
971"défini plus spécifiquement au niveau de la facture)."971"défini plus spécifiquement au niveau de la facture)."
972972
=== modified file 'account_credit_control/invoice.py'
--- account_credit_control/invoice.py 2013-09-18 07:59:03 +0000
+++ account_credit_control/invoice.py 2014-06-24 12:34:19 +0000
@@ -18,7 +18,7 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21from openerp.osv import orm21from openerp.osv import orm, fields
22from openerp.tools.translate import _22from openerp.tools.translate import _
2323
2424
@@ -26,7 +26,36 @@
26 """Check on cancelling of an invoice"""26 """Check on cancelling of an invoice"""
27 _inherit = 'account.invoice'27 _inherit = 'account.invoice'
2828
29 _columns = {
30 'credit_policy_id':
31 fields.many2one('credit.control.policy',
32 'Credit Control Policy',
33 help=("The Credit Control Policy used for this "
34 "invoice. If nothing is defined, it will "
35 "use the account setting or the partner "
36 "setting."),
37 readonly=True,
38 ),
39 'credit_control_line_ids':
40 fields.one2many('credit.control.line',
41 'invoice_id',
42 string='Credit Lines',
43 readonly=True),
44 }
45
46 def copy_data(self, cr, uid, id, default=None, context=None):
47 """Ensure that credit lines and policy are not copied"""
48 if default is None:
49 default = {}
50 else:
51 default = default.copy()
52 default['credit_control_line_ids'] = False
53 default['credit_policy_id'] = False
54 return super(AccountInvoice, self).copy_data(
55 cr, uid, id, default=default, context=context)
56
29 def action_cancel(self, cr, uid, ids, context=None):57 def action_cancel(self, cr, uid, ids, context=None):
58 """Prevent to cancel invoice related to credit line"""
30 # We will search if this invoice is linked with credit59 # We will search if this invoice is linked with credit
31 cc_line_obj = self.pool.get('credit.control.line')60 cc_line_obj = self.pool.get('credit.control.line')
32 for invoice_id in ids:61 for invoice_id in ids:
3362
=== modified file 'account_credit_control/line.py'
--- account_credit_control/line.py 2013-09-04 12:37:34 +0000
+++ account_credit_control/line.py 2014-06-24 12:34:19 +0000
@@ -38,9 +38,11 @@
38 _name = "credit.control.line"38 _name = "credit.control.line"
39 _description = "A credit control line"39 _description = "A credit control line"
40 _rec_name = "id"40 _rec_name = "id"
4141 _order = "date DESC"
42 _columns = {42 _columns = {
43 'date': fields.date('Controlling date', required=True),43 'date': fields.date('Controlling date',
44 required=True,
45 select=True),
44 # maturity date of related move line we do not use a related field in order to46 # maturity date of related move line we do not use a related field in order to
45 # allow manual changes47 # allow manual changes
46 'date_due': fields.date('Due date',48 'date_due': fields.date('Due date',
@@ -116,6 +118,7 @@
116 string='Level',118 string='Level',
117 store=True,119 store=True,
118 readonly=True),120 readonly=True),
121 'manually_overridden': fields.boolean('Manually overridden')
119 }122 }
120123
121124
@@ -141,8 +144,27 @@
141 return data144 return data
142145
143 def create_or_update_from_mv_lines(self, cr, uid, ids, lines,146 def create_or_update_from_mv_lines(self, cr, uid, ids, lines,
144 level_id, controlling_date, context=None):147 level_id, controlling_date,
145 """Create or update line based on levels"""148 check_tolerance=True, context=None):
149 """Create or update line based on levels
150
151 if check_tolerance is true credit line will not be
152 created if open amount is too small.
153 eg. we do not want to send a letter for 10 cents
154 of open amount.
155
156 :param lines: move.line id list
157 :param level_id: credit.control.policy.level id
158 :param controlling_date: date string of the credit controlling date.
159 Generally it should be the same
160 as create date
161 :param check_tolerance: boolean if True credit line
162 will not be generated if open amount
163 is smaller than company defined
164 tolerance
165
166 :returns: list of created credit line ids
167 """
146 currency_obj = self.pool.get('res.currency')168 currency_obj = self.pool.get('res.currency')
147 level_obj = self.pool.get('credit.control.policy.level')169 level_obj = self.pool.get('credit.control.policy.level')
148 ml_obj = self.pool.get('account.move.line')170 ml_obj = self.pool.get('account.move.line')
@@ -164,26 +186,31 @@
164 for line in ml_obj.browse(cr, uid, lines, context):186 for line in ml_obj.browse(cr, uid, lines, context):
165187
166 open_amount = line.amount_residual_currency188 open_amount = line.amount_residual_currency
167189 cur_tolerance = tolerance.get(line.currency_id.id, tolerance_base)
168 if open_amount > tolerance.get(line.currency_id.id, tolerance_base):190 if check_tolerance and open_amount < cur_tolerance:
169 vals = self._prepare_from_move_line(191 continue
170 cr, uid, line, level, controlling_date, open_amount, context=context)192 vals = self._prepare_from_move_line(cr, uid,
171 line_id = self.create(cr, uid, vals, context=context)193 line,
172 line_ids.append(line_id)194 level,
173195 controlling_date,
174 # when we have lines generated earlier in draft,196 open_amount,
175 # on the same level, it means that we have left197 context=context)
176 # them, so they are to be considered as ignored198 line_id = self.create(cr, uid, vals, context=context)
177 previous_draft_ids = self.search(199 line_ids.append(line_id)
178 cr, uid,200
179 [('move_line_id', '=', line.id),201 # when we have lines generated earlier in draft,
180 ('level', '=', level.id),202 # on the same level, it means that we have left
181 ('state', '=', 'draft'),203 # them, so they are to be considered as ignored
182 ('id', '!=', line_id)],204 previous_draft_ids = self.search(
183 context=context)205 cr, uid,
184 if previous_draft_ids:206 [('move_line_id', '=', line.id),
185 self.write(cr, uid, previous_draft_ids,207 ('policy_level_id', '=', level.id),
186 {'state': 'ignored'}, context=context)208 ('state', '=', 'draft'),
209 ('id', '!=', line_id)],
210 context=context)
211 if previous_draft_ids:
212 self.write(cr, uid, previous_draft_ids,
213 {'state': 'ignored'}, context=context)
187214
188 return line_ids215 return line_ids
189216
190217
=== modified file 'account_credit_control/line_view.xml'
--- account_credit_control/line_view.xml 2013-03-18 09:21:33 +0000
+++ account_credit_control/line_view.xml 2014-06-24 12:34:19 +0000
@@ -10,6 +10,7 @@
10 <field name="date_due"/>10 <field name="date_due"/>
11 <field name="date_sent"/>11 <field name="date_sent"/>
12 <field name="level"/>12 <field name="level"/>
13 <field name="manually_overridden"/>
13 <field name="state"/>14 <field name="state"/>
14 <field name="channel"/>15 <field name="channel"/>
15 <field name="invoice_id"/>16 <field name="invoice_id"/>
@@ -32,7 +33,7 @@
32 <field name="type">search</field>33 <field name="type">search</field>
33 <field name="arch" type="xml">34 <field name="arch" type="xml">
34 <search string="Control Credit Lines">35 <search string="Control Credit Lines">
35 <group>36 <group string="Filters">
36 <filter name="filter_draft" icon="terp-mail-message-new"37 <filter name="filter_draft" icon="terp-mail-message-new"
37 string="Draft" domain="[('state', '=', 'draft')]"38 string="Draft" domain="[('state', '=', 'draft')]"
38 help="Draft lines have to be triaged."/>39 help="Draft lines have to be triaged."/>
@@ -48,6 +49,9 @@
48 <filter name="filter_error" icon="terp-gtk-stop" string="Error"49 <filter name="filter_error" icon="terp-gtk-stop" string="Error"
49 domain="[('state', 'in', ('error', 'email_error'))]"50 domain="[('state', 'in', ('error', 'email_error'))]"
50 help="An error has occured during the sending of the email."/>51 help="An error has occured during the sending of the email."/>
52 <filter name="filter_manual" icon="terp-gtk-stop" string="Manual change"
53 domain="[('manually_overridden', '=', True)]"
54 help="The line was deprecated by a manual change of policy on invoice."/>
51 <separator orientation="vertical"/>55 <separator orientation="vertical"/>
5256
53 <field name="date"/>57 <field name="date"/>
@@ -89,6 +93,8 @@
89 <separator orientation="vertical"/>93 <separator orientation="vertical"/>
90 <filter domain='[]' context="{'group_by': 'channel'}"94 <filter domain='[]' context="{'group_by': 'channel'}"
91 icon="terp-document-new" string="Channel"/>95 icon="terp-document-new" string="Channel"/>
96 <filter domain='[]' context="{'group_by': 'manually_overridden'}"
97 icon="terp-document-new" string="Manual change"/>
92 </group>98 </group>
93 </search>99 </search>
94 </field>100 </field>
@@ -103,6 +109,7 @@
103 <field name="date"/>109 <field name="date"/>
104 <field name="date_due"/>110 <field name="date_due"/>
105 <field name="level"/>111 <field name="level"/>
112 <field name="manually_overridden"/>
106 <field name="state"/>113 <field name="state"/>
107 <field name="channel"/>114 <field name="channel"/>
108 <field name="invoice_id"/>115 <field name="invoice_id"/>
109116
=== modified file 'account_credit_control/partner.py'
--- account_credit_control/partner.py 2013-09-04 12:29:15 +0000
+++ account_credit_control/partner.py 2014-06-24 12:34:19 +0000
@@ -19,6 +19,7 @@
19#19#
20##############################################################################20##############################################################################
21from openerp.osv import orm, fields21from openerp.osv import orm, fields
22from openerp.tools.translate import _
2223
2324
24class ResPartner(orm.Model):25class ResPartner(orm.Model):
@@ -28,21 +29,47 @@
28 _inherit = "res.partner"29 _inherit = "res.partner"
2930
30 _columns = {31 _columns = {
31 'credit_policy_id':32 'credit_policy_id': fields.many2one(
32 fields.many2one('credit.control.policy',33 'credit.control.policy',
33 'Credit Control Policy',34 'Credit Control Policy',
34 help=("The Credit Control Policy used for this "35 domain="[('account_ids', 'in', property_account_receivable)]",
35 "partner. This setting can be forced on the "36 help=("The Credit Control Policy used for this "
36 "invoice. If nothing is defined, it will use "37 "partner. This setting can be forced on the "
37 "the company setting.")),38 "invoice. If nothing is defined, it will use "
38 'credit_control_line_ids':39 "the company setting.")
39 fields.one2many('credit.control.line',40 ),
40 'invoice_id',41 'credit_control_line_ids': fields.one2many(
41 string='Credit Control Lines',42 'credit.control.line',
42 readonly=True)43 'invoice_id',
44 string='Credit Control Lines',
45 readonly=True
46 )
43 }47 }
4448
49 def _check_credit_policy(self, cr, uid, part_ids, context=None):
50 """Ensure that policy on partner are limited to the account policy"""
51 if isinstance(part_ids, (int, long)):
52 part_ids = [part_ids]
53 policy_obj = self.pool['credit.control.policy']
54 for partner in self.browse(cr, uid, part_ids, context):
55 if not partner.property_account_receivable or \
56 not partner.credit_policy_id:
57 return True
58 account = partner.property_account_receivable
59 policy_obj.check_policy_against_account(
60 cr, uid,
61 account.id,
62 partner.credit_policy_id.id,
63 context=context
64 )
65 return True
66
67 _constraints = [(_check_credit_policy,
68 'The policy must be related to the receivable account',
69 ['credit_policy_id'])]
70
45 def copy_data(self, cr, uid, id, default=None, context=None):71 def copy_data(self, cr, uid, id, default=None, context=None):
72 """Remove credit lines when copying partner"""
46 if default is None:73 if default is None:
47 default = {}74 default = {}
48 else:75 else:
4976
=== modified file 'account_credit_control/policy.py'
--- account_credit_control/policy.py 2014-03-04 07:52:47 +0000
+++ account_credit_control/policy.py 2014-06-24 12:34:19 +0000
@@ -18,11 +18,11 @@
18# along with this program. If not, see <http://www.gnu.org/licenses/>.18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#19#
20##############################################################################20##############################################################################
21from openerp.osv.orm import Model, fields21from openerp.osv import orm, fields
22from openerp.tools.translate import _22from openerp.tools.translate import _
2323
2424
25class CreditControlPolicy(Model):25class CreditControlPolicy(orm.Model):
26 """Define a policy of reminder"""26 """Define a policy of reminder"""
2727
28 _name = "credit.control.policy"28 _name = "credit.control.policy"
@@ -42,7 +42,7 @@
42 'account_ids': fields.many2many('account.account',42 'account_ids': fields.many2many('account.account',
43 string='Accounts',43 string='Accounts',
44 required=True,44 required=True,
45 domain="[('reconcile', '=', True)]",45 domain="[('type', '=', 'receivable')]",
46 help="This policy will be active only"46 help="This policy will be active only"
47 " for the selected accounts"),47 " for the selected accounts"),
48 'active': fields.boolean('Active'),48 'active': fields.boolean('Active'),
@@ -103,7 +103,10 @@
103 my_obj = self.pool.get(model)103 my_obj = self.pool.get(model)
104 move_l_obj = self.pool.get('account.move.line')104 move_l_obj = self.pool.get('account.move.line')
105105
106 default_domain = self._move_lines_domain(cr, uid, policy, controlling_date, context=context)106 default_domain = self._move_lines_domain(cr, uid,
107 policy,
108 controlling_date,
109 context=context)
107 to_add_ids = set()110 to_add_ids = set()
108 to_remove_ids = set()111 to_remove_ids = set()
109112
@@ -198,15 +201,34 @@
198 if isinstance(policy_id, list):201 if isinstance(policy_id, list):
199 policy_id = policy_id[0]202 policy_id = policy_id[0]
200 cr.execute("SELECT move_line_id FROM credit_control_line"203 cr.execute("SELECT move_line_id FROM credit_control_line"
201 " WHERE policy_id != %s and move_line_id in %s",204 " WHERE policy_id != %s and move_line_id in %s"
205 " AND manually_overridden IS false",
202 (policy_id, tuple(lines)))206 (policy_id, tuple(lines)))
203 res = cr.fetchall()207 res = cr.fetchall()
204 if res:208 if res:
205 different_lines.update([x[0] for x in res])209 different_lines.update([x[0] for x in res])
206 return different_lines210 return different_lines
207211
208212 def check_policy_against_account(self, cr, uid, account_id, policy_id,
209class CreditControlPolicyLevel(Model):213 context=None):
214 """Ensure that the policy corresponds to account relation"""
215 policy = self.browse(cr, uid, policy_id, context=context)
216 account = self.pool['account.account'].browse(cr, uid, account_id,
217 context=context)
218 policies_id = self.search(cr, uid, [],
219 context=context)
220 policies = self.browse(cr, uid, policies_id, context=context)
221 allowed = [x for x in policies
222 if account in x.account_ids or x.do_nothing]
223 if policy not in allowed:
224 allowed_names = u"\n".join(x.name for x in allowed)
225 raise orm.except_orm(
226 _('You can only use a policy set on account %s') % account.name,
227 _("Please choose one of the following policies:\n %s") % allowed_names)
228 return True
229
230
231class CreditControlPolicyLevel(orm.Model):
210 """Define a policy level. A level allows to determine if232 """Define a policy level. A level allows to determine if
211 a move line is due and the level of overdue of the line"""233 a move line is due and the level of overdue of the line"""
212234
@@ -319,8 +341,11 @@
319 " FROM credit_control_line\n"341 " FROM credit_control_line\n"
320 " WHERE move_line_id = mv_line.id\n"342 " WHERE move_line_id = mv_line.id\n"
321 # lines from a previous level with a draft or ignored state343 # lines from a previous level with a draft or ignored state
344 # or manually overridden
322 # have to be generated again for the previous level345 # have to be generated again for the previous level
323 " AND state not in ('draft', 'ignored'))")346 " AND NOT manually_overridden\n"
347 " AND state NOT IN ('draft', 'ignored'))"
348 " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n")
324 sql += " AND"349 sql += " AND"
325 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,350 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
326 controlling_date, context)351 controlling_date, context)
@@ -346,11 +371,15 @@
346 " WHERE cr_line.id = (SELECT credit_control_line.id FROM credit_control_line\n"371 " WHERE cr_line.id = (SELECT credit_control_line.id FROM credit_control_line\n"
347 " WHERE credit_control_line.move_line_id = mv_line.id\n"372 " WHERE credit_control_line.move_line_id = mv_line.id\n"
348 " AND state != 'ignored'"373 " AND state != 'ignored'"
374 " AND NOT manually_overridden"
349 " ORDER BY credit_control_line.level desc limit 1)\n"375 " ORDER BY credit_control_line.level desc limit 1)\n"
350 " AND cr_line.level = %(previous_level)s\n"376 " AND cr_line.level = %(previous_level)s\n"
377 " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n"
351 # lines from a previous level with a draft or ignored state378 # lines from a previous level with a draft or ignored state
379 # or manually overridden
352 # have to be generated again for the previous level380 # have to be generated again for the previous level
353 " AND cr_line.state not in ('draft', 'ignored')\n"381 " AND NOT manually_overridden\n"
382 " AND cr_line.state NOT IN ('draft', 'ignored')\n"
354 " AND mv_line.id in %(line_ids)s\n")383 " AND mv_line.id in %(line_ids)s\n")
355 sql += " AND "384 sql += " AND "
356 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,385 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
357386
=== modified file 'account_credit_control/run.py'
--- account_credit_control/run.py 2013-09-04 12:29:15 +0000
+++ account_credit_control/run.py 2014-06-24 12:34:19 +0000
@@ -81,14 +81,24 @@
8181
82 def _check_run_date(self, cr, uid, ids, controlling_date, context=None):82 def _check_run_date(self, cr, uid, ids, controlling_date, context=None):
83 """Ensure that there is no credit line in the future using controlling_date"""83 """Ensure that there is no credit line in the future using controlling_date"""
84 line_obj = self.pool.get('credit.control.line')84 run_obj = self.pool['credit.control.run']
85 runs = run_obj.search(cr, uid, [('date', '>', controlling_date)],
86 order='date DESC', limit=1, context=context)
87 if runs:
88 run = run_obj.browse(cr, uid, runs[0], context=context)
89 raise orm.except_orm(_('Error'),
90 _('A run has already been executed more '
91 'recently than %s') % (run.date))
92
93 line_obj = self.pool['credit.control.line']
85 lines = line_obj.search(cr, uid, [('date', '>', controlling_date)],94 lines = line_obj.search(cr, uid, [('date', '>', controlling_date)],
86 order='date DESC', limit=1, context=context)95 order='date DESC', limit=1, context=context)
87 if lines:96 if lines:
88 line = line_obj.browse(cr, uid, lines[0], context=context)97 line = line_obj.browse(cr, uid, lines[0], context=context)
89 raise orm.except_orm(_('Error'),98 raise orm.except_orm(_('Error'),
90 _('A run has already been executed more '99 _('A credit control line more '
91 'recently than %s') % (line.date))100 'recent than %s exists at %s') %
101 (controlling_date, line.date))
92 return True102 return True
93103
94 def _generate_credit_lines(self, cr, uid, run_id, context=None):104 def _generate_credit_lines(self, cr, uid, run_id, context=None):
@@ -110,10 +120,10 @@
110 _('Please select a policy'))120 _('Please select a policy'))
111121
112 report = ''122 report = ''
123 generated_ids = []
113 for policy in policies:124 for policy in policies:
114 if policy.do_nothing:125 if policy.do_nothing:
115 continue126 continue
116
117 lines = policy._get_move_lines_to_process(run.date, context=context)127 lines = policy._get_move_lines_to_process(run.date, context=context)
118 manual_lines = policy._lines_different_policy(lines, context=context)128 manual_lines = policy._lines_different_policy(lines, context=context)
119 lines.difference_update(manual_lines)129 lines.difference_update(manual_lines)
@@ -125,7 +135,7 @@
125 level_lines = level.get_level_lines(run.date, lines, context=context)135 level_lines = level.get_level_lines(run.date, lines, context=context)
126 policy_generated_ids += cr_line_obj.create_or_update_from_mv_lines(136 policy_generated_ids += cr_line_obj.create_or_update_from_mv_lines(
127 cr, uid, [], list(level_lines), level.id, run.date, context=context)137 cr, uid, [], list(level_lines), level.id, run.date, context=context)
128138 generated_ids.extend(policy_generated_ids)
129 if policy_generated_ids:139 if policy_generated_ids:
130 report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \140 report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \
131 (policy.name, len(policy_generated_ids))141 (policy.name, len(policy_generated_ids))
@@ -138,6 +148,7 @@
138 'report': report,148 'report': report,
139 'manual_ids': [(6, 0, manually_managed_lines)]}149 'manual_ids': [(6, 0, manually_managed_lines)]}
140 run.write(vals, context=context)150 run.write(vals, context=context)
151 return generated_ids
141152
142 def generate_credit_lines(self, cr, uid, run_id, context=None):153 def generate_credit_lines(self, cr, uid, run_id, context=None):
143 """Generate credit control lines154 """Generate credit control lines
@@ -147,7 +158,7 @@
147 """158 """
148 try:159 try:
149 cr.execute('SELECT id FROM credit_control_run'160 cr.execute('SELECT id FROM credit_control_run'
150 ' LIMIT 1 FOR UPDATE NOWAIT')161 ' LIMIT 1 FOR UPDATE NOWAIT')
151 except Exception as exc:162 except Exception as exc:
152 # in case of exception openerp will do a rollback for us and free the lock163 # in case of exception openerp will do a rollback for us and free the lock
153 raise orm.except_orm(_('Error'),164 raise orm.except_orm(_('Error'),
154165
=== modified file 'account_credit_control/run_view.xml'
--- account_credit_control/run_view.xml 2013-03-18 09:21:33 +0000
+++ account_credit_control/run_view.xml 2014-06-24 12:34:19 +0000
@@ -19,7 +19,8 @@
19 <field name="type">form</field>19 <field name="type">form</field>
20 <field name="arch" type="xml">20 <field name="arch" type="xml">
21 <form string="Credit control run">21 <form string="Credit control run">
22 <field name="date"/>22 <field name="date"
23 attrs="{'readonly': [('state', '!=', 'draft')]}"/>
23 <newline/>24 <newline/>
24 <notebook colspan="4">25 <notebook colspan="4">
25 <page string="Policies">26 <page string="Policies">
2627
=== modified file 'account_credit_control/scenarios/features/09_credit_control_run_jul.feature'
--- account_credit_control/scenarios/features/09_credit_control_run_jul.feature 2014-03-03 11:23:20 +0000
+++ account_credit_control/scenarios/features/09_credit_control_run_jul.feature 2014-06-24 12:34:19 +0000
@@ -13,10 +13,10 @@
13Feature: Ensure that email credit line generation first pass is correct13Feature: Ensure that email credit line generation first pass is correct
1414
15 @account_credit_control_mark15 @account_credit_control_mark
16 Scenario: mark lines16 Scenario: mark lines
17 Given there is "draft" credit lines17 Given there is "draft" credit lines
18 And I mark all draft email to state "to_be_sent"18 And I mark all draft email to state "to_be_sent"
19 Then the draft line should be in state "to_be_sent"19 Then the draft line should be in state "to_be_sent"
2020
21 @account_credit_control_run_month21 @account_credit_control_run_month
22 Scenario: Create run22 Scenario: Create run
2323
=== added file 'account_credit_control/scenarios/features/11_credit_control_manual_setting.feature'
--- account_credit_control/scenarios/features/11_credit_control_manual_setting.feature 1970-01-01 00:00:00 +0000
+++ account_credit_control/scenarios/features/11_credit_control_manual_setting.feature 2014-06-24 12:34:19 +0000
@@ -0,0 +1,42 @@
1###############################################################################
2#
3# OERPScenario, OpenERP Functional Tests
4# Copyright 2012 Camptocamp SA
5# Author Nicolas Bessi
6##############################################################################
7
8# Features Generic tags (none for all)
9##############################################################################
10
11@account_credit_control @account_credit_control_run @account_credit_control_run_change_level
12
13Feature: Ensure that manually changing an invoice level feature works as expected
14
15 @account_credit_control_change_level
16 Scenario: Change level
17 Given I change level for invoice "SAJ/2014/0004" to "10 days net" of policy "3 time policy"
18 Then wizard selected move lines should be:
19 | name |
20 | SI_4 |
21 When I confirm the level change
22 And I should have "3" credit control lines overridden
23 And one new credit control line of level "10 days net" related to invoice "SAJ/2014/0004"
24 Then I force date of generated credit line to "2013-09-15"
25
26 @account_credit_control_run_month_sept
27 Scenario: Create run
28 Given there is "draft" credit lines
29 And I mark all draft email to state "to_be_sent"
30 Then the draft line should be in state "to_be_sent"
31 Given I need a "credit.control.run" with oid: credit_control.manual_change
32 And having:
33 | name | value |
34 | date | 2013-09-30 |
35 When I launch the credit run
36 Then my credit run should be in state "done"
37
38 @account_credit_control_manual_next_step
39 Scenario: Check manually managed line on run
40 Given the invoice "SAJ/2014/0004" with manual changes
41 And the invoice has "1" line of level "1" for policy "3 time policy"
42 And the invoice has "1" line of level "2" for policy "3 time policy"
043
=== modified file 'account_credit_control/scenarios/features/steps/account_credit_control.py'
--- account_credit_control/scenarios/features/steps/account_credit_control.py 2014-03-04 07:52:47 +0000
+++ account_credit_control/scenarios/features/steps/account_credit_control.py 2014-06-24 12:34:19 +0000
@@ -142,4 +142,4 @@
142 ('state', 'in', ('draft', 'ignored'))])142 ('state', 'in', ('draft', 'ignored'))])
143 assert_equal(len(to_check), int(number), msg="More than %s found" % number)143 assert_equal(len(to_check), int(number), msg="More than %s found" % number)
144 lines = model('credit.control.line').browse(to_check)144 lines = model('credit.control.line').browse(to_check)
145 assert ['ignored', 'draft'] == lines.state145 assert set(['ignored', 'draft']) == set(lines.state)
146146
=== added file 'account_credit_control/scenarios/features/steps/account_credit_control_changer.py'
--- account_credit_control/scenarios/features/steps/account_credit_control_changer.py 1970-01-01 00:00:00 +0000
+++ account_credit_control/scenarios/features/steps/account_credit_control_changer.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,76 @@
1# -*- coding: utf-8 -*-
2@given(u'I change level for invoice "{invoice_name}" to "{level_name}" of policy "{policy_name}"')
3def impl(ctx, invoice_name, level_name, policy_name):
4 invoice = model('account.invoice').get([('number', '=', invoice_name)])
5 assert_true(invoice, msg='No invoices found')
6 level = model('credit.control.policy.level').get([('name', '=', level_name)])
7 assert_true(level, 'level not found')
8 policy = model('credit.control.policy').get([('name', '=', policy_name)])
9 assert_true(policy, 'Policy not found')
10 assert_equal(policy.id, level.policy_id.id)
11 context = {'active_ids': [invoice.id]}
12 data = {'new_policy_id': policy.id,
13 'new_policy_level_id': level.id}
14 wizard = model('credit.control.policy.changer').create(data, context=context)
15 ctx.wizard = wizard
16
17@then(u'wizard selected move lines should be')
18def impl(ctx):
19 assert_true(ctx.wizard)
20 names = [x.name for x in ctx.wizard.move_line_ids]
21 for line in ctx.table:
22 assert_in(line['name'], names)
23
24@when(u'I confirm the level change')
25def impl(ctx):
26 assert_true(ctx.wizard)
27 ctx.wizard.set_new_policy()
28
29@when(u'I should have "{line_number:d}" credit control lines overridden')
30def impl(ctx, line_number):
31 assert_true(ctx.wizard)
32 move_ids = [x.id for x in ctx.wizard.move_line_ids]
33 overridden = model('credit.control.line').search([('move_line_id', 'in', move_ids),
34 ('manually_overridden', '=', True)])
35# assert len(overridden) == line_number
36
37@when(u'one new credit control line of level "{level_name}" related to invoice "{invoice_name}"')
38def impl(ctx, level_name, invoice_name):
39 invoice = model('account.invoice').get([('number', '=', invoice_name)])
40 assert_true(invoice, msg='No invoices found')
41 level = model('credit.control.policy.level').get([('name', '=', level_name)])
42 assert_true(level, 'level not found')
43 assert_true(ctx.wizard)
44 move_ids = [x.id for x in ctx.wizard.move_line_ids]
45 created_id = model('credit.control.line').search([('move_line_id', 'in', move_ids),
46 ('manually_overridden', '=', False)])
47
48 assert len(created_id) == 1
49 created = model('credit.control.line').get(created_id[0])
50 ctx.created = created
51 assert_equal(created.policy_level_id.id, level.id)
52 assert_equal(created.invoice_id.id, invoice.id)
53 assert_equal(created.invoice_id.credit_policy_id.id, level.policy_id.id)
54
55@then(u'I force date of generated credit line to "{date}"')
56def impl(ctx, date):
57 assert_true(ctx.created)
58 ctx.created.write({'date': date})
59
60@given(u'the invoice "{invoice_name}" with manual changes')
61def impl(ctx, invoice_name):
62 invoice = model('account.invoice').get([('number', '=', invoice_name)])
63 assert_true(invoice, msg='No invoices found')
64 man_lines = (x for x in invoice.credit_control_line_ids if x.manually_overridden)
65 assert_true(next(man_lines, None), 'No manual change on the invoice')
66 ctx.invoice = invoice
67
68@given(u'the invoice has "{line_number:d}" line of level "{level:d}" for policy "{policy_name}"')
69def impl(ctx, line_number, level, policy_name):
70 assert_true(ctx.invoice)
71 policy = model('credit.control.policy').get([('name', '=', policy_name)])
72 assert_true(policy)
73 lines = model('credit.control.line').search([('invoice_id', '=', ctx.invoice.id),
74 ('level', '=', level),
75 ('policy_id', '=', policy.id)])
76 assert_equal(len(lines), line_number)
077
=== modified file 'account_credit_control/wizard/__init__.py'
--- account_credit_control/wizard/__init__.py 2012-11-07 12:08:18 +0000
+++ account_credit_control/wizard/__init__.py 2014-06-24 12:34:19 +0000
@@ -22,3 +22,4 @@
22from . import credit_control_marker22from . import credit_control_marker
23from . import credit_control_printer23from . import credit_control_printer
24from . import credit_control_communication24from . import credit_control_communication
25from . import credit_control_policy_changer
2526
=== modified file 'account_credit_control/wizard/credit_control_communication.py'
--- account_credit_control/wizard/credit_control_communication.py 2013-07-12 10:44:13 +0000
+++ account_credit_control/wizard/credit_control_communication.py 2014-06-24 12:34:19 +0000
@@ -80,14 +80,18 @@
80 return cr_l_ids80 return cr_l_ids
8181
82 def _generate_comm_from_credit_line_ids(self, cr, uid, line_ids, context=None):82 def _generate_comm_from_credit_line_ids(self, cr, uid, line_ids, context=None):
83 """Aggregate credit control line by partner, level, and currency
84 It also generate a communication object per aggregation.
85 """
83 if not line_ids:86 if not line_ids:
84 return []87 return []
85 comms = []88 comms = []
86 sql = ("SELECT distinct partner_id, policy_level_id, credit_control_policy_level.level"89 sql = ("SELECT distinct partner_id, policy_level_id, "
90 " credit_control_line.currency_id, credit_control_policy_level.level"
87 " FROM credit_control_line JOIN credit_control_policy_level "91 " FROM credit_control_line JOIN credit_control_policy_level "
88 " ON (credit_control_line.policy_level_id = credit_control_policy_level.id)"92 " ON (credit_control_line.policy_level_id = credit_control_policy_level.id)"
89 " WHERE credit_control_line.id in %s"93 " WHERE credit_control_line.id in %s"
90 " ORDER by credit_control_policy_level.level")94 " ORDER by credit_control_policy_level.level, credit_control_line.currency_id")
9195
92 cr.execute(sql, (tuple(line_ids),))96 cr.execute(sql, (tuple(line_ids),))
93 res = cr.dictfetchall()97 res = cr.dictfetchall()
@@ -101,8 +105,6 @@
101 data['partner_id'] = level_assoc['partner_id']105 data['partner_id'] = level_assoc['partner_id']
102 data['current_policy_level'] = level_assoc['policy_level_id']106 data['current_policy_level'] = level_assoc['policy_level_id']
103 comm_id = self.create(cr, uid, data, context=context)107 comm_id = self.create(cr, uid, data, context=context)
104
105
106 comms.append(self.browse(cr, uid, comm_id, context=context))108 comms.append(self.browse(cr, uid, comm_id, context=context))
107 return comms109 return comms
108110
109111
=== modified file 'account_credit_control/wizard/credit_control_emailer_view.xml'
--- account_credit_control/wizard/credit_control_emailer_view.xml 2013-03-18 09:21:33 +0000
+++ account_credit_control/wizard/credit_control_emailer_view.xml 2014-06-24 12:34:19 +0000
@@ -4,17 +4,27 @@
4 <record id="credit_line_emailer_form" model="ir.ui.view">4 <record id="credit_line_emailer_form" model="ir.ui.view">
5 <field name="name">credit.line.emailer.form</field>5 <field name="name">credit.line.emailer.form</field>
6 <field name="model">credit.control.emailer</field>6 <field name="model">credit.control.emailer</field>
7 <field name="type">form</field>
8 <field name="arch" type="xml">7 <field name="arch" type="xml">
9 <form string="Mailer">8 <form string="Mailer" version="7.0">
10 <separator string="Send emails for the selected lines" colspan="4"/>9 <separator string="Send emails for the selected lines" colspan="4"/>
11 <newline/>10 <newline/>
12 <field name="line_ids" colspan="4" nolabel="1" />11 <notebook>
12 <page string="Lines">
13 <field name="line_ids" colspan="4" nolabel="1" />
14 </page>
15 </notebook>
13 <newline/>16 <newline/>
14 <group colspan="4">17 <footer>
15 <button special="cancel" string="Cancel" icon='gtk-cancel'/>18 <button class="oe_highlight"
16 <button name="email_lines" string="Send the emails" type="object" icon="gtk-execute"/>19 name="email_lines"
17 </group>20 string="Send the emails"
21 type="object"/>
22 or
23 <button
24 class="oe_link"
25 special="cancel"
26 string="Cancel"/>
27 </footer>
18 </form>28 </form>
19 </field>29 </field>
20 </record>30 </record>
2131
=== modified file 'account_credit_control/wizard/credit_control_marker_view.xml'
--- account_credit_control/wizard/credit_control_marker_view.xml 2013-03-18 09:21:33 +0000
+++ account_credit_control/wizard/credit_control_marker_view.xml 2014-06-24 12:34:19 +0000
@@ -4,19 +4,34 @@
4 <record id="credit_line_marker_form" model="ir.ui.view">4 <record id="credit_line_marker_form" model="ir.ui.view">
5 <field name="name">credit.line.marker.form</field>5 <field name="name">credit.line.marker.form</field>
6 <field name="model">credit.control.marker</field>6 <field name="model">credit.control.marker</field>
7 <field name="type">form</field>
8 <field name="arch" type="xml">7 <field name="arch" type="xml">
9 <form string="Lines marker">8 <form string="Lines marker" version="7.0">
10 <separator string="Change the state of the selected lines." colspan="4"/>9 <separator string="Change the state of the selected lines" colspan="4"/>
10 <newline/>
11 <label string="Warning: you will maybe not be able to revert this operation." colspan="4"></label>11 <label string="Warning: you will maybe not be able to revert this operation." colspan="4"></label>
12 <newline/>12 <newline/>
13 <field name="name" colspan="4"/>13 <group>
14 <field name="line_ids" colspan="4" nolabel="1"/>14 <group><field name="name"/></group>
15 <newline/>15 <group></group>
16 <group colspan="4">
17 <button special="cancel" string="Cancel" icon='gtk-cancel'/>
18 <button name="mark_lines" string="Change Lines' State" type="object" icon="gtk-execute"/>
19 </group>16 </group>
17 <notebook>
18 <page string="Lines">
19 <field name="line_ids" colspan="4" nolabel="1"/>
20 </page>
21 </notebook>
22 <newline/>
23 <footer>
24 <button
25 class="oe_highlight"
26 name="mark_lines"
27 string="Change Lines' State"
28 type="object"/>
29 or
30 <button
31 class="oe_link"
32 special="cancel"
33 string="Cancel"/>
34 </footer>
20 </form>35 </form>
21 </field>36 </field>
22 </record>37 </record>
2338
=== added file 'account_credit_control/wizard/credit_control_policy_changer.py'
--- account_credit_control/wizard/credit_control_policy_changer.py 1970-01-01 00:00:00 +0000
+++ account_credit_control/wizard/credit_control_policy_changer.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,178 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 logging
22from openerp.tools.translate import _
23from openerp.osv import orm, fields
24logger = logging.getLogger(__name__)
25
26
27class credit_control_policy_changer(orm.TransientModel):
28 """Wizard that is run from invoices and allows to set manually a policy
29 Policy are actually apply to related move lines availabe
30 in selection widget
31
32 """
33 _name = "credit.control.policy.changer"
34 _columns = {
35 'new_policy_id': fields.many2one('credit.control.policy',
36 'New Policy to Apply',
37 required=True),
38 'new_policy_level_id': fields.many2one('credit.control.policy.level',
39 'New level to apply',
40 required=True),
41 # Only used to provide dynamic filtering on form
42 'do_nothing': fields.boolean('No follow policy'),
43 'move_line_ids': fields.many2many('account.move.line',
44 rel='credit_changer_ml_rel',
45 string='Move line to change'),
46 }
47
48 def _get_default_lines(self, cr, uid, context=None):
49 """Get default lines for fields move_line_ids
50 of wizard. Only take lines that are on the same account
51 and move of the invoice and not reconciled
52
53 :return: list of compliant move line ids
54
55 """
56 if context is None:
57 context = {}
58 active_ids = context.get('active_ids')
59 selected_line_ids = []
60 inv_model = self.pool['account.invoice']
61 move_line_model = self.pool['account.move.line']
62 if not active_ids:
63 return False
64 # raise ValueError('No active_ids passed in context')
65 for invoice in inv_model.browse(cr, uid, active_ids, context=context):
66 if invoice.type in ('in_invoice', 'in_refund', 'out_refund'):
67 raise orm.except_orm(_('User error'),
68 _('Please use wizard on cutomer invoices'))
69
70 domain = [('account_id', '=', invoice.account_id.id),
71 ('move_id', '=', invoice.move_id.id),
72 ('reconcile_id', '=', False)]
73 move_ids = move_line_model.search(cr, uid, domain, context=context)
74 selected_line_ids.extend(move_ids)
75 return selected_line_ids
76
77 _defaults = {'move_line_ids': _get_default_lines}
78
79 def onchange_policy_id(self, cr, uid, ids, new_policy_id, context=None):
80 if not new_policy_id:
81 return {}
82 policy = self.pool['credit.control.policy'].browse(cr, uid,
83 new_policy_id,
84 context=context)
85 return {'value': {'do_nothing': policy.do_nothing}}
86
87 def _mark_as_overridden(self, cr, uid, move_lines, context=None):
88 """Mark `move_lines` related credit control line as overridden
89 This is done by setting manually_overridden fields to True
90
91 :param move_lines: move line to mark as overridden
92
93 :retun: list of credit line ids that where marked as overridden
94
95 """
96 credit_model = self.pool['credit.control.line']
97 domain = [('move_line_id', 'in', [x.id for x in move_lines])]
98 credits_ids = credit_model.search(cr, uid, domain, context=context)
99 credit_model.write(cr, uid,
100 credits_ids,
101 {'manually_overridden': True},
102 context)
103 return credits_ids
104
105 def _set_invoice_policy(self, cr, uid, move_line_ids, policy,
106 context=None):
107 """Force policy on invoice"""
108 invoice_model = self.pool['account.invoice']
109 invoice_ids = set([x.invoice.id for x in move_line_ids if x.invoice])
110 invoice_model.write(cr, uid, list(invoice_ids),
111 {'credit_policy_id': policy.id},
112 context=context)
113
114 def _check_accounts_policies(self, cr, uid, lines, policy, context=None):
115 policy_obj = self.pool['credit.control.policy']
116 for line in lines:
117 policy_obj.check_policy_against_account(
118 cr, uid,
119 line.account_id.id,
120 policy.id,
121 context=context
122 )
123 return True
124
125 def set_new_policy(self, cr, uid, wizard_id, context=None):
126 """Set new policy on an invoice.
127
128 This is done by creating a new credit control line
129 related to the move line and the policy setted in
130 the wizard form
131
132 :return: ir.actions.act_windows dict
133
134 """
135 assert len(wizard_id) == 1, "Only one id expected"
136 wizard_id = wizard_id[0]
137
138 credit_line_model = self.pool['credit.control.line']
139 ir_model = self.pool['ir.model.data']
140 ui_act_model = self.pool['ir.actions.act_window']
141 wizard = self.browse(cr, uid, wizard_id, context=context)
142 controlling_date = fields.date.today()
143 self._check_accounts_policies(
144 cr,
145 uid,
146 wizard.move_line_ids,
147 wizard.new_policy_id)
148 self._mark_as_overridden(cr,
149 uid,
150 wizard.move_line_ids,
151 context=context)
152 # As disscused with business expert
153 # draft lines should be passed to ignored
154 # if same level as the new one
155 # As it is a manual action
156 # We also ignore rounding tolerance
157 generated_ids = None
158 generated_ids = credit_line_model.create_or_update_from_mv_lines(
159 cr, uid, [],
160 [x.id for x in wizard.move_line_ids],
161 wizard.new_policy_level_id.id,
162 controlling_date,
163 check_tolerance=False,
164 context=None
165 )
166 self._set_invoice_policy(cr, uid,
167 wizard.move_line_ids,
168 wizard.new_policy_id,
169 context=context)
170 if not generated_ids:
171 return {}
172 view_id = ir_model.get_object_reference(cr, uid,
173 "account_credit_control",
174 "credit_control_line_action")
175 assert view_id, 'No view found'
176 action = ui_act_model.read(cr, uid, view_id[1], context=context)
177 action['domain'] = [('id', 'in', generated_ids)]
178 return action
0179
=== added file 'account_credit_control/wizard/credit_control_policy_changer_view.xml'
--- account_credit_control/wizard/credit_control_policy_changer_view.xml 1970-01-01 00:00:00 +0000
+++ account_credit_control/wizard/credit_control_policy_changer_view.xml 2014-06-24 12:34:19 +0000
@@ -0,0 +1,65 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <record id="credit_control_policy_changer_form" model="ir.ui.view">
5 <field name="name">credit control policy form</field>
6 <field name="model">credit.control.policy.changer</field>
7 <field name="arch" type="xml">
8 <form version="7.0" string="Set current credit level">
9 <separator string="Change the overdue level of current invoice" colspan="4"/>
10 <label string="This wizard will let you set the overdue policy and level for selected invoices"/>
11 <newline/>
12 <group>
13 <group>
14 <field name="new_policy_id"
15 on_change="onchange_policy_id(new_policy_id)"/>
16 <field name="do_nothing"
17 invisible="1"/>
18 <field name="new_policy_level_id"
19 domain="[('policy_id', '=', new_policy_id)]"/>
20 </group>
21 <group></group>
22 </group>
23 <notebook colspan="4">
24 <page string="Move lines to affect">
25 <field name="move_line_ids"/>
26 </page>
27 </notebook>
28 <footer>
29 <button class="oe_highlight"
30 name="set_new_policy"
31 string="Set new policy"
32 type="object"/>
33 or
34 <button class="oe_link"
35 special="cancel"
36 string="Cancel"/>
37 </footer>
38 </form>
39
40 </field>
41 </record>
42
43 <!-- for button -->
44 <record id="action_wizard_credit_policy_changer" model="ir.actions.act_window">
45 <field name="name">Change current credit policy</field>
46 <field name="res_model">credit.control.policy.changer</field>
47 <field name="src_model">account.invoice</field>
48 <field name="view_type">form</field>
49 <field name="view_mode">form</field>
50 <field name="view_id" ref="credit_control_policy_changer_form"/>
51 <field name="target">new</field>
52 <field name="help">Allows to manually change credit level</field>
53 </record>
54
55 <!-- for menu -->
56 <act_window name="Change current credit policy"
57 res_model="credit.control.policy.changer"
58 src_model="account.invoice"
59 view_mode="form"
60 target="new"
61 key2="client_action_multi"
62 id="action_wizard_credit_policy_changer_menu_action"/>
63
64 </data>
65</openerp>
066
=== modified file 'account_credit_control/wizard/credit_control_printer.py'
--- account_credit_control/wizard/credit_control_printer.py 2013-06-08 09:34:35 +0000
+++ account_credit_control/wizard/credit_control_printer.py 2014-06-24 12:34:19 +0000
@@ -23,6 +23,7 @@
23from openerp.osv import orm, fields23from openerp.osv import orm, fields
24from openerp.tools.translate import _24from openerp.tools.translate import _
2525
26
26class CreditControlPrinter(orm.TransientModel):27class CreditControlPrinter(orm.TransientModel):
27 """Print lines"""28 """Print lines"""
2829
@@ -34,15 +35,16 @@
34 if context is None:35 if context is None:
35 context = {}36 context = {}
36 res = False37 res = False
37 if (context.get('active_model') == 'credit.control.line' and38 if context.get('active_model') != 'credit.control.line':
38 context.get('active_ids')):39 return res
39 res = context['active_ids']40 res = context.get('active_ids', False)
40 return res41 return res
4142
42 _columns = {43 _columns = {
43 'mark_as_sent': fields.boolean('Mark letter lines as sent',44 'mark_as_sent': fields.boolean('Mark letter lines as sent',
44 help="Only letter lines will be marked."),45 help="Only letter lines will be marked."),
45 'report_file': fields.binary('Generated Report', readonly=True),46 'report_file': fields.binary('Generated Report', readonly=True),
47 'report_name': fields.char('Report name'),
46 'state': fields.char('state', size=32),48 'state': fields.char('state', size=32),
47 'line_ids': fields.many2many(49 'line_ids': fields.many2many(
48 'credit.control.line',50 'credit.control.line',
@@ -62,6 +64,12 @@
62 ('channel', '=', 'letter')]64 ('channel', '=', 'letter')]
63 return line_obj.search(cr, uid, domain, context=context)65 return line_obj.search(cr, uid, domain, context=context)
6466
67 def _credit_line_predicate(self, cr, uid, line_record, context=None):
68 return True
69
70 def _get_line_ids(self, cr, uid, lines, predicate, context=None):
71 return [l.id for l in lines if predicate(cr, uid, l, context)]
72
65 def print_lines(self, cr, uid, wiz_id, context=None):73 def print_lines(self, cr, uid, wiz_id, context=None):
66 assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \74 assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
67 "wiz_id: only one id expected"75 "wiz_id: only one id expected"
@@ -71,14 +79,22 @@
71 form = self.browse(cr, uid, wiz_id, context)79 form = self.browse(cr, uid, wiz_id, context)
7280
73 if not form.line_ids and not form.print_all:81 if not form.line_ids and not form.print_all:
74 raise orm.except_orm(_('Error'), _('No credit control lines selected.'))82 raise orm.except_orm(_('Error'),
7583 _('No credit control lines selected.'))
76 line_ids = [l.id for l in form.line_ids]84
85 line_ids = self._get_line_ids(cr,
86 uid,
87 form.line_ids,
88 self._credit_line_predicate,
89 context=context)
90
77 comms = comm_obj._generate_comm_from_credit_line_ids(cr, uid, line_ids,91 comms = comm_obj._generate_comm_from_credit_line_ids(cr, uid, line_ids,
78 context=context)92 context=context)
79 report_file = comm_obj._generate_report(cr, uid, comms, context=context)93 report_file = comm_obj._generate_report(cr, uid, comms, context=context)
8094
81 form.write({'report_file': base64.b64encode(report_file), 'state': 'done'})95 form.write({'report_file': base64.b64encode(report_file),
96 'report_name': 'credit_control_esr_bvr_%s.pdf' % fields.datetime.now(),
97 'state': 'done'})
8298
83 if form.mark_as_sent:99 if form.mark_as_sent:
84 comm_obj._mark_credit_line_as_sent(cr, uid, comms, context=context)100 comm_obj._mark_credit_line_as_sent(cr, uid, comms, context=context)
85101
=== modified file 'account_credit_control/wizard/credit_control_printer_view.xml'
--- account_credit_control/wizard/credit_control_printer_view.xml 2013-03-18 09:21:33 +0000
+++ account_credit_control/wizard/credit_control_printer_view.xml 2014-06-24 12:34:19 +0000
@@ -6,21 +6,34 @@
6 <field name="model">credit.control.printer</field>6 <field name="model">credit.control.printer</field>
7 <field name="type">form</field>7 <field name="type">form</field>
8 <field name="arch" type="xml">8 <field name="arch" type="xml">
9 <form string="Lines report">9 <form string="Lines report" version="7.0">
10 <separator colspan="4" string="Print the selected lines"/>10 <separator string="Print the selected lines" colspan="4"/>
11 <newline/>11 <newline/>
12 <field name="mark_as_sent" colspan="4" attrs="{'invisible': [('state', '=', 'done')]}"/>12 <group>
13 <newline/>13 <field name="mark_as_sent"
14 colspan="4"
15 attrs="{'invisible': [('state', '=', 'done')]}"/>
16 </group>
17 <newline/>
18 <notebook>
19 <page string="Lines" attrs="{'invisible': [('state', '=', 'done')]}">
14 <field name="line_ids" colspan="4" nolabel="1"20 <field name="line_ids" colspan="4" nolabel="1"
15 attrs="{'invisible': [('state', '=', 'done')]}" />21 attrs="{'invisible': [('state', '=', 'done')]}" />
16 <field name="report_file" colspan="4" attrs="{'invisible': [('state', '!=', 'done')]}"/>22 </page>
23 </notebook>
24 <field name="report_name"
25 invisible="1"/>
26 <field name="report_file"
27 colspan="4"
28 filename="report_name"
29 attrs="{'invisible': [('state', '!=', 'done')]}"/>
17 <field name="state" invisible="1" />30 <field name="state" invisible="1" />
18 <newline/>31 <newline/>
19 <group colspan="4">32 <footer>
20 <button special="cancel" string="Cancel" icon='gtk-cancel' attrs="{'invisible': [('state', '==', 'done')]}"/>33 <button class="oe_highlight" name="print_lines" string="Print" type="object" attrs="{'invisible': [('state', '=', 'done')]}"/>
21 <button name="print_lines" string="Print" type="object" icon="gtk-execute" attrs="{'invisible': [('state', '==', 'done')]}"/>34 <button special="cancel" string="Cancel" icon='gtk-cancel' attrs="{'invisible': [('state', '=', 'done')]}"/>
22 <button special="cancel" string="Close" icon='gtk-close' attrs="{'invisible': [('state', '!=', 'done')]}"/>35 <button special="cancel" string="Close" icon='gtk-close' attrs="{'invisible': [('state', '!=', 'done')]}"/>
23 </group>36 </footer>
24 </form>37 </form>
25 </field>38 </field>
26 </record>39 </record>
2740
=== added directory 'account_credit_control_dunning_fees'
=== added file 'account_credit_control_dunning_fees/__init__.py'
--- account_credit_control_dunning_fees/__init__.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/__init__.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,21 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 . import model
022
=== added file 'account_credit_control_dunning_fees/__openerp__.py'
--- account_credit_control_dunning_fees/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/__openerp__.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,71 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21{'name': 'Credit control dunning fees',
22 'version': '0.1.0',
23 'author': 'Camptocamp',
24 'maintainer': 'Camptocamp',
25 'category': 'Accounting',
26 'complexity': 'normal',
27 'depends': ['account_credit_control'],
28 'description': """
29Dunning Fees for Credit Control
30===============================
31
32This extention of credit control adds the notion of dunning fees
33on credit control lines.
34
35Configuration
36-------------
37For release 0.1 only fixed fees are supported.
38
39You can specifiy a fixed fees amount, a product and a currency
40on the credit control level form.
41
42The amount will be used as fees values the currency will determine
43the currency of the fee. If the credit control line has not the
44same currency as the fees currency, fees will be converted to
45the credit control line currency.
46
47The product is used to compute taxes in reconciliation process.
48
49Run
50---
51Fees are automatically computed on credit run and saved
52on the generated credit lines.
53
54Fees can be manually edited as long credit line is draft
55
56Credit control Summary report includes a new fees column.
57-------
58Support of fees price list
59
60""",
61 'website': 'http://www.camptocamp.com',
62 'data': ['view/policy_view.xml',
63 'view/line_view.xml',
64 'report/report.xml'],
65 'demo': [],
66 'test': [],
67 'installable': True,
68 'auto_install': False,
69 'license': 'AGPL-3',
70 'application': False,
71}
072
=== added directory 'account_credit_control_dunning_fees/i18n'
=== added file 'account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot'
--- account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot 2014-06-24 12:34:19 +0000
@@ -0,0 +1,81 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * account_credit_control_dunning_fees
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-05-07 11:44+0000\n"
10"PO-Revision-Date: 2014-05-07 11:44+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_credit_control_dunning_fees
19#: code:_description:0
20#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
21#, python-format
22msgid "A credit control line"
23msgstr ""
24
25#. module: account_credit_control_dunning_fees
26#: code:_description:0
27#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
28#, python-format
29msgid "A credit control policy level"
30msgstr ""
31
32#. module: account_credit_control_dunning_fees
33#: code:_description:0
34#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
35#, python-format
36msgid "Credit control line generator"
37msgstr ""
38
39#. module: account_credit_control_dunning_fees
40#: field:credit.control.line,dunning_fees_amount:0
41#: view:credit.control.policy:0
42msgid "Fees"
43msgstr ""
44
45#. module: account_credit_control_dunning_fees
46#: field:credit.control.policy.level,dunning_fixed_amount:0
47msgid "Fees Fixed Amount"
48msgstr ""
49
50#. module: account_credit_control_dunning_fees
51#: field:credit.control.policy.level,dunning_product_id:0
52msgid "Fees Product"
53msgstr ""
54
55#. module: account_credit_control_dunning_fees
56#: field:credit.control.policy.level,dunning_currency_id:0
57msgid "Fees currency"
58msgstr ""
59
60#. module: account_credit_control_dunning_fees
61#: selection:credit.control.policy.level,dunning_fees_type:0
62msgid "Fixed"
63msgstr ""
64
65#. module: account_credit_control_dunning_fees
66#: view:credit.control.policy:0
67msgid "Mail and reporting"
68msgstr ""
69
70#. module: account_credit_control_dunning_fees
71#: code:_description:0
72#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
73#, python-format
74msgid "credit.control.dunning.fees.computer"
75msgstr ""
76
77#. module: account_credit_control_dunning_fees
78#: field:credit.control.policy.level,dunning_fees_type:0
79msgid "unknown"
80msgstr ""
81
082
=== added file 'account_credit_control_dunning_fees/i18n/fr.po'
--- account_credit_control_dunning_fees/i18n/fr.po 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/i18n/fr.po 2014-06-24 12:34:19 +0000
@@ -0,0 +1,80 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * account_credit_control_dunning_fees
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-04-16 07:11+0000\n"
10"PO-Revision-Date: 2014-04-16 07:11+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_credit_control_dunning_fees
19#: code:_description:0
20#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
21#, python-format
22msgid "A credit control line"
23msgstr "Ligne de relance"
24
25#. module: account_credit_control_dunning_fees
26#: code:_description:0
27#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
28#, python-format
29msgid "A credit control policy level"
30msgstr "Une politique de relance"
31
32#. module: account_credit_control_dunning_fees
33#: code:_description:0
34#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
35#, python-format
36msgid "Credit control line generator"
37msgstr "Générateur de relance"
38
39#. module: account_credit_control_dunning_fees
40#: field:credit.control.line,dunning_fees_amount:0
41#: view:credit.control.policy:0
42msgid "Fees"
43msgstr "Frais de relance"
44
45#. module: account_credit_control_dunning_fees
46#: field:credit.control.policy.level,dunning_fixed_amount:0
47msgid "Fees Fixed Amount"
48msgstr "Montant des frais"
49
50#. module: account_credit_control_dunning_fees
51#: field:credit.control.policy.level,dunning_product_id:0
52msgid "Fees Product"
53msgstr "Article lié"
54
55#. module: account_credit_control_dunning_fees
56#: field:credit.control.policy.level,dunning_currency_id:0
57msgid "Fees currency"
58msgstr "Devises"
59
60#. module: account_credit_control_dunning_fees
61#: selection:credit.control.policy.level,dunning_fees_type:0
62msgid "Fixed"
63msgstr "Fixe"
64
65#. module: account_credit_control_dunning_fees
66#: view:credit.control.policy:0
67msgid "Mail and reporting"
68msgstr "Lettres et e-mails"
69
70#. module: account_credit_control_dunning_fees
71#: code:_description:0
72#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
73#, python-format
74msgid "credit.control.dunning.fees.computer"
75msgstr "credit.control.dunning.fees.computer"
76
77#. module: account_credit_control_dunning_fees
78#: field:credit.control.policy.level,dunning_fees_type:0
79msgid "unknown"
80msgstr "inconnu"
081
=== added directory 'account_credit_control_dunning_fees/model'
=== added file 'account_credit_control_dunning_fees/model/__init__.py'
--- account_credit_control_dunning_fees/model/__init__.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/model/__init__.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,24 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 . import line
22from . import policy
23from . import run
24from . import dunning
025
=== added file 'account_credit_control_dunning_fees/model/dunning.py'
--- account_credit_control_dunning_fees/model/dunning.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/model/dunning.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,120 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 import orm
22
23
24class FeesComputer(orm.BaseModel):
25 """Model that compute dunnig fees.
26
27 This class does not need any database storage as
28 it contains pure logic.
29
30 It inherits form ``orm.BaseModel`` to benefit of orm facility
31
32 Similar to AbstractModel but log access and actions
33 """
34
35 _name = 'credit.control.dunning.fees.computer'
36 _auto = False
37 _log_access = True
38 _register = True
39 _transient = False
40
41 def _get_compute_fun(self, level_fees_type):
42 """Retrieve function of class that should compute the fees based on type
43
44 :param level_fee_type: type exisiting in model `credit.control.policy.level`
45 for field dunning_fees_type
46
47 :returns: a function of class :class:`FeesComputer` with following signature
48 self, cr, uid, credit_line (record), context
49
50 """
51 if level_fees_type == 'fixed':
52 return self.compute_fixed_fees
53 else:
54 raise NotImplementedError('fees type %s is not supported' % level_fees_type)
55
56 def _compute_fees(self, cr, uid, credit_line_ids, context=None):
57 """Compute fees for `credit_line_ids` parameter
58
59 Fees amount is written on credit line in field dunning_fees_amount
60
61 :param credit_line_ids: list of `credit.control.line` ids
62
63 :returns: `credit_line_ids` list of `credit.control.line` ids
64
65 """
66 if context is None:
67 context = {}
68 if not credit_line_ids:
69 return credit_line_ids
70 c_model = self.pool['credit.control.line']
71 credit_lines = c_model.browse(cr, uid, credit_line_ids, context=context)
72 for credit in credit_lines:
73 # if there is no dependence between generated credit lines
74 # this could be threaded
75 self._compute(cr, uid, credit, context=context),
76 return credit_line_ids
77
78 def _compute(self, cr, uid, credit_line, context=None):
79 """Compute fees for a given credit line
80
81 Fees amount is written on credit line in field dunning_fees_amount
82
83 :param credit_line: credit line record
84
85 :returns: `credit_line` record
86 """
87 fees_type = credit_line.policy_level_id.dunning_fees_type
88 compute = self._get_compute_fun(fees_type)
89 fees = compute(cr, uid, credit_line, context=context)
90 if fees:
91 credit_line.write({'dunning_fees_amount': fees},
92 context=context)
93 return credit_line
94
95 def compute_fixed_fees(self, cr, uid, credit_line, context=None):
96 """Compute fees amount for fixed fees.
97 Correspond to the fixed dunning fees type
98
99 if currency of the fees is not the same as the currency
100 of the credit line, fees amount is converted to
101 currency of credit line.
102
103 :param credit_line: credit line record
104
105 :return: fees amount float (in credit line currency)
106
107 """
108 currency_model = self.pool['res.currency']
109 credit_currency = credit_line.currency_id
110 level = credit_line.policy_level_id
111 fees_amount = level.dunning_fixed_amount
112 if not fees_amount:
113 return 0.0
114 fees_currency = level.dunning_currency_id
115 if fees_currency == credit_currency:
116 return fees_amount
117 else:
118 return currency_model.compute(cr, uid, fees_currency.id,
119 credit_currency.id, fees_amount,
120 context=context)
0121
=== added file 'account_credit_control_dunning_fees/model/line.py'
--- account_credit_control_dunning_fees/model/line.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/model/line.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,29 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 import orm, fields
22
23
24class credit_control_line(orm.Model):
25 """Add dunning_fees_amount_fees field"""
26
27 _inherit = "credit.control.line"
28
29 _columns = {'dunning_fees_amount': fields.float('Fees')}
030
=== added file 'account_credit_control_dunning_fees/model/policy.py'
--- account_credit_control_dunning_fees/model/policy.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/model/policy.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,36 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 import orm, fields
22
23
24class credit_control_policy(orm.Model):
25 """ADD dunning fees fields"""
26
27 _inherit = "credit.control.policy.level"
28 _columns = {'dunning_product_id': fields.many2one('product.product',
29 'Fees Product'),
30 'dunning_fixed_amount': fields.float('Fees Fixed Amount'),
31 'dunning_currency_id': fields.many2one('res.currency',
32 'Fees currency'),
33 # planned type are fixed, percent, compound
34 'dunning_fees_type': fields.selection([('fixed', 'Fixed')])}
35
36 _defaults = {'dunning_fees_type': 'fixed'}
037
=== added file 'account_credit_control_dunning_fees/model/run.py'
--- account_credit_control_dunning_fees/model/run.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/model/run.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,39 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 import orm, fields
22
23
24class credit_control_run(orm.Model):
25 """Add computation of fees"""
26
27 _inherit = "credit.control.run"
28
29 def _generate_credit_lines(self, cr, uid, run_id, context=None):
30 """Override method to add fees computation"""
31 credit_line_ids = super(credit_control_run, self)._generate_credit_lines(
32 cr,
33 uid,
34 run_id,
35 context=context
36 )
37 fees_model = self.pool['credit.control.dunning.fees.computer']
38 fees_model._compute_fees(cr, uid, credit_line_ids, context=context)
39 return credit_line_ids
040
=== added directory 'account_credit_control_dunning_fees/report'
=== added file 'account_credit_control_dunning_fees/report/credit_control_summary.html.mako'
--- account_credit_control_dunning_fees/report/credit_control_summary.html.mako 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/report/credit_control_summary.html.mako 2014-06-24 12:34:19 +0000
@@ -0,0 +1,246 @@
1## -*- coding: utf-8 -*-
2<html>
3 <head>
4 <style type="text/css">
5 ${css}
6body {
7 font-family: helvetica;
8 font-size: 12px;
9}
10
11.custom_text {
12 font-family: helvetica;
13 font-size: 12px;
14}
15
16table {
17 font-family: helvetica;
18 font-size: 12px;
19}
20
21.header {
22 margin-left: 0px;
23 text-align: left;
24 width: 300px;
25 font-size: 12px;
26}
27
28.title {
29 font-size: 16px;
30 font-weight: bold;
31}
32
33.basic_table{
34 text-align: center;
35 border: 1px solid lightGrey;
36 border-collapse: collapse;
37 font-family: helvetica;
38 font-size: 12px;
39}
40
41.basic_table th {
42 border: 1px solid lightGrey;
43 font-size: 11px;
44 font-weight: bold;
45
46}
47
48.basic_table td {
49 border: 1px solid lightGrey;
50 font-size: 12px;
51}
52
53.list_table {
54 border-color: black;
55 text-align: center;
56 border-collapse: collapse;
57}
58
59.list_table td {
60 border-color: gray;
61 border-top: 1px solid gray;
62 text-align: left;
63 font-size: 12px;
64 padding-right: 3px;
65 padding-left: 3px;
66 padding-top: 3px;
67 padding-bottom:3px;
68}
69
70.list_table th {
71 border-bottom: 2px solid black;
72 text-align: left;
73 font-size: 11px;
74 font-weight: bold;
75 padding-right: 3px
76 padding-left: 3px
77}
78
79.list_table thead {
80 display: table-header-group;
81}
82
83.address table {
84 font-size: 11px;
85 border-collapse: collapse;
86 margin: 0px;
87 padding: 0px;
88}
89
90.address .shipping {
91
92}
93
94.address .invoice {
95 margin-top: 10px;
96}
97
98.address .recipient {
99 font-size: 13px;
100 margin-right: 120px;
101 margin-left: 350px;
102 float: right;
103}
104
105
106table .address_title {
107 font-weight: bold;
108}
109
110.address td.name {
111 font-weight: bold;
112}
113
114td.amount, th.amount {
115 text-align: right;
116 padding-right:2px;
117}
118
119h1 {
120 font-size: 16px;
121 font-weight: bold;
122}
123
124tr.line .note {
125 border-style: none;
126 font-size: 9px;
127 padding-left: 10px;
128}
129
130tr.line {
131 margin-bottom: 10px;
132}
133 </style>
134 </head>
135 <body>
136
137 %for comm in objects :
138 <% setLang(comm.get_contact_address().lang) %>
139 <div class="address">
140 <table class="recipient">
141 <%
142 add = comm.get_contact_address()
143 %>
144 %if comm.partner_id.id == add.id:
145 <tr><td class="name">${comm.partner_id.title and comm.partner_id.title.name or ''} ${comm.partner_id.name }</td></tr>
146 <% address_lines = comm.partner_id.contact_address.split("\n") %>
147
148 %else:
149 <tr><td class="name">${comm.partner_id.name or ''}</td></tr>
150 <tr><td>${add.title and add.title.name or ''} ${add.name}</td></tr>
151 <% address_lines = add.contact_address.split("\n")[1:] %>
152 %endif
153 %for part in address_lines:
154 %if part:
155 <tr><td>${part}</td></tr>
156 %endif
157 %endfor
158 </table>
159 <br/>
160 <br/>
161 <br/>
162 <br/>
163
164 </div>
165 <br/>
166 <br/>
167 <br/>
168 <div>
169
170 <h3 style="clear: both; padding-top: 20px;">
171 ${_('Reminder')}: ${comm.current_policy_level.name or '' }
172 </h3>
173
174 <p>${_('Dear')},</p>
175 <p class="custom_text" width="95%">${comm.current_policy_level.custom_text.replace('\n', '<br />')}</p>
176
177 <br/>
178 <br/>
179 <p><b>${_('Summary')}<br/></b></p>
180 <table class="basic_table" style="width: 100%;">
181 <tr>
182 <th width="200">${_('Invoice number')}</th>
183 <th>${_('Invoice date')}</th>
184 <th>${_('Date due')}</th>
185 <th>${_('Invoiced amount')}</th>
186 <th>${_('Open amount')}</th>
187 <th>${_('Fees')}</th>
188 <th>${_('Currency')}</th>
189
190 </tr>
191%for line in comm.credit_control_line_ids:
192 <tr>
193 %if line.invoice_id:
194 <td width="200">${line.invoice_id.number}
195 %if line.invoice_id.name:
196 <br/>
197 ${line.invoice_id.name}
198 %endif
199 </td>
200 %else:
201 <td width="200">${line.move_line_id.name}</td>
202 %endif
203 <td class="date">${line.date_entry}</td>
204 <td class="date">${line.date_due}</td>
205 <td class="amount">${line.amount_due}</td>
206 <td class="amount">${line.balance_due}</td>
207 <td class="amount">${line.dunning_fees_amount}</td>
208 <td class="amount">${line.currency_id.name or comm.company_id.currency_id.name}</td>
209 </tr>
210%endfor
211 </table>
212 <br/>
213 <br/>
214<%doc>
215 <!-- uncomment to have info after summary -->
216 <p>${_('If you have any question, do not hesitate to contact us.')}</p>
217
218 <p>${comm.user_id.name} ${comm.user_id.email and '<%s>'%(comm.user_id.email) or ''}<br/>
219 ${comm.company_id.name}<br/>
220 % if comm.company_id.street:
221 ${comm.company_id.street or ''}<br/>
222
223 % endif
224
225 % if comm.company_id.street2:
226 ${comm.company_id.street2}<br/>
227 % endif
228 % if comm.company_id.city or comm.company_id.zip:
229 ${comm.company_id.zip or ''} ${comm.company_id.city or ''}<br/>
230 % endif
231 % if comm.company_id.country_id:
232 ${comm.company_id.state_id and ('%s, ' % comm.company_id.state_id.name) or ''} ${comm.company_id.country_id.name or ''}<br/>
233 % endif
234 % if comm.company_id.phone:
235 Phone: ${comm.company_id.phone}<br/>
236 % endif
237 % if comm.company_id.website:
238 ${comm.company_id.website or ''}<br/>
239 % endif
240</%doc>
241
242 <p style="page-break-after:always"></p>
243 %endfor
244
245 </body>
246</html>
0247
=== added file 'account_credit_control_dunning_fees/report/report.xml'
--- account_credit_control_dunning_fees/report/report.xml 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/report/report.xml 2014-06-24 12:34:19 +0000
@@ -0,0 +1,12 @@
1<openerp>
2 <data>
3 <report auto="False"
4 id="account_credit_control.report_webkit_html"
5 model="credit.control.communication"
6 name="credit_control_summary"
7 file="account_credit_control_dunning_fees/report/credit_control_summary.html.mako"
8 string="Credit Summary"
9 report_type="webkit"
10 webkit_header="report_webkit.ir_header_webkit_basesample0"/>
11 </data>
12</openerp>
013
=== added directory 'account_credit_control_dunning_fees/security'
=== added directory 'account_credit_control_dunning_fees/tests'
=== added file 'account_credit_control_dunning_fees/tests/__init__.py'
--- account_credit_control_dunning_fees/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/tests/__init__.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,23 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 . import test_fees_generation
22
23checks = [test_fees_generation]
024
=== added file 'account_credit_control_dunning_fees/tests/test_fees_generation.py'
--- account_credit_control_dunning_fees/tests/test_fees_generation.py 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/tests/test_fees_generation.py 2014-06-24 12:34:19 +0000
@@ -0,0 +1,95 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# Author: Nicolas Bessi
5# Copyright 2014 Camptocamp SA
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 mock import MagicMock
22import openerp.tests.common as test_common
23
24
25class FixedFeesTester(test_common.TransactionCase):
26
27 def setUp(self):
28 """Initialize credit control level mock to test fees computations"""
29 super(FixedFeesTester, self).setUp()
30 self.currency_model = self.registry('res.currency')
31 self.euro = self.currency_model.search(self.cr, self.uid,
32 [('name', '=', 'EUR')])
33 self.assertTrue(self.euro)
34 self.euro = self.registry('res.currency').browse(self.cr,
35 self.uid,
36 self.euro[0])
37
38 self.usd = self.currency_model.search(self.cr, self.uid,
39 [('name', '=', 'USD')])
40 self.assertTrue(self.usd)
41 self.usd = self.registry('res.currency').browse(self.cr,
42 self.uid,
43 self.usd[0])
44
45 self.euro_level = MagicMock(name='Euro policy level')
46 self.euro_level.dunning_fixed_amount = 5.0
47 self.euro_level.dunning_currency_id = self.euro
48 self.euro_level.dunning_type = 'fixed'
49
50 self.usd_level = MagicMock(name='USD policy level')
51 self.usd_level.dunning_fixed_amount = 5.0
52 self.usd_level.dunning_currency_id = self.usd
53 self.usd_level.dunning_type = 'fixed'
54 self.dunning_model = self.registry('credit.control.dunning.fees.computer')
55
56 def test_type_getter(self):
57 """Test that correct compute function is returned for "fixed" type"""
58 c_fun = self.dunning_model._get_compute_fun('fixed')
59 self.assertEqual(c_fun, self.dunning_model.compute_fixed_fees)
60
61 def test_unknow_type(self):
62 """Test that non implemented error is raised if invalide fees type"""
63 with self.assertRaises(NotImplementedError):
64 self.dunning_model._get_compute_fun('bang')
65
66 def test_computation_same_currency(self):
67 """Test that fees are correctly computed with same currency"""
68 credit_line = MagicMock(name='Euro credit line')
69 credit_line.policy_level_id = self.euro_level
70 credit_line.currency_id = self.euro
71 fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
72 credit_line,
73 {})
74 self.assertEqual(fees, self.euro_level.dunning_fixed_amount)
75
76 def test_computation_different_currency(self):
77 """Test that fees are correctly computed with different currency"""
78 credit_line = MagicMock(name='USD credit line')
79 credit_line.policy_level_id = self.euro_level
80 credit_line.currency_id = self.usd
81 fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
82 credit_line,
83 {})
84 self.assertNotEqual(fees, self.euro_level.dunning_fixed_amount)
85
86 def test_no_fees(self):
87 """Test that fees are not generated if no amount defined on level"""
88 credit_line = MagicMock(name='USD credit line')
89 credit_line.policy_level_id = self.euro_level
90 self.euro_level.dunning_fixed_amount = 0.0
91 credit_line.currency_id = self.usd
92 fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
93 credit_line,
94 {})
95 self.assertEqual(fees, 0.0)
096
=== added directory 'account_credit_control_dunning_fees/view'
=== added file 'account_credit_control_dunning_fees/view/line_view.xml'
--- account_credit_control_dunning_fees/view/line_view.xml 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/view/line_view.xml 2014-06-24 12:34:19 +0000
@@ -0,0 +1,30 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <record id="add_fees_on_credit_control_line" model="ir.ui.view">
6 <field name="name">add fees on credit control line</field>
7 <field name="model">credit.control.line</field>
8 <field name="inherit_id" ref="account_credit_control.credit_control_line_tree" />
9 <field name="arch" type="xml">
10 <field name="balance_due" position="after">
11 <field name="dunning_fees_amount"
12 attrs="{'readonly': [('state', '!=', 'draft')]}"/>
13 </field>
14 </field>
15 </record>
16
17 <record id="add_fees_on_credit_control_line_from" model="ir.ui.view">
18 <field name="name">add fees on credit control line form</field>
19 <field name="model">credit.control.line</field>
20 <field name="inherit_id" ref="account_credit_control.credit_control_line_form"/>
21 <field name="arch" type="xml">
22 <field name="balance_due" position="after">
23 <field name="dunning_fees_amount"
24 attrs="{'readonly': [('state', '!=', 'draft')]}"/>
25 </field>
26 </field>
27 </record>
28
29 </data>
30</openerp>
031
=== added file 'account_credit_control_dunning_fees/view/policy_view.xml'
--- account_credit_control_dunning_fees/view/policy_view.xml 1970-01-01 00:00:00 +0000
+++ account_credit_control_dunning_fees/view/policy_view.xml 2014-06-24 12:34:19 +0000
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <record id="add_dunning_fees_on_policy" model="ir.ui.view">
5 <field name="name">add dunning fees on policy</field>
6 <field name="model">credit.control.policy</field>
7 <field name="inherit_id" ref="account_credit_control.credit_control_policy_form" />
8 <field name="arch" type="xml">
9 <page string="Mail and reporting" position="after">
10 <page string="Fees">
11 <group>
12 <group>
13 <field name="dunning_fixed_amount"/>
14 <field name="dunning_product_id"
15 attrs="{'required': [('dunning_fixed_amount', '!=', False)]}"/>
16 <field name="dunning_currency_id"
17 attrs="{'required': [('dunning_fixed_amount', '!=', False)]}"/>
18 </group>
19 </group>
20 </page>
21 </page>
22 </field>
23 </record>
24
25 </data>
26</openerp>
027
=== modified file 'async_move_line_importer/model/move_line_importer.py'
--- async_move_line_importer/model/move_line_importer.py 2013-11-01 10:54:13 +0000
+++ async_move_line_importer/model/move_line_importer.py 2014-06-24 12:34:19 +0000
@@ -41,7 +41,7 @@
4141
42 It will parse the saved CSV file using orm.BaseModel.load42 It will parse the saved CSV file using orm.BaseModel.load
43 in a thread. If you set bypass_orm to True then the load function43 in a thread. If you set bypass_orm to True then the load function
44 will use a totally overriden create function that is a lot faster44 will use a totally overridden create function that is a lot faster
45 but that totally bypass the ORM45 but that totally bypass the ORM
4646
47 """47 """
@@ -303,7 +303,7 @@
303 Will generate an success/failure report and generate some303 Will generate an success/failure report and generate some
304 maile threads. It uses BaseModel.load to lookup CSV.304 maile threads. It uses BaseModel.load to lookup CSV.
305 If you set bypass_orm to True then the load function305 If you set bypass_orm to True then the load function
306 will use a totally overriden create function that is a lot faster306 will use a totally overridden create function that is a lot faster
307 but that totally bypass the ORM307 but that totally bypass the ORM
308308
309 """309 """

Subscribers

People subscribed via source and target branches