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
1=== modified file 'account_credit_control/__openerp__.py'
2--- account_credit_control/__openerp__.py 2014-03-24 08:37:48 +0000
3+++ account_credit_control/__openerp__.py 2014-06-24 12:34:19 +0000
4@@ -19,7 +19,7 @@
5 #
6 ##############################################################################
7 {'name': 'Account Credit Control',
8- 'version': '0.1.1',
9+ 'version': '0.2.0',
10 'author': 'Camptocamp',
11 'maintainer': 'Camptocamp',
12 'category': 'Finance',
13@@ -69,6 +69,7 @@
14 "wizard/credit_control_emailer_view.xml",
15 "wizard/credit_control_marker_view.xml",
16 "wizard/credit_control_printer_view.xml",
17+ "wizard/credit_control_policy_changer_view.xml",
18 "security/ir.model.access.csv"],
19 'demo_xml': ["credit_control_demo.xml"],
20 'tests': [],
21
22=== modified file 'account_credit_control/account.py'
23--- account_credit_control/account.py 2013-09-04 12:37:34 +0000
24+++ account_credit_control/account.py 2014-06-24 12:34:19 +0000
25@@ -27,11 +27,11 @@
26 _inherit = "account.account"
27
28 _columns = {
29- 'credit_control_line_ids':
30- fields.one2many('credit.control.line',
31- 'account_id',
32- string='Credit Lines',
33- readonly=True),
34+ 'credit_control_line_ids': fields.one2many(
35+ 'credit.control.line',
36+ 'account_id',
37+ string='Credit Lines',
38+ readonly=True),
39 }
40
41 def copy_data(self, cr, uid, id, default=None, context=None):
42@@ -42,34 +42,3 @@
43 default['credit_control_line_ids'] = False
44 return super(AccountAccount, self).copy_data(
45 cr, uid, id, default=default, context=context)
46-
47-
48-class AccountInvoice(orm.Model):
49- """Add a link to a credit control policy on account.account"""
50-
51- _inherit = "account.invoice"
52- _columns = {
53- 'credit_policy_id':
54- fields.many2one('credit.control.policy',
55- 'Credit Control Policy',
56- help=("The Credit Control Policy used for this "
57- "invoice. If nothing is defined, it will "
58- "use the account setting or the partner "
59- "setting.")
60- ),
61- 'credit_control_line_ids':
62- fields.one2many('credit.control.line',
63- 'invoice_id',
64- string='Credit Lines',
65- readonly=True),
66- }
67-
68- def copy_data(self, cr, uid, id, default=None, context=None):
69- if default is None:
70- default = {}
71- else:
72- default = default.copy()
73- default = default.copy()
74- default['credit_control_line_ids'] = False
75- return super(AccountInvoice, self).copy_data(
76- cr, uid, id, default=default, context=context)
77
78=== modified file 'account_credit_control/company.py'
79--- account_credit_control/company.py 2013-03-15 14:50:58 +0000
80+++ account_credit_control/company.py 2014-06-24 12:34:19 +0000
81@@ -31,7 +31,7 @@
82 'credit_policy_id': fields.many2one('credit.control.policy',
83 'Credit Control Policy',
84 help=("The Credit Control Policy used on partners"
85- " by default. This setting can be overriden"
86+ " by default. This setting can be overridden"
87 " on partners or invoices.")),
88 }
89
90
91=== modified file 'account_credit_control/data.xml'
92--- account_credit_control/data.xml 2013-07-12 13:03:20 +0000
93+++ account_credit_control/data.xml 2014-06-24 12:34:19 +0000
94@@ -25,6 +25,21 @@
95 <field name="do_nothing" eval="1"/>
96 </record>
97
98+ <!-- no follow policy -->
99+ <record model="credit.control.policy.level"
100+ id="no_follow_1">
101+ <field name="name">No follow</field>
102+ <field name="level" eval="1"/>
103+ <field name="computation_mode">net_days</field>
104+ <field name="delay_days" eval="0"/>
105+ <field name="email_template_id" ref="email_template_credit_control_base"/>
106+ <field name="policy_id" ref="credit_control_no_follow"/>
107+ <field name="channel">email</field>
108+ <field name="custom_text">Manual no follow</field>
109+
110+ <field name="custom_mail_text">Manual no follow</field>
111+ </record>
112+
113 <!-- policy 1 -->
114 <record model="credit.control.policy"
115 id="credit_control_3_time">
116
117=== modified file 'account_credit_control/i18n/de.po'
118--- account_credit_control/i18n/de.po 2013-09-12 14:39:17 +0000
119+++ account_credit_control/i18n/de.po 2014-06-24 12:34:19 +0000
120@@ -984,10 +984,10 @@
121 #: help:res.company,credit_policy_id:0
122 msgid ""
123 "The Credit Control Policy used on partners by default. This setting can be "
124-"overriden on partners or invoices."
125+"overridden on partners or invoices."
126 msgstr ""
127 "The Credit Control Policy used on partners by default. This setting can be "
128-"overriden on partners or invoices."
129+"overridden on partners or invoices."
130
131 #. module: account_credit_control
132 #: field:credit.control.line,policy_level_id:0
133
134=== modified file 'account_credit_control/i18n/en.po'
135--- account_credit_control/i18n/en.po 2013-09-12 14:39:17 +0000
136+++ account_credit_control/i18n/en.po 2014-06-24 12:34:19 +0000
137@@ -984,10 +984,10 @@
138 #: help:res.company,credit_policy_id:0
139 msgid ""
140 "The Credit Control Policy used on partners by default. This setting can be "
141-"overriden on partners or invoices."
142+"overridden on partners or invoices."
143 msgstr ""
144 "The Credit Control Policy used on partners by default. This setting can be "
145-"overriden on partners or invoices."
146+"overridden on partners or invoices."
147
148 #. module: account_credit_control
149 #: field:credit.control.line,policy_level_id:0
150@@ -1042,4 +1042,4 @@
151 #. module: account_credit_control
152 #: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
153 msgid "If you have any question, do not hesitate to contact us."
154-msgstr "If you have any question, do not hesitate to contact us."
155\ No newline at end of file
156+msgstr "If you have any question, do not hesitate to contact us."
157
158=== modified file 'account_credit_control/i18n/es.po'
159--- account_credit_control/i18n/es.po 2013-09-12 14:39:17 +0000
160+++ account_credit_control/i18n/es.po 2014-06-24 12:34:19 +0000
161@@ -982,10 +982,10 @@
162 #: help:res.company,credit_policy_id:0
163 msgid ""
164 "The Credit Control Policy used on partners by default. This setting can be "
165-"overriden on partners or invoices."
166+"overridden on partners or invoices."
167 msgstr ""
168 "The Credit Control Policy used on partners by default. This setting can be "
169-"overriden on partners or invoices."
170+"overridden on partners or invoices."
171
172 #. module: account_credit_control
173 #: field:credit.control.line,policy_level_id:0
174@@ -1040,4 +1040,4 @@
175 #. module: account_credit_control
176 #: report:addons/account_credit_control/report/credit_control_summary.html.mako:211
177 msgid "If you have any question, do not hesitate to contact us."
178-msgstr "If you have any question, do not hesitate to contact us."
179\ No newline at end of file
180+msgstr "If you have any question, do not hesitate to contact us."
181
182=== modified file 'account_credit_control/i18n/fr.po'
183--- account_credit_control/i18n/fr.po 2013-09-12 14:39:17 +0000
184+++ account_credit_control/i18n/fr.po 2014-06-24 12:34:19 +0000
185@@ -965,7 +965,7 @@
186 #: help:res.company,credit_policy_id:0
187 msgid ""
188 "The Credit Control Policy used on partners by default. This setting can be "
189-"overriden on partners or invoices."
190+"overridden on partners or invoices."
191 msgstr ""
192 "Politique de relance par défaut du client. (Ce paramétrage peut être "
193 "défini plus spécifiquement au niveau de la facture)."
194
195=== modified file 'account_credit_control/invoice.py'
196--- account_credit_control/invoice.py 2013-09-18 07:59:03 +0000
197+++ account_credit_control/invoice.py 2014-06-24 12:34:19 +0000
198@@ -18,7 +18,7 @@
199 # along with this program. If not, see <http://www.gnu.org/licenses/>.
200 #
201 ##############################################################################
202-from openerp.osv import orm
203+from openerp.osv import orm, fields
204 from openerp.tools.translate import _
205
206
207@@ -26,7 +26,36 @@
208 """Check on cancelling of an invoice"""
209 _inherit = 'account.invoice'
210
211+ _columns = {
212+ 'credit_policy_id':
213+ fields.many2one('credit.control.policy',
214+ 'Credit Control Policy',
215+ help=("The Credit Control Policy used for this "
216+ "invoice. If nothing is defined, it will "
217+ "use the account setting or the partner "
218+ "setting."),
219+ readonly=True,
220+ ),
221+ 'credit_control_line_ids':
222+ fields.one2many('credit.control.line',
223+ 'invoice_id',
224+ string='Credit Lines',
225+ readonly=True),
226+ }
227+
228+ def copy_data(self, cr, uid, id, default=None, context=None):
229+ """Ensure that credit lines and policy are not copied"""
230+ if default is None:
231+ default = {}
232+ else:
233+ default = default.copy()
234+ default['credit_control_line_ids'] = False
235+ default['credit_policy_id'] = False
236+ return super(AccountInvoice, self).copy_data(
237+ cr, uid, id, default=default, context=context)
238+
239 def action_cancel(self, cr, uid, ids, context=None):
240+ """Prevent to cancel invoice related to credit line"""
241 # We will search if this invoice is linked with credit
242 cc_line_obj = self.pool.get('credit.control.line')
243 for invoice_id in ids:
244
245=== modified file 'account_credit_control/line.py'
246--- account_credit_control/line.py 2013-09-04 12:37:34 +0000
247+++ account_credit_control/line.py 2014-06-24 12:34:19 +0000
248@@ -38,9 +38,11 @@
249 _name = "credit.control.line"
250 _description = "A credit control line"
251 _rec_name = "id"
252-
253+ _order = "date DESC"
254 _columns = {
255- 'date': fields.date('Controlling date', required=True),
256+ 'date': fields.date('Controlling date',
257+ required=True,
258+ select=True),
259 # maturity date of related move line we do not use a related field in order to
260 # allow manual changes
261 'date_due': fields.date('Due date',
262@@ -116,6 +118,7 @@
263 string='Level',
264 store=True,
265 readonly=True),
266+ 'manually_overridden': fields.boolean('Manually overridden')
267 }
268
269
270@@ -141,8 +144,27 @@
271 return data
272
273 def create_or_update_from_mv_lines(self, cr, uid, ids, lines,
274- level_id, controlling_date, context=None):
275- """Create or update line based on levels"""
276+ level_id, controlling_date,
277+ check_tolerance=True, context=None):
278+ """Create or update line based on levels
279+
280+ if check_tolerance is true credit line will not be
281+ created if open amount is too small.
282+ eg. we do not want to send a letter for 10 cents
283+ of open amount.
284+
285+ :param lines: move.line id list
286+ :param level_id: credit.control.policy.level id
287+ :param controlling_date: date string of the credit controlling date.
288+ Generally it should be the same
289+ as create date
290+ :param check_tolerance: boolean if True credit line
291+ will not be generated if open amount
292+ is smaller than company defined
293+ tolerance
294+
295+ :returns: list of created credit line ids
296+ """
297 currency_obj = self.pool.get('res.currency')
298 level_obj = self.pool.get('credit.control.policy.level')
299 ml_obj = self.pool.get('account.move.line')
300@@ -164,26 +186,31 @@
301 for line in ml_obj.browse(cr, uid, lines, context):
302
303 open_amount = line.amount_residual_currency
304-
305- if open_amount > tolerance.get(line.currency_id.id, tolerance_base):
306- vals = self._prepare_from_move_line(
307- cr, uid, line, level, controlling_date, open_amount, context=context)
308- line_id = self.create(cr, uid, vals, context=context)
309- line_ids.append(line_id)
310-
311- # when we have lines generated earlier in draft,
312- # on the same level, it means that we have left
313- # them, so they are to be considered as ignored
314- previous_draft_ids = self.search(
315- cr, uid,
316- [('move_line_id', '=', line.id),
317- ('level', '=', level.id),
318- ('state', '=', 'draft'),
319- ('id', '!=', line_id)],
320- context=context)
321- if previous_draft_ids:
322- self.write(cr, uid, previous_draft_ids,
323- {'state': 'ignored'}, context=context)
324+ cur_tolerance = tolerance.get(line.currency_id.id, tolerance_base)
325+ if check_tolerance and open_amount < cur_tolerance:
326+ continue
327+ vals = self._prepare_from_move_line(cr, uid,
328+ line,
329+ level,
330+ controlling_date,
331+ open_amount,
332+ context=context)
333+ line_id = self.create(cr, uid, vals, context=context)
334+ line_ids.append(line_id)
335+
336+ # when we have lines generated earlier in draft,
337+ # on the same level, it means that we have left
338+ # them, so they are to be considered as ignored
339+ previous_draft_ids = self.search(
340+ cr, uid,
341+ [('move_line_id', '=', line.id),
342+ ('policy_level_id', '=', level.id),
343+ ('state', '=', 'draft'),
344+ ('id', '!=', line_id)],
345+ context=context)
346+ if previous_draft_ids:
347+ self.write(cr, uid, previous_draft_ids,
348+ {'state': 'ignored'}, context=context)
349
350 return line_ids
351
352
353=== modified file 'account_credit_control/line_view.xml'
354--- account_credit_control/line_view.xml 2013-03-18 09:21:33 +0000
355+++ account_credit_control/line_view.xml 2014-06-24 12:34:19 +0000
356@@ -10,6 +10,7 @@
357 <field name="date_due"/>
358 <field name="date_sent"/>
359 <field name="level"/>
360+ <field name="manually_overridden"/>
361 <field name="state"/>
362 <field name="channel"/>
363 <field name="invoice_id"/>
364@@ -32,7 +33,7 @@
365 <field name="type">search</field>
366 <field name="arch" type="xml">
367 <search string="Control Credit Lines">
368- <group>
369+ <group string="Filters">
370 <filter name="filter_draft" icon="terp-mail-message-new"
371 string="Draft" domain="[('state', '=', 'draft')]"
372 help="Draft lines have to be triaged."/>
373@@ -48,6 +49,9 @@
374 <filter name="filter_error" icon="terp-gtk-stop" string="Error"
375 domain="[('state', 'in', ('error', 'email_error'))]"
376 help="An error has occured during the sending of the email."/>
377+ <filter name="filter_manual" icon="terp-gtk-stop" string="Manual change"
378+ domain="[('manually_overridden', '=', True)]"
379+ help="The line was deprecated by a manual change of policy on invoice."/>
380 <separator orientation="vertical"/>
381
382 <field name="date"/>
383@@ -89,6 +93,8 @@
384 <separator orientation="vertical"/>
385 <filter domain='[]' context="{'group_by': 'channel'}"
386 icon="terp-document-new" string="Channel"/>
387+ <filter domain='[]' context="{'group_by': 'manually_overridden'}"
388+ icon="terp-document-new" string="Manual change"/>
389 </group>
390 </search>
391 </field>
392@@ -103,6 +109,7 @@
393 <field name="date"/>
394 <field name="date_due"/>
395 <field name="level"/>
396+ <field name="manually_overridden"/>
397 <field name="state"/>
398 <field name="channel"/>
399 <field name="invoice_id"/>
400
401=== modified file 'account_credit_control/partner.py'
402--- account_credit_control/partner.py 2013-09-04 12:29:15 +0000
403+++ account_credit_control/partner.py 2014-06-24 12:34:19 +0000
404@@ -19,6 +19,7 @@
405 #
406 ##############################################################################
407 from openerp.osv import orm, fields
408+from openerp.tools.translate import _
409
410
411 class ResPartner(orm.Model):
412@@ -28,21 +29,47 @@
413 _inherit = "res.partner"
414
415 _columns = {
416- 'credit_policy_id':
417- fields.many2one('credit.control.policy',
418- 'Credit Control Policy',
419- help=("The Credit Control Policy used for this "
420- "partner. This setting can be forced on the "
421- "invoice. If nothing is defined, it will use "
422- "the company setting.")),
423- 'credit_control_line_ids':
424- fields.one2many('credit.control.line',
425- 'invoice_id',
426- string='Credit Control Lines',
427- readonly=True)
428+ 'credit_policy_id': fields.many2one(
429+ 'credit.control.policy',
430+ 'Credit Control Policy',
431+ domain="[('account_ids', 'in', property_account_receivable)]",
432+ help=("The Credit Control Policy used for this "
433+ "partner. This setting can be forced on the "
434+ "invoice. If nothing is defined, it will use "
435+ "the company setting.")
436+ ),
437+ 'credit_control_line_ids': fields.one2many(
438+ 'credit.control.line',
439+ 'invoice_id',
440+ string='Credit Control Lines',
441+ readonly=True
442+ )
443 }
444
445+ def _check_credit_policy(self, cr, uid, part_ids, context=None):
446+ """Ensure that policy on partner are limited to the account policy"""
447+ if isinstance(part_ids, (int, long)):
448+ part_ids = [part_ids]
449+ policy_obj = self.pool['credit.control.policy']
450+ for partner in self.browse(cr, uid, part_ids, context):
451+ if not partner.property_account_receivable or \
452+ not partner.credit_policy_id:
453+ return True
454+ account = partner.property_account_receivable
455+ policy_obj.check_policy_against_account(
456+ cr, uid,
457+ account.id,
458+ partner.credit_policy_id.id,
459+ context=context
460+ )
461+ return True
462+
463+ _constraints = [(_check_credit_policy,
464+ 'The policy must be related to the receivable account',
465+ ['credit_policy_id'])]
466+
467 def copy_data(self, cr, uid, id, default=None, context=None):
468+ """Remove credit lines when copying partner"""
469 if default is None:
470 default = {}
471 else:
472
473=== modified file 'account_credit_control/policy.py'
474--- account_credit_control/policy.py 2014-03-04 07:52:47 +0000
475+++ account_credit_control/policy.py 2014-06-24 12:34:19 +0000
476@@ -18,11 +18,11 @@
477 # along with this program. If not, see <http://www.gnu.org/licenses/>.
478 #
479 ##############################################################################
480-from openerp.osv.orm import Model, fields
481+from openerp.osv import orm, fields
482 from openerp.tools.translate import _
483
484
485-class CreditControlPolicy(Model):
486+class CreditControlPolicy(orm.Model):
487 """Define a policy of reminder"""
488
489 _name = "credit.control.policy"
490@@ -42,7 +42,7 @@
491 'account_ids': fields.many2many('account.account',
492 string='Accounts',
493 required=True,
494- domain="[('reconcile', '=', True)]",
495+ domain="[('type', '=', 'receivable')]",
496 help="This policy will be active only"
497 " for the selected accounts"),
498 'active': fields.boolean('Active'),
499@@ -103,7 +103,10 @@
500 my_obj = self.pool.get(model)
501 move_l_obj = self.pool.get('account.move.line')
502
503- default_domain = self._move_lines_domain(cr, uid, policy, controlling_date, context=context)
504+ default_domain = self._move_lines_domain(cr, uid,
505+ policy,
506+ controlling_date,
507+ context=context)
508 to_add_ids = set()
509 to_remove_ids = set()
510
511@@ -198,15 +201,34 @@
512 if isinstance(policy_id, list):
513 policy_id = policy_id[0]
514 cr.execute("SELECT move_line_id FROM credit_control_line"
515- " WHERE policy_id != %s and move_line_id in %s",
516+ " WHERE policy_id != %s and move_line_id in %s"
517+ " AND manually_overridden IS false",
518 (policy_id, tuple(lines)))
519 res = cr.fetchall()
520 if res:
521 different_lines.update([x[0] for x in res])
522 return different_lines
523
524-
525-class CreditControlPolicyLevel(Model):
526+ def check_policy_against_account(self, cr, uid, account_id, policy_id,
527+ context=None):
528+ """Ensure that the policy corresponds to account relation"""
529+ policy = self.browse(cr, uid, policy_id, context=context)
530+ account = self.pool['account.account'].browse(cr, uid, account_id,
531+ context=context)
532+ policies_id = self.search(cr, uid, [],
533+ context=context)
534+ policies = self.browse(cr, uid, policies_id, context=context)
535+ allowed = [x for x in policies
536+ if account in x.account_ids or x.do_nothing]
537+ if policy not in allowed:
538+ allowed_names = u"\n".join(x.name for x in allowed)
539+ raise orm.except_orm(
540+ _('You can only use a policy set on account %s') % account.name,
541+ _("Please choose one of the following policies:\n %s") % allowed_names)
542+ return True
543+
544+
545+class CreditControlPolicyLevel(orm.Model):
546 """Define a policy level. A level allows to determine if
547 a move line is due and the level of overdue of the line"""
548
549@@ -319,8 +341,11 @@
550 " FROM credit_control_line\n"
551 " WHERE move_line_id = mv_line.id\n"
552 # lines from a previous level with a draft or ignored state
553+ # or manually overridden
554 # have to be generated again for the previous level
555- " AND state not in ('draft', 'ignored'))")
556+ " AND NOT manually_overridden\n"
557+ " AND state NOT IN ('draft', 'ignored'))"
558+ " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n")
559 sql += " AND"
560 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
561 controlling_date, context)
562@@ -346,11 +371,15 @@
563 " WHERE cr_line.id = (SELECT credit_control_line.id FROM credit_control_line\n"
564 " WHERE credit_control_line.move_line_id = mv_line.id\n"
565 " AND state != 'ignored'"
566+ " AND NOT manually_overridden"
567 " ORDER BY credit_control_line.level desc limit 1)\n"
568 " AND cr_line.level = %(previous_level)s\n"
569+ " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n"
570 # lines from a previous level with a draft or ignored state
571+ # or manually overridden
572 # have to be generated again for the previous level
573- " AND cr_line.state not in ('draft', 'ignored')\n"
574+ " AND NOT manually_overridden\n"
575+ " AND cr_line.state NOT IN ('draft', 'ignored')\n"
576 " AND mv_line.id in %(line_ids)s\n")
577 sql += " AND "
578 sql += self._get_sql_date_boundary_for_computation_mode(cr, uid, level,
579
580=== modified file 'account_credit_control/run.py'
581--- account_credit_control/run.py 2013-09-04 12:29:15 +0000
582+++ account_credit_control/run.py 2014-06-24 12:34:19 +0000
583@@ -81,14 +81,24 @@
584
585 def _check_run_date(self, cr, uid, ids, controlling_date, context=None):
586 """Ensure that there is no credit line in the future using controlling_date"""
587- line_obj = self.pool.get('credit.control.line')
588+ run_obj = self.pool['credit.control.run']
589+ runs = run_obj.search(cr, uid, [('date', '>', controlling_date)],
590+ order='date DESC', limit=1, context=context)
591+ if runs:
592+ run = run_obj.browse(cr, uid, runs[0], context=context)
593+ raise orm.except_orm(_('Error'),
594+ _('A run has already been executed more '
595+ 'recently than %s') % (run.date))
596+
597+ line_obj = self.pool['credit.control.line']
598 lines = line_obj.search(cr, uid, [('date', '>', controlling_date)],
599 order='date DESC', limit=1, context=context)
600 if lines:
601 line = line_obj.browse(cr, uid, lines[0], context=context)
602 raise orm.except_orm(_('Error'),
603- _('A run has already been executed more '
604- 'recently than %s') % (line.date))
605+ _('A credit control line more '
606+ 'recent than %s exists at %s') %
607+ (controlling_date, line.date))
608 return True
609
610 def _generate_credit_lines(self, cr, uid, run_id, context=None):
611@@ -110,10 +120,10 @@
612 _('Please select a policy'))
613
614 report = ''
615+ generated_ids = []
616 for policy in policies:
617 if policy.do_nothing:
618 continue
619-
620 lines = policy._get_move_lines_to_process(run.date, context=context)
621 manual_lines = policy._lines_different_policy(lines, context=context)
622 lines.difference_update(manual_lines)
623@@ -125,7 +135,7 @@
624 level_lines = level.get_level_lines(run.date, lines, context=context)
625 policy_generated_ids += cr_line_obj.create_or_update_from_mv_lines(
626 cr, uid, [], list(level_lines), level.id, run.date, context=context)
627-
628+ generated_ids.extend(policy_generated_ids)
629 if policy_generated_ids:
630 report += _("Policy \"%s\" has generated %d Credit Control Lines.\n") % \
631 (policy.name, len(policy_generated_ids))
632@@ -138,6 +148,7 @@
633 'report': report,
634 'manual_ids': [(6, 0, manually_managed_lines)]}
635 run.write(vals, context=context)
636+ return generated_ids
637
638 def generate_credit_lines(self, cr, uid, run_id, context=None):
639 """Generate credit control lines
640@@ -147,7 +158,7 @@
641 """
642 try:
643 cr.execute('SELECT id FROM credit_control_run'
644- ' LIMIT 1 FOR UPDATE NOWAIT')
645+ ' LIMIT 1 FOR UPDATE NOWAIT')
646 except Exception as exc:
647 # in case of exception openerp will do a rollback for us and free the lock
648 raise orm.except_orm(_('Error'),
649
650=== modified file 'account_credit_control/run_view.xml'
651--- account_credit_control/run_view.xml 2013-03-18 09:21:33 +0000
652+++ account_credit_control/run_view.xml 2014-06-24 12:34:19 +0000
653@@ -19,7 +19,8 @@
654 <field name="type">form</field>
655 <field name="arch" type="xml">
656 <form string="Credit control run">
657- <field name="date"/>
658+ <field name="date"
659+ attrs="{'readonly': [('state', '!=', 'draft')]}"/>
660 <newline/>
661 <notebook colspan="4">
662 <page string="Policies">
663
664=== modified file 'account_credit_control/scenarios/features/09_credit_control_run_jul.feature'
665--- account_credit_control/scenarios/features/09_credit_control_run_jul.feature 2014-03-03 11:23:20 +0000
666+++ account_credit_control/scenarios/features/09_credit_control_run_jul.feature 2014-06-24 12:34:19 +0000
667@@ -13,10 +13,10 @@
668 Feature: Ensure that email credit line generation first pass is correct
669
670 @account_credit_control_mark
671- Scenario: mark lines
672- Given there is "draft" credit lines
673- And I mark all draft email to state "to_be_sent"
674- Then the draft line should be in state "to_be_sent"
675+ Scenario: mark lines
676+ Given there is "draft" credit lines
677+ And I mark all draft email to state "to_be_sent"
678+ Then the draft line should be in state "to_be_sent"
679
680 @account_credit_control_run_month
681 Scenario: Create run
682
683=== added file 'account_credit_control/scenarios/features/11_credit_control_manual_setting.feature'
684--- account_credit_control/scenarios/features/11_credit_control_manual_setting.feature 1970-01-01 00:00:00 +0000
685+++ account_credit_control/scenarios/features/11_credit_control_manual_setting.feature 2014-06-24 12:34:19 +0000
686@@ -0,0 +1,42 @@
687+###############################################################################
688+#
689+# OERPScenario, OpenERP Functional Tests
690+# Copyright 2012 Camptocamp SA
691+# Author Nicolas Bessi
692+##############################################################################
693+
694+# Features Generic tags (none for all)
695+##############################################################################
696+
697+@account_credit_control @account_credit_control_run @account_credit_control_run_change_level
698+
699+Feature: Ensure that manually changing an invoice level feature works as expected
700+
701+ @account_credit_control_change_level
702+ Scenario: Change level
703+ Given I change level for invoice "SAJ/2014/0004" to "10 days net" of policy "3 time policy"
704+ Then wizard selected move lines should be:
705+ | name |
706+ | SI_4 |
707+ When I confirm the level change
708+ And I should have "3" credit control lines overridden
709+ And one new credit control line of level "10 days net" related to invoice "SAJ/2014/0004"
710+ Then I force date of generated credit line to "2013-09-15"
711+
712+ @account_credit_control_run_month_sept
713+ Scenario: Create run
714+ Given there is "draft" credit lines
715+ And I mark all draft email to state "to_be_sent"
716+ Then the draft line should be in state "to_be_sent"
717+ Given I need a "credit.control.run" with oid: credit_control.manual_change
718+ And having:
719+ | name | value |
720+ | date | 2013-09-30 |
721+ When I launch the credit run
722+ Then my credit run should be in state "done"
723+
724+ @account_credit_control_manual_next_step
725+ Scenario: Check manually managed line on run
726+ Given the invoice "SAJ/2014/0004" with manual changes
727+ And the invoice has "1" line of level "1" for policy "3 time policy"
728+ And the invoice has "1" line of level "2" for policy "3 time policy"
729
730=== modified file 'account_credit_control/scenarios/features/steps/account_credit_control.py'
731--- account_credit_control/scenarios/features/steps/account_credit_control.py 2014-03-04 07:52:47 +0000
732+++ account_credit_control/scenarios/features/steps/account_credit_control.py 2014-06-24 12:34:19 +0000
733@@ -142,4 +142,4 @@
734 ('state', 'in', ('draft', 'ignored'))])
735 assert_equal(len(to_check), int(number), msg="More than %s found" % number)
736 lines = model('credit.control.line').browse(to_check)
737- assert ['ignored', 'draft'] == lines.state
738+ assert set(['ignored', 'draft']) == set(lines.state)
739
740=== added file 'account_credit_control/scenarios/features/steps/account_credit_control_changer.py'
741--- account_credit_control/scenarios/features/steps/account_credit_control_changer.py 1970-01-01 00:00:00 +0000
742+++ account_credit_control/scenarios/features/steps/account_credit_control_changer.py 2014-06-24 12:34:19 +0000
743@@ -0,0 +1,76 @@
744+# -*- coding: utf-8 -*-
745+@given(u'I change level for invoice "{invoice_name}" to "{level_name}" of policy "{policy_name}"')
746+def impl(ctx, invoice_name, level_name, policy_name):
747+ invoice = model('account.invoice').get([('number', '=', invoice_name)])
748+ assert_true(invoice, msg='No invoices found')
749+ level = model('credit.control.policy.level').get([('name', '=', level_name)])
750+ assert_true(level, 'level not found')
751+ policy = model('credit.control.policy').get([('name', '=', policy_name)])
752+ assert_true(policy, 'Policy not found')
753+ assert_equal(policy.id, level.policy_id.id)
754+ context = {'active_ids': [invoice.id]}
755+ data = {'new_policy_id': policy.id,
756+ 'new_policy_level_id': level.id}
757+ wizard = model('credit.control.policy.changer').create(data, context=context)
758+ ctx.wizard = wizard
759+
760+@then(u'wizard selected move lines should be')
761+def impl(ctx):
762+ assert_true(ctx.wizard)
763+ names = [x.name for x in ctx.wizard.move_line_ids]
764+ for line in ctx.table:
765+ assert_in(line['name'], names)
766+
767+@when(u'I confirm the level change')
768+def impl(ctx):
769+ assert_true(ctx.wizard)
770+ ctx.wizard.set_new_policy()
771+
772+@when(u'I should have "{line_number:d}" credit control lines overridden')
773+def impl(ctx, line_number):
774+ assert_true(ctx.wizard)
775+ move_ids = [x.id for x in ctx.wizard.move_line_ids]
776+ overridden = model('credit.control.line').search([('move_line_id', 'in', move_ids),
777+ ('manually_overridden', '=', True)])
778+# assert len(overridden) == line_number
779+
780+@when(u'one new credit control line of level "{level_name}" related to invoice "{invoice_name}"')
781+def impl(ctx, level_name, invoice_name):
782+ invoice = model('account.invoice').get([('number', '=', invoice_name)])
783+ assert_true(invoice, msg='No invoices found')
784+ level = model('credit.control.policy.level').get([('name', '=', level_name)])
785+ assert_true(level, 'level not found')
786+ assert_true(ctx.wizard)
787+ move_ids = [x.id for x in ctx.wizard.move_line_ids]
788+ created_id = model('credit.control.line').search([('move_line_id', 'in', move_ids),
789+ ('manually_overridden', '=', False)])
790+
791+ assert len(created_id) == 1
792+ created = model('credit.control.line').get(created_id[0])
793+ ctx.created = created
794+ assert_equal(created.policy_level_id.id, level.id)
795+ assert_equal(created.invoice_id.id, invoice.id)
796+ assert_equal(created.invoice_id.credit_policy_id.id, level.policy_id.id)
797+
798+@then(u'I force date of generated credit line to "{date}"')
799+def impl(ctx, date):
800+ assert_true(ctx.created)
801+ ctx.created.write({'date': date})
802+
803+@given(u'the invoice "{invoice_name}" with manual changes')
804+def impl(ctx, invoice_name):
805+ invoice = model('account.invoice').get([('number', '=', invoice_name)])
806+ assert_true(invoice, msg='No invoices found')
807+ man_lines = (x for x in invoice.credit_control_line_ids if x.manually_overridden)
808+ assert_true(next(man_lines, None), 'No manual change on the invoice')
809+ ctx.invoice = invoice
810+
811+@given(u'the invoice has "{line_number:d}" line of level "{level:d}" for policy "{policy_name}"')
812+def impl(ctx, line_number, level, policy_name):
813+ assert_true(ctx.invoice)
814+ policy = model('credit.control.policy').get([('name', '=', policy_name)])
815+ assert_true(policy)
816+ lines = model('credit.control.line').search([('invoice_id', '=', ctx.invoice.id),
817+ ('level', '=', level),
818+ ('policy_id', '=', policy.id)])
819+ assert_equal(len(lines), line_number)
820
821=== modified file 'account_credit_control/wizard/__init__.py'
822--- account_credit_control/wizard/__init__.py 2012-11-07 12:08:18 +0000
823+++ account_credit_control/wizard/__init__.py 2014-06-24 12:34:19 +0000
824@@ -22,3 +22,4 @@
825 from . import credit_control_marker
826 from . import credit_control_printer
827 from . import credit_control_communication
828+from . import credit_control_policy_changer
829
830=== modified file 'account_credit_control/wizard/credit_control_communication.py'
831--- account_credit_control/wizard/credit_control_communication.py 2013-07-12 10:44:13 +0000
832+++ account_credit_control/wizard/credit_control_communication.py 2014-06-24 12:34:19 +0000
833@@ -80,14 +80,18 @@
834 return cr_l_ids
835
836 def _generate_comm_from_credit_line_ids(self, cr, uid, line_ids, context=None):
837+ """Aggregate credit control line by partner, level, and currency
838+ It also generate a communication object per aggregation.
839+ """
840 if not line_ids:
841 return []
842 comms = []
843- sql = ("SELECT distinct partner_id, policy_level_id, credit_control_policy_level.level"
844+ sql = ("SELECT distinct partner_id, policy_level_id, "
845+ " credit_control_line.currency_id, credit_control_policy_level.level"
846 " FROM credit_control_line JOIN credit_control_policy_level "
847 " ON (credit_control_line.policy_level_id = credit_control_policy_level.id)"
848 " WHERE credit_control_line.id in %s"
849- " ORDER by credit_control_policy_level.level")
850+ " ORDER by credit_control_policy_level.level, credit_control_line.currency_id")
851
852 cr.execute(sql, (tuple(line_ids),))
853 res = cr.dictfetchall()
854@@ -101,8 +105,6 @@
855 data['partner_id'] = level_assoc['partner_id']
856 data['current_policy_level'] = level_assoc['policy_level_id']
857 comm_id = self.create(cr, uid, data, context=context)
858-
859-
860 comms.append(self.browse(cr, uid, comm_id, context=context))
861 return comms
862
863
864=== modified file 'account_credit_control/wizard/credit_control_emailer_view.xml'
865--- account_credit_control/wizard/credit_control_emailer_view.xml 2013-03-18 09:21:33 +0000
866+++ account_credit_control/wizard/credit_control_emailer_view.xml 2014-06-24 12:34:19 +0000
867@@ -4,17 +4,27 @@
868 <record id="credit_line_emailer_form" model="ir.ui.view">
869 <field name="name">credit.line.emailer.form</field>
870 <field name="model">credit.control.emailer</field>
871- <field name="type">form</field>
872 <field name="arch" type="xml">
873- <form string="Mailer">
874+ <form string="Mailer" version="7.0">
875 <separator string="Send emails for the selected lines" colspan="4"/>
876 <newline/>
877- <field name="line_ids" colspan="4" nolabel="1" />
878+ <notebook>
879+ <page string="Lines">
880+ <field name="line_ids" colspan="4" nolabel="1" />
881+ </page>
882+ </notebook>
883 <newline/>
884- <group colspan="4">
885- <button special="cancel" string="Cancel" icon='gtk-cancel'/>
886- <button name="email_lines" string="Send the emails" type="object" icon="gtk-execute"/>
887- </group>
888+ <footer>
889+ <button class="oe_highlight"
890+ name="email_lines"
891+ string="Send the emails"
892+ type="object"/>
893+ or
894+ <button
895+ class="oe_link"
896+ special="cancel"
897+ string="Cancel"/>
898+ </footer>
899 </form>
900 </field>
901 </record>
902
903=== modified file 'account_credit_control/wizard/credit_control_marker_view.xml'
904--- account_credit_control/wizard/credit_control_marker_view.xml 2013-03-18 09:21:33 +0000
905+++ account_credit_control/wizard/credit_control_marker_view.xml 2014-06-24 12:34:19 +0000
906@@ -4,19 +4,34 @@
907 <record id="credit_line_marker_form" model="ir.ui.view">
908 <field name="name">credit.line.marker.form</field>
909 <field name="model">credit.control.marker</field>
910- <field name="type">form</field>
911 <field name="arch" type="xml">
912- <form string="Lines marker">
913- <separator string="Change the state of the selected lines." colspan="4"/>
914+ <form string="Lines marker" version="7.0">
915+ <separator string="Change the state of the selected lines" colspan="4"/>
916+ <newline/>
917 <label string="Warning: you will maybe not be able to revert this operation." colspan="4"></label>
918 <newline/>
919- <field name="name" colspan="4"/>
920- <field name="line_ids" colspan="4" nolabel="1"/>
921- <newline/>
922- <group colspan="4">
923- <button special="cancel" string="Cancel" icon='gtk-cancel'/>
924- <button name="mark_lines" string="Change Lines' State" type="object" icon="gtk-execute"/>
925+ <group>
926+ <group><field name="name"/></group>
927+ <group></group>
928 </group>
929+ <notebook>
930+ <page string="Lines">
931+ <field name="line_ids" colspan="4" nolabel="1"/>
932+ </page>
933+ </notebook>
934+ <newline/>
935+ <footer>
936+ <button
937+ class="oe_highlight"
938+ name="mark_lines"
939+ string="Change Lines' State"
940+ type="object"/>
941+ or
942+ <button
943+ class="oe_link"
944+ special="cancel"
945+ string="Cancel"/>
946+ </footer>
947 </form>
948 </field>
949 </record>
950
951=== added file 'account_credit_control/wizard/credit_control_policy_changer.py'
952--- account_credit_control/wizard/credit_control_policy_changer.py 1970-01-01 00:00:00 +0000
953+++ account_credit_control/wizard/credit_control_policy_changer.py 2014-06-24 12:34:19 +0000
954@@ -0,0 +1,178 @@
955+# -*- coding: utf-8 -*-
956+##############################################################################
957+#
958+# Author: Nicolas Bessi
959+# Copyright 2014 Camptocamp SA
960+#
961+# This program is free software: you can redistribute it and/or modify
962+# it under the terms of the GNU Affero General Public License as
963+# published by the Free Software Foundation, either version 3 of the
964+# License, or (at your option) any later version.
965+#
966+# This program is distributed in the hope that it will be useful,
967+# but WITHOUT ANY WARRANTY; without even the implied warranty of
968+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
969+# GNU Affero General Public License for more details.
970+#
971+# You should have received a copy of the GNU Affero General Public License
972+# along with this program. If not, see <http://www.gnu.org/licenses/>.
973+#
974+##############################################################################
975+import logging
976+from openerp.tools.translate import _
977+from openerp.osv import orm, fields
978+logger = logging.getLogger(__name__)
979+
980+
981+class credit_control_policy_changer(orm.TransientModel):
982+ """Wizard that is run from invoices and allows to set manually a policy
983+ Policy are actually apply to related move lines availabe
984+ in selection widget
985+
986+ """
987+ _name = "credit.control.policy.changer"
988+ _columns = {
989+ 'new_policy_id': fields.many2one('credit.control.policy',
990+ 'New Policy to Apply',
991+ required=True),
992+ 'new_policy_level_id': fields.many2one('credit.control.policy.level',
993+ 'New level to apply',
994+ required=True),
995+ # Only used to provide dynamic filtering on form
996+ 'do_nothing': fields.boolean('No follow policy'),
997+ 'move_line_ids': fields.many2many('account.move.line',
998+ rel='credit_changer_ml_rel',
999+ string='Move line to change'),
1000+ }
1001+
1002+ def _get_default_lines(self, cr, uid, context=None):
1003+ """Get default lines for fields move_line_ids
1004+ of wizard. Only take lines that are on the same account
1005+ and move of the invoice and not reconciled
1006+
1007+ :return: list of compliant move line ids
1008+
1009+ """
1010+ if context is None:
1011+ context = {}
1012+ active_ids = context.get('active_ids')
1013+ selected_line_ids = []
1014+ inv_model = self.pool['account.invoice']
1015+ move_line_model = self.pool['account.move.line']
1016+ if not active_ids:
1017+ return False
1018+ # raise ValueError('No active_ids passed in context')
1019+ for invoice in inv_model.browse(cr, uid, active_ids, context=context):
1020+ if invoice.type in ('in_invoice', 'in_refund', 'out_refund'):
1021+ raise orm.except_orm(_('User error'),
1022+ _('Please use wizard on cutomer invoices'))
1023+
1024+ domain = [('account_id', '=', invoice.account_id.id),
1025+ ('move_id', '=', invoice.move_id.id),
1026+ ('reconcile_id', '=', False)]
1027+ move_ids = move_line_model.search(cr, uid, domain, context=context)
1028+ selected_line_ids.extend(move_ids)
1029+ return selected_line_ids
1030+
1031+ _defaults = {'move_line_ids': _get_default_lines}
1032+
1033+ def onchange_policy_id(self, cr, uid, ids, new_policy_id, context=None):
1034+ if not new_policy_id:
1035+ return {}
1036+ policy = self.pool['credit.control.policy'].browse(cr, uid,
1037+ new_policy_id,
1038+ context=context)
1039+ return {'value': {'do_nothing': policy.do_nothing}}
1040+
1041+ def _mark_as_overridden(self, cr, uid, move_lines, context=None):
1042+ """Mark `move_lines` related credit control line as overridden
1043+ This is done by setting manually_overridden fields to True
1044+
1045+ :param move_lines: move line to mark as overridden
1046+
1047+ :retun: list of credit line ids that where marked as overridden
1048+
1049+ """
1050+ credit_model = self.pool['credit.control.line']
1051+ domain = [('move_line_id', 'in', [x.id for x in move_lines])]
1052+ credits_ids = credit_model.search(cr, uid, domain, context=context)
1053+ credit_model.write(cr, uid,
1054+ credits_ids,
1055+ {'manually_overridden': True},
1056+ context)
1057+ return credits_ids
1058+
1059+ def _set_invoice_policy(self, cr, uid, move_line_ids, policy,
1060+ context=None):
1061+ """Force policy on invoice"""
1062+ invoice_model = self.pool['account.invoice']
1063+ invoice_ids = set([x.invoice.id for x in move_line_ids if x.invoice])
1064+ invoice_model.write(cr, uid, list(invoice_ids),
1065+ {'credit_policy_id': policy.id},
1066+ context=context)
1067+
1068+ def _check_accounts_policies(self, cr, uid, lines, policy, context=None):
1069+ policy_obj = self.pool['credit.control.policy']
1070+ for line in lines:
1071+ policy_obj.check_policy_against_account(
1072+ cr, uid,
1073+ line.account_id.id,
1074+ policy.id,
1075+ context=context
1076+ )
1077+ return True
1078+
1079+ def set_new_policy(self, cr, uid, wizard_id, context=None):
1080+ """Set new policy on an invoice.
1081+
1082+ This is done by creating a new credit control line
1083+ related to the move line and the policy setted in
1084+ the wizard form
1085+
1086+ :return: ir.actions.act_windows dict
1087+
1088+ """
1089+ assert len(wizard_id) == 1, "Only one id expected"
1090+ wizard_id = wizard_id[0]
1091+
1092+ credit_line_model = self.pool['credit.control.line']
1093+ ir_model = self.pool['ir.model.data']
1094+ ui_act_model = self.pool['ir.actions.act_window']
1095+ wizard = self.browse(cr, uid, wizard_id, context=context)
1096+ controlling_date = fields.date.today()
1097+ self._check_accounts_policies(
1098+ cr,
1099+ uid,
1100+ wizard.move_line_ids,
1101+ wizard.new_policy_id)
1102+ self._mark_as_overridden(cr,
1103+ uid,
1104+ wizard.move_line_ids,
1105+ context=context)
1106+ # As disscused with business expert
1107+ # draft lines should be passed to ignored
1108+ # if same level as the new one
1109+ # As it is a manual action
1110+ # We also ignore rounding tolerance
1111+ generated_ids = None
1112+ generated_ids = credit_line_model.create_or_update_from_mv_lines(
1113+ cr, uid, [],
1114+ [x.id for x in wizard.move_line_ids],
1115+ wizard.new_policy_level_id.id,
1116+ controlling_date,
1117+ check_tolerance=False,
1118+ context=None
1119+ )
1120+ self._set_invoice_policy(cr, uid,
1121+ wizard.move_line_ids,
1122+ wizard.new_policy_id,
1123+ context=context)
1124+ if not generated_ids:
1125+ return {}
1126+ view_id = ir_model.get_object_reference(cr, uid,
1127+ "account_credit_control",
1128+ "credit_control_line_action")
1129+ assert view_id, 'No view found'
1130+ action = ui_act_model.read(cr, uid, view_id[1], context=context)
1131+ action['domain'] = [('id', 'in', generated_ids)]
1132+ return action
1133
1134=== added file 'account_credit_control/wizard/credit_control_policy_changer_view.xml'
1135--- account_credit_control/wizard/credit_control_policy_changer_view.xml 1970-01-01 00:00:00 +0000
1136+++ account_credit_control/wizard/credit_control_policy_changer_view.xml 2014-06-24 12:34:19 +0000
1137@@ -0,0 +1,65 @@
1138+<?xml version="1.0" encoding="utf-8"?>
1139+<openerp>
1140+ <data>
1141+ <record id="credit_control_policy_changer_form" model="ir.ui.view">
1142+ <field name="name">credit control policy form</field>
1143+ <field name="model">credit.control.policy.changer</field>
1144+ <field name="arch" type="xml">
1145+ <form version="7.0" string="Set current credit level">
1146+ <separator string="Change the overdue level of current invoice" colspan="4"/>
1147+ <label string="This wizard will let you set the overdue policy and level for selected invoices"/>
1148+ <newline/>
1149+ <group>
1150+ <group>
1151+ <field name="new_policy_id"
1152+ on_change="onchange_policy_id(new_policy_id)"/>
1153+ <field name="do_nothing"
1154+ invisible="1"/>
1155+ <field name="new_policy_level_id"
1156+ domain="[('policy_id', '=', new_policy_id)]"/>
1157+ </group>
1158+ <group></group>
1159+ </group>
1160+ <notebook colspan="4">
1161+ <page string="Move lines to affect">
1162+ <field name="move_line_ids"/>
1163+ </page>
1164+ </notebook>
1165+ <footer>
1166+ <button class="oe_highlight"
1167+ name="set_new_policy"
1168+ string="Set new policy"
1169+ type="object"/>
1170+ or
1171+ <button class="oe_link"
1172+ special="cancel"
1173+ string="Cancel"/>
1174+ </footer>
1175+ </form>
1176+
1177+ </field>
1178+ </record>
1179+
1180+ <!-- for button -->
1181+ <record id="action_wizard_credit_policy_changer" model="ir.actions.act_window">
1182+ <field name="name">Change current credit policy</field>
1183+ <field name="res_model">credit.control.policy.changer</field>
1184+ <field name="src_model">account.invoice</field>
1185+ <field name="view_type">form</field>
1186+ <field name="view_mode">form</field>
1187+ <field name="view_id" ref="credit_control_policy_changer_form"/>
1188+ <field name="target">new</field>
1189+ <field name="help">Allows to manually change credit level</field>
1190+ </record>
1191+
1192+ <!-- for menu -->
1193+ <act_window name="Change current credit policy"
1194+ res_model="credit.control.policy.changer"
1195+ src_model="account.invoice"
1196+ view_mode="form"
1197+ target="new"
1198+ key2="client_action_multi"
1199+ id="action_wizard_credit_policy_changer_menu_action"/>
1200+
1201+ </data>
1202+</openerp>
1203
1204=== modified file 'account_credit_control/wizard/credit_control_printer.py'
1205--- account_credit_control/wizard/credit_control_printer.py 2013-06-08 09:34:35 +0000
1206+++ account_credit_control/wizard/credit_control_printer.py 2014-06-24 12:34:19 +0000
1207@@ -23,6 +23,7 @@
1208 from openerp.osv import orm, fields
1209 from openerp.tools.translate import _
1210
1211+
1212 class CreditControlPrinter(orm.TransientModel):
1213 """Print lines"""
1214
1215@@ -34,15 +35,16 @@
1216 if context is None:
1217 context = {}
1218 res = False
1219- if (context.get('active_model') == 'credit.control.line' and
1220- context.get('active_ids')):
1221- res = context['active_ids']
1222+ if context.get('active_model') != 'credit.control.line':
1223+ return res
1224+ res = context.get('active_ids', False)
1225 return res
1226
1227 _columns = {
1228 'mark_as_sent': fields.boolean('Mark letter lines as sent',
1229 help="Only letter lines will be marked."),
1230 'report_file': fields.binary('Generated Report', readonly=True),
1231+ 'report_name': fields.char('Report name'),
1232 'state': fields.char('state', size=32),
1233 'line_ids': fields.many2many(
1234 'credit.control.line',
1235@@ -62,6 +64,12 @@
1236 ('channel', '=', 'letter')]
1237 return line_obj.search(cr, uid, domain, context=context)
1238
1239+ def _credit_line_predicate(self, cr, uid, line_record, context=None):
1240+ return True
1241+
1242+ def _get_line_ids(self, cr, uid, lines, predicate, context=None):
1243+ return [l.id for l in lines if predicate(cr, uid, l, context)]
1244+
1245 def print_lines(self, cr, uid, wiz_id, context=None):
1246 assert not (isinstance(wiz_id, list) and len(wiz_id) > 1), \
1247 "wiz_id: only one id expected"
1248@@ -71,14 +79,22 @@
1249 form = self.browse(cr, uid, wiz_id, context)
1250
1251 if not form.line_ids and not form.print_all:
1252- raise orm.except_orm(_('Error'), _('No credit control lines selected.'))
1253-
1254- line_ids = [l.id for l in form.line_ids]
1255+ raise orm.except_orm(_('Error'),
1256+ _('No credit control lines selected.'))
1257+
1258+ line_ids = self._get_line_ids(cr,
1259+ uid,
1260+ form.line_ids,
1261+ self._credit_line_predicate,
1262+ context=context)
1263+
1264 comms = comm_obj._generate_comm_from_credit_line_ids(cr, uid, line_ids,
1265 context=context)
1266 report_file = comm_obj._generate_report(cr, uid, comms, context=context)
1267
1268- form.write({'report_file': base64.b64encode(report_file), 'state': 'done'})
1269+ form.write({'report_file': base64.b64encode(report_file),
1270+ 'report_name': 'credit_control_esr_bvr_%s.pdf' % fields.datetime.now(),
1271+ 'state': 'done'})
1272
1273 if form.mark_as_sent:
1274 comm_obj._mark_credit_line_as_sent(cr, uid, comms, context=context)
1275
1276=== modified file 'account_credit_control/wizard/credit_control_printer_view.xml'
1277--- account_credit_control/wizard/credit_control_printer_view.xml 2013-03-18 09:21:33 +0000
1278+++ account_credit_control/wizard/credit_control_printer_view.xml 2014-06-24 12:34:19 +0000
1279@@ -6,21 +6,34 @@
1280 <field name="model">credit.control.printer</field>
1281 <field name="type">form</field>
1282 <field name="arch" type="xml">
1283- <form string="Lines report">
1284- <separator colspan="4" string="Print the selected lines"/>
1285- <newline/>
1286- <field name="mark_as_sent" colspan="4" attrs="{'invisible': [('state', '=', 'done')]}"/>
1287- <newline/>
1288+ <form string="Lines report" version="7.0">
1289+ <separator string="Print the selected lines" colspan="4"/>
1290+ <newline/>
1291+ <group>
1292+ <field name="mark_as_sent"
1293+ colspan="4"
1294+ attrs="{'invisible': [('state', '=', 'done')]}"/>
1295+ </group>
1296+ <newline/>
1297+ <notebook>
1298+ <page string="Lines" attrs="{'invisible': [('state', '=', 'done')]}">
1299 <field name="line_ids" colspan="4" nolabel="1"
1300 attrs="{'invisible': [('state', '=', 'done')]}" />
1301- <field name="report_file" colspan="4" attrs="{'invisible': [('state', '!=', 'done')]}"/>
1302+ </page>
1303+ </notebook>
1304+ <field name="report_name"
1305+ invisible="1"/>
1306+ <field name="report_file"
1307+ colspan="4"
1308+ filename="report_name"
1309+ attrs="{'invisible': [('state', '!=', 'done')]}"/>
1310 <field name="state" invisible="1" />
1311 <newline/>
1312- <group colspan="4">
1313- <button special="cancel" string="Cancel" icon='gtk-cancel' attrs="{'invisible': [('state', '==', 'done')]}"/>
1314- <button name="print_lines" string="Print" type="object" icon="gtk-execute" attrs="{'invisible': [('state', '==', 'done')]}"/>
1315+ <footer>
1316+ <button class="oe_highlight" name="print_lines" string="Print" type="object" attrs="{'invisible': [('state', '=', 'done')]}"/>
1317+ <button special="cancel" string="Cancel" icon='gtk-cancel' attrs="{'invisible': [('state', '=', 'done')]}"/>
1318 <button special="cancel" string="Close" icon='gtk-close' attrs="{'invisible': [('state', '!=', 'done')]}"/>
1319- </group>
1320+ </footer>
1321 </form>
1322 </field>
1323 </record>
1324
1325=== added directory 'account_credit_control_dunning_fees'
1326=== added file 'account_credit_control_dunning_fees/__init__.py'
1327--- account_credit_control_dunning_fees/__init__.py 1970-01-01 00:00:00 +0000
1328+++ account_credit_control_dunning_fees/__init__.py 2014-06-24 12:34:19 +0000
1329@@ -0,0 +1,21 @@
1330+# -*- coding: utf-8 -*-
1331+##############################################################################
1332+#
1333+# Author: Nicolas Bessi
1334+# Copyright 2014 Camptocamp SA
1335+#
1336+# This program is free software: you can redistribute it and/or modify
1337+# it under the terms of the GNU Affero General Public License as
1338+# published by the Free Software Foundation, either version 3 of the
1339+# License, or (at your option) any later version.
1340+#
1341+# This program is distributed in the hope that it will be useful,
1342+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1343+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1344+# GNU Affero General Public License for more details.
1345+#
1346+# You should have received a copy of the GNU Affero General Public License
1347+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1348+#
1349+##############################################################################
1350+from . import model
1351
1352=== added file 'account_credit_control_dunning_fees/__openerp__.py'
1353--- account_credit_control_dunning_fees/__openerp__.py 1970-01-01 00:00:00 +0000
1354+++ account_credit_control_dunning_fees/__openerp__.py 2014-06-24 12:34:19 +0000
1355@@ -0,0 +1,71 @@
1356+# -*- coding: utf-8 -*-
1357+##############################################################################
1358+#
1359+# Author: Nicolas Bessi
1360+# Copyright 2014 Camptocamp SA
1361+#
1362+# This program is free software: you can redistribute it and/or modify
1363+# it under the terms of the GNU Affero General Public License as
1364+# published by the Free Software Foundation, either version 3 of the
1365+# License, or (at your option) any later version.
1366+#
1367+# This program is distributed in the hope that it will be useful,
1368+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1369+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1370+# GNU Affero General Public License for more details.
1371+#
1372+# You should have received a copy of the GNU Affero General Public License
1373+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1374+#
1375+##############################################################################
1376+{'name': 'Credit control dunning fees',
1377+ 'version': '0.1.0',
1378+ 'author': 'Camptocamp',
1379+ 'maintainer': 'Camptocamp',
1380+ 'category': 'Accounting',
1381+ 'complexity': 'normal',
1382+ 'depends': ['account_credit_control'],
1383+ 'description': """
1384+Dunning Fees for Credit Control
1385+===============================
1386+
1387+This extention of credit control adds the notion of dunning fees
1388+on credit control lines.
1389+
1390+Configuration
1391+-------------
1392+For release 0.1 only fixed fees are supported.
1393+
1394+You can specifiy a fixed fees amount, a product and a currency
1395+on the credit control level form.
1396+
1397+The amount will be used as fees values the currency will determine
1398+the currency of the fee. If the credit control line has not the
1399+same currency as the fees currency, fees will be converted to
1400+the credit control line currency.
1401+
1402+The product is used to compute taxes in reconciliation process.
1403+
1404+Run
1405+---
1406+Fees are automatically computed on credit run and saved
1407+on the generated credit lines.
1408+
1409+Fees can be manually edited as long credit line is draft
1410+
1411+Credit control Summary report includes a new fees column.
1412+-------
1413+Support of fees price list
1414+
1415+""",
1416+ 'website': 'http://www.camptocamp.com',
1417+ 'data': ['view/policy_view.xml',
1418+ 'view/line_view.xml',
1419+ 'report/report.xml'],
1420+ 'demo': [],
1421+ 'test': [],
1422+ 'installable': True,
1423+ 'auto_install': False,
1424+ 'license': 'AGPL-3',
1425+ 'application': False,
1426+}
1427
1428=== added directory 'account_credit_control_dunning_fees/i18n'
1429=== added file 'account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot'
1430--- account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot 1970-01-01 00:00:00 +0000
1431+++ account_credit_control_dunning_fees/i18n/account_credit_control_dunning_fees.pot 2014-06-24 12:34:19 +0000
1432@@ -0,0 +1,81 @@
1433+# Translation of OpenERP Server.
1434+# This file contains the translation of the following modules:
1435+# * account_credit_control_dunning_fees
1436+#
1437+msgid ""
1438+msgstr ""
1439+"Project-Id-Version: OpenERP Server 7.0\n"
1440+"Report-Msgid-Bugs-To: \n"
1441+"POT-Creation-Date: 2014-05-07 11:44+0000\n"
1442+"PO-Revision-Date: 2014-05-07 11:44+0000\n"
1443+"Last-Translator: <>\n"
1444+"Language-Team: \n"
1445+"MIME-Version: 1.0\n"
1446+"Content-Type: text/plain; charset=UTF-8\n"
1447+"Content-Transfer-Encoding: \n"
1448+"Plural-Forms: \n"
1449+
1450+#. module: account_credit_control_dunning_fees
1451+#: code:_description:0
1452+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
1453+#, python-format
1454+msgid "A credit control line"
1455+msgstr ""
1456+
1457+#. module: account_credit_control_dunning_fees
1458+#: code:_description:0
1459+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
1460+#, python-format
1461+msgid "A credit control policy level"
1462+msgstr ""
1463+
1464+#. module: account_credit_control_dunning_fees
1465+#: code:_description:0
1466+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
1467+#, python-format
1468+msgid "Credit control line generator"
1469+msgstr ""
1470+
1471+#. module: account_credit_control_dunning_fees
1472+#: field:credit.control.line,dunning_fees_amount:0
1473+#: view:credit.control.policy:0
1474+msgid "Fees"
1475+msgstr ""
1476+
1477+#. module: account_credit_control_dunning_fees
1478+#: field:credit.control.policy.level,dunning_fixed_amount:0
1479+msgid "Fees Fixed Amount"
1480+msgstr ""
1481+
1482+#. module: account_credit_control_dunning_fees
1483+#: field:credit.control.policy.level,dunning_product_id:0
1484+msgid "Fees Product"
1485+msgstr ""
1486+
1487+#. module: account_credit_control_dunning_fees
1488+#: field:credit.control.policy.level,dunning_currency_id:0
1489+msgid "Fees currency"
1490+msgstr ""
1491+
1492+#. module: account_credit_control_dunning_fees
1493+#: selection:credit.control.policy.level,dunning_fees_type:0
1494+msgid "Fixed"
1495+msgstr ""
1496+
1497+#. module: account_credit_control_dunning_fees
1498+#: view:credit.control.policy:0
1499+msgid "Mail and reporting"
1500+msgstr ""
1501+
1502+#. module: account_credit_control_dunning_fees
1503+#: code:_description:0
1504+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
1505+#, python-format
1506+msgid "credit.control.dunning.fees.computer"
1507+msgstr ""
1508+
1509+#. module: account_credit_control_dunning_fees
1510+#: field:credit.control.policy.level,dunning_fees_type:0
1511+msgid "unknown"
1512+msgstr ""
1513+
1514
1515=== added file 'account_credit_control_dunning_fees/i18n/fr.po'
1516--- account_credit_control_dunning_fees/i18n/fr.po 1970-01-01 00:00:00 +0000
1517+++ account_credit_control_dunning_fees/i18n/fr.po 2014-06-24 12:34:19 +0000
1518@@ -0,0 +1,80 @@
1519+# Translation of OpenERP Server.
1520+# This file contains the translation of the following modules:
1521+# * account_credit_control_dunning_fees
1522+#
1523+msgid ""
1524+msgstr ""
1525+"Project-Id-Version: OpenERP Server 7.0\n"
1526+"Report-Msgid-Bugs-To: \n"
1527+"POT-Creation-Date: 2014-04-16 07:11+0000\n"
1528+"PO-Revision-Date: 2014-04-16 07:11+0000\n"
1529+"Last-Translator: <>\n"
1530+"Language-Team: \n"
1531+"MIME-Version: 1.0\n"
1532+"Content-Type: text/plain; charset=UTF-8\n"
1533+"Content-Transfer-Encoding: \n"
1534+"Plural-Forms: \n"
1535+
1536+#. module: account_credit_control_dunning_fees
1537+#: code:_description:0
1538+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_line
1539+#, python-format
1540+msgid "A credit control line"
1541+msgstr "Ligne de relance"
1542+
1543+#. module: account_credit_control_dunning_fees
1544+#: code:_description:0
1545+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_policy_level
1546+#, python-format
1547+msgid "A credit control policy level"
1548+msgstr "Une politique de relance"
1549+
1550+#. module: account_credit_control_dunning_fees
1551+#: code:_description:0
1552+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_run
1553+#, python-format
1554+msgid "Credit control line generator"
1555+msgstr "Générateur de relance"
1556+
1557+#. module: account_credit_control_dunning_fees
1558+#: field:credit.control.line,dunning_fees_amount:0
1559+#: view:credit.control.policy:0
1560+msgid "Fees"
1561+msgstr "Frais de relance"
1562+
1563+#. module: account_credit_control_dunning_fees
1564+#: field:credit.control.policy.level,dunning_fixed_amount:0
1565+msgid "Fees Fixed Amount"
1566+msgstr "Montant des frais"
1567+
1568+#. module: account_credit_control_dunning_fees
1569+#: field:credit.control.policy.level,dunning_product_id:0
1570+msgid "Fees Product"
1571+msgstr "Article lié"
1572+
1573+#. module: account_credit_control_dunning_fees
1574+#: field:credit.control.policy.level,dunning_currency_id:0
1575+msgid "Fees currency"
1576+msgstr "Devises"
1577+
1578+#. module: account_credit_control_dunning_fees
1579+#: selection:credit.control.policy.level,dunning_fees_type:0
1580+msgid "Fixed"
1581+msgstr "Fixe"
1582+
1583+#. module: account_credit_control_dunning_fees
1584+#: view:credit.control.policy:0
1585+msgid "Mail and reporting"
1586+msgstr "Lettres et e-mails"
1587+
1588+#. module: account_credit_control_dunning_fees
1589+#: code:_description:0
1590+#: model:ir.model,name:account_credit_control_dunning_fees.model_credit_control_dunning_fees_computer
1591+#, python-format
1592+msgid "credit.control.dunning.fees.computer"
1593+msgstr "credit.control.dunning.fees.computer"
1594+
1595+#. module: account_credit_control_dunning_fees
1596+#: field:credit.control.policy.level,dunning_fees_type:0
1597+msgid "unknown"
1598+msgstr "inconnu"
1599
1600=== added directory 'account_credit_control_dunning_fees/model'
1601=== added file 'account_credit_control_dunning_fees/model/__init__.py'
1602--- account_credit_control_dunning_fees/model/__init__.py 1970-01-01 00:00:00 +0000
1603+++ account_credit_control_dunning_fees/model/__init__.py 2014-06-24 12:34:19 +0000
1604@@ -0,0 +1,24 @@
1605+# -*- coding: utf-8 -*-
1606+##############################################################################
1607+#
1608+# Author: Nicolas Bessi
1609+# Copyright 2014 Camptocamp SA
1610+#
1611+# This program is free software: you can redistribute it and/or modify
1612+# it under the terms of the GNU Affero General Public License as
1613+# published by the Free Software Foundation, either version 3 of the
1614+# License, or (at your option) any later version.
1615+#
1616+# This program is distributed in the hope that it will be useful,
1617+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1618+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1619+# GNU Affero General Public License for more details.
1620+#
1621+# You should have received a copy of the GNU Affero General Public License
1622+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1623+#
1624+##############################################################################
1625+from . import line
1626+from . import policy
1627+from . import run
1628+from . import dunning
1629
1630=== added file 'account_credit_control_dunning_fees/model/dunning.py'
1631--- account_credit_control_dunning_fees/model/dunning.py 1970-01-01 00:00:00 +0000
1632+++ account_credit_control_dunning_fees/model/dunning.py 2014-06-24 12:34:19 +0000
1633@@ -0,0 +1,120 @@
1634+# -*- coding: utf-8 -*-
1635+##############################################################################
1636+#
1637+# Author: Nicolas Bessi
1638+# Copyright 2014 Camptocamp SA
1639+#
1640+# This program is free software: you can redistribute it and/or modify
1641+# it under the terms of the GNU Affero General Public License as
1642+# published by the Free Software Foundation, either version 3 of the
1643+# License, or (at your option) any later version.
1644+#
1645+# This program is distributed in the hope that it will be useful,
1646+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1647+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1648+# GNU Affero General Public License for more details.
1649+#
1650+# You should have received a copy of the GNU Affero General Public License
1651+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1652+#
1653+##############################################################################
1654+from openerp.osv import orm
1655+
1656+
1657+class FeesComputer(orm.BaseModel):
1658+ """Model that compute dunnig fees.
1659+
1660+ This class does not need any database storage as
1661+ it contains pure logic.
1662+
1663+ It inherits form ``orm.BaseModel`` to benefit of orm facility
1664+
1665+ Similar to AbstractModel but log access and actions
1666+ """
1667+
1668+ _name = 'credit.control.dunning.fees.computer'
1669+ _auto = False
1670+ _log_access = True
1671+ _register = True
1672+ _transient = False
1673+
1674+ def _get_compute_fun(self, level_fees_type):
1675+ """Retrieve function of class that should compute the fees based on type
1676+
1677+ :param level_fee_type: type exisiting in model `credit.control.policy.level`
1678+ for field dunning_fees_type
1679+
1680+ :returns: a function of class :class:`FeesComputer` with following signature
1681+ self, cr, uid, credit_line (record), context
1682+
1683+ """
1684+ if level_fees_type == 'fixed':
1685+ return self.compute_fixed_fees
1686+ else:
1687+ raise NotImplementedError('fees type %s is not supported' % level_fees_type)
1688+
1689+ def _compute_fees(self, cr, uid, credit_line_ids, context=None):
1690+ """Compute fees for `credit_line_ids` parameter
1691+
1692+ Fees amount is written on credit line in field dunning_fees_amount
1693+
1694+ :param credit_line_ids: list of `credit.control.line` ids
1695+
1696+ :returns: `credit_line_ids` list of `credit.control.line` ids
1697+
1698+ """
1699+ if context is None:
1700+ context = {}
1701+ if not credit_line_ids:
1702+ return credit_line_ids
1703+ c_model = self.pool['credit.control.line']
1704+ credit_lines = c_model.browse(cr, uid, credit_line_ids, context=context)
1705+ for credit in credit_lines:
1706+ # if there is no dependence between generated credit lines
1707+ # this could be threaded
1708+ self._compute(cr, uid, credit, context=context),
1709+ return credit_line_ids
1710+
1711+ def _compute(self, cr, uid, credit_line, context=None):
1712+ """Compute fees for a given credit line
1713+
1714+ Fees amount is written on credit line in field dunning_fees_amount
1715+
1716+ :param credit_line: credit line record
1717+
1718+ :returns: `credit_line` record
1719+ """
1720+ fees_type = credit_line.policy_level_id.dunning_fees_type
1721+ compute = self._get_compute_fun(fees_type)
1722+ fees = compute(cr, uid, credit_line, context=context)
1723+ if fees:
1724+ credit_line.write({'dunning_fees_amount': fees},
1725+ context=context)
1726+ return credit_line
1727+
1728+ def compute_fixed_fees(self, cr, uid, credit_line, context=None):
1729+ """Compute fees amount for fixed fees.
1730+ Correspond to the fixed dunning fees type
1731+
1732+ if currency of the fees is not the same as the currency
1733+ of the credit line, fees amount is converted to
1734+ currency of credit line.
1735+
1736+ :param credit_line: credit line record
1737+
1738+ :return: fees amount float (in credit line currency)
1739+
1740+ """
1741+ currency_model = self.pool['res.currency']
1742+ credit_currency = credit_line.currency_id
1743+ level = credit_line.policy_level_id
1744+ fees_amount = level.dunning_fixed_amount
1745+ if not fees_amount:
1746+ return 0.0
1747+ fees_currency = level.dunning_currency_id
1748+ if fees_currency == credit_currency:
1749+ return fees_amount
1750+ else:
1751+ return currency_model.compute(cr, uid, fees_currency.id,
1752+ credit_currency.id, fees_amount,
1753+ context=context)
1754
1755=== added file 'account_credit_control_dunning_fees/model/line.py'
1756--- account_credit_control_dunning_fees/model/line.py 1970-01-01 00:00:00 +0000
1757+++ account_credit_control_dunning_fees/model/line.py 2014-06-24 12:34:19 +0000
1758@@ -0,0 +1,29 @@
1759+# -*- coding: utf-8 -*-
1760+##############################################################################
1761+#
1762+# Author: Nicolas Bessi
1763+# Copyright 2014 Camptocamp SA
1764+#
1765+# This program is free software: you can redistribute it and/or modify
1766+# it under the terms of the GNU Affero General Public License as
1767+# published by the Free Software Foundation, either version 3 of the
1768+# License, or (at your option) any later version.
1769+#
1770+# This program is distributed in the hope that it will be useful,
1771+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1772+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1773+# GNU Affero General Public License for more details.
1774+#
1775+# You should have received a copy of the GNU Affero General Public License
1776+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1777+#
1778+##############################################################################
1779+from openerp.osv import orm, fields
1780+
1781+
1782+class credit_control_line(orm.Model):
1783+ """Add dunning_fees_amount_fees field"""
1784+
1785+ _inherit = "credit.control.line"
1786+
1787+ _columns = {'dunning_fees_amount': fields.float('Fees')}
1788
1789=== added file 'account_credit_control_dunning_fees/model/policy.py'
1790--- account_credit_control_dunning_fees/model/policy.py 1970-01-01 00:00:00 +0000
1791+++ account_credit_control_dunning_fees/model/policy.py 2014-06-24 12:34:19 +0000
1792@@ -0,0 +1,36 @@
1793+# -*- coding: utf-8 -*-
1794+##############################################################################
1795+#
1796+# Author: Nicolas Bessi
1797+# Copyright 2014 Camptocamp SA
1798+#
1799+# This program is free software: you can redistribute it and/or modify
1800+# it under the terms of the GNU Affero General Public License as
1801+# published by the Free Software Foundation, either version 3 of the
1802+# License, or (at your option) any later version.
1803+#
1804+# This program is distributed in the hope that it will be useful,
1805+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1806+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1807+# GNU Affero General Public License for more details.
1808+#
1809+# You should have received a copy of the GNU Affero General Public License
1810+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1811+#
1812+##############################################################################
1813+from openerp.osv import orm, fields
1814+
1815+
1816+class credit_control_policy(orm.Model):
1817+ """ADD dunning fees fields"""
1818+
1819+ _inherit = "credit.control.policy.level"
1820+ _columns = {'dunning_product_id': fields.many2one('product.product',
1821+ 'Fees Product'),
1822+ 'dunning_fixed_amount': fields.float('Fees Fixed Amount'),
1823+ 'dunning_currency_id': fields.many2one('res.currency',
1824+ 'Fees currency'),
1825+ # planned type are fixed, percent, compound
1826+ 'dunning_fees_type': fields.selection([('fixed', 'Fixed')])}
1827+
1828+ _defaults = {'dunning_fees_type': 'fixed'}
1829
1830=== added file 'account_credit_control_dunning_fees/model/run.py'
1831--- account_credit_control_dunning_fees/model/run.py 1970-01-01 00:00:00 +0000
1832+++ account_credit_control_dunning_fees/model/run.py 2014-06-24 12:34:19 +0000
1833@@ -0,0 +1,39 @@
1834+# -*- coding: utf-8 -*-
1835+##############################################################################
1836+#
1837+# Author: Nicolas Bessi
1838+# Copyright 2014 Camptocamp SA
1839+#
1840+# This program is free software: you can redistribute it and/or modify
1841+# it under the terms of the GNU Affero General Public License as
1842+# published by the Free Software Foundation, either version 3 of the
1843+# License, or (at your option) any later version.
1844+#
1845+# This program is distributed in the hope that it will be useful,
1846+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1847+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1848+# GNU Affero General Public License for more details.
1849+#
1850+# You should have received a copy of the GNU Affero General Public License
1851+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1852+#
1853+##############################################################################
1854+from openerp.osv import orm, fields
1855+
1856+
1857+class credit_control_run(orm.Model):
1858+ """Add computation of fees"""
1859+
1860+ _inherit = "credit.control.run"
1861+
1862+ def _generate_credit_lines(self, cr, uid, run_id, context=None):
1863+ """Override method to add fees computation"""
1864+ credit_line_ids = super(credit_control_run, self)._generate_credit_lines(
1865+ cr,
1866+ uid,
1867+ run_id,
1868+ context=context
1869+ )
1870+ fees_model = self.pool['credit.control.dunning.fees.computer']
1871+ fees_model._compute_fees(cr, uid, credit_line_ids, context=context)
1872+ return credit_line_ids
1873
1874=== added directory 'account_credit_control_dunning_fees/report'
1875=== added file 'account_credit_control_dunning_fees/report/credit_control_summary.html.mako'
1876--- account_credit_control_dunning_fees/report/credit_control_summary.html.mako 1970-01-01 00:00:00 +0000
1877+++ account_credit_control_dunning_fees/report/credit_control_summary.html.mako 2014-06-24 12:34:19 +0000
1878@@ -0,0 +1,246 @@
1879+## -*- coding: utf-8 -*-
1880+<html>
1881+ <head>
1882+ <style type="text/css">
1883+ ${css}
1884+body {
1885+ font-family: helvetica;
1886+ font-size: 12px;
1887+}
1888+
1889+.custom_text {
1890+ font-family: helvetica;
1891+ font-size: 12px;
1892+}
1893+
1894+table {
1895+ font-family: helvetica;
1896+ font-size: 12px;
1897+}
1898+
1899+.header {
1900+ margin-left: 0px;
1901+ text-align: left;
1902+ width: 300px;
1903+ font-size: 12px;
1904+}
1905+
1906+.title {
1907+ font-size: 16px;
1908+ font-weight: bold;
1909+}
1910+
1911+.basic_table{
1912+ text-align: center;
1913+ border: 1px solid lightGrey;
1914+ border-collapse: collapse;
1915+ font-family: helvetica;
1916+ font-size: 12px;
1917+}
1918+
1919+.basic_table th {
1920+ border: 1px solid lightGrey;
1921+ font-size: 11px;
1922+ font-weight: bold;
1923+
1924+}
1925+
1926+.basic_table td {
1927+ border: 1px solid lightGrey;
1928+ font-size: 12px;
1929+}
1930+
1931+.list_table {
1932+ border-color: black;
1933+ text-align: center;
1934+ border-collapse: collapse;
1935+}
1936+
1937+.list_table td {
1938+ border-color: gray;
1939+ border-top: 1px solid gray;
1940+ text-align: left;
1941+ font-size: 12px;
1942+ padding-right: 3px;
1943+ padding-left: 3px;
1944+ padding-top: 3px;
1945+ padding-bottom:3px;
1946+}
1947+
1948+.list_table th {
1949+ border-bottom: 2px solid black;
1950+ text-align: left;
1951+ font-size: 11px;
1952+ font-weight: bold;
1953+ padding-right: 3px
1954+ padding-left: 3px
1955+}
1956+
1957+.list_table thead {
1958+ display: table-header-group;
1959+}
1960+
1961+.address table {
1962+ font-size: 11px;
1963+ border-collapse: collapse;
1964+ margin: 0px;
1965+ padding: 0px;
1966+}
1967+
1968+.address .shipping {
1969+
1970+}
1971+
1972+.address .invoice {
1973+ margin-top: 10px;
1974+}
1975+
1976+.address .recipient {
1977+ font-size: 13px;
1978+ margin-right: 120px;
1979+ margin-left: 350px;
1980+ float: right;
1981+}
1982+
1983+
1984+table .address_title {
1985+ font-weight: bold;
1986+}
1987+
1988+.address td.name {
1989+ font-weight: bold;
1990+}
1991+
1992+td.amount, th.amount {
1993+ text-align: right;
1994+ padding-right:2px;
1995+}
1996+
1997+h1 {
1998+ font-size: 16px;
1999+ font-weight: bold;
2000+}
2001+
2002+tr.line .note {
2003+ border-style: none;
2004+ font-size: 9px;
2005+ padding-left: 10px;
2006+}
2007+
2008+tr.line {
2009+ margin-bottom: 10px;
2010+}
2011+ </style>
2012+ </head>
2013+ <body>
2014+
2015+ %for comm in objects :
2016+ <% setLang(comm.get_contact_address().lang) %>
2017+ <div class="address">
2018+ <table class="recipient">
2019+ <%
2020+ add = comm.get_contact_address()
2021+ %>
2022+ %if comm.partner_id.id == add.id:
2023+ <tr><td class="name">${comm.partner_id.title and comm.partner_id.title.name or ''} ${comm.partner_id.name }</td></tr>
2024+ <% address_lines = comm.partner_id.contact_address.split("\n") %>
2025+
2026+ %else:
2027+ <tr><td class="name">${comm.partner_id.name or ''}</td></tr>
2028+ <tr><td>${add.title and add.title.name or ''} ${add.name}</td></tr>
2029+ <% address_lines = add.contact_address.split("\n")[1:] %>
2030+ %endif
2031+ %for part in address_lines:
2032+ %if part:
2033+ <tr><td>${part}</td></tr>
2034+ %endif
2035+ %endfor
2036+ </table>
2037+ <br/>
2038+ <br/>
2039+ <br/>
2040+ <br/>
2041+
2042+ </div>
2043+ <br/>
2044+ <br/>
2045+ <br/>
2046+ <div>
2047+
2048+ <h3 style="clear: both; padding-top: 20px;">
2049+ ${_('Reminder')}: ${comm.current_policy_level.name or '' }
2050+ </h3>
2051+
2052+ <p>${_('Dear')},</p>
2053+ <p class="custom_text" width="95%">${comm.current_policy_level.custom_text.replace('\n', '<br />')}</p>
2054+
2055+ <br/>
2056+ <br/>
2057+ <p><b>${_('Summary')}<br/></b></p>
2058+ <table class="basic_table" style="width: 100%;">
2059+ <tr>
2060+ <th width="200">${_('Invoice number')}</th>
2061+ <th>${_('Invoice date')}</th>
2062+ <th>${_('Date due')}</th>
2063+ <th>${_('Invoiced amount')}</th>
2064+ <th>${_('Open amount')}</th>
2065+ <th>${_('Fees')}</th>
2066+ <th>${_('Currency')}</th>
2067+
2068+ </tr>
2069+%for line in comm.credit_control_line_ids:
2070+ <tr>
2071+ %if line.invoice_id:
2072+ <td width="200">${line.invoice_id.number}
2073+ %if line.invoice_id.name:
2074+ <br/>
2075+ ${line.invoice_id.name}
2076+ %endif
2077+ </td>
2078+ %else:
2079+ <td width="200">${line.move_line_id.name}</td>
2080+ %endif
2081+ <td class="date">${line.date_entry}</td>
2082+ <td class="date">${line.date_due}</td>
2083+ <td class="amount">${line.amount_due}</td>
2084+ <td class="amount">${line.balance_due}</td>
2085+ <td class="amount">${line.dunning_fees_amount}</td>
2086+ <td class="amount">${line.currency_id.name or comm.company_id.currency_id.name}</td>
2087+ </tr>
2088+%endfor
2089+ </table>
2090+ <br/>
2091+ <br/>
2092+<%doc>
2093+ <!-- uncomment to have info after summary -->
2094+ <p>${_('If you have any question, do not hesitate to contact us.')}</p>
2095+
2096+ <p>${comm.user_id.name} ${comm.user_id.email and '<%s>'%(comm.user_id.email) or ''}<br/>
2097+ ${comm.company_id.name}<br/>
2098+ % if comm.company_id.street:
2099+ ${comm.company_id.street or ''}<br/>
2100+
2101+ % endif
2102+
2103+ % if comm.company_id.street2:
2104+ ${comm.company_id.street2}<br/>
2105+ % endif
2106+ % if comm.company_id.city or comm.company_id.zip:
2107+ ${comm.company_id.zip or ''} ${comm.company_id.city or ''}<br/>
2108+ % endif
2109+ % if comm.company_id.country_id:
2110+ ${comm.company_id.state_id and ('%s, ' % comm.company_id.state_id.name) or ''} ${comm.company_id.country_id.name or ''}<br/>
2111+ % endif
2112+ % if comm.company_id.phone:
2113+ Phone: ${comm.company_id.phone}<br/>
2114+ % endif
2115+ % if comm.company_id.website:
2116+ ${comm.company_id.website or ''}<br/>
2117+ % endif
2118+</%doc>
2119+
2120+ <p style="page-break-after:always"></p>
2121+ %endfor
2122+
2123+ </body>
2124+</html>
2125
2126=== added file 'account_credit_control_dunning_fees/report/report.xml'
2127--- account_credit_control_dunning_fees/report/report.xml 1970-01-01 00:00:00 +0000
2128+++ account_credit_control_dunning_fees/report/report.xml 2014-06-24 12:34:19 +0000
2129@@ -0,0 +1,12 @@
2130+<openerp>
2131+ <data>
2132+ <report auto="False"
2133+ id="account_credit_control.report_webkit_html"
2134+ model="credit.control.communication"
2135+ name="credit_control_summary"
2136+ file="account_credit_control_dunning_fees/report/credit_control_summary.html.mako"
2137+ string="Credit Summary"
2138+ report_type="webkit"
2139+ webkit_header="report_webkit.ir_header_webkit_basesample0"/>
2140+ </data>
2141+</openerp>
2142
2143=== added directory 'account_credit_control_dunning_fees/security'
2144=== added directory 'account_credit_control_dunning_fees/tests'
2145=== added file 'account_credit_control_dunning_fees/tests/__init__.py'
2146--- account_credit_control_dunning_fees/tests/__init__.py 1970-01-01 00:00:00 +0000
2147+++ account_credit_control_dunning_fees/tests/__init__.py 2014-06-24 12:34:19 +0000
2148@@ -0,0 +1,23 @@
2149+# -*- coding: utf-8 -*-
2150+##############################################################################
2151+#
2152+# Author: Nicolas Bessi
2153+# Copyright 2014 Camptocamp SA
2154+#
2155+# This program is free software: you can redistribute it and/or modify
2156+# it under the terms of the GNU Affero General Public License as
2157+# published by the Free Software Foundation, either version 3 of the
2158+# License, or (at your option) any later version.
2159+#
2160+# This program is distributed in the hope that it will be useful,
2161+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2162+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2163+# GNU Affero General Public License for more details.
2164+#
2165+# You should have received a copy of the GNU Affero General Public License
2166+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2167+#
2168+##############################################################################
2169+from . import test_fees_generation
2170+
2171+checks = [test_fees_generation]
2172
2173=== added file 'account_credit_control_dunning_fees/tests/test_fees_generation.py'
2174--- account_credit_control_dunning_fees/tests/test_fees_generation.py 1970-01-01 00:00:00 +0000
2175+++ account_credit_control_dunning_fees/tests/test_fees_generation.py 2014-06-24 12:34:19 +0000
2176@@ -0,0 +1,95 @@
2177+# -*- coding: utf-8 -*-
2178+##############################################################################
2179+#
2180+# Author: Nicolas Bessi
2181+# Copyright 2014 Camptocamp SA
2182+#
2183+# This program is free software: you can redistribute it and/or modify
2184+# it under the terms of the GNU Affero General Public License as
2185+# published by the Free Software Foundation, either version 3 of the
2186+# License, or (at your option) any later version.
2187+#
2188+# This program is distributed in the hope that it will be useful,
2189+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2190+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2191+# GNU Affero General Public License for more details.
2192+#
2193+# You should have received a copy of the GNU Affero General Public License
2194+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2195+#
2196+##############################################################################
2197+from mock import MagicMock
2198+import openerp.tests.common as test_common
2199+
2200+
2201+class FixedFeesTester(test_common.TransactionCase):
2202+
2203+ def setUp(self):
2204+ """Initialize credit control level mock to test fees computations"""
2205+ super(FixedFeesTester, self).setUp()
2206+ self.currency_model = self.registry('res.currency')
2207+ self.euro = self.currency_model.search(self.cr, self.uid,
2208+ [('name', '=', 'EUR')])
2209+ self.assertTrue(self.euro)
2210+ self.euro = self.registry('res.currency').browse(self.cr,
2211+ self.uid,
2212+ self.euro[0])
2213+
2214+ self.usd = self.currency_model.search(self.cr, self.uid,
2215+ [('name', '=', 'USD')])
2216+ self.assertTrue(self.usd)
2217+ self.usd = self.registry('res.currency').browse(self.cr,
2218+ self.uid,
2219+ self.usd[0])
2220+
2221+ self.euro_level = MagicMock(name='Euro policy level')
2222+ self.euro_level.dunning_fixed_amount = 5.0
2223+ self.euro_level.dunning_currency_id = self.euro
2224+ self.euro_level.dunning_type = 'fixed'
2225+
2226+ self.usd_level = MagicMock(name='USD policy level')
2227+ self.usd_level.dunning_fixed_amount = 5.0
2228+ self.usd_level.dunning_currency_id = self.usd
2229+ self.usd_level.dunning_type = 'fixed'
2230+ self.dunning_model = self.registry('credit.control.dunning.fees.computer')
2231+
2232+ def test_type_getter(self):
2233+ """Test that correct compute function is returned for "fixed" type"""
2234+ c_fun = self.dunning_model._get_compute_fun('fixed')
2235+ self.assertEqual(c_fun, self.dunning_model.compute_fixed_fees)
2236+
2237+ def test_unknow_type(self):
2238+ """Test that non implemented error is raised if invalide fees type"""
2239+ with self.assertRaises(NotImplementedError):
2240+ self.dunning_model._get_compute_fun('bang')
2241+
2242+ def test_computation_same_currency(self):
2243+ """Test that fees are correctly computed with same currency"""
2244+ credit_line = MagicMock(name='Euro credit line')
2245+ credit_line.policy_level_id = self.euro_level
2246+ credit_line.currency_id = self.euro
2247+ fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
2248+ credit_line,
2249+ {})
2250+ self.assertEqual(fees, self.euro_level.dunning_fixed_amount)
2251+
2252+ def test_computation_different_currency(self):
2253+ """Test that fees are correctly computed with different currency"""
2254+ credit_line = MagicMock(name='USD credit line')
2255+ credit_line.policy_level_id = self.euro_level
2256+ credit_line.currency_id = self.usd
2257+ fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
2258+ credit_line,
2259+ {})
2260+ self.assertNotEqual(fees, self.euro_level.dunning_fixed_amount)
2261+
2262+ def test_no_fees(self):
2263+ """Test that fees are not generated if no amount defined on level"""
2264+ credit_line = MagicMock(name='USD credit line')
2265+ credit_line.policy_level_id = self.euro_level
2266+ self.euro_level.dunning_fixed_amount = 0.0
2267+ credit_line.currency_id = self.usd
2268+ fees = self.dunning_model.compute_fixed_fees(self.cr, self.uid,
2269+ credit_line,
2270+ {})
2271+ self.assertEqual(fees, 0.0)
2272
2273=== added directory 'account_credit_control_dunning_fees/view'
2274=== added file 'account_credit_control_dunning_fees/view/line_view.xml'
2275--- account_credit_control_dunning_fees/view/line_view.xml 1970-01-01 00:00:00 +0000
2276+++ account_credit_control_dunning_fees/view/line_view.xml 2014-06-24 12:34:19 +0000
2277@@ -0,0 +1,30 @@
2278+<?xml version="1.0" encoding="utf-8"?>
2279+<openerp>
2280+ <data>
2281+
2282+ <record id="add_fees_on_credit_control_line" model="ir.ui.view">
2283+ <field name="name">add fees on credit control line</field>
2284+ <field name="model">credit.control.line</field>
2285+ <field name="inherit_id" ref="account_credit_control.credit_control_line_tree" />
2286+ <field name="arch" type="xml">
2287+ <field name="balance_due" position="after">
2288+ <field name="dunning_fees_amount"
2289+ attrs="{'readonly': [('state', '!=', 'draft')]}"/>
2290+ </field>
2291+ </field>
2292+ </record>
2293+
2294+ <record id="add_fees_on_credit_control_line_from" model="ir.ui.view">
2295+ <field name="name">add fees on credit control line form</field>
2296+ <field name="model">credit.control.line</field>
2297+ <field name="inherit_id" ref="account_credit_control.credit_control_line_form"/>
2298+ <field name="arch" type="xml">
2299+ <field name="balance_due" position="after">
2300+ <field name="dunning_fees_amount"
2301+ attrs="{'readonly': [('state', '!=', 'draft')]}"/>
2302+ </field>
2303+ </field>
2304+ </record>
2305+
2306+ </data>
2307+</openerp>
2308
2309=== added file 'account_credit_control_dunning_fees/view/policy_view.xml'
2310--- account_credit_control_dunning_fees/view/policy_view.xml 1970-01-01 00:00:00 +0000
2311+++ account_credit_control_dunning_fees/view/policy_view.xml 2014-06-24 12:34:19 +0000
2312@@ -0,0 +1,26 @@
2313+<?xml version="1.0" encoding="utf-8"?>
2314+<openerp>
2315+ <data>
2316+ <record id="add_dunning_fees_on_policy" model="ir.ui.view">
2317+ <field name="name">add dunning fees on policy</field>
2318+ <field name="model">credit.control.policy</field>
2319+ <field name="inherit_id" ref="account_credit_control.credit_control_policy_form" />
2320+ <field name="arch" type="xml">
2321+ <page string="Mail and reporting" position="after">
2322+ <page string="Fees">
2323+ <group>
2324+ <group>
2325+ <field name="dunning_fixed_amount"/>
2326+ <field name="dunning_product_id"
2327+ attrs="{'required': [('dunning_fixed_amount', '!=', False)]}"/>
2328+ <field name="dunning_currency_id"
2329+ attrs="{'required': [('dunning_fixed_amount', '!=', False)]}"/>
2330+ </group>
2331+ </group>
2332+ </page>
2333+ </page>
2334+ </field>
2335+ </record>
2336+
2337+ </data>
2338+</openerp>
2339
2340=== modified file 'async_move_line_importer/model/move_line_importer.py'
2341--- async_move_line_importer/model/move_line_importer.py 2013-11-01 10:54:13 +0000
2342+++ async_move_line_importer/model/move_line_importer.py 2014-06-24 12:34:19 +0000
2343@@ -41,7 +41,7 @@
2344
2345 It will parse the saved CSV file using orm.BaseModel.load
2346 in a thread. If you set bypass_orm to True then the load function
2347- will use a totally overriden create function that is a lot faster
2348+ will use a totally overridden create function that is a lot faster
2349 but that totally bypass the ORM
2350
2351 """
2352@@ -303,7 +303,7 @@
2353 Will generate an success/failure report and generate some
2354 maile threads. It uses BaseModel.load to lookup CSV.
2355 If you set bypass_orm to True then the load function
2356- will use a totally overriden create function that is a lot faster
2357+ will use a totally overridden create function that is a lot faster
2358 but that totally bypass the ORM
2359
2360 """

Subscribers

People subscribed via source and target branches