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

Proposed by jftempo
Status: Merged
Merged at revision: 5874
Proposed branch: lp:~julie-w/unifield-server/US-7472
Merge into: lp:unifield-server
Diff against target: 1141 lines (+301/-194)
26 files modified
bin/addons/account_corrections/wizard/analytic_distribution_wizard.py (+5/-0)
bin/addons/account_hq_entries/hq_entries.py (+6/-3)
bin/addons/account_hq_entries/wizard/hq_entries_split.py (+4/-1)
bin/addons/account_hq_entries/wizard/hq_reallocation.py (+5/-26)
bin/addons/account_hq_entries/wizard/wizard_view.xml (+1/-1)
bin/addons/account_mcdb/mass_reallocation_search.py (+5/-3)
bin/addons/analytic_distribution/account_commitment_view.xml (+1/-1)
bin/addons/analytic_distribution/account_move_line.py (+4/-2)
bin/addons/analytic_distribution/analytic_account_view.xml (+7/-3)
bin/addons/analytic_distribution/analytic_distribution.py (+44/-2)
bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml (+1/-1)
bin/addons/analytic_distribution/analytic_line.py (+10/-10)
bin/addons/analytic_distribution/report/funding_pool.py (+1/-0)
bin/addons/analytic_distribution/report/funding_pool.rml (+1/-1)
bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py (+13/-25)
bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py (+5/-26)
bin/addons/analytic_override/analytic_account.py (+107/-13)
bin/addons/msf_audittrail/audittrail_invoice_data.yml (+1/-1)
bin/addons/msf_doc_import/account.py (+3/-3)
bin/addons/msf_homere_interface/hr.py (+16/-31)
bin/addons/msf_homere_interface/hr_payroll.py (+7/-4)
bin/addons/msf_homere_interface/hr_payroll_wizard.xml (+3/-1)
bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py (+3/-31)
bin/addons/msf_profile/i18n/fr_MF.po (+44/-3)
bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+1/-1)
bin/addons/register_accounting/wizard/wizard_cash_return.py (+3/-1)
To merge this branch: bzr merge lp:~julie-w/unifield-server/US-7472
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+393177@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 2020-02-06 16:52:43 +0000
3+++ bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2020-11-02 12:53:01 +0000
4@@ -269,6 +269,11 @@
5 _('The Cost Center %s is not compatible with the Destination %s.') %
6 (wiz_line.cost_center_id.code or '', wiz_line.destination_id.code or ''))
7
8+ if not ad_obj.check_fp_cc_compatibility(cr, uid, wiz_line.analytic_id.id, wiz_line.cost_center_id.id, context=context):
9+ raise osv.except_osv(_('Error'),
10+ _('The Cost Center %s is not compatible with the Funding Pool %s.') %
11+ (wiz_line.cost_center_id.code or '', wiz_line.analytic_id.code or ''))
12+
13 if not wiz_line.distribution_line_id or wiz_line.distribution_line_id.id not in old_line_ids:
14 # new distribution line
15 #if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [wiz_line.analytic_id.id]):
16
17=== modified file 'bin/addons/account_hq_entries/hq_entries.py'
18--- bin/addons/account_hq_entries/hq_entries.py 2019-03-28 13:35:11 +0000
19+++ bin/addons/account_hq_entries/hq_entries.py 2020-11-02 12:53:01 +0000
20@@ -106,7 +106,7 @@
21 continue
22 if line.analytic_id and not line.destination_id: # CASE 2/
23 # D Check, except B check
24- if line.cost_center_id.id not in [x.id for x in line.analytic_id.cost_center_ids] and line.analytic_id.id != fp_id:
25+ if not ad_obj.check_fp_cc_compatibility(cr, uid, line.analytic_id.id, line.cost_center_id.id, context=context):
26 res[line.id] = 'invalid'
27 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: CC (%s) not found in FP (%s)') % (line.id or '', line.cost_center_id.code or '', line.analytic_id.code or ''))
28 continue
29@@ -124,7 +124,7 @@
30 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: Tuple Account/DEST (%s/%s) not found in FP (%s)') % (line.id or '', line.account_id.code or '', line.destination_id.code or '', line.analytic_id.code or ''))
31 continue
32 # D Check, except B check
33- if line.cost_center_id.id not in [x.id for x in line.analytic_id.cost_center_ids] and line.analytic_id.id != fp_id:
34+ if not ad_obj.check_fp_cc_compatibility(cr, uid, line.analytic_id.id, line.cost_center_id.id, context=context):
35 res[line.id] = 'invalid'
36 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: CC (%s) not found in FP (%s)') % (line.id or '', line.cost_center_id.code or '', line.analytic_id.code or ''))
37 continue
38@@ -470,7 +470,10 @@
39 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
40 except ValueError:
41 fp_id = 0
42- fields[0].set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id)
43+ fields[0].set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
44+ "'|', "
45+ "'&', ('fp_compatible_with_cc_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), "
46+ "('id', '=', %s)]" % fp_id)
47 # Change Destination field
48 dest_fields = arch.xpath('field[@name="destination_id"]')
49 for field in dest_fields:
50
51=== modified file 'bin/addons/account_hq_entries/wizard/hq_entries_split.py'
52--- bin/addons/account_hq_entries/wizard/hq_entries_split.py 2018-08-17 08:28:25 +0000
53+++ bin/addons/account_hq_entries/wizard/hq_entries_split.py 2020-11-02 12:53:01 +0000
54@@ -292,7 +292,10 @@
55 arch = etree.fromstring(viewtemp['tree']['arch']) # the analytic_id is found in the line_ids, one level down
56 fields = arch.xpath('field[@name="analytic_id"]')
57 if fields:
58- fields[0].set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id)
59+ fields[0].set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'),"
60+ "'|', "
61+ "'&', ('fp_compatible_with_cc_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), "
62+ "('id', '=', %s)]" % fp_id)
63
64 # Change Destination field
65 dest_fields = arch.xpath('field[@name="destination_id"]')
66
67=== modified file 'bin/addons/account_hq_entries/wizard/hq_reallocation.py'
68--- bin/addons/account_hq_entries/wizard/hq_reallocation.py 2019-02-06 09:25:31 +0000
69+++ bin/addons/account_hq_entries/wizard/hq_reallocation.py 2020-11-02 12:53:01 +0000
70@@ -51,7 +51,7 @@
71
72 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
73 """
74- Change funding pool domain in order to include MSF Private fund
75+ Adapts domain for AD fields
76 """
77 if not context:
78 context = {}
79@@ -68,39 +68,18 @@
80 for field in fields:
81 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
82 # Change FP field
83- try:
84- fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
85- except ValueError:
86- fp_id = 0
87 fp_fields = form.xpath('//field[@name="analytic_id"]')
88 # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE!
89 for field in fp_fields:
90- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
91+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
92+ "('fp_compatible_with_cc_ids', '=', cost_center_id)]")
93 # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE!
94 view['arch'] = etree.tostring(form)
95 return view
96
97 def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, analytic_id=False):
98- """
99- Check given cost_center with funding pool
100- """
101- # Prepare some values
102- res = {}
103- if cost_center_id and analytic_id:
104- fp_line = self.pool.get('account.analytic.account').browse(cr, uid, analytic_id)
105- # Search MSF Private Fund element, because it's valid with all accounts
106- try:
107- fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
108- 'analytic_account_msf_private_funds')[1]
109- except ValueError:
110- fp_id = 0
111- if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and analytic_id != fp_id:
112- res = {'value': {'analytic_id': False}}
113- elif not cost_center_id:
114- res = {}
115- else:
116- res = {'value': {'analytic_id': False}}
117- return res
118+ return self.pool.get('analytic.distribution').\
119+ onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=analytic_id, fp_field_name='analytic_id')
120
121 def button_validate(self, cr, uid ,ids, context=None):
122 """
123
124=== modified file 'bin/addons/account_hq_entries/wizard/wizard_view.xml'
125--- bin/addons/account_hq_entries/wizard/wizard_view.xml 2019-01-08 11:20:04 +0000
126+++ bin/addons/account_hq_entries/wizard/wizard_view.xml 2020-11-02 12:53:01 +0000
127@@ -51,7 +51,7 @@
128 <field name="destination_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}"/>
129 <field name="cost_center_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}"/>
130 <field name="analytic_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}"
131- domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]"
132+ domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('fp_compatible_with_cc_ids', '=', cost_center_id)]"
133 string="Funding Pool"
134 context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/>
135 <field name="state"/>
136
137=== modified file 'bin/addons/account_mcdb/mass_reallocation_search.py'
138--- bin/addons/account_mcdb/mass_reallocation_search.py 2013-10-04 14:54:39 +0000
139+++ bin/addons/account_mcdb/mass_reallocation_search.py 2020-11-02 12:53:01 +0000
140@@ -37,8 +37,9 @@
141 context = {}
142 if isinstance(ids, (int, long)):
143 ids = [ids]
144+ analytic_acc_obj = self.pool.get('account.analytic.account')
145 # Only process first id
146- account = self.pool.get('account.analytic.account').browse(cr, uid, ids, context=context)[0]
147+ account = analytic_acc_obj.browse(cr, uid, ids, context=context)[0]
148 if account.category != 'FUNDING':
149 raise osv.except_osv(_('Error'), _('This action only works for Funding Pool accounts!'))
150 # Take all elements to create a domain
151@@ -58,8 +59,9 @@
152 else:
153 # trick to avoid problem with FP that have NO destination link. So we need to search a "False" Destination.
154 search.append(('destination_id', '=', 0))
155- if account.cost_center_ids:
156- search.append(('cost_center_id', 'in', [x.id for x in account.cost_center_ids]))
157+ cost_centers = analytic_acc_obj.get_cc_linked_to_fp(cr, uid, account.id, context=context)
158+ if cost_centers:
159+ search.append(('cost_center_id', 'in', [c.id for c in cost_centers]))
160 else:
161 # trick to avoid problem with FP that have NO CC.
162 search.append(('cost_center_id', '=', 0))
163
164=== modified file 'bin/addons/analytic_distribution/account_commitment_view.xml'
165--- bin/addons/analytic_distribution/account_commitment_view.xml 2020-05-07 08:28:47 +0000
166+++ bin/addons/analytic_distribution/account_commitment_view.xml 2020-11-02 12:53:01 +0000
167@@ -204,8 +204,8 @@
168 <field name="arch" type="xml">
169 <form string="Intl Commitments Analytic Reallocation">
170 <group colspan="6" col="6">
171+ <field name="cost_center_id" required="0" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
172 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
173- <field name="cost_center_id" required="0" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
174 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
175 </group>
176 <newline/>
177
178=== modified file 'bin/addons/analytic_distribution/account_move_line.py'
179--- bin/addons/analytic_distribution/account_move_line.py 2020-02-24 17:11:37 +0000
180+++ bin/addons/analytic_distribution/account_move_line.py 2020-11-02 12:53:01 +0000
181@@ -412,6 +412,7 @@
182 context = {}
183 if isinstance(ids, (int, long)):
184 ids = [ids]
185+ ad_obj = self.pool.get('analytic.distribution')
186 aml_duplication = '__copy_data_seen' in context and 'account.move.line' in context['__copy_data_seen'] or False
187 from_duplication = context.get('copy', False) or aml_duplication
188 if context.get('from_je_import', False) or from_duplication:
189@@ -432,7 +433,8 @@
190 vals.update({'destination_id': l.account_id.default_destination_id.id})
191 if l.employee_id.funding_pool_id:
192 vals.update({'analytic_id': l.employee_id.funding_pool_id.id})
193- if vals.get('cost_center_id') not in [cc.id for cc in l.employee_id.funding_pool_id.cost_center_ids]:
194+ if not ad_obj.check_fp_cc_compatibility(cr, uid, l.employee_id.funding_pool_id.id, l.employee_id.cost_center_id.id,
195+ context=context):
196 # Fetch default funding pool: MSF Private Fund
197 try:
198 msf_fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
199@@ -458,7 +460,7 @@
200 to_change = True
201
202 if to_change:
203- distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {'name': 'check_employee_analytic_distribution'})
204+ distrib_id = ad_obj.create(cr, uid, {'name': 'check_employee_analytic_distribution'}, context=context)
205 vals.update({'distribution_id': distrib_id, 'percentage': 100.0, 'currency_id': l.currency_id.id})
206 # Create funding pool lines
207 self.pool.get('funding.pool.distribution.line').create(cr, uid, vals)
208
209=== modified file 'bin/addons/analytic_distribution/analytic_account_view.xml'
210--- bin/addons/analytic_distribution/analytic_account_view.xml 2020-01-30 10:18:45 +0000
211+++ bin/addons/analytic_distribution/analytic_account_view.xml 2020-11-02 12:53:01 +0000
212@@ -83,9 +83,13 @@
213 <field name="date" select="2"/>
214 </page>
215 <page string="Cost centers" attrs="{'invisible': [('category', '!=', 'FUNDING')]}">
216- <button name="button_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="1"/>
217+ <field name="allow_all_cc_with_fp" colspan="4"
218+ on_change="on_change_allow_all_cc_with_fp(allow_all_cc_with_fp, cost_center_ids)"/>
219+ <button name="button_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/>
220 <separator/>
221- <field name="cost_center_ids" nolabel="1" domain="[('type', '!=', 'view'), ('category', '=', 'OC')]">
222+ <field name="cost_center_ids" nolabel="1" colspan="4"
223+ domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"
224+ on_change="on_change_cc_with_fp(cost_center_ids)">
225 <tree string="Cost Centers" >
226 <field name="code"/>
227 <field name="name"/>
228@@ -112,7 +116,7 @@
229 <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_ids)"/>
230 <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/>
231 <separator/>
232- <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_dest_cc_ids(dest_cc_ids)">
233+ <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_cc_ids(dest_cc_ids)">
234 <tree string="Cost Centers">
235 <field name="code"/>
236 <field name="name"/>
237
238=== modified file 'bin/addons/analytic_distribution/analytic_distribution.py'
239--- bin/addons/analytic_distribution/analytic_distribution.py 2020-01-30 17:08:29 +0000
240+++ bin/addons/analytic_distribution/analytic_distribution.py 2020-11-02 12:53:01 +0000
241@@ -44,6 +44,48 @@
242 return False
243 return True
244
245+ def check_fp_cc_compatibility(self, cr, uid, fp_id, cost_center_id, context=None):
246+ """
247+ Checks the compatibility between the FP and the Cost Center (cf. CC tab in the FP form).
248+ Returns False if they aren't compatible.
249+
250+ If "Allow all Cost Centers" is ticked: only CC linked to the prop. instance of the FP are allowed.
251+ """
252+ if context is None:
253+ context = {}
254+ analytic_acc_obj = self.pool.get('account.analytic.account')
255+ ir_model_data_obj = self.pool.get('ir.model.data')
256+ res = True
257+ if fp_id and cost_center_id:
258+ # The Funding Pool PF is compatible with every CC
259+ try:
260+ pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
261+ except ValueError:
262+ pf_id = 0
263+ if fp_id != pf_id:
264+ fp = analytic_acc_obj.browse(cr, uid, fp_id,
265+ fields_to_fetch=['category', 'allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'],
266+ context=context)
267+ cc = analytic_acc_obj.browse(cr, uid, cost_center_id, fields_to_fetch=['category', 'type', 'cc_instance_ids'], context=context)
268+ if fp and cc and fp.category == 'FUNDING' and cc.category == 'OC':
269+ if fp.allow_all_cc_with_fp and cc.type != 'view' and fp.instance_id and \
270+ fp.instance_id.id in [inst.id for inst in cc.cc_instance_ids]:
271+ res = True
272+ elif cc.id in [c.id for c in fp.cost_center_ids]:
273+ res = True
274+ else:
275+ res = False
276+ return res
277+
278+ def onchange_ad_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False, fp_field_name='funding_pool_id'):
279+ """
280+ Resets the FP in case the CC selected isn't compatible with it.
281+ """
282+ res = {}
283+ if cost_center_id and funding_pool_id and not self.check_fp_cc_compatibility(cr, uid, funding_pool_id, cost_center_id):
284+ res = {'value': {fp_field_name: False}}
285+ return res
286+
287 def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None,
288 doc_date=False, posting_date=False, manual=False, amount=False):
289 """
290@@ -110,7 +152,7 @@
291 continue
292 if (account_id, fp_line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.analytic_id.tuple_destination_account_ids if not x.disabled]:
293 return 'invalid'
294- if fp_line.cost_center_id.id not in [x.id for x in fp_line.analytic_id.cost_center_ids]:
295+ if not self.check_fp_cc_compatibility(cr, uid, fp_line.analytic_id.id, fp_line.cost_center_id.id, context=context):
296 return 'invalid'
297 # Check the date validity of the free accounts used in manual entries
298 if manual and doc_date:
299@@ -152,7 +194,7 @@
300 return 'invalid', _('Cost Center not compatible with destination')
301 if not is_private_fund:
302 # Check that cost center is compatible with FP (except if FP is MSF Private Fund)
303- if cost_center_id not in [x.id for x in fp.cost_center_ids]:
304+ if not self.check_fp_cc_compatibility(cr, uid, analytic_id, cost_center_id, context=context):
305 return 'invalid', _('Cost Center not compatible with FP')
306 # Check that tuple account/destination is compatible with FP (except if FP is MSF Private Fund):
307 if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp.tuple_destination_account_ids if not x.disabled]:
308
309=== modified file 'bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml'
310--- bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2020-02-10 14:51:45 +0000
311+++ bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2020-11-02 12:53:01 +0000
312@@ -32,7 +32,7 @@
313 <field name="destination_id" on_change="onchange_destination(destination_id, analytic_id, parent.account_id)"
314 context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
315 <field name="analytic_id"
316- domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]"
317+ domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('fp_compatible_with_cc_ids', '=', cost_center_id)]"
318 string="Funding Pool" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/>
319 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
320 <field name="amount" sum="Total Amount"/>
321
322=== modified file 'bin/addons/analytic_distribution/analytic_line.py'
323--- bin/addons/analytic_distribution/analytic_line.py 2019-12-12 14:07:37 +0000
324+++ bin/addons/analytic_distribution/analytic_line.py 2020-11-02 12:53:01 +0000
325@@ -46,6 +46,7 @@
326 if not args:
327 return []
328 res = []
329+ analytic_acc_obj = self.pool.get('account.analytic.account')
330 # We just support '=' operator
331 for arg in args:
332 if not arg[1]:
333@@ -54,9 +55,9 @@
334 raise osv.except_osv(_('Warning'), _('This filter is not implemented yet!'))
335 if not arg[2]:
336 raise osv.except_osv(_('Warning'), _('Some search args are missing!'))
337- analytic_account = self.pool.get('account.analytic.account').browse(cr, uid, arg[2])
338+ analytic_account = analytic_acc_obj.browse(cr, uid, arg[2], fields_to_fetch=['tuple_destination_account_ids'], context=context)
339 tuple_list = [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in analytic_account.tuple_destination_account_ids if not x.disabled]
340- cost_center_ids = [x and x.id for x in analytic_account.cost_center_ids]
341+ cost_center_ids = [c.id for c in analytic_acc_obj.get_cc_linked_to_fp(cr, uid, analytic_account.id, context=context)]
342 for cc in cost_center_ids:
343 for t in tuple_list:
344 if res:
345@@ -457,12 +458,10 @@
346 account_id, context=context):
347 if aline.account_id and aline.account_id.id == msf_private_fund:
348 res.append(aline.id)
349- elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids:
350- 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:
351- res.append(aline.id)
352+ elif aline.account_id and ad_obj.check_fp_cc_compatibility(cr, uid, aline.account_id.id, account_id, context=context):
353+ res.append(aline.id)
354 elif account_type == 'FUNDING':
355- fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['cost_center_ids', 'tuple_destination_account_ids'], context=context)
356- cc_ids = fp and fp.get('cost_center_ids', []) or []
357+ fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['tuple_destination_account_ids'], context=context)
358 tuple_destination_account_ids = fp and fp.get('tuple_destination_account_ids', []) or []
359 tuple_list = [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in self.pool.get('account.destination.link').browse(cr, uid, tuple_destination_account_ids) if not x.disabled]
360 # Browse all analytic line to verify them
361@@ -481,7 +480,9 @@
362 # - the cost_center is in compatible cost center from the new funding pool
363 # - the general account is in compatible account/destination tuple
364 # - the destination is in compatible account/destination tuple
365- if aline.cost_center_id and aline.cost_center_id.id in cc_ids and aline.general_account_id and aline.destination_id and (aline.general_account_id.id, aline.destination_id.id) in tuple_list:
366+ if aline.cost_center_id and ad_obj.check_fp_cc_compatibility(cr, uid, account_id, aline.cost_center_id.id, context=context)\
367+ and aline.general_account_id and aline.destination_id and\
368+ (aline.general_account_id.id, aline.destination_id.id) in tuple_list:
369 res.append(aline.id)
370 elif account_type == "DEST":
371 for aline in self.browse(cr, uid, ids, context=context):
372@@ -541,8 +542,7 @@
373 # check funding pool (expect for MSF Private Fund)
374 if not new_fp_id == msf_pf_id: # all OK for MSF Private Fund
375 # - cost center and funding pool compatibility
376- cc_ids = [cc.id for cc in new_fp_br.cost_center_ids]
377- if not new_cc_id in cc_ids:
378+ if not ad_obj.check_fp_cc_compatibility(cr, uid, new_fp_id, new_cc_id, context=context):
379 # not compatible with CC
380 res.append((id, entry_sequence, _('CC')))
381 return False
382
383=== modified file 'bin/addons/analytic_distribution/report/funding_pool.py'
384--- bin/addons/analytic_distribution/report/funding_pool.py 2019-04-03 13:47:21 +0000
385+++ bin/addons/analytic_distribution/report/funding_pool.py 2020-11-02 12:53:01 +0000
386@@ -29,6 +29,7 @@
387 self.localcontext.update({
388 'locale': locale,
389 'today': self.today,
390+ 'all_cc': lambda f: self.pool.get('account.analytic.account').get_cc_linked_to_fp(cr, uid, f, context=context),
391 })
392
393 def today(self):
394
395=== modified file 'bin/addons/analytic_distribution/report/funding_pool.rml'
396--- bin/addons/analytic_distribution/report/funding_pool.rml 2019-04-17 10:00:14 +0000
397+++ bin/addons/analytic_distribution/report/funding_pool.rml 2020-11-02 12:53:01 +0000
398@@ -206,7 +206,7 @@
399 </td>
400 </tr>
401 <tr>
402- <para style="P17">[[repeatIn(o.cost_center_ids,'line')]]</para>
403+ <para style="P17">[[repeatIn(all_cc(o.id), 'line')]]</para>
404 <td>
405 <para style="P7">[[ line.code or '' ]]</para>
406 </td>
407
408=== modified file 'bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py'
409--- bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2020-07-16 15:42:42 +0000
410+++ bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2020-11-02 12:53:01 +0000
411@@ -237,12 +237,18 @@
412 elif (context.get('from_invoice', False) and isinstance(context.get('from_invoice'), int)) or (context.get('from_commitment', False) and isinstance(context.get('from_commitment'), int)) \
413 or (context.get('from_model', False) and isinstance(context.get('from_model'), int)) \
414 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \
415- or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):
416- # Filter is only on cost_center and MSF Private Fund on invoice header
417- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), ('hide_closed_fp', '=', True), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
418+ or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int))\
419+ or (context.get('direct_invoice_id', False) and isinstance(context.get('direct_invoice_id'), int)):
420+ # Filter is only on cost_centers on invoice header
421+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
422+ "('hide_closed_fp', '=', True), ('fp_compatible_with_cc_ids', '=', cost_center_id)]")
423 else:
424 # Add account_id constraints for invoice lines
425- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), ('hide_closed_fp', '=', True), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (parent.account_id, destination_id)), ('id', '=', %s)]" % fp_id)
426+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
427+ "('hide_closed_fp', '=', True), "
428+ "'|', "
429+ "'&', ('fp_compatible_with_cc_ids', '=', cost_center_id), ('tuple_destination', '=', (parent.account_id, destination_id)), "
430+ "('id', '=', %s)]" % fp_id)
431 # Change Destination field
432 dest_fields = tree.xpath('/tree/field[@name="destination_id"]')
433 for field in dest_fields:
434@@ -433,31 +439,13 @@
435 return res
436
437 def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, analytic_id=False):
438- """
439- Check given cost_center with funding pool
440- """
441- # Prepare some values
442- res = {}
443- # Search MSF Private Fund element, because it's valid with all accounts
444- try:
445- fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
446- 'analytic_account_msf_private_funds')[1]
447- except ValueError:
448- fp_id = 0
449+ return self.pool.get('analytic.distribution').\
450+ onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=analytic_id, fp_field_name='analytic_id')
451
452- if cost_center_id and analytic_id:
453- fp_line = self.pool.get('account.analytic.account').browse(cr, uid, analytic_id)
454- if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and analytic_id != fp_id:
455- res = {'value': {'analytic_id': False}}
456- elif not cost_center_id \
457- or analytic_id == fp_id: # PF always compatible:
458- res = {}
459- else:
460- res = {'value': {'analytic_id': False}}
461- return res
462
463 analytic_distribution_wizard_fp_lines()
464
465+
466 class analytic_distribution_wizard_f1_lines(osv.osv_memory):
467 _name = 'analytic.distribution.wizard.f1.lines'
468 _description = 'analytic.distribution.wizard.lines'
469
470=== modified file 'bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py'
471--- bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py 2015-02-09 12:53:11 +0000
472+++ bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py 2020-11-02 12:53:01 +0000
473@@ -38,7 +38,7 @@
474
475 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
476 """
477- Change funding pool domain in order to include MSF Private fund
478+ Adapts domain for AD fields
479 """
480 if context is None:
481 context = {}
482@@ -55,40 +55,19 @@
483 for field in fields:
484 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
485 # Change FP field
486- try:
487- fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
488- except ValueError:
489- fp_id = 0
490 fp_fields = form.xpath('//field[@name="funding_pool_id"]')
491 # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE!
492 for field in fp_fields:
493- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
494+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
495+ "('fp_compatible_with_cc_ids', '=', cost_center_id)]")
496 # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE!
497 # Apply changes
498 view['arch'] = etree.tostring(form)
499 return view
500
501 def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False):
502- """
503- Check given cost_center with funding pool
504- """
505- # Prepare some values
506- res = {}
507- if cost_center_id and funding_pool_id:
508- fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id)
509- # Search MSF Private Fund element, because it's valid with all accounts
510- try:
511- fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
512- 'analytic_account_msf_private_funds')[1]
513- except ValueError:
514- fp_id = 0
515- if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and funding_pool_id != fp_id:
516- res = {'value': {'funding_pool_id': False}}
517- elif not cost_center_id:
518- res = {}
519- else:
520- res = {'value': {'funding_pool_id': False}}
521- return res
522+ return self.pool.get('analytic.distribution').\
523+ onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id)
524
525 def button_validate(self, cr, uid ,ids, context=None):
526 """
527
528=== modified file 'bin/addons/analytic_override/analytic_account.py'
529--- bin/addons/analytic_override/analytic_account.py 2020-10-09 14:34:01 +0000
530+++ bin/addons/analytic_override/analytic_account.py 2020-11-02 12:53:01 +0000
531@@ -290,6 +290,50 @@
532 dom.append(('id', 'in', compatible_dest_ids))
533 return dom
534
535+ def _search_fp_compatible_with_cc_ids(self, cr, uid, obj, name, args, context=None):
536+ """
537+ Returns a domain with all funding pools compatible with the selected Cost Center
538+ E.g.: to get the FPs compatible with the CC 2, use the dom [('fp_compatible_with_cc_ids', '=', 2)]
539+ """
540+ dom = []
541+ if context is None:
542+ context = {}
543+ ir_model_data_obj = self.pool.get('ir.model.data')
544+ for arg in args:
545+ if arg[0] == 'fp_compatible_with_cc_ids':
546+ operator = arg[1]
547+ cc_id = arg[2]
548+ if operator != '=':
549+ raise osv.except_osv(_('Error'), _('Filter not implemented on Funding Pools.'))
550+ cc = False
551+ if cc_id and isinstance(cc_id, (int, long)):
552+ cc = self.browse(cr, uid, cc_id, fields_to_fetch=['category', 'type', 'cc_instance_ids'], context=context)
553+ if cc.category != 'OC' or cc.type == 'view':
554+ raise osv.except_osv(_('Error'), _('Filter only compatible with a normal-type Cost Center.'))
555+ compatible_fp_ids = []
556+ # The Funding Pool PF is compatible with every CC
557+ try:
558+ pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
559+ except ValueError:
560+ pf_id = 0
561+ compatible_fp_ids.append(pf_id)
562+ if cc:
563+ other_fp_ids = self.search(cr, uid, [('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('id', '!=', pf_id)],
564+ context=context)
565+ for fp in self.browse(cr, uid, other_fp_ids,
566+ fields_to_fetch=['allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'],
567+ context=context):
568+ if fp.allow_all_cc_with_fp and fp.instance_id and fp.instance_id.id in [inst.id for inst in cc.cc_instance_ids]:
569+ compatible = True
570+ elif cc.id in [c.id for c in fp.cost_center_ids]:
571+ compatible = True
572+ else:
573+ compatible = False
574+ if compatible:
575+ compatible_fp_ids.append(fp.id)
576+ dom.append(('id', 'in', compatible_fp_ids))
577+ return dom
578+
579 def _get_cc_instance_ids(self, cr, uid, ids, fields, arg, context=None):
580 """
581 Computes the values for fields.function fields, retrieving:
582@@ -299,7 +343,8 @@
583 ...as Cost centre picked for PO/FO reference => po_fo_cc_instance_ids
584 (Note that those fields should theoretically always be linked to one single instance,
585 but they are set as one2many in order to be consistent with the type of fields used in the related object.)
586- - the Missions where the Cost Center is added to => cc_missions
587+ - the Instances where the Cost Center is added to => cc_instance_ids
588+ - the related Missions => cc_missions
589 """
590 if context is None:
591 context = {}
592@@ -311,6 +356,7 @@
593 top_instance_ids = []
594 target_instance_ids = []
595 po_fo_instance_ids = []
596+ all_instance_ids = []
597 missions = set()
598 missions_str = ""
599 target_cc_ids = acc_target_cc_obj.search(cr, uid, [('cost_center_id', '=', analytic_acc_id)], context=context)
600@@ -318,6 +364,7 @@
601 field_list = ['instance_id', 'is_target', 'is_po_fo_cost_center', 'is_top_cost_center']
602 for target_cc in acc_target_cc_obj.browse(cr, uid, target_cc_ids, fields_to_fetch=field_list, context=context):
603 instance = target_cc.instance_id
604+ all_instance_ids.append(instance.id)
605 if instance.mission:
606 missions.add(instance.mission)
607 if target_cc.is_top_cost_center:
608@@ -333,6 +380,7 @@
609 'is_target_cc_instance_ids': target_instance_ids,
610 'po_fo_cc_instance_ids': po_fo_instance_ids,
611 'cc_missions': missions_str,
612+ 'cc_instance_ids': all_instance_ids,
613 }
614 return res
615
616@@ -357,13 +405,18 @@
617 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel',
618 'destination_id', 'cost_center_id', string='Cost Centers',
619 domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"),
620- 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"),
621+ 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"), # for the Destinations
622+ 'allow_all_cc_with_fp': fields.boolean(string="Allow all Cost Centers"), # for the Funding Pools
623 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,
624 string='Destinations compatible with the Cost Center',
625 type='many2many', relation='account.analytic.account',
626 fnct_search=_search_dest_compatible_with_cc_ids),
627 'dest_without_cc': fields.function(_get_dest_without_cc, type='boolean', method=True, store=False,
628 string="Destination allowing no Cost Center",),
629+ 'fp_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,
630+ string='Funding Pools compatible with the Cost Center',
631+ type='many2many', relation='account.analytic.account',
632+ fnct_search=_search_fp_compatible_with_cc_ids),
633 'top_cc_instance_ids': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True,
634 string="Instances having the CC as Top CC",
635 type="one2many", relation="msf.instance", multi="cc_instances"),
636@@ -376,12 +429,16 @@
637 'cc_missions': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True,
638 string="Missions where the CC is added to",
639 type='char', multi="cc_instances"),
640+ 'cc_instance_ids': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True,
641+ string="Instances where the CC is added to",
642+ type="one2many", relation="msf.instance", multi="cc_instances"),
643 }
644
645 _defaults ={
646 'date_start': lambda *a: (datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'),
647 'for_fx_gain_loss': lambda *a: False,
648 'allow_all_cc': lambda *a: False,
649+ 'allow_all_cc_with_fp': lambda *a: False,
650 }
651
652 def _check_code_unicity(self, cr, uid, ids, context=None):
653@@ -453,30 +510,40 @@
654 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]
655 return res
656
657- def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, dest_cc_ids, context=None):
658+ def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, cc_ids, acc_type='destination', field_name='allow_all_cc', context=None):
659 """
660 If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected,
661 informs him that he has to remove the CC first
662+ (acc_type = name of the Analytic Account Type to which the CC are linked, displayed in the warning msg)
663 """
664 res = {}
665- if allow_all_cc and dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
666+ if allow_all_cc and cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
667+ # NOTE: the msg is stored in a variable on purpose, otherwise the ".po" translation files would wrongly contain Python code
668+ msg = 'Please remove the Cost Centers linked to the %s before ticking this box.' % acc_type.title()
669 warning = {
670 'title': _('Warning!'),
671- 'message': _('Please remove the Cost Centers linked to the Destination before ticking this box.')
672+ 'message': _(msg)
673 }
674 res['warning'] = warning
675- res['value'] = {'allow_all_cc': False, }
676+ res['value'] = {field_name: False, }
677 return res
678
679- def on_change_dest_cc_ids(self, cr, uid, ids, dest_cc_ids, context=None):
680+ def on_change_allow_all_cc_with_fp(self, cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, context=None):
681+ return self.on_change_allow_all_cc(cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, acc_type='funding pool',
682+ field_name='allow_all_cc_with_fp', context=context)
683+
684+ def on_change_cc_ids(self, cr, uid, ids, cc_ids, field_name='allow_all_cc', context=None):
685 """
686 If at least a CC is selected, unticks the box "Allow all Cost Centers"
687 """
688 res = {}
689- if dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
690- res['value'] = {'allow_all_cc': False, }
691+ if cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
692+ res['value'] = {field_name: False, }
693 return res
694
695+ def on_change_cc_with_fp(self, cr, uid, ids, cost_center_ids, context=None):
696+ return self.on_change_cc_ids(cr, uid, ids, cost_center_ids, field_name='allow_all_cc_with_fp', context=context)
697+
698 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
699 if not context:
700 context = {}
701@@ -539,12 +606,12 @@
702 vals['parent_id'] = funding_pool_parent
703
704 def remove_inappropriate_links(self, vals, context=None):
705- '''
706- Remove relations that are incoherent regarding the category selected. For instance an account with
707- category "Funding Pool" can have associated cost centers, whereas a "Destination" shouldn't.
708+ """
709+ Removes relations that are inconsistent regarding the category selected. For instance an account with the
710+ category "Funding Pool" can have associated cost centers, whereas a "Cost Center" shouldn't.
711 (That would happen if the category is modified after that the relations have been created).
712 :return: corrected vals
713- '''
714+ """
715 if context is None:
716 context = {}
717 if 'category' in vals:
718@@ -555,6 +622,7 @@
719 if vals['category'] != 'FUNDING':
720 vals['tuple_destination_account_ids'] = [(6, 0, [])]
721 vals['cost_center_ids'] = [(6, 0, [])]
722+ vals['allow_all_cc_with_fp'] = False # default value
723 return vals
724
725 def _check_date(self, vals):
726@@ -720,6 +788,32 @@
727 'target': 'current',
728 }
729
730+ def get_cc_linked_to_fp(self, cr, uid, fp_id, context=None):
731+ """
732+ Returns a browse record list of all Cost Centers compatible with the Funding Pool in parameter:
733+ - if "Allow all Cost Centers" is ticked: all CC linked to the prop. instance of the FP
734+ - else all CC selected in the FP form.
735+
736+ Note: this method matches with what has been selected in the Cost centers tab of the FP form.
737+ It returns an empty list for PF.
738+ """
739+ if context is None:
740+ context = {}
741+ cc_list = []
742+ fp = self.browse(cr, uid, fp_id,
743+ fields_to_fetch=['category', 'allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'],
744+ context=context)
745+ if fp.category == 'FUNDING':
746+ if fp.allow_all_cc_with_fp and fp.instance_id:
747+ # inactive CC are included on purpose, to match with selectable CC in FP form
748+ for cc_id in self.search(cr, uid, [('category', '=', 'OC'), ('type', '!=', 'view')], order='code', context=context):
749+ cc = self.browse(cr, uid, cc_id, context=context)
750+ if fp.instance_id.id in [inst.id for inst in cc.cc_instance_ids]:
751+ cc_list.append(cc)
752+ else:
753+ cc_list = fp.cost_center_ids or []
754+ return cc_list
755+
756 def button_cc_clear(self, cr, uid, ids, context=None):
757 self.write(cr, uid, ids, {'cost_center_ids':[(6, 0, [])]}, context=context)
758 return True
759
760=== modified file 'bin/addons/msf_audittrail/audittrail_invoice_data.yml'
761--- bin/addons/msf_audittrail/audittrail_invoice_data.yml 2019-06-26 10:13:00 +0000
762+++ bin/addons/msf_audittrail/audittrail_invoice_data.yml 2020-11-02 12:53:01 +0000
763@@ -259,7 +259,7 @@
764 # Create the rule
765 fields = ['state', 'category', 'code', 'complete_name', 'cost_center_ids', 'date', 'date_start', 'name', 'parent_id',
766 'type', 'for_fx_gain_loss', 'instance_id', 'tuple_destination_account_ids', 'description', 'destination_ids',
767- 'dest_cc_ids', 'allow_all_cc']
768+ 'dest_cc_ids', 'allow_all_cc', 'allow_all_cc_with_fp']
769
770 fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'account.analytic.account'), ('name', 'in', fields)], context=context)
771
772
773=== modified file 'bin/addons/msf_doc_import/account.py'
774--- bin/addons/msf_doc_import/account.py 2020-06-25 08:27:13 +0000
775+++ bin/addons/msf_doc_import/account.py 2020-11-02 12:53:01 +0000
776@@ -519,9 +519,9 @@
777 continue
778 # if the Fund. Pool used is NOT "PF" check the compatibility with the (account, dest) and the CC
779 if r_fp != msf_fp_id:
780- fp_fields = ['tuple_destination_account_ids', 'cost_center_ids']
781 fp = self.pool.get('account.analytic.account').browse(cr, uid, r_fp,
782- fields_to_fetch=fp_fields, context=context)
783+ fields_to_fetch=['tuple_destination_account_ids'],
784+ context=context)
785 if (account.id, r_destination) not in \
786 [t.account_id and t.destination_id and (t.account_id.id, t.destination_id.id)
787 for t in fp.tuple_destination_account_ids if not t.disabled]:
788@@ -530,7 +530,7 @@
789 (current_line_num, line[cols['G/L Account']], line[cols['Destination']],
790 line[cols['Funding Pool']]))
791 continue
792- if cc.id not in [c.id for c in fp.cost_center_ids]:
793+ if not ad_obj.check_fp_cc_compatibility(cr, uid, fp.id, cc.id, context=context):
794 errors.append(_('Line %s. The Cost Center %s is not compatible with the Funding Pool %s.') %
795 (current_line_num, line[cols['Cost Centre']], line[cols['Funding Pool']]))
796 continue
797
798=== modified file 'bin/addons/msf_homere_interface/hr.py'
799--- bin/addons/msf_homere_interface/hr.py 2020-09-01 08:22:26 +0000
800+++ bin/addons/msf_homere_interface/hr.py 2020-11-02 12:53:01 +0000
801@@ -209,21 +209,26 @@
802 (_check_unicity, "Another employee has the same Identification No.", ['identification_id']),
803 ]
804
805- def _check_employe_dest_cc_compatibility(self, cr, uid, employee_id, context=None):
806+ def _check_employee_cc_compatibility(self, cr, uid, employee_id, context=None):
807 """
808- Raises an error in case the employee Destination and Cost Center are not compatible
809+ Raises an error in case the employee "Destination and Cost Center" or "Funding Pool and Cost Center" are not compatible.
810 """
811 if context is None:
812 context = {}
813 ad_obj = self.pool.get('analytic.distribution')
814- employee_fields = ['destination_id', 'cost_center_id', 'name_resource']
815+ employee_fields = ['destination_id', 'cost_center_id', 'funding_pool_id', 'name_resource']
816 employee = self.browse(cr, uid, employee_id, fields_to_fetch=employee_fields, context=context)
817 emp_dest = employee.destination_id
818 emp_cc = employee.cost_center_id
819+ emp_fp = employee.funding_pool_id
820 if emp_dest and emp_cc:
821 if not ad_obj.check_dest_cc_compatibility(cr, uid, emp_dest.id, emp_cc.id, context=context):
822 raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Destination %s.') %
823 (employee.name_resource, emp_cc.code or '', emp_dest.code or ''))
824+ if emp_fp and emp_cc:
825+ if not ad_obj.check_fp_cc_compatibility(cr, uid, emp_fp.id, emp_cc.id, context=context):
826+ raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Funding Pool %s.') %
827+ (employee.name_resource, emp_cc.code or '', emp_fp.code or ''))
828
829 def create(self, cr, uid, vals, context=None):
830 """
831@@ -246,7 +251,7 @@
832 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:
833 raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.'))
834 employee_id = super(hr_employee, self).create(cr, uid, vals, context)
835- self._check_employe_dest_cc_compatibility(cr, uid, employee_id, context=context)
836+ self._check_employee_cc_compatibility(cr, uid, employee_id, context=context)
837 return employee_id
838
839 def write(self, cr, uid, ids, vals, context=None):
840@@ -298,7 +303,7 @@
841 employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context)
842 if employee_id:
843 res.append(employee_id)
844- self._check_employe_dest_cc_compatibility(cr, uid, emp.id, context=context)
845+ self._check_employee_cc_compatibility(cr, uid, emp.id, context=context)
846 return res
847
848 def unlink(self, cr, uid, ids, context=None):
849@@ -328,7 +333,7 @@
850
851 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
852 """
853- Change funding pool domain in order to include MSF Private fund
854+ Adapts domain for AD fields
855 """
856 if not context:
857 context = {}
858@@ -350,36 +355,16 @@
859 dest_field.set('domain', "[('category', '=', 'DEST'), ('type', '!=', 'view'), "
860 "('dest_compatible_with_cc_ids', '=', cost_center_id)]")
861 # Change FP field
862- try:
863- fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
864- except ValueError:
865- fp_id = 0
866- fp_fields = form.xpath('/' + view_type + '//field[@name="funding_pool_id"]')
867+ fp_fields = form.xpath('/' + view_type + '//field[@name="funding_pool_id"]')
868 for field in fp_fields:
869- field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('state', '=', 'open'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
870+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
871+ "('fp_compatible_with_cc_ids', '=', cost_center_id)]")
872 view['arch'] = etree.tostring(form)
873 return view
874
875 def onchange_cc(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False):
876- """
877- Update FP or CC regarding both.
878- """
879- # Prepare some values
880- vals = {}
881- if not cost_center_id or not funding_pool_id:
882- return {}
883- if cost_center_id and funding_pool_id:
884- fp = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id)
885- try:
886- fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
887- except ValueError:
888- fp_id = 0
889- # Exception for MSF Private Fund
890- if funding_pool_id == fp_id:
891- return {}
892- if cost_center_id not in [x.id for x in fp.cost_center_ids]:
893- vals.update({'funding_pool_id': False})
894- return {'value': vals}
895+ return self.pool.get('analytic.distribution').\
896+ onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id)
897
898 def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
899
900
901=== modified file 'bin/addons/msf_homere_interface/hr_payroll.py'
902--- bin/addons/msf_homere_interface/hr_payroll.py 2019-05-14 15:12:35 +0000
903+++ bin/addons/msf_homere_interface/hr_payroll.py 2020-11-02 12:53:01 +0000
904@@ -103,7 +103,7 @@
905 continue
906 if line.funding_pool_id and not line.destination_id: # CASE 2/
907 # D Check, except B check
908- if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id:
909+ if not ad_obj.check_fp_cc_compatibility(cr, uid, line.funding_pool_id.id, line.cost_center_id.id, context=context):
910 res[line.id] = 'invalid'
911 continue
912 elif not line.funding_pool_id and line.destination_id: # CASE 3/
913@@ -118,7 +118,7 @@
914 res[line.id] = 'invalid'
915 continue
916 # D Check, except B check
917- if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id:
918+ if not ad_obj.check_fp_cc_compatibility(cr, uid, line.funding_pool_id.id, line.cost_center_id.id, context=context):
919 res[line.id] = 'invalid'
920 continue
921 # E Check
922@@ -255,7 +255,7 @@
923
924 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
925 """
926- Change funding pool domain in order to include MSF Private fund
927+ Adapts domain for AD fields
928 """
929 if not context:
930 context = {}
931@@ -278,7 +278,10 @@
932 fp_id = 0
933 fp_fields = form.xpath('//field[@name="funding_pool_id"]')
934 for field in fp_fields:
935- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id)
936+ field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), "
937+ "'|', "
938+ "'&', ('fp_compatible_with_cc_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), "
939+ " ('id', '=', %s)]" % fp_id)
940 # Change Destination field
941 dest_fields = form.xpath('//field[@name="destination_id"]')
942 for field in dest_fields:
943
944=== modified file 'bin/addons/msf_homere_interface/hr_payroll_wizard.xml'
945--- bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2020-02-07 15:20:55 +0000
946+++ bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2020-11-02 12:53:01 +0000
947@@ -18,7 +18,9 @@
948 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"
949 domain="[('category', '=', 'DEST'), ('type', '!=', 'view'),
950 ('dest_compatible_with_cc_ids', '=', cost_center_id)]"/>
951- <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
952+ <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"
953+ domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'),
954+ ('fp_compatible_with_cc_ids', '=', cost_center_id)]"/>
955 </group>
956 <newline/>
957 <field name="free1_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
958
959=== modified file 'bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py'
960--- bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py 2015-07-22 09:03:35 +0000
961+++ bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py 2020-11-02 12:53:01 +0000
962@@ -40,7 +40,7 @@
963
964 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
965 """
966- Change funding pool domain in order to include MSF Private fund
967+ Computes the domain for the Cost Center field
968 """
969 if not context:
970 context = {}
971@@ -56,41 +56,13 @@
972 fields = form.xpath('//field[@name="cost_center_id"]')
973 for field in fields:
974 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
975- # Change FP field
976- try:
977- fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
978- except ValueError:
979- fp_id = 0
980- fp_fields = form.xpath('//field[@name="funding_pool_id"]')
981- # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE!
982- for field in fp_fields:
983- field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id)
984- # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE!
985 # Apply changes
986 view['arch'] = etree.tostring(form)
987 return view
988
989 def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False):
990- """
991- Check given cost_center with funding pool
992- """
993- # Prepare some values
994- res = {}
995- if cost_center_id and funding_pool_id:
996- fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id)
997- # Search MSF Private Fund element, because it's valid with all accounts
998- try:
999- fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
1000- 'analytic_account_msf_private_funds')[1]
1001- except ValueError:
1002- fp_id = 0
1003- if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and funding_pool_id != fp_id:
1004- res = {'value': {'funding_pool_id': False}}
1005- elif not cost_center_id:
1006- res = {}
1007- else:
1008- res = {'value': {'funding_pool_id': False}}
1009- return res
1010+ return self.pool.get('analytic.distribution').\
1011+ onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id)
1012
1013 def button_validate(self, cr, uid ,ids, context=None):
1014 """
1015
1016=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
1017--- bin/addons/msf_profile/i18n/fr_MF.po 2020-10-28 17:16:11 +0000
1018+++ bin/addons/msf_profile/i18n/fr_MF.po 2020-11-02 12:53:01 +0000
1019@@ -61535,10 +61535,10 @@
1020 msgstr "Le total hors-taxe"
1021
1022 #. module: register_accounting
1023-#: code:addons/register_accounting/wizard/wizard_cash_return.py:800
1024+#: code:addons/register_accounting/wizard/wizard_cash_return.py:824
1025 #, python-format
1026-msgid "All advance lines with account that depends on analytic distribution must have an allocation."
1027-msgstr "All advance lines with account that depends on analytic distribution must have an allocation."
1028+msgid "All advance lines with an account depending on an analytic distribution must have a valid allocation."
1029+msgstr "Toutes les lignes d'avance dont le compte dépend d'une distribution analytique doivent avoir une allocation valide."
1030
1031 #. module: stock
1032 #: field:product.category,property_stock_account_output_categ:0
1033@@ -104790,7 +104790,20 @@
1034 msgstr "Filtre non mis en oeuvre sur les Destinations."
1035
1036 #. module: analytic_override
1037+#: code:addons/analytic_override/analytic_account.py:307
1038+#, python-format
1039+msgid "Filter not implemented on Funding Pools."
1040+msgstr "Filtre non mis en oeuvre sur les Funding Pools."
1041+
1042+#. module: analytic_override
1043+#: code:addons/analytic_override/analytic_account.py:312
1044+#, python-format
1045+msgid "Filter only compatible with a normal-type Cost Center."
1046+msgstr "Filtre compatible uniquement avec un Centre de Coût de type normal."
1047+
1048+#. module: analytic_override
1049 #: field:account.analytic.account,allow_all_cc:0
1050+#: field:account.analytic.account,allow_all_cc_with_fp:0
1051 msgid "Allow all Cost Centers"
1052 msgstr "Autoriser tous les Centres de Coût"
1053
1054@@ -104806,17 +104819,34 @@
1055 msgstr "Destinations compatibles avec le Centre de Coût"
1056
1057 #. module: analytic_override
1058+#: field:account.analytic.account,fp_compatible_with_cc_ids:0
1059+msgid "Funding Pools compatible with the Cost Center"
1060+msgstr "Funding Pools compatibles avec le Centre de Coût"
1061+
1062+#. module: analytic_override
1063 #: code:addons/analytic_override/analytic_account.py:347
1064 #, python-format
1065 msgid "Please remove the Cost Centers linked to the Destination before ticking this box."
1066 msgstr "Veuillez supprimer les Centres de Coût liés à la Destination avant de cocher cette case."
1067
1068+#. module: analytic_override
1069+#: code:addons/analytic_override/analytic_account.py:347
1070+#, python-format
1071+msgid "Please remove the Cost Centers linked to the Funding Pool before ticking this box."
1072+msgstr "Veuillez supprimer les Centres de Coût liés au Funding Pool avant de cocher cette case."
1073+
1074 #. module: account_corrections
1075 #: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:246
1076 #, python-format
1077 msgid "The Cost Center %s is not compatible with the Destination %s."
1078 msgstr "Le Centre de Coût %s n'est pas compatible avec la Destination %s."
1079
1080+#. module: account_corrections
1081+#: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:274
1082+#, python-format
1083+msgid "The Cost Center %s is not compatible with the Funding Pool %s."
1084+msgstr "Le Centre de Coût %s n'est pas compatible avec le Funding Pool %s."
1085+
1086 #. module: msf_doc_import
1087 #: code:addons/msf_doc_import/account.py:490
1088 #, python-format
1089@@ -105166,6 +105196,12 @@
1090 msgid "Employee %s: the Cost Center %s is not compatible with the Destination %s."
1091 msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec la Destination %s."
1092
1093+#. module: msf_homere_interface
1094+#: code:addons/msf_homere_interface/hr.py:229
1095+#, python-format
1096+msgid "Employee %s: the Cost Center %s is not compatible with the Funding Pool %s."
1097+msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec le Funding Pool %s."
1098+
1099 #. module: msf_supply_doc_export
1100 #: report:po.follow.up_rml:0
1101 msgid "Status:"
1102@@ -109907,6 +109943,11 @@
1103 msgid "Missions where the CC is added to"
1104 msgstr "Missions dans lesquelles le CC est ajouté"
1105
1106+#. module: analytic_override
1107+#: field:account.analytic.account,cc_instance_ids:0
1108+msgid "Instances where the CC is added to"
1109+msgstr "Instances dans lesquelles le CC est ajouté"
1110+
1111 #. module: sync_client
1112 #: field:sync_client.survey.user,nb_displayed:0
1113 msgid "# Display"
1114
1115=== modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv'
1116--- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-09-01 08:22:26 +0000
1117+++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-11-02 12:53:01 +0000
1118@@ -26,7 +26,7 @@
1119 msf_sync_data_server.analytical_journal_project,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional,"[('instance_id.level', '=', 'project'),('code','!=','ENGI')]","['code', 'name', 'type','instance_id/id']",MISSION,account.analytic.journal,,Analytical Journal (Project),Valid,,121
1120 msf_sync_data_server.analytical_journal,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,[],"['code', 'name', 'type','instance_id/id']",OC,account.analytic.journal,instance_id,Analytical Journal,Valid,,122
1121 msf_sync_data_server.link_accounts_destination,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,[],"['account_id/id', 'destination_id/id', 'disabled']",OC,account.destination.link,,Link accounts Destination,Valid,,123
1122-msf_sync_data_server.funding_pool_to_coordo,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,"[('category' , '=' , 'FUNDING')]","['category', 'code', 'cost_center_ids/id', 'date', 'date_start', 'description', 'instance_id/id', 'name', 'tuple_destination_account_ids/id', 'type']",HQ + MISSION,account.analytic.account,instance_id,Funding Pool linked to a special coordo,Valid,,124
1123+msf_sync_data_server.funding_pool_to_coordo,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,"[('category' , '=' , 'FUNDING')]","['category', 'code', 'cost_center_ids/id', 'date', 'date_start', 'description', 'instance_id/id', 'name', 'tuple_destination_account_ids/id', 'type', 'allow_all_cc_with_fp']",HQ + MISSION,account.analytic.account,instance_id,Funding Pool linked to a special coordo,Valid,,124
1124 msf_sync_data_server.fp_tree0,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('category', 'in', ['FREE1', 'FREE2', 'DEST']),('parent_id', '!=', '')]","['parent_id/id', 'instance_id/id']",OC,account.analytic.account,instance_id,"DestF1F2 tree (only F1, F2 and Dest)",Valid,,126
1125 msf_sync_data_server.fp_tree,FALSE,TRUE,TRUE,FALSE,bidirectional,Bidirectional,"[('category', 'in', ['FREE1', 'FREE2', 'DEST']),('parent_id', '!=', '')]",['parent_id/id'],MISSION,account.analytic.account,,"Analytic account tree (only F1, F2 and Dest) – Coordo/Projects only",Valid,,127
1126 msf_sync_data_server.currency_rate,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('rate' , '!=' , ''),('currency_id', '!=', ''), ('currency_id', 'in', ('res.currency', 'id', [('active', 'in', ['t', 'f']), ('currency_table_id', '=', '')]))]","['currency_id/id', 'name', 'rate']",OC,res.currency.rate,,Currency Rate,Valid,,128
1127
1128=== modified file 'bin/addons/register_accounting/wizard/wizard_cash_return.py'
1129--- bin/addons/register_accounting/wizard/wizard_cash_return.py 2020-02-04 13:20:04 +0000
1130+++ bin/addons/register_accounting/wizard/wizard_cash_return.py 2020-11-02 12:53:01 +0000
1131@@ -820,7 +820,9 @@
1132 # check if any line with an analytic-a-holic account missing the distribution_id value
1133 for st_line in wizard.advance_line_ids:
1134 if st_line.account_id.is_analytic_addicted and st_line.analytic_distribution_state != 'valid':
1135- raise osv.except_osv(_('Warning'), _('All advance lines with account that depends on analytic distribution must have an allocation.'))
1136+ raise osv.except_osv(_('Warning'),
1137+ _('All advance lines with an account depending on an analytic distribution '
1138+ 'must have a valid allocation.'))
1139
1140 # Do computation of total_amount of advance return lines
1141 self.compute_total_amount(cr, uid, ids, context=context)

Subscribers

People subscribed via source and target branches