Merge lp:~savoirfairelinux-openerp/openerp-isp/invoice_refactoring into lp:openerp-isp

Proposed by Joao Alfredo Gama Batista
Status: Needs review
Proposed branch: lp:~savoirfairelinux-openerp/openerp-isp/invoice_refactoring
Merge into: lp:openerp-isp
Diff against target: 6437 lines (+4500/-1134)
34 files modified
contract_isp/contract.py (+219/-75)
contract_isp/contract_isp_view.xml (+80/-74)
contract_isp/i18n/contract_isp.pot (+278/-184)
contract_isp/i18n/fr.po (+338/-0)
contract_isp/wizard/activate_contract_service.py (+141/-67)
contract_isp/wizard/activate_contract_service.xml (+70/-35)
contract_isp_automatic_invoicing/contract.py (+6/-2)
contract_isp_automatic_invoicing/i18n/contract_isp_automatic_invoicing.pot (+24/-0)
contract_isp_automatic_invoicing/i18n/fr.po (+24/-0)
contract_isp_invoice/__init__.py (+5/-2)
contract_isp_invoice/contract.py (+590/-294)
contract_isp_invoice/contract_isp_invoice_view.xml (+1/-2)
contract_isp_invoice/i18n/contract_isp_invoice.pot (+217/-25)
contract_isp_invoice/i18n/fr.po (+291/-0)
contract_isp_invoice/invoice.py (+40/-0)
contract_isp_invoice/tests/__init__.py (+33/-0)
contract_isp_invoice/tests/common.py (+142/-0)
contract_isp_invoice/tests/test_deactivate.py (+165/-0)
contract_isp_invoice/tests/test_prorata.py (+179/-0)
contract_isp_invoice/tests/test_reseller.py (+70/-0)
contract_isp_invoice/wizard/activate_contract_service.py (+30/-22)
contract_isp_invoice/wizard/close_contract.py (+10/-7)
contract_isp_package_configurator/__init__.py (+7/-3)
contract_isp_package_configurator/__openerp__.py (+1/-0)
contract_isp_package_configurator/account_analytic.py (+61/-0)
contract_isp_package_configurator/contract.py (+35/-0)
contract_isp_package_configurator/contract_isp_package_configurator_view.xml (+40/-22)
contract_isp_package_configurator/i18n/contract_isp_package_configurator.pot (+343/-0)
contract_isp_package_configurator/i18n/fr.po (+346/-0)
contract_isp_package_configurator/security/groups.xml (+23/-0)
contract_isp_package_configurator/wizard/package_configurator.py (+260/-164)
contract_isp_package_configurator/wizard/package_configurator.xml (+286/-156)
product_dependencies/i18n/fr.po (+72/-0)
product_dependencies/i18n/product_dependecies.pot (+73/-0)
To merge this branch: bzr merge lp:~savoirfairelinux-openerp/openerp-isp/invoice_refactoring
Reviewer Review Type Date Requested Status
OpenERP ISP Core Editors Pending
Review via email: mp+226031@code.launchpad.net

Description of the change

 * Split the big invoice routine into 3 separate methods.
 * Minor adjustments and code clean-up.

To post a comment you must log in.
32. By Joao Alfredo Gama Batista

[FIX] Fix error in voucher creation

33. By Joao Alfredo Gama Batista

[FIX] Fix typo in action context

34. By Vincent Vinet

pep8 + add deactivation date on contracts

35. By Vincent Vinet

flake cleanup

36. By Vincent Vinet

make sure we assign account invoice lines

37. By Vincent Vinet

add prorata invoicing tests

38. By Vincent Vinet

refac tests to allow deactivate tests, complete activate tests and pass them

39. By Vincent Vinet

fix refunds

40. By Vincent Vinet

create prorata invoices on deactivation

41. By Vincent Vinet

remove debug print

42. By Vincent Vinet

remove seemingly unused field

43. By Vincent Vinet

also remove from view ¬¬

44. By Vincent Vinet

remove serial assignment from packager configurator, move into 3 simple wizards

45. By Vincent Vinet

merge Marc Cassuto 2014-09-12 contract_isp

46. By Vincent Vinet

add groups and states handling to show add/reserve/return buttons

47. By Vincent Vinet

test that reseller prorata invoice is assigned correctly

48. By Vincent Vinet

fix tests to run in oe test env

49. By Marc Cassuto (SFL)

contract_isp_package_configurator : Add fr translation

50. By Marc Cassuto (SFL)

contract_isp_package_configurator : Add fr translation

51. By Marc Cassuto (SFL)

product_dependencies - Add fr translation

52. By Marc Cassuto (SFL)

contract_isp - Add fr translation

53. By Vincent Vinet

refac to allow integrating class of service better, pep8 cleanup

54. By Marc Cassuto (SFL)

contract_isp_invoice - Add fr translation

55. By Marc Cassuto (SFL)

Merge diverted branches

56. By Vincent Vinet

do not show reserve/return buttons for non-stockable products

57. By Vincent Vinet

restrict production lots to available ones in add/replace wizards

58. By Vincent Vinet

fix serial number checking

59. By Vincent Vinet

disallow activation dates in the future

60. By Vincent Vinet

log reserve/exchange/return to contract

61. By Marc Cassuto (SFL)

Update .pot files and fr translations for all modules

62. By Marc Cassuto (SFL)

contract_isp_package_configurator - Tried to get the translation from the wizard

63. By Vincent Vinet

use the right account and journal to create vouchers

64. By Vincent Vinet

prevent rounding a None currency

65. By Vincent Vinet

prevent double billing exception lines and one time lines

66. By Vincent Vinet

allow searching non-prorata invoice dates

67. By Vincent Vinet

in package configurator, call contract.service onchange product id to get duration before create

68. By Vincent Vinet

specify source process when creating invoices

69. By Vincent Vinet

actually use the source_process

70. By Vincent Vinet

prevent error when getting invoice dates for empty ids

71. By Vincent Vinet

wrong period id when confirming voucher

72. By Vincent Vinet

do not bill activation multiple times

73. By Vincent Vinet

revert checking activation line status on subscription

74. By Vincent Vinet

clean date_interval/format_interval usage

75. By Vincent Vinet

default date format for format_interval

76. By Vincent Vinet

use price, not list price, for analytic lines

77. By Vincent Vinet

only create prorata lines on activation, allow passing in date strings for operation date, do not filter already created

78. By Vincent Vinet

do not show line_cr/dr_ids in the payment voucher launched from contract

79. By Marc Cassuto (SFL)

contract_isp_invoice - change 'Create voucher' button name to 'Create Initial Invoice'

80. By Marc Cassuto (SFL)

contract_isp_invoice - change 'Create voucher' button name to 'Create Initial Invoice'

81. By Vincent Vinet

allow passing operation_date in str form

82. By Vincent Vinet

do not multiply total price by quantity when creating analytic lines

83. By Vincent Vinet

revert r78 as it hides payments at too many places

84. By Vincent Vinet

show smarter error on account blank pricelist voucher creation

85. By Vincent Vinet

create recurrent invoices at invoice day

86. By Vincent Vinet

do not allow deactivate in the future

87. By Vincent Vinet

use partner fiscal position to calculate taxes on default voucher amount

88. By Vincent Vinet

do not create invoices grouped by journal type for contracts

89. By Vincent Vinet

copy context to avoid side effects in batch invoice creation

90. By Vincent Vinet

bill onetime services in recurrent billing

Unmerged revisions

90. By Vincent Vinet

bill onetime services in recurrent billing

89. By Vincent Vinet

copy context to avoid side effects in batch invoice creation

88. By Vincent Vinet

do not create invoices grouped by journal type for contracts

87. By Vincent Vinet

use partner fiscal position to calculate taxes on default voucher amount

86. By Vincent Vinet

do not allow deactivate in the future

85. By Vincent Vinet

create recurrent invoices at invoice day

84. By Vincent Vinet

show smarter error on account blank pricelist voucher creation

83. By Vincent Vinet

revert r78 as it hides payments at too many places

82. By Vincent Vinet

do not multiply total price by quantity when creating analytic lines

81. By Vincent Vinet

allow passing operation_date in str form

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'contract_isp/contract.py'
2--- contract_isp/contract.py 2013-11-07 15:10:04 +0000
3+++ contract_isp/contract.py 2014-11-24 15:05:14 +0000
4@@ -21,14 +21,19 @@
5 ##############################################################################
6
7 import logging
8+_logger = logging.getLogger(__name__)
9 import calendar
10 import datetime
11+
12 import openerp.addons.decimal_precision as dp
13 from openerp.osv import orm, fields
14-from openerp.report import report_sxw
15-from openerp.tools import convert
16+from openerp.tools import SUPERUSER_ID, DEFAULT_SERVER_DATE_FORMAT
17 from openerp.tools.translate import _
18
19+LINE_TYPE_EXCEPTION = 'x'
20+LINE_TYPE_RECURRENT = 'r'
21+LINE_TYPE_ONETIME = 'o'
22+
23
24 def add_months(sourcedate, months):
25 month = sourcedate.month - 1 + months
26@@ -38,7 +43,7 @@
27 return datetime.date(year, month, day)
28
29
30-def date_interval(start_date, month_end=True, date_format='%m/%d/%Y'):
31+def date_interval(start_date, month_end=True):
32 if month_end:
33 end_date = datetime.date(start_date.year,
34 start_date.month,
35@@ -47,10 +52,12 @@
36 else:
37 end_date = add_months(start_date, 1) - datetime.timedelta(days=1)
38
39- interval = '(%s - %s)' % (start_date.strftime(date_format),
40- end_date.strftime(date_format))
41-
42- return interval
43+ return start_date, end_date
44+
45+
46+def format_interval(start, end, date_format=DEFAULT_SERVER_DATE_FORMAT):
47+ return '(%s - %s)' % (start.strftime(date_format),
48+ end.strftime(date_format))
49
50
51 class res_company(orm.Model):
52@@ -105,10 +112,11 @@
53 _inherit = 'product.product'
54
55 _columns = {
56- 'analytic_line_type': fields.selection((('r', 'Recurrent'),
57- ('x', 'Exception'),
58- ('o', 'One time')),
59- 'Type in contract'),
60+ 'analytic_line_type': fields.selection(((LINE_TYPE_RECURRENT, 'Recurrent'),
61+ (LINE_TYPE_EXCEPTION, 'Exception'),
62+ (LINE_TYPE_ONETIME, 'One time')),
63+ 'Type in contract',
64+ required=True),
65 'require_activation': fields.boolean('Require activation')
66 }
67
68@@ -117,7 +125,6 @@
69 _name = 'contract.service'
70
71 def _get_product_price(self, cr, uid, ids, field_name, arg, context=None):
72- product_obj = self.pool.get('product.product')
73 product_pricelist_obj = self.pool.get('product.pricelist')
74 partner_id = self.browse(
75 cr, uid, ids[0],
76@@ -150,6 +157,8 @@
77
78 _columns = {
79 'activation_date': fields.datetime('Activation date'),
80+ 'billed_to_date': fields.date('Billed until date'),
81+ 'deactivation_date': fields.datetime('Deactivation date'),
82 'duration': fields.integer('Duration'),
83 'product_id': fields.many2one('product.product',
84 'Product',
85@@ -159,10 +168,12 @@
86 digits_compute=dp.get_precision('Product Unit of Measure')),
87 'category_id': fields.many2one('product.category', 'Product Category'),
88 'name': fields.char('Description', size=64),
89- 'analytic_line_type': fields.selection((('r', 'Recurrent'),
90- ('x', 'Exception'),
91- ('o', 'One time')),
92- 'Type'),
93+ 'analytic_line_type': fields.selection(
94+ ((LINE_TYPE_RECURRENT, 'Recurrent'),
95+ (LINE_TYPE_EXCEPTION, 'Exception'),
96+ (LINE_TYPE_ONETIME, 'One time')),
97+ 'Type'),
98+ # XXX Should this be a function based on product.product?
99 'require_activation': fields.boolean('Require activation'),
100 'account_id': fields.many2one('account.analytic.account', 'Contract'),
101 'unit_price': fields.function(
102@@ -201,7 +212,7 @@
103 ret['value']['require_activation'] = product.require_activation
104 ret['value']['category_id'] = product.categ_id.id
105 ret['value']['unit_price'] = product.list_price
106- if product.analytic_line_type in ('r', 'o'):
107+ if product.analytic_line_type == LINE_TYPE_RECURRENT:
108 ret['value']['duration'] = 0
109 else:
110 ret['value']['duration'] = 1
111@@ -215,13 +226,63 @@
112
113 return ret
114
115+ def _prorata_rate(self, days_used, days_in_month):
116+ """ Returns a rate to compute prorata invoices.
117+ Current method is days_used / days_in_month, rounded DOWN
118+ to 2 digits
119+ """
120+ return (100 * days_used / days_in_month) / 100.0
121+
122+ def _get_prorata_interval_rate(self, cr, uid, change_date, context=None):
123+ """ Get the prorata interval and price rate.
124+
125+ Returns a tuple (start_date, end_date, price percent)
126+ """
127+ month_days = calendar.monthrange(change_date.year,
128+ change_date.month)[1]
129+ start_date = add_months(change_date, 1)
130+ end_date = start_date.replace(day=month_days)
131+ used_days = month_days - change_date.day
132+ ptx = self._prorata_rate(used_days, month_days)
133+
134+ return start_date, end_date, ptx
135+
136+ def _get_prorata_interval_rate_deactivate(self, cr, uid, change_date,
137+ context=None):
138+ start_date, end_date, ptx = self._get_prorata_interval_rate(
139+ cr, uid, change_date, context=context)
140+ ptx = ptx * -1
141+ return start_date, end_date, ptx
142+
143+ def _get_date_format(self, cr, uid, obj, context):
144+ partner_lang = obj.account_id.partner_id.lang
145+ res_lang_obj = self.pool['res.lang']
146+ query = [
147+ ('code', '=', partner_lang),
148+ ('active', '=', True)
149+ ]
150+ lang_id = res_lang_obj.search(cr, uid, query, context=context)
151+ if lang_id:
152+ date_format = res_lang_obj.browse(cr, uid,
153+ lang_id[0],
154+ context=context).date_format
155+
156+ else:
157+ date_format = '%Y/%m/%d'
158+ return date_format
159+
160 def create_analytic_line(self, cr, uid, ids,
161 mode='manual',
162 date=None,
163 context=None):
164-
165- if not date:
166- date = datetime.date.today()
167+ context = context or {}
168+ if date is None:
169+ date = context.get("operation_date", datetime.date.today())
170+ if not isinstance(date, datetime.date):
171+ date = datetime.datetime.strptime(
172+ date,
173+ DEFAULT_SERVER_DATE_FORMAT,
174+ ).date()
175
176 if type(ids) is int:
177 ids = [ids]
178@@ -229,72 +290,130 @@
179 ret = []
180 record = {}
181 next_month = None
182- company_obj = self.pool.get('res.company')
183- company_id = company_obj._company_default_get(cr, uid, context)
184- company = company_obj.browse(cr, uid, company_id, context)
185-
186 account_analytic_line_obj = self.pool.get('account.analytic.line')
187 for line in self.browse(cr, uid, ids, context):
188- account_id = line.account_id.id
189- partner_lang = line.account_id.partner_id.lang
190- res_lang_obj = self.pool.get('res.lang')
191- query = [
192- ('code', '=', partner_lang),
193- ('active', '=', True)
194- ]
195- lang_id = res_lang_obj.search(cr, uid, query, context=context)
196- if lang_id:
197- date_format = res_lang_obj.browse(cr, uid,
198- lang_id[0],
199- context=context).date_format
200-
201- else:
202- date_format = '%Y/%m/%d'
203-
204- if line.analytic_line_type == 'r':
205+ date_format = self._get_date_format(cr, uid, line, context=context)
206+ start, end = None, None
207+
208+ amount = line.price
209+
210+ if line.analytic_line_type == LINE_TYPE_RECURRENT:
211 if mode == 'prorata':
212 activation_date = date
213-
214- month_days = calendar.monthrange(activation_date.year,
215- activation_date.month)[1]
216-
217- used_days = month_days - activation_date.day
218- ptx = (100 * used_days / month_days) / 100.0
219-
220- amount = line.product_id.list_price * ptx
221- interval = date_interval(add_months(date, 1),
222- True,
223- date_format)
224+ start, end, ptx = self._get_prorata_interval_rate(
225+ cr, uid,
226+ activation_date,
227+ context=context,
228+ )
229+
230+ amount = amount * ptx
231
232 elif mode == 'cron':
233- amount = line.product_id.list_price
234 next_month = add_months(date, 1)
235 next_month = datetime.date(
236 next_month.year,
237 next_month.month,
238 1)
239- interval = date_interval(next_month,
240- False,
241- date_format)
242+ start, end = date_interval(next_month, False)
243
244 elif mode == 'manual':
245- amount = line.product_id.list_price
246- interval = date_interval(date, False, date_format)
247+ start, end = date_interval(date, False)
248
249 elif mode == 'subscription':
250- amount = line.product_id.list_price
251- interval = ''
252+ line.write({'activation_line_generated': True})
253
254+ if start and end:
255+ interval = format_interval(start, end, date_format)
256 else:
257 interval = ''
258- amount = line.product_id.list_price
259
260 general_account_id = line.product_id.property_account_expense.id \
261 or line.product_id.categ_id.property_account_expense_categ.id
262
263 record = {
264 'name': ' '.join([line.product_id.name,
265- True and line.name or '',
266+ line.name or '',
267+ interval]),
268+ 'amount': (amount * -1),
269+ 'account_id': line.account_id.id,
270+ 'user_id': uid,
271+ 'general_account_id': general_account_id,
272+ 'product_id': line.product_id.id,
273+ 'contract_service_id': line.id,
274+ 'to_invoice': 1,
275+ 'unit_amount': line.qty,
276+ 'is_prorata': mode == 'prorata',
277+ 'date': (next_month or date).strftime('%Y-%m-%d'),
278+ 'journal_id': 1
279+ }
280+
281+ if line.analytic_line_type == LINE_TYPE_EXCEPTION:
282+ new_duration = line.duration - 1
283+ line.write({'duration': new_duration})
284+ if new_duration <= 0:
285+ self.unlink(cr, SUPERUSER_ID, line.id)
286+ record['contract_service_id'] = False
287+ elif line.analytic_line_type == LINE_TYPE_ONETIME:
288+ if line.duration > 0:
289+ line.write({'duration': line.duration - 1})
290+ else:
291+ # Do not create an already billed line
292+ continue
293+
294+ if 'default_type' in context:
295+ context.pop('default_type')
296+
297+ ret.append(account_analytic_line_obj.create(cr, uid, record,
298+ context))
299+ return ret
300+
301+ def create_refund_line(self, cr, uid, ids,
302+ mode='manual',
303+ date=None,
304+ context=None):
305+ context = context or {}
306+ if date is None:
307+ date = context.get("operation_date", datetime.date.today())
308+ if not isinstance(date, datetime.date):
309+ date = datetime.datetime.strptime(
310+ date,
311+ DEFAULT_SERVER_DATE_FORMAT,
312+ ).date()
313+
314+ if type(ids) is int:
315+ ids = [ids]
316+
317+ ret = []
318+ record = {}
319+ account_analytic_line_obj = self.pool.get('account.analytic.line')
320+ for line in self.browse(cr, uid, ids, context):
321+ if any((line.analytic_line_type != LINE_TYPE_RECURRENT,
322+ mode != "prorata")):
323+ # Not handled for now, only pro-rata deactivate
324+ continue
325+
326+ date_format = self._get_date_format(cr, uid, line, context=context)
327+
328+ deactivation_date = date
329+ start, end, ptx = self._get_prorata_interval_rate_deactivate(
330+ cr, uid,
331+ deactivation_date,
332+ context=context,
333+ )
334+
335+ amount = line.product_id.list_price * ptx
336+
337+ interval = format_interval(start, end,
338+ date_format=date_format)
339+
340+ general_account_id = (
341+ line.product_id.property_account_expense.id or
342+ line.product_id.categ_id.property_account_expense_categ.id
343+ )
344+
345+ record = {
346+ 'name': ' '.join([line.product_id.name,
347+ line.name or '',
348 interval]),
349 'amount': (amount * -1) * line.qty,
350 'account_id': line.account_id.id,
351@@ -305,16 +424,10 @@
352 'to_invoice': 1,
353 'unit_amount': line.qty,
354 'is_prorata': mode == 'prorata',
355- 'date': next_month and next_month.strftime('%Y-%m-%d') or date.strftime('%Y-%m-%d'),
356+ 'date': date.strftime('%Y-%m-%d'),
357 'journal_id': 1
358 }
359
360- if line.analytic_line_type == 'x':
361- line.write({'duration': line.duration - 1})
362- if line.duration <= 0:
363- line.unlink()
364- record['contract_service_id'] = False
365-
366 if 'default_type' in context:
367 context.pop('default_type')
368
369@@ -330,10 +443,16 @@
370
371 return ret
372
373- def action_desactivate(self, cr, uid, ids, context):
374- return self.write(cr, uid, ids,
375- {'state': 'inactive', 'activation_date': None},
376- context)
377+ def action_deactivate(self, cr, uid, ids, context):
378+ values = {'state': 'inactive'}
379+ if "deactivation_date" in context:
380+ values["deactivation_date"] = context["deactivation_date"]
381+ else:
382+ values["deactivation_date"] = fields.datetime.now()
383+
384+ self.write(cr, uid, ids, values, context)
385+
386+ return True
387
388
389 class account_analytic_account(orm.Model):
390@@ -364,12 +483,13 @@
391 if context and context.get('create_analytic_line_mode', False):
392 mode = context.get('create_analytic_line_mode')
393
394- account_analytic_line_obj = self.pool.get('account.analytic.line')
395 contract_service_obj = self.pool.get('contract.service')
396 query = [
397 ('account_id', 'in', ids),
398 ('state', '=', 'active'),
399- ('analytic_line_type', 'in', ('r', 'x'))
400+ ('analytic_line_type', 'in', (LINE_TYPE_RECURRENT,
401+ LINE_TYPE_ONETIME,
402+ LINE_TYPE_EXCEPTION))
403 ]
404 contract_service_ids = contract_service_obj.search(cr, uid,
405 query,
406@@ -384,6 +504,30 @@
407
408 return {}
409
410+ def create_refund_lines(self, cr, uid, ids, context=None):
411+ context = context or {}
412+ mode = context.get('create_analytic_line_mode', 'manual')
413+
414+ contract_service_obj = self.pool["contract.service"]
415+ query = [
416+ ('account_id', 'in', ids),
417+ ('state', '=', 'inactive'),
418+ # only recurrent is handled in refund right now
419+ ('analytic_line_type', 'in', (LINE_TYPE_RECURRENT,)),
420+ ]
421+ contract_service_ids = contract_service_obj.search(cr, uid,
422+ query,
423+ order='account_id',
424+ context=context)
425+
426+ if contract_service_ids:
427+ contract_service_obj.create_refund_line(cr, uid,
428+ contract_service_ids,
429+ mode=mode,
430+ context=context)
431+
432+ return {}
433+
434 def create(self, cr, uid, values, context=None):
435 if values['type'] == 'contract' and values['use_contract_services']:
436 values['name'] = values['code']
437
438=== modified file 'contract_isp/contract_isp_view.xml'
439--- contract_isp/contract_isp_view.xml 2013-11-07 15:10:04 +0000
440+++ contract_isp/contract_isp_view.xml 2014-11-24 15:05:14 +0000
441@@ -22,68 +22,68 @@
442 </field>
443 </record>
444
445- <record id="view_contract_isp_form" model="ir.ui.view">
446- <field name="name">contract.isp.form</field>
447- <field name="model">account.analytic.account</field>
448- <field name="inherit_id" ref="account_analytic_analysis.account_analytic_account_form_form"/>
449- <field name="arch" type="xml">
450- <xpath expr='//div[@name="project"]' position='inside'>
451- <field name="use_contract_services" />
452- <label for="use_contract_services" />
453- </xpath>
454+ <record id="view_contract_isp_form" model="ir.ui.view">
455+ <field name="name">contract.isp.form</field>
456+ <field name="model">account.analytic.account</field>
457+ <field name="inherit_id" ref="account_analytic_analysis.account_analytic_account_form_form"/>
458+ <field name="arch" type="xml">
459+ <xpath expr='//div[@name="project"]' position='inside'>
460+ <field name="use_contract_services" />
461+ <label for="use_contract_services" />
462+ </xpath>
463
464- <xpath expr="//separator[@string='Invoicing']" position="before">
465- <separator
466- name="services"
467- string="Services"
468- attrs="{'invisible': [('use_contract_services', '=', False)]}"/>
469- <field
470- name="contract_service_ids"
471- mode="tree"
472- select="1"
473- options="{'always_reload': True, 'reload_on_button': True}"
474- attrs="{'invisible': [('use_contract_services', '=', False)]}">
475- <tree editable="botton" colors="green:state=='active'" >
476- <field name="account_id" invisible="1"/>
477- <field name="state" readonly="True" />
478- <field name="activation_date"
479- attrs="{'readonly': [('state', 'in', ('draft', 'active'))]}" />
480- <field name="category_id" invisible="1" />
481- <field name="product_id"
482- on_change="on_change_product_id(product_id)"
483- attrs="{'readonly': [('state', '=', 'active')]}"
484- domain="['&amp;', ('analytic_line_type', 'in', ('r', 'x', 'o')), ('categ_id', '=', category_id)]" />
485- <field name="name" />
486- <field name="qty" on_change="on_change_qty(qty, unit_price)" />
487- <field name="unit_price" invisible="1" />
488- <field name="price" readonly="1" sum="Total Price" />
489- <field name="analytic_line_type" />
490- <field name="require_activation" invisible="True" />
491- <field
492- name="duration"
493- attrs="{'readonly': [('state', 'in', ('draft', 'active')),
494- ('analytic_line_type', 'in', ('r', 'o'))],
495- 'required': [('analytic_line_type', '=', 'x')]}" />
496- <field name="account_id" invisible="True" />
497- <button
498- name="%(action_view_contract_service_activate)d"
499- string="Activate"
500- type="action"
501- states="draft,inactive"
502- icon="gtk-yes"
503- groups="contract_isp.group_isp_agent"/>
504- <button
505- name="action_desactivate"
506- string="Desactivate"
507- type="object"
508- states="active"
509- icon="gtk-no"
510- groups="contract_isp.group_isp_agent"/>
511- </tree>
512- </field>
513- </xpath>
514- </field>
515- </record>
516+ <xpath expr="//separator[@string='Invoicing']" position="before">
517+ <separator
518+ name="services"
519+ string="Services"
520+ attrs="{'invisible': [('use_contract_services', '=', False)]}"/>
521+ <field
522+ name="contract_service_ids"
523+ mode="tree"
524+ select="1"
525+ options="{'always_reload': True, 'reload_on_button': True}"
526+ attrs="{'invisible': [('use_contract_services', '=', False)]}">
527+ <tree editable="botton" colors="black:require_activation==False; green:state=='active'" >
528+ <field name="account_id" invisible="1"/>
529+ <field name="state" readonly="True" />
530+ <field name="activation_date"
531+ attrs="{'readonly': [('state', 'in', ('draft', 'active'))]}" />
532+ <field name="category_id" invisible="1" />
533+ <field name="product_id"
534+ on_change="on_change_product_id(product_id)"
535+ attrs="{'readonly': [('state', '=', 'active')]}"
536+ domain="['&amp;', ('analytic_line_type', 'in', ('r', 'x', 'o')), ('categ_id', '=', category_id)]" />
537+ <field name="name" />
538+ <field name="qty" on_change="on_change_qty(qty, unit_price)" />
539+ <field name="unit_price" invisible="1" />
540+ <field name="price" readonly="1" sum="Total Price" />
541+ <field name="analytic_line_type" />
542+ <field name="require_activation" invisible="True" />
543+ <field
544+ name="duration"
545+ attrs="{'readonly': [('state', 'in', ('draft', 'active')),
546+ ('analytic_line_type', 'in', ('r', 'o'))],
547+ 'required': [('analytic_line_type', '=', 'x')]}" />
548+ <field name="account_id" invisible="True" />
549+ <button
550+ name="%(action_view_contract_service_activate)d"
551+ string="Activate"
552+ type="action"
553+ icon="gtk-yes"
554+ attrs="{'invisible': ['|', ('state', 'not in', ('draft', 'inactive')), ('require_activation', '=', False)]}"
555+ groups="contract_isp.group_isp_agent"/>
556+ <button
557+ name="%(action_view_contract_service_deactivate)d"
558+ string="Desactivate"
559+ type="action"
560+ icon="gtk-no"
561+ attrs="{'invisible': ['|', ('state', '!=', 'active'), ('require_activation', '=', False)]}"
562+ groups="contract_isp.group_isp_agent"/>
563+ </tree>
564+ </field>
565+ </xpath>
566+ </field>
567+ </record>
568
569 <record id="view_contract_service_form" model="ir.ui.view">
570 <field name="name">contract.service.form</field>
571@@ -126,11 +126,11 @@
572 states="draft"
573 groups="contract_isp.group_isp_agent" />
574 <button
575- name="action_desactivate"
576+ name="%(action_view_contract_service_deactivate)d"
577 string="Desactivate"
578 icon="gtk-no"
579 states="active"
580- type="object"
581+ type="action"
582 groups="contract_isp.group_isp_agent" />
583 </tree>
584 </field>
585@@ -148,17 +148,23 @@
586 </field>
587 </record>
588
589- <record id="contract_isp_form_suspend" model="ir.ui.view">
590- <field name="name">contract.isp.form.suspend</field>
591- <field name="model">account.analytic.account</field>
592- <field name="inherit_id" ref="hr_timesheet_invoice.account_analytic_account_form_form"/>
593- <field name="arch" type="xml">
594- <xpath expr="//header/button[@name='set_pending']" position='attributes'>
595- <attribute name="string">Suspend</attribute>
596- </xpath>
597- </field>
598- </record>
599-
600+ <record id="contract_isp_form_buttons_adjust" model="ir.ui.view">
601+ <field name="name">contract.isp.form.suspend</field>
602+ <field name="model">account.analytic.account</field>
603+ <field name="inherit_id" ref="hr_timesheet_invoice.account_analytic_account_form_form"/>
604+ <field name="arch" type="xml">
605+ <xpath expr="//header/button[@name='set_close']" position='attributes'>
606+ <attribute name="groups">contract_isp.group_isp_agent2</attribute>
607+ </xpath>
608+ <xpath expr="//header/button[@name='set_cancel']" position='attributes'>
609+ <attribute name="groups">contract_isp.group_isp_agent2</attribute>
610+ </xpath>
611+ <xpath expr="//header/button[@name='set_pending']" position='attributes'>
612+ <attribute name="string">Suspend</attribute>
613+ <attribute name="groups">contract_isp.group_isp_agent2</attribute>
614+ </xpath>
615+ </field>
616+ </record>
617
618 <act_window
619 id="action_contract_service_manage"
620
621=== modified file 'contract_isp/i18n/contract_isp.pot'
622--- contract_isp/i18n/contract_isp.pot 2013-09-16 17:59:01 +0000
623+++ contract_isp/i18n/contract_isp.pot 2014-11-24 15:05:14 +0000
624@@ -6,8 +6,8 @@
625 msgstr ""
626 "Project-Id-Version: OpenERP Server 7.0\n"
627 "Report-Msgid-Bugs-To: \n"
628-"POT-Creation-Date: 2013-08-13 12:37+0000\n"
629-"PO-Revision-Date: 2013-08-13 12:37+0000\n"
630+"POT-Creation-Date: 2014-09-21 02:02+0000\n"
631+"PO-Revision-Date: 2014-09-21 02:02+0000\n"
632 "Last-Translator: <>\n"
633 "Language-Team: \n"
634 "MIME-Version: 1.0\n"
635@@ -16,26 +16,32 @@
636 "Plural-Forms: \n"
637
638 #. module: contract_isp
639-#: field:account.analytic.line,contract_service_id:0
640-#: field:contract.service.activate,service_id:0
641-msgid "Service"
642-msgstr ""
643-
644-#. module: contract_isp
645-#: selection:contract.service,analytic_line_type:0
646-#: selection:product.product,analytic_line_type:0
647-msgid "Recurrent"
648-msgstr ""
649-
650-#. module: contract_isp
651-#: selection:contract.service,analytic_line_type:0
652-#: selection:product.product,analytic_line_type:0
653-msgid "Exception"
654-msgstr ""
655-
656-#. module: contract_isp
657-#: field:contract.service,state:0
658-msgid "State"
659+#: field:contract.service.activate,account_id:0
660+#: field:contract.service.deactivate,account_id:0
661+msgid "Account"
662+msgstr ""
663+
664+#. module: contract_isp
665+#: view:account.analytic.account:0
666+#: view:contract.service:0
667+#: view:contract.service.activate:0
668+msgid "Activate"
669+msgstr ""
670+
671+#. module: contract_isp
672+#: view:contract.service.activate:0
673+#: model:ir.actions.act_window,name:contract_isp.action_view_contract_service_activate
674+msgid "Activate Service"
675+msgstr ""
676+
677+#. module: contract_isp
678+#: field:contract.service.activate,activation_date:0
679+msgid "Activation Date"
680+msgstr ""
681+
682+#. module: contract_isp
683+#: field:contract.service,activation_line_generated:0
684+msgid "Activation Line Generated?"
685 msgstr ""
686
687 #. module: contract_isp
688@@ -44,23 +50,56 @@
689 msgstr ""
690
691 #. module: contract_isp
692-#: field:res.partner,account_analytic_account_ids:0
693-msgid "Analytic Accounts/Contracts"
694-msgstr ""
695-
696-#. module: contract_isp
697-#: field:contract.service,analytic_line_type:0
698-msgid "Type"
699-msgstr ""
700-
701-#. module: contract_isp
702-#: model:res.groups,comment:contract_isp.group_isp_agent
703-msgid "The user will be able to manage customers and contracts"
704-msgstr ""
705-
706-#. module: contract_isp
707-#: view:account.analytic.account:0
708-msgid "Total Price"
709+#: selection:contract.service,state:0
710+msgid "Active"
711+msgstr ""
712+
713+#. module: contract_isp
714+#: model:res.groups,name:contract_isp.group_isp_agent
715+msgid "Agent Level 1"
716+msgstr ""
717+
718+#. module: contract_isp
719+#: model:res.groups,name:contract_isp.group_isp_agent2
720+msgid "Agent Level 2"
721+msgstr ""
722+
723+#. module: contract_isp
724+#: code:_description:0
725+#: view:contract.service:0
726+#: model:ir.model,name:contract_isp.model_account_analytic_account
727+#, python-format
728+msgid "Analytic Account"
729+msgstr ""
730+
731+#. module: contract_isp
732+#: code:_description:0
733+#: model:ir.model,name:contract_isp.model_account_analytic_line
734+#, python-format
735+msgid "Analytic Line"
736+msgstr ""
737+
738+#. module: contract_isp
739+#: view:contract.service.activate:0
740+#: view:contract.service.deactivate:0
741+msgid "Cancel"
742+msgstr ""
743+
744+#. module: contract_isp
745+#: code:_description:0
746+#: model:ir.model,name:contract_isp.model_res_company
747+#, python-format
748+msgid "Companies"
749+msgstr ""
750+
751+#. module: contract_isp
752+#: field:contract.service,account_id:0
753+msgid "Contract"
754+msgstr ""
755+
756+#. module: contract_isp
757+#: view:contract.service:0
758+msgid "Contract Services"
759 msgstr ""
760
761 #. module: contract_isp
762@@ -69,19 +108,137 @@
763 msgstr ""
764
765 #. module: contract_isp
766+#: field:res.company,cutoff_day:0
767+msgid "Cutoff day"
768+msgstr ""
769+
770+#. module: contract_isp
771+#: model:account.analytic.journal,name:contract_isp.analytic_journal_data
772+msgid "Data Use"
773+msgstr ""
774+
775+#. module: contract_isp
776+#: view:contract.service.deactivate:0
777+msgid "Deactivate"
778+msgstr ""
779+
780+#. module: contract_isp
781+#: model:ir.actions.act_window,name:contract_isp.action_view_contract_service_deactivate
782+msgid "Deactivate Service"
783+msgstr ""
784+
785+#. module: contract_isp
786+#: field:contract.service.deactivate,deactivation_date:0
787+msgid "Deactivation Date"
788+msgstr ""
789+
790+#. module: contract_isp
791+#: field:contract.service,deactivation_date:0
792+msgid "Deactivation date"
793+msgstr ""
794+
795+#. module: contract_isp
796+#: view:contract.service.deactivate:0
797+msgid "Dectivate Service"
798+msgstr ""
799+
800+#. module: contract_isp
801+#: field:res.company,default_journal_id:0
802+msgid "Default Journal"
803+msgstr ""
804+
805+#. module: contract_isp
806+#: view:account.analytic.account:0
807+#: view:contract.service:0
808+msgid "Desactivate"
809+msgstr ""
810+
811+#. module: contract_isp
812+#: field:contract.service,name:0
813+msgid "Description"
814+msgstr ""
815+
816+#. module: contract_isp
817+#: field:contract.service,duration:0
818+msgid "Duration"
819+msgstr ""
820+
821+#. module: contract_isp
822+#: selection:contract.service,analytic_line_type:0
823+#: selection:product.product,analytic_line_type:0
824+msgid "Exception"
825+msgstr ""
826+
827+#. module: contract_isp
828+#: view:res.company:0
829+msgid "ISP Contract Management"
830+msgstr ""
831+
832+#. module: contract_isp
833+#: selection:contract.service,state:0
834+msgid "Inactive"
835+msgstr ""
836+
837+#. module: contract_isp
838+#: selection:contract.service,analytic_line_type:0
839+#: selection:product.product,analytic_line_type:0
840+msgid "One time"
841+msgstr ""
842+
843+#. module: contract_isp
844+#: field:res.company,parent_account_id:0
845+msgid "Parent Analytic Account"
846+msgstr ""
847+
848+#. module: contract_isp
849+#: code:_description:0
850+#: model:ir.model,name:contract_isp.model_res_partner
851+#, python-format
852+msgid "Partner"
853+msgstr ""
854+
855+#. module: contract_isp
856+#: field:res.partner,partner_analytic_account_id:0
857+msgid "Partner Analytic Account"
858+msgstr ""
859+
860+#. module: contract_isp
861+#: field:contract.service,price:0
862+msgid "Price"
863+msgstr ""
864+
865+#. module: contract_isp
866+#: code:_description:0
867 #: field:contract.service,product_id:0
868 #: model:ir.model,name:contract_isp.model_product_product
869+#, python-format
870 msgid "Product"
871 msgstr ""
872
873 #. module: contract_isp
874-#: model:ir.model,name:contract_isp.model_account_analytic_line
875-msgid "Analytic Line"
876-msgstr ""
877-
878-#. module: contract_isp
879-#: field:contract.service,price:0
880-msgid "Price"
881+#: field:contract.service,category_id:0
882+msgid "Product Category"
883+msgstr ""
884+
885+#. module: contract_isp
886+#: field:account.analytic.line,is_prorata:0
887+msgid "Prorata"
888+msgstr ""
889+
890+#. module: contract_isp
891+#: field:contract.service,qty:0
892+msgid "Qty"
893+msgstr ""
894+
895+#. module: contract_isp
896+#: selection:contract.service,analytic_line_type:0
897+#: selection:product.product,analytic_line_type:0
898+msgid "Recurrent"
899+msgstr ""
900+
901+#. module: contract_isp
902+#: model:account.analytic.journal,name:contract_isp.analytic_journal_recurrent
903+msgid "Recurrent Service"
904 msgstr ""
905
906 #. module: contract_isp
907@@ -91,87 +248,58 @@
908 msgstr ""
909
910 #. module: contract_isp
911+#: field:account.analytic.line,contract_service_id:0
912+#: field:contract.service.activate,service_id:0
913+#: field:contract.service.deactivate,service_id:0
914+msgid "Service"
915+msgstr ""
916+
917+#. module: contract_isp
918+#: view:account.analytic.account:0
919+#: field:account.analytic.account,contract_service_ids:0
920+#: model:ir.actions.act_window,name:contract_isp.action_contract_service_manage
921+msgid "Services"
922+msgstr ""
923+
924+#. module: contract_isp
925+#: field:contract.service,state:0
926+msgid "State"
927+msgstr ""
928+
929+#. module: contract_isp
930+#: view:account.analytic.account:0
931+msgid "Suspend"
932+msgstr ""
933+
934+#. module: contract_isp
935+#: model:res.groups,comment:contract_isp.group_isp_agent
936+#: model:res.groups,comment:contract_isp.group_isp_agent2
937+msgid "The users in this group will be able to manage customers and contracts"
938+msgstr ""
939+
940+#. module: contract_isp
941+#: view:account.analytic.account:0
942+msgid "Total Price"
943+msgstr ""
944+
945+#. module: contract_isp
946+#: field:contract.service,analytic_line_type:0
947+msgid "Type"
948+msgstr ""
949+
950+#. module: contract_isp
951 #: field:product.product,analytic_line_type:0
952 msgid "Type in contract"
953 msgstr ""
954
955 #. module: contract_isp
956-#: selection:contract.service,state:0
957-msgid "Inactive"
958-msgstr ""
959-
960-#. module: contract_isp
961-#: field:res.company,parent_account_id:0
962-msgid "Parent Analytic Account"
963-msgstr ""
964-
965-#. module: contract_isp
966-#: selection:contract.service,state:0
967-msgid "Active"
968-msgstr ""
969-
970-#. module: contract_isp
971-#: view:res.company:0
972-msgid "ISP Contract Management"
973-msgstr ""
974-
975-#. module: contract_isp
976-#: field:res.partner,analytic_data_lines_ids:0
977-#: field:res.partner,analytic_tel_lines_ids:0
978-msgid "Account Lines"
979-msgstr ""
980-
981-#. module: contract_isp
982-#: field:contract.service.activate,account_id:0
983-msgid "Account"
984-msgstr ""
985-
986-#. module: contract_isp
987-#: selection:contract.service,analytic_line_type:0
988-#: selection:product.product,analytic_line_type:0
989-msgid "One time"
990-msgstr ""
991-
992-#. module: contract_isp
993-#: view:account.analytic.account:0
994-#: view:contract.service:0
995-msgid "Desactivate"
996-msgstr ""
997-
998-#. module: contract_isp
999-#: model:ir.model,name:contract_isp.model_contract_service_activate
1000-msgid "contract.service.activate"
1001-msgstr ""
1002-
1003-#. module: contract_isp
1004-#: model:ir.model,name:contract_isp.model_res_company
1005-msgid "Companies"
1006-msgstr ""
1007-
1008-#. module: contract_isp
1009-#: field:contract.service,account_id:0
1010-msgid "Contract"
1011-msgstr ""
1012-
1013-#. module: contract_isp
1014-#: view:contract.service.activate:0
1015-msgid "Cancel"
1016-msgstr ""
1017-
1018-#. module: contract_isp
1019-#: view:contract.service.activate:0
1020-#: model:ir.actions.act_window,name:contract_isp.action_view_contract_service_activate
1021-msgid "Activate Service"
1022-msgstr ""
1023-
1024-#. module: contract_isp
1025-#: field:res.company,cutoff_day:0
1026-msgid "Cutoff day"
1027-msgstr ""
1028-
1029-#. module: contract_isp
1030-#: field:res.partner,payments_ids:0
1031-msgid "Payments"
1032+#: field:contract.service,unit_price:0
1033+msgid "Unit Price"
1034+msgstr ""
1035+
1036+#. module: contract_isp
1037+#: model:account.analytic.journal,name:contract_isp.analytic_journal_tel
1038+msgid "Voice Use"
1039 msgstr ""
1040
1041 #. module: contract_isp
1042@@ -180,72 +308,38 @@
1043 msgstr ""
1044
1045 #. module: contract_isp
1046-#: field:res.partner,partner_analytic_account_id:0
1047-msgid "Partner Analytic Account"
1048-msgstr ""
1049-
1050-#. module: contract_isp
1051-#: view:account.analytic.account:0
1052-#: view:contract.service:0
1053-#: view:contract.service.activate:0
1054-msgid "Activate"
1055-msgstr ""
1056-
1057-#. module: contract_isp
1058-#: field:contract.service,account_line_id:0
1059-msgid "Account Entry"
1060-msgstr ""
1061-
1062-#. module: contract_isp
1063+#: constraint:contract.service.activate:0
1064+msgid "You cannot activate a service in the future."
1065+msgstr ""
1066+
1067+#. module: contract_isp
1068+#: constraint:contract.service.deactivate:0
1069+msgid "You cannot deactivate a service in the future."
1070+msgstr ""
1071+
1072+#. module: contract_isp
1073+#: code:_description:0
1074 #: model:ir.model,name:contract_isp.model_contract_service
1075+#, python-format
1076 msgid "contract.service"
1077 msgstr ""
1078
1079 #. module: contract_isp
1080-#: model:res.groups,name:contract_isp.group_isp_agent
1081-msgid "Agent"
1082-msgstr ""
1083-
1084-#. module: contract_isp
1085-#: view:account.analytic.account:0
1086-msgid "Suspend"
1087-msgstr ""
1088-
1089-#. module: contract_isp
1090-#: view:contract.service:0
1091-#: model:ir.model,name:contract_isp.model_account_analytic_account
1092-msgid "Analytic Account"
1093-msgstr ""
1094-
1095-#. module: contract_isp
1096-#: field:contract.service.activate,activation_date:0
1097-msgid "Activation Date"
1098-msgstr ""
1099-
1100-#. module: contract_isp
1101-#: view:account.analytic.account:0
1102-#: field:account.analytic.account,contract_service_ids:0
1103-#: model:ir.actions.act_window,name:contract_isp.action_contract_service_manage
1104-msgid "Services"
1105-msgstr ""
1106-
1107-#. module: contract_isp
1108-#: field:contract.service,duration:0
1109-msgid "Duration"
1110-msgstr ""
1111-
1112-#. module: contract_isp
1113-#: model:ir.model,name:contract_isp.model_res_partner
1114-msgid "Partner"
1115-msgstr ""
1116-
1117-#. module: contract_isp
1118-#: field:contract.service,activation_line_generated:0
1119-msgid "Activation Line Generated?"
1120-msgstr ""
1121-
1122-#. module: contract_isp
1123-#: view:contract.service:0
1124-msgid "Contract Services"
1125+#: code:_description:0
1126+#: model:ir.model,name:contract_isp.model_contract_service_activate
1127+#, python-format
1128+msgid "contract.service.activate"
1129+msgstr ""
1130+
1131+#. module: contract_isp
1132+#: code:_description:0
1133+#: model:ir.model,name:contract_isp.model_contract_service_deactivate
1134+#, python-format
1135+msgid "contract.service.deactivate"
1136+msgstr ""
1137+
1138+#. module: contract_isp
1139+#: view:account.analytic.account:0
1140+msgid "contract_isp.group_isp_agent2"
1141 msgstr ""
1142
1143
1144=== added file 'contract_isp/i18n/fr.po'
1145--- contract_isp/i18n/fr.po 1970-01-01 00:00:00 +0000
1146+++ contract_isp/i18n/fr.po 2014-11-24 15:05:14 +0000
1147@@ -0,0 +1,338 @@
1148+# Translation of OpenERP Server.
1149+# This file contains the translation of the following modules:
1150+# * contract_isp
1151+#
1152+msgid ""
1153+msgstr ""
1154+"Project-Id-Version: OpenERP Server 7.0\n"
1155+"Report-Msgid-Bugs-To: \n"
1156+"POT-Creation-Date: 2014-09-21 02:02+0000\n"
1157+"PO-Revision-Date: 2014-09-20 22:45-0500\n"
1158+"Last-Translator: Marc Cassuto <marc.cassuto@savoirfairelinux.com>\n"
1159+"Language-Team: \n"
1160+"Language: fr\n"
1161+"MIME-Version: 1.0\n"
1162+"Content-Type: text/plain; charset=UTF-8\n"
1163+"Content-Transfer-Encoding: 8bit\n"
1164+"X-Generator: Poedit 1.5.4\n"
1165+
1166+#. module: contract_isp
1167+#: field:contract.service.activate,account_id:0
1168+#: field:contract.service.deactivate,account_id:0
1169+msgid "Account"
1170+msgstr "Compte"
1171+
1172+#. module: contract_isp
1173+#: view:account.analytic.account:0 view:contract.service:0
1174+#: view:contract.service.activate:0
1175+msgid "Activate"
1176+msgstr "Activer"
1177+
1178+#. module: contract_isp
1179+#: view:contract.service.activate:0
1180+#: model:ir.actions.act_window,name:contract_isp.action_view_contract_service_activate
1181+msgid "Activate Service"
1182+msgstr "Activer un Service"
1183+
1184+#. module: contract_isp
1185+#: field:contract.service.activate,activation_date:0
1186+msgid "Activation Date"
1187+msgstr "Date d'Activation"
1188+
1189+#. module: contract_isp
1190+#: field:contract.service,activation_line_generated:0
1191+msgid "Activation Line Generated?"
1192+msgstr "Ligne d'Activation Générée ?"
1193+
1194+#. module: contract_isp
1195+#: field:contract.service,activation_date:0
1196+msgid "Activation date"
1197+msgstr "Date d'Activation"
1198+
1199+#. module: contract_isp
1200+#: selection:contract.service,state:0
1201+msgid "Active"
1202+msgstr "Actif"
1203+
1204+#. module: contract_isp
1205+#: model:res.groups,name:contract_isp.group_isp_agent
1206+msgid "Agent Level 1"
1207+msgstr "Agent Niveau 1"
1208+
1209+#. module: contract_isp
1210+#: model:res.groups,name:contract_isp.group_isp_agent2
1211+msgid "Agent Level 2"
1212+msgstr "Agent niveau 2"
1213+
1214+#. module: contract_isp
1215+#: code:_description:0 view:contract.service:0
1216+#: model:ir.model,name:contract_isp.model_account_analytic_account
1217+#, python-format
1218+msgid "Analytic Account"
1219+msgstr "Contrat"
1220+
1221+#. module: contract_isp
1222+#: code:_description:0
1223+#: model:ir.model,name:contract_isp.model_account_analytic_line
1224+#, python-format
1225+msgid "Analytic Line"
1226+msgstr "Lignes facturables"
1227+
1228+#. module: contract_isp
1229+#: view:contract.service.activate:0 view:contract.service.deactivate:0
1230+msgid "Cancel"
1231+msgstr "Annuler"
1232+
1233+#. module: contract_isp
1234+#: code:_description:0 model:ir.model,name:contract_isp.model_res_company
1235+#, python-format
1236+msgid "Companies"
1237+msgstr "Companies"
1238+
1239+#. module: contract_isp
1240+#: field:contract.service,account_id:0
1241+msgid "Contract"
1242+msgstr "Contrat"
1243+
1244+#. module: contract_isp
1245+#: view:contract.service:0
1246+msgid "Contract Services"
1247+msgstr "Services du Contrat"
1248+
1249+#. module: contract_isp
1250+#: field:account.analytic.account,use_contract_services:0
1251+msgid "Contract services"
1252+msgstr "Services du Contrat"
1253+
1254+#. module: contract_isp
1255+#: field:res.company,cutoff_day:0
1256+msgid "Cutoff day"
1257+msgstr "Date de Cut-off"
1258+
1259+#. module: contract_isp
1260+#: model:account.analytic.journal,name:contract_isp.analytic_journal_data
1261+msgid "Data Use"
1262+msgstr "Consommations de données"
1263+
1264+#. module: contract_isp
1265+#: view:contract.service.deactivate:0
1266+msgid "Deactivate"
1267+msgstr "Désactiver"
1268+
1269+#. module: contract_isp
1270+#: model:ir.actions.act_window,name:contract_isp.action_view_contract_service_deactivate
1271+msgid "Deactivate Service"
1272+msgstr "Désactiver un Service"
1273+
1274+#. module: contract_isp
1275+#: field:contract.service.deactivate,deactivation_date:0
1276+msgid "Deactivation Date"
1277+msgstr "Date de Désactivation"
1278+
1279+#. module: contract_isp
1280+#: field:contract.service,deactivation_date:0
1281+msgid "Deactivation date"
1282+msgstr "Date de Désactivation"
1283+
1284+#. module: contract_isp
1285+#: view:contract.service.deactivate:0
1286+msgid "Dectivate Service"
1287+msgstr "Désactiver un Service"
1288+
1289+#. module: contract_isp
1290+#: field:res.company,default_journal_id:0
1291+msgid "Default Journal"
1292+msgstr "Journal Comptable par défaut"
1293+
1294+#. module: contract_isp
1295+#: view:account.analytic.account:0 view:contract.service:0
1296+msgid "Desactivate"
1297+msgstr "Désactiver"
1298+
1299+#. module: contract_isp
1300+#: field:contract.service,name:0
1301+msgid "Description"
1302+msgstr "Description"
1303+
1304+#. module: contract_isp
1305+#: field:contract.service,duration:0
1306+msgid "Duration"
1307+msgstr "Durée"
1308+
1309+#. module: contract_isp
1310+#: selection:contract.service,analytic_line_type:0
1311+#: selection:product.product,analytic_line_type:0
1312+msgid "Exception"
1313+msgstr "Exception"
1314+
1315+#. module: contract_isp
1316+#: view:res.company:0
1317+msgid "ISP Contract Management"
1318+msgstr "Gestion de contrats pour les FSI"
1319+
1320+#. module: contract_isp
1321+#: selection:contract.service,state:0
1322+msgid "Inactive"
1323+msgstr "Inactif"
1324+
1325+#. module: contract_isp
1326+#: selection:contract.service,analytic_line_type:0
1327+#: selection:product.product,analytic_line_type:0
1328+msgid "One time"
1329+msgstr "Facturé une fois"
1330+
1331+#. module: contract_isp
1332+#: field:res.company,parent_account_id:0
1333+msgid "Parent Analytic Account"
1334+msgstr "Contrat parent"
1335+
1336+#. module: contract_isp
1337+#: code:_description:0 model:ir.model,name:contract_isp.model_res_partner
1338+#, python-format
1339+msgid "Partner"
1340+msgstr "Partenaire"
1341+
1342+#. module: contract_isp
1343+#: field:res.partner,partner_analytic_account_id:0
1344+msgid "Partner Analytic Account"
1345+msgstr "Contrat du partenaire"
1346+
1347+#. module: contract_isp
1348+#: field:contract.service,price:0
1349+msgid "Price"
1350+msgstr "Prix"
1351+
1352+#. module: contract_isp
1353+#: code:_description:0 field:contract.service,product_id:0
1354+#: model:ir.model,name:contract_isp.model_product_product
1355+#, python-format
1356+msgid "Product"
1357+msgstr "Produit"
1358+
1359+#. module: contract_isp
1360+#: field:contract.service,category_id:0
1361+msgid "Product Category"
1362+msgstr "Catégorie de Produit"
1363+
1364+#. module: contract_isp
1365+#: field:account.analytic.line,is_prorata:0
1366+msgid "Prorata"
1367+msgstr "Pro-rata"
1368+
1369+#. module: contract_isp
1370+#: field:contract.service,qty:0
1371+msgid "Qty"
1372+msgstr "Qté"
1373+
1374+#. module: contract_isp
1375+#: selection:contract.service,analytic_line_type:0
1376+#: selection:product.product,analytic_line_type:0
1377+msgid "Recurrent"
1378+msgstr "Récurrent"
1379+
1380+#. module: contract_isp
1381+#: model:account.analytic.journal,name:contract_isp.analytic_journal_recurrent
1382+msgid "Recurrent Service"
1383+msgstr "Service Récurrent"
1384+
1385+#. module: contract_isp
1386+#: field:contract.service,require_activation:0
1387+#: field:product.product,require_activation:0
1388+msgid "Require activation"
1389+msgstr "Nécessite une acitvation"
1390+
1391+#. module: contract_isp
1392+#: field:account.analytic.line,contract_service_id:0
1393+#: field:contract.service.activate,service_id:0
1394+#: field:contract.service.deactivate,service_id:0
1395+msgid "Service"
1396+msgstr "Service"
1397+
1398+#. module: contract_isp
1399+#: view:account.analytic.account:0
1400+#: field:account.analytic.account,contract_service_ids:0
1401+#: model:ir.actions.act_window,name:contract_isp.action_contract_service_manage
1402+msgid "Services"
1403+msgstr "Services"
1404+
1405+#. module: contract_isp
1406+#: field:contract.service,state:0
1407+msgid "State"
1408+msgstr "Statut"
1409+
1410+#. module: contract_isp
1411+#: view:account.analytic.account:0
1412+msgid "Suspend"
1413+msgstr "Suspendre"
1414+
1415+#. module: contract_isp
1416+#: model:res.groups,comment:contract_isp.group_isp_agent
1417+#: model:res.groups,comment:contract_isp.group_isp_agent2
1418+msgid "The users in this group will be able to manage customers and contracts"
1419+msgstr ""
1420+"Les utilisateurs de ce groupe pourront gérer les clients et les contrats"
1421+
1422+#. module: contract_isp
1423+#: view:account.analytic.account:0
1424+msgid "Total Price"
1425+msgstr "Prix Total"
1426+
1427+#. module: contract_isp
1428+#: field:contract.service,analytic_line_type:0
1429+msgid "Type"
1430+msgstr "Type"
1431+
1432+#. module: contract_isp
1433+#: field:product.product,analytic_line_type:0
1434+msgid "Type in contract"
1435+msgstr "Type dans le contrat"
1436+
1437+#. module: contract_isp
1438+#: field:contract.service,unit_price:0
1439+msgid "Unit Price"
1440+msgstr "Prix Unitaire"
1441+
1442+#. module: contract_isp
1443+#: model:account.analytic.journal,name:contract_isp.analytic_journal_tel
1444+msgid "Voice Use"
1445+msgstr "Consommation Téléphonique"
1446+
1447+#. module: contract_isp
1448+#: selection:contract.service,state:0
1449+msgid "Waiting for activating"
1450+msgstr "En attente d'activation"
1451+
1452+#. module: contract_isp
1453+#: constraint:contract.service.activate:0
1454+msgid "You cannot activate a service in the future."
1455+msgstr "Vous ne pouvez pas activer un service dans le futur."
1456+
1457+#. module: contract_isp
1458+#: constraint:contract.service.deactivate:0
1459+msgid "You cannot deactivate a service in the future."
1460+msgstr "Vous ne pouvez pas désactiver un service dans le futur."
1461+
1462+#. module: contract_isp
1463+#: code:_description:0 model:ir.model,name:contract_isp.model_contract_service
1464+#, python-format
1465+msgid "contract.service"
1466+msgstr "contract.service"
1467+
1468+#. module: contract_isp
1469+#: code:_description:0
1470+#: model:ir.model,name:contract_isp.model_contract_service_activate
1471+#, python-format
1472+msgid "contract.service.activate"
1473+msgstr "contract.service.activate"
1474+
1475+#. module: contract_isp
1476+#: code:_description:0
1477+#: model:ir.model,name:contract_isp.model_contract_service_deactivate
1478+#, python-format
1479+msgid "contract.service.deactivate"
1480+msgstr "contract.service.deactivate"
1481+
1482+#. module: contract_isp
1483+#: view:account.analytic.account:0
1484+msgid "contract_isp.group_isp_agent2"
1485+msgstr "contract_isp.group_isp_agent2"
1486
1487=== modified file 'contract_isp/wizard/activate_contract_service.py'
1488--- contract_isp/wizard/activate_contract_service.py 2013-09-16 17:59:01 +0000
1489+++ contract_isp/wizard/activate_contract_service.py 2014-11-24 15:05:14 +0000
1490@@ -21,30 +21,38 @@
1491 ##############################################################################
1492
1493 import datetime
1494+
1495+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
1496+from openerp.tools.translate import _
1497 from openerp.osv import orm, fields
1498-from openerp.addons.contract_isp.contract import add_months
1499+
1500+from ..contract import LINE_TYPE_RECURRENT
1501+
1502+
1503+def _get_account_id(self, cr, uid, context=None):
1504+ if context.get('active_model', '') == 'contract.service':
1505+ contract_id = context.get('active_id')
1506+ contract_service = self.pool.get('contract.service').browse(
1507+ cr, uid, contract_id, context)
1508+
1509+ return contract_service.account_id.id
1510+ return None
1511+
1512+
1513+def _get_service_id(self, cr, uid, context=None):
1514+ if context.get('active_model', '') == 'contract.service':
1515+ service_id = context.get('active_id')
1516+ contract_service = self.pool.get('contract.service').browse(
1517+ cr, uid, service_id, context)
1518+
1519+ return contract_service.id
1520+ return None
1521
1522
1523 class contract_service_activate(orm.TransientModel):
1524 _name = 'contract.service.activate'
1525-
1526- def _get_account_id(self, cr, uid, context=None):
1527- if context.get('active_model', '') == 'contract.service':
1528- contract_id = context.get('active_id')
1529- contract_service = self.pool.get('contract.service').browse(
1530- cr, uid, contract_id, context)
1531-
1532- return contract_service.account_id.id
1533- return None
1534-
1535- def _get_service_id(self, cr, uid, context=None):
1536- if context.get('active_model', '') == 'contract.service':
1537- service_id = context.get('active_id')
1538- contract_service = self.pool.get('contract.service').browse(
1539- cr, uid, service_id, context)
1540-
1541- return contract_service.id
1542- return None
1543+ _get_account_id = _get_account_id
1544+ _get_service_id = _get_service_id
1545
1546 _columns = {
1547 'activation_date': fields.datetime('Activation Date'),
1548@@ -54,15 +62,27 @@
1549
1550 _defaults = {
1551 'activation_date': fields.datetime.now,
1552- 'account_id': lambda s, cr, uid, ctx: s._get_account_id(cr, uid, ctx),
1553- 'service_id': lambda s, cr, uid, ctx: s._get_service_id(cr, uid, ctx)
1554+ 'account_id': _get_account_id,
1555+ 'service_id': _get_service_id,
1556 }
1557
1558 def activate(self, cr, uid, ids, context=None):
1559 wizard = self.browse(cr, uid, ids[0], context)
1560- company_obj = self.pool.get('res.company')
1561- company_id = company_obj._company_default_get(cr, uid, context)
1562- cutoff = company_obj.read(cr, uid, company_id, 'cutoff_day', context)
1563+ contract_service_obj = self.pool.get('contract.service')
1564+ contract_service = contract_service_obj.browse(
1565+ cr, uid, wizard.service_id.id, context)
1566+
1567+ contract_service.write({
1568+ 'activation_date': wizard.activation_date,
1569+ 'state': 'active'
1570+ })
1571+
1572+ self._create_analytic_lines(cr, uid, ids[0], context)
1573+
1574+ return True
1575+
1576+ def _create_analytic_lines(self, cr, uid, wiz_id, context=None):
1577+ wizard = self.browse(cr, uid, wiz_id, context)
1578 contract_service_obj = self.pool.get('contract.service')
1579 contract_service = contract_service_obj.browse(
1580 cr, uid, wizard.service_id.id, context)
1581@@ -72,48 +92,102 @@
1582 int(wizard.activation_date[5:7]),
1583 int(wizard.activation_date[8:10]))
1584
1585- cuttoff_day = company_obj.read(
1586- cr, uid,
1587- company_id,
1588- fields=['cutoff_day'],
1589- context=context)['cutoff_day']
1590-
1591- invoice_day = company_obj.read(
1592- cr, uid,
1593- company_id,
1594- fields=['invoice_day'],
1595- context=context)['invoice_day']
1596-
1597- cutoff_date = datetime.date(
1598- datetime.date.today().year,
1599- datetime.date.today().month,
1600- int(cuttoff_day))
1601-
1602- invoice_date = datetime.date(
1603- datetime.date.today().year,
1604- datetime.date.today().month,
1605- int(invoice_day))
1606-
1607- contract_service.write({
1608- 'activation_date': wizard.activation_date,
1609- 'state': 'active'
1610+ # When registering an initial payment, we generate activation lines
1611+ # we do not wish to generate another month entry if not needed
1612+ # vvinet: This is not currently part of our scenarios, remove it
1613+ # it can be put back if we want to handle it
1614+ # if contract_service.activation_line_generated is False:
1615+ # contract_service.create_analytic_line(mode='manual',
1616+ # date=activation_date,
1617+ # context=context)
1618+ # contract_service.write({'activation_line_generated': True})
1619+
1620+ # Upon activating, always create activation lines
1621+ if contract_service.analytic_line_type == LINE_TYPE_RECURRENT:
1622+ contract_service.create_analytic_line(mode='prorata',
1623+ date=activation_date,
1624+ context=context)
1625+
1626+ def _check_future_date(self, cr, uid, ids, context=None):
1627+ today = datetime.datetime.utcnow().date()
1628+ for wiz in self.browse(cr, uid, ids, context=None):
1629+ act_date = datetime.datetime.strptime(
1630+ wiz.activation_date, DEFAULT_SERVER_DATETIME_FORMAT,
1631+ ).date()
1632+
1633+ if act_date > today:
1634+ return False
1635+ return True
1636+
1637+ _constraints = [
1638+ (_check_future_date,
1639+ _('You cannot activate a service in the future.'),
1640+ ['activation_date']),
1641+ ]
1642+
1643+
1644+class contract_service_deactivate(orm.TransientModel):
1645+ _name = 'contract.service.deactivate'
1646+
1647+ _columns = {
1648+ 'deactivation_date': fields.datetime('Deactivation Date'),
1649+ 'account_id': fields.many2one('account.analytic.account', 'Account'),
1650+ 'service_id': fields.many2one('contract.service', 'Service')
1651+ }
1652+
1653+ _defaults = {
1654+ 'deactivation_date': fields.datetime.now,
1655+ 'account_id': _get_account_id,
1656+ 'service_id': _get_service_id,
1657+ }
1658+
1659+ def deactivate(self, cr, uid, ids, context=None):
1660+ context = context or {}
1661+ wizard = self.browse(cr, uid, ids[0], context)
1662+ contract_service_obj = self.pool.get('contract.service')
1663+ context["deactivation_date"] = wizard.deactivation_date
1664+ contract_service_obj.action_deactivate(cr, uid,
1665+ [wizard.service_id.id],
1666+ context=context)
1667+ # If we activate again, we will need a new activation line, as
1668+ # we will have refunded any extra, and will need to bill in
1669+ # advance again.
1670+ contract_service_obj.write(cr, uid, [wizard.service_id.id], {
1671+ 'activation_line_generated': False,
1672 })
1673
1674- query = [
1675- ('account_id', '=', wizard.account_id.id),
1676- ('state', '=', 'draft')
1677- ]
1678- draft_line_ids = contract_service_obj.search(cr, uid, query,
1679- context=context)
1680-
1681- if not draft_line_ids:
1682- for line in wizard.account_id.contract_service_ids:
1683- if line.activation_line_generated is False:
1684- line.create_analytic_line(mode='manual',
1685- date=activation_date)
1686-
1687- if line.analytic_line_type == 'r':
1688- line.create_analytic_line(mode='prorata',
1689- date=activation_date)
1690-
1691- return True
1692+ self._create_refund_lines(cr, uid, ids[0], context)
1693+ return True
1694+
1695+ def _create_refund_lines(self, cr, uid, wiz_id, context=None):
1696+ wizard = self.browse(cr, uid, wiz_id, context)
1697+ contract_service_obj = self.pool.get('contract.service')
1698+
1699+ deactivate_date = datetime.datetime.strptime(
1700+ wizard.deactivation_date,
1701+ DEFAULT_SERVER_DATETIME_FORMAT,
1702+ ).date()
1703+
1704+ contract_service_obj.create_refund_line(
1705+ cr, uid,
1706+ [wizard.service_id.id],
1707+ mode='prorata',
1708+ date=deactivate_date,
1709+ context=context)
1710+
1711+ def _check_future_date(self, cr, uid, ids, context=None):
1712+ today = datetime.datetime.utcnow().date()
1713+ for wiz in self.browse(cr, uid, ids, context=None):
1714+ act_date = datetime.datetime.strptime(
1715+ wiz.deactivation_date, DEFAULT_SERVER_DATETIME_FORMAT,
1716+ ).date()
1717+
1718+ if act_date > today:
1719+ return False
1720+ return True
1721+
1722+ _constraints = [
1723+ (_check_future_date,
1724+ _('You cannot deactivate a service in the future.'),
1725+ ['deactivation_date']),
1726+ ]
1727
1728=== modified file 'contract_isp/wizard/activate_contract_service.xml'
1729--- contract_isp/wizard/activate_contract_service.xml 2013-09-16 17:59:01 +0000
1730+++ contract_isp/wizard/activate_contract_service.xml 2014-11-24 15:05:14 +0000
1731@@ -1,40 +1,75 @@
1732 <?xml version="1.0" encoding="utf-8"?>
1733 <openerp>
1734- <data>
1735- <record id="view_contract_service_activate" model="ir.ui.view">
1736- <field name="name">Activate Service</field>
1737- <field name="model">contract.service.activate</field>
1738- <field name="arch" type="xml">
1739- <form string="Activate Service" version="7.0">
1740- <group>
1741- <field name="activation_date" />
1742- <field name="account_id" invisible="1" />
1743- <field name="service_id" invisible="1" />
1744- </group>
1745- <footer>
1746- <button
1747- name="activate"
1748- string="Activate"
1749- type="object"
1750- class="oe_highlight" />
1751- <button
1752- string="Cancel"
1753- class="oe_link"
1754- special="cancel" />
1755- </footer>
1756- </form>
1757- </field>
1758- </record>
1759- <record id="action_view_contract_service_activate" model="ir.actions.act_window">
1760- <field name="name">Activate Service</field>
1761- <field name="type">ir.actions.act_window</field>
1762- <field name="src_model">contract.service</field>
1763- <field name="res_model">contract.service.activate</field>
1764- <field name="view_type">form</field>
1765- <field name="view_mode">form</field>
1766- <field name="context">{'default_service_id': active_id}</field>
1767- <field name="target">new</field>
1768- </record>
1769+ <data>
1770+ <record id="view_contract_service_activate" model="ir.ui.view">
1771+ <field name="name">Activate Service</field>
1772+ <field name="model">contract.service.activate</field>
1773+ <field name="arch" type="xml">
1774+ <form string="Activate Service" version="7.0">
1775+ <group>
1776+ <field name="activation_date" />
1777+ <field name="account_id" invisible="1" />
1778+ <field name="service_id" invisible="1" />
1779+ </group>
1780+ <footer>
1781+ <button
1782+ name="activate"
1783+ string="Activate"
1784+ type="object"
1785+ class="oe_highlight" />
1786+ <button
1787+ string="Cancel"
1788+ class="oe_link"
1789+ special="cancel" />
1790+ </footer>
1791+ </form>
1792+ </field>
1793+ </record>
1794+
1795+ <record id="view_contract_service_deactivate" model="ir.ui.view">
1796+ <field name="name">Deactivate Service</field>
1797+ <field name="model">contract.service.deactivate</field>
1798+ <field name="arch" type="xml">
1799+ <form string="Dectivate Service" version="7.0">
1800+ <group>
1801+ <field name="deactivation_date" />
1802+ <field name="account_id" invisible="1" />
1803+ <field name="service_id" invisible="1" />
1804+ </group>
1805+ <footer>
1806+ <button
1807+ name="deactivate"
1808+ string="Deactivate"
1809+ type="object"
1810+ class="oe_highlight" />
1811+ <button
1812+ string="Cancel"
1813+ class="oe_link"
1814+ special="cancel" />
1815+ </footer>
1816+ </form>
1817+ </field>
1818+ </record>
1819+ <record id="action_view_contract_service_activate" model="ir.actions.act_window">
1820+ <field name="name">Activate Service</field>
1821+ <field name="type">ir.actions.act_window</field>
1822+ <field name="src_model">contract.service</field>
1823+ <field name="res_model">contract.service.activate</field>
1824+ <field name="view_type">form</field>
1825+ <field name="view_mode">form</field>
1826+ <field name="context">{'default_service_id': active_id}</field>
1827+ <field name="target">new</field>
1828+ </record>
1829+ <record id="action_view_contract_service_deactivate" model="ir.actions.act_window">
1830+ <field name="name">Deactivate Service</field>
1831+ <field name="type">ir.actions.act_window</field>
1832+ <field name="src_model">contract.service</field>
1833+ <field name="res_model">contract.service.deactivate</field>
1834+ <field name="view_type">form</field>
1835+ <field name="view_mode">form</field>
1836+ <field name="context">{'default_service_id': active_id}</field>
1837+ <field name="target">new</field>
1838+ </record>
1839
1840 </data>
1841 </openerp>
1842
1843=== modified file 'contract_isp_automatic_invoicing/contract.py'
1844--- contract_isp_automatic_invoicing/contract.py 2013-11-01 16:15:27 +0000
1845+++ contract_isp_automatic_invoicing/contract.py 2014-11-24 15:05:14 +0000
1846@@ -19,7 +19,11 @@
1847 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1848 #
1849 ##############################################################################
1850-from openerp.osv import orm, fields
1851+from openerp.osv import orm
1852+
1853+from openerp.addons.contract_isp_invoice.invoice import (
1854+ PROCESS_CRON,
1855+)
1856
1857
1858 class account_analytic_account(orm.Model):
1859@@ -42,5 +46,5 @@
1860
1861 for contract_id in ids_to_invoice:
1862 self.create_analytic_lines(cr, uid, [contract_id], context=context)
1863- self.create_invoice(cr, uid, contract_id, context=context)
1864+ self.create_invoice(cr, uid, contract_id, PROCESS_CRON, context=context)
1865 cr.commit()
1866
1867=== modified file 'contract_isp_automatic_invoicing/i18n/contract_isp_automatic_invoicing.pot'
1868--- contract_isp_automatic_invoicing/i18n/contract_isp_automatic_invoicing.pot 2013-09-16 17:59:01 +0000
1869+++ contract_isp_automatic_invoicing/i18n/contract_isp_automatic_invoicing.pot 2014-11-24 15:05:14 +0000
1870@@ -0,0 +1,24 @@
1871+# Translation of OpenERP Server.
1872+# This file contains the translation of the following modules:
1873+# * contract_isp_automatic_invoicing
1874+#
1875+msgid ""
1876+msgstr ""
1877+"Project-Id-Version: OpenERP Server 7.0\n"
1878+"Report-Msgid-Bugs-To: \n"
1879+"POT-Creation-Date: 2014-09-21 02:50+0000\n"
1880+"PO-Revision-Date: 2014-09-21 02:50+0000\n"
1881+"Last-Translator: <>\n"
1882+"Language-Team: \n"
1883+"MIME-Version: 1.0\n"
1884+"Content-Type: text/plain; charset=UTF-8\n"
1885+"Content-Transfer-Encoding: \n"
1886+"Plural-Forms: \n"
1887+
1888+#. module: contract_isp_automatic_invoicing
1889+#: code:_description:0
1890+#: model:ir.model,name:contract_isp_automatic_invoicing.model_account_analytic_account
1891+#, python-format
1892+msgid "Analytic Account"
1893+msgstr ""
1894+
1895
1896=== added file 'contract_isp_automatic_invoicing/i18n/fr.po'
1897--- contract_isp_automatic_invoicing/i18n/fr.po 1970-01-01 00:00:00 +0000
1898+++ contract_isp_automatic_invoicing/i18n/fr.po 2014-11-24 15:05:14 +0000
1899@@ -0,0 +1,24 @@
1900+# Translation of OpenERP Server.
1901+# This file contains the translation of the following modules:
1902+# * contract_isp_automatic_invoicing
1903+#
1904+msgid ""
1905+msgstr ""
1906+"Project-Id-Version: OpenERP Server 7.0\n"
1907+"Report-Msgid-Bugs-To: \n"
1908+"POT-Creation-Date: 2014-09-21 02:50+0000\n"
1909+"PO-Revision-Date: 2014-09-20 22:52-0500\n"
1910+"Last-Translator: Marc Cassuto <marc.cassuto@savoirfairelinux.com>\n"
1911+"Language-Team: \n"
1912+"MIME-Version: 1.0\n"
1913+"Content-Type: text/plain; charset=UTF-8\n"
1914+"Content-Transfer-Encoding: 8bit\n"
1915+"Language: fr\n"
1916+"X-Generator: Poedit 1.5.4\n"
1917+
1918+#. module: contract_isp_automatic_invoicing
1919+#: code:_description:0
1920+#: model:ir.model,name:contract_isp_automatic_invoicing.model_account_analytic_account
1921+#, python-format
1922+msgid "Analytic Account"
1923+msgstr "Contrat"
1924
1925=== modified file 'contract_isp_invoice/__init__.py'
1926--- contract_isp_invoice/__init__.py 2013-09-16 17:59:01 +0000
1927+++ contract_isp_invoice/__init__.py 2014-11-24 15:05:14 +0000
1928@@ -20,5 +20,8 @@
1929 #
1930 ##############################################################################
1931
1932-import contract
1933-import wizard
1934+from . import (
1935+ contract,
1936+ invoice,
1937+ wizard,
1938+)
1939
1940=== modified file 'contract_isp_invoice/contract.py'
1941--- contract_isp_invoice/contract.py 2014-01-20 04:25:39 +0000
1942+++ contract_isp_invoice/contract.py 2014-11-24 15:05:14 +0000
1943@@ -20,20 +20,38 @@
1944 #
1945 ##############################################################################
1946
1947+import calendar
1948+import datetime
1949+import logging
1950 import sys
1951-import logging
1952 import time
1953-import datetime
1954+
1955 from openerp.osv import orm, fields
1956 from openerp.tools.translate import _
1957-import openerp.addons.decimal_precision as dp
1958-from openerp.addons.contract_isp.contract import add_months, date_interval
1959+from openerp.addons.contract_isp.contract import add_months
1960+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
1961 from openerp import netsvc
1962 import openerp.exceptions
1963
1964+from .invoice import PROCESS_PRORATA, PROCESS_RECURRENT
1965+
1966 _logger = logging.getLogger(__name__)
1967
1968
1969+def count_months_stupid(from_date, to_date):
1970+ from_date = from_date.replace(day=1)
1971+ to_date = to_date.replace(day=1)
1972+ # Don't even bother making this smart for now
1973+ count = 0
1974+ while from_date < to_date:
1975+ count += 1
1976+ from_date = (
1977+ from_date + datetime.timedelta(days=32)
1978+ ).replace(day=1)
1979+
1980+ return count
1981+
1982+
1983 class res_company(orm.Model):
1984 _inherit = "res.company"
1985
1986@@ -68,10 +86,6 @@
1987
1988 _columns = {
1989 'later_validation': fields.boolean('Later Validation'),
1990- 'original_amount': fields.float(
1991- 'Original Amount', digits_compute=dp.get_precision('Account'),
1992- required=True, readonly=True,
1993- states={'draft':[('readonly',False)]}),
1994 }
1995
1996 _defaults = {
1997@@ -98,11 +112,6 @@
1998 def create(self, cr, uid, data, context=None):
1999 if context is None:
2000 context = {}
2001- if context.get('original_amount', False) and data.get('amount', False):
2002- if data['amount'] < data['original_amount']:
2003- raise orm.except_orm(
2004- _('Error'),
2005- _('Amount cannot be less than the invoice amount'))
2006
2007 return super(account_voucher, self).create(
2008 cr, uid, data, context=context)
2009@@ -114,7 +123,6 @@
2010 ret = True
2011 account_analytic_account_obj = self.pool.get(
2012 'account.analytic.account')
2013- account_invoice_obj = self.pool.get('account.invoice')
2014
2015 voucher = self.browse(cr, uid, ids[0], context=context)
2016
2017@@ -132,10 +140,14 @@
2018 inv = account_analytic_account_obj.create_invoice(
2019 cr, uid, context.get('active_id'), context=context)
2020
2021- a = account_invoice_obj._workflow_signal(
2022- cr, uid, [inv],
2023- 'invoice_open', context=context)
2024-
2025+ wf_service = netsvc.LocalService("workflow")
2026+ if isinstance(inv, list):
2027+ for i in inv:
2028+ wf_service.trg_validate(
2029+ uid, 'account.invoice', i, 'invoice_open', cr)
2030+ else:
2031+ wf_service.trg_validate(
2032+ uid, 'account.invoice', inv, 'invoice_open', cr)
2033 else:
2034 raise openerp.exceptions.Warning(_('Contract not found'))
2035
2036@@ -143,12 +155,12 @@
2037 ret = super(account_voucher, self).proforma_voucher(
2038 cr, uid, ids, context=context)
2039
2040-
2041 if voucher.journal_id.later_validation is False:
2042 account_move_line_obj = self.pool.get('account.move.line')
2043+ account_id = voucher.partner_id.property_account_receivable.id
2044 query = [
2045 ('partner_id', '=', voucher.partner_id.id),
2046- ('account_id', '=', voucher.partner_id.property_account_receivable.id),
2047+ ('account_id', '=', account_id),
2048 ('reconcile_id', '=', False)
2049 ]
2050
2051@@ -160,14 +172,13 @@
2052 period_obj = self.pool.get('account.period')
2053 date = False
2054 period_id = False
2055- journal_id = False
2056- account_id = False
2057+ journal_id = voucher.journal_id.id
2058
2059 date = time.strftime('%Y-%m-%d')
2060 ctx = dict(context or {}, account_period_prefer_normal=True)
2061 period_ids = period_obj.find(cr, uid, dt=date, context=ctx)
2062 if period_ids:
2063- period_id = ids[0]
2064+ period_id = period_ids[0]
2065 account_move_line_obj.reconcile(cr, uid, ids_to_reconcile,
2066 'manual', account_id,
2067 period_id, journal_id,
2068@@ -204,140 +215,399 @@
2069 }
2070
2071
2072+class contract_service(orm.Model):
2073+ _inherit = 'contract.service'
2074+
2075+ def _get_invoice_day(self, cr, uid, context):
2076+ res_company_obj = self.pool["res.company"]
2077+ res_company_data = res_company_obj.read(
2078+ cr, uid,
2079+ res_company_obj._company_default_get(cr, uid, context),
2080+ context=context)
2081+
2082+ invoice_day = int(res_company_data['invoice_day'])
2083+ return invoice_day
2084+
2085+ def _get_prorata_interval_rate(self, cr, uid, change_date, context=None):
2086+ """ Get the prorata interval and price rate.
2087+
2088+ Returns a tuple (start_date, end_date, price percent)
2089+ """
2090+ today = (context or {}).get("operation_date", datetime.date.today())
2091+ invoice_day = self._get_invoice_day(cr, uid, context)
2092+
2093+ curmonth_days = calendar.monthrange(today.year,
2094+ today.month)[1]
2095+ month_days = calendar.monthrange(change_date.year,
2096+ change_date.month)[1]
2097+
2098+ if invoice_day < today.day:
2099+ start_date = add_months(change_date, 1)
2100+ end_date = add_months(today, 1)
2101+ end_date = end_date.replace(
2102+ day=calendar.monthrange(end_date.year,
2103+ end_date.month)[1],
2104+ )
2105+ month_days = calendar.monthrange(start_date.year,
2106+ start_date.month)[1]
2107+ ptx = self._prorata_rate(
2108+ month_days - start_date.day,
2109+ month_days,
2110+ )
2111+ ptx += count_months_stupid(start_date, end_date)
2112+
2113+ else: # if today_date <= invoice_day:
2114+ if change_date < today.replace(day=1):
2115+ # Activation in previous month
2116+ start_date = add_months(change_date, 1)
2117+ end_date = today.replace(day=curmonth_days)
2118+ month_days = calendar.monthrange(start_date.year,
2119+ start_date.month)[1]
2120+ ptx = self._prorata_rate(
2121+ month_days - start_date.day,
2122+ month_days,
2123+ )
2124+ ptx += count_months_stupid(start_date, end_date)
2125+ else:
2126+ # Activation is in current month (or future - same)
2127+ end_date = add_months(change_date, 1)
2128+ start_date = end_date.replace(day=1)
2129+ ptx = -1 * self._prorata_rate(
2130+ end_date.day,
2131+ calendar.monthrange(end_date.year,
2132+ end_date.month)[1],
2133+ )
2134+
2135+ return start_date, end_date, ptx
2136+
2137+ def _get_prorata_interval_rate_deactivate(self, cr, uid, change_date,
2138+ context=None):
2139+ today = (context or {}).get("operation_date", datetime.date.today())
2140+ invoice_day = self._get_invoice_day(cr, uid, context)
2141+
2142+ if invoice_day < today.day:
2143+ start_date = change_date
2144+ end_date = add_months(today, 1)
2145+ end_date = end_date.replace(
2146+ day=calendar.monthrange(end_date.year,
2147+ end_date.month)[1],
2148+ )
2149+
2150+ month_days = calendar.monthrange(start_date.year,
2151+ start_date.month)[1]
2152+ ptx = -1 * self._prorata_rate(
2153+ month_days - start_date.day,
2154+ month_days,
2155+ )
2156+ ptx -= count_months_stupid(start_date, end_date)
2157+ else: # if today <= invoice_day
2158+ if change_date < today:
2159+ # Deactivation in the past, refund period from
2160+ # deactivation to end of current month
2161+ start_date = change_date
2162+ end_date = today.replace(
2163+ day=calendar.monthrange(today.year,
2164+ today.month)[1],
2165+ )
2166+
2167+ month_days = calendar.monthrange(start_date.year,
2168+ start_date.month)[1]
2169+ ptx = -1 * self._prorata_rate(
2170+ month_days - start_date.day,
2171+ month_days,
2172+ )
2173+ ptx -= count_months_stupid(start_date, end_date)
2174+ else:
2175+ # Deactivation in future, bill period from now to deactivation
2176+ start_date = today
2177+ end_date = change_date
2178+ start_month_days = calendar.monthrange(start_date.year,
2179+ start_date.month)[1]
2180+ end_month_days = calendar.monthrange(end_date.year,
2181+ end_date.month)[1]
2182+ if add_months(start_date, 1).replace(day=1) > end_date:
2183+ # Both dates in same month
2184+ ptx = self._prorata_rate(
2185+ end_date.day - start_date.day,
2186+ start_month_days,
2187+ )
2188+ else:
2189+ ptx = sum((
2190+ # Rate for first month
2191+ self._prorata_rate(start_month_days - start_date.day,
2192+ start_month_days),
2193+ # Each full month excluding end month
2194+ count_months_stupid(start_date, end_date) - 1,
2195+ # Rate for end month
2196+ self._prorata_rate(end_date.day,
2197+ end_month_days),
2198+ ))
2199+
2200+ return start_date, end_date, ptx
2201+
2202+
2203 class account_analytic_account(orm.Model):
2204 _inherit = "account.analytic.account"
2205
2206 _columns = {
2207 'close_date': fields.datetime('Close date'),
2208- 'close_reason': fields.text('Reasons')
2209+ 'close_reason': fields.text('Reasons'),
2210 }
2211
2212 _defaults = {
2213 'close_date': fields.datetime.now
2214 }
2215
2216- def create_invoice(self, cr, uid, ids, prorata=False, context=None):
2217- return_int = False
2218- if isinstance(ids, int):
2219- return_int = True
2220+ def get_last_invoice_date(self, cr, uid, ids, source_process, inv=False,
2221+ context=None):
2222+ res = {}
2223+ source_process = tuple((source_process or '').split(","))
2224+ account_ids = tuple(ids)
2225+ if not ids:
2226+ return res
2227+
2228+ cr.execute(
2229+ """
2230+ SELECT account_analytic_line.account_id
2231+ , DATE(MAX(account_invoice.date_invoice))
2232+ FROM account_analytic_line
2233+ JOIN account_invoice
2234+ ON account_analytic_line.invoice_id = account_invoice.id
2235+ WHERE account_analytic_line.account_id IN %s
2236+ AND account_analytic_line.invoice_id IS NOT NULL
2237+ AND {neg} COALESCE(account_invoice.source_process, '') IN %s
2238+ GROUP BY account_analytic_line.account_id
2239+ """.format(neg=('NOT' if inv else '')),
2240+ (account_ids, source_process),
2241+ )
2242+
2243+ for account_id, lid in cr.fetchall():
2244+ res[account_id] = lid
2245+ return res
2246+
2247+ def get_last_invoice_date_non_prorata(self, cr, uid, ids, context=None):
2248+ return self.get_last_invoice_date(cr, uid, ids,
2249+ PROCESS_PRORATA, inv=True,
2250+ context=context)
2251+
2252+ def send_email_contract_invoice(self, cr, uid, ids, context=None):
2253+ context = context or {}
2254+
2255+ if not isinstance(ids, list):
2256 ids = [ids]
2257
2258- account_analytic_account_obj = self.pool.get('account.analytic.account')
2259- account_analytic_line = self.pool.get('account.analytic.line')
2260- contract_service_obj = self.pool.get('contract.service')
2261- res_company_obj = self.pool.get('res.company')
2262 account_invoice_obj = self.pool.get('account.invoice')
2263- res_company_data = res_company_obj.read(
2264- cr, uid,
2265- res_company_obj._company_default_get(cr, uid, context),
2266- context=context)
2267+ mail_template_obj = self.pool.get('email.template')
2268+ ir_model_data_obj = self.pool.get('ir.model.data')
2269+ mail_template_id = ir_model_data_obj.get_object_reference(
2270+ cr, uid, 'account',
2271+ 'email_template_edi_invoice')[1]
2272+ mail_mail_obj = self.pool.get('mail.mail')
2273+
2274+ for inv in ids:
2275+ _logger.info(
2276+ "Mailing invoice %s" % account_invoice_obj.browse(
2277+ cr, uid, inv, context=context).name)
2278+
2279+ try:
2280+ mail_id = mail_template_obj.send_mail(
2281+ cr, uid, mail_template_id, inv, context=context)
2282+ mail_message = mail_mail_obj.browse(
2283+ cr, uid, mail_id,
2284+ context=context).mail_message_id
2285+ mail_message.write({'type': 'email'})
2286+ except:
2287+ _logger.error(
2288+ 'Error generating mail for invoice %s:'
2289+ '\n\n%s' % (
2290+ account_invoice_obj.browse(
2291+ cr, uid, inv, context=context).name,
2292+ sys.exc_info()[0]))
2293+
2294+ return True
2295+
2296+ def _create_invoice(self, cr, uid, ids, context=None):
2297+ context = context or {}
2298+
2299+ data = {
2300+ 'name': True,
2301+ }
2302+
2303+ if context.get('create_invoice_mode', 'contract') == 'reseller':
2304+ data.update({'partner': True})
2305+
2306+ res = []
2307+
2308+ account_analytic_line_obj = self.pool['account.analytic.line']
2309+ account_invoice_obj = self.pool['account.invoice']
2310+ account_invoice_line_obj = self.pool['account.invoice.line']
2311+ move_obj = self.pool["account.move"]
2312 wf_service = netsvc.LocalService("workflow")
2313
2314- if res_company_data['send_email_contract_invoice']:
2315- mail_template_obj = self.pool.get('email.template')
2316- ir_model_data_obj = self.pool.get('ir.model.data')
2317- mail_template_id = ir_model_data_obj.get_object_reference(
2318- cr, uid, 'account',
2319- 'email_template_edi_invoice')[1]
2320- mail_mail_obj = self.pool.get('mail.mail')
2321-
2322- cuttoff_day = res_company_data['cutoff_day']
2323-
2324- cutoff_date = datetime.date(
2325- datetime.date.today().year,
2326- datetime.date.today().month,
2327- int(cuttoff_day)
2328+ if sum(-line.amount
2329+ for line in account_analytic_line_obj.browse(
2330+ cr, uid, ids, context=context)
2331+ ) < 0:
2332+ for line in account_analytic_line_obj.browse(cr, uid, ids, context=context):
2333+ line.write({"amount": -line.amount})
2334+ context["type"] = "out_refund"
2335+
2336+ inv = account_analytic_line_obj.invoice_cost_create(
2337+ cr, uid, ids, data=data, context=context)
2338+
2339+ if isinstance(inv, list):
2340+ # vvinet - this could mess up refunds/invoices, does it get called?
2341+ if len(inv) > 1:
2342+ # Merge invoices
2343+ query = [('invoice_id', 'in', inv)]
2344+ line_ids = account_invoice_line_obj.search(
2345+ cr, uid, query, context=context)
2346+ account_invoice_line_obj.write(
2347+ cr, uid, line_ids, {'invoice_id': inv[0]},
2348+ context=context)
2349+ account_invoice_obj.button_compute(
2350+ cr, uid, [inv[0]], context=context)
2351+ account_invoice_obj.unlink(cr, uid, inv[1:], context=context)
2352+
2353+ inv = inv[0]
2354+
2355+ # vvinet - any of the passed ids that did not get assigned to an
2356+ # invoice gets assigned to the first one
2357+ unassigned_ids = account_invoice_line_obj.search(
2358+ cr, uid, [('id', 'in', ids), ('invoice_id', '=', False)],
2359 )
2360-
2361+ if unassigned_ids:
2362+ account_invoice_line_obj.write(cr, uid, {'invoice_id': inv})
2363+
2364+ res.append(inv)
2365+
2366+ # jgama - If its a prorata invoice, change the invoice date
2367+ # according to the invoice_day variable
2368+ to_write = {}
2369+ if context.get('source_process'):
2370+ to_write['source_process'] = context['source_process']
2371+ if context.get('date_invoice') and (
2372+ context.get('prorata', False) or
2373+ context.get('source_process') == PROCESS_RECURRENT):
2374+ to_write['date_invoice'] = context.get('date_invoice')
2375+
2376+ if to_write:
2377+ account_invoice_obj.write(cr, uid, inv, to_write, context=context)
2378+
2379+ if context.get('not_subscription_voucher', True):
2380+ _logger.debug(
2381+ "Opening invoice %s" % account_invoice_obj.browse(
2382+ cr, uid, inv, context=context).name)
2383+
2384+ wf_service.trg_validate(
2385+ uid, 'account.invoice', inv, 'invoice_open', cr)
2386+
2387+ return res
2388+
2389+ def create_invoice(self, cr, uid, ids, source_process=None, context=None):
2390+ context = context or {}
2391+ prorata = (source_process == PROCESS_PRORATA)
2392+ context["source_process"] = source_process
2393+ _logger.debug("create_invoice %r %s", ids, prorata)
2394+
2395+ if not isinstance(ids, list):
2396+ ids = [ids]
2397+
2398+ date = context.get("operation_date", datetime.date.today())
2399+ if not isinstance(date, datetime.date):
2400+ date = datetime.datetime.strptime(
2401+ date,
2402+ DEFAULT_SERVER_DATE_FORMAT,
2403+ ).date()
2404+
2405+ account_analytic_account_obj = self.pool['account.analytic.account']
2406+ account_analytic_line_obj = self.pool['account.analytic.line']
2407+ res_company_obj = self.pool['res.company']
2408+ res_company_data = res_company_obj.read(
2409+ cr, uid,
2410+ res_company_obj._company_default_get(cr, uid, context),
2411+ context=context)
2412+
2413+ cutoff_day = res_company_data['cutoff_day']
2414+
2415+ cutoff_date = datetime.date(date.year, date.month, int(cutoff_day))
2416 invoice_day = res_company_data['invoice_day']
2417
2418- invoice_date = datetime.date(
2419- datetime.date.today().year,
2420- datetime.date.today().month,
2421- int(invoice_day)
2422- )
2423+ invoice_date = date.replace(day=int(invoice_day))
2424
2425- if prorata:
2426- if datetime.date.today() <= cutoff_date:
2427- date_invoice = invoice_date.strftime('%Y-%m-%d')
2428+ ctx = dict(context.copy(), prorata=prorata)
2429+ if prorata or source_process == PROCESS_RECURRENT:
2430+ if date <= cutoff_date:
2431+ ctx.update(date_invoice=invoice_date.strftime('%Y-%m-%d'))
2432 else:
2433- date_invoice = add_months(invoice_date, 1).strftime(
2434- '%Y-%m-%d')
2435-
2436- ret = []
2437- for contract_id in ids:
2438- query = [('account_id', '=', contract_id),
2439+ ctx.update(
2440+ date_invoice=add_months(invoice_date, 1).strftime(
2441+ '%Y-%m-%d')
2442+ )
2443+
2444+ res = []
2445+
2446+ if context.get('create_invoice_mode', 'contract') != 'reseller':
2447+ for contract_id in ids:
2448+ query = [('account_id', '=', contract_id),
2449+ ('to_invoice', '!=', False),
2450+ ('invoice_id', '=', False),
2451+ ('product_id', '!=', False),
2452+ ('is_prorata', '=', prorata)]
2453+
2454+ ids_to_invoice = account_analytic_line_obj.search(
2455+ cr, uid, query, context=context)
2456+
2457+ if ids_to_invoice:
2458+ _logger.info(
2459+ "Invoicing contract %s lines %r",
2460+ account_analytic_account_obj.browse(
2461+ cr, uid, contract_id, context=context).name,
2462+ ids_to_invoice,
2463+ )
2464+
2465+ inv = self._create_invoice(
2466+ cr, uid, ids_to_invoice, context=ctx.copy())
2467+
2468+ if isinstance(inv, list):
2469+ for i in inv:
2470+ res.append(i)
2471+ else:
2472+ res.append(inv)
2473+
2474+ if res_company_data['send_email_contract_invoice']:
2475+ self.send_email_contract_invoice(
2476+ cr, uid, inv, context=context)
2477+
2478+ else:
2479+ query = [('account_id', 'in', ids),
2480 ('to_invoice', '!=', False),
2481 ('invoice_id', '=', False),
2482 ('product_id', '!=', False),
2483 ('is_prorata', '=', prorata)]
2484
2485- ids_to_invoice = account_analytic_line.search(cr, uid, query,
2486- context=context)
2487+ ids_to_invoice = account_analytic_line_obj.search(
2488+ cr, uid, query, context=context)
2489 if ids_to_invoice:
2490 _logger.info(
2491- "Invoicing contract %s" %
2492+ "Invoicing partner %s" %
2493 account_analytic_account_obj.browse(
2494- cr, uid, contract_id, context=context).name)
2495-
2496- data = {
2497- 'name': True,
2498- }
2499- inv = account_analytic_line.invoice_cost_create(
2500- cr, uid, ids_to_invoice, data=data, context=context)
2501+ cr, uid, ids[0], context=context).partner_id.parent_id.name)
2502+
2503+ inv = self._create_invoice(
2504+ cr, uid, ids_to_invoice, context=ctx)
2505+
2506 if isinstance(inv, list):
2507 for i in inv:
2508- ret.append(i)
2509-
2510+ res.append(i)
2511 else:
2512- ret.append(inv)
2513-
2514- # jgama - If its a prorata invoice, change the invoice date
2515- # according to the invoice_day variable
2516- if prorata:
2517- account_invoice_obj.write(
2518- cr, uid, inv, {'date_invoice': date_invoice},
2519- context=context)
2520-
2521- if context.get('not_subscription_voucher', True):
2522- _logger.debug(
2523- "Opening invoice %s" % account_invoice_obj.browse(
2524- cr, uid, inv[0], context=context).name)
2525-
2526- wf_service.trg_validate(
2527- uid, 'account.invoice', inv[0], 'invoice_open', cr)
2528- #a = account_invoice_obj._workflow_signal(
2529- # cr, uid, inv, 'invoice_open', context)
2530-
2531- if res_company_data['send_email_contract_invoice']:
2532- _logger.info(
2533- "Mailing invoice %s" % account_invoice_obj.browse(
2534- cr, uid, inv[0], context=context).name)
2535-
2536- ctx = dict(context, default_type='email')
2537-
2538- try:
2539- mail_id = mail_template_obj.send_mail(
2540- cr, uid, mail_template_id, inv[0], context=ctx)
2541- mail_message = mail_mail_obj.browse(
2542- cr, uid, mail_id,
2543- context=context).mail_message_id
2544- mail_message.write({'type': 'email'})
2545- except:
2546- _logger.error(
2547- 'Error generating mail for invoice %s:'
2548- '\n\n%s' % (
2549- account_invoice_obj.browse(
2550- cr, uid, inv[0], context=context).name,
2551- sys.exc_info()[0]))
2552-
2553- if return_int:
2554- if len(ret) == 0:
2555- return None
2556- else:
2557- return ret[0]
2558- else:
2559- return ret
2560+ res.append(inv)
2561+
2562+ if res_company_data['send_email_contract_invoice']:
2563+ self.send_email_contract_invoice(
2564+ cr, uid, inv, context=context)
2565+
2566+ return res
2567
2568 def set_close(self, cr, uid, ids, context=None):
2569
2570@@ -354,24 +624,36 @@
2571 if context is None:
2572 context = {}
2573
2574- res_currency_obj = self.pool.get('res.currency')
2575- account_analytic_account_obj = self.pool.get(
2576- 'account.analytic.account')
2577- account_invoice_obj = self.pool.get('account.invoice')
2578-
2579- cur = self.browse(
2580- cr, uid, ids[0], context=context).pricelist_id.currency_id
2581-
2582+ res_currency_obj = self.pool['res.currency']
2583+ fposition_obj = self.pool["account.fiscal.position"]
2584+ tax_obj = self.pool["account.tax"]
2585+
2586+ voucher = self.browse(cr, uid, ids[0], context=context)
2587+ cur = voucher.pricelist_id.currency_id
2588 amount_tax = amount_untaxed = 0
2589- for line in self.browse(
2590- cr, uid, ids[0], context=context).contract_service_ids:
2591- for c in self.pool.get('account.tax').compute_all(
2592- cr, uid, line.product_id.taxes_id, line.unit_price,
2593+ for line in self.browse(cr, uid, ids[0],
2594+ context=context).contract_service_ids:
2595+ line_tax_ids = fposition_obj.map_tax(
2596+ cr, uid,
2597+ voucher.partner_id.property_account_position,
2598+ line.product_id.taxes_id,
2599+ context=context)
2600+ line_tax_ids = tax_obj.browse(cr, uid, line_tax_ids,
2601+ context=context)
2602+
2603+ for c in tax_obj.compute_all(
2604+ cr, uid, line_tax_ids, line.unit_price,
2605 line.qty, line.product_id,
2606 line.account_id.partner_id)['taxes']:
2607 amount_tax += c.get('amount', 0.0)
2608
2609 amount_untaxed += line.unit_price * line.qty
2610+ if cur is None:
2611+ raise orm.except_orm(
2612+ _('No pricelist specified'),
2613+ _('You must set a pricelist on the analytic account before'
2614+ ' creating vouchers.'))
2615+
2616 amount = res_currency_obj.round(cr, uid, cur, amount_tax) + \
2617 res_currency_obj.round(cr, uid, cur, amount_untaxed)
2618
2619@@ -391,7 +673,7 @@
2620 partner.parent_id.id or partner.id
2621
2622 return {
2623- 'name': _('Create Voucher'),
2624+ 'name': _('Create Initial Invoice'),
2625 'type': 'ir.actions.act_window',
2626 'res_model': 'account.voucher',
2627 'view_type': 'form',
2628@@ -401,7 +683,7 @@
2629 'context': {'not_subscription_voucher': False,
2630 'default_type': 'receipt',
2631 'default_amount': amount,
2632- 'original_amount': amount,
2633+ 'default_original_amount': amount,
2634 'default_partner_id': voucher_partner_id}
2635 }
2636
2637@@ -412,6 +694,7 @@
2638 def invoice_cost_create(self, cr, uid, ids, data=None, context=None):
2639 # jgama - Sligtly modified version. Original from
2640 # hr_timesheet_invoice.py
2641+ # vvinet - removed grouping by journal type
2642
2643 analytic_account_obj = self.pool.get('account.analytic.account')
2644 account_payment_term_obj = self.pool.get('account.payment.term')
2645@@ -427,162 +710,175 @@
2646 if data is None:
2647 data = {}
2648
2649- journal_types = {}
2650-
2651- # prepare for iteration on journal and accounts
2652- for line in self.pool.get('account.analytic.line').browse(
2653- cr, uid, ids, context=context):
2654- if line.journal_id.type not in journal_types:
2655- journal_types[line.journal_id.type] = set()
2656- journal_types[line.journal_id.type].add(line.account_id.id)
2657- for journal_type, account_ids in journal_types.items():
2658- for account in analytic_account_obj.browse(
2659- cr, uid, list(account_ids), context=context):
2660- # jgama - If there's a parent, invoice the parent
2661- partner = account.partner_id.parent_id or account.partner_id
2662-
2663- if (not partner) or not (account.pricelist_id):
2664- raise orm.except_orm(
2665- _('Analytic Account Incomplete!'),
2666- _('Contract incomplete. Please fill in the Customer and Pricelist fields.'))
2667-
2668- date_due = False
2669- if partner.property_payment_term:
2670- pterm_list = account_payment_term_obj.compute(
2671- cr, uid, partner.property_payment_term.id, value=1,
2672- date_ref=time.strftime('%Y-%m-%d'))
2673- if pterm_list:
2674- pterm_list = [line[0] for line in pterm_list]
2675- pterm_list.sort()
2676- date_due = pterm_list[-1]
2677-
2678- curr_invoice = {
2679- 'name': time.strftime('%d/%m/%Y') + ' - ' + account.name,
2680- 'origin': account.name,
2681- 'partner_id': partner.id,
2682- 'company_id': account.company_id.id,
2683- 'payment_term': partner.property_payment_term.id or False,
2684- 'account_id': partner.property_account_receivable.id,
2685- 'currency_id': account.pricelist_id.currency_id.id,
2686- 'date_due': date_due,
2687- 'fiscal_position': partner.property_account_position.id
2688+ account_ids = list(set(
2689+ line.account_id.id
2690+ for line in self.pool.get('account.analytic.line').browse(
2691+ cr, uid, ids, context=context)
2692+ ))
2693+
2694+ for account in analytic_account_obj.browse(
2695+ cr, uid, list(account_ids), context=context):
2696+ # jgama - If there's a parent, invoice the parent
2697+ if account.partner_id.parent_id:
2698+ partner = account.partner_id.parent_id
2699+ line_prefix = u" - ".join(filter(None, (
2700+ account.partner_id.code,
2701+ account.partner_id.name,
2702+ )))
2703+ else:
2704+ partner = account.partner_id
2705+ line_prefix = None
2706+
2707+ if (not partner) or not (account.pricelist_id):
2708+ raise orm.except_orm(
2709+ _('Analytic Account Incomplete!'),
2710+ _('Contract incomplete. Please fill in the Customer '
2711+ 'and Pricelist fields.'))
2712+
2713+ date_due = False
2714+ if partner.property_payment_term:
2715+ pterm_list = account_payment_term_obj.compute(
2716+ cr, uid, partner.property_payment_term.id, value=1,
2717+ date_ref=time.strftime('%Y-%m-%d'))
2718+ if pterm_list:
2719+ pterm_list = [line[0] for line in pterm_list]
2720+ pterm_list.sort()
2721+ date_due = pterm_list[-1]
2722+
2723+ curr_invoice = {
2724+ 'name': time.strftime('%d/%m/%Y') + ' - ' + account.name,
2725+ 'origin': account.name,
2726+ 'partner_id': partner.id,
2727+ 'company_id': account.company_id.id,
2728+ 'payment_term': partner.property_payment_term.id or False,
2729+ 'account_id': partner.property_account_receivable.id,
2730+ 'currency_id': account.pricelist_id.currency_id.id,
2731+ 'date_due': date_due,
2732+ 'fiscal_position': partner.property_account_position.id
2733+ }
2734+ context2 = context.copy()
2735+ context2['lang'] = partner.lang
2736+ # set company_id in context, so the correct default journal
2737+ # will be selected
2738+ context2['force_company'] = curr_invoice['company_id']
2739+ # set force_company in context so the correct product
2740+ # properties are selected (eg. income account)
2741+ context2['company_id'] = curr_invoice['company_id']
2742+
2743+ last_invoice = invoice_obj.create(
2744+ cr, uid, curr_invoice, context=context2)
2745+ invoices.append(last_invoice)
2746+
2747+ cr.execute("""
2748+ SELECT product_id, user_id, to_invoice, sum(amount), sum(unit_amount), product_uom_id
2749+ FROM account_analytic_line as line
2750+ WHERE account_id = %s
2751+ AND line.id IN %s
2752+ AND to_invoice IS NOT NULL
2753+ GROUP BY product_id, user_id, to_invoice, product_uom_id
2754+ """, (account.id, tuple(ids)))
2755+
2756+ for product_id, user_id, factor_id, total_price, qty, uom in cr.fetchall():
2757+ context2.update({'uom': uom})
2758+
2759+ if data.get('product'):
2760+ # force product, use its public price
2761+ product_id = data['product'][0]
2762+ unit_price = self._get_invoice_price(
2763+ cr, uid, account, product_id, user_id, qty, context2)
2764+ else:
2765+ # expenses, using price from amount field
2766+ unit_price = qty and total_price * -1.0 / qty or 0.0
2767+
2768+ factor = invoice_factor_obj.browse(
2769+ cr, uid, factor_id, context=context2)
2770+ # factor_name = factor.customer_name and line_name + ' - ' + factor.customer_name or line_name
2771+ factor_name = ''
2772+ if data.get('factor_name', False):
2773+ factor_name = factor.customer_name
2774+
2775+ curr_line = {
2776+ 'price_unit': unit_price,
2777+ 'quantity': qty,
2778+ 'product_id': product_id or False,
2779+ 'discount': factor.factor,
2780+ 'invoice_id': last_invoice,
2781+ 'name': factor_name,
2782+ 'uos_id': uom,
2783+ 'account_analytic_id': account.id,
2784 }
2785- context2 = context.copy()
2786- context2['lang'] = partner.lang
2787- # set company_id in context, so the correct default journal
2788- # will be selected
2789- context2['force_company'] = curr_invoice['company_id']
2790- # set force_company in context so the correct product
2791- # properties are selected (eg. income account)
2792- context2['company_id'] = curr_invoice['company_id']
2793-
2794- last_invoice = invoice_obj.create(
2795- cr, uid, curr_invoice, context=context2)
2796- invoices.append(last_invoice)
2797-
2798- cr.execute("""SELECT product_id, user_id, to_invoice, sum(amount), sum(unit_amount), product_uom_id
2799- FROM account_analytic_line as line LEFT JOIN account_analytic_journal journal ON (line.journal_id = journal.id)
2800- WHERE account_id = %s
2801- AND line.id IN %s AND journal.type = %s AND to_invoice IS NOT NULL
2802- GROUP BY product_id, user_id, to_invoice, product_uom_id""", (account.id, tuple(ids), journal_type))
2803-
2804- for product_id, user_id, factor_id, total_price, qty, uom in cr.fetchall():
2805- context2.update({'uom': uom})
2806-
2807- if data.get('product'):
2808- # force product, use its public price
2809- product_id = data['product'][0]
2810- unit_price = self._get_invoice_price(
2811- cr, uid, account, product_id, user_id, qty, context2)
2812- #elif journal_type == 'general' and product_id:
2813- # # timesheets, use sale price
2814- # unit_price = self._get_invoice_price(cr, uid, account, product_id, user_id, qty, context2)
2815- else:
2816- # expenses, using price from amount field
2817- unit_price = qty and total_price * -1.0 / qty or 0.0
2818-
2819- factor = invoice_factor_obj.browse(
2820- cr, uid, factor_id, context=context2)
2821- # factor_name = factor.customer_name and line_name + ' - ' + factor.customer_name or line_name
2822- factor_name = ''
2823- if data.get('factor_name', False):
2824- factor_name = factor.customer_name
2825-
2826- curr_line = {
2827- 'price_unit': unit_price,
2828- 'quantity': qty,
2829- 'product_id': product_id or False,
2830- 'discount': factor.factor,
2831- 'invoice_id': last_invoice,
2832+ product = product_obj.browse(
2833+ cr, uid, product_id, context=context2)
2834+ if product:
2835+ factor_name = data.get('product_name', '') and \
2836+ product_obj.name_get(
2837+ cr, uid, [product_id], context=context2)[0][1]
2838+ if factor.customer_name and data.get('factor_name',
2839+ False):
2840+ factor_name += ' - ' + factor.customer_name
2841+
2842+ general_account = product.property_account_income or \
2843+ product.categ_id.property_account_income_categ
2844+ if not general_account:
2845+ raise orm.except_orm(
2846+ _("Configuration Error!"),
2847+ _("Please define income account for product '%s'.") % product.name)
2848+ taxes = product.taxes_id or general_account.tax_ids
2849+ tax = fiscal_pos_obj.map_tax(
2850+ cr, uid,
2851+ account.partner_id.property_account_position,
2852+ taxes)
2853+ curr_line.update({
2854+ 'invoice_line_tax_id': [(6, 0, tax)],
2855 'name': factor_name,
2856- 'uos_id': uom,
2857- 'account_analytic_id': account.id,
2858- }
2859- product = product_obj.browse(
2860- cr, uid, product_id, context=context2)
2861- if product:
2862- factor_name = data.get('product_name', '') and \
2863- product_obj.name_get(
2864- cr, uid, [product_id], context=context2)[0][1]
2865- if factor.customer_name and data.get('factor_name',
2866- False):
2867- factor_name += ' - ' + factor.customer_name
2868-
2869- general_account = product.property_account_income or \
2870- product.categ_id.property_account_income_categ
2871- if not general_account:
2872- raise orm.except_orm(
2873- _("Configuration Error!"),
2874- _("Please define income account for product '%s'.") % product.name)
2875- taxes = product.taxes_id or general_account.tax_ids
2876- tax = fiscal_pos_obj.map_tax(
2877- cr, uid,
2878- account.partner_id.property_account_position,
2879- taxes)
2880- curr_line.update({
2881- 'invoice_line_tax_id': [(6, 0, tax)],
2882- 'name': factor_name,
2883- 'invoice_line_tax_id': [(6, 0, tax)],
2884- 'account_id': general_account.id,
2885- })
2886- #
2887- # Compute for lines
2888- #
2889- cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id))
2890-
2891- line_ids = cr.dictfetchall()
2892- note = []
2893- for line in line_ids:
2894- # set invoice_line_note
2895- details = []
2896- if data.get('date', False):
2897- details.append(line['date'])
2898- if data.get('time', False):
2899- if line['product_uom_id']:
2900- details.append("%s %s" % (
2901- line['unit_amount'],
2902- product_uom_obj.browse(
2903- cr, uid, [line['product_uom_id']],
2904- context2)[0].name))
2905-
2906- else:
2907- details.append("%s" % (line['unit_amount'], ))
2908- if data.get('name', False):
2909- details.append(line['name'])
2910- note.append(u' - '.join(
2911- map(lambda x: unicode(x) or '', details)))
2912- if note:
2913- if curr_line['name']:
2914- curr_line['name'] += "\n" + ("\n".join(
2915- map(lambda x: unicode(x) or '', note)))
2916+ 'invoice_line_tax_id': [(6, 0, tax)],
2917+ 'account_id': general_account.id,
2918+ })
2919+
2920+ if line_prefix:
2921+ curr_line["name"] = u'{0}\n{1}'.format(
2922+ line_prefix, curr_line["name"])
2923+
2924+ #
2925+ # Compute for lines
2926+ #
2927+ cr.execute("SELECT * FROM account_analytic_line WHERE account_id = %s and id IN %s AND product_id=%s and to_invoice=%s ORDER BY account_analytic_line.date", (account.id, tuple(ids), product_id, factor_id))
2928+
2929+ line_ids = cr.dictfetchall()
2930+ note = []
2931+ for line in line_ids:
2932+ # set invoice_line_note
2933+ details = []
2934+ if data.get('date', False):
2935+ details.append(line['date'])
2936+ if data.get('time', False):
2937+ if line['product_uom_id']:
2938+ details.append("%s %s" % (
2939+ line['unit_amount'],
2940+ product_uom_obj.browse(
2941+ cr, uid, [line['product_uom_id']],
2942+ context2)[0].name))
2943+
2944 else:
2945- curr_line['name'] = "\n".join(
2946- map(lambda x: unicode(x) or '', note))
2947- invoice_line_obj.create(
2948- cr, uid, curr_line, context=context)
2949- cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
2950-
2951- invoice_obj.button_reset_taxes(
2952- cr, uid, [last_invoice], context)
2953+ details.append("%s" % (line['unit_amount'], ))
2954+ if data.get('name', False):
2955+ details.append(line['name'])
2956+ if data.get('partner', False):
2957+ details.append(account.partner_id.name)
2958+ note.append(u' - '.join(
2959+ map(lambda x: unicode(x) or '', details)))
2960+ if note:
2961+ if curr_line['name']:
2962+ curr_line['name'] += "\n" + ("\n".join(
2963+ map(lambda x: unicode(x) or '', note)))
2964+ else:
2965+ curr_line['name'] = "\n".join(
2966+ map(lambda x: unicode(x) or '', note))
2967+ invoice_line_obj.create(
2968+ cr, uid, curr_line, context=context)
2969+ cr.execute("update account_analytic_line set invoice_id=%s WHERE account_id = %s and id IN %s", (last_invoice, account.id, tuple(ids)))
2970+
2971+ invoice_obj.button_reset_taxes(
2972+ cr, uid, [last_invoice], context)
2973+
2974 return invoices
2975-
2976
2977=== modified file 'contract_isp_invoice/contract_isp_invoice_view.xml'
2978--- contract_isp_invoice/contract_isp_invoice_view.xml 2014-01-20 04:25:39 +0000
2979+++ contract_isp_invoice/contract_isp_invoice_view.xml 2014-11-24 15:05:14 +0000
2980@@ -19,7 +19,7 @@
2981 <field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
2982 <field name="arch" type="xml">
2983 <xpath expr="//div[@name='buttons']" position='inside'>
2984- <button string="Create Voucher"
2985+ <button string="Create Initial Invoice"
2986 name="prepare_voucher"
2987 context="{'default_partner_id': partner_id}"
2988 type="object" />
2989@@ -34,7 +34,6 @@
2990 <field name="arch" type="xml">
2991 <field name="name" position="after">
2992 <field name="later_validation" invisible="1" />
2993- <field name="original_amount" invisible="1" />
2994 </field>
2995 <button name="proforma_voucher" position="attributes">
2996 <attribute name="attrs">{'invisible': [('later_validation', '=', True)]}</attribute>
2997
2998=== modified file 'contract_isp_invoice/i18n/contract_isp_invoice.pot'
2999--- contract_isp_invoice/i18n/contract_isp_invoice.pot 2013-09-16 17:59:01 +0000
3000+++ contract_isp_invoice/i18n/contract_isp_invoice.pot 2014-11-24 15:05:14 +0000
3001@@ -6,8 +6,8 @@
3002 msgstr ""
3003 "Project-Id-Version: OpenERP Server 7.0\n"
3004 "Report-Msgid-Bugs-To: \n"
3005-"POT-Creation-Date: 2013-08-13 12:38+0000\n"
3006-"PO-Revision-Date: 2013-08-13 12:38+0000\n"
3007+"POT-Creation-Date: 2014-09-16 16:39+0000\n"
3008+"PO-Revision-Date: 2014-09-16 16:39+0000\n"
3009 "Last-Translator: <>\n"
3010 "Language-Team: \n"
3011 "MIME-Version: 1.0\n"
3012@@ -16,43 +16,235 @@
3013 "Plural-Forms: \n"
3014
3015 #. module: contract_isp_invoice
3016-#: view:account.analytic.account:0
3017-#: model:ir.actions.act_window,name:contract_isp_invoice.action_view_create_voucher
3018-msgid "Create Voucher"
3019-msgstr ""
3020-
3021-#. module: contract_isp_invoice
3022-#: model:ir.model,name:contract_isp_invoice.model_contract_service_activate
3023-msgid "contract.service.activate"
3024-msgstr ""
3025-
3026-#. module: contract_isp_invoice
3027+#: model:email.template,body_html:contract_isp_invoice.email_template_contract_isp_invoice_close
3028+msgid "\n"
3029+"<div style=\"font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); \">\n"
3030+"\n"
3031+" <p>Dear Accountant,</p>\n"
3032+" <p>\n"
3033+" Please take notice that the following contract is being terminated.\n"
3034+"\n"
3035+" </p>\n"
3036+"<br/>\n"
3037+" <table>\n"
3038+" <tr>\n"
3039+" <td>Contract:</td><td>${object.name}</td>\n"
3040+" </tr>\n"
3041+" <tr>\n"
3042+" <td>Partner:</td><td>${object.partner_id.name}</td>\n"
3043+" </tr> \n"
3044+" <tr>\n"
3045+" <td>Close date:</td><td>${object.close_date}</td>\n"
3046+" </tr>\n"
3047+" </table>\n"
3048+" <h3>Reasons/Comments</h3>\n"
3049+" <p>${object.close_reason}</p>\n"
3050+"<br/>\n"
3051+"Best Regards,\n"
3052+"<br/>\n"
3053+" <br/>\n"
3054+"${user.name}\n"
3055+"\n"
3056+"<br/>\n"
3057+"\n"
3058+" <br/>\n"
3059+"\n"
3060+"</div>\n"
3061+" "
3062+msgstr ""
3063+
3064+#. module: contract_isp_invoice
3065+#: code:_description:0
3066+#: model:ir.model,name:contract_isp_invoice.model_account_voucher
3067+#, python-format
3068+msgid "Accounting Voucher"
3069+msgstr ""
3070+
3071+#. module: contract_isp_invoice
3072+#: code:_description:0
3073+#: model:ir.model,name:contract_isp_invoice.model_account_analytic_account
3074+#, python-format
3075+msgid "Analytic Account"
3076+msgstr ""
3077+
3078+#. module: contract_isp_invoice
3079+#: code:_description:0
3080 #: model:ir.model,name:contract_isp_invoice.model_account_analytic_line
3081+#, python-format
3082 msgid "Analytic Line"
3083 msgstr ""
3084
3085 #. module: contract_isp_invoice
3086+#: view:contract.isp.close:0
3087+msgid "Cancel"
3088+msgstr ""
3089+
3090+#. module: contract_isp_invoice
3091+#: view:account.voucher:0
3092+msgid "Cancel Receipt"
3093+msgstr ""
3094+
3095+#. module: contract_isp_invoice
3096+#: view:contract.isp.close:0
3097+msgid "Close Contract"
3098+msgstr ""
3099+
3100+#. module: contract_isp_invoice
3101+#: model:ir.actions.act_window,name:contract_isp_invoice.action_view_contract_isp_close
3102+msgid "Close contract"
3103+msgstr ""
3104+
3105+#. module: contract_isp_invoice
3106+#: field:account.analytic.account,close_date:0
3107+#: field:contract.isp.close,close_date:0
3108+msgid "Close date"
3109+msgstr ""
3110+
3111+#. module: contract_isp_invoice
3112+#: code:_description:0
3113+#: model:ir.model,name:contract_isp_invoice.model_res_company
3114+#, python-format
3115+msgid "Companies"
3116+msgstr ""
3117+
3118+#. module: contract_isp_invoice
3119+#: view:contract.isp.close:0
3120+msgid "Confirm"
3121+msgstr ""
3122+
3123+#. module: contract_isp_invoice
3124+#: view:contract.isp.close:0
3125+msgid "Confirm the termination of this contract?"
3126+msgstr ""
3127+
3128+#. module: contract_isp_invoice
3129+#: field:contract.isp.close,account_id:0
3130+msgid "Contract"
3131+msgstr ""
3132+
3133+#. module: contract_isp_invoice
3134+#: model:email.template,subject:contract_isp_invoice.email_template_contract_isp_invoice_close
3135+msgid "Contract n. ${object.name}"
3136+msgstr ""
3137+
3138+#. module: contract_isp_invoice
3139+#: view:account.analytic.account:0
3140+msgid "Create Initial Invoice"
3141+msgstr ""
3142+
3143+#. module: contract_isp_invoice
3144+#: code:_description:0
3145+#: model:ir.model,name:contract_isp_invoice.model_hr_timesheet_invoice_create
3146+#, python-format
3147+msgid "Create invoice from timesheet"
3148+msgstr ""
3149+
3150+#. module: contract_isp_invoice
3151 #: model:account.payment.term,name:contract_isp_invoice.account_payment_term_end_of_month
3152 msgid "End of Month"
3153 msgstr ""
3154
3155 #. module: contract_isp_invoice
3156+#: model:account.payment.term,note:contract_isp_invoice.account_payment_term_end_of_month
3157+msgid "End of current month"
3158+msgstr ""
3159+
3160+#. module: contract_isp_invoice
3161+#: field:hr.timesheet.invoice.create,factor_name:0
3162+msgid "Invoice Factor"
3163+msgstr ""
3164+
3165+#. module: contract_isp_invoice
3166 #: field:res.company,invoice_day:0
3167 msgid "Invoice day"
3168 msgstr ""
3169
3170 #. module: contract_isp_invoice
3171-#: model:ir.model,name:contract_isp_invoice.model_res_company
3172-msgid "Companies"
3173-msgstr ""
3174-
3175-#. module: contract_isp_invoice
3176-#: model:ir.model,name:contract_isp_invoice.model_account_analytic_account
3177-msgid "Analytic Account"
3178-msgstr ""
3179-
3180-#. module: contract_isp_invoice
3181-#: model:account.payment.term,note:contract_isp_invoice.account_payment_term_end_of_month
3182-msgid "End of current month"
3183+#: code:_description:0
3184+#: model:ir.model,name:contract_isp_invoice.model_account_journal
3185+#, python-format
3186+msgid "Journal"
3187+msgstr ""
3188+
3189+#. module: contract_isp_invoice
3190+#: field:account.journal,later_validation:0
3191+#: field:account.voucher,later_validation:0
3192+msgid "Later Validation"
3193+msgstr ""
3194+
3195+#. module: contract_isp_invoice
3196+#: code:_description:0
3197+#: model:ir.model,name:contract_isp_invoice.model_res_partner
3198+#, python-format
3199+msgid "Partner"
3200+msgstr ""
3201+
3202+#. module: contract_isp_invoice
3203+#: field:hr.timesheet.invoice.create,product_name:0
3204+msgid "Product Name"
3205+msgstr ""
3206+
3207+#. module: contract_isp_invoice
3208+#: view:contract.isp.close:0
3209+#: field:contract.isp.close,close_reason:0
3210+msgid "Reason"
3211+msgstr ""
3212+
3213+#. module: contract_isp_invoice
3214+#: field:account.analytic.account,close_reason:0
3215+msgid "Reasons"
3216+msgstr ""
3217+
3218+#. module: contract_isp_invoice
3219+#: field:res.company,send_email_contract_invoice:0
3220+msgid "Send invoice by email"
3221+msgstr ""
3222+
3223+#. module: contract_isp_invoice
3224+#: help:hr.timesheet.invoice.create,factor_name:0
3225+msgid "Show the invoice factor"
3226+msgstr ""
3227+
3228+#. module: contract_isp_invoice
3229+#: help:hr.timesheet.invoice.create,product_name:0
3230+msgid "Show the product name"
3231+msgstr ""
3232+
3233+#. module: contract_isp_invoice
3234+#: code:_description:0
3235+#: model:ir.model,name:contract_isp_invoice.model_contract_isp_close
3236+#, python-format
3237+msgid "contract.isp.close"
3238+msgstr ""
3239+
3240+#. module: contract_isp_invoice
3241+#: code:_description:0
3242+#: model:ir.model,name:contract_isp_invoice.model_contract_service
3243+#, python-format
3244+msgid "contract.service"
3245+msgstr ""
3246+
3247+#. module: contract_isp_invoice
3248+#: code:_description:0
3249+#: model:ir.model,name:contract_isp_invoice.model_contract_service_activate
3250+#, python-format
3251+msgid "contract.service.activate"
3252+msgstr ""
3253+
3254+#. module: contract_isp_invoice
3255+#: code:_description:0
3256+#: model:ir.model,name:contract_isp_invoice.model_contract_service_deactivate
3257+#, python-format
3258+msgid "contract.service.deactivate"
3259+msgstr ""
3260+
3261+#. module: contract_isp_invoice
3262+#: view:account.voucher:0
3263+msgid "object"
3264+msgstr ""
3265+
3266+#. module: contract_isp_invoice
3267+#: view:account.voucher:0
3268+msgid "{'invisible': [('later_validation', '=', True)]}"
3269 msgstr ""
3270
3271
3272=== added file 'contract_isp_invoice/i18n/fr.po'
3273--- contract_isp_invoice/i18n/fr.po 1970-01-01 00:00:00 +0000
3274+++ contract_isp_invoice/i18n/fr.po 2014-11-24 15:05:14 +0000
3275@@ -0,0 +1,291 @@
3276+# Translation of OpenERP Server.
3277+# This file contains the translation of the following modules:
3278+# * contract_isp_invoice
3279+#
3280+msgid ""
3281+msgstr ""
3282+"Project-Id-Version: OpenERP Server 7.0\n"
3283+"Report-Msgid-Bugs-To: \n"
3284+"POT-Creation-Date: 2014-09-16 16:39+0000\n"
3285+"PO-Revision-Date: 2014-09-16 12:54-0500\n"
3286+"Last-Translator: Marc Cassuto <marc.cassuto@savoirfairelinux.com>\n"
3287+"Language-Team: \n"
3288+"MIME-Version: 1.0\n"
3289+"Content-Type: text/plain; charset=UTF-8\n"
3290+"Content-Transfer-Encoding: 8bit\n"
3291+"Language: fr\n"
3292+"X-Generator: Poedit 1.5.4\n"
3293+
3294+#. module: contract_isp_invoice
3295+#: model:email.template,body_html:contract_isp_invoice.email_template_contract_isp_invoice_close
3296+msgid ""
3297+"\n"
3298+"<div style=\"font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-"
3299+"serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, "
3300+"255, 255); \">\n"
3301+"\n"
3302+" <p>Dear Accountant,</p>\n"
3303+" <p>\n"
3304+" Please take notice that the following contract is being terminated.\n"
3305+"\n"
3306+" </p>\n"
3307+"<br/>\n"
3308+" <table>\n"
3309+" <tr>\n"
3310+" <td>Contract:</td><td>${object.name}</td>\n"
3311+" </tr>\n"
3312+" <tr>\n"
3313+" <td>Partner:</td><td>${object.partner_id.name}</td>\n"
3314+" </tr> \n"
3315+" <tr>\n"
3316+" <td>Close date:</td><td>${object.close_date}</td>\n"
3317+" </tr>\n"
3318+" </table>\n"
3319+" <h3>Reasons/Comments</h3>\n"
3320+" <p>${object.close_reason}</p>\n"
3321+"<br/>\n"
3322+"Best Regards,\n"
3323+"<br/>\n"
3324+" <br/>\n"
3325+"${user.name}\n"
3326+"\n"
3327+"<br/>\n"
3328+"\n"
3329+" <br/>\n"
3330+"\n"
3331+"</div>\n"
3332+" "
3333+msgstr ""
3334+"\n"
3335+"<div style=\"font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-"
3336+"serif; font-size: 12px; color: rgb(34, 34, 34); background-color: rgb(255, "
3337+"255, 255); \">\n"
3338+"\n"
3339+" <p>Bonjour,</p>\n"
3340+" <p>\n"
3341+" Prenez note que le contrat suivant vient d'être clôturé:\n"
3342+"\n"
3343+" </p>\n"
3344+"<br/>\n"
3345+" <table>\n"
3346+" <tr>\n"
3347+" <td>Contrat:</td><td>${object.name}</td>\n"
3348+" </tr>\n"
3349+" <tr>\n"
3350+" <td>Code client:</td><td>${object.partner_id.code}</td>\n"
3351+" </tr> \n"
3352+" <tr>\n"
3353+" <td>Client:</td><td>${object.partner_id.name}</td>\n"
3354+" </tr> \n"
3355+" <tr>\n"
3356+" <td>Date de clôture:</td><td>${object.close_date}</td>\n"
3357+" </tr>\n"
3358+" </table>\n"
3359+" <h3>Raison / Commentaire</h3>\n"
3360+" <p>${object.close_reason}</p>\n"
3361+"<br/>\n"
3362+"Meilleurs salutations,\n"
3363+"<br/>\n"
3364+" <br/>\n"
3365+"${user.name}\n"
3366+"\n"
3367+"<br/>\n"
3368+"\n"
3369+" <br/>\n"
3370+"\n"
3371+"</div>\n"
3372+" "
3373+
3374+#. module: contract_isp_invoice
3375+#: code:_description:0
3376+#: model:ir.model,name:contract_isp_invoice.model_account_voucher
3377+#, python-format
3378+msgid "Accounting Voucher"
3379+msgstr "Paiement"
3380+
3381+#. module: contract_isp_invoice
3382+#: code:_description:0
3383+#: model:ir.model,name:contract_isp_invoice.model_account_analytic_account
3384+#, python-format
3385+msgid "Analytic Account"
3386+msgstr "Contrat"
3387+
3388+#. module: contract_isp_invoice
3389+#: code:_description:0
3390+#: model:ir.model,name:contract_isp_invoice.model_account_analytic_line
3391+#, python-format
3392+msgid "Analytic Line"
3393+msgstr "Ligne facturable"
3394+
3395+#. module: contract_isp_invoice
3396+#: view:contract.isp.close:0
3397+msgid "Cancel"
3398+msgstr "Annuler"
3399+
3400+#. module: contract_isp_invoice
3401+#: view:account.voucher:0
3402+msgid "Cancel Receipt"
3403+msgstr "Annuler le payment"
3404+
3405+#. module: contract_isp_invoice
3406+#: view:contract.isp.close:0
3407+msgid "Close Contract"
3408+msgstr "Clôturer le contrat"
3409+
3410+#. module: contract_isp_invoice
3411+#: model:ir.actions.act_window,name:contract_isp_invoice.action_view_contract_isp_close
3412+msgid "Close contract"
3413+msgstr "Clôturer le contrat"
3414+
3415+#. module: contract_isp_invoice
3416+#: field:account.analytic.account,close_date:0
3417+#: field:contract.isp.close,close_date:0
3418+msgid "Close date"
3419+msgstr "Date de Clôture"
3420+
3421+#. module: contract_isp_invoice
3422+#: code:_description:0
3423+#: model:ir.model,name:contract_isp_invoice.model_res_company
3424+#, python-format
3425+msgid "Companies"
3426+msgstr "Companies"
3427+
3428+#. module: contract_isp_invoice
3429+#: view:contract.isp.close:0
3430+msgid "Confirm"
3431+msgstr "Confirmer"
3432+
3433+#. module: contract_isp_invoice
3434+#: view:contract.isp.close:0
3435+msgid "Confirm the termination of this contract?"
3436+msgstr "Souhaitez-vous clôturer ce contrat ?"
3437+
3438+#. module: contract_isp_invoice
3439+#: field:contract.isp.close,account_id:0
3440+msgid "Contract"
3441+msgstr "Contrat"
3442+
3443+#. module: contract_isp_invoice
3444+#: model:email.template,subject:contract_isp_invoice.email_template_contract_isp_invoice_close
3445+msgid "Contract n. ${object.name}"
3446+msgstr "Contrat # ${object.name}"
3447+
3448+#. module: contract_isp_invoice
3449+#: view:account.analytic.account:0
3450+msgid "Create Initial Invoice"
3451+msgstr "Créer la facture initiale"
3452+
3453+#. module: contract_isp_invoice
3454+#: code:_description:0
3455+#: model:ir.model,name:contract_isp_invoice.model_hr_timesheet_invoice_create
3456+#, python-format
3457+msgid "Create invoice from timesheet"
3458+msgstr "Créer des factures à partir des feuilles de temps"
3459+
3460+#. module: contract_isp_invoice
3461+#: model:account.payment.term,name:contract_isp_invoice.account_payment_term_end_of_month
3462+msgid "End of Month"
3463+msgstr "Fin de Mois"
3464+
3465+#. module: contract_isp_invoice
3466+#: model:account.payment.term,note:contract_isp_invoice.account_payment_term_end_of_month
3467+msgid "End of current month"
3468+msgstr "Fin du mois courant"
3469+
3470+#. module: contract_isp_invoice
3471+#: field:hr.timesheet.invoice.create,factor_name:0
3472+msgid "Invoice Factor"
3473+msgstr "Taux facturable"
3474+
3475+#. module: contract_isp_invoice
3476+#: field:res.company,invoice_day:0
3477+msgid "Invoice day"
3478+msgstr "Jour de facturation"
3479+
3480+#. module: contract_isp_invoice
3481+#: code:_description:0
3482+#: model:ir.model,name:contract_isp_invoice.model_account_journal
3483+#, python-format
3484+msgid "Journal"
3485+msgstr "Journal Comptable"
3486+
3487+#. module: contract_isp_invoice
3488+#: field:account.journal,later_validation:0
3489+#: field:account.voucher,later_validation:0
3490+msgid "Later Validation"
3491+msgstr "Validation Ultérieure"
3492+
3493+#. module: contract_isp_invoice
3494+#: code:_description:0
3495+#: model:ir.model,name:contract_isp_invoice.model_res_partner
3496+#, python-format
3497+msgid "Partner"
3498+msgstr "Partenaire"
3499+
3500+#. module: contract_isp_invoice
3501+#: field:hr.timesheet.invoice.create,product_name:0
3502+msgid "Product Name"
3503+msgstr "Produit"
3504+
3505+#. module: contract_isp_invoice
3506+#: view:contract.isp.close:0 field:contract.isp.close,close_reason:0
3507+msgid "Reason"
3508+msgstr "Raison"
3509+
3510+#. module: contract_isp_invoice
3511+#: field:account.analytic.account,close_reason:0
3512+msgid "Reasons"
3513+msgstr "Raisons"
3514+
3515+#. module: contract_isp_invoice
3516+#: field:res.company,send_email_contract_invoice:0
3517+msgid "Send invoice by email"
3518+msgstr "Envoyer la facture par couriel"
3519+
3520+#. module: contract_isp_invoice
3521+#: help:hr.timesheet.invoice.create,factor_name:0
3522+msgid "Show the invoice factor"
3523+msgstr "Montre le taux facturable"
3524+
3525+#. module: contract_isp_invoice
3526+#: help:hr.timesheet.invoice.create,product_name:0
3527+msgid "Show the product name"
3528+msgstr "Montre le produit"
3529+
3530+#. module: contract_isp_invoice
3531+#: code:_description:0
3532+#: model:ir.model,name:contract_isp_invoice.model_contract_isp_close
3533+#, python-format
3534+msgid "contract.isp.close"
3535+msgstr "contract.isp.close"
3536+
3537+#. module: contract_isp_invoice
3538+#: code:_description:0
3539+#: model:ir.model,name:contract_isp_invoice.model_contract_service
3540+#, python-format
3541+msgid "contract.service"
3542+msgstr "contract.service"
3543+
3544+#. module: contract_isp_invoice
3545+#: code:_description:0
3546+#: model:ir.model,name:contract_isp_invoice.model_contract_service_activate
3547+#, python-format
3548+msgid "contract.service.activate"
3549+msgstr "contract.service.activate"
3550+
3551+#. module: contract_isp_invoice
3552+#: code:_description:0
3553+#: model:ir.model,name:contract_isp_invoice.model_contract_service_deactivate
3554+#, python-format
3555+msgid "contract.service.deactivate"
3556+msgstr "contract.service.deactivate"
3557+
3558+#. module: contract_isp_invoice
3559+#: view:account.voucher:0
3560+msgid "object"
3561+msgstr "objet"
3562+
3563+#. module: contract_isp_invoice
3564+#: view:account.voucher:0
3565+msgid "{'invisible': [('later_validation', '=', True)]}"
3566+msgstr "{'invisible': [('later_validation', '=', True)]}"
3567
3568=== added file 'contract_isp_invoice/invoice.py'
3569--- contract_isp_invoice/invoice.py 1970-01-01 00:00:00 +0000
3570+++ contract_isp_invoice/invoice.py 2014-11-24 15:05:14 +0000
3571@@ -0,0 +1,40 @@
3572+# -*- coding: utf-8 -*-
3573+
3574+##############################################################################
3575+#
3576+# OpenERP, Open Source Management Solution
3577+# Copyright (C) 2014 Savoir-faire Linux (<www.savoirfairelinux.com>).
3578+#
3579+# This program is free software: you can redistribute it and/or modify
3580+# it under the terms of the GNU Affero General Public License as
3581+# published by the Free Software Foundation, either version 3 of the
3582+# License, or (at your option) any later version.
3583+#
3584+# This program is distributed in the hope that it will be useful,
3585+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3586+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3587+# GNU Affero General Public License for more details.
3588+#
3589+# You should have received a copy of the GNU Affero General Public License
3590+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3591+#
3592+##############################################################################
3593+
3594+from openerp.osv import orm, fields
3595+
3596+PROCESS_RECURRENT = 'recurrent'
3597+PROCESS_PRORATA = 'prorata'
3598+PROCESS_CRON = 'cron'
3599+PROCESS_MANUAL = 'manual'
3600+
3601+
3602+class Invoice(orm.Model):
3603+ _name = _inherit = 'account.invoice'
3604+ _columns = {
3605+ 'source_process': fields.selection([
3606+ (PROCESS_RECURRENT, 'Recurrent Billing'),
3607+ (PROCESS_PRORATA, 'Pro Rata'),
3608+ (PROCESS_MANUAL, 'Manual'),
3609+ (PROCESS_CRON, 'Scheduled'),
3610+ ], 'Billing Process', required=False),
3611+ }
3612
3613=== added directory 'contract_isp_invoice/tests'
3614=== added file 'contract_isp_invoice/tests/__init__.py'
3615--- contract_isp_invoice/tests/__init__.py 1970-01-01 00:00:00 +0000
3616+++ contract_isp_invoice/tests/__init__.py 2014-11-24 15:05:14 +0000
3617@@ -0,0 +1,33 @@
3618+# -*- encoding: utf-8 -*-
3619+###############################################################################
3620+#
3621+# OpenERP, Open Source Management Solution
3622+# This module copyright (C) 2014 Savoir-faire Linux
3623+# (<http://www.savoirfairelinux.com>).
3624+#
3625+# This program is free software: you can redistribute it and/or modify
3626+# it under the terms of the GNU Affero General Public License as
3627+# published by the Free Software Foundation, either version 3 of the
3628+# License, or (at your option) any later version.
3629+#
3630+# This program is distributed in the hope that it will be useful,
3631+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3632+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3633+# GNU Affero General Public License for more details.
3634+#
3635+# You should have received a copy of the GNU Affero General Public License
3636+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3637+#
3638+###############################################################################
3639+
3640+from . import (
3641+ test_prorata,
3642+ test_deactivate,
3643+ test_reseller,
3644+)
3645+
3646+checks = [
3647+ test_deactivate,
3648+ test_prorata,
3649+ test_reseller,
3650+]
3651
3652=== added file 'contract_isp_invoice/tests/common.py'
3653--- contract_isp_invoice/tests/common.py 1970-01-01 00:00:00 +0000
3654+++ contract_isp_invoice/tests/common.py 2014-11-24 15:05:14 +0000
3655@@ -0,0 +1,142 @@
3656+# -*- coding: utf-8 -*-
3657+##############################################################################
3658+#
3659+# OpenERP, Open Source Management Solution
3660+# Copyright (C) 2014 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
3661+#
3662+# This program is free software: you can redistribute it and/or modify
3663+# it under the terms of the GNU Affero General Public License as
3664+# published by the Free Software Foundation, either version 3 of the
3665+# License, or (at your option) any later version.
3666+#
3667+# This program is distributed in the hope that it will be useful,
3668+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3669+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3670+# GNU Affero General Public License for more details.
3671+#
3672+# You should have received a copy of the GNU Affero General Public License
3673+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3674+#
3675+##############################################################################
3676+
3677+from openerp.addons.contract_isp.contract import (
3678+ LINE_TYPE_RECURRENT,
3679+ LINE_TYPE_ONETIME,
3680+)
3681+
3682+
3683+class ServiceSetup(object):
3684+ """ Mixin class to provide setup and utils for testing invoicing """
3685+
3686+ def _common_setup(self):
3687+ self._init_modules()
3688+ self._configure_company()
3689+ self._create_partner()
3690+ self._create_account()
3691+ self._create_products()
3692+
3693+ def _init_modules(self):
3694+ self.account_obj = self.registry("account.analytic.account")
3695+ self.analytic_line_obj = self.registry("account.analytic.line")
3696+ self.company_obj = self.registry("res.company")
3697+ self.service_obj = self.registry("contract.service")
3698+ self.partner_obj = self.registry("res.partner")
3699+ self.product_obj = self.registry("product.product")
3700+ self.invoice_obj = self.registry("account.invoice")
3701+ self.wiz_activate_obj = self.registry("contract.service.activate")
3702+ self.wiz_deactivate_obj = self.registry("contract.service.deactivate")
3703+
3704+ def _configure_company(self):
3705+ self.company = self.company_obj.browse(self.cr, self.uid, 1)
3706+ self.company.write({"cutoff_day": "7", "invoice_day": "14"})
3707+
3708+ def _create_partner(self):
3709+ self.partner_id = self.partner_obj.name_create(
3710+ self.cr, self.uid,
3711+ "Test Partner"
3712+ )[0]
3713+
3714+ def _get_last_invoice(self, query=None, partner_id=None):
3715+ domain = [("partner_id", "=", partner_id or self.partner_id)]
3716+ if query:
3717+ domain.extend(query)
3718+ invoice = self.invoice_obj.browse(
3719+ self.cr, self.uid,
3720+ self.invoice_obj.search(
3721+ self.cr, self.uid,
3722+ domain,
3723+ order="id desc",
3724+ limit=1,
3725+ )[0]
3726+ )
3727+ return invoice
3728+
3729+ def _create_products(self):
3730+ cr, uid = self.cr, self.uid
3731+ self.p_internet = self.product_obj.create(cr, uid, {
3732+ "name": "Cable Internet",
3733+ "type": "service",
3734+ "analytic_line_type": LINE_TYPE_RECURRENT,
3735+ "code": "CBL",
3736+ "list_price": 56.0,
3737+ })
3738+ self.p_inst = self.product_obj.create(cr, uid, {
3739+ "name": "Installation",
3740+ "type": "service",
3741+ "analytic_line_type": LINE_TYPE_ONETIME,
3742+ "code": "INST",
3743+ "list_price": 50.0,
3744+ })
3745+
3746+ def _create_account(self):
3747+ wr = self.account_obj.default_get(self.cr, self.uid,
3748+ self.account_obj._columns.keys())
3749+ wr.update({
3750+ "partner_id": self.partner_id,
3751+ "pricelist_id": 1,
3752+ })
3753+ onchange = self.account_obj.on_change_partner_id(
3754+ self.cr, self.uid, [],
3755+ self.partner_id, False, False,
3756+ )["value"]
3757+ wr.update(onchange)
3758+
3759+ self.account_id = self.account_obj.create(self.cr, self.uid, wr)
3760+
3761+ def _create_activate_service(self, product, activation_date, context=None):
3762+ context = context or {}
3763+ cr, uid = self.cr, self.uid
3764+ service = self.service_obj.on_change_product_id(
3765+ cr, uid, [],
3766+ self.p_internet,
3767+ )["value"]
3768+ service.update({
3769+ "product_id": self.p_internet,
3770+ "account_id": self.account_id,
3771+ })
3772+ service_id = self.service_obj.create(cr, uid, service, context=context)
3773+ wiz_id = self.wiz_activate_obj.create(
3774+ cr, uid, {
3775+ "account_id": self.account_id,
3776+ "service_id": service_id,
3777+ "activation_date": activation_date,
3778+ },
3779+ context=context,
3780+ )
3781+ self.wiz_activate_obj.activate(cr, uid, [wiz_id], context=context)
3782+ return service_id
3783+
3784+ def _get_invoice_date_range(self, invoice_id):
3785+ # Until we put a bit more common sense into period handling, this
3786+ # is our way of knowing the start/end dates of an invoice
3787+ cr, uid = self.cr, self.uid
3788+ invoice_line = self.analytic_line_obj.browse(
3789+ cr, uid,
3790+ self.analytic_line_obj.search(cr, uid, [
3791+ ('to_invoice', '!=', False),
3792+ ('invoice_id', '=', invoice_id),
3793+ ])[0]
3794+ )
3795+ date_from = invoice_line.name[-24:-14]
3796+ date_to = invoice_line.name[-11:-1]
3797+ return date_from, date_to
3798
3799=== added file 'contract_isp_invoice/tests/test_deactivate.py'
3800--- contract_isp_invoice/tests/test_deactivate.py 1970-01-01 00:00:00 +0000
3801+++ contract_isp_invoice/tests/test_deactivate.py 2014-11-24 15:05:14 +0000
3802@@ -0,0 +1,165 @@
3803+# -*- coding: utf-8 -*-
3804+##############################################################################
3805+#
3806+# OpenERP, Open Source Management Solution
3807+# Copyright (C) 2014 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
3808+#
3809+# This program is free software: you can redistribute it and/or modify
3810+# it under the terms of the GNU Affero General Public License as
3811+# published by the Free Software Foundation, either version 3 of the
3812+# License, or (at your option) any later version.
3813+#
3814+# This program is distributed in the hope that it will be useful,
3815+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3816+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3817+# GNU Affero General Public License for more details.
3818+#
3819+# You should have received a copy of the GNU Affero General Public License
3820+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3821+#
3822+##############################################################################
3823+from __future__ import unicode_literals
3824+
3825+from datetime import date
3826+
3827+from openerp.tests.common import TransactionCase
3828+
3829+from .common import ServiceSetup
3830+
3831+
3832+class test_prorata_deactivate_service(TransactionCase, ServiceSetup):
3833+ """
3834+ Tests pro-rata crediting of services at deactivation
3835+ """
3836+ def setUp(self):
3837+ super(test_prorata_deactivate_service, self).setUp()
3838+ self._common_setup()
3839+
3840+ def _common_setup(self):
3841+ super(test_prorata_deactivate_service, self)._common_setup()
3842+ self._create_active_service()
3843+
3844+ def _create_active_service(self):
3845+ cr, uid = self.cr, self.uid
3846+ service = self.service_obj.on_change_product_id(
3847+ cr, uid, [],
3848+ self.p_internet,
3849+ )["value"]
3850+ service.update({
3851+ "product_id": self.p_internet,
3852+ "account_id": self.account_id,
3853+ "state": "active",
3854+ })
3855+ self.service_id = self.service_obj.create(cr, uid, service)
3856+
3857+ def _deactivate_service(self, deactivation_date, context=None):
3858+ context = context or {}
3859+ cr, uid = self.cr, self.uid
3860+ wiz_id = self.wiz_deactivate_obj.create(
3861+ cr, uid, {
3862+ "account_id": self.account_id,
3863+ "service_id": self.service_id,
3864+ "deactivation_date": deactivation_date,
3865+ },
3866+ context=context,
3867+ )
3868+ self.wiz_deactivate_obj.deactivate(cr, uid, [wiz_id], context=context)
3869+
3870+ def test_after_invoice_before_cutoff(self):
3871+ self.company.write({"invoice_day": "7", "cutoff_day": "21"})
3872+ self._deactivate_service("2014-02-14", {
3873+ "operation_date": date(2014, 2, 17),
3874+ })
3875+ invoice = self._get_last_invoice()
3876+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3877+ self.assertEquals(invoice.date_invoice, "2014-02-07")
3878+ self.assertEquals(date_from, "2014-02-14", "Wrong start_date")
3879+ self.assertEquals(date_to, "2014-03-31", "Wrong end_date")
3880+ self.assertAlmostEquals(
3881+ invoice.amount_untaxed,
3882+ (1 + self.service_obj._prorata_rate(14, 28)) * 56,
3883+ msg="Expect refund for 14-28 feb + march")
3884+ self.assertEquals(invoice.type, "out_refund")
3885+
3886+ def test_after_invoice_after_cutoff(self):
3887+ self.company.write({"invoice_day": "7", "cutoff_day": "21"})
3888+ self._deactivate_service("2014-02-14", {
3889+ "operation_date": date(2014, 2, 23),
3890+ })
3891+ invoice = self._get_last_invoice()
3892+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3893+ self.assertEquals(invoice.date_invoice, "2014-03-07")
3894+ self.assertEquals(date_from, "2014-02-14", "Wrong start_date")
3895+ self.assertEquals(date_to, "2014-03-31", "Wrong end_date")
3896+ self.assertAlmostEquals(
3897+ invoice.amount_untaxed,
3898+ (1 + self.service_obj._prorata_rate(14, 28)) * 56,
3899+ msg="Expect refund for 14-28 feb + march")
3900+ self.assertEquals(invoice.type, "out_refund")
3901+
3902+ def test_before_invoice_past_deactivation_curmonth(self):
3903+ self.company.write({"invoice_day": "14", "cutoff_day": "21"})
3904+ self._deactivate_service("2014-02-07", {
3905+ "operation_date": date(2014, 2, 8),
3906+ })
3907+ invoice = self._get_last_invoice()
3908+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3909+ self.assertEquals(invoice.date_invoice, "2014-02-14")
3910+ self.assertEquals(date_from, "2014-02-07", "Wrong start_date")
3911+ self.assertEquals(date_to, "2014-02-28", "Wrong end_date")
3912+ self.assertAlmostEquals(
3913+ invoice.amount_untaxed,
3914+ self.service_obj._prorata_rate(21, 28) * 56,
3915+ msg="Expect refund for 7-28 feb")
3916+ self.assertEquals(invoice.type, "out_refund")
3917+
3918+ def test_before_invoice_past_deactivation_past_month(self):
3919+ self.company.write({"invoice_day": "14", "cutoff_day": "21"})
3920+ self._deactivate_service("2014-02-07", {
3921+ "operation_date": date(2014, 3, 8),
3922+ })
3923+ invoice = self._get_last_invoice()
3924+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3925+ self.assertEquals(invoice.date_invoice, "2014-03-14")
3926+ self.assertEquals(date_from, "2014-02-07", "Wrong start_date")
3927+ self.assertEquals(date_to, "2014-03-31", "Wrong end_date")
3928+ self.assertAlmostEquals(
3929+ invoice.amount_untaxed,
3930+ (1 + self.service_obj._prorata_rate(21, 28)) * 56,
3931+ msg="Expect refund for 7-28 feb + march")
3932+ self.assertEquals(invoice.type, "out_refund")
3933+
3934+ def test_before_invoice_day_future_deactivation_same_month(self):
3935+ self.company.write({"invoice_day": "14", "cutoff_day": "21"})
3936+ self._deactivate_service("2014-02-14", {
3937+ "operation_date": date(2014, 2, 7),
3938+ })
3939+ invoice = self._get_last_invoice()
3940+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3941+ self.assertEquals(invoice.date_invoice, "2014-02-14")
3942+ self.assertEquals(date_from, "2014-02-07", "Wrong start_date")
3943+ self.assertEquals(date_to, "2014-02-14", "Wrong end_date")
3944+ self.assertAlmostEquals(
3945+ invoice.amount_untaxed,
3946+ self.service_obj._prorata_rate(7, 28) * 56,
3947+ msg="Expect invoice for 7-14 feb")
3948+ self.assertEquals(invoice.type, "out_invoice")
3949+
3950+ def test_before_invoice_day_future_deactivation_future_month(self):
3951+ self.company.write({"invoice_day": "14", "cutoff_day": "21"})
3952+ self._deactivate_service("2014-04-16", {
3953+ "operation_date": date(2014, 2, 7),
3954+ })
3955+ invoice = self._get_last_invoice()
3956+ date_from, date_to = self._get_invoice_date_range(invoice.id)
3957+ self.assertEquals(invoice.date_invoice, "2014-02-14")
3958+ self.assertEquals(date_from, "2014-02-07", "Wrong start_date")
3959+ self.assertEquals(date_to, "2014-04-16", "Wrong end_date")
3960+ self.assertAlmostEquals(
3961+ invoice.amount_untaxed,
3962+ 56 * (
3963+ self.service_obj._prorata_rate(21, 28) +
3964+ self.service_obj._prorata_rate(16, 30) +
3965+ 1),
3966+ msg="Expect invoice for 7-28 feb, march, 1-16 apr")
3967+ self.assertEquals(invoice.type, "out_invoice")
3968
3969=== added file 'contract_isp_invoice/tests/test_prorata.py'
3970--- contract_isp_invoice/tests/test_prorata.py 1970-01-01 00:00:00 +0000
3971+++ contract_isp_invoice/tests/test_prorata.py 2014-11-24 15:05:14 +0000
3972@@ -0,0 +1,179 @@
3973+# -*- coding: utf-8 -*-
3974+##############################################################################
3975+#
3976+# OpenERP, Open Source Management Solution
3977+# Copyright (C) 2014 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
3978+#
3979+# This program is free software: you can redistribute it and/or modify
3980+# it under the terms of the GNU Affero General Public License as
3981+# published by the Free Software Foundation, either version 3 of the
3982+# License, or (at your option) any later version.
3983+#
3984+# This program is distributed in the hope that it will be useful,
3985+# but WITHOUT ANY WARRANTY; without even the implied warranty of
3986+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3987+# GNU Affero General Public License for more details.
3988+#
3989+# You should have received a copy of the GNU Affero General Public License
3990+# along with this program. If not, see <http://www.gnu.org/licenses/>.
3991+#
3992+##############################################################################
3993+from __future__ import unicode_literals
3994+
3995+from datetime import date
3996+
3997+from openerp.tests.common import TransactionCase
3998+
3999+from .common import ServiceSetup
4000+
4001+
4002+class test_prorata_activate_service(TransactionCase, ServiceSetup):
4003+ """
4004+ Tests pro-rata invoicing of services
4005+ """
4006+
4007+ def setUp(self):
4008+ super(test_prorata_activate_service, self).setUp()
4009+ self._common_setup()
4010+
4011+ def test_prorata_after_invoice_before_cutoff_current_month(self):
4012+ """
4013+ Test prorata when:
4014+ invoice_day < operation_date <= end of month
4015+ operation date < cutoff date
4016+ """
4017+ cr, uid = self.cr, self.uid
4018+ self.company.write({"invoice_day": "7", "cutoff_day": "24"})
4019+ self._create_activate_service(self.p_internet, "2014-01-13", {
4020+ "operation_date": date(2014, 1, 14),
4021+ })
4022+
4023+ invoice = self.invoice_obj.browse(
4024+ cr, uid,
4025+ self.invoice_obj.search(cr, uid,
4026+ [('partner_id', '=', self.partner_id)])[0]
4027+ )
4028+ self.assertEquals(invoice.date_invoice, "2014-01-07")
4029+ date_from, date_to = self._get_invoice_date_range(invoice.id)
4030+ self.assertEquals(date_from, "2014-02-13", "Wrong start date")
4031+ self.assertEquals(date_to, "2014-02-28", "Wrong end date")
4032+ self.assertAlmostEquals(
4033+ invoice.amount_untaxed,
4034+ self.service_obj._prorata_rate(15, 28) * 56,
4035+ msg="~Half month, 15/28 of 56",
4036+ delta=0.01)
4037+
4038+ def test_prorata_after_invoice_before_cutoff_past_month(self):
4039+ """
4040+ Test prorata when:
4041+ invoice_day < operation_date <= end of month
4042+ operation date < cutoff date
4043+ """
4044+ cr, uid = self.cr, self.uid
4045+ self.company.write({"invoice_day": "7", "cutoff_day": "21"})
4046+ self._create_activate_service(self.p_internet, "2014-08-11", {
4047+ "operation_date": date(2014, 9, 10),
4048+ })
4049+
4050+ invoice = self.invoice_obj.browse(
4051+ cr, uid,
4052+ self.invoice_obj.search(cr, uid,
4053+ [('partner_id', '=', self.partner_id)])[0]
4054+ )
4055+ self.assertEquals(invoice.date_invoice, "2014-09-07")
4056+ date_from, date_to = self._get_invoice_date_range(invoice.id)
4057+ self.assertEquals(date_from, "2014-09-11",
4058+ "Wrong start date, expected activation + 1 month")
4059+ self.assertEquals(date_to, "2014-10-31",
4060+ "Wrong end date, end of (op) next month")
4061+
4062+ self.assertAlmostEquals(
4063+ invoice.amount_untaxed,
4064+ (1 + self.service_obj._prorata_rate(19, 30)) * 56,
4065+ msg="Oct 100%, Sept 11-30",
4066+ delta=0.01)
4067+
4068+ def test_prorata_after_invoice_after_cutoff(self):
4069+ """
4070+ Test prorata when:
4071+ invoice_day < operation_date <= end of month
4072+ operation date > cutoff date
4073+ """
4074+ cr, uid = self.cr, self.uid
4075+ self.company.write({"invoice_day": "7", "cutoff_day": "24"})
4076+ self._create_activate_service(self.p_internet, "2014-01-15", {
4077+ "operation_date": date(2014, 1, 26),
4078+ })
4079+
4080+ invoice = self.invoice_obj.browse(
4081+ cr, uid,
4082+ self.invoice_obj.search(cr, uid,
4083+ [('partner_id', '=', self.partner_id)])[0]
4084+ )
4085+ self.assertEquals(invoice.date_invoice, "2014-02-07")
4086+ date_from, date_to = self._get_invoice_date_range(invoice.id)
4087+ self.assertEquals(date_from, "2014-02-15", "Wrong start date")
4088+ self.assertEquals(date_to, "2014-02-28", "Wrong end date")
4089+ self.assertAlmostEquals(
4090+ invoice.amount_untaxed,
4091+ self.service_obj._prorata_rate(13, 28) * 56,
4092+ msg="13/28 of 56",
4093+ delta=0.01)
4094+
4095+ def test_prorata_before_invoice_past_month_activation(self):
4096+ """
4097+ Test prorata when:
4098+ 1 <= operation_date <= invoice_day
4099+ activation is in the past month
4100+
4101+ """
4102+ cr, uid = self.cr, self.uid
4103+ self.company.write({"invoice_day": "7", "cutoff_day": "21"})
4104+ self._create_activate_service(self.p_internet, "2014-01-27", {
4105+ "operation_date": date(2014, 2, 1),
4106+ })
4107+
4108+ invoice = self.invoice_obj.browse(
4109+ cr, uid,
4110+ self.invoice_obj.search(cr, uid,
4111+ [('partner_id', '=', self.partner_id)])[0]
4112+ )
4113+ self.assertEquals(invoice.date_invoice, "2014-02-07")
4114+ date_from, date_to = self._get_invoice_date_range(invoice.id)
4115+ self.assertEquals(date_from, "2014-02-27", "Wrong start date")
4116+ self.assertEquals(date_to, "2014-02-28", "Wrong end date")
4117+ self.assertAlmostEquals(
4118+ invoice.amount_untaxed,
4119+ self.service_obj._prorata_rate(1, 28) * 56,
4120+ msg="1 day in feb",
4121+ delta=0.01)
4122+
4123+ def test_prorata_before_invoice_current_month_activation(self):
4124+ """
4125+ Test prorata when:
4126+ 1 <= operation_date <= invoice_day
4127+ activation is in the current month and < invoice_day
4128+
4129+ """
4130+ cr, uid = self.cr, self.uid
4131+ self.company.write({"invoice_day": "21", "cutoff_day": "24"})
4132+ self._create_activate_service(self.p_internet, "2014-02-14", {
4133+ "operation_date": date(2014, 2, 10),
4134+ })
4135+
4136+ invoice = self.invoice_obj.browse(
4137+ cr, uid,
4138+ self.invoice_obj.search(cr, uid,
4139+ [('partner_id', '=', self.partner_id)])[0]
4140+ )
4141+ self.assertEquals(invoice.date_invoice, "2014-02-21")
4142+ date_from, date_to = self._get_invoice_date_range(invoice.id)
4143+ self.assertEquals(date_from, "2014-03-01", "Wrong start date")
4144+ self.assertEquals(date_to, "2014-03-14", "Wrong end date")
4145+ self.assertAlmostEquals(
4146+ invoice.amount_untaxed,
4147+ self.service_obj._prorata_rate(14, 31) * 56,
4148+ msg="credit for 1-14 in match",
4149+ delta=0.01)
4150+ self.assertEquals(invoice.type, "out_refund",
4151+ msg="Expected Credit Note")
4152
4153=== added file 'contract_isp_invoice/tests/test_reseller.py'
4154--- contract_isp_invoice/tests/test_reseller.py 1970-01-01 00:00:00 +0000
4155+++ contract_isp_invoice/tests/test_reseller.py 2014-11-24 15:05:14 +0000
4156@@ -0,0 +1,70 @@
4157+# -*- coding: utf-8 -*-
4158+##############################################################################
4159+#
4160+# OpenERP, Open Source Management Solution
4161+# Copyright (C) 2014 Savoir-faire Linux (<http://www.savoirfairelinux.com>).
4162+#
4163+# This program is free software: you can redistribute it and/or modify
4164+# it under the terms of the GNU Affero General Public License as
4165+# published by the Free Software Foundation, either version 3 of the
4166+# License, or (at your option) any later version.
4167+#
4168+# This program is distributed in the hope that it will be useful,
4169+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4170+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4171+# GNU Affero General Public License for more details.
4172+#
4173+# You should have received a copy of the GNU Affero General Public License
4174+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4175+#
4176+##############################################################################
4177+from __future__ import unicode_literals
4178+
4179+from datetime import date
4180+
4181+from openerp.tests.common import TransactionCase
4182+
4183+from .common import ServiceSetup
4184+
4185+
4186+class test_reseller_invoice(TransactionCase, ServiceSetup):
4187+ """
4188+ Tests specific reseller behaviors for invoices
4189+ """
4190+ def setUp(self):
4191+ super(test_reseller_invoice, self).setUp()
4192+ self._common_setup()
4193+
4194+ def _create_partner(self):
4195+ cr, uid = self.cr, self.uid
4196+ p_o = self.partner_obj
4197+ self.parent_id = p_o.create(cr, uid, {
4198+ "is_company": True,
4199+ "is_reseller": True,
4200+ "name": "Test Company",
4201+ })
4202+ self.partner_id = p_o.create(cr, uid, {
4203+ "name": "Test Partner",
4204+ "parent_id": self.parent_id,
4205+ })
4206+
4207+ def test_prorata_invoice(self):
4208+ self._create_activate_service(self.p_internet, "2014-02-08", {
4209+ "operation_date": date(2014, 2, 10),
4210+ })
4211+
4212+ self.assertEquals(
4213+ len(self.invoice_obj.search(self.cr, self.uid, [
4214+ ('partner_id', '=', self.parent_id),
4215+ ])),
4216+ 1,
4217+ "Expected one new invoice on parent"
4218+ )
4219+
4220+ self.assertEquals(
4221+ len(self.invoice_obj.search(self.cr, self.uid, [
4222+ ('partner_id', '=', self.partner_id),
4223+ ])),
4224+ 0,
4225+ "Expected no new invoice on child"
4226+ )
4227
4228=== modified file 'contract_isp_invoice/wizard/activate_contract_service.py'
4229--- contract_isp_invoice/wizard/activate_contract_service.py 2013-10-04 16:19:20 +0000
4230+++ contract_isp_invoice/wizard/activate_contract_service.py 2014-11-24 15:05:14 +0000
4231@@ -20,10 +20,8 @@
4232 #
4233 ##############################################################################
4234
4235-import time
4236-import datetime
4237-from openerp.osv import orm, fields
4238-from openerp.addons.contract_isp.contract import add_months
4239+from openerp.osv import orm
4240+from ..invoice import PROCESS_PRORATA
4241
4242
4243 class contract_service_activate(orm.TransientModel):
4244@@ -33,29 +31,39 @@
4245 if context is None:
4246 context = {}
4247
4248- account_invoice_obj = self.pool.get('account.invoice')
4249- account_voucher_obj = self.pool.get('account.voucher')
4250- account_move_obj = self.pool.get('account.move')
4251- res_company_obj = self.pool.get('res.company')
4252 wizard = self.browse(cr, uid, ids[0], context)
4253
4254 ret = super(contract_service_activate, self).activate(cr, uid,
4255 ids,
4256 context=context)
4257
4258- contract_service_obj = self.pool.get('contract.service')
4259- account_analytic_account_obj = self.pool.get('account.analytic.account')
4260- account_move_line_obj = self.pool.get('account.move.line')
4261-
4262- query = [
4263- ('account_id', '=', wizard.account_id.id),
4264- ('state', '=', 'draft')
4265- ]
4266- # Check if all services were activated
4267- if not contract_service_obj.search(cr, uid, query, context=context):
4268-
4269- # jgama - Try to create the prorata invoice
4270- pro_inv = account_analytic_account_obj.create_invoice(
4271- cr, uid, wizard.account_id.id, prorata=True, context=context)
4272+ account_analytic_account_obj = self.pool.get('account.analytic.account')
4273+
4274+ account_analytic_account_obj.create_invoice(
4275+ cr, uid, wizard.account_id.id,
4276+ source_process=PROCESS_PRORATA,
4277+ context=context)
4278+
4279+ return ret
4280+
4281+
4282+class contract_service_deactivate(orm.TransientModel):
4283+ _inherit = 'contract.service.deactivate'
4284+
4285+ def deactivate(self, cr, uid, ids, context=None):
4286+ if context is None:
4287+ context = {}
4288+
4289+ wizard = self.browse(cr, uid, ids[0], context)
4290+
4291+ ret = super(contract_service_deactivate, self).deactivate(
4292+ cr, uid, ids, context=context)
4293+
4294+ account_analytic_account_obj = self.pool.get('account.analytic.account')
4295+
4296+ account_analytic_account_obj.create_invoice(
4297+ cr, uid, wizard.account_id.id,
4298+ source_process=PROCESS_PRORATA,
4299+ context=context)
4300
4301 return ret
4302
4303=== modified file 'contract_isp_invoice/wizard/close_contract.py'
4304--- contract_isp_invoice/wizard/close_contract.py 2013-09-16 17:59:01 +0000
4305+++ contract_isp_invoice/wizard/close_contract.py 2014-11-24 15:05:14 +0000
4306@@ -27,7 +27,7 @@
4307 from openerp.report import report_sxw
4308 from openerp.tools import convert
4309 from openerp.tools.translate import _
4310-from openerp.addons.contract_isp.contract import date_interval
4311+from openerp.addons.contract_isp.contract import date_interval, format_interval
4312
4313
4314 class contract_isp_close(orm.TransientModel):
4315@@ -71,7 +71,8 @@
4316 last_invoice_id[-1],
4317 context=context)
4318 if last_invoice.date_invoice > wizard.close_date:
4319- raise orm.except_orm(_('Error!'), _('Close date before last invoice date!'))
4320+ raise orm.except_orm(_('Error!'),
4321+ _('Close date before last invoice date!'))
4322
4323 amount_untaxed = last_invoice.amount_untaxed
4324
4325@@ -81,10 +82,12 @@
4326 used_days = month_days - int(wizard.close_date[8:10])
4327 ptx = (100 * used_days / month_days) / 100.0
4328 amount = amount_untaxed * ptx
4329- interval = date_interval(datetime.date(int(wizard.close_date[:4]),
4330- int(wizard.close_date[5:7]),
4331- int(wizard.close_date[8:10])),
4332- True)
4333+ start_date, end_date = date_interval(
4334+ datetime.date(int(wizard.close_date[:4]),
4335+ int(wizard.close_date[5:7]),
4336+ int(wizard.close_date[8:10])),
4337+ True)
4338+ interval = format_interval(start_date, end_date)
4339
4340 line = {
4341 'name': ' '.join([_('Credit refund'), interval]),
4342@@ -111,4 +114,4 @@
4343 mail_message.write({'type': 'email'})
4344 contract.write({'state': 'close'})
4345 return {}
4346-
4347\ No newline at end of file
4348+
4349
4350=== modified file 'contract_isp_package_configurator/__init__.py'
4351--- contract_isp_package_configurator/__init__.py 2013-09-16 17:59:01 +0000
4352+++ contract_isp_package_configurator/__init__.py 2014-11-24 15:05:14 +0000
4353@@ -20,6 +20,10 @@
4354 #
4355 ##############################################################################
4356
4357-import wizard
4358-import workflow
4359-import company
4360+from . import (
4361+ account_analytic,
4362+ company,
4363+ contract,
4364+ wizard,
4365+ workflow,
4366+)
4367
4368=== modified file 'contract_isp_package_configurator/__openerp__.py'
4369--- contract_isp_package_configurator/__openerp__.py 2013-09-16 17:59:01 +0000
4370+++ contract_isp_package_configurator/__openerp__.py 2014-11-24 15:05:14 +0000
4371@@ -35,6 +35,7 @@
4372 'depends': ['contract_isp', 'product_dependencies', 'stock'],
4373 'data': [
4374 'security/ir.model.access.csv',
4375+ 'security/groups.xml',
4376 'wizard/package_configurator.xml',
4377 'workflow/contract_isp_package_configurator.xml',
4378 'contract_isp_package_configurator_view.xml',
4379
4380=== added file 'contract_isp_package_configurator/account_analytic.py'
4381--- contract_isp_package_configurator/account_analytic.py 1970-01-01 00:00:00 +0000
4382+++ contract_isp_package_configurator/account_analytic.py 2014-11-24 15:05:14 +0000
4383@@ -0,0 +1,61 @@
4384+# -*- coding: utf-8 -*-
4385+
4386+##############################################################################
4387+#
4388+# OpenERP, Open Source Management Solution
4389+# Copyright (C) 2014 Savoirfaire-Linux Inc. (<www.savoirfairelinux.com>).
4390+#
4391+# This program is free software: you can redistribute it and/or modify
4392+# it under the terms of the GNU Affero General Public License as
4393+# published by the Free Software Foundation, either version 3 of the
4394+# License, or (at your option) any later version.
4395+#
4396+# This program is distributed in the hope that it will be useful,
4397+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4398+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4399+# GNU Affero General Public License for more details.
4400+#
4401+# You should have received a copy of the GNU Affero General Public License
4402+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4403+#
4404+##############################################################################
4405+
4406+from openerp.osv import fields, orm
4407+
4408+
4409+class AccountAnalyticAccount(orm.Model):
4410+ _name = 'account.analytic.account'
4411+ _inherit = 'account.analytic.account'
4412+
4413+ def _get_ui_flags(self, cr, uid, ids, field_names, arg, context):
4414+ res = {}
4415+ for account in self.browse(cr, uid, ids, context=context):
4416+ res[account.id] = cur = {
4417+ 'ui_reserve': False,
4418+ 'ui_return': False,
4419+ }
4420+ for service in account.contract_service_ids:
4421+ if not service.product_id.type == "product":
4422+ continue
4423+ if service.prodlot_id:
4424+ cur['ui_return'] = True
4425+ else:
4426+ cur['ui_reserve'] = True
4427+
4428+ return res
4429+
4430+ _columns = {
4431+ 'prodlot_id': fields.many2one(
4432+ 'stock.production.lot',
4433+ 'Serial Number',
4434+ required=False,
4435+ ),
4436+ 'ui_reserve': fields.function(
4437+ _get_ui_flags,
4438+ type='boolean', method=True, multi='ui_flags',
4439+ ),
4440+ 'ui_return': fields.function(
4441+ _get_ui_flags,
4442+ type='boolean', method=True, multi='ui_flags',
4443+ ),
4444+ }
4445
4446=== added file 'contract_isp_package_configurator/contract.py'
4447--- contract_isp_package_configurator/contract.py 1970-01-01 00:00:00 +0000
4448+++ contract_isp_package_configurator/contract.py 2014-11-24 15:05:14 +0000
4449@@ -0,0 +1,35 @@
4450+# -*- coding: utf-8 -*-
4451+
4452+##############################################################################
4453+#
4454+# OpenERP, Open Source Management Solution
4455+# Copyright (C) 2014 Savoirfaire-Linux Inc. (<www.savoirfairelinux.com>).
4456+#
4457+# This program is free software: you can redistribute it and/or modify
4458+# it under the terms of the GNU Affero General Public License as
4459+# published by the Free Software Foundation, either version 3 of the
4460+# License, or (at your option) any later version.
4461+#
4462+# This program is distributed in the hope that it will be useful,
4463+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4464+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4465+# GNU Affero General Public License for more details.
4466+#
4467+# You should have received a copy of the GNU Affero General Public License
4468+# along with this program. If not, see <http://www.gnu.org/licenses/>.
4469+#
4470+##############################################################################
4471+
4472+from openerp.osv import fields, orm
4473+
4474+
4475+class ContractService(orm.Model):
4476+ _name = 'contract.service'
4477+ _inherit = 'contract.service'
4478+ _columns = {
4479+ 'prodlot_id': fields.many2one(
4480+ 'stock.production.lot',
4481+ 'Serial Number',
4482+ required=False,
4483+ ),
4484+ }
4485
4486=== modified file 'contract_isp_package_configurator/contract_isp_package_configurator_view.xml'
4487--- contract_isp_package_configurator/contract_isp_package_configurator_view.xml 2013-09-16 17:59:01 +0000
4488+++ contract_isp_package_configurator/contract_isp_package_configurator_view.xml 2014-11-24 15:05:14 +0000
4489@@ -1,28 +1,46 @@
4490 <?xml version="1.0" encoding="UTF-8"?>
4491 <openerp>
4492 <data>
4493- <record id="contract_isp_form_package_configurator_wizard" model="ir.ui.view">
4494- <field name="name">contract.isp.form.package.configurator.wizard</field>
4495- <field name="model">account.analytic.account</field>
4496- <field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
4497- <field name="arch" type="xml">
4498- <xpath expr="//div[@name='buttons']" position='inside'>
4499- <button string="Package Configurator"
4500- name="%(action_contract_isp_package_configurator)d"
4501- type="action" />
4502- </xpath>
4503- </field>
4504- </record>
4505- <record id="view_company_form_package_configurator" model="ir.ui.view">
4506- <field name="name">company.form.package.configurator</field>
4507- <field name="model">res.company</field>
4508- <field name="inherit_id" ref="contract_isp.view_company_contract_isp_form"/>
4509- <field name="arch" type="xml">
4510- <field name="parent_account_id" position="before">
4511- <field name="default_product_category" />
4512- </field>
4513- </field>
4514- </record>
4515+ <record id="contract_isp_form_package_configurator_wizard" model="ir.ui.view">
4516+ <field name="name">contract.isp.form.package.configurator.wizard</field>
4517+ <field name="model">account.analytic.account</field>
4518+ <field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
4519+ <field name="arch" type="xml">
4520+ <xpath expr="//div[@name='buttons']" position='inside'>
4521+ <field name="ui_reserve" invisible="1" />
4522+ <field name="ui_return" invisible="1" />
4523+ <button string="Package Configurator"
4524+ name="%(action_contract_isp_package_configurator)d"
4525+ type="action" />
4526+ <button string="Reserve Equipment"
4527+ name="%(action_contract_isp_reserve_equipment)d"
4528+ attrs="{'invisible': [('ui_reserve', '=', False)]}"
4529+ groups="contract_isp_package_configurator.group_isp_add_equipment"
4530+ type="action" />
4531+ <button string="Exchange Equipment"
4532+ name="%(action_contract_isp_exchange_equipment)d"
4533+ attrs="{'invisible': [('ui_return', '=', False)]}"
4534+ groups="contract_isp_package_configurator.group_isp_add_equipment"
4535+ type="action" />
4536+ <button string="Return Equipment"
4537+ name="%(action_contract_isp_return_equipment)d"
4538+ attrs="{'invisible': [('ui_return', '=', False)]}"
4539+ groups="contract_isp_package_configurator.group_isp_remove_equipment"
4540+ type="action" />
4541+ </xpath>
4542+ </field>
4543+ </record>
4544+
4545+ <record id="view_company_form_package_configurator" model="ir.ui.view">
4546+ <field name="name">company.form.package.configurator</field>
4547+ <field name="model">res.company</field>
4548+ <field name="inherit_id" ref="contract_isp.view_company_contract_isp_form"/>
4549+ <field name="arch" type="xml">
4550+ <field name="parent_account_id" position="before">
4551+ <field name="default_product_category" />
4552+ </field>
4553+ </field>
4554+ </record>
4555
4556 </data>
4557 </openerp>
4558
4559=== modified file 'contract_isp_package_configurator/i18n/contract_isp_package_configurator.pot'
4560--- contract_isp_package_configurator/i18n/contract_isp_package_configurator.pot 2013-09-16 17:59:01 +0000
4561+++ contract_isp_package_configurator/i18n/contract_isp_package_configurator.pot 2014-11-24 15:05:14 +0000
4562@@ -0,0 +1,343 @@
4563+# Translation of OpenERP Server.
4564+# This file contains the translation of the following modules:
4565+# * contract_isp_package_configurator
4566+#
4567+msgid ""
4568+msgstr ""
4569+"Project-Id-Version: OpenERP Server 7.0\n"
4570+"Report-Msgid-Bugs-To: \n"
4571+"POT-Creation-Date: 2014-09-23 00:06+0000\n"
4572+"PO-Revision-Date: 2014-09-23 00:06+0000\n"
4573+"Last-Translator: <>\n"
4574+"Language-Team: \n"
4575+"MIME-Version: 1.0\n"
4576+"Content-Type: text/plain; charset=UTF-8\n"
4577+"Content-Transfer-Encoding: \n"
4578+"Plural-Forms: \n"
4579+
4580+#. module: contract_isp_package_configurator
4581+#: view:contract.service.configurator:0
4582+msgid "Add"
4583+msgstr ""
4584+
4585+#. module: contract_isp_package_configurator
4586+#: model:res.groups,name:contract_isp_package_configurator.group_isp_add_equipment
4587+msgid "Add Equipment"
4588+msgstr ""
4589+
4590+#. module: contract_isp_package_configurator
4591+#: field:contract.service.configurator,current_product_id:0
4592+msgid "Add Product"
4593+msgstr ""
4594+
4595+#. module: contract_isp_package_configurator
4596+#: selection:contract.service.configurator.dependency.line,state:0
4597+#: selection:contract.service.configurator.line,state:0
4598+msgid "Added"
4599+msgstr ""
4600+
4601+#. module: contract_isp_package_configurator
4602+#: code:_description:0
4603+#: model:ir.model,name:contract_isp_package_configurator.model_account_analytic_account
4604+#, python-format
4605+msgid "Analytic Account"
4606+msgstr ""
4607+
4608+#. module: contract_isp_package_configurator
4609+#: view:contract.service.configurator:0
4610+#: view:contract.service.equipment.manage:0
4611+msgid "Cancel"
4612+msgstr ""
4613+
4614+#. module: contract_isp_package_configurator
4615+#: view:contract.service.configurator:0
4616+#: field:contract.service.configurator,product_category_id:0
4617+#: field:contract.service.configurator,root_category_id:0
4618+msgid "Category"
4619+msgstr ""
4620+
4621+#. module: contract_isp_package_configurator
4622+#: view:contract.service.configurator:0
4623+msgid "Click on add to view the available components"
4624+msgstr ""
4625+
4626+#. module: contract_isp_package_configurator
4627+#: code:_description:0
4628+#: model:ir.model,name:contract_isp_package_configurator.model_res_company
4629+#, python-format
4630+msgid "Companies"
4631+msgstr ""
4632+
4633+#. module: contract_isp_package_configurator
4634+#: field:contract.service.configurator,contract_id:0
4635+#: field:contract.service.equipment.manage,contract_id:0
4636+msgid "Contract"
4637+msgstr ""
4638+
4639+#. module: contract_isp_package_configurator
4640+#: field:res.company,default_product_category:0
4641+msgid "Default Product Category"
4642+msgstr ""
4643+
4644+#. module: contract_isp_package_configurator
4645+#: field:contract.service.configurator,dependency_ids:0
4646+msgid "Dependencies"
4647+msgstr ""
4648+
4649+#. module: contract_isp_package_configurator
4650+#: view:contract.service.configurator:0
4651+#: selection:contract.service.configurator,state:0
4652+#: selection:contract.service.configurator.dependency.line,state:0
4653+#: selection:contract.service.configurator.line,state:0
4654+msgid "Done"
4655+msgstr ""
4656+
4657+#. module: contract_isp_package_configurator
4658+#: view:contract.service.equipment.manage:0
4659+msgid "Exchange"
4660+msgstr ""
4661+
4662+#. module: contract_isp_package_configurator
4663+#: view:account.analytic.account:0
4664+#: model:ir.actions.act_window,name:contract_isp_package_configurator.action_contract_isp_exchange_equipment
4665+msgid "Exchange Equipment"
4666+msgstr ""
4667+
4668+#. module: contract_isp_package_configurator
4669+#: field:contract.service.configurator.dependency.line,handle_dependency:0
4670+#: field:contract.service.configurator.line,handle_dependency:0
4671+msgid "Handle dependencies"
4672+msgstr ""
4673+
4674+#. module: contract_isp_package_configurator
4675+#: selection:contract.service.configurator.dependency.line,state:0
4676+#: selection:contract.service.configurator.line,state:0
4677+msgid "Information"
4678+msgstr ""
4679+
4680+#. module: contract_isp_package_configurator
4681+#: field:contract.service.configurator,is_level2:0
4682+msgid "Is level 2"
4683+msgstr ""
4684+
4685+#. module: contract_isp_package_configurator
4686+#: field:contract.service.configurator,line_ids:0
4687+msgid "Line"
4688+msgstr ""
4689+
4690+#. module: contract_isp_package_configurator
4691+#: field:contract.service.configurator.dependency.line,manager_id:0
4692+#: field:contract.service.configurator.line,manager_id:0
4693+msgid "Manage Equipment Wizard"
4694+msgstr ""
4695+
4696+#. module: contract_isp_package_configurator
4697+#: field:contract.service.configurator.dependency.line,message:0
4698+#: field:contract.service.configurator.line,message:0
4699+msgid "Message"
4700+msgstr ""
4701+
4702+#. module: contract_isp_package_configurator
4703+#: field:contract.service.configurator.dependency.line,name:0
4704+#: field:contract.service.configurator.line,name:0
4705+msgid "Name"
4706+msgstr ""
4707+
4708+#. module: contract_isp_package_configurator
4709+#: view:contract.service.configurator:0
4710+msgid "Next"
4711+msgstr ""
4712+
4713+#. module: contract_isp_package_configurator
4714+#: selection:contract.service.configurator.dependency.line,state:0
4715+#: selection:contract.service.configurator.line,state:0
4716+msgid "No Stock"
4717+msgstr ""
4718+
4719+#. module: contract_isp_package_configurator
4720+#: view:contract.service.configurator.line:0
4721+msgid "Ok"
4722+msgstr ""
4723+
4724+#. module: contract_isp_package_configurator
4725+#: view:account.analytic.account:0
4726+#: view:contract.service.configurator:0
4727+#: field:contract.service.configurator.dependency.line,configurator_id:0
4728+#: view:contract.service.configurator.line:0
4729+#: field:contract.service.configurator.line,configurator_id:0
4730+#: model:ir.actions.act_window,name:contract_isp_package_configurator.action_contract_isp_package_configurator
4731+msgid "Package Configurator"
4732+msgstr ""
4733+
4734+#. module: contract_isp_package_configurator
4735+#: field:contract.service.configurator.dependency.line,parent_id:0
4736+#: field:contract.service.configurator.line,parent_id:0
4737+msgid "Parent"
4738+msgstr ""
4739+
4740+#. module: contract_isp_package_configurator
4741+#: view:contract.service.configurator:0
4742+#: field:contract.service.configurator.dependency.line,product_id:0
4743+#: field:contract.service.configurator.line,product_id:0
4744+#: view:contract.service.equipment.manage:0
4745+msgid "Product"
4746+msgstr ""
4747+
4748+#. module: contract_isp_package_configurator
4749+#: view:contract.service.configurator:0
4750+#: view:contract.service.configurator.line:0
4751+msgid "Products"
4752+msgstr ""
4753+
4754+#. module: contract_isp_package_configurator
4755+#: model:res.groups,name:contract_isp_package_configurator.group_isp_remove_equipment
4756+msgid "Remove Equipment"
4757+msgstr ""
4758+
4759+#. module: contract_isp_package_configurator
4760+#: view:contract.service.equipment.manage:0
4761+msgid "Reserve"
4762+msgstr ""
4763+
4764+#. module: contract_isp_package_configurator
4765+#: view:account.analytic.account:0
4766+#: view:contract.service.equipment.manage:0
4767+#: model:ir.actions.act_window,name:contract_isp_package_configurator.action_contract_isp_reserve_equipment
4768+msgid "Reserve Equipment"
4769+msgstr ""
4770+
4771+#. module: contract_isp_package_configurator
4772+#: view:contract.service.equipment.manage:0
4773+msgid "Return"
4774+msgstr ""
4775+
4776+#. module: contract_isp_package_configurator
4777+#: view:account.analytic.account:0
4778+#: view:contract.service.equipment.manage:0
4779+#: model:ir.actions.act_window,name:contract_isp_package_configurator.action_contract_isp_return_equipment
4780+msgid "Return Equipment"
4781+msgstr ""
4782+
4783+#. module: contract_isp_package_configurator
4784+#: selection:contract.service.configurator,state:0
4785+msgid "Select components"
4786+msgstr ""
4787+
4788+#. module: contract_isp_package_configurator
4789+#: selection:contract.service.configurator,state:0
4790+msgid "Select product"
4791+msgstr ""
4792+
4793+#. module: contract_isp_package_configurator
4794+#: selection:contract.service.configurator.dependency.line,state:0
4795+#: selection:contract.service.configurator.line,state:0
4796+msgid "Select serial number"
4797+msgstr ""
4798+
4799+#. module: contract_isp_package_configurator
4800+#: field:contract.service.equipment.manage,product_id:0
4801+msgid "Selected Product"
4802+msgstr ""
4803+
4804+#. module: contract_isp_package_configurator
4805+#: view:contract.service.equipment.manage:0
4806+msgid "Serial"
4807+msgstr ""
4808+
4809+#. module: contract_isp_package_configurator
4810+#: field:account.analytic.account,prodlot_id:0
4811+#: field:contract.service,prodlot_id:0
4812+#: field:contract.service.configurator.dependency.line,serial:0
4813+#: view:contract.service.configurator.line:0
4814+#: field:contract.service.configurator.line,serial:0
4815+#: field:contract.service.equipment.manage,prodlot_id:0
4816+msgid "Serial Number"
4817+msgstr ""
4818+
4819+#. module: contract_isp_package_configurator
4820+#: help:res.company,default_product_category:0
4821+msgid "Set the default starting point for the package configuration wizard"
4822+msgstr ""
4823+
4824+#. module: contract_isp_package_configurator
4825+#: selection:contract.service.configurator,state:0
4826+msgid "Start"
4827+msgstr ""
4828+
4829+#. module: contract_isp_package_configurator
4830+#: field:contract.service.configurator,state:0
4831+#: field:contract.service.configurator.dependency.line,state:0
4832+#: field:contract.service.configurator.line,state:0
4833+msgid "State"
4834+msgstr ""
4835+
4836+#. module: contract_isp_package_configurator
4837+#: field:contract.service.configurator.dependency.line,stock_move_id:0
4838+#: field:contract.service.configurator.line,stock_move_id:0
4839+msgid "Stock Move"
4840+msgstr ""
4841+
4842+#. module: contract_isp_package_configurator
4843+#: model:res.groups,comment:contract_isp_package_configurator.group_isp_add_equipment
4844+msgid "The users in this group will be able to assign customer equipment"
4845+msgstr ""
4846+
4847+#. module: contract_isp_package_configurator
4848+#: model:res.groups,comment:contract_isp_package_configurator.group_isp_remove_equipment
4849+msgid "The users in this group will be able to unassign customers equipment"
4850+msgstr ""
4851+
4852+#. module: contract_isp_package_configurator
4853+#: view:contract.service.configurator.line:0
4854+msgid "We don't have this product in stock at the moment!"
4855+msgstr ""
4856+
4857+#. module: contract_isp_package_configurator
4858+#: code:_description:0
4859+#: model:ir.model,name:contract_isp_package_configurator.model_contract_service
4860+#, python-format
4861+msgid "contract.service"
4862+msgstr ""
4863+
4864+#. module: contract_isp_package_configurator
4865+#: code:_description:0
4866+#: model:ir.model,name:contract_isp_package_configurator.model_contract_service_configurator
4867+#, python-format
4868+msgid "contract.service.configurator"
4869+msgstr ""
4870+
4871+#. module: contract_isp_package_configurator
4872+#: code:_description:0
4873+#: model:ir.model,name:contract_isp_package_configurator.model_contract_service_configurator_dependency_line
4874+#, python-format
4875+msgid "contract.service.configurator.dependency.line"
4876+msgstr ""
4877+
4878+#. module: contract_isp_package_configurator
4879+#: code:_description:0
4880+#: model:ir.model,name:contract_isp_package_configurator.model_contract_service_configurator_line
4881+#, python-format
4882+msgid "contract.service.configurator.line"
4883+msgstr ""
4884+
4885+#. module: contract_isp_package_configurator
4886+#: code:_description:0
4887+#: model:ir.model,name:contract_isp_package_configurator.model_contract_service_equipment_manage
4888+#, python-format
4889+msgid "contract.service.equipment.manage"
4890+msgstr ""
4891+
4892+#. module: contract_isp_package_configurator
4893+#: view:contract.service.equipment.manage:0
4894+msgid "or"
4895+msgstr ""
4896+
4897+#. module: contract_isp_package_configurator
4898+#: field:account.analytic.account,ui_reserve:0
4899+#: field:account.analytic.account,ui_return:0
4900+#: field:contract.service.equipment.manage,products_all:0
4901+#: field:contract.service.equipment.manage,products_no_serial:0
4902+#: field:contract.service.equipment.manage,products_with_serial:0
4903+msgid "unknown"
4904+msgstr ""
4905+
4906
4907=== added file 'contract_isp_package_configurator/i18n/fr.po'
4908--- contract_isp_package_configurator/i18n/fr.po 1970-01-01 00:00:00 +0000
4909+++ contract_isp_package_configurator/i18n/fr.po 2014-11-24 15:05:14 +0000
4910@@ -0,0 +1,346 @@
4911+# Translation of OpenERP Server.
4912+# This file contains the translation of the following modules:
4913+# * contract_isp_package_configurator
4914+#
4915+msgid ""
4916+msgstr ""
4917+"Project-Id-Version: OpenERP Server 7.0\n"
4918+"Report-Msgid-Bugs-To: \n"
4919+"POT-Creation-Date: 2014-09-23 00:06+0000\n"
4920+"PO-Revision-Date: 2014-09-22 20:07-0500\n"
4921+"Last-Translator: Marc Cassuto <marc.cassuto@savoirfairelinux.com>\n"
4922+"Language-Team: \n"
4923+"Language: fr\n"
4924+"MIME-Version: 1.0\n"
4925+"Content-Type: text/plain; charset=UTF-8\n"
4926+"Content-Transfer-Encoding: 8bit\n"
4927+"X-Generator: Poedit 1.5.4\n"
4928+
4929+#. module: contract_isp_package_configurator
4930+#: view:contract.service.configurator:0
4931+msgid "Add"
4932+msgstr "Ajouter"
4933+
4934+#. module: contract_isp_package_configurator
4935+#: model:res.groups,name:contract_isp_package_configurator.group_isp_add_equipment
4936+msgid "Add Equipment"
4937+msgstr "Ajouter un équipement"
4938+
4939+#. module: contract_isp_package_configurator
4940+#: field:contract.service.configurator,current_product_id:0
4941+msgid "Add Product"
4942+msgstr "Ajouter un produit"
4943+
4944+#. module: contract_isp_package_configurator
4945+#: selection:contract.service.configurator.dependency.line,state:0
4946+#: selection:contract.service.configurator.line,state:0
4947+msgid "Added"
4948+msgstr "Ajouté"
4949+
4950+#. module: contract_isp_package_configurator
4951+#: code:_description:0
4952+#: model:ir.model,name:contract_isp_package_configurator.model_account_analytic_account
4953+#, python-format
4954+msgid "Analytic Account"
4955+msgstr "Contrat"
4956+
4957+#. module: contract_isp_package_configurator
4958+#: view:contract.service.configurator:0
4959+#: view:contract.service.equipment.manage:0
4960+msgid "Cancel"
4961+msgstr "Annuler"
4962+
4963+#. module: contract_isp_package_configurator
4964+#: view:contract.service.configurator:0
4965+#: field:contract.service.configurator,product_category_id:0
4966+#: field:contract.service.configurator,root_category_id:0
4967+msgid "Category"
4968+msgstr "Catégorie"
4969+
4970+#. module: contract_isp_package_configurator
4971+#: view:contract.service.configurator:0
4972+msgid "Click on add to view the available components"
4973+msgstr "Cliquer sur le bouton 'Ajouter' pour voir les produits disponibles"
4974+
4975+#. module: contract_isp_package_configurator
4976+#: code:_description:0
4977+#: model:ir.model,name:contract_isp_package_configurator.model_res_company
4978+#, python-format
4979+msgid "Companies"
4980+msgstr "Companies"
4981+
4982+#. module: contract_isp_package_configurator
4983+#: field:contract.service.configurator,contract_id:0
4984+#: field:contract.service.equipment.manage,contract_id:0
4985+msgid "Contract"
4986+msgstr "Contrat"
4987+
4988+#. module: contract_isp_package_configurator
4989+#: field:res.company,default_product_category:0
4990+msgid "Default Product Category"
4991+msgstr "Catégorie de Produit par défault"
4992+
4993+#. module: contract_isp_package_configurator
4994+#: field:contract.service.configurator,dependency_ids:0
4995+msgid "Dependencies"
4996+msgstr "Dépendances"
4997+
4998+#. module: contract_isp_package_configurator
4999+#: view:contract.service.configurator:0
5000+#: selection:contract.service.configurator,state:0
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches