Merge lp:~julie-w/unifield-server/US-5771 into lp:unifield-server

Proposed by jftempo on 2019-05-06
Status: Merged
Merged at revision: 5372
Proposed branch: lp:~julie-w/unifield-server/US-5771
Merge into: lp:unifield-server
Diff against target: 1509 lines (+707/-170)
22 files modified
bin/addons/account_corrections/wizard/analytic_distribution_wizard.py (+6/-0)
bin/addons/account_hq_entries/hq_entries.py (+10/-0)
bin/addons/analytic_distribution/account.py (+5/-75)
bin/addons/analytic_distribution/analytic_account_view.xml (+75/-4)
bin/addons/analytic_distribution/analytic_distribution.py (+26/-1)
bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml (+10/-4)
bin/addons/analytic_distribution/analytic_line.py (+16/-6)
bin/addons/analytic_distribution/report/funding_pool.py (+0/-27)
bin/addons/analytic_distribution/report/funding_pool.rml (+28/-28)
bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py (+26/-4)
bin/addons/analytic_override/analytic_account.py (+90/-0)
bin/addons/msf_doc_import/account.py (+5/-0)
bin/addons/msf_doc_import/msf_import_export.py (+123/-4)
bin/addons/msf_doc_import/msf_import_export_conf.py (+91/-5)
bin/addons/msf_homere_interface/hr.py (+25/-1)
bin/addons/msf_homere_interface/hr_payroll.py (+7/-0)
bin/addons/msf_homere_interface/hr_payroll_wizard.xml (+5/-2)
bin/addons/msf_homere_interface/hr_view.xml (+1/-1)
bin/addons/msf_profile/data/patches.xml (+5/-0)
bin/addons/msf_profile/i18n/fr_MF.po (+134/-8)
bin/addons/msf_profile/msf_profile.py (+18/-0)
bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+1/-0)
To merge this branch: bzr merge lp:~julie-w/unifield-server/US-5771
Reviewer Review Type Date Requested Status
UniField Reviewer Team 2019-05-06 Pending
Review via email: mp+366989@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/account_corrections/wizard/analytic_distribution_wizard.py'
2--- bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2018-09-28 16:00:25 +0000
3+++ bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2019-05-06 14:14:05 +0000
4@@ -148,6 +148,7 @@
5 context = {}
6 # Prepare some values
7 wizard = self.browse(cr, uid, wizard_id)
8+ ad_obj = self.pool.get('analytic.distribution')
9 company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
10 ml = wizard.move_line_id
11 orig_date = ml.source_date or ml.date
12@@ -240,6 +241,11 @@
13 break
14
15 for wiz_line in self.pool.get('analytic.distribution.wizard.fp.lines').browse(cr, uid, wiz_line_ids):
16+ if not ad_obj.check_dest_cc_compatibility(cr, uid, wiz_line.destination_id.id, wiz_line.cost_center_id.id, context=context):
17+ raise osv.except_osv(_('Error'),
18+ _('The Cost Center %s is not compatible with the Destination %s.') %
19+ (wiz_line.cost_center_id.code or '', wiz_line.destination_id.code or ''))
20+
21 if not wiz_line.distribution_line_id or wiz_line.distribution_line_id.id not in old_line_ids:
22 # new distribution line
23 #if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [wiz_line.analytic_id.id]):
24
25=== modified file 'bin/addons/account_hq_entries/hq_entries.py'
26--- bin/addons/account_hq_entries/hq_entries.py 2019-03-07 10:00:29 +0000
27+++ bin/addons/account_hq_entries/hq_entries.py 2019-05-06 14:14:05 +0000
28@@ -42,6 +42,7 @@
29 # Prepare some values
30 res = {}
31 logger = netsvc.Logger()
32+ ad_obj = self.pool.get('analytic.distribution')
33 # Search MSF Private Fund element, because it's valid with all accounts
34 try:
35 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
36@@ -57,6 +58,7 @@
37 # E/ DEST in list of available DEST in ACCOUNT
38 # F/ Check posting date with cost center and destination if exists
39 # G/ Check document date with funding pool
40+ # H/ Check Cost Center / Destination compatibility
41 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).
42 ## CC is mandatory, so always available:
43 # 1/ no FP, no DEST => Distro = valid
44@@ -132,6 +134,14 @@
45 res[line.id] = 'invalid'
46 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: DEST (%s) not compatible with account (%s)') % (line.id or '', line.destination_id.code or '', account.code or ''))
47 continue
48+ # H check
49+ if line.destination_id and line.cost_center_id and \
50+ not ad_obj.check_dest_cc_compatibility(cr, uid, line.destination_id.id, line.cost_center_id.id, context=context):
51+ res[line.id] = 'invalid'
52+ logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING,
53+ _('%s: CC (%s) not compatible with DEST (%s)') %
54+ (line.id or '', line.cost_center_id.code or '', line.destination_id.code or ''))
55+ continue
56 return res
57
58 def _get_cc_changed(self, cr, uid, ids, field_name, arg, context=None):
59
60=== modified file 'bin/addons/analytic_distribution/account.py'
61--- bin/addons/analytic_distribution/account.py 2018-09-18 12:25:20 +0000
62+++ bin/addons/analytic_distribution/account.py 2019-05-06 14:14:05 +0000
63@@ -120,80 +120,9 @@
64 _columns = {
65 'account_id': fields.many2one('account.account', "G/L Account"),
66 'funding_pool_id': fields.many2one('account.analytic.account', 'Funding Pool'),
67+ 'destination_id': fields.many2one('account.analytic.account', 'Destination'),
68 }
69
70- def fields_get(self, cr, uid, fields=None, context=None, with_uom_rounding=False):
71- fields = super(account_destination_summary, self).fields_get(cr, uid, fields, context)
72- dest_obj = self.pool.get('account.analytic.account')
73- destination_ids = dest_obj.search(cr, uid, [('type', '!=', 'view'), ('category', '=', 'DEST'), ('parent_id', '!=', False)])
74- for dest in dest_obj.read(cr, uid, destination_ids, ['name']):
75- fields['dest_%s'%(dest['id'])] = {'type': 'boolean', 'string': dest['name']}
76- return fields
77-
78- def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
79- view = super(account_destination_summary, self).fields_view_get(cr, uid, view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
80- if view_type == 'tree':
81- fields_to_add = []
82- form = etree.fromstring(view['arch'])
83- tree = form.xpath('//tree')
84- for field in view.get('fields', {}):
85- if field.startswith('dest_'):
86- fields_to_add.append(int(field.split('_')[1]))
87-
88- if fields_to_add:
89- for dest_order in self.pool.get('account.analytic.account').search(cr, uid, [('id', 'in', fields_to_add)], order='name'):
90- new_field = etree.Element('field', attrib={'name': 'dest_%d'%dest_order})
91- tree[0].append(new_field)
92- view['arch'] = etree.tostring(form)
93- return view
94-
95- def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'):
96- first = False
97- if isinstance(ids, (int, long)):
98- ids = [ids]
99- first = True
100- if fields_to_read is None:
101- fields_to_read = []
102- ret = super(account_destination_summary, self).read(cr, uid, ids, fields_to_read, context, load)
103- f_to_read = []
104- for field in fields_to_read:
105- if field.startswith('dest_'):
106- f_to_read.append(field)
107-
108- if f_to_read:
109- cr.execute('''
110- SELECT
111- sum.id,
112- l.destination_id
113- FROM
114- account_destination_link l,
115- account_destination_summary sum,
116- funding_pool_associated_destinations d
117- WHERE
118- l.disabled = 'f' and
119- d.tuple_id = l.id and
120- sum.account_id = l.account_id and
121- sum.funding_pool_id = d.funding_pool_id and
122- sum.id in %s
123- ''',(tuple(ids),)
124- )
125- tmp_result = {}
126- for x in cr.fetchall():
127- tmp_result.setdefault(x[0], []).append(x[1])
128-
129- for x in ret:
130- for dest in tmp_result.get(x['id'], []):
131- x['dest_%s'%(dest,)] = True
132- for false_value in f_to_read:
133- if false_value not in x:
134- x[false_value] = False
135-
136- if first:
137- return ret[0]
138- return ret
139-
140-
141-
142 def init(self, cr):
143 # test if id exists in funding_pool_associated_destinations or create it
144 cr.execute("SELECT attr.attname FROM pg_attribute attr, pg_class class WHERE attr.attrelid = class.oid AND class.relname = 'funding_pool_associated_destinations' AND attr.attname='id'")
145@@ -206,7 +135,8 @@
146 SELECT
147 min(d.id) AS id,
148 l.account_id AS account_id,
149- d.funding_pool_id AS funding_pool_id
150+ d.funding_pool_id AS funding_pool_id,
151+ l.destination_id AS destination_id
152 FROM
153 account_destination_link l,
154 funding_pool_associated_destinations d
155@@ -214,10 +144,10 @@
156 d.tuple_id = l.id and
157 l.disabled = 'f'
158 GROUP BY
159- l.account_id,d.funding_pool_id
160+ l.account_id, d.funding_pool_id, l.destination_id
161 )
162 """)
163- _order = 'account_id'
164+ _order = 'funding_pool_id, account_id, destination_id'
165 account_destination_summary()
166
167 class account_account(osv.osv):
168
169=== modified file 'bin/addons/analytic_distribution/analytic_account_view.xml'
170--- bin/addons/analytic_distribution/analytic_account_view.xml 2018-04-03 13:02:04 +0000
171+++ bin/addons/analytic_distribution/analytic_account_view.xml 2019-05-06 14:14:05 +0000
172@@ -97,9 +97,6 @@
173 <separator/>
174 <field name="tuple_destination_account_ids" nolabel="1" context="{'dest_in_use':tuple_destination_account_ids}"/>
175 </page>
176- <page string="Destinations by accounts" attrs="{'invisible': [('category', '!=', 'FUNDING')]}">
177- <field name="tuple_destination_summary" nolabel="1" readonly="1"/>
178- </page>
179 <page string="Expense accounts" attrs="{'invisible': [('category', '!=', 'DEST')]}">
180 <field name="destination_ids" nolabel="1" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]" context="{'destination_id': record_id}">
181 <tree string="Expenses accounts list" colors="red:inactivated_for_dest">
182@@ -111,6 +108,17 @@
183 </tree>
184 </field>
185 </page>
186+ <page string="Cost Centers" attrs="{'invisible': [('category', '!=', 'DEST')]}">
187+ <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_ids)"/>
188+ <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/>
189+ <separator/>
190+ <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_dest_cc_ids(dest_cc_ids)">
191+ <tree string="Cost Centers">
192+ <field name="code"/>
193+ <field name="name"/>
194+ </tree>
195+ </field>
196+ </page>
197 </page>
198 </data>
199 </field>
200@@ -121,8 +129,10 @@
201 <field name="model">account.destination.summary</field>
202 <field name="type">tree</field>
203 <field name="arch" type="xml">
204- <tree string="Destinations by accounts">
205+ <tree string="Destinations by accounts" hide_new_button="1" hide_edit_button="1" hide_delete_button="1">
206+ <field name="funding_pool_id"/>
207 <field name="account_id" />
208+ <field name="destination_id"/>
209 </tree>
210 </field>
211 </record>
212@@ -275,6 +285,67 @@
213 <field name="context">{'search_default_active': 1, 'filter_inactive_accounts': 1, 'from_web': True, 'display_disabled': 1}</field>
214 </record>
215
216+ <!-- Destinations by accounts Search view -->
217+ <record id="view_account_destination_summary_search" model="ir.ui.view">
218+ <field name="name">account.destination.summary.search</field>
219+ <field name="model">account.destination.summary</field>
220+ <field name="type">search</field>
221+ <field name="priority" eval="16"/>
222+ <field name="arch" type="xml">
223+ <search>
224+ <group>
225+ <field name="funding_pool_id" domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING')]"/>
226+ <field name="account_id" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]"/>
227+ <field name="destination_id" domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]"/>
228+ </group>
229+ <newline/>
230+ <group expand="1" string="Group By...">
231+ <filter string="Funding Pool" name="group_funding_pool_id"
232+ icon="terp-folder-blue" domain="[]" context="{'group_by':'funding_pool_id'}"/>
233+ <separator orientation="vertical"/>
234+ <filter string="G/L account" name="group_account_id"
235+ icon="terp-folder-orange" domain="[]" context="{'group_by':'account_id'}"/>
236+ <separator orientation="vertical"/>
237+ <filter string="Destination" name="group_destination_id"
238+ icon="terp-folder-violet" domain="[]" context="{'group_by':'destination_id'}"/>
239+ </group>
240+ </search>
241+ </field>
242+ </record>
243+
244+ <!-- Destinations by accounts Form view -->
245+ <record id="view_account_destination_summary_form" model="ir.ui.view">
246+ <field name="name">account.destination.summary.form</field>
247+ <field name="model">account.destination.summary</field>
248+ <field name="type">form</field>
249+ <field name="priority" eval="16"/>
250+ <field name="arch" type="xml">
251+ <!-- this form must be read-only: acc/dest links must be created in the Destination tab of the FP -->
252+ <form noteditable="1" hide_delete_button="1" hide_new_button="1">
253+ <field name="funding_pool_id"/>
254+ <newline/>
255+ <field name="account_id"/>
256+ <field name="destination_id"/>
257+ </form>
258+ </field>
259+ </record>
260+
261+ <!-- Destinations by accounts Menu entry -->
262+ <record id="action_analytic_acc_dest_summary" model="ir.actions.server">
263+ <field name="name">Destinations by accounts</field>
264+ <field name="model_id" ref="model_account_analytic_account"/>
265+ <field name="state">code</field>
266+ <field name="code">action = obj.get_destinations_by_accounts(context=context)</field>
267+ </record>
268+ <record id="ir_destinations_by_accounts_search" model="ir.values">
269+ <field name="key2">client_action_relate</field>
270+ <field name="model">account.analytic.account</field>
271+ <field name="name">destinations_by_accounts</field>
272+ <field eval="'ir.actions.server,%d'%action_analytic_acc_dest_summary" name="value"/>
273+ <field eval="True" name="object"/>
274+ <field name="sequence" eval="110"/>
275+ </record>
276+
277 <menuitem action="action_account_analytic_account_form"
278 id="account.account_analytic_def_account"
279 parent="account.menu_analytic_accounting"
280
281=== modified file 'bin/addons/analytic_distribution/analytic_distribution.py'
282--- bin/addons/analytic_distribution/analytic_distribution.py 2019-03-28 15:40:47 +0000
283+++ bin/addons/analytic_distribution/analytic_distribution.py 2019-05-06 14:14:05 +0000
284@@ -26,6 +26,22 @@
285 _name = 'analytic.distribution'
286 _inherit = 'analytic.distribution'
287
288+ def check_dest_cc_compatibility(self, cr, uid, destination_id, cost_center_id, context=None):
289+ """
290+ Checks the compatibility between the Destination and the Cost Center (cf. CC tab in the Destination form).
291+ Returns False if they aren't compatible.
292+ """
293+ if context is None:
294+ context = {}
295+ analytic_acc_obj = self.pool.get('account.analytic.account')
296+ if destination_id and cost_center_id:
297+ dest = analytic_acc_obj.browse(cr, uid, destination_id, fields_to_fetch=['category', 'allow_all_cc', 'dest_cc_ids'], context=context)
298+ cc = analytic_acc_obj.browse(cr, uid, cost_center_id, fields_to_fetch=['category'], context=context)
299+ if dest and cc and dest.category == 'DEST' and cc.category == 'OC' and not dest.allow_all_cc and \
300+ cc.id not in [c.id for c in dest.dest_cc_ids]:
301+ return False
302+ return True
303+
304 def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None):
305 """
306 Return distribution state
307@@ -51,17 +67,23 @@
308 except ValueError:
309 fp_id = 0
310 account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids'])
311- # Check Cost Center lines with destination/account link
312+ # Check Cost Center lines regarding destination/account and destination/CC links
313 for cc_line in distrib.cost_center_lines:
314 if cc_line.destination_id.id not in account.get('destination_ids', []):
315 return 'invalid'
316+ if not self.check_dest_cc_compatibility(cr, uid, cc_line.destination_id.id,
317+ cc_line.analytic_id and cc_line.analytic_id.id or False, context=context):
318+ return 'invalid'
319 # Check Funding pool lines regarding:
320 # - destination / account
321+ # - destination / cost center
322 # - If analytic account is MSF Private funds
323 # - Cost center and funding pool compatibility
324 for fp_line in distrib.funding_pool_lines:
325 if fp_line.destination_id.id not in account.get('destination_ids', []):
326 return 'invalid'
327+ if not self.check_dest_cc_compatibility(cr, uid, fp_line.destination_id.id, fp_line.cost_center_id.id, context=context):
328+ return 'invalid'
329 # If fp_line is MSF Private Fund, all is ok
330 if fp_line.analytic_id.id == fp_id:
331 continue
332@@ -96,6 +118,9 @@
333 # Check that destination is compatible with account
334 if destination_id not in [x.id for x in account.destination_ids]:
335 return 'invalid', _('Destination not compatible with account')
336+ # Check that Destination and Cost Center are compatible
337+ if not self.check_dest_cc_compatibility(cr, uid, destination_id, cost_center_id, context=context):
338+ return 'invalid', _('Cost Center not compatible with destination')
339 if not is_private_fund:
340 # Check that cost center is compatible with FP (except if FP is MSF Private Fund)
341 if cost_center_id not in [x.id for x in fp.cost_center_ids]:
342
343=== modified file 'bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml'
344--- bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2018-06-22 09:57:41 +0000
345+++ bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2019-05-06 14:14:05 +0000
346@@ -10,8 +10,10 @@
347 <field name="arch" type='xml'>
348 <tree string="" editable="top">
349 <field name="is_percentage_amount_touched" invisible="1" />
350+ <field name="analytic_id" domain="[('type', '!=', 'view'), ('category', '=', 'OC'), ('state', '=', 'open')]"
351+ string="Cost Center"
352+ context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
353 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
354- <field name="analytic_id" domain="[('type', '!=', 'view'), ('category', '=', 'OC'), ('state', '=', 'open')]" string="Cost Center" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
355 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
356 <field name="amount" sum="Total Amount"/>
357 </tree>
358@@ -25,9 +27,13 @@
359 <field name="arch" type='xml'>
360 <tree string="" editable="top">
361 <field name="is_percentage_amount_touched" invisible="1" />
362- <field name="destination_id" on_change="onchange_destination(destination_id, analytic_id, parent.account_id)" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
363- <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, analytic_id)" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
364- <field name="analytic_id" domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]" string="Funding Pool" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/>
365+ <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, analytic_id)"
366+ context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
367+ <field name="destination_id" on_change="onchange_destination(destination_id, analytic_id, parent.account_id)"
368+ context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
369+ <field name="analytic_id"
370+ domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]"
371+ string="Funding Pool" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/>
372 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
373 <field name="amount" sum="Total Amount"/>
374 </tree>
375
376=== modified file 'bin/addons/analytic_distribution/analytic_line.py'
377--- bin/addons/analytic_distribution/analytic_line.py 2018-08-21 13:08:14 +0000
378+++ bin/addons/analytic_distribution/analytic_line.py 2019-05-06 14:14:05 +0000
379@@ -401,6 +401,7 @@
380 if isinstance(ids, (int, long)):
381 ids = [ids]
382 # Prepare some value
383+ ad_obj = self.pool.get('analytic.distribution')
384 account = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['category', 'date_start', 'date'], context=context)
385 account_type = account and account.get('category', False) or False
386 res = []
387@@ -442,12 +443,13 @@
388 check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id])
389 if check_accounts and aline.account_id.id in check_accounts:
390 continue
391-
392- if aline.account_id and aline.account_id.id == msf_private_fund:
393- res.append(aline.id)
394- elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids:
395- if account_id in [x and x.id for x in aline.account_id.cost_center_ids] or aline.account_id.id == msf_private_fund:
396+ if ad_obj.check_dest_cc_compatibility(cr, uid, aline.destination_id and aline.destination_id.id or False,
397+ account_id, context=context):
398+ if aline.account_id and aline.account_id.id == msf_private_fund:
399 res.append(aline.id)
400+ elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids:
401+ if account_id in [x and x.id for x in aline.account_id.cost_center_ids] or aline.account_id.id == msf_private_fund:
402+ res.append(aline.id)
403 elif account_type == 'FUNDING':
404 fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['cost_center_ids', 'tuple_destination_account_ids'], context=context)
405 cc_ids = fp and fp.get('cost_center_ids', []) or []
406@@ -473,7 +475,8 @@
407 res.append(aline.id)
408 elif account_type == "DEST":
409 for aline in self.browse(cr, uid, ids, context=context):
410- if aline.general_account_id and account_id in [x.id for x in aline.general_account_id.destination_ids]:
411+ if ad_obj.check_dest_cc_compatibility(cr, uid, account_id, aline.cost_center_id and aline.cost_center_id.id or False, context=context) and \
412+ aline.general_account_id and account_id in [x.id for x in aline.general_account_id.destination_ids]:
413 res.append(aline.id)
414 else:
415 # Case of FREE1 and FREE2 lines
416@@ -507,6 +510,7 @@
417 new_dest_id, new_dest_br,
418 new_cc_id, new_cc_br,
419 new_fp_id, new_fp_br):
420+ ad_obj = self.pool.get('analytic.distribution')
421 if not general_account_br.is_analytic_addicted:
422 res.append((id, entry_sequence, ''))
423 return False
424@@ -518,6 +522,12 @@
425 res.append((id, entry_sequence, _('DEST')))
426 return False
427
428+ # check cost center with destination
429+ if new_dest_id and new_cc_id:
430+ if not ad_obj.check_dest_cc_compatibility(cr, uid, new_dest_id, new_cc_id, context=context):
431+ res.append((id, entry_sequence, _('CC/DEST')))
432+ return False
433+
434 # check funding pool (expect for MSF Private Fund)
435 if not new_fp_id == msf_pf_id: # all OK for MSF Private Fund
436 # - cost center and funding pool compatibility
437
438=== modified file 'bin/addons/analytic_distribution/report/funding_pool.py'
439--- bin/addons/analytic_distribution/report/funding_pool.py 2014-10-29 16:27:26 +0000
440+++ bin/addons/analytic_distribution/report/funding_pool.py 2019-05-06 14:14:05 +0000
441@@ -21,7 +21,6 @@
442
443 from report import report_sxw
444 import locale
445-import pooler
446 import time
447
448 class funding(report_sxw.rml_parse):
449@@ -29,37 +28,11 @@
450 super(funding, self).__init__(cr, uid, name, context=context)
451 self.localcontext.update({
452 'locale': locale,
453- 'getDest': self.getDestinations,
454- 'getBoolDest': self.getBoolDest,
455 'today': self.today,
456 })
457
458 def today(self):
459 return time.strftime('%Y-%m-%d',time.localtime())
460
461- def getDestinations(self):
462- """
463- Fetch destination analytic account:
464- * ID
465- * code
466- Then sort by code
467- """
468- res = [('Code', False), ('Name', False)] # We need the 2 first column header name
469- pool = pooler.get_pool(self.cr.dbname)
470- destination_ids = pool.get('account.analytic.account').search(self.cr, self.uid, [('category', '=', 'DEST'), ('type', '!=', 'view')], order='id')
471- data = pool.get('account.analytic.account').read(self.cr, self.uid, destination_ids, ['code'])
472- res += [(x.get('code'), x.get('id')) for x in data]
473- return res
474-
475- def getBoolDest(self, line, o):
476- pool = pooler.get_pool(self.cr.dbname)
477- fields = []
478- for field in pool.get('account.destination.summary').fields_get(self.cr, self.uid, ['account_id']):
479- fields.append(field)
480- r = pool.get('account.destination.summary').read(self.cr, self.uid, line.id, fields)
481- if r[o]:
482- return 'x'
483- return ''
484-
485 report_sxw.report_sxw('report.funding.pool', 'account.analytic.account', 'addons/analytic_distribution/report/funding_pool.rml', parser=funding)
486 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
487
488=== modified file 'bin/addons/analytic_distribution/report/funding_pool.rml'
489--- bin/addons/analytic_distribution/report/funding_pool.rml 2017-08-28 12:25:59 +0000
490+++ bin/addons/analytic_distribution/report/funding_pool.rml 2019-05-06 14:14:05 +0000
491@@ -62,13 +62,7 @@
492 <blockTableStyle id="Table4">
493 <blockAlignment value="LEFT"/>
494 <blockValign value="TOP"/>
495- <lineStyle kind="LINEBELOW" colorName="#000000" start="0,0" stop="0,0"/>
496- <lineStyle kind="LINEBELOW" colorName="#000000" start="1,0" stop="1,0"/>
497- <lineStyle kind="LINEBELOW" colorName="#000000" start="2,0" stop="2,0"/>
498- <lineStyle kind="LINEBELOW" colorName="#000000" start="3,0" stop="3,0"/>
499- <lineStyle kind="LINEBELOW" colorName="#000000" start="4,0" stop="4,0"/>
500- <lineStyle kind="LINEBELOW" colorName="#000000" start="5,0" stop="5,0"/>
501- <lineStyle kind="LINEBELOW" colorName="#000000" start="6,0" stop="6,0"/>
502+ <lineStyle kind="LINEBELOW" colorName="#000000" start="0,0" stop="-1,0"/>
503 </blockTableStyle>
504 <blockTableStyle id="Table5">
505 <blockAlignment value="LEFT"/>
506@@ -202,22 +196,22 @@
507 </para>
508 <para style="P6">Cost centers:</para>
509
510- <blockTable colWidths="260.0,258.0" style="Table11" repeatRows="1">
511+ <blockTable colWidths="260.0,260.0" style="Table11" repeatRows="1">
512 <tr>
513 <td>
514- <para style="P2" alignment="LEFT">Cost center name</para>
515+ <para style="P8">Cost center code</para>
516 </td>
517 <td>
518- <para style="P2" alignment="LEFT">Cost center code</para>
519+ <para style="P8">Cost center name</para>
520 </td>
521 </tr>
522 <tr>
523 <para style="P17">[[repeatIn(o.cost_center_ids,'line')]]</para>
524 <td>
525- <para style="P3" alignment="LEFT">[[ line.name ]]</para>
526+ <para style="P7">[[ line.code or '' ]]</para>
527 </td>
528 <td>
529- <para style="P3" alignment="LEFT">[[ line.code ]]</para>
530+ <para style="P7">[[ line.name or '' ]]</para>
531 </td>
532 </tr>
533 </blockTable>
534@@ -228,31 +222,37 @@
535 <para style="P8">
536 <font color="white"> </font>
537 </para>
538- <para style="P6">Accounts:</para>
539+ <para style="P6">Account/Destination:</para>
540
541- <!-- Seems that its not possible to change colWidths regarding a condition.
542- Cf. https://www.odoo.com/forum/help-1/question/reportlab-conditional-table-header-15143
543- Previous colWidths: colWidths="40.0,260.0,55.0,55.0,55.0,55.0"
544- -->
545- <blockTable repeatRows="1" style="Table4">
546+ <blockTable repeatRows="1" style="Table4" colWidths="95.0,165.0,95.0,165.0">
547 <tr>
548 <td>
549- [[repeatIn(getDest(),'dest_data','td')]]
550- <para style="P24" alignment="CENTER">[[ dest_data[0] ]]</para>
551+ <para style="P8">Account code</para>
552+ </td>
553+ <td>
554+ <para style="P8">Account name</para>
555+ </td>
556+ <td>
557+ <para style="P8">Destination code</para>
558+ </td>
559+ <td>
560+ <para style="P8">Destination name</para>
561 </td>
562 </tr>
563
564 <tr>
565 [[repeatIn(o.tuple_destination_summary,'line')]]
566 <td>
567- <para style="P16" alignment="LEFT">[[ line.account_id and line.account_id.code ]]</para>
568- </td>
569- <td>
570- <para style="P11" alignment="LEFT">[[ line.account_id and line.account_id.name ]]</para>
571- </td>
572- <td>
573- [[repeatIn(getDest()[2:],'dest_data','td')]]
574- <para style="P28" alignment="CENTER">[[ getBoolDest(line,'dest_'+ str(dest_data[1])) ]]</para>
575+ <para style="P7">[[ line.account_id and line.account_id.code or '' ]]</para>
576+ </td>
577+ <td>
578+ <para style="P7">[[ line.account_id and line.account_id.name or '' ]]</para>
579+ </td>
580+ <td>
581+ <para style="P7">[[ line.destination_id and line.destination_id.code or '' ]]</para>
582+ </td>
583+ <td>
584+ <para style="P7">[[ line.destination_id and line.destination_id.name or '' ]]</para>
585 </td>
586 </tr>
587 </blockTable>
588
589=== modified file 'bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py'
590--- bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2018-09-28 16:00:25 +0000
591+++ bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2019-05-06 14:14:05 +0000
592@@ -159,6 +159,18 @@
593 percentage = abs((amount / total_amount) * 100)
594 return {'value': {'percentage': percentage, 'is_percentage_amount_touched': True}}
595
596+ def _dest_compatible_with_cc_domain_part(self, tree):
597+ """
598+ Returns the domain condition to restrict the destination regarding the cost_center_id (for finance views),
599+ or if this field doesn't exist in the view, to the analytic_id (Cost Center in Supply views)
600+ """
601+ dom_part = ""
602+ if tree.xpath('/tree/field[@name="cost_center_id"]'):
603+ dom_part = "('dest_compatible_with_cc_ids', '=', cost_center_id)"
604+ elif tree.xpath('/tree/field[@name="analytic_id"]'):
605+ dom_part = "('dest_compatible_with_cc_ids', '=', analytic_id)"
606+ return dom_part
607+
608 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
609 """
610 Rewrite view in order:
611@@ -195,9 +207,14 @@
612 or (context.get('direct_invoice_id', False) and isinstance(context.get('direct_invoice_id'), int)) \
613 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \
614 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):
615- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST')]")
616+ domain_part = self._dest_compatible_with_cc_domain_part(tree)
617+ domain = "[('type', '!=', 'view'), ('category', '=', 'DEST') %s]" % (domain_part and ', %s' % domain_part or '')
618+ field.set('domain', domain)
619 else:
620- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', parent.account_id)]")
621+ domain_part = self._dest_compatible_with_cc_domain_part(tree)
622+ domain = "[('type', '!=', 'view'), ('category', '=', 'DEST'), " \
623+ "('destination_ids', '=', parent.account_id) %s]" % (domain_part and ', %s' % domain_part or '')
624+ field.set('domain', domain)
625 ## FUNDING POOL
626 if line_type == 'analytic.distribution.wizard.fp.lines':
627 # Change OC field
628@@ -235,9 +252,14 @@
629 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \
630 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):
631
632- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST')]")
633+ domain_part = self._dest_compatible_with_cc_domain_part(tree)
634+ domain = "[('type', '!=', 'view'), ('category', '=', 'DEST') %s]" % (domain_part and ', %s' % domain_part or '')
635+ field.set('domain', domain)
636 else:
637- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', parent.account_id)]")
638+ domain_part = self._dest_compatible_with_cc_domain_part(tree)
639+ domain = "[('type', '!=', 'view'), ('category', '=', 'DEST'), " \
640+ "('destination_ids', '=', parent.account_id) %s]" % (domain_part and ', %s' % domain_part or '')
641+ field.set('domain', domain)
642
643 ## FREE 1
644 if line_type == 'analytic.distribution.wizard.f1.lines':
645
646=== modified file 'bin/addons/analytic_override/analytic_account.py'
647--- bin/addons/analytic_override/analytic_account.py 2019-03-20 10:39:41 +0000
648+++ bin/addons/analytic_override/analytic_account.py 2019-05-06 14:14:05 +0000
649@@ -211,6 +211,28 @@
650 account_ids += tmp_ids
651 return account_ids
652
653+ def _search_dest_compatible_with_cc_ids(self, cr, uid, obj, name, args, context=None):
654+ """
655+ Returns a domain with all destinations compatible with the selected Cost Center
656+ Ex: to get the dest. compatible with the CC 2, use the dom [('dest_compatible_with_cc_ids', '=', 2)]
657+ """
658+ dom = []
659+ if context is None:
660+ context = {}
661+ for arg in args:
662+ if arg[0] == 'dest_compatible_with_cc_ids':
663+ operator = arg[1]
664+ cc = arg[2]
665+ if operator != '=' or not isinstance(cc, (int, long)):
666+ raise osv.except_osv(_('Error'), _('Filter not implemented on Destinations.'))
667+ all_dest_ids = self.search(cr, uid, [('category', '=', 'DEST')], context=context)
668+ compatible_dest_ids = []
669+ for dest in self.browse(cr, uid, all_dest_ids, fields_to_fetch=['allow_all_cc', 'dest_cc_ids'], context=context):
670+ if dest.allow_all_cc or (cc and cc in [c.id for c in dest.dest_cc_ids]):
671+ compatible_dest_ids.append(dest.id)
672+ dom.append(('id', 'in', compatible_dest_ids))
673+ return dom
674+
675 _columns = {
676 'name': fields.char('Name', size=128, required=True, translate=1),
677 'code': fields.char('Code', size=24),
678@@ -228,11 +250,20 @@
679 'filter_active': fields.function(_get_active, fnct_search=_search_filter_active, type="boolean", method=True, store=False, string="Show only active analytic accounts",),
680 'intermission_restricted': fields.function(_get_fake, type="boolean", method=True, store=False, string="Domain to restrict intermission cc"),
681 'balance': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Balance', digits_compute=dp.get_precision('Account'), multi='debit_credit_bal_qtty'),
682+ 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel',
683+ 'destination_id', 'cost_center_id', string='Cost Centers',
684+ domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"),
685+ 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"),
686+ 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,
687+ string='Destinations compatible with the Cost Center',
688+ type='many2many', relation='account.analytic.account',
689+ fnct_search=_search_dest_compatible_with_cc_ids),
690 }
691
692 _defaults ={
693 'date_start': lambda *a: (datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'),
694 'for_fx_gain_loss': lambda *a: False,
695+ 'allow_all_cc': lambda *a: False,
696 }
697
698 def _check_code_unicity(self, cr, uid, ids, context=None):
699@@ -304,6 +335,30 @@
700 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]
701 return res
702
703+ def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, dest_cc_ids, context=None):
704+ """
705+ If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected,
706+ informs him that he has to remove the CC first
707+ """
708+ res = {}
709+ if allow_all_cc and dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
710+ warning = {
711+ 'title': _('Warning!'),
712+ 'message': _('Please remove the Cost Centers linked to the Destination before ticking this box.')
713+ }
714+ res['warning'] = warning
715+ res['value'] = {'allow_all_cc': False, }
716+ return res
717+
718+ def on_change_dest_cc_ids(self, cr, uid, ids, dest_cc_ids, context=None):
719+ """
720+ If at least a CC is selected, unticks the box "Allow all Cost Centers"
721+ """
722+ res = {}
723+ if dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
724+ res['value'] = {'allow_all_cc': False, }
725+ return res
726+
727 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
728 if not context:
729 context = {}
730@@ -377,6 +432,8 @@
731 if 'category' in vals:
732 if vals['category'] != 'DEST':
733 vals['destination_ids'] = [(6, 0, [])]
734+ vals['dest_cc_ids'] = [(6, 0, [])]
735+ vals['allow_all_cc'] = False # default value
736 if vals['category'] != 'FUNDING':
737 vals['tuple_destination_account_ids'] = [(6, 0, [])]
738 vals['cost_center_ids'] = [(6, 0, [])]
739@@ -408,6 +465,7 @@
740 default['child_ids'] = [] # do not copy the child_ids
741 default['tuple_destination_summary'] = []
742 default['line_ids'] = []
743+ default['dest_cc_ids'] = []
744 return super(analytic_account, self).copy(cr, uid, a_id, default, context=context)
745
746 def _check_name_unicity(self, cr, uid, ids, context=None):
747@@ -568,9 +626,41 @@
748 self.write(cr, uid, ids, {'cost_center_ids':[(6, 0, [])]}, context=context)
749 return True
750
751+ def button_dest_cc_clear(self, cr, uid, ids, context=None):
752+ """
753+ Removes all Cost Centers selected in the Destination view
754+ """
755+ self.write(cr, uid, ids, {'dest_cc_ids': [(6, 0, [])]}, context=context)
756+ return True
757+
758 def button_dest_clear(self, cr, uid, ids, context=None):
759 self.write(cr, uid, ids, {'tuple_destination_account_ids':[(6, 0, [])]}, context=context)
760 return True
761
762+ def get_destinations_by_accounts(self, cr, uid, ids, context=None):
763+ """
764+ Returns a view with the Destinations by accounts (for the FP selected if any, otherwise for all the FP)
765+ """
766+ if context is None:
767+ context = {}
768+ ir_model_obj = self.pool.get('ir.model.data')
769+ active_ids = context.get('active_ids', [])
770+ if active_ids:
771+ analytic_acc_category = self.browse(cr, uid, active_ids[0], fields_to_fetch=['category'], context=context).category or ''
772+ if analytic_acc_category == 'FUNDING':
773+ context.update({'search_default_funding_pool_id': active_ids[0]})
774+ search_view_id = ir_model_obj.get_object_reference(cr, uid, 'analytic_distribution', 'view_account_destination_summary_search')
775+ search_view_id = search_view_id and search_view_id[1] or False
776+ return {
777+ 'name': _('Destinations by accounts'),
778+ 'type': 'ir.actions.act_window',
779+ 'res_model': 'account.destination.summary',
780+ 'view_type': 'form',
781+ 'view_mode': 'tree,form',
782+ 'search_view_id': [search_view_id],
783+ 'context': context,
784+ 'target': 'current',
785+ }
786+
787 analytic_account()
788 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
789
790=== modified file 'bin/addons/msf_doc_import/account.py'
791--- bin/addons/msf_doc_import/account.py 2019-01-03 09:17:52 +0000
792+++ bin/addons/msf_doc_import/account.py 2019-05-06 14:14:05 +0000
793@@ -194,6 +194,7 @@
794 self.pool.get('msf.doc.import.accounting.lines').unlink(cr, uid, old_lines_ids)
795
796 # Check wizard data
797+ ad_obj = self.pool.get('analytic.distribution')
798 period_obj = self.pool.get('account.period')
799 period_ctx = context.copy()
800 period_ctx['extend_december'] = True
801@@ -485,6 +486,10 @@
802 errors.append(_('Line %s. The destination %s is not compatible with the account %s.') %
803 (current_line_num, line[cols['Destination']], line[cols['G/L Account']]))
804 continue
805+ if not ad_obj.check_dest_cc_compatibility(cr, uid, r_destination, r_cc, context=context):
806+ errors.append(_('Line %s. The Cost Center %s is not compatible with the Destination %s.') %
807+ (current_line_num, line[cols['Cost Centre']], line[cols['Destination']]))
808+ continue
809 # if the Fund. Pool used is NOT "PF" check the compatibility with the (account, dest) and the CC
810 if r_fp != msf_fp_id:
811 fp_fields = ['tuple_destination_account_ids', 'cost_center_ids']
812
813=== modified file 'bin/addons/msf_doc_import/msf_import_export.py'
814--- bin/addons/msf_doc_import/msf_import_export.py 2018-11-16 10:06:13 +0000
815+++ bin/addons/msf_doc_import/msf_import_export.py 2019-05-06 14:14:05 +0000
816@@ -633,6 +633,7 @@
817 model = MODEL_DICT[import_brw.model_list_selection]['model']
818 impobj = self.pool.get(model)
819 acc_obj = self.pool.get('account.account')
820+ acc_analytic_obj = self.pool.get('account.analytic.account')
821 acc_dest_obj = self.pool.get('account.destination.link')
822
823 import_data_obj = self.pool.get('import_data')
824@@ -930,11 +931,20 @@
825 if len(ids_to_update) > 1:
826 raise Exception('%d records found for rule=%s, model=%s' % (len(ids_to_update), data.get('name'), data.get('model_id')))
827
828- # Analytic Accounts
829- if import_brw.model_list_selection == 'analytic_accounts':
830+ # Funding Pools
831+ if import_brw.model_list_selection == 'funding_pools':
832 context['from_import_menu'] = True
833+ data['category'] = 'FUNDING'
834+ # Parent Analytic Account
835+ if data.get('parent_id'):
836+ parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
837+ fields_to_fetch=['type', 'category'], context=context)
838+ parent_type = parent_id.type or ''
839+ parent_category = parent_id.category or ''
840+ if parent_type != 'view' or parent_category != 'FUNDING':
841+ raise Exception(_('The Parent Analytic Account must be a View type Funding Pool.'))
842 # Cost Centers
843- if data.get('cost_center_ids') and data.get('category', '') == 'FUNDING':
844+ if data.get('cost_center_ids'):
845 cc_list = []
846 for cost_center in data.get('cost_center_ids').split(','):
847 cc = cost_center.strip()
848@@ -949,7 +959,7 @@
849 else:
850 data['cost_center_ids'] = [(6, 0, [])]
851 # Account/Destination
852- if data.get('tuple_destination_account_ids') and data.get('category', '') == 'FUNDING':
853+ if data.get('tuple_destination_account_ids'):
854 dest_acc_list = []
855 for destination_account in data.get('tuple_destination_account_ids').split(','):
856 dest_acc_ids = []
857@@ -969,6 +979,115 @@
858 else:
859 data['tuple_destination_account_ids'] = [(6, 0, [])]
860
861+ # Destinations
862+ if import_brw.model_list_selection == 'destinations':
863+ context['from_import_menu'] = True
864+ data['category'] = 'DEST'
865+ # Parent Analytic Account
866+ if data.get('parent_id'):
867+ parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'], fields_to_fetch=['type', 'category'], context=context)
868+ parent_type = parent_id.type or ''
869+ parent_category = parent_id.category or ''
870+ if parent_type != 'view' or parent_category != 'DEST':
871+ raise Exception(_('The Parent Analytic Account must be a View type Destination.'))
872+ # Type
873+ if data['type'] not in ['normal', 'view']:
874+ raise Exception(_('The Type must be either "Normal" or "View".'))
875+ # Cost Centers
876+ if data.get('dest_cc_ids'):
877+ if data.get('allow_all_cc'):
878+ raise Exception(_("Please either list the Cost Centers to allow, or allow all Cost Centers."))
879+ dest_cc_list = []
880+ for cost_center in data.get('dest_cc_ids').split(','):
881+ cc = cost_center.strip()
882+ cc_dom = [('category', '=', 'OC'), ('type', '=', 'normal'),
883+ '|', ('code', '=', cc), ('name', '=', cc)]
884+ cc_ids = impobj.search(cr, uid, cc_dom, order='id', limit=1, context=context)
885+ if cc_ids:
886+ dest_cc_list.append(cc_ids[0])
887+ else:
888+ raise Exception(_('Cost Center "%s" not found.') % cc)
889+ data['dest_cc_ids'] = [(6, 0, dest_cc_list)]
890+ else:
891+ data['dest_cc_ids'] = [(6, 0, [])]
892+ # Accounts
893+ if data.get('destination_ids'): # "destinations_ids" corresponds to G/L accounts...
894+ acc_list = []
895+ for account in data.get('destination_ids').split(','):
896+ acc = account.strip()
897+ acc_dom = [('type', '!=', 'view'), ('is_analytic_addicted', '=', True), ('code', '=', acc)]
898+ acc_ids = acc_obj.search(cr, uid, acc_dom, order='id', limit=1, context=context)
899+ if acc_ids:
900+ acc_list.append(acc_ids[0])
901+ else:
902+ raise Exception(_("Account code \"%s\" doesn't exist or isn't allowed.") % acc)
903+ data['destination_ids'] = [(6, 0, acc_list)]
904+ else:
905+ data['destination_ids'] = [(6, 0, [])]
906+ # if the code matches with an existing destination: update it
907+ if data.get('code'):
908+ ids_to_update = impobj.search(cr, uid, [('category', '=', 'DEST'), ('code', '=', data['code'])],
909+ limit=1, context=context)
910+ if ids_to_update:
911+ # in case of empty columns on non-required fields, existing values should be deleted
912+ if 'date' not in data:
913+ data['date'] = False
914+ if 'dest_cc_ids' not in data:
915+ data['dest_cc_ids'] = [(6, 0, [])]
916+ if 'allow_all_cc' not in data:
917+ data['allow_all_cc'] = False
918+ if 'destination_ids' not in data:
919+ data['destination_ids'] = [(6, 0, [])]
920+ elif data['destination_ids'][0][2]:
921+ # accounts already linked to the destination:
922+ # - if they don't appear in the new list: will be automatically de-activated
923+ # - if they appear in the list: must be re-activated if they are currently disabled
924+ link_ids = acc_dest_obj.search(cr, uid,
925+ [('account_id', 'in', data['destination_ids'][0][2]),
926+ ('destination_id', '=', ids_to_update[0]),
927+ ('disabled', '=', True)], context=context)
928+ if link_ids:
929+ acc_dest_obj.write(cr, uid, link_ids, {'disabled': False}, context=context)
930+
931+ # Cost Centers
932+ if import_brw.model_list_selection == 'cost_centers':
933+ context['from_import_menu'] = True
934+ data['category'] = 'OC'
935+ # Parent Analytic Account
936+ if data.get('parent_id'):
937+ parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
938+ fields_to_fetch=['type', 'category'], context=context)
939+ parent_type = parent_id.type or ''
940+ parent_category = parent_id.category or ''
941+ if parent_type != 'view' or parent_category != 'OC':
942+ raise Exception(_('The Parent Analytic Account must be a View type Cost Center.'))
943+
944+ # Free 1
945+ if import_brw.model_list_selection == 'free1':
946+ context['from_import_menu'] = True
947+ data['category'] = 'FREE1'
948+ # Parent Analytic Account
949+ if data.get('parent_id'):
950+ parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
951+ fields_to_fetch=['type', 'category'], context=context)
952+ parent_type = parent_id.type or ''
953+ parent_category = parent_id.category or ''
954+ if parent_type != 'view' or parent_category != 'FREE1':
955+ raise Exception(_('The Parent Analytic Account must be a View type Free 1 account.'))
956+
957+ # Free 2
958+ if import_brw.model_list_selection == 'free2':
959+ context['from_import_menu'] = True
960+ data['category'] = 'FREE2'
961+ # Parent Analytic Account
962+ if data.get('parent_id'):
963+ parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
964+ fields_to_fetch=['type', 'category'], context=context)
965+ parent_type = parent_id.type or ''
966+ parent_category = parent_id.category or ''
967+ if parent_type != 'view' or parent_category != 'FREE2':
968+ raise Exception(_('The Parent Analytic Account must be a View type Free 2 account.'))
969+
970 if import_brw.model_list_selection == 'record_rules':
971 if not data.get('groups'):
972 data['groups'] = [(6, 0, [])]
973
974=== modified file 'bin/addons/msf_doc_import/msf_import_export_conf.py'
975--- bin/addons/msf_doc_import/msf_import_export_conf.py 2018-10-03 14:41:22 +0000
976+++ bin/addons/msf_doc_import/msf_import_export_conf.py 2019-05-06 14:14:05 +0000
977@@ -95,8 +95,28 @@
978 'domain_type': 'finance',
979 'model': 'account.journal'
980 },
981- 'analytic_accounts': {
982- 'name': 'Analytic Accounts',
983+ 'funding_pools': {
984+ 'name': 'Funding Pools',
985+ 'domain_type': 'finance',
986+ 'model': 'account.analytic.account'
987+ },
988+ 'destinations': {
989+ 'name': 'Destinations',
990+ 'domain_type': 'finance',
991+ 'model': 'account.analytic.account'
992+ },
993+ 'cost_centers': {
994+ 'name': 'Cost Centers',
995+ 'domain_type': 'finance',
996+ 'model': 'account.analytic.account'
997+ },
998+ 'free1': {
999+ 'name': 'Free 1',
1000+ 'domain_type': 'finance',
1001+ 'model': 'account.analytic.account'
1002+ },
1003+ 'free2': {
1004+ 'name': 'Free 2',
1005 'domain_type': 'finance',
1006 'model': 'account.analytic.account'
1007 },
1008@@ -466,11 +486,10 @@
1009 'analytic_journal_id.name',
1010 ],
1011 },
1012- 'analytic_accounts': {
1013+ 'funding_pools': {
1014 'header_list': [
1015 'name',
1016 'code',
1017- 'category',
1018 'parent_id.code',
1019 'type',
1020 'date_start',
1021@@ -482,7 +501,74 @@
1022 'required_field_list': [
1023 'name',
1024 'code',
1025- 'category',
1026+ 'parent_id.code',
1027+ 'date_start',
1028+ ],
1029+ },
1030+ 'cost_centers': {
1031+ 'header_list': [
1032+ 'name',
1033+ 'code',
1034+ 'parent_id.code',
1035+ 'type',
1036+ 'date_start',
1037+ 'date', # "inactive from"
1038+ ],
1039+ 'required_field_list': [
1040+ 'name',
1041+ 'code',
1042+ 'parent_id.code',
1043+ 'date_start',
1044+ ],
1045+ },
1046+ 'destinations': {
1047+ 'header_list': [
1048+ 'name',
1049+ 'code',
1050+ 'parent_id.code',
1051+ 'type',
1052+ 'date_start',
1053+ 'date', # "inactive from"
1054+ 'dest_cc_ids',
1055+ 'destination_ids',
1056+ 'allow_all_cc',
1057+ ],
1058+ 'required_field_list': [
1059+ 'name',
1060+ 'code',
1061+ 'parent_id.code',
1062+ 'type',
1063+ 'date_start',
1064+ ],
1065+ },
1066+ 'free1': {
1067+ 'header_list': [
1068+ 'name',
1069+ 'code',
1070+ 'parent_id.code',
1071+ 'type',
1072+ 'date_start',
1073+ 'date', # "inactive from"
1074+ ],
1075+ 'required_field_list': [
1076+ 'name',
1077+ 'code',
1078+ 'parent_id.code',
1079+ 'date_start',
1080+ ],
1081+ },
1082+ 'free2': {
1083+ 'header_list': [
1084+ 'name',
1085+ 'code',
1086+ 'parent_id.code',
1087+ 'type',
1088+ 'date_start',
1089+ 'date', # "inactive from"
1090+ ],
1091+ 'required_field_list': [
1092+ 'name',
1093+ 'code',
1094 'parent_id.code',
1095 'date_start',
1096 ],
1097
1098=== modified file 'bin/addons/msf_homere_interface/hr.py'
1099--- bin/addons/msf_homere_interface/hr.py 2019-02-06 09:47:00 +0000
1100+++ bin/addons/msf_homere_interface/hr.py 2019-05-06 14:14:05 +0000
1101@@ -208,6 +208,22 @@
1102 (_check_unicity, "Another employee has the same Identification No.", ['identification_id']),
1103 ]
1104
1105+ def _check_employe_dest_cc_compatibility(self, cr, uid, employee_id, context=None):
1106+ """
1107+ Raises an error in case the employee Destination and Cost Center are not compatible
1108+ """
1109+ if context is None:
1110+ context = {}
1111+ ad_obj = self.pool.get('analytic.distribution')
1112+ employee_fields = ['destination_id', 'cost_center_id', 'name_resource']
1113+ employee = self.browse(cr, uid, employee_id, fields_to_fetch=employee_fields, context=context)
1114+ emp_dest = employee.destination_id
1115+ emp_cc = employee.cost_center_id
1116+ if emp_dest and emp_cc:
1117+ if not ad_obj.check_dest_cc_compatibility(cr, uid, emp_dest.id, emp_cc.id, context=context):
1118+ raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Destination %s.') %
1119+ (employee.name_resource, emp_cc.code or '', emp_dest.code or ''))
1120+
1121 def create(self, cr, uid, vals, context=None):
1122 """
1123 Block creation for local staff if no 'from' in context
1124@@ -228,7 +244,9 @@
1125 # Raise an error if employee is created manually
1126 if (not context.get('from', False) or context.get('from') not in ['yaml', 'import']) and not context.get('sync_update_execution', False) and not allow_edition:
1127 raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.'))
1128- return super(hr_employee, self).create(cr, uid, vals, context)
1129+ employee_id = super(hr_employee, self).create(cr, uid, vals, context)
1130+ self._check_employe_dest_cc_compatibility(cr, uid, employee_id, context=context)
1131+ return employee_id
1132
1133 def write(self, cr, uid, ids, vals, context=None):
1134 """
1135@@ -277,6 +295,7 @@
1136 employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context)
1137 if employee_id:
1138 res.append(employee_id)
1139+ self._check_employe_dest_cc_compatibility(cr, uid, emp.id, context=context)
1140 return res
1141
1142 def unlink(self, cr, uid, ids, context=None):
1143@@ -322,6 +341,11 @@
1144 fields = form.xpath('/' + view_type + '//field[@name="cost_center_id"]')
1145 for field in fields:
1146 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
1147+ # Change DEST field
1148+ dest_fields = form.xpath('/' + view_type + '//field[@name="destination_id"]')
1149+ for dest_field in dest_fields:
1150+ dest_field.set('domain', "[('category', '=', 'DEST'), ('type', '!=', 'view'), "
1151+ "('dest_compatible_with_cc_ids', '=', cost_center_id)]")
1152 # Change FP field
1153 try:
1154 fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
1155
1156=== modified file 'bin/addons/msf_homere_interface/hr_payroll.py'
1157--- bin/addons/msf_homere_interface/hr_payroll.py 2018-10-30 15:33:33 +0000
1158+++ bin/addons/msf_homere_interface/hr_payroll.py 2019-05-06 14:14:05 +0000
1159@@ -42,6 +42,7 @@
1160 ids = [ids]
1161 # Prepare some values
1162 res = {}
1163+ ad_obj = self.pool.get('analytic.distribution')
1164 # Search MSF Private Fund element, because it's valid with all accounts
1165 try:
1166 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
1167@@ -57,6 +58,7 @@
1168 # E/ DEST in list of available DEST in ACCOUNT
1169 # F/ Check posting date with cost center and destination if exists
1170 # G/ Check document date with funding pool
1171+ # H/ Check Cost Center / Destination compatibility
1172 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).
1173 ## CC is mandatory, so always available:
1174 # 1/ no FP, no DEST => Distro = valid
1175@@ -124,6 +126,11 @@
1176 if line.destination_id.id not in [x.id for x in account.destination_ids]:
1177 res[line.id] = 'invalid'
1178 continue
1179+ # H check
1180+ if line.destination_id and line.cost_center_id and \
1181+ not ad_obj.check_dest_cc_compatibility(cr, uid, line.destination_id.id, line.cost_center_id.id, context=context):
1182+ res[line.id] = 'invalid'
1183+ continue
1184 return res
1185
1186 def _get_third_parties(self, cr, uid, ids, field_name=None, arg=None, context=None):
1187
1188=== modified file 'bin/addons/msf_homere_interface/hr_payroll_wizard.xml'
1189--- bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2019-01-21 09:53:32 +0000
1190+++ bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2019-05-06 14:14:05 +0000
1191@@ -13,8 +13,11 @@
1192 <field name="arch" type="xml">
1193 <form string="Analytic Reallocation">
1194 <group colspan="6" col="6">
1195- <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1196- <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1197+ <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, funding_pool_id)"
1198+ context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1199+ <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"
1200+ domain="[('category', '=', 'DEST'), ('type', '!=', 'view'),
1201+ ('dest_compatible_with_cc_ids', '=', cost_center_id)]"/>
1202 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1203 </group>
1204 <newline/>
1205
1206=== modified file 'bin/addons/msf_homere_interface/hr_view.xml'
1207--- bin/addons/msf_homere_interface/hr_view.xml 2018-08-21 19:39:57 +0000
1208+++ bin/addons/msf_homere_interface/hr_view.xml 2019-05-06 14:14:05 +0000
1209@@ -27,8 +27,8 @@
1210 <field name="homere_codeterrain" invisible="1"/>
1211 </group>
1212 <group colspan="4" col="8">
1213- <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1214 <field name="cost_center_id" on_change="onchange_cc(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1215+ <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1216 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1217 <newline />
1218 <field name="free1_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
1219
1220=== modified file 'bin/addons/msf_profile/data/patches.xml'
1221--- bin/addons/msf_profile/data/patches.xml 2019-03-28 13:22:15 +0000
1222+++ bin/addons/msf_profile/data/patches.xml 2019-05-06 14:14:05 +0000
1223@@ -411,5 +411,10 @@
1224 <field name="method">us_5667_remove_contract_workflow</field>
1225 </record>
1226
1227+ <!-- UF13.0 -->
1228+ <record id="us_5771_allow_all_cc_in_default_dest" model="patch.scripts">
1229+ <field name="method">us_5771_allow_all_cc_in_default_dest</field>
1230+ </record>
1231+
1232 </data>
1233 </openerp>
1234
1235=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
1236--- bin/addons/msf_profile/i18n/fr_MF.po 2019-04-08 09:30:07 +0000
1237+++ bin/addons/msf_profile/i18n/fr_MF.po 2019-05-06 14:14:05 +0000
1238@@ -5227,8 +5227,9 @@
1239 msgid "Close period (Mission)"
1240 msgstr "Clôturer la Période (Mission)"
1241
1242-#. module: msf_budget
1243+#. modules: msf_budget, analytic_distribution
1244 #: report:addons/msf_budget/report/report_local_expenses_xls.mako:116
1245+#: report:funding.pool:0
1246 msgid "Account name"
1247 msgstr "Nom du compte"
1248
1249@@ -16477,6 +16478,7 @@
1250 #: view:threshold.value:0
1251 #: view:international.transport.cost.report:0
1252 #: view:local.transport.cost.report:0
1253+#: view:account.destination.summary:0
1254 msgid "Group By..."
1255 msgstr "Grouper Par..."
1256
1257@@ -16779,7 +16781,7 @@
1258 #. module: analytic_distribution
1259 #: view:account.analytic.account:0
1260 msgid "Remove all"
1261-msgstr "Remove all"
1262+msgstr "Tout supprimer"
1263
1264 #. module: vertical_integration
1265 #: code:addons/vertical_integration/report/hq_report_oca.py:176
1266@@ -31372,9 +31374,10 @@
1267 msgid "Type of file"
1268 msgstr "Type de fichier"
1269
1270-#. module: msf_budget
1271+#. modules: msf_budget, analytic_distribution
1272 #: report:addons/msf_budget/report/report_local_expenses_xls.mako:115
1273 #: field:msf.budget.line,account_code:0
1274+#: report:funding.pool:0
1275 msgid "Account code"
1276 msgstr "Code du compte"
1277
1278@@ -38475,6 +38478,7 @@
1279 #: field:hr.payroll.msf,funding_pool_id:0
1280 #: field:account.invoice.line,funding_pool_id:0
1281 #: report:addons/account/report/free_allocation_report.mako:208
1282+#: view:account.destination.summary:0
1283 #, python-format
1284 msgid "Funding Pool"
1285 msgstr "Funding Pool"
1286@@ -47675,10 +47679,11 @@
1287 msgid "America/Sitka"
1288 msgstr "America/Sitka"
1289
1290-#. module: account
1291+#. modules: account, analytic_distribution
1292 #: view:account.analytic.line:0
1293+#: view:account.destination.summary:0
1294 msgid "G/L account"
1295-msgstr "G/L account"
1296+msgstr "Compte Grand Livre"
1297
1298 #. modules: account, register_accounting
1299 #: help:account.invoice,state:0
1300@@ -67403,6 +67408,7 @@
1301 #: view:msf.instance:0
1302 #: report:addons/account/report/free_allocation_report.mako:176
1303 #: field:free.allocation.wizard,cost_center_ids:0
1304+#: field:account.analytic.account,dest_cc_ids:0
1305 msgid "Cost Centers"
1306 msgstr "Centres de Coût"
1307
1308@@ -86607,10 +86613,12 @@
1309 msgid "Total weight:"
1310 msgstr "Total weight:"
1311
1312-#. module: analytic_distribution
1313+#. modules: analytic_distribution, analytic_override
1314 #: view:account.analytic.account:0
1315 #: view:account.destination.summary:0
1316 #: model:ir.model,name:analytic_distribution.model_account_destination_summary
1317+#: model:ir.actions.server,name:analytic_distribution.action_analytic_acc_dest_summary
1318+#: code:addons/analytic_override/analytic_account.py:652
1319 msgid "Destinations by accounts"
1320 msgstr "Destinations par Compte"
1321
1322@@ -91150,8 +91158,8 @@
1323
1324 #. module: analytic_distribution
1325 #: report:funding.pool:0
1326-msgid "Accounts:"
1327-msgstr "Comptes:"
1328+msgid "Account/Destination:"
1329+msgstr "Compte / Destination :"
1330
1331 #. module: msf_outgoing
1332 #: help:return.pack.shipment.processor,address_id:0
1333@@ -96457,6 +96465,8 @@
1334 #: view:sync.client.message_to_send:0
1335 #: report:addons/account/report/free_allocation_report.mako:202
1336 #: report:addons/stock_override/report/report_stock_move_xls.mako:143
1337+#: field:account.destination.summary,destination_id:0
1338+#: view:account.destination.summary:0
1339 #, python-format
1340 msgid "Destination"
1341 msgstr "Destination"
1342@@ -102110,6 +102120,12 @@
1343 msgstr "%s: DEST (%s) incompatible avec le compte (%s)"
1344
1345 #. module: account_hq_entries
1346+#: code:addons/account_hq_entries/hq_entries.py:142
1347+#, python-format
1348+msgid "%s: CC (%s) not compatible with DEST (%s)"
1349+msgstr "%s : CC (%s) non compatible avec la DEST (%s)"
1350+
1351+#. module: account_hq_entries
1352 #: code:addons/account_hq_entries/hq_entries.py:103
1353 #, python-format
1354 msgid "%s: No CC"
1355@@ -105388,3 +105404,113 @@
1356 #, python-format
1357 msgid "Product %s, BN: %s not enough stock to process quantity %s %s (stock level: %s)"
1358 msgstr "Produit %s, Lot: %s, pas assez de stock pour traiter la qantité %s %s (quantité en stock: %s)"
1359+
1360+#. module: analytic_distribution
1361+#: code:addons/analytic_distribution/analytic_distribution.py:123
1362+#, python-format
1363+msgid "Cost Center not compatible with destination"
1364+msgstr "Centre de Coût non compatible avec la destination"
1365+
1366+#. module: analytic_distribution
1367+#: code:addons/analytic_distribution/analytic_line.py:528
1368+#, python-format
1369+msgid "CC/DEST"
1370+msgstr "CC / DEST"
1371+
1372+#. module: analytic_distribution
1373+#: report:funding.pool:0
1374+msgid "Destination code"
1375+msgstr "Code de la destination"
1376+
1377+#. module: analytic_distribution
1378+#: report:funding.pool:0
1379+msgid "Destination name"
1380+msgstr "Nom de la destination"
1381+
1382+#. module: analytic_override
1383+#: code:addons/analytic_override/analytic_account.py:227
1384+#, python-format
1385+msgid "Filter not implemented on Destinations."
1386+msgstr "Filtre non mis en oeuvre sur les Destinations."
1387+
1388+#. module: analytic_override
1389+#: field:account.analytic.account,allow_all_cc:0
1390+msgid "Allow all Cost Centers"
1391+msgstr "Autoriser tous les Centres de Coût"
1392+
1393+#. module: msf_doc_import
1394+#: code:addons/msf_doc_import/msf_import_export.py:999
1395+#, python-format
1396+msgid "Please either list the Cost Centers to allow, or allow all Cost Centers."
1397+msgstr "Veuillez soit lister les Centres de Coût à autoriser, soit autoriser tous les Centres de Coût."
1398+
1399+#. module: analytic_override
1400+#: field:account.analytic.account,dest_compatible_with_cc_ids:0
1401+msgid "Destinations compatible with the Cost Center"
1402+msgstr "Destinations compatibles avec le Centre de Coût"
1403+
1404+#. module: analytic_override
1405+#: code:addons/analytic_override/analytic_account.py:347
1406+#, python-format
1407+msgid "Please remove the Cost Centers linked to the Destination before ticking this box."
1408+msgstr "Veuillez supprimer les Centres de Coût liés à la Destination avant de cocher cette case."
1409+
1410+#. module: account_corrections
1411+#: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:246
1412+#, python-format
1413+msgid "The Cost Center %s is not compatible with the Destination %s."
1414+msgstr "Le Centre de Coût %s n'est pas compatible avec la Destination %s."
1415+
1416+#. module: msf_doc_import
1417+#: code:addons/msf_doc_import/account.py:490
1418+#, python-format
1419+msgid "Line %s. The Cost Center %s is not compatible with the Destination %s."
1420+msgstr "Ligne %s. Le Centre de Coût %s n'est pas compatible avec la Destination %s."
1421+
1422+#. module: msf_doc_import
1423+#: code:addons/msf_doc_import/msf_import_export.py:945
1424+#, python-format
1425+msgid "The Parent Analytic Account must be a View type Funding Pool."
1426+msgstr "Le Compte Analytique Parent doit être un Funding Pool de type Vue."
1427+
1428+#. module: msf_doc_import
1429+#: code:addons/msf_doc_import/msf_import_export.py:992
1430+#, python-format
1431+msgid "The Parent Analytic Account must be a View type Destination."
1432+msgstr "Le Compte Analytique Parent doit être une Destination de type Vue."
1433+
1434+#. module: msf_doc_import
1435+#: code:addons/msf_doc_import/msf_import_export.py:1063
1436+#, python-format
1437+msgid "The Parent Analytic Account must be a View type Cost Center."
1438+msgstr "Le Compte Analytique Parent doit être un Centre de Coût de type Vue."
1439+
1440+#. module: msf_doc_import
1441+#: code:addons/msf_doc_import/msf_import_export.py:1076
1442+#, python-format
1443+msgid "The Parent Analytic Account must be a View type Free 1 account."
1444+msgstr "Le Compte Analytique Parent doit être un compte \"Option 1\" de type Vue."
1445+
1446+#. module: msf_doc_import
1447+#: code:addons/msf_doc_import/msf_import_export.py:1089
1448+#, python-format
1449+msgid "The Parent Analytic Account must be a View type Free 2 account."
1450+msgstr "Le Compte Analytique Parent doit être un compte \"Option 2\" de type Vue."
1451+
1452+#. module: msf_doc_import
1453+#: code:addons/msf_doc_import/msf_import_export.py:995
1454+#, python-format
1455+msgid "The Type must be either \"Normal\" or \"View\"."
1456+msgstr "Le Type doit être soit \"Normal\" soit \"Vue\"."
1457+
1458+#. module: msf_doc_import
1459+#: code:addons/msf_doc_import/msf_import_export.py:1023
1460+#, python-format
1461+msgid "Account code \"%s\" doesn't exist or isn't allowed."
1462+msgstr "Le code comptable \"%s\" n'existe pas ou n'est pas autorisé."
1463+
1464+#. module: msf_homere_interface
1465+#: code:addons/msf_homere_interface/hr.py:224
1466+#, python-format
1467+msgid "Employee %s: the Cost Center %s is not compatible with the Destination %s."
1468+msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec la Destination %s."
1469
1470=== modified file 'bin/addons/msf_profile/msf_profile.py'
1471--- bin/addons/msf_profile/msf_profile.py 2019-03-28 13:22:15 +0000
1472+++ bin/addons/msf_profile/msf_profile.py 2019-05-06 14:14:05 +0000
1473@@ -69,6 +69,24 @@
1474 err_msg,
1475 )
1476
1477+ # UF13.0
1478+ def us_5771_allow_all_cc_in_default_dest(self, cr, uid, *a, **b):
1479+ """
1480+ Set the default created destinations (OPS/EXP/SUP/NAT) as "Allow all Cost Centers"
1481+ """
1482+ update_dests = """
1483+ UPDATE account_analytic_account
1484+ SET allow_all_cc = 't'
1485+ WHERE category = 'DEST'
1486+ AND id IN (SELECT res_id FROM ir_model_data WHERE module='analytic_distribution' AND name IN (
1487+ 'analytic_account_destination_operation',
1488+ 'analytic_account_destination_expatriates',
1489+ 'analytic_account_destination_support',
1490+ 'analytic_account_destination_national_staff'));
1491+ """
1492+ cr.execute(update_dests)
1493+ return True
1494+
1495 # UF12.1
1496 def us_5199_fix_cancel_partial_move_sol_id(self, cr, uid, *a, **b):
1497 '''
1498
1499=== modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv'
1500--- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2019-02-28 15:19:06 +0000
1501+++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2019-05-06 14:14:05 +0000
1502@@ -31,6 +31,7 @@
1503 msf_sync_data_server.periods_state,TRUE,TRUE,TRUE,TRUE,bidirectional,Up,[],"['state', 'period_id/id', 'instance_id/id']",HQ + MISSION,account.period.state,,Periods states,Valid,,129
1504 msf_sync_data_server.fys_state,TRUE,TRUE,TRUE,TRUE,bidirectional,Up,[],"['state', 'fy_id/id', 'instance_id/id']",HQ + MISSION,account.fiscalyear.state,,Fiscal years states,Valid,,130
1505 msf_sync_data_server.cost_center_cc_intermission,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('category' , '=' , 'OC'), ('code', '=', 'cc-intermission')]","['category', 'code', 'date', 'date_start', 'description', 'name', 'type']",OC,account.analytic.account,,CC-Intermission,Valid,,140
1506+msf_sync_data_server.destination_cc,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,"[('category' , '=' , 'DEST')]","['dest_cc_ids/id', 'allow_all_cc']",OC,account.analytic.account,,Destinations: Cost Center fields,Valid,,145
1507 msf_sync_data_server.gl_accounts_reconciliation,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],"['reconciliation_debit_account_id/id', 'reconciliation_credit_account_id/id']",OC,account.account,,GL Accounts Reconciliation Accounts,Valid,,150
1508 msf_sync_data_server.analytic_distribution,TRUE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],['name'],HQ + MISSION,analytic.distribution,,Analytic Distribution,Valid,,200
1509 msf_sync_data_server.cost_center_distribution_line,TRUE,TRUE,TRUE,FALSE,bidirectional,Bidirectional,"[('partner_type','=','internal')]","['amount', 'analytic_id/id', 'currency_id/id', 'date', 'destination_id/id', 'distribution_id/id', 'name', 'percentage', 'partner_type', 'source_date']",HQ + MISSION,cost.center.distribution.line,analytic_id,Cost Center Distribution Line - Internal partner,Valid,,201

Subscribers

People subscribed via source and target branches