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

Proposed by jftempo
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 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
=== modified file 'bin/addons/account_corrections/wizard/analytic_distribution_wizard.py'
--- bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2018-09-28 16:00:25 +0000
+++ bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2019-05-06 14:14:05 +0000
@@ -148,6 +148,7 @@
148 context = {}148 context = {}
149 # Prepare some values149 # Prepare some values
150 wizard = self.browse(cr, uid, wizard_id)150 wizard = self.browse(cr, uid, wizard_id)
151 ad_obj = self.pool.get('analytic.distribution')
151 company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id152 company_currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
152 ml = wizard.move_line_id153 ml = wizard.move_line_id
153 orig_date = ml.source_date or ml.date154 orig_date = ml.source_date or ml.date
@@ -240,6 +241,11 @@
240 break241 break
241242
242 for wiz_line in self.pool.get('analytic.distribution.wizard.fp.lines').browse(cr, uid, wiz_line_ids):243 for wiz_line in self.pool.get('analytic.distribution.wizard.fp.lines').browse(cr, uid, wiz_line_ids):
244 if not ad_obj.check_dest_cc_compatibility(cr, uid, wiz_line.destination_id.id, wiz_line.cost_center_id.id, context=context):
245 raise osv.except_osv(_('Error'),
246 _('The Cost Center %s is not compatible with the Destination %s.') %
247 (wiz_line.cost_center_id.code or '', wiz_line.destination_id.code or ''))
248
243 if not wiz_line.distribution_line_id or wiz_line.distribution_line_id.id not in old_line_ids:249 if not wiz_line.distribution_line_id or wiz_line.distribution_line_id.id not in old_line_ids:
244 # new distribution line250 # new distribution line
245 #if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [wiz_line.analytic_id.id]):251 #if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [wiz_line.analytic_id.id]):
246252
=== modified file 'bin/addons/account_hq_entries/hq_entries.py'
--- bin/addons/account_hq_entries/hq_entries.py 2019-03-07 10:00:29 +0000
+++ bin/addons/account_hq_entries/hq_entries.py 2019-05-06 14:14:05 +0000
@@ -42,6 +42,7 @@
42 # Prepare some values42 # Prepare some values
43 res = {}43 res = {}
44 logger = netsvc.Logger()44 logger = netsvc.Logger()
45 ad_obj = self.pool.get('analytic.distribution')
45 # Search MSF Private Fund element, because it's valid with all accounts46 # Search MSF Private Fund element, because it's valid with all accounts
46 try:47 try:
47 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',48 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
@@ -57,6 +58,7 @@
57 # E/ DEST in list of available DEST in ACCOUNT58 # E/ DEST in list of available DEST in ACCOUNT
58 # F/ Check posting date with cost center and destination if exists59 # F/ Check posting date with cost center and destination if exists
59 # G/ Check document date with funding pool60 # G/ Check document date with funding pool
61 # H/ Check Cost Center / Destination compatibility
60 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).62 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).
61 ## CC is mandatory, so always available:63 ## CC is mandatory, so always available:
62 # 1/ no FP, no DEST => Distro = valid64 # 1/ no FP, no DEST => Distro = valid
@@ -132,6 +134,14 @@
132 res[line.id] = 'invalid'134 res[line.id] = 'invalid'
133 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 ''))135 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 ''))
134 continue136 continue
137 # H check
138 if line.destination_id and line.cost_center_id and \
139 not ad_obj.check_dest_cc_compatibility(cr, uid, line.destination_id.id, line.cost_center_id.id, context=context):
140 res[line.id] = 'invalid'
141 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING,
142 _('%s: CC (%s) not compatible with DEST (%s)') %
143 (line.id or '', line.cost_center_id.code or '', line.destination_id.code or ''))
144 continue
135 return res145 return res
136146
137 def _get_cc_changed(self, cr, uid, ids, field_name, arg, context=None):147 def _get_cc_changed(self, cr, uid, ids, field_name, arg, context=None):
138148
=== modified file 'bin/addons/analytic_distribution/account.py'
--- bin/addons/analytic_distribution/account.py 2018-09-18 12:25:20 +0000
+++ bin/addons/analytic_distribution/account.py 2019-05-06 14:14:05 +0000
@@ -120,80 +120,9 @@
120 _columns = {120 _columns = {
121 'account_id': fields.many2one('account.account', "G/L Account"),121 'account_id': fields.many2one('account.account', "G/L Account"),
122 'funding_pool_id': fields.many2one('account.analytic.account', 'Funding Pool'),122 'funding_pool_id': fields.many2one('account.analytic.account', 'Funding Pool'),
123 'destination_id': fields.many2one('account.analytic.account', 'Destination'),
123 }124 }
124125
125 def fields_get(self, cr, uid, fields=None, context=None, with_uom_rounding=False):
126 fields = super(account_destination_summary, self).fields_get(cr, uid, fields, context)
127 dest_obj = self.pool.get('account.analytic.account')
128 destination_ids = dest_obj.search(cr, uid, [('type', '!=', 'view'), ('category', '=', 'DEST'), ('parent_id', '!=', False)])
129 for dest in dest_obj.read(cr, uid, destination_ids, ['name']):
130 fields['dest_%s'%(dest['id'])] = {'type': 'boolean', 'string': dest['name']}
131 return fields
132
133 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
134 view = super(account_destination_summary, self).fields_view_get(cr, uid, view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
135 if view_type == 'tree':
136 fields_to_add = []
137 form = etree.fromstring(view['arch'])
138 tree = form.xpath('//tree')
139 for field in view.get('fields', {}):
140 if field.startswith('dest_'):
141 fields_to_add.append(int(field.split('_')[1]))
142
143 if fields_to_add:
144 for dest_order in self.pool.get('account.analytic.account').search(cr, uid, [('id', 'in', fields_to_add)], order='name'):
145 new_field = etree.Element('field', attrib={'name': 'dest_%d'%dest_order})
146 tree[0].append(new_field)
147 view['arch'] = etree.tostring(form)
148 return view
149
150 def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'):
151 first = False
152 if isinstance(ids, (int, long)):
153 ids = [ids]
154 first = True
155 if fields_to_read is None:
156 fields_to_read = []
157 ret = super(account_destination_summary, self).read(cr, uid, ids, fields_to_read, context, load)
158 f_to_read = []
159 for field in fields_to_read:
160 if field.startswith('dest_'):
161 f_to_read.append(field)
162
163 if f_to_read:
164 cr.execute('''
165 SELECT
166 sum.id,
167 l.destination_id
168 FROM
169 account_destination_link l,
170 account_destination_summary sum,
171 funding_pool_associated_destinations d
172 WHERE
173 l.disabled = 'f' and
174 d.tuple_id = l.id and
175 sum.account_id = l.account_id and
176 sum.funding_pool_id = d.funding_pool_id and
177 sum.id in %s
178 ''',(tuple(ids),)
179 )
180 tmp_result = {}
181 for x in cr.fetchall():
182 tmp_result.setdefault(x[0], []).append(x[1])
183
184 for x in ret:
185 for dest in tmp_result.get(x['id'], []):
186 x['dest_%s'%(dest,)] = True
187 for false_value in f_to_read:
188 if false_value not in x:
189 x[false_value] = False
190
191 if first:
192 return ret[0]
193 return ret
194
195
196
197 def init(self, cr):126 def init(self, cr):
198 # test if id exists in funding_pool_associated_destinations or create it127 # test if id exists in funding_pool_associated_destinations or create it
199 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'")128 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'")
@@ -206,7 +135,8 @@
206 SELECT135 SELECT
207 min(d.id) AS id,136 min(d.id) AS id,
208 l.account_id AS account_id,137 l.account_id AS account_id,
209 d.funding_pool_id AS funding_pool_id138 d.funding_pool_id AS funding_pool_id,
139 l.destination_id AS destination_id
210 FROM140 FROM
211 account_destination_link l,141 account_destination_link l,
212 funding_pool_associated_destinations d142 funding_pool_associated_destinations d
@@ -214,10 +144,10 @@
214 d.tuple_id = l.id and144 d.tuple_id = l.id and
215 l.disabled = 'f'145 l.disabled = 'f'
216 GROUP BY146 GROUP BY
217 l.account_id,d.funding_pool_id147 l.account_id, d.funding_pool_id, l.destination_id
218 )148 )
219 """)149 """)
220 _order = 'account_id'150 _order = 'funding_pool_id, account_id, destination_id'
221account_destination_summary()151account_destination_summary()
222152
223class account_account(osv.osv):153class account_account(osv.osv):
224154
=== modified file 'bin/addons/analytic_distribution/analytic_account_view.xml'
--- bin/addons/analytic_distribution/analytic_account_view.xml 2018-04-03 13:02:04 +0000
+++ bin/addons/analytic_distribution/analytic_account_view.xml 2019-05-06 14:14:05 +0000
@@ -97,9 +97,6 @@
97 <separator/>97 <separator/>
98 <field name="tuple_destination_account_ids" nolabel="1" context="{'dest_in_use':tuple_destination_account_ids}"/>98 <field name="tuple_destination_account_ids" nolabel="1" context="{'dest_in_use':tuple_destination_account_ids}"/>
99 </page>99 </page>
100 <page string="Destinations by accounts" attrs="{'invisible': [('category', '!=', 'FUNDING')]}">
101 <field name="tuple_destination_summary" nolabel="1" readonly="1"/>
102 </page>
103 <page string="Expense accounts" attrs="{'invisible': [('category', '!=', 'DEST')]}">100 <page string="Expense accounts" attrs="{'invisible': [('category', '!=', 'DEST')]}">
104 <field name="destination_ids" nolabel="1" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]" context="{'destination_id': record_id}">101 <field name="destination_ids" nolabel="1" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]" context="{'destination_id': record_id}">
105 <tree string="Expenses accounts list" colors="red:inactivated_for_dest">102 <tree string="Expenses accounts list" colors="red:inactivated_for_dest">
@@ -111,6 +108,17 @@
111 </tree>108 </tree>
112 </field>109 </field>
113 </page>110 </page>
111 <page string="Cost Centers" attrs="{'invisible': [('category', '!=', 'DEST')]}">
112 <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_ids)"/>
113 <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/>
114 <separator/>
115 <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_dest_cc_ids(dest_cc_ids)">
116 <tree string="Cost Centers">
117 <field name="code"/>
118 <field name="name"/>
119 </tree>
120 </field>
121 </page>
114 </page>122 </page>
115 </data>123 </data>
116 </field>124 </field>
@@ -121,8 +129,10 @@
121 <field name="model">account.destination.summary</field>129 <field name="model">account.destination.summary</field>
122 <field name="type">tree</field>130 <field name="type">tree</field>
123 <field name="arch" type="xml">131 <field name="arch" type="xml">
124 <tree string="Destinations by accounts">132 <tree string="Destinations by accounts" hide_new_button="1" hide_edit_button="1" hide_delete_button="1">
133 <field name="funding_pool_id"/>
125 <field name="account_id" />134 <field name="account_id" />
135 <field name="destination_id"/>
126 </tree>136 </tree>
127 </field>137 </field>
128 </record>138 </record>
@@ -275,6 +285,67 @@
275 <field name="context">{'search_default_active': 1, 'filter_inactive_accounts': 1, 'from_web': True, 'display_disabled': 1}</field>285 <field name="context">{'search_default_active': 1, 'filter_inactive_accounts': 1, 'from_web': True, 'display_disabled': 1}</field>
276 </record>286 </record>
277287
288 <!-- Destinations by accounts Search view -->
289 <record id="view_account_destination_summary_search" model="ir.ui.view">
290 <field name="name">account.destination.summary.search</field>
291 <field name="model">account.destination.summary</field>
292 <field name="type">search</field>
293 <field name="priority" eval="16"/>
294 <field name="arch" type="xml">
295 <search>
296 <group>
297 <field name="funding_pool_id" domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING')]"/>
298 <field name="account_id" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]"/>
299 <field name="destination_id" domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]"/>
300 </group>
301 <newline/>
302 <group expand="1" string="Group By...">
303 <filter string="Funding Pool" name="group_funding_pool_id"
304 icon="terp-folder-blue" domain="[]" context="{'group_by':'funding_pool_id'}"/>
305 <separator orientation="vertical"/>
306 <filter string="G/L account" name="group_account_id"
307 icon="terp-folder-orange" domain="[]" context="{'group_by':'account_id'}"/>
308 <separator orientation="vertical"/>
309 <filter string="Destination" name="group_destination_id"
310 icon="terp-folder-violet" domain="[]" context="{'group_by':'destination_id'}"/>
311 </group>
312 </search>
313 </field>
314 </record>
315
316 <!-- Destinations by accounts Form view -->
317 <record id="view_account_destination_summary_form" model="ir.ui.view">
318 <field name="name">account.destination.summary.form</field>
319 <field name="model">account.destination.summary</field>
320 <field name="type">form</field>
321 <field name="priority" eval="16"/>
322 <field name="arch" type="xml">
323 <!-- this form must be read-only: acc/dest links must be created in the Destination tab of the FP -->
324 <form noteditable="1" hide_delete_button="1" hide_new_button="1">
325 <field name="funding_pool_id"/>
326 <newline/>
327 <field name="account_id"/>
328 <field name="destination_id"/>
329 </form>
330 </field>
331 </record>
332
333 <!-- Destinations by accounts Menu entry -->
334 <record id="action_analytic_acc_dest_summary" model="ir.actions.server">
335 <field name="name">Destinations by accounts</field>
336 <field name="model_id" ref="model_account_analytic_account"/>
337 <field name="state">code</field>
338 <field name="code">action = obj.get_destinations_by_accounts(context=context)</field>
339 </record>
340 <record id="ir_destinations_by_accounts_search" model="ir.values">
341 <field name="key2">client_action_relate</field>
342 <field name="model">account.analytic.account</field>
343 <field name="name">destinations_by_accounts</field>
344 <field eval="'ir.actions.server,%d'%action_analytic_acc_dest_summary" name="value"/>
345 <field eval="True" name="object"/>
346 <field name="sequence" eval="110"/>
347 </record>
348
278 <menuitem action="action_account_analytic_account_form"349 <menuitem action="action_account_analytic_account_form"
279 id="account.account_analytic_def_account"350 id="account.account_analytic_def_account"
280 parent="account.menu_analytic_accounting"351 parent="account.menu_analytic_accounting"
281352
=== modified file 'bin/addons/analytic_distribution/analytic_distribution.py'
--- bin/addons/analytic_distribution/analytic_distribution.py 2019-03-28 15:40:47 +0000
+++ bin/addons/analytic_distribution/analytic_distribution.py 2019-05-06 14:14:05 +0000
@@ -26,6 +26,22 @@
26 _name = 'analytic.distribution'26 _name = 'analytic.distribution'
27 _inherit = 'analytic.distribution'27 _inherit = 'analytic.distribution'
2828
29 def check_dest_cc_compatibility(self, cr, uid, destination_id, cost_center_id, context=None):
30 """
31 Checks the compatibility between the Destination and the Cost Center (cf. CC tab in the Destination form).
32 Returns False if they aren't compatible.
33 """
34 if context is None:
35 context = {}
36 analytic_acc_obj = self.pool.get('account.analytic.account')
37 if destination_id and cost_center_id:
38 dest = analytic_acc_obj.browse(cr, uid, destination_id, fields_to_fetch=['category', 'allow_all_cc', 'dest_cc_ids'], context=context)
39 cc = analytic_acc_obj.browse(cr, uid, cost_center_id, fields_to_fetch=['category'], context=context)
40 if dest and cc and dest.category == 'DEST' and cc.category == 'OC' and not dest.allow_all_cc and \
41 cc.id not in [c.id for c in dest.dest_cc_ids]:
42 return False
43 return True
44
29 def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None):45 def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None):
30 """46 """
31 Return distribution state47 Return distribution state
@@ -51,17 +67,23 @@
51 except ValueError:67 except ValueError:
52 fp_id = 068 fp_id = 0
53 account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids'])69 account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids'])
54 # Check Cost Center lines with destination/account link70 # Check Cost Center lines regarding destination/account and destination/CC links
55 for cc_line in distrib.cost_center_lines:71 for cc_line in distrib.cost_center_lines:
56 if cc_line.destination_id.id not in account.get('destination_ids', []):72 if cc_line.destination_id.id not in account.get('destination_ids', []):
57 return 'invalid'73 return 'invalid'
74 if not self.check_dest_cc_compatibility(cr, uid, cc_line.destination_id.id,
75 cc_line.analytic_id and cc_line.analytic_id.id or False, context=context):
76 return 'invalid'
58 # Check Funding pool lines regarding:77 # Check Funding pool lines regarding:
59 # - destination / account78 # - destination / account
79 # - destination / cost center
60 # - If analytic account is MSF Private funds80 # - If analytic account is MSF Private funds
61 # - Cost center and funding pool compatibility81 # - Cost center and funding pool compatibility
62 for fp_line in distrib.funding_pool_lines:82 for fp_line in distrib.funding_pool_lines:
63 if fp_line.destination_id.id not in account.get('destination_ids', []):83 if fp_line.destination_id.id not in account.get('destination_ids', []):
64 return 'invalid'84 return 'invalid'
85 if not self.check_dest_cc_compatibility(cr, uid, fp_line.destination_id.id, fp_line.cost_center_id.id, context=context):
86 return 'invalid'
65 # If fp_line is MSF Private Fund, all is ok87 # If fp_line is MSF Private Fund, all is ok
66 if fp_line.analytic_id.id == fp_id:88 if fp_line.analytic_id.id == fp_id:
67 continue89 continue
@@ -96,6 +118,9 @@
96 # Check that destination is compatible with account118 # Check that destination is compatible with account
97 if destination_id not in [x.id for x in account.destination_ids]:119 if destination_id not in [x.id for x in account.destination_ids]:
98 return 'invalid', _('Destination not compatible with account')120 return 'invalid', _('Destination not compatible with account')
121 # Check that Destination and Cost Center are compatible
122 if not self.check_dest_cc_compatibility(cr, uid, destination_id, cost_center_id, context=context):
123 return 'invalid', _('Cost Center not compatible with destination')
99 if not is_private_fund:124 if not is_private_fund:
100 # Check that cost center is compatible with FP (except if FP is MSF Private Fund)125 # Check that cost center is compatible with FP (except if FP is MSF Private Fund)
101 if cost_center_id not in [x.id for x in fp.cost_center_ids]:126 if cost_center_id not in [x.id for x in fp.cost_center_ids]:
102127
=== modified file 'bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml'
--- bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2018-06-22 09:57:41 +0000
+++ bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2019-05-06 14:14:05 +0000
@@ -10,8 +10,10 @@
10 <field name="arch" type='xml'>10 <field name="arch" type='xml'>
11 <tree string="" editable="top">11 <tree string="" editable="top">
12 <field name="is_percentage_amount_touched" invisible="1" />12 <field name="is_percentage_amount_touched" invisible="1" />
13 <field name="analytic_id" domain="[('type', '!=', 'view'), ('category', '=', 'OC'), ('state', '=', 'open')]"
14 string="Cost Center"
15 context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
13 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>16 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
14 <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')}"/>
15 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>17 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
16 <field name="amount" sum="Total Amount"/>18 <field name="amount" sum="Total Amount"/>
17 </tree>19 </tree>
@@ -25,9 +27,13 @@
25 <field name="arch" type='xml'>27 <field name="arch" type='xml'>
26 <tree string="" editable="top">28 <tree string="" editable="top">
27 <field name="is_percentage_amount_touched" invisible="1" />29 <field name="is_percentage_amount_touched" invisible="1" />
28 <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')}"/>30 <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, analytic_id)"
29 <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')}"/>31 context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
30 <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')}"/>32 <field name="destination_id" on_change="onchange_destination(destination_id, analytic_id, parent.account_id)"
33 context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/>
34 <field name="analytic_id"
35 domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]"
36 string="Funding Pool" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/>
31 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>37 <field name="percentage" sum="Total Percentage" digits="(16,2)"/>
32 <field name="amount" sum="Total Amount"/>38 <field name="amount" sum="Total Amount"/>
33 </tree>39 </tree>
3440
=== modified file 'bin/addons/analytic_distribution/analytic_line.py'
--- bin/addons/analytic_distribution/analytic_line.py 2018-08-21 13:08:14 +0000
+++ bin/addons/analytic_distribution/analytic_line.py 2019-05-06 14:14:05 +0000
@@ -401,6 +401,7 @@
401 if isinstance(ids, (int, long)):401 if isinstance(ids, (int, long)):
402 ids = [ids]402 ids = [ids]
403 # Prepare some value403 # Prepare some value
404 ad_obj = self.pool.get('analytic.distribution')
404 account = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['category', 'date_start', 'date'], context=context)405 account = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['category', 'date_start', 'date'], context=context)
405 account_type = account and account.get('category', False) or False406 account_type = account and account.get('category', False) or False
406 res = []407 res = []
@@ -442,12 +443,13 @@
442 check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id])443 check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id])
443 if check_accounts and aline.account_id.id in check_accounts:444 if check_accounts and aline.account_id.id in check_accounts:
444 continue445 continue
445446 if ad_obj.check_dest_cc_compatibility(cr, uid, aline.destination_id and aline.destination_id.id or False,
446 if aline.account_id and aline.account_id.id == msf_private_fund:447 account_id, context=context):
447 res.append(aline.id)448 if aline.account_id and aline.account_id.id == msf_private_fund:
448 elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids:
449 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:
450 res.append(aline.id)449 res.append(aline.id)
450 elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids:
451 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:
452 res.append(aline.id)
451 elif account_type == 'FUNDING':453 elif account_type == 'FUNDING':
452 fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['cost_center_ids', 'tuple_destination_account_ids'], context=context)454 fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['cost_center_ids', 'tuple_destination_account_ids'], context=context)
453 cc_ids = fp and fp.get('cost_center_ids', []) or []455 cc_ids = fp and fp.get('cost_center_ids', []) or []
@@ -473,7 +475,8 @@
473 res.append(aline.id)475 res.append(aline.id)
474 elif account_type == "DEST":476 elif account_type == "DEST":
475 for aline in self.browse(cr, uid, ids, context=context):477 for aline in self.browse(cr, uid, ids, context=context):
476 if aline.general_account_id and account_id in [x.id for x in aline.general_account_id.destination_ids]:478 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 \
479 aline.general_account_id and account_id in [x.id for x in aline.general_account_id.destination_ids]:
477 res.append(aline.id)480 res.append(aline.id)
478 else:481 else:
479 # Case of FREE1 and FREE2 lines482 # Case of FREE1 and FREE2 lines
@@ -507,6 +510,7 @@
507 new_dest_id, new_dest_br,510 new_dest_id, new_dest_br,
508 new_cc_id, new_cc_br,511 new_cc_id, new_cc_br,
509 new_fp_id, new_fp_br):512 new_fp_id, new_fp_br):
513 ad_obj = self.pool.get('analytic.distribution')
510 if not general_account_br.is_analytic_addicted:514 if not general_account_br.is_analytic_addicted:
511 res.append((id, entry_sequence, ''))515 res.append((id, entry_sequence, ''))
512 return False516 return False
@@ -518,6 +522,12 @@
518 res.append((id, entry_sequence, _('DEST')))522 res.append((id, entry_sequence, _('DEST')))
519 return False523 return False
520524
525 # check cost center with destination
526 if new_dest_id and new_cc_id:
527 if not ad_obj.check_dest_cc_compatibility(cr, uid, new_dest_id, new_cc_id, context=context):
528 res.append((id, entry_sequence, _('CC/DEST')))
529 return False
530
521 # check funding pool (expect for MSF Private Fund)531 # check funding pool (expect for MSF Private Fund)
522 if not new_fp_id == msf_pf_id: # all OK for MSF Private Fund532 if not new_fp_id == msf_pf_id: # all OK for MSF Private Fund
523 # - cost center and funding pool compatibility533 # - cost center and funding pool compatibility
524534
=== modified file 'bin/addons/analytic_distribution/report/funding_pool.py'
--- bin/addons/analytic_distribution/report/funding_pool.py 2014-10-29 16:27:26 +0000
+++ bin/addons/analytic_distribution/report/funding_pool.py 2019-05-06 14:14:05 +0000
@@ -21,7 +21,6 @@
2121
22from report import report_sxw22from report import report_sxw
23import locale23import locale
24import pooler
25import time24import time
2625
27class funding(report_sxw.rml_parse):26class funding(report_sxw.rml_parse):
@@ -29,37 +28,11 @@
29 super(funding, self).__init__(cr, uid, name, context=context)28 super(funding, self).__init__(cr, uid, name, context=context)
30 self.localcontext.update({29 self.localcontext.update({
31 'locale': locale,30 'locale': locale,
32 'getDest': self.getDestinations,
33 'getBoolDest': self.getBoolDest,
34 'today': self.today,31 'today': self.today,
35 })32 })
3633
37 def today(self):34 def today(self):
38 return time.strftime('%Y-%m-%d',time.localtime())35 return time.strftime('%Y-%m-%d',time.localtime())
3936
40 def getDestinations(self):
41 """
42 Fetch destination analytic account:
43 * ID
44 * code
45 Then sort by code
46 """
47 res = [('Code', False), ('Name', False)] # We need the 2 first column header name
48 pool = pooler.get_pool(self.cr.dbname)
49 destination_ids = pool.get('account.analytic.account').search(self.cr, self.uid, [('category', '=', 'DEST'), ('type', '!=', 'view')], order='id')
50 data = pool.get('account.analytic.account').read(self.cr, self.uid, destination_ids, ['code'])
51 res += [(x.get('code'), x.get('id')) for x in data]
52 return res
53
54 def getBoolDest(self, line, o):
55 pool = pooler.get_pool(self.cr.dbname)
56 fields = []
57 for field in pool.get('account.destination.summary').fields_get(self.cr, self.uid, ['account_id']):
58 fields.append(field)
59 r = pool.get('account.destination.summary').read(self.cr, self.uid, line.id, fields)
60 if r[o]:
61 return 'x'
62 return ''
63
64report_sxw.report_sxw('report.funding.pool', 'account.analytic.account', 'addons/analytic_distribution/report/funding_pool.rml', parser=funding)37report_sxw.report_sxw('report.funding.pool', 'account.analytic.account', 'addons/analytic_distribution/report/funding_pool.rml', parser=funding)
65# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:38# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
6639
=== modified file 'bin/addons/analytic_distribution/report/funding_pool.rml'
--- bin/addons/analytic_distribution/report/funding_pool.rml 2017-08-28 12:25:59 +0000
+++ bin/addons/analytic_distribution/report/funding_pool.rml 2019-05-06 14:14:05 +0000
@@ -62,13 +62,7 @@
62 <blockTableStyle id="Table4">62 <blockTableStyle id="Table4">
63 <blockAlignment value="LEFT"/>63 <blockAlignment value="LEFT"/>
64 <blockValign value="TOP"/>64 <blockValign value="TOP"/>
65 <lineStyle kind="LINEBELOW" colorName="#000000" start="0,0" stop="0,0"/>65 <lineStyle kind="LINEBELOW" colorName="#000000" start="0,0" stop="-1,0"/>
66 <lineStyle kind="LINEBELOW" colorName="#000000" start="1,0" stop="1,0"/>
67 <lineStyle kind="LINEBELOW" colorName="#000000" start="2,0" stop="2,0"/>
68 <lineStyle kind="LINEBELOW" colorName="#000000" start="3,0" stop="3,0"/>
69 <lineStyle kind="LINEBELOW" colorName="#000000" start="4,0" stop="4,0"/>
70 <lineStyle kind="LINEBELOW" colorName="#000000" start="5,0" stop="5,0"/>
71 <lineStyle kind="LINEBELOW" colorName="#000000" start="6,0" stop="6,0"/>
72 </blockTableStyle>66 </blockTableStyle>
73 <blockTableStyle id="Table5">67 <blockTableStyle id="Table5">
74 <blockAlignment value="LEFT"/>68 <blockAlignment value="LEFT"/>
@@ -202,22 +196,22 @@
202 </para>196 </para>
203 <para style="P6">Cost centers:</para>197 <para style="P6">Cost centers:</para>
204198
205 <blockTable colWidths="260.0,258.0" style="Table11" repeatRows="1">199 <blockTable colWidths="260.0,260.0" style="Table11" repeatRows="1">
206 <tr>200 <tr>
207 <td>201 <td>
208 <para style="P2" alignment="LEFT">Cost center name</para>202 <para style="P8">Cost center code</para>
209 </td>203 </td>
210 <td>204 <td>
211 <para style="P2" alignment="LEFT">Cost center code</para>205 <para style="P8">Cost center name</para>
212 </td>206 </td>
213 </tr>207 </tr>
214 <tr>208 <tr>
215 <para style="P17">[[repeatIn(o.cost_center_ids,'line')]]</para>209 <para style="P17">[[repeatIn(o.cost_center_ids,'line')]]</para>
216 <td>210 <td>
217 <para style="P3" alignment="LEFT">[[ line.name ]]</para>211 <para style="P7">[[ line.code or '' ]]</para>
218 </td>212 </td>
219 <td>213 <td>
220 <para style="P3" alignment="LEFT">[[ line.code ]]</para>214 <para style="P7">[[ line.name or '' ]]</para>
221 </td>215 </td>
222 </tr>216 </tr>
223 </blockTable>217 </blockTable>
@@ -228,31 +222,37 @@
228 <para style="P8">222 <para style="P8">
229 <font color="white"> </font>223 <font color="white"> </font>
230 </para>224 </para>
231 <para style="P6">Accounts:</para>225 <para style="P6">Account/Destination:</para>
232226
233 <!-- Seems that its not possible to change colWidths regarding a condition.227 <blockTable repeatRows="1" style="Table4" colWidths="95.0,165.0,95.0,165.0">
234 Cf. https://www.odoo.com/forum/help-1/question/reportlab-conditional-table-header-15143
235 Previous colWidths: colWidths="40.0,260.0,55.0,55.0,55.0,55.0"
236 -->
237 <blockTable repeatRows="1" style="Table4">
238 <tr>228 <tr>
239 <td>229 <td>
240 [[repeatIn(getDest(),'dest_data','td')]]230 <para style="P8">Account code</para>
241 <para style="P24" alignment="CENTER">[[ dest_data[0] ]]</para>231 </td>
232 <td>
233 <para style="P8">Account name</para>
234 </td>
235 <td>
236 <para style="P8">Destination code</para>
237 </td>
238 <td>
239 <para style="P8">Destination name</para>
242 </td>240 </td>
243 </tr>241 </tr>
244242
245 <tr>243 <tr>
246 [[repeatIn(o.tuple_destination_summary,'line')]]244 [[repeatIn(o.tuple_destination_summary,'line')]]
247 <td>245 <td>
248 <para style="P16" alignment="LEFT">[[ line.account_id and line.account_id.code ]]</para>246 <para style="P7">[[ line.account_id and line.account_id.code or '' ]]</para>
249 </td>247 </td>
250 <td>248 <td>
251 <para style="P11" alignment="LEFT">[[ line.account_id and line.account_id.name ]]</para>249 <para style="P7">[[ line.account_id and line.account_id.name or '' ]]</para>
252 </td>250 </td>
253 <td>251 <td>
254 [[repeatIn(getDest()[2:],'dest_data','td')]]252 <para style="P7">[[ line.destination_id and line.destination_id.code or '' ]]</para>
255 <para style="P28" alignment="CENTER">[[ getBoolDest(line,'dest_'+ str(dest_data[1])) ]]</para>253 </td>
254 <td>
255 <para style="P7">[[ line.destination_id and line.destination_id.name or '' ]]</para>
256 </td>256 </td>
257 </tr>257 </tr>
258 </blockTable>258 </blockTable>
259259
=== modified file 'bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py'
--- bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2018-09-28 16:00:25 +0000
+++ bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2019-05-06 14:14:05 +0000
@@ -159,6 +159,18 @@
159 percentage = abs((amount / total_amount) * 100)159 percentage = abs((amount / total_amount) * 100)
160 return {'value': {'percentage': percentage, 'is_percentage_amount_touched': True}}160 return {'value': {'percentage': percentage, 'is_percentage_amount_touched': True}}
161161
162 def _dest_compatible_with_cc_domain_part(self, tree):
163 """
164 Returns the domain condition to restrict the destination regarding the cost_center_id (for finance views),
165 or if this field doesn't exist in the view, to the analytic_id (Cost Center in Supply views)
166 """
167 dom_part = ""
168 if tree.xpath('/tree/field[@name="cost_center_id"]'):
169 dom_part = "('dest_compatible_with_cc_ids', '=', cost_center_id)"
170 elif tree.xpath('/tree/field[@name="analytic_id"]'):
171 dom_part = "('dest_compatible_with_cc_ids', '=', analytic_id)"
172 return dom_part
173
162 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):174 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
163 """175 """
164 Rewrite view in order:176 Rewrite view in order:
@@ -195,9 +207,14 @@
195 or (context.get('direct_invoice_id', False) and isinstance(context.get('direct_invoice_id'), int)) \207 or (context.get('direct_invoice_id', False) and isinstance(context.get('direct_invoice_id'), int)) \
196 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \208 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \
197 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):209 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):
198 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST')]")210 domain_part = self._dest_compatible_with_cc_domain_part(tree)
211 domain = "[('type', '!=', 'view'), ('category', '=', 'DEST') %s]" % (domain_part and ', %s' % domain_part or '')
212 field.set('domain', domain)
199 else:213 else:
200 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', parent.account_id)]")214 domain_part = self._dest_compatible_with_cc_domain_part(tree)
215 domain = "[('type', '!=', 'view'), ('category', '=', 'DEST'), " \
216 "('destination_ids', '=', parent.account_id) %s]" % (domain_part and ', %s' % domain_part or '')
217 field.set('domain', domain)
201 ## FUNDING POOL218 ## FUNDING POOL
202 if line_type == 'analytic.distribution.wizard.fp.lines':219 if line_type == 'analytic.distribution.wizard.fp.lines':
203 # Change OC field220 # Change OC field
@@ -235,9 +252,14 @@
235 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \252 or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \
236 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):253 or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)):
237254
238 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST')]")255 domain_part = self._dest_compatible_with_cc_domain_part(tree)
256 domain = "[('type', '!=', 'view'), ('category', '=', 'DEST') %s]" % (domain_part and ', %s' % domain_part or '')
257 field.set('domain', domain)
239 else:258 else:
240 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', parent.account_id)]")259 domain_part = self._dest_compatible_with_cc_domain_part(tree)
260 domain = "[('type', '!=', 'view'), ('category', '=', 'DEST'), " \
261 "('destination_ids', '=', parent.account_id) %s]" % (domain_part and ', %s' % domain_part or '')
262 field.set('domain', domain)
241263
242 ## FREE 1264 ## FREE 1
243 if line_type == 'analytic.distribution.wizard.f1.lines':265 if line_type == 'analytic.distribution.wizard.f1.lines':
244266
=== modified file 'bin/addons/analytic_override/analytic_account.py'
--- bin/addons/analytic_override/analytic_account.py 2019-03-20 10:39:41 +0000
+++ bin/addons/analytic_override/analytic_account.py 2019-05-06 14:14:05 +0000
@@ -211,6 +211,28 @@
211 account_ids += tmp_ids211 account_ids += tmp_ids
212 return account_ids212 return account_ids
213213
214 def _search_dest_compatible_with_cc_ids(self, cr, uid, obj, name, args, context=None):
215 """
216 Returns a domain with all destinations compatible with the selected Cost Center
217 Ex: to get the dest. compatible with the CC 2, use the dom [('dest_compatible_with_cc_ids', '=', 2)]
218 """
219 dom = []
220 if context is None:
221 context = {}
222 for arg in args:
223 if arg[0] == 'dest_compatible_with_cc_ids':
224 operator = arg[1]
225 cc = arg[2]
226 if operator != '=' or not isinstance(cc, (int, long)):
227 raise osv.except_osv(_('Error'), _('Filter not implemented on Destinations.'))
228 all_dest_ids = self.search(cr, uid, [('category', '=', 'DEST')], context=context)
229 compatible_dest_ids = []
230 for dest in self.browse(cr, uid, all_dest_ids, fields_to_fetch=['allow_all_cc', 'dest_cc_ids'], context=context):
231 if dest.allow_all_cc or (cc and cc in [c.id for c in dest.dest_cc_ids]):
232 compatible_dest_ids.append(dest.id)
233 dom.append(('id', 'in', compatible_dest_ids))
234 return dom
235
214 _columns = {236 _columns = {
215 'name': fields.char('Name', size=128, required=True, translate=1),237 'name': fields.char('Name', size=128, required=True, translate=1),
216 'code': fields.char('Code', size=24),238 'code': fields.char('Code', size=24),
@@ -228,11 +250,20 @@
228 'filter_active': fields.function(_get_active, fnct_search=_search_filter_active, type="boolean", method=True, store=False, string="Show only active analytic accounts",),250 'filter_active': fields.function(_get_active, fnct_search=_search_filter_active, type="boolean", method=True, store=False, string="Show only active analytic accounts",),
229 'intermission_restricted': fields.function(_get_fake, type="boolean", method=True, store=False, string="Domain to restrict intermission cc"),251 'intermission_restricted': fields.function(_get_fake, type="boolean", method=True, store=False, string="Domain to restrict intermission cc"),
230 'balance': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Balance', digits_compute=dp.get_precision('Account'), multi='debit_credit_bal_qtty'),252 'balance': fields.function(_debit_credit_bal_qtty, method=True, type='float', string='Balance', digits_compute=dp.get_precision('Account'), multi='debit_credit_bal_qtty'),
253 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel',
254 'destination_id', 'cost_center_id', string='Cost Centers',
255 domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"),
256 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"),
257 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,
258 string='Destinations compatible with the Cost Center',
259 type='many2many', relation='account.analytic.account',
260 fnct_search=_search_dest_compatible_with_cc_ids),
231 }261 }
232262
233 _defaults ={263 _defaults ={
234 'date_start': lambda *a: (datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'),264 'date_start': lambda *a: (datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'),
235 'for_fx_gain_loss': lambda *a: False,265 'for_fx_gain_loss': lambda *a: False,
266 'allow_all_cc': lambda *a: False,
236 }267 }
237268
238 def _check_code_unicity(self, cr, uid, ids, context=None):269 def _check_code_unicity(self, cr, uid, ids, context=None):
@@ -304,6 +335,30 @@
304 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]335 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]
305 return res336 return res
306337
338 def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, dest_cc_ids, context=None):
339 """
340 If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected,
341 informs him that he has to remove the CC first
342 """
343 res = {}
344 if allow_all_cc and dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
345 warning = {
346 'title': _('Warning!'),
347 'message': _('Please remove the Cost Centers linked to the Destination before ticking this box.')
348 }
349 res['warning'] = warning
350 res['value'] = {'allow_all_cc': False, }
351 return res
352
353 def on_change_dest_cc_ids(self, cr, uid, ids, dest_cc_ids, context=None):
354 """
355 If at least a CC is selected, unticks the box "Allow all Cost Centers"
356 """
357 res = {}
358 if dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]
359 res['value'] = {'allow_all_cc': False, }
360 return res
361
307 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):362 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
308 if not context:363 if not context:
309 context = {}364 context = {}
@@ -377,6 +432,8 @@
377 if 'category' in vals:432 if 'category' in vals:
378 if vals['category'] != 'DEST':433 if vals['category'] != 'DEST':
379 vals['destination_ids'] = [(6, 0, [])]434 vals['destination_ids'] = [(6, 0, [])]
435 vals['dest_cc_ids'] = [(6, 0, [])]
436 vals['allow_all_cc'] = False # default value
380 if vals['category'] != 'FUNDING':437 if vals['category'] != 'FUNDING':
381 vals['tuple_destination_account_ids'] = [(6, 0, [])]438 vals['tuple_destination_account_ids'] = [(6, 0, [])]
382 vals['cost_center_ids'] = [(6, 0, [])]439 vals['cost_center_ids'] = [(6, 0, [])]
@@ -408,6 +465,7 @@
408 default['child_ids'] = [] # do not copy the child_ids465 default['child_ids'] = [] # do not copy the child_ids
409 default['tuple_destination_summary'] = []466 default['tuple_destination_summary'] = []
410 default['line_ids'] = []467 default['line_ids'] = []
468 default['dest_cc_ids'] = []
411 return super(analytic_account, self).copy(cr, uid, a_id, default, context=context)469 return super(analytic_account, self).copy(cr, uid, a_id, default, context=context)
412470
413 def _check_name_unicity(self, cr, uid, ids, context=None):471 def _check_name_unicity(self, cr, uid, ids, context=None):
@@ -568,9 +626,41 @@
568 self.write(cr, uid, ids, {'cost_center_ids':[(6, 0, [])]}, context=context)626 self.write(cr, uid, ids, {'cost_center_ids':[(6, 0, [])]}, context=context)
569 return True627 return True
570628
629 def button_dest_cc_clear(self, cr, uid, ids, context=None):
630 """
631 Removes all Cost Centers selected in the Destination view
632 """
633 self.write(cr, uid, ids, {'dest_cc_ids': [(6, 0, [])]}, context=context)
634 return True
635
571 def button_dest_clear(self, cr, uid, ids, context=None):636 def button_dest_clear(self, cr, uid, ids, context=None):
572 self.write(cr, uid, ids, {'tuple_destination_account_ids':[(6, 0, [])]}, context=context)637 self.write(cr, uid, ids, {'tuple_destination_account_ids':[(6, 0, [])]}, context=context)
573 return True638 return True
574639
640 def get_destinations_by_accounts(self, cr, uid, ids, context=None):
641 """
642 Returns a view with the Destinations by accounts (for the FP selected if any, otherwise for all the FP)
643 """
644 if context is None:
645 context = {}
646 ir_model_obj = self.pool.get('ir.model.data')
647 active_ids = context.get('active_ids', [])
648 if active_ids:
649 analytic_acc_category = self.browse(cr, uid, active_ids[0], fields_to_fetch=['category'], context=context).category or ''
650 if analytic_acc_category == 'FUNDING':
651 context.update({'search_default_funding_pool_id': active_ids[0]})
652 search_view_id = ir_model_obj.get_object_reference(cr, uid, 'analytic_distribution', 'view_account_destination_summary_search')
653 search_view_id = search_view_id and search_view_id[1] or False
654 return {
655 'name': _('Destinations by accounts'),
656 'type': 'ir.actions.act_window',
657 'res_model': 'account.destination.summary',
658 'view_type': 'form',
659 'view_mode': 'tree,form',
660 'search_view_id': [search_view_id],
661 'context': context,
662 'target': 'current',
663 }
664
575analytic_account()665analytic_account()
576# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:666# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
577667
=== modified file 'bin/addons/msf_doc_import/account.py'
--- bin/addons/msf_doc_import/account.py 2019-01-03 09:17:52 +0000
+++ bin/addons/msf_doc_import/account.py 2019-05-06 14:14:05 +0000
@@ -194,6 +194,7 @@
194 self.pool.get('msf.doc.import.accounting.lines').unlink(cr, uid, old_lines_ids)194 self.pool.get('msf.doc.import.accounting.lines').unlink(cr, uid, old_lines_ids)
195195
196 # Check wizard data196 # Check wizard data
197 ad_obj = self.pool.get('analytic.distribution')
197 period_obj = self.pool.get('account.period')198 period_obj = self.pool.get('account.period')
198 period_ctx = context.copy()199 period_ctx = context.copy()
199 period_ctx['extend_december'] = True200 period_ctx['extend_december'] = True
@@ -485,6 +486,10 @@
485 errors.append(_('Line %s. The destination %s is not compatible with the account %s.') %486 errors.append(_('Line %s. The destination %s is not compatible with the account %s.') %
486 (current_line_num, line[cols['Destination']], line[cols['G/L Account']]))487 (current_line_num, line[cols['Destination']], line[cols['G/L Account']]))
487 continue488 continue
489 if not ad_obj.check_dest_cc_compatibility(cr, uid, r_destination, r_cc, context=context):
490 errors.append(_('Line %s. The Cost Center %s is not compatible with the Destination %s.') %
491 (current_line_num, line[cols['Cost Centre']], line[cols['Destination']]))
492 continue
488 # if the Fund. Pool used is NOT "PF" check the compatibility with the (account, dest) and the CC493 # if the Fund. Pool used is NOT "PF" check the compatibility with the (account, dest) and the CC
489 if r_fp != msf_fp_id:494 if r_fp != msf_fp_id:
490 fp_fields = ['tuple_destination_account_ids', 'cost_center_ids']495 fp_fields = ['tuple_destination_account_ids', 'cost_center_ids']
491496
=== modified file 'bin/addons/msf_doc_import/msf_import_export.py'
--- bin/addons/msf_doc_import/msf_import_export.py 2018-11-16 10:06:13 +0000
+++ bin/addons/msf_doc_import/msf_import_export.py 2019-05-06 14:14:05 +0000
@@ -633,6 +633,7 @@
633 model = MODEL_DICT[import_brw.model_list_selection]['model']633 model = MODEL_DICT[import_brw.model_list_selection]['model']
634 impobj = self.pool.get(model)634 impobj = self.pool.get(model)
635 acc_obj = self.pool.get('account.account')635 acc_obj = self.pool.get('account.account')
636 acc_analytic_obj = self.pool.get('account.analytic.account')
636 acc_dest_obj = self.pool.get('account.destination.link')637 acc_dest_obj = self.pool.get('account.destination.link')
637638
638 import_data_obj = self.pool.get('import_data')639 import_data_obj = self.pool.get('import_data')
@@ -930,11 +931,20 @@
930 if len(ids_to_update) > 1:931 if len(ids_to_update) > 1:
931 raise Exception('%d records found for rule=%s, model=%s' % (len(ids_to_update), data.get('name'), data.get('model_id')))932 raise Exception('%d records found for rule=%s, model=%s' % (len(ids_to_update), data.get('name'), data.get('model_id')))
932933
933 # Analytic Accounts934 # Funding Pools
934 if import_brw.model_list_selection == 'analytic_accounts':935 if import_brw.model_list_selection == 'funding_pools':
935 context['from_import_menu'] = True936 context['from_import_menu'] = True
937 data['category'] = 'FUNDING'
938 # Parent Analytic Account
939 if data.get('parent_id'):
940 parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
941 fields_to_fetch=['type', 'category'], context=context)
942 parent_type = parent_id.type or ''
943 parent_category = parent_id.category or ''
944 if parent_type != 'view' or parent_category != 'FUNDING':
945 raise Exception(_('The Parent Analytic Account must be a View type Funding Pool.'))
936 # Cost Centers946 # Cost Centers
937 if data.get('cost_center_ids') and data.get('category', '') == 'FUNDING':947 if data.get('cost_center_ids'):
938 cc_list = []948 cc_list = []
939 for cost_center in data.get('cost_center_ids').split(','):949 for cost_center in data.get('cost_center_ids').split(','):
940 cc = cost_center.strip()950 cc = cost_center.strip()
@@ -949,7 +959,7 @@
949 else:959 else:
950 data['cost_center_ids'] = [(6, 0, [])]960 data['cost_center_ids'] = [(6, 0, [])]
951 # Account/Destination961 # Account/Destination
952 if data.get('tuple_destination_account_ids') and data.get('category', '') == 'FUNDING':962 if data.get('tuple_destination_account_ids'):
953 dest_acc_list = []963 dest_acc_list = []
954 for destination_account in data.get('tuple_destination_account_ids').split(','):964 for destination_account in data.get('tuple_destination_account_ids').split(','):
955 dest_acc_ids = []965 dest_acc_ids = []
@@ -969,6 +979,115 @@
969 else:979 else:
970 data['tuple_destination_account_ids'] = [(6, 0, [])]980 data['tuple_destination_account_ids'] = [(6, 0, [])]
971981
982 # Destinations
983 if import_brw.model_list_selection == 'destinations':
984 context['from_import_menu'] = True
985 data['category'] = 'DEST'
986 # Parent Analytic Account
987 if data.get('parent_id'):
988 parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'], fields_to_fetch=['type', 'category'], context=context)
989 parent_type = parent_id.type or ''
990 parent_category = parent_id.category or ''
991 if parent_type != 'view' or parent_category != 'DEST':
992 raise Exception(_('The Parent Analytic Account must be a View type Destination.'))
993 # Type
994 if data['type'] not in ['normal', 'view']:
995 raise Exception(_('The Type must be either "Normal" or "View".'))
996 # Cost Centers
997 if data.get('dest_cc_ids'):
998 if data.get('allow_all_cc'):
999 raise Exception(_("Please either list the Cost Centers to allow, or allow all Cost Centers."))
1000 dest_cc_list = []
1001 for cost_center in data.get('dest_cc_ids').split(','):
1002 cc = cost_center.strip()
1003 cc_dom = [('category', '=', 'OC'), ('type', '=', 'normal'),
1004 '|', ('code', '=', cc), ('name', '=', cc)]
1005 cc_ids = impobj.search(cr, uid, cc_dom, order='id', limit=1, context=context)
1006 if cc_ids:
1007 dest_cc_list.append(cc_ids[0])
1008 else:
1009 raise Exception(_('Cost Center "%s" not found.') % cc)
1010 data['dest_cc_ids'] = [(6, 0, dest_cc_list)]
1011 else:
1012 data['dest_cc_ids'] = [(6, 0, [])]
1013 # Accounts
1014 if data.get('destination_ids'): # "destinations_ids" corresponds to G/L accounts...
1015 acc_list = []
1016 for account in data.get('destination_ids').split(','):
1017 acc = account.strip()
1018 acc_dom = [('type', '!=', 'view'), ('is_analytic_addicted', '=', True), ('code', '=', acc)]
1019 acc_ids = acc_obj.search(cr, uid, acc_dom, order='id', limit=1, context=context)
1020 if acc_ids:
1021 acc_list.append(acc_ids[0])
1022 else:
1023 raise Exception(_("Account code \"%s\" doesn't exist or isn't allowed.") % acc)
1024 data['destination_ids'] = [(6, 0, acc_list)]
1025 else:
1026 data['destination_ids'] = [(6, 0, [])]
1027 # if the code matches with an existing destination: update it
1028 if data.get('code'):
1029 ids_to_update = impobj.search(cr, uid, [('category', '=', 'DEST'), ('code', '=', data['code'])],
1030 limit=1, context=context)
1031 if ids_to_update:
1032 # in case of empty columns on non-required fields, existing values should be deleted
1033 if 'date' not in data:
1034 data['date'] = False
1035 if 'dest_cc_ids' not in data:
1036 data['dest_cc_ids'] = [(6, 0, [])]
1037 if 'allow_all_cc' not in data:
1038 data['allow_all_cc'] = False
1039 if 'destination_ids' not in data:
1040 data['destination_ids'] = [(6, 0, [])]
1041 elif data['destination_ids'][0][2]:
1042 # accounts already linked to the destination:
1043 # - if they don't appear in the new list: will be automatically de-activated
1044 # - if they appear in the list: must be re-activated if they are currently disabled
1045 link_ids = acc_dest_obj.search(cr, uid,
1046 [('account_id', 'in', data['destination_ids'][0][2]),
1047 ('destination_id', '=', ids_to_update[0]),
1048 ('disabled', '=', True)], context=context)
1049 if link_ids:
1050 acc_dest_obj.write(cr, uid, link_ids, {'disabled': False}, context=context)
1051
1052 # Cost Centers
1053 if import_brw.model_list_selection == 'cost_centers':
1054 context['from_import_menu'] = True
1055 data['category'] = 'OC'
1056 # Parent Analytic Account
1057 if data.get('parent_id'):
1058 parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
1059 fields_to_fetch=['type', 'category'], context=context)
1060 parent_type = parent_id.type or ''
1061 parent_category = parent_id.category or ''
1062 if parent_type != 'view' or parent_category != 'OC':
1063 raise Exception(_('The Parent Analytic Account must be a View type Cost Center.'))
1064
1065 # Free 1
1066 if import_brw.model_list_selection == 'free1':
1067 context['from_import_menu'] = True
1068 data['category'] = 'FREE1'
1069 # Parent Analytic Account
1070 if data.get('parent_id'):
1071 parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
1072 fields_to_fetch=['type', 'category'], context=context)
1073 parent_type = parent_id.type or ''
1074 parent_category = parent_id.category or ''
1075 if parent_type != 'view' or parent_category != 'FREE1':
1076 raise Exception(_('The Parent Analytic Account must be a View type Free 1 account.'))
1077
1078 # Free 2
1079 if import_brw.model_list_selection == 'free2':
1080 context['from_import_menu'] = True
1081 data['category'] = 'FREE2'
1082 # Parent Analytic Account
1083 if data.get('parent_id'):
1084 parent_id = acc_analytic_obj.browse(cr, uid, data['parent_id'],
1085 fields_to_fetch=['type', 'category'], context=context)
1086 parent_type = parent_id.type or ''
1087 parent_category = parent_id.category or ''
1088 if parent_type != 'view' or parent_category != 'FREE2':
1089 raise Exception(_('The Parent Analytic Account must be a View type Free 2 account.'))
1090
972 if import_brw.model_list_selection == 'record_rules':1091 if import_brw.model_list_selection == 'record_rules':
973 if not data.get('groups'):1092 if not data.get('groups'):
974 data['groups'] = [(6, 0, [])]1093 data['groups'] = [(6, 0, [])]
9751094
=== modified file 'bin/addons/msf_doc_import/msf_import_export_conf.py'
--- bin/addons/msf_doc_import/msf_import_export_conf.py 2018-10-03 14:41:22 +0000
+++ bin/addons/msf_doc_import/msf_import_export_conf.py 2019-05-06 14:14:05 +0000
@@ -95,8 +95,28 @@
95 'domain_type': 'finance',95 'domain_type': 'finance',
96 'model': 'account.journal'96 'model': 'account.journal'
97 },97 },
98 'analytic_accounts': {98 'funding_pools': {
99 'name': 'Analytic Accounts',99 'name': 'Funding Pools',
100 'domain_type': 'finance',
101 'model': 'account.analytic.account'
102 },
103 'destinations': {
104 'name': 'Destinations',
105 'domain_type': 'finance',
106 'model': 'account.analytic.account'
107 },
108 'cost_centers': {
109 'name': 'Cost Centers',
110 'domain_type': 'finance',
111 'model': 'account.analytic.account'
112 },
113 'free1': {
114 'name': 'Free 1',
115 'domain_type': 'finance',
116 'model': 'account.analytic.account'
117 },
118 'free2': {
119 'name': 'Free 2',
100 'domain_type': 'finance',120 'domain_type': 'finance',
101 'model': 'account.analytic.account'121 'model': 'account.analytic.account'
102 },122 },
@@ -466,11 +486,10 @@
466 'analytic_journal_id.name',486 'analytic_journal_id.name',
467 ],487 ],
468 },488 },
469 'analytic_accounts': {489 'funding_pools': {
470 'header_list': [490 'header_list': [
471 'name',491 'name',
472 'code',492 'code',
473 'category',
474 'parent_id.code',493 'parent_id.code',
475 'type',494 'type',
476 'date_start',495 'date_start',
@@ -482,7 +501,74 @@
482 'required_field_list': [501 'required_field_list': [
483 'name',502 'name',
484 'code',503 'code',
485 'category',504 'parent_id.code',
505 'date_start',
506 ],
507 },
508 'cost_centers': {
509 'header_list': [
510 'name',
511 'code',
512 'parent_id.code',
513 'type',
514 'date_start',
515 'date', # "inactive from"
516 ],
517 'required_field_list': [
518 'name',
519 'code',
520 'parent_id.code',
521 'date_start',
522 ],
523 },
524 'destinations': {
525 'header_list': [
526 'name',
527 'code',
528 'parent_id.code',
529 'type',
530 'date_start',
531 'date', # "inactive from"
532 'dest_cc_ids',
533 'destination_ids',
534 'allow_all_cc',
535 ],
536 'required_field_list': [
537 'name',
538 'code',
539 'parent_id.code',
540 'type',
541 'date_start',
542 ],
543 },
544 'free1': {
545 'header_list': [
546 'name',
547 'code',
548 'parent_id.code',
549 'type',
550 'date_start',
551 'date', # "inactive from"
552 ],
553 'required_field_list': [
554 'name',
555 'code',
556 'parent_id.code',
557 'date_start',
558 ],
559 },
560 'free2': {
561 'header_list': [
562 'name',
563 'code',
564 'parent_id.code',
565 'type',
566 'date_start',
567 'date', # "inactive from"
568 ],
569 'required_field_list': [
570 'name',
571 'code',
486 'parent_id.code',572 'parent_id.code',
487 'date_start',573 'date_start',
488 ],574 ],
489575
=== modified file 'bin/addons/msf_homere_interface/hr.py'
--- bin/addons/msf_homere_interface/hr.py 2019-02-06 09:47:00 +0000
+++ bin/addons/msf_homere_interface/hr.py 2019-05-06 14:14:05 +0000
@@ -208,6 +208,22 @@
208 (_check_unicity, "Another employee has the same Identification No.", ['identification_id']),208 (_check_unicity, "Another employee has the same Identification No.", ['identification_id']),
209 ]209 ]
210210
211 def _check_employe_dest_cc_compatibility(self, cr, uid, employee_id, context=None):
212 """
213 Raises an error in case the employee Destination and Cost Center are not compatible
214 """
215 if context is None:
216 context = {}
217 ad_obj = self.pool.get('analytic.distribution')
218 employee_fields = ['destination_id', 'cost_center_id', 'name_resource']
219 employee = self.browse(cr, uid, employee_id, fields_to_fetch=employee_fields, context=context)
220 emp_dest = employee.destination_id
221 emp_cc = employee.cost_center_id
222 if emp_dest and emp_cc:
223 if not ad_obj.check_dest_cc_compatibility(cr, uid, emp_dest.id, emp_cc.id, context=context):
224 raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Destination %s.') %
225 (employee.name_resource, emp_cc.code or '', emp_dest.code or ''))
226
211 def create(self, cr, uid, vals, context=None):227 def create(self, cr, uid, vals, context=None):
212 """228 """
213 Block creation for local staff if no 'from' in context229 Block creation for local staff if no 'from' in context
@@ -228,7 +244,9 @@
228 # Raise an error if employee is created manually244 # Raise an error if employee is created manually
229 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:245 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:
230 raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.'))246 raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.'))
231 return super(hr_employee, self).create(cr, uid, vals, context)247 employee_id = super(hr_employee, self).create(cr, uid, vals, context)
248 self._check_employe_dest_cc_compatibility(cr, uid, employee_id, context=context)
249 return employee_id
232250
233 def write(self, cr, uid, ids, vals, context=None):251 def write(self, cr, uid, ids, vals, context=None):
234 """252 """
@@ -277,6 +295,7 @@
277 employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context)295 employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context)
278 if employee_id:296 if employee_id:
279 res.append(employee_id)297 res.append(employee_id)
298 self._check_employe_dest_cc_compatibility(cr, uid, emp.id, context=context)
280 return res299 return res
281300
282 def unlink(self, cr, uid, ids, context=None):301 def unlink(self, cr, uid, ids, context=None):
@@ -322,6 +341,11 @@
322 fields = form.xpath('/' + view_type + '//field[@name="cost_center_id"]')341 fields = form.xpath('/' + view_type + '//field[@name="cost_center_id"]')
323 for field in fields:342 for field in fields:
324 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)343 field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id)
344 # Change DEST field
345 dest_fields = form.xpath('/' + view_type + '//field[@name="destination_id"]')
346 for dest_field in dest_fields:
347 dest_field.set('domain', "[('category', '=', 'DEST'), ('type', '!=', 'view'), "
348 "('dest_compatible_with_cc_ids', '=', cost_center_id)]")
325 # Change FP field349 # Change FP field
326 try:350 try:
327 fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]351 fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1]
328352
=== modified file 'bin/addons/msf_homere_interface/hr_payroll.py'
--- bin/addons/msf_homere_interface/hr_payroll.py 2018-10-30 15:33:33 +0000
+++ bin/addons/msf_homere_interface/hr_payroll.py 2019-05-06 14:14:05 +0000
@@ -42,6 +42,7 @@
42 ids = [ids]42 ids = [ids]
43 # Prepare some values43 # Prepare some values
44 res = {}44 res = {}
45 ad_obj = self.pool.get('analytic.distribution')
45 # Search MSF Private Fund element, because it's valid with all accounts46 # Search MSF Private Fund element, because it's valid with all accounts
46 try:47 try:
47 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',48 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
@@ -57,6 +58,7 @@
57 # E/ DEST in list of available DEST in ACCOUNT58 # E/ DEST in list of available DEST in ACCOUNT
58 # F/ Check posting date with cost center and destination if exists59 # F/ Check posting date with cost center and destination if exists
59 # G/ Check document date with funding pool60 # G/ Check document date with funding pool
61 # H/ Check Cost Center / Destination compatibility
60 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).62 ## CASES where FP is filled in (or not) and/or DEST is filled in (or not).
61 ## CC is mandatory, so always available:63 ## CC is mandatory, so always available:
62 # 1/ no FP, no DEST => Distro = valid64 # 1/ no FP, no DEST => Distro = valid
@@ -124,6 +126,11 @@
124 if line.destination_id.id not in [x.id for x in account.destination_ids]:126 if line.destination_id.id not in [x.id for x in account.destination_ids]:
125 res[line.id] = 'invalid'127 res[line.id] = 'invalid'
126 continue128 continue
129 # H check
130 if line.destination_id and line.cost_center_id and \
131 not ad_obj.check_dest_cc_compatibility(cr, uid, line.destination_id.id, line.cost_center_id.id, context=context):
132 res[line.id] = 'invalid'
133 continue
127 return res134 return res
128135
129 def _get_third_parties(self, cr, uid, ids, field_name=None, arg=None, context=None):136 def _get_third_parties(self, cr, uid, ids, field_name=None, arg=None, context=None):
130137
=== modified file 'bin/addons/msf_homere_interface/hr_payroll_wizard.xml'
--- bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2019-01-21 09:53:32 +0000
+++ bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2019-05-06 14:14:05 +0000
@@ -13,8 +13,11 @@
13 <field name="arch" type="xml">13 <field name="arch" type="xml">
14 <form string="Analytic Reallocation">14 <form string="Analytic Reallocation">
15 <group colspan="6" col="6">15 <group colspan="6" col="6">
16 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>16 <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, funding_pool_id)"
17 <field name="cost_center_id" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>17 context="{'search_default_active': 1, 'hide_inactive': 1}"/>
18 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"
19 domain="[('category', '=', 'DEST'), ('type', '!=', 'view'),
20 ('dest_compatible_with_cc_ids', '=', cost_center_id)]"/>
18 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>21 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
19 </group>22 </group>
20 <newline/>23 <newline/>
2124
=== modified file 'bin/addons/msf_homere_interface/hr_view.xml'
--- bin/addons/msf_homere_interface/hr_view.xml 2018-08-21 19:39:57 +0000
+++ bin/addons/msf_homere_interface/hr_view.xml 2019-05-06 14:14:05 +0000
@@ -27,8 +27,8 @@
27 <field name="homere_codeterrain" invisible="1"/>27 <field name="homere_codeterrain" invisible="1"/>
28 </group>28 </group>
29 <group colspan="4" col="8">29 <group colspan="4" col="8">
30 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
31 <field name="cost_center_id" on_change="onchange_cc(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>30 <field name="cost_center_id" on_change="onchange_cc(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
31 <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
32 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>32 <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
33 <newline />33 <newline />
34 <field name="free1_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>34 <field name="free1_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/>
3535
=== modified file 'bin/addons/msf_profile/data/patches.xml'
--- bin/addons/msf_profile/data/patches.xml 2019-03-28 13:22:15 +0000
+++ bin/addons/msf_profile/data/patches.xml 2019-05-06 14:14:05 +0000
@@ -411,5 +411,10 @@
411 <field name="method">us_5667_remove_contract_workflow</field>411 <field name="method">us_5667_remove_contract_workflow</field>
412 </record>412 </record>
413413
414 <!-- UF13.0 -->
415 <record id="us_5771_allow_all_cc_in_default_dest" model="patch.scripts">
416 <field name="method">us_5771_allow_all_cc_in_default_dest</field>
417 </record>
418
414 </data>419 </data>
415</openerp>420</openerp>
416421
=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
--- bin/addons/msf_profile/i18n/fr_MF.po 2019-04-08 09:30:07 +0000
+++ bin/addons/msf_profile/i18n/fr_MF.po 2019-05-06 14:14:05 +0000
@@ -5227,8 +5227,9 @@
5227msgid "Close period (Mission)"5227msgid "Close period (Mission)"
5228msgstr "Clôturer la Période (Mission)"5228msgstr "Clôturer la Période (Mission)"
52295229
5230#. module: msf_budget5230#. modules: msf_budget, analytic_distribution
5231#: report:addons/msf_budget/report/report_local_expenses_xls.mako:1165231#: report:addons/msf_budget/report/report_local_expenses_xls.mako:116
5232#: report:funding.pool:0
5232msgid "Account name"5233msgid "Account name"
5233msgstr "Nom du compte"5234msgstr "Nom du compte"
52345235
@@ -16477,6 +16478,7 @@
16477#: view:threshold.value:016478#: view:threshold.value:0
16478#: view:international.transport.cost.report:016479#: view:international.transport.cost.report:0
16479#: view:local.transport.cost.report:016480#: view:local.transport.cost.report:0
16481#: view:account.destination.summary:0
16480msgid "Group By..."16482msgid "Group By..."
16481msgstr "Grouper Par..."16483msgstr "Grouper Par..."
1648216484
@@ -16779,7 +16781,7 @@
16779#. module: analytic_distribution16781#. module: analytic_distribution
16780#: view:account.analytic.account:016782#: view:account.analytic.account:0
16781msgid "Remove all"16783msgid "Remove all"
16782msgstr "Remove all"16784msgstr "Tout supprimer"
1678316785
16784#. module: vertical_integration16786#. module: vertical_integration
16785#: code:addons/vertical_integration/report/hq_report_oca.py:17616787#: code:addons/vertical_integration/report/hq_report_oca.py:176
@@ -31372,9 +31374,10 @@
31372msgid "Type of file"31374msgid "Type of file"
31373msgstr "Type de fichier"31375msgstr "Type de fichier"
3137431376
31375#. module: msf_budget31377#. modules: msf_budget, analytic_distribution
31376#: report:addons/msf_budget/report/report_local_expenses_xls.mako:11531378#: report:addons/msf_budget/report/report_local_expenses_xls.mako:115
31377#: field:msf.budget.line,account_code:031379#: field:msf.budget.line,account_code:0
31380#: report:funding.pool:0
31378msgid "Account code"31381msgid "Account code"
31379msgstr "Code du compte"31382msgstr "Code du compte"
3138031383
@@ -38475,6 +38478,7 @@
38475#: field:hr.payroll.msf,funding_pool_id:038478#: field:hr.payroll.msf,funding_pool_id:0
38476#: field:account.invoice.line,funding_pool_id:038479#: field:account.invoice.line,funding_pool_id:0
38477#: report:addons/account/report/free_allocation_report.mako:20838480#: report:addons/account/report/free_allocation_report.mako:208
38481#: view:account.destination.summary:0
38478#, python-format38482#, python-format
38479msgid "Funding Pool"38483msgid "Funding Pool"
38480msgstr "Funding Pool"38484msgstr "Funding Pool"
@@ -47675,10 +47679,11 @@
47675msgid "America/Sitka"47679msgid "America/Sitka"
47676msgstr "America/Sitka"47680msgstr "America/Sitka"
4767747681
47678#. module: account47682#. modules: account, analytic_distribution
47679#: view:account.analytic.line:047683#: view:account.analytic.line:0
47684#: view:account.destination.summary:0
47680msgid "G/L account"47685msgid "G/L account"
47681msgstr "G/L account"47686msgstr "Compte Grand Livre"
4768247687
47683#. modules: account, register_accounting47688#. modules: account, register_accounting
47684#: help:account.invoice,state:047689#: help:account.invoice,state:0
@@ -67403,6 +67408,7 @@
67403#: view:msf.instance:067408#: view:msf.instance:0
67404#: report:addons/account/report/free_allocation_report.mako:17667409#: report:addons/account/report/free_allocation_report.mako:176
67405#: field:free.allocation.wizard,cost_center_ids:067410#: field:free.allocation.wizard,cost_center_ids:0
67411#: field:account.analytic.account,dest_cc_ids:0
67406msgid "Cost Centers"67412msgid "Cost Centers"
67407msgstr "Centres de Coût"67413msgstr "Centres de Coût"
6740867414
@@ -86607,10 +86613,12 @@
86607msgid "Total weight:"86613msgid "Total weight:"
86608msgstr "Total weight:"86614msgstr "Total weight:"
8660986615
86610#. module: analytic_distribution86616#. modules: analytic_distribution, analytic_override
86611#: view:account.analytic.account:086617#: view:account.analytic.account:0
86612#: view:account.destination.summary:086618#: view:account.destination.summary:0
86613#: model:ir.model,name:analytic_distribution.model_account_destination_summary86619#: model:ir.model,name:analytic_distribution.model_account_destination_summary
86620#: model:ir.actions.server,name:analytic_distribution.action_analytic_acc_dest_summary
86621#: code:addons/analytic_override/analytic_account.py:652
86614msgid "Destinations by accounts"86622msgid "Destinations by accounts"
86615msgstr "Destinations par Compte"86623msgstr "Destinations par Compte"
8661686624
@@ -91150,8 +91158,8 @@
9115091158
91151#. module: analytic_distribution91159#. module: analytic_distribution
91152#: report:funding.pool:091160#: report:funding.pool:0
91153msgid "Accounts:"91161msgid "Account/Destination:"
91154msgstr "Comptes:"91162msgstr "Compte / Destination :"
9115591163
91156#. module: msf_outgoing91164#. module: msf_outgoing
91157#: help:return.pack.shipment.processor,address_id:091165#: help:return.pack.shipment.processor,address_id:0
@@ -96457,6 +96465,8 @@
96457#: view:sync.client.message_to_send:096465#: view:sync.client.message_to_send:0
96458#: report:addons/account/report/free_allocation_report.mako:20296466#: report:addons/account/report/free_allocation_report.mako:202
96459#: report:addons/stock_override/report/report_stock_move_xls.mako:14396467#: report:addons/stock_override/report/report_stock_move_xls.mako:143
96468#: field:account.destination.summary,destination_id:0
96469#: view:account.destination.summary:0
96460#, python-format96470#, python-format
96461msgid "Destination"96471msgid "Destination"
96462msgstr "Destination"96472msgstr "Destination"
@@ -102110,6 +102120,12 @@
102110msgstr "%s: DEST (%s) incompatible avec le compte (%s)"102120msgstr "%s: DEST (%s) incompatible avec le compte (%s)"
102111102121
102112#. module: account_hq_entries102122#. module: account_hq_entries
102123#: code:addons/account_hq_entries/hq_entries.py:142
102124#, python-format
102125msgid "%s: CC (%s) not compatible with DEST (%s)"
102126msgstr "%s : CC (%s) non compatible avec la DEST (%s)"
102127
102128#. module: account_hq_entries
102113#: code:addons/account_hq_entries/hq_entries.py:103102129#: code:addons/account_hq_entries/hq_entries.py:103
102114#, python-format102130#, python-format
102115msgid "%s: No CC"102131msgid "%s: No CC"
@@ -105388,3 +105404,113 @@
105388#, python-format105404#, python-format
105389msgid "Product %s, BN: %s not enough stock to process quantity %s %s (stock level: %s)"105405msgid "Product %s, BN: %s not enough stock to process quantity %s %s (stock level: %s)"
105390msgstr "Produit %s, Lot: %s, pas assez de stock pour traiter la qantité %s %s (quantité en stock: %s)"105406msgstr "Produit %s, Lot: %s, pas assez de stock pour traiter la qantité %s %s (quantité en stock: %s)"
105407
105408#. module: analytic_distribution
105409#: code:addons/analytic_distribution/analytic_distribution.py:123
105410#, python-format
105411msgid "Cost Center not compatible with destination"
105412msgstr "Centre de Coût non compatible avec la destination"
105413
105414#. module: analytic_distribution
105415#: code:addons/analytic_distribution/analytic_line.py:528
105416#, python-format
105417msgid "CC/DEST"
105418msgstr "CC / DEST"
105419
105420#. module: analytic_distribution
105421#: report:funding.pool:0
105422msgid "Destination code"
105423msgstr "Code de la destination"
105424
105425#. module: analytic_distribution
105426#: report:funding.pool:0
105427msgid "Destination name"
105428msgstr "Nom de la destination"
105429
105430#. module: analytic_override
105431#: code:addons/analytic_override/analytic_account.py:227
105432#, python-format
105433msgid "Filter not implemented on Destinations."
105434msgstr "Filtre non mis en oeuvre sur les Destinations."
105435
105436#. module: analytic_override
105437#: field:account.analytic.account,allow_all_cc:0
105438msgid "Allow all Cost Centers"
105439msgstr "Autoriser tous les Centres de Coût"
105440
105441#. module: msf_doc_import
105442#: code:addons/msf_doc_import/msf_import_export.py:999
105443#, python-format
105444msgid "Please either list the Cost Centers to allow, or allow all Cost Centers."
105445msgstr "Veuillez soit lister les Centres de Coût à autoriser, soit autoriser tous les Centres de Coût."
105446
105447#. module: analytic_override
105448#: field:account.analytic.account,dest_compatible_with_cc_ids:0
105449msgid "Destinations compatible with the Cost Center"
105450msgstr "Destinations compatibles avec le Centre de Coût"
105451
105452#. module: analytic_override
105453#: code:addons/analytic_override/analytic_account.py:347
105454#, python-format
105455msgid "Please remove the Cost Centers linked to the Destination before ticking this box."
105456msgstr "Veuillez supprimer les Centres de Coût liés à la Destination avant de cocher cette case."
105457
105458#. module: account_corrections
105459#: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:246
105460#, python-format
105461msgid "The Cost Center %s is not compatible with the Destination %s."
105462msgstr "Le Centre de Coût %s n'est pas compatible avec la Destination %s."
105463
105464#. module: msf_doc_import
105465#: code:addons/msf_doc_import/account.py:490
105466#, python-format
105467msgid "Line %s. The Cost Center %s is not compatible with the Destination %s."
105468msgstr "Ligne %s. Le Centre de Coût %s n'est pas compatible avec la Destination %s."
105469
105470#. module: msf_doc_import
105471#: code:addons/msf_doc_import/msf_import_export.py:945
105472#, python-format
105473msgid "The Parent Analytic Account must be a View type Funding Pool."
105474msgstr "Le Compte Analytique Parent doit être un Funding Pool de type Vue."
105475
105476#. module: msf_doc_import
105477#: code:addons/msf_doc_import/msf_import_export.py:992
105478#, python-format
105479msgid "The Parent Analytic Account must be a View type Destination."
105480msgstr "Le Compte Analytique Parent doit être une Destination de type Vue."
105481
105482#. module: msf_doc_import
105483#: code:addons/msf_doc_import/msf_import_export.py:1063
105484#, python-format
105485msgid "The Parent Analytic Account must be a View type Cost Center."
105486msgstr "Le Compte Analytique Parent doit être un Centre de Coût de type Vue."
105487
105488#. module: msf_doc_import
105489#: code:addons/msf_doc_import/msf_import_export.py:1076
105490#, python-format
105491msgid "The Parent Analytic Account must be a View type Free 1 account."
105492msgstr "Le Compte Analytique Parent doit être un compte \"Option 1\" de type Vue."
105493
105494#. module: msf_doc_import
105495#: code:addons/msf_doc_import/msf_import_export.py:1089
105496#, python-format
105497msgid "The Parent Analytic Account must be a View type Free 2 account."
105498msgstr "Le Compte Analytique Parent doit être un compte \"Option 2\" de type Vue."
105499
105500#. module: msf_doc_import
105501#: code:addons/msf_doc_import/msf_import_export.py:995
105502#, python-format
105503msgid "The Type must be either \"Normal\" or \"View\"."
105504msgstr "Le Type doit être soit \"Normal\" soit \"Vue\"."
105505
105506#. module: msf_doc_import
105507#: code:addons/msf_doc_import/msf_import_export.py:1023
105508#, python-format
105509msgid "Account code \"%s\" doesn't exist or isn't allowed."
105510msgstr "Le code comptable \"%s\" n'existe pas ou n'est pas autorisé."
105511
105512#. module: msf_homere_interface
105513#: code:addons/msf_homere_interface/hr.py:224
105514#, python-format
105515msgid "Employee %s: the Cost Center %s is not compatible with the Destination %s."
105516msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec la Destination %s."
105391105517
=== modified file 'bin/addons/msf_profile/msf_profile.py'
--- bin/addons/msf_profile/msf_profile.py 2019-03-28 13:22:15 +0000
+++ bin/addons/msf_profile/msf_profile.py 2019-05-06 14:14:05 +0000
@@ -69,6 +69,24 @@
69 err_msg,69 err_msg,
70 )70 )
7171
72 # UF13.0
73 def us_5771_allow_all_cc_in_default_dest(self, cr, uid, *a, **b):
74 """
75 Set the default created destinations (OPS/EXP/SUP/NAT) as "Allow all Cost Centers"
76 """
77 update_dests = """
78 UPDATE account_analytic_account
79 SET allow_all_cc = 't'
80 WHERE category = 'DEST'
81 AND id IN (SELECT res_id FROM ir_model_data WHERE module='analytic_distribution' AND name IN (
82 'analytic_account_destination_operation',
83 'analytic_account_destination_expatriates',
84 'analytic_account_destination_support',
85 'analytic_account_destination_national_staff'));
86 """
87 cr.execute(update_dests)
88 return True
89
72 # UF12.190 # UF12.1
73 def us_5199_fix_cancel_partial_move_sol_id(self, cr, uid, *a, **b):91 def us_5199_fix_cancel_partial_move_sol_id(self, cr, uid, *a, **b):
74 '''92 '''
7593
=== modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv'
--- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2019-02-28 15:19:06 +0000
+++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2019-05-06 14:14:05 +0000
@@ -31,6 +31,7 @@
31msf_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,,12931msf_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
32msf_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,,13032msf_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
33msf_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,,14033msf_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
34msf_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
34msf_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,,15035msf_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
35msf_sync_data_server.analytic_distribution,TRUE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],['name'],HQ + MISSION,analytic.distribution,,Analytic Distribution,Valid,,20036msf_sync_data_server.analytic_distribution,TRUE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],['name'],HQ + MISSION,analytic.distribution,,Analytic Distribution,Valid,,200
36msf_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,,20137msf_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