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

Proposed by jftempo
Status: Merged
Merged at revision: 5993
Proposed branch: lp:~julie-w/unifield-server/US-8001
Merge into: lp:unifield-server
Diff against target: 2031 lines (+1072/-124) (has conflicts)
30 files modified
bin/addons/account_corrections/wizard/analytic_distribution_wizard.py (+18/-31)
bin/addons/account_hq_entries/hq_entries.py (+11/-0)
bin/addons/account_hq_entries/wizard/hq_entries_split.py (+3/-1)
bin/addons/analytic_distribution/account_commitment.py (+16/-2)
bin/addons/analytic_distribution/analytic_account_view.xml (+24/-8)
bin/addons/analytic_distribution/analytic_distribution.py (+27/-9)
bin/addons/analytic_distribution/analytic_line.py (+33/-14)
bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py (+5/-1)
bin/addons/analytic_override/__init__.py (+2/-0)
bin/addons/analytic_override/__openerp__.py (+2/-0)
bin/addons/analytic_override/analytic_account.py (+197/-29)
bin/addons/analytic_override/analytic_line.py (+6/-0)
bin/addons/analytic_override/dest_cc_link.py (+174/-0)
bin/addons/analytic_override/dest_cc_link.xml (+49/-0)
bin/addons/analytic_override/multiple_cc_selection_wizard.py (+56/-0)
bin/addons/analytic_override/multiple_cc_selection_wizard.xml (+30/-0)
bin/addons/financing_contract/financing_contract_account_quadruplet.py (+9/-8)
bin/addons/msf_audittrail/__openerp__.py (+4/-0)
bin/addons/msf_audittrail/data/audittrail_dest_cc_link.yml (+39/-0)
bin/addons/msf_doc_import/msf_import_export.py (+118/-11)
bin/addons/msf_doc_import/msf_import_export_conf.py (+3/-1)
bin/addons/msf_homere_interface/hr_payroll.py (+28/-1)
bin/addons/msf_profile/data/patches.xml (+8/-0)
bin/addons/msf_profile/i18n/fr_MF.po (+152/-3)
bin/addons/msf_profile/msf_profile.py (+17/-0)
bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+1/-0)
bin/addons/sync_client/update.py (+1/-0)
bin/addons/sync_common/common.py (+1/-0)
bin/addons/sync_so/specific_xml_id.py (+24/-0)
bin/osv/orm.py (+14/-5)
Text conflict in bin/addons/msf_audittrail/__openerp__.py
Text conflict in bin/addons/msf_profile/data/patches.xml
Text conflict in bin/addons/msf_profile/msf_profile.py
To merge this branch: bzr merge lp:~julie-w/unifield-server/US-8001
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+402434@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 2020-09-22 13:17:55 +0000
+++ bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2021-05-07 16:01:07 +0000
@@ -283,38 +283,25 @@
283 old_line = self.pool.get('funding.pool.distribution.line').browse(cr, uid, wiz_line.distribution_line_id.id)283 old_line = self.pool.get('funding.pool.distribution.line').browse(cr, uid, wiz_line.distribution_line_id.id)
284284
285 if old_line:285 if old_line:
286 #US-714: For HQ Entries, always create the COR and REV even the period is closed286 # existing line, test modifications
287 original_al_id = ana_line_obj.search(cr, uid, [('distrib_line_id', '=', 'funding.pool.distribution.line,%d'%old_line.id), ('is_reversal', '=', False), ('is_reallocated', '=', False)])287 # for FP, percentage, CC or destination changes regarding contracts
288288 if old_line.analytic_id.id != wiz_line.analytic_id.id \
289 is_HQ_entries = False289 or old_line.percentage != wiz_line.percentage \
290 if original_al_id and len(original_al_id) == 1:290 or old_line.cost_center_id.id != wiz_line.cost_center_id.id \
291 original_al = ana_line_obj.browse(cr, uid, original_al_id[0], context)291 or old_line.destination_id.id != wiz_line.destination_id.id:
292 if original_al.journal_id.type == 'hq':292 # FP account changed or % modified
293 is_HQ_entries = True293 if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [old_line.analytic_id.id]):
294294 raise osv.except_osv(_('Error'), _("Funding pool is on a soft/hard closed contract: %s")%(old_line.analytic_id.code))
295 # In case it's an HQ entries, just generate the REV and COR295
296 if is_HQ_entries:296 if (old_line.cost_center_id.id != wiz_line.cost_center_id.id or
297 to_reverse.append(wiz_line)297 old_line.destination_id.id != wiz_line.destination_id.id or
298 else:298 old_line.percentage != wiz_line.percentage):
299 # existing line, test modifications299 if self._check_period_closed_on_fp_distrib_line(cr, uid, old_line.id, is_HQ_origin=is_HQ_origin):
300 # for FP, percentage, CC or destination changes regarding contracts300 to_reverse.append(wiz_line)
301 if old_line.analytic_id.id != wiz_line.analytic_id.id \301 else:
302 or old_line.percentage != wiz_line.percentage \
303 or old_line.cost_center_id.id != wiz_line.cost_center_id.id \
304 or old_line.destination_id.id != wiz_line.destination_id.id:
305 # FP account changed or % modified
306 if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [old_line.analytic_id.id]):
307 raise osv.except_osv(_('Error'), _("Funding pool is on a soft/hard closed contract: %s")%(old_line.analytic_id.code))
308
309 if (old_line.cost_center_id.id != wiz_line.cost_center_id.id or
310 old_line.destination_id.id != wiz_line.destination_id.id or
311 old_line.percentage != wiz_line.percentage):
312 if self._check_period_closed_on_fp_distrib_line(cr, uid, old_line.id):
313 to_reverse.append(wiz_line)
314 else:
315 to_override.append(wiz_line)
316 elif old_line.analytic_id.id != wiz_line.analytic_id.id:
317 to_override.append(wiz_line)302 to_override.append(wiz_line)
303 elif old_line.analytic_id.id != wiz_line.analytic_id.id:
304 to_override.append(wiz_line)
318305
319 old_line_ok.append(old_line.id)306 old_line_ok.append(old_line.id)
320307
321308
=== modified file 'bin/addons/account_hq_entries/hq_entries.py'
--- bin/addons/account_hq_entries/hq_entries.py 2021-05-05 16:04:38 +0000
+++ bin/addons/account_hq_entries/hq_entries.py 2021-05-07 16:01:07 +0000
@@ -44,6 +44,7 @@
44 res = {}44 res = {}
45 logger = netsvc.Logger()45 logger = netsvc.Logger()
46 ad_obj = self.pool.get('analytic.distribution')46 ad_obj = self.pool.get('analytic.distribution')
47 dest_cc_link_obj = self.pool.get('dest.cc.link')
47 # Search MSF Private Fund element, because it's valid with all accounts48 # Search MSF Private Fund element, because it's valid with all accounts
48 try:49 try:
49 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',50 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
@@ -87,6 +88,13 @@
87 res[line.id] = 'invalid'88 res[line.id] = 'invalid'
88 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: inactive DEST (%s)') % (line.id or '', dest.code or ''))89 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: inactive DEST (%s)') % (line.id or '', dest.code or ''))
89 continue90 continue
91 if line.destination_id and line.cost_center_id and line.date and \
92 dest_cc_link_obj.is_inactive_dcl(cr, uid, line.destination_id.id, line.cost_center_id.id, line.date, context=context):
93 res[line.id] = 'invalid'
94 logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING,
95 _('%s: inactive combination (%s - %s)') %
96 (line.id or '', line.destination_id.code or '', line.cost_center_id.code or ''))
97 continue
90 # G Check98 # G Check
91 if line.analytic_id:99 if line.analytic_id:
92 fp = self.pool.get('account.analytic.account').browse(cr, uid, line.analytic_id.id, context={'date': line.document_date})100 fp = self.pool.get('account.analytic.account').browse(cr, uid, line.analytic_id.id, context={'date': line.document_date})
@@ -485,6 +493,9 @@
485 def _check_cc(self, cr, uid, ids, context=None):493 def _check_cc(self, cr, uid, ids, context=None):
486 """494 """
487 At synchro time sets HQ entry to Not Run if the Cost Center used in the line doesn't exist or is inactive495 At synchro time sets HQ entry to Not Run if the Cost Center used in the line doesn't exist or is inactive
496
497 Note: if the CC is active but the Dest/CC combination is inactive, the sync update is NOT blocked:
498 the HQ entry will be created with an invalid AD to be fixed before validation.
488 """499 """
489 if isinstance(ids, (int, long)):500 if isinstance(ids, (int, long)):
490 ids = [ids]501 ids = [ids]
491502
=== modified file 'bin/addons/account_hq_entries/wizard/hq_entries_split.py'
--- bin/addons/account_hq_entries/wizard/hq_entries_split.py 2020-11-02 17:29:34 +0000
+++ bin/addons/account_hq_entries/wizard/hq_entries_split.py 2021-05-07 16:01:07 +0000
@@ -42,7 +42,9 @@
42 # Process42 # Process
43 for line in self.browse(cr, uid, ids, context=context):43 for line in self.browse(cr, uid, ids, context=context):
44 res[line.id] = {'state_info': False, 'state': 'none'}44 res[line.id] = {'state_info': False, 'state': 'none'}
45 state, info = self.pool.get('analytic.distribution').analytic_state_from_info(cr, uid, line.account_id.id, line.destination_id.id, line.cost_center_id.id, line.analytic_id.id, context=context)45 state, info = self.pool.get('analytic.distribution').analytic_state_from_info(cr, uid, line.account_id.id, line.destination_id.id,
46 line.cost_center_id.id, line.analytic_id.id,
47 posting_date=line.wizard_id.date, context=context)
46 res[line.id].update({'state_info': info, 'state': state,})48 res[line.id].update({'state_info': info, 'state': state,})
47 return res49 return res
4850
4951
=== modified file 'bin/addons/analytic_distribution/account_commitment.py'
--- bin/addons/analytic_distribution/account_commitment.py 2020-05-07 10:01:57 +0000
+++ bin/addons/analytic_distribution/account_commitment.py 2021-05-07 16:01:07 +0000
@@ -169,6 +169,7 @@
169 aal_obj = self.pool.get('account.analytic.line')169 aal_obj = self.pool.get('account.analytic.line')
170 curr_obj = self.pool.get('res.currency')170 curr_obj = self.pool.get('res.currency')
171 user_obj = self.pool.get('res.users')171 user_obj = self.pool.get('res.users')
172 dest_cc_link_obj = self.pool.get('dest.cc.link')
172 # Browse elements if 'date' in vals173 # Browse elements if 'date' in vals
173 if vals.get('date', False):174 if vals.get('date', False):
174 date = vals.get('date')175 date = vals.get('date')
@@ -188,8 +189,21 @@
188 raise osv.except_osv(_('Warning'), _('No analytic distribution found for %s %s') % (cl.account_id.code, cl.initial_amount))189 raise osv.except_osv(_('Warning'), _('No analytic distribution found for %s %s') % (cl.account_id.code, cl.initial_amount))
189 for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:190 for distrib_lines in [distrib.cost_center_lines, distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]:
190 for distrib_line in distrib_lines:191 for distrib_line in distrib_lines:
191 if (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start) or (distrib_line.analytic_id.date and date > distrib_line.analytic_id.date):192 if distrib_line.analytic_id and \
192 raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') % (distrib_line.analytic_id.name,))193 (distrib_line.analytic_id.date_start and date < distrib_line.analytic_id.date_start or
194 distrib_line.analytic_id.date and date >= distrib_line.analytic_id.date):
195 raise osv.except_osv(_('Error'), _('The analytic account %s is not active for given date.') %
196 (distrib_line.analytic_id.name,))
197 dest_cc_tuples = set() # check each Dest/CC combination only once
198 for distrib_cc_l in distrib.cost_center_lines:
199 if distrib_cc_l.analytic_id: # non mandatory field
200 dest_cc_tuples.add((distrib_cc_l.destination_id, distrib_cc_l.analytic_id))
201 for distrib_fp_l in distrib.funding_pool_lines:
202 dest_cc_tuples.add((distrib_fp_l.destination_id, distrib_fp_l.cost_center_id))
203 for dest, cc in dest_cc_tuples:
204 if dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context):
205 raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") %
206 (dest.code or '', cc.code or '', date))
193 # update the dates and fctal amounts of the related analytic lines207 # update the dates and fctal amounts of the related analytic lines
194 context.update({'currency_date': date}) # same date used for doc, posting and source date of all lines208 context.update({'currency_date': date}) # same date used for doc, posting and source date of all lines
195 for aal in cl.analytic_lines:209 for aal in cl.analytic_lines:
196210
=== modified file 'bin/addons/analytic_distribution/analytic_account_view.xml'
--- bin/addons/analytic_distribution/analytic_account_view.xml 2020-10-08 13:11:14 +0000
+++ bin/addons/analytic_distribution/analytic_account_view.xml 2021-05-07 16:01:07 +0000
@@ -132,13 +132,28 @@
132 </field>132 </field>
133 </page>133 </page>
134 <page string="Cost Centers" attrs="{'invisible': [('category', '!=', 'DEST')]}">134 <page string="Cost Centers" attrs="{'invisible': [('category', '!=', 'DEST')]}">
135 <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_ids)"/>135 <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_link_ids)"/>
136 <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/>136 <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"
137 confirm="Do you really want to remove all the Cost Centers selected?"
138 />
137 <separator/>139 <separator/>
138 <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_cc_ids(dest_cc_ids)">140 <group colspan="6" col="4">
139 <tree string="Cost Centers">141 <button icon="gtk-add" string="Add several Cost Centers" colspan="1"
140 <field name="code"/>142 name="open_multiple_cc_selection_wizard" type="object"
141 <field name="name"/>143 context="{'current_destination_id': record_id}"/>
144 <label string="" colspan="3"/>
145 </group>
146 <field name="dest_cc_link_ids" nolabel="1" colspan="4"
147 on_change="on_change_cc_ids(dest_cc_link_ids)"
148 context="{'current_destination_id': record_id}">
149 <tree string="Cost Centers" editable="bottom">
150 <field name="current_id" invisible="1"/>
151 <field name="cc_id" string="Code"
152 on_change="on_change_cc_id(cc_id)"
153 attrs="{'readonly': [('current_id', '!=', False)]}"/>
154 <field name="cc_name" string="Name"/>
155 <field name="active_from"/>
156 <field name="inactive_from"/>
142 </tree>157 </tree>
143 </field>158 </field>
144 </page>159 </page>
@@ -262,9 +277,10 @@
262 <field name="inherit_id" ref="account.view_account_analytic_account_list"/>277 <field name="inherit_id" ref="account.view_account_analytic_account_list"/>
263 <field name="arch" type="xml">278 <field name="arch" type="xml">
264 <tree string="Analytic Accounts" position="replace">279 <tree string="Analytic Accounts" position="replace">
265 <tree toolbar="1" colors="red:(date and date&lt;=current_date or dest_without_cc==True)"280 <tree toolbar="1" colors="red:(date and date&lt;=current_date or dest_without_cc==True);grey:selected_in_dest"
266 string="Analytic Accounts">281 notselectable="selected_in_dest" string="Analytic Accounts">
267 <field name="dest_without_cc" invisible="1"/>282 <field name="dest_without_cc" invisible="1"/>
283 <field name="selected_in_dest" invisible="1"/>
268 <field name="name"/>284 <field name="name"/>
269 <field name="code"/>285 <field name="code"/>
270 <field name="description"/>286 <field name="description"/>
271287
=== modified file 'bin/addons/analytic_distribution/analytic_distribution.py'
--- bin/addons/analytic_distribution/analytic_distribution.py 2020-11-02 17:29:34 +0000
+++ bin/addons/analytic_distribution/analytic_distribution.py 2021-05-07 16:01:07 +0000
@@ -36,13 +36,16 @@
36 if context is None:36 if context is None:
37 context = {}37 context = {}
38 analytic_acc_obj = self.pool.get('account.analytic.account')38 analytic_acc_obj = self.pool.get('account.analytic.account')
39 dest_cc_link_obj = self.pool.get('dest.cc.link')
40 ret = True # by default if either dest or cc is missing
39 if destination_id and cost_center_id:41 if destination_id and cost_center_id:
40 dest = analytic_acc_obj.browse(cr, uid, destination_id, fields_to_fetch=['category', 'allow_all_cc', 'dest_cc_ids'], context=context)42 if analytic_acc_obj.search_exist(cr, uid, [('id', '=', destination_id), ('allow_all_cc', '=', True)], context=context):
41 cc = analytic_acc_obj.browse(cr, uid, cost_center_id, fields_to_fetch=['category'], context=context)43 ret = True
42 if dest and cc and dest.category == 'DEST' and cc.category == 'OC' and not dest.allow_all_cc and \44 elif dest_cc_link_obj.search_exist(cr, uid, [('dest_id', '=', destination_id), ('cc_id', '=', cost_center_id)], context=context):
43 cc.id not in [c.id for c in dest.dest_cc_ids]:45 ret = True
44 return False46 else:
45 return True47 ret = False
48 return ret
4649
47 def check_fp_cc_compatibility(self, cr, uid, fp_id, cost_center_id, context=None):50 def check_fp_cc_compatibility(self, cr, uid, fp_id, cost_center_id, context=None):
48 """51 """
@@ -146,6 +149,7 @@
146 if context is None:149 if context is None:
147 context = {}150 context = {}
148 analytic_acc_obj = self.pool.get('account.analytic.account')151 analytic_acc_obj = self.pool.get('account.analytic.account')
152 dest_cc_link_obj = self.pool.get('dest.cc.link')
149 # Have an analytic distribution on another account than analytic-a-holic account make no sense. So their analytic distribution is valid153 # Have an analytic distribution on another account than analytic-a-holic account make no sense. So their analytic distribution is valid
150 if account_id:154 if account_id:
151 account = self.pool.get('account.account').read(cr, uid, account_id, ['is_analytic_addicted'])155 account = self.pool.get('account.account').read(cr, uid, account_id, ['is_analytic_addicted'])
@@ -153,7 +157,8 @@
153 return 'valid'157 return 'valid'
154 if not distrib_id:158 if not distrib_id:
155 if parent_id:159 if parent_id:
156 return self._get_distribution_state(cr, uid, parent_id, False, account_id, context, amount=amount)160 return self._get_distribution_state(cr, uid, parent_id, False, account_id, context=context,
161 doc_date=doc_date, posting_date=posting_date, manual=manual, amount=amount)
157 return 'none'162 return 'none'
158 distrib = self.browse(cr, uid, distrib_id)163 distrib = self.browse(cr, uid, distrib_id)
159 if not distrib.funding_pool_lines:164 if not distrib.funding_pool_lines:
@@ -183,6 +188,9 @@
183 return 'invalid'188 return 'invalid'
184 if not analytic_acc_obj.is_account_active(fp_line.cost_center_id, posting_date):189 if not analytic_acc_obj.is_account_active(fp_line.cost_center_id, posting_date):
185 return 'invalid'190 return 'invalid'
191 if dest_cc_link_obj.is_inactive_dcl(cr, uid, fp_line.destination_id.id, fp_line.cost_center_id.id,
192 posting_date, context=context):
193 return 'invalid'
186 if doc_date and fp_line.analytic_id and not analytic_acc_obj.is_account_active(fp_line.analytic_id, doc_date):194 if doc_date and fp_line.analytic_id and not analytic_acc_obj.is_account_active(fp_line.analytic_id, doc_date):
187 return 'invalid'195 return 'invalid'
188 if fp_line.destination_id.id not in account.get('destination_ids', []):196 if fp_line.destination_id.id not in account.get('destination_ids', []):
@@ -206,7 +214,7 @@
206 return 'invalid'214 return 'invalid'
207 return 'valid'215 return 'valid'
208216
209 def analytic_state_from_info(self, cr, uid, account_id, destination_id, cost_center_id, analytic_id, context=None):217 def analytic_state_from_info(self, cr, uid, account_id, destination_id, cost_center_id, analytic_id, posting_date=False, context=None):
210 """218 """
211 Give analytic state from the given information.219 Give analytic state from the given information.
212 Return result and some info if needed.220 Return result and some info if needed.
@@ -217,6 +225,7 @@
217 # Prepare some values225 # Prepare some values
218 res = 'valid'226 res = 'valid'
219 info = ''227 info = ''
228 dest_cc_link_obj = self.pool.get('dest.cc.link')
220 account = self.pool.get('account.account').browse(cr, uid, account_id, context=context)229 account = self.pool.get('account.account').browse(cr, uid, account_id, context=context)
221 # DISTRIBUTION VERIFICATION230 # DISTRIBUTION VERIFICATION
222 # Check that destination is compatible with account231 # Check that destination is compatible with account
@@ -225,6 +234,9 @@
225 # Check that Destination and Cost Center are compatible234 # Check that Destination and Cost Center are compatible
226 if not self.check_dest_cc_compatibility(cr, uid, destination_id, cost_center_id, context=context):235 if not self.check_dest_cc_compatibility(cr, uid, destination_id, cost_center_id, context=context):
227 return 'invalid', _('Cost Center not compatible with destination')236 return 'invalid', _('Cost Center not compatible with destination')
237 # Check that their combination is active
238 if posting_date and dest_cc_link_obj.is_inactive_dcl(cr, uid, destination_id, cost_center_id, posting_date, context=context):
239 return 'invalid', _('Inactive DEST/CC combination')
228 # Check that cost center is compatible with FP240 # Check that cost center is compatible with FP
229 if not self.check_fp_cc_compatibility(cr, uid, analytic_id, cost_center_id, context=context):241 if not self.check_fp_cc_compatibility(cr, uid, analytic_id, cost_center_id, context=context):
230 return 'invalid', _('Cost Center not compatible with FP')242 return 'invalid', _('Cost Center not compatible with FP')
@@ -236,10 +248,11 @@
236 def check_cc_distrib_active(self, cr, uid, distrib_br, posting_date=False, prefix='', from_supply=False):248 def check_cc_distrib_active(self, cr, uid, distrib_br, posting_date=False, prefix='', from_supply=False):
237 """249 """
238 Checks the Cost Center Distribution Lines of the distribution in param.:250 Checks the Cost Center Distribution Lines of the distribution in param.:
239 raises an error if the CC or the Dest. used is not active at the posting date selected (or today's date)251 raises an error if the CC, the Dest., or their combination is not active at the posting date selected (or today's date)
240 If needed a "prefix" can be added to the error message.252 If needed a "prefix" can be added to the error message.
241 """253 """
242 cc_distrib_line_obj = self.pool.get('cost.center.distribution.line')254 cc_distrib_line_obj = self.pool.get('cost.center.distribution.line')
255 dest_cc_link_obj = self.pool.get('dest.cc.link')
243 if distrib_br:256 if distrib_br:
244 if not posting_date:257 if not posting_date:
245 posting_date = time.strftime('%Y-%m-%d')258 posting_date = time.strftime('%Y-%m-%d')
@@ -257,6 +270,11 @@
257 else:270 else:
258 raise osv.except_osv(_('Error'), _('%sDestination %s is either inactive at the date %s, or it allows no Cost Center.') %271 raise osv.except_osv(_('Error'), _('%sDestination %s is either inactive at the date %s, or it allows no Cost Center.') %
259 (prefix, cline.destination_id.code or '', posting_date))272 (prefix, cline.destination_id.code or '', posting_date))
273 if cline.destination_id and cline.analytic_id and \
274 dest_cc_link_obj.is_inactive_dcl(cr, uid, cline.destination_id.id, cline.analytic_id.id, posting_date):
275 raise osv.except_osv(_('Error'), _("%sThe combination \"%s - %s\" is not active at this date: %s") %
276 (prefix, cline.destination_id.code or '', cline.analytic_id.code or '', posting_date))
277
260278
261279
262analytic_distribution()280analytic_distribution()
263281
=== modified file 'bin/addons/analytic_distribution/analytic_line.py'
--- bin/addons/analytic_distribution/analytic_line.py 2020-12-01 17:29:45 +0000
+++ bin/addons/analytic_distribution/analytic_line.py 2021-05-07 16:01:07 +0000
@@ -413,6 +413,8 @@
413 ids = [ids]413 ids = [ids]
414 # Prepare some value414 # Prepare some value
415 ad_obj = self.pool.get('analytic.distribution')415 ad_obj = self.pool.get('analytic.distribution')
416 dest_cc_link_obj = self.pool.get('dest.cc.link')
417 period_obj = self.pool.get('account.period')
416 account = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['category', 'date_start', 'date'], context=context)418 account = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['category', 'date_start', 'date'], context=context)
417 account_type = account and account.get('category', False) or False419 account_type = account and account.get('category', False) or False
418 res = []420 res = []
@@ -422,6 +424,9 @@
422 date_start = account and account.get('date_start', False) or False424 date_start = account and account.get('date_start', False) or False
423 date_stop = account and account.get('date', False) or False425 date_stop = account and account.get('date', False) or False
424 # Date verification for all lines and fetch all necessary elements sorted by analytic distribution426 # Date verification for all lines and fetch all necessary elements sorted by analytic distribution
427 cmp_dates = {}
428 wiz_period_open = period_obj.search_exist(cr, uid, [('date_start', '<=', wiz_date), ('date_stop', '>=', wiz_date),
429 ('special', '=', False), ('state', '=', 'draft')], context=context)
425 for aline in self.browse(cr, uid, ids):430 for aline in self.browse(cr, uid, ids):
426 # UTP-800: Change date comparison regarding FP. If FP, use document date. Otherwise use date.431 # UTP-800: Change date comparison regarding FP. If FP, use document date. Otherwise use date.
427 aline_cmp_date = aline.date432 aline_cmp_date = aline.date
@@ -435,13 +440,25 @@
435 if aline.journal_id.type == 'hq' or aline.period_id and aline.period_id.state in ['done', 'mission-closed']:440 if aline.journal_id.type == 'hq' or aline.period_id and aline.period_id.state in ['done', 'mission-closed']:
436 aline_cmp_date = wiz_date441 aline_cmp_date = wiz_date
437 # these lines will be reverted, check if the reverted line is active442 # these lines will be reverted, check if the reverted line is active
438 oc_dest_date_start = max(aline.cost_center_id.date_start, aline.destination_id.date_start)443 if not wiz_period_open:
439 oc_dest_date_stop = min(aline.cost_center_id.date or '9999-01-01', aline.destination_id.date or '9999-01-01')
440 if (oc_dest_date_start and wiz_date < oc_dest_date_start) or (oc_dest_date_stop and wiz_date >= oc_dest_date_stop):
441 expired_date_ids.append(aline.id)444 expired_date_ids.append(aline.id)
445 else:
446 oc_dest_date_start = max(aline.cost_center_id.date_start, aline.destination_id.date_start)
447 oc_dest_date_stop = min(aline.cost_center_id.date or '9999-01-01', aline.destination_id.date or '9999-01-01')
448 if (oc_dest_date_start and wiz_date < oc_dest_date_start) or (oc_dest_date_stop and wiz_date >= oc_dest_date_stop):
449 expired_date_ids.append(aline.id)
450 else:
451 # check the Dest/CC link validity with the original Dest and CC which will be used in the REV
452 destination_id = aline.destination_id and aline.destination_id.id or False
453 cost_center_id = aline.cost_center_id and aline.cost_center_id.id or False
454 if destination_id and cost_center_id and \
455 dest_cc_link_obj.is_inactive_dcl(cr, uid, destination_id, cost_center_id, wiz_date, context=context):
456 expired_date_ids.append(aline.id)
442 if (date_start and aline_cmp_date < date_start) or (date_stop and aline_cmp_date >= date_stop):457 if (date_start and aline_cmp_date < date_start) or (date_stop and aline_cmp_date >= date_stop):
443 expired_date_ids.append(aline.id)458 expired_date_ids.append(aline.id)
459 cmp_dates[aline.id] = aline_cmp_date
444 # Process regarding account_type460 # Process regarding account_type
461 ids = [i for i in ids if i not in expired_date_ids] # exclude the AJI in expired_date_ids
445 if account_type == 'OC':462 if account_type == 'OC':
446 for aline in self.browse(cr, uid, ids):463 for aline in self.browse(cr, uid, ids):
447 # Verify that:464 # Verify that:
@@ -449,10 +466,11 @@
449 check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id])466 check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id])
450 if check_accounts and aline.account_id.id in check_accounts:467 if check_accounts and aline.account_id.id in check_accounts:
451 continue468 continue
452 if ad_obj.check_dest_cc_compatibility(cr, uid, aline.destination_id and aline.destination_id.id or False,469 dest_id = aline.destination_id and aline.destination_id.id or False
453 account_id, context=context):470 if ad_obj.check_dest_cc_compatibility(cr, uid, dest_id, account_id, context=context) and \
454 if ad_obj.check_fp_cc_compatibility(cr, uid, aline.account_id.id, account_id, context=context):471 ad_obj.check_fp_cc_compatibility(cr, uid, aline.account_id.id, account_id, context=context) and \
455 res.append(aline.id)472 not dest_cc_link_obj.is_inactive_dcl(cr, uid, dest_id, account_id, cmp_dates[aline.id], context=context):
473 res.append(aline.id)
456 elif account_type == 'FUNDING':474 elif account_type == 'FUNDING':
457 # Browse all analytic line to verify them475 # Browse all analytic line to verify them
458 for aline in self.browse(cr, uid, ids):476 for aline in self.browse(cr, uid, ids):
@@ -475,19 +493,16 @@
475 for aline in self.browse(cr, uid, ids, context=context):493 for aline in self.browse(cr, uid, ids, context=context):
476 # the following check is included into check_fp_acc_dest_compatibility:494 # the following check is included into check_fp_acc_dest_compatibility:
477 # account_id in [x.id for x in aline.general_account_id.destination_ids]495 # 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,496 cc_id = aline.cost_center_id and aline.cost_center_id.id or False
479 context=context) and \497 if ad_obj.check_dest_cc_compatibility(cr, uid, account_id, cc_id, context=context) and \
480 ad_obj.check_fp_acc_dest_compatibility(cr, uid, aline.account_id.id, aline.general_account_id.id,498 ad_obj.check_fp_acc_dest_compatibility(cr, uid, aline.account_id.id, aline.general_account_id.id,
481 account_id, context=context):499 account_id, context=context) and \
500 not dest_cc_link_obj.is_inactive_dcl(cr, uid, account_id, cc_id, cmp_dates[aline.id], context=context):
482 res.append(aline.id)501 res.append(aline.id)
483 else:502 else:
484 # Case of FREE1 and FREE2 lines503 # Case of FREE1 and FREE2 lines
485 for i in ids:504 for i in ids:
486 res.append(i)505 res.append(i)
487 # Delete elements that are in expired_date_ids
488 for e in expired_date_ids:
489 if e in res:
490 res.remove(e)
491 return res506 return res
492507
493 def check_dest_cc_fp_compatibility(self, cr, uid, ids,508 def check_dest_cc_fp_compatibility(self, cr, uid, ids,
@@ -513,6 +528,7 @@
513 new_cc_id, new_cc_br,528 new_cc_id, new_cc_br,
514 new_fp_id, new_fp_br):529 new_fp_id, new_fp_br):
515 ad_obj = self.pool.get('analytic.distribution')530 ad_obj = self.pool.get('analytic.distribution')
531 dest_cc_link_obj = self.pool.get('dest.cc.link')
516 if not general_account_br.is_analytic_addicted:532 if not general_account_br.is_analytic_addicted:
517 res.append((id, entry_sequence, ''))533 res.append((id, entry_sequence, ''))
518 return False534 return False
@@ -549,6 +565,9 @@
549 if not check_date(new_cc_br, posting_date):565 if not check_date(new_cc_br, posting_date):
550 res.append((id, entry_sequence, _('CC date')))566 res.append((id, entry_sequence, _('CC date')))
551 return False567 return False
568 if new_dest_id and new_cc_id and dest_cc_link_obj.is_inactive_dcl(cr, uid, new_dest_id, new_cc_id, posting_date, context=context):
569 res.append((id, entry_sequence, _('DEST/CC combination date')))
570 return False
552 if new_fp_id != msf_pf_id and not \571 if new_fp_id != msf_pf_id and not \
553 check_date(new_fp_br, posting_date):572 check_date(new_fp_br, posting_date):
554 res.append((id, entry_sequence, _('FP date')))573 res.append((id, entry_sequence, _('FP date')))
555574
=== modified file 'bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py'
--- bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2021-04-26 09:35:14 +0000
+++ bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2021-05-07 16:01:07 +0000
@@ -1008,6 +1008,7 @@
1008 if isinstance(ids, (int, long)):1008 if isinstance(ids, (int, long)):
1009 ids = [ids]1009 ids = [ids]
1010 distrib_obj = self.pool.get('analytic.distribution')1010 distrib_obj = self.pool.get('analytic.distribution')
1011 dest_cc_link_obj = self.pool.get('dest.cc.link')
1011 for w in self.browse(cr, uid, ids):1012 for w in self.browse(cr, uid, ids):
1012 # UF-16781013 # UF-1678
1013 # For Cost center and destination analytic accounts, check is done on POSTING date. It HAVE TO BE in context to be well processed (filter_active is a function that need a context)1014 # For Cost center and destination analytic accounts, check is done on POSTING date. It HAVE TO BE in context to be well processed (filter_active is a function that need a context)
@@ -1021,6 +1022,9 @@
1021 if not fpline.destination_id.filter_active:1022 if not fpline.destination_id.filter_active:
1022 raise osv.except_osv(_('Error'), _('Destination %s is either inactive at the date %s, or it allows no Cost Center.')1023 raise osv.except_osv(_('Error'), _('Destination %s is either inactive at the date %s, or it allows no Cost Center.')
1023 % (fpline.destination_id.code or '', w.posting_date))1024 % (fpline.destination_id.code or '', w.posting_date))
1025 if dest_cc_link_obj.is_inactive_dcl(cr, uid, fpline.destination_id.id, fpline.cost_center_id.id, w.posting_date):
1026 raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active at this date: %s") %
1027 (fpline.destination_id.code or '', fpline.cost_center_id.code or '', w.posting_date))
1024 # UF-16781028 # UF-1678
1025 # For funding pool analytic account, check is done on DOCUMENT date. It HAVE TO BE in context to be well processed (filter_active is a function that need a context)1029 # For funding pool analytic account, check is done on DOCUMENT date. It HAVE TO BE in context to be well processed (filter_active is a function that need a context)
1026 if w.distribution_id and w.document_date:1030 if w.distribution_id and w.document_date:
@@ -1081,7 +1085,7 @@
1081 self.wizard_verifications(cr, uid, wiz.id, context=context)1085 self.wizard_verifications(cr, uid, wiz.id, context=context)
1082 # And do distribution creation if necessary1086 # And do distribution creation if necessary
1083 distrib_id = wiz.distribution_id and wiz.distribution_id.id or False1087 distrib_id = wiz.distribution_id and wiz.distribution_id.id or False
1084 if not distrib_id:1088 if not distrib_id or not self.pool.get('analytic.distribution').exists(cr, uid, distrib_id, context=context):
1085 # create a new analytic distribution1089 # create a new analytic distribution
1086 analytic_vals = {}1090 analytic_vals = {}
1087 if wiz.partner_type:#UF-2138: added the ref to partner type of FO/PO1091 if wiz.partner_type:#UF-2138: added the ref to partner type of FO/PO
10881092
=== modified file 'bin/addons/analytic_override/__init__.py'
--- bin/addons/analytic_override/__init__.py 2014-03-14 09:40:12 +0000
+++ bin/addons/analytic_override/__init__.py 2021-05-07 16:01:07 +0000
@@ -22,5 +22,7 @@
22import analytic_distribution22import analytic_distribution
23import analytic_account23import analytic_account
24import analytic_line24import analytic_line
25import dest_cc_link
26import multiple_cc_selection_wizard
2527
26# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:28# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
27\ No newline at end of file29\ No newline at end of file
2830
=== modified file 'bin/addons/analytic_override/__openerp__.py'
--- bin/addons/analytic_override/__openerp__.py 2014-10-29 13:47:46 +0000
+++ bin/addons/analytic_override/__openerp__.py 2021-05-07 16:01:07 +0000
@@ -32,6 +32,8 @@
32 "init_xml" : [],32 "init_xml" : [],
33 "update_xml": [33 "update_xml": [
34 'security/ir.model.access.csv',34 'security/ir.model.access.csv',
35 'dest_cc_link.xml',
36 'multiple_cc_selection_wizard.xml',
35 ],37 ],
36 'test': [],38 'test': [],
37 'demo_xml': [39 'demo_xml': [
3840
=== modified file 'bin/addons/analytic_override/analytic_account.py'
--- bin/addons/analytic_override/analytic_account.py 2020-10-30 17:31:31 +0000
+++ bin/addons/analytic_override/analytic_account.py 2021-05-07 16:01:07 +0000
@@ -40,8 +40,8 @@
40 if context is None:40 if context is None:
41 context = {}41 context = {}
42 res = {}42 res = {}
43 for a in self.browse(cr, uid, ids, fields_to_fetch=['category', 'type', 'allow_all_cc', 'dest_cc_ids'], context=context):43 for a in self.browse(cr, uid, ids, fields_to_fetch=['category', 'type', 'allow_all_cc', 'dest_cc_link_ids'], context=context):
44 if a.category == 'DEST' and a.type == 'normal' and not a.allow_all_cc and not a.dest_cc_ids:44 if a.category == 'DEST' and a.type != 'view' and not a.allow_all_cc and not a.dest_cc_link_ids:
45 res[a.id] = True45 res[a.id] = True
46 else:46 else:
47 res[a.id] = False47 res[a.id] = False
@@ -95,7 +95,7 @@
95 arg.append(('category', '!=', 'DEST'))95 arg.append(('category', '!=', 'DEST'))
96 arg.append(('type', '=', 'view'))96 arg.append(('type', '=', 'view'))
97 arg.append(('allow_all_cc', '=', True))97 arg.append(('allow_all_cc', '=', True))
98 arg.append(('dest_cc_ids', '!=', False))98 arg.append(('dest_cc_link_ids', '!=', False))
99 # filter: inactive99 # filter: inactive
100 elif x[0] == 'filter_active' and x[2] is False:100 elif x[0] == 'filter_active' and x[2] is False:
101 arg.append('|')101 arg.append('|')
@@ -106,9 +106,9 @@
106 arg.append('&')106 arg.append('&')
107 arg.append('&')107 arg.append('&')
108 arg.append(('category', '=', 'DEST'))108 arg.append(('category', '=', 'DEST'))
109 arg.append(('type', '=', 'normal'))109 arg.append(('type', '!=', 'view'))
110 arg.append(('allow_all_cc', '=', False))110 arg.append(('allow_all_cc', '=', False))
111 arg.append(('dest_cc_ids', '=', False))111 arg.append(('dest_cc_link_ids', '=', False))
112 return arg112 return arg
113113
114 def _get_fake(self, cr, uid, ids, *a, **b):114 def _get_fake(self, cr, uid, ids, *a, **b):
@@ -282,11 +282,20 @@
282 cc = arg[2]282 cc = arg[2]
283 if operator != '=' or not isinstance(cc, (int, long)):283 if operator != '=' or not isinstance(cc, (int, long)):
284 raise osv.except_osv(_('Error'), _('Filter not implemented on Destinations.'))284 raise osv.except_osv(_('Error'), _('Filter not implemented on Destinations.'))
285 all_dest_ids = self.search(cr, uid, [('category', '=', 'DEST')], context=context)285 if not cc:
286 compatible_dest_ids = []286 # by default if no CC is selected display only the Destinations compatible with all CC
287 for dest in self.browse(cr, uid, all_dest_ids, fields_to_fetch=['allow_all_cc', 'dest_cc_ids'], context=context):287 compatible_dest_ids = self.search(cr, uid, [('category', '=', 'DEST'),
288 if dest.allow_all_cc or (cc and cc in [c.id for c in dest.dest_cc_ids]):288 ('type', '!=', 'view'),
289 compatible_dest_ids.append(dest.id)289 ('allow_all_cc', '=', True)], context=context)
290 else:
291 compatible_dest_sql = """
292 SELECT id
293 FROM account_analytic_account
294 WHERE category = 'DEST' AND type != 'view'
295 AND (allow_all_cc = 't' OR id IN (SELECT dest_id FROM dest_cc_link WHERE cc_id = %s));
296 """
297 cr.execute(compatible_dest_sql, (cc,))
298 compatible_dest_ids = [x[0] for x in cr.fetchall()]
290 dom.append(('id', 'in', compatible_dest_ids))299 dom.append(('id', 'in', compatible_dest_ids))
291 return dom300 return dom
292301
@@ -442,6 +451,54 @@
442 }451 }
443 return res452 return res
444453
454 def _get_selected_in_dest(self, cr, uid, cc_ids, name=False, args=False, context=None):
455 """
456 Returns True for the Cost Centers already selected in the Destination:
457 they will be displayed in grey in the list and won't be re-selectable.
458 """
459 if context is None:
460 context = {}
461 if isinstance(cc_ids, (int, long)):
462 cc_ids = [cc_ids]
463 selected = []
464 dest_id = context.get('current_destination_id') or False
465 if dest_id:
466 dest = self.browse(cr, uid, dest_id, fields_to_fetch=['dest_cc_link_ids'], context=context)
467 selected = [dest_cc_link.cc_id.id for dest_cc_link in dest.dest_cc_link_ids]
468 res = {}
469 for cc_id in cc_ids:
470 res[cc_id] = cc_id in selected
471 return res
472
473 def _get_dest_cc_link_dates(self, cr, uid, ids, field_name, args, context=None):
474 """
475 Returns a dict with key = id of the analytic account,
476 and value = dict with dest_cc_link_active_from and dest_cc_link_inactive_from dates separated by commas (String).
477 Note that the date format is the same in EN and FR, and that empty dates are not ignored.
478 E.g.: '2021-03-02,2021-03-01,,2021-03-03,'
479
480 This is used in Destination Import Tools, in particular for the Export of existing entries used as examples.
481 """
482 if context is None:
483 context = {}
484 if isinstance(ids, (int, long)):
485 ids = [ids]
486 res = {}
487 for a in self.browse(cr, uid, ids, fields_to_fetch=['category', 'dest_cc_link_ids'], context=context):
488 active_date_list = []
489 inactive_date_list = []
490 if a.category == 'DEST':
491 for cc_link in a.dest_cc_link_ids:
492 active_date_str = "%s" % (cc_link.active_from or "")
493 active_date_list.append(active_date_str)
494 inactive_date_str = "%s" % (cc_link.inactive_from or "")
495 inactive_date_list.append(inactive_date_str)
496 res[a.id] = {
497 'dest_cc_link_active_from': ",".join(active_date_list),
498 'dest_cc_link_inactive_from': ",".join(inactive_date_list),
499 }
500 return res
501
445 _columns = {502 _columns = {
446 'name': fields.char('Name', size=128, required=True, translate=1),503 'name': fields.char('Name', size=128, required=True, translate=1),
447 'code': fields.char('Code', size=24),504 'code': fields.char('Code', size=24),
@@ -463,6 +520,17 @@
463 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel',520 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel',
464 'destination_id', 'cost_center_id', string='Cost Centers',521 'destination_id', 'cost_center_id', string='Cost Centers',
465 domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"),522 domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"),
523 'dest_cc_link_ids': fields.one2many('dest.cc.link', 'dest_id', string="Cost Centers", required=False),
524 'dest_cc_link_active_from': fields.function(_get_dest_cc_link_dates, method=True, type='char',
525 store=False, readonly=True,
526 string='Activation Combination Dest / CC from',
527 help="Technical field used for Import Tools only",
528 multi="dest_cc_link_dates"),
529 'dest_cc_link_inactive_from': fields.function(_get_dest_cc_link_dates, method=True, type='char',
530 store=False, readonly=True,
531 string='Inactivation Combination Dest / CC from',
532 help="Technical field used for Import Tools only",
533 multi="dest_cc_link_dates"),
466 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"), # for the Destinations534 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"), # for the Destinations
467 'allow_all_cc_with_fp': fields.boolean(string="Allow all Cost Centers"), # for the Funding Pools535 'allow_all_cc_with_fp': fields.boolean(string="Allow all Cost Centers"), # for the Funding Pools
468 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,536 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False,
@@ -498,6 +566,8 @@
498 'fp_account_ids': fields.many2many('account.account', 'fp_account_rel', 'fp_id', 'account_id', string='G/L Accounts',566 'fp_account_ids': fields.many2many('account.account', 'fp_account_rel', 'fp_id', 'account_id', string='G/L Accounts',
499 domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True), ('active', '=', 't')]",567 domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True), ('active', '=', 't')]",
500 help="G/L accounts linked to the Funding Pool", order_by='code'),568 help="G/L accounts linked to the Funding Pool", order_by='code'),
569 'selected_in_dest': fields.function(_get_selected_in_dest, string='Selected in Destination', method=True,
570 type='boolean', store=False),
501 }571 }
502572
503 _defaults ={573 _defaults ={
@@ -577,39 +647,49 @@
577 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]647 res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')]
578 return res648 return res
579649
580 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):650 def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, cc_ids, acc_type='destination', field_name='allow_all_cc',
651 m2m=False, context=None):
581 """652 """
582 If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected,653 If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected,
583 informs him that he has to remove the CC first654 informs him that he has to remove the CC first
584 (acc_type = name of the Analytic Account Type to which the CC are linked, displayed in the warning msg)655 (acc_type = name of the Analytic Account Type to which the CC are linked, displayed in the warning msg)
585 """656 """
586 res = {}657 res = {}
587 if allow_all_cc and cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]658 if allow_all_cc:
588 # NOTE: the msg is stored in a variable on purpose, otherwise the ".po" translation files would wrongly contain Python code659 if m2m:
589 msg = 'Please remove the Cost Centers linked to the %s before ticking this box.' % acc_type.title()660 cc_filled_in = cc_ids and cc_ids[0][2] or False # e.g. [(6, 0, [1, 2])]
590 warning = {661 else:
591 'title': _('Warning!'),662 cc_filled_in = cc_ids or False
592 'message': _(msg)663 if cc_filled_in:
593 }664 # NOTE: the msg is stored in a variable on purpose, otherwise the ".po" translation files would wrongly contain Python code
594 res['warning'] = warning665 msg = 'Please remove the Cost Centers linked to the %s before ticking this box.' % acc_type.title()
595 res['value'] = {field_name: False, }666 warning = {
667 'title': _('Warning!'),
668 'message': _(msg)
669 }
670 res['warning'] = warning
671 res['value'] = {field_name: False, }
596 return res672 return res
597673
598 def on_change_allow_all_cc_with_fp(self, cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, context=None):674 def on_change_allow_all_cc_with_fp(self, cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, context=None):
599 return self.on_change_allow_all_cc(cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, acc_type='funding pool',675 return self.on_change_allow_all_cc(cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, acc_type='funding pool',
600 field_name='allow_all_cc_with_fp', context=context)676 field_name='allow_all_cc_with_fp', m2m=True, context=context)
601677
602 def on_change_cc_ids(self, cr, uid, ids, cc_ids, field_name='allow_all_cc', context=None):678 def on_change_cc_ids(self, cr, uid, ids, cc_ids, field_name='allow_all_cc', m2m=False, context=None):
603 """679 """
604 If at least a CC is selected, unticks the box "Allow all Cost Centers"680 If at least a CC is selected, unticks the box "Allow all Cost Centers"
605 """681 """
606 res = {}682 res = {}
607 if cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])]683 if m2m:
684 cc_filled_in = cc_ids and cc_ids[0][2] or False # e.g. [(6, 0, [1, 2])]
685 else:
686 cc_filled_in = cc_ids or False
687 if cc_filled_in:
608 res['value'] = {field_name: False, }688 res['value'] = {field_name: False, }
609 return res689 return res
610690
611 def on_change_cc_with_fp(self, cr, uid, ids, cost_center_ids, context=None):691 def on_change_cc_with_fp(self, cr, uid, ids, cost_center_ids, context=None):
612 return self.on_change_cc_ids(cr, uid, ids, cost_center_ids, field_name='allow_all_cc_with_fp', context=context)692 return self.on_change_cc_ids(cr, uid, ids, cost_center_ids, field_name='allow_all_cc_with_fp', m2m=True, context=context)
613693
614 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):694 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
615 if not context:695 if not context:
@@ -685,6 +765,7 @@
685 if vals['category'] != 'DEST':765 if vals['category'] != 'DEST':
686 vals['destination_ids'] = [(6, 0, [])]766 vals['destination_ids'] = [(6, 0, [])]
687 vals['dest_cc_ids'] = [(6, 0, [])]767 vals['dest_cc_ids'] = [(6, 0, [])]
768 vals['dest_cc_link_ids'] = [] # related dest.cc.links (if any) are deleted in _clean_dest_cc_link
688 vals['allow_all_cc'] = False # default value769 vals['allow_all_cc'] = False # default value
689 if vals['category'] != 'FUNDING':770 if vals['category'] != 'FUNDING':
690 vals['tuple_destination_account_ids'] = [(6, 0, [])]771 vals['tuple_destination_account_ids'] = [(6, 0, [])]
@@ -727,6 +808,7 @@
727 default['tuple_destination_summary'] = []808 default['tuple_destination_summary'] = []
728 default['line_ids'] = []809 default['line_ids'] = []
729 default['dest_cc_ids'] = []810 default['dest_cc_ids'] = []
811 default['dest_cc_link_ids'] = []
730 return super(analytic_account, self).copy(cr, uid, a_id, default, context=context)812 return super(analytic_account, self).copy(cr, uid, a_id, default, context=context)
731813
732 def _check_name_unicity(self, cr, uid, ids, context=None):814 def _check_name_unicity(self, cr, uid, ids, context=None):
@@ -785,6 +867,57 @@
785 self.log(cr, uid, analytic_account_id, _('At least one Analytic Journal Item using the Analytic Account %s '867 self.log(cr, uid, analytic_account_id, _('At least one Analytic Journal Item using the Analytic Account %s '
786 'has a Posting Date outside the activation dates selected.') % (analytic_acc.code))868 'has a Posting Date outside the activation dates selected.') % (analytic_acc.code))
787869
870 def _clean_dest_cc_link(self, cr, uid, ids, vals, context=None):
871 """
872 In case Dest CC Links are reset in an analytic account: deletes the related existing Dest CC Links if any.
873 Probable UC: Dest CC Links selected on a destination, then account changed to another category.
874 """
875 if context is None:
876 context = {}
877 if isinstance(ids, (int, long)):
878 ids = [ids]
879 if 'dest_cc_link_ids' in vals and not vals['dest_cc_link_ids']:
880 dcl_ids = []
881 for analytic_acc in self.browse(cr, uid, ids, fields_to_fetch=['dest_cc_link_ids'], context=context):
882 dcl_ids.extend([dcl.id for dcl in analytic_acc.dest_cc_link_ids])
883 if dcl_ids:
884 self.pool.get('dest.cc.link').unlink(cr, uid, dcl_ids, context=context)
885 return True
886
887 def _dest_cc_ids_must_be_updated(self, vals, context):
888 """
889 Returns True if dest_cc_ids in vals must be changed to dest_cc_link_ids (the goal of this method is to ensure
890 that the same condition is used everywhere and that the UC where all CC are removed is taken into account)
891 """
892 if context and vals and context.get('sync_update_execution') and vals.get('dest_cc_ids') and vals['dest_cc_ids'][0][2] is not None:
893 return True
894 return False
895
896 def _update_synched_dest_cc_ids(self, cr, uid, dest_ids, vals, context):
897 """
898 For synch made before or while US-7295 was released: changes the dest_cc_ids into dest_cc_link_ids
899 """
900 if self._dest_cc_ids_must_be_updated(vals, context):
901 dest_cc_link_obj = self.pool.get('dest.cc.link')
902 if isinstance(dest_ids, (int, long)):
903 dest_ids = [dest_ids]
904 for dest_id in dest_ids:
905 dest = self.browse(cr, uid, dest_id, fields_to_fetch=['dest_cc_link_ids'], context=context)
906 # note: after US-7295 patch script no instance has any dest_cc_ids, all CC links are necessarily dest.cc.link
907 current_cc_ids = [dest_cc_link.cc_id.id for dest_cc_link in dest.dest_cc_link_ids]
908 new_cc_ids = vals['dest_cc_ids'][0][2] or [] # take into account the UC where all CC are removed
909 # delete the CC to be deleted
910 cc_to_be_deleted = [c for c in current_cc_ids if c not in new_cc_ids]
911 if cc_to_be_deleted:
912 dcl_to_be_deleted = dest_cc_link_obj.search(cr, uid, [('dest_id', '=', dest_id), ('cc_id', 'in', cc_to_be_deleted)],
913 order='NO_ORDER', context=context)
914 dest_cc_link_obj.unlink(cr, uid, dcl_to_be_deleted, context=context)
915 # create the CC to be created
916 for cc_id in [c for c in new_cc_ids if c not in current_cc_ids]:
917 dest_cc_link_obj.create(cr, uid, {'dest_id': dest_id, 'cc_id': cc_id}, context=context)
918 del vals['dest_cc_ids']
919 return True
920
788 def create(self, cr, uid, vals, context=None):921 def create(self, cr, uid, vals, context=None):
789 """922 """
790 Some verifications before analytic account creation923 Some verifications before analytic account creation
@@ -797,6 +930,9 @@
797 self._check_date(vals)930 self._check_date(vals)
798 self.set_funding_pool_parent(cr, uid, vals)931 self.set_funding_pool_parent(cr, uid, vals)
799 vals = self.remove_inappropriate_links(vals, context=context)932 vals = self.remove_inappropriate_links(vals, context=context)
933 vals_copy = vals.copy()
934 if self._dest_cc_ids_must_be_updated(vals, context):
935 del vals['dest_cc_ids'] # replaced by dest_cc_link_ids in _update_synched_dest_cc_ids (called after create as it uses the new id)
800 # for auto instance creation, fx gain has been stored, need HQ sync + instance sync to get CC936 # for auto instance creation, fx gain has been stored, need HQ sync + instance sync to get CC
801 if context.get('sync_update_execution') and vals.get('code') and vals.get('category') == 'OC':937 if context.get('sync_update_execution') and vals.get('code') and vals.get('category') == 'OC':
802 param = self.pool.get('ir.config_parameter')938 param = self.pool.get('ir.config_parameter')
@@ -804,9 +940,11 @@
804 if init_cc_fx_gain and vals.get('code') == init_cc_fx_gain:940 if init_cc_fx_gain and vals.get('code') == init_cc_fx_gain:
805 vals['for_fx_gain_loss'] = True941 vals['for_fx_gain_loss'] = True
806 param.set_param(cr, 1, 'INIT_CC_FX_GAIN', '')942 param.set_param(cr, 1, 'INIT_CC_FX_GAIN', '')
807 ids = super(analytic_account, self).create(cr, uid, vals, context=context)943 analytic_acc_id = super(analytic_account, self).create(cr, uid, vals, context=context)
808 self._check_name_unicity(cr, uid, ids, context=context)944 self._check_name_unicity(cr, uid, analytic_acc_id, context=context)
809 return ids945 self._clean_dest_cc_link(cr, uid, analytic_acc_id, vals, context=context)
946 self._update_synched_dest_cc_ids(cr, uid, analytic_acc_id, vals_copy, context)
947 return analytic_acc_id
810948
811 def write(self, cr, uid, ids, vals, context=None):949 def write(self, cr, uid, ids, vals, context=None):
812 """950 """
@@ -822,7 +960,9 @@
822 self._check_date(vals)960 self._check_date(vals)
823 self.set_funding_pool_parent(cr, uid, vals)961 self.set_funding_pool_parent(cr, uid, vals)
824 vals = self.remove_inappropriate_links(vals, context=context)962 vals = self.remove_inappropriate_links(vals, context=context)
963 self._update_synched_dest_cc_ids(cr, uid, ids, vals, context)
825 res = super(analytic_account, self).write(cr, uid, ids, vals, context=context)964 res = super(analytic_account, self).write(cr, uid, ids, vals, context=context)
965 self._clean_dest_cc_link(cr, uid, ids, vals, context=context)
826 self.check_access_rule(cr, uid, ids, 'write', context=context)966 self.check_access_rule(cr, uid, ids, 'write', context=context)
827 if context.get('from_web', False) or context.get('from_import_menu', False):967 if context.get('from_web', False) or context.get('from_import_menu', False):
828 cat_instance = self.read(cr, uid, ids, ['category', 'instance_id', 'is_pf'], context=context)[0]968 cat_instance = self.read(cr, uid, ids, ['category', 'instance_id', 'is_pf'], context=context)[0]
@@ -919,9 +1059,15 @@
9191059
920 def button_dest_cc_clear(self, cr, uid, ids, context=None):1060 def button_dest_cc_clear(self, cr, uid, ids, context=None):
921 """1061 """
922 Removes all Cost Centers selected in the Destination view1062 Removes all Dest / CC combinations selected in the Cost Centers tab of the Destination form
923 """1063 """
924 self.write(cr, uid, ids, {'dest_cc_ids': [(6, 0, [])]}, context=context)1064 if context is None:
1065 context = {}
1066 dest_cc_link_obj = self.pool.get('dest.cc.link')
1067 for dest in self.browse(cr, uid, ids, fields_to_fetch=['dest_cc_link_ids'], context=context):
1068 dest_cc_link_ids = [dcl.id for dcl in dest.dest_cc_link_ids]
1069 if dest_cc_link_ids:
1070 dest_cc_link_obj.unlink(cr, uid, dest_cc_link_ids, context=context)
925 return True1071 return True
9261072
927 def button_dest_clear(self, cr, uid, ids, context=None):1073 def button_dest_clear(self, cr, uid, ids, context=None):
@@ -968,6 +1114,28 @@
968 return False1114 return False
969 return True1115 return True
9701116
1117 def open_multiple_cc_selection_wizard(self, cr, uid, ids, context=None):
1118 """
1119 Creates and displays a Multiple CC Selection Wizard linked to the current Destination
1120 """
1121 if context is None:
1122 context = {}
1123 if isinstance(ids, (int, long)):
1124 ids = [ids]
1125 multiple_cc_wiz_obj = self.pool.get('multiple.cc.selection.wizard')
1126 if ids:
1127 multiple_cc_wiz_id = multiple_cc_wiz_obj.create(cr, uid, {'dest_id': ids[0]}, context=context)
1128 return {
1129 'type': 'ir.actions.act_window',
1130 'res_model': 'multiple.cc.selection.wizard',
1131 'view_type': 'form',
1132 'view_mode': 'form',
1133 'target': 'new',
1134 'res_id': [multiple_cc_wiz_id],
1135 'context': context,
1136 }
1137 return True
1138
9711139
972analytic_account()1140analytic_account()
973# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:1141# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
9741142
=== modified file 'bin/addons/analytic_override/analytic_line.py'
--- bin/addons/analytic_override/analytic_line.py 2021-04-23 12:31:26 +0000
+++ bin/addons/analytic_override/analytic_line.py 2021-05-07 16:01:07 +0000
@@ -159,6 +159,7 @@
159 return True159 return True
160160
161 account_obj = self.pool.get('account.analytic.account')161 account_obj = self.pool.get('account.analytic.account')
162 dest_cc_link_obj = self.pool.get('dest.cc.link')
162163
163 #US-419: Use the document date and not posting date when checking the validity of analytic account164 #US-419: Use the document date and not posting date when checking the validity of analytic account
164 # tech: replaced all date by document_date165 # tech: replaced all date by document_date
@@ -171,6 +172,8 @@
171 raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (account.name or '',))172 raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (account.name or '',))
172 if 'date' in vals and vals['date'] is not False:173 if 'date' in vals and vals['date'] is not False:
173 date = vals['date']174 date = vals['date']
175 dest = False
176 cc = False
174 if vals.get('cost_center_id', False):177 if vals.get('cost_center_id', False):
175 cc = account_obj.browse(cr, uid, vals['cost_center_id'], context=context)178 cc = account_obj.browse(cr, uid, vals['cost_center_id'], context=context)
176 if date < cc.date_start or (cc.date != False and date >= cc.date):179 if date < cc.date_start or (cc.date != False and date >= cc.date):
@@ -181,6 +184,9 @@
181 if date < dest.date_start or (dest.date != False and date >= dest.date):184 if date < dest.date_start or (dest.date != False and date >= dest.date):
182 if 'from' not in context or context.get('from') != 'mass_reallocation':185 if 'from' not in context or context.get('from') != 'mass_reallocation':
183 raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (dest.name or '',))186 raise osv.except_osv(_('Error'), _("The analytic account selected '%s' is not active.") % (dest.name or '',))
187 if context.get('from') != 'mass_reallocation' and dest and cc and \
188 dest_cc_link_obj.is_inactive_dcl(cr, uid, dest.id, cc.id, date, context=context):
189 raise osv.except_osv(_('Error'), _("The combination \"%s - %s\" is not active.") % (dest.code or '', cc.code or ''))
184 return True190 return True
185191
186 def _check_document_date(self, cr, uid, ids):192 def _check_document_date(self, cr, uid, ids):
187193
=== added file 'bin/addons/analytic_override/dest_cc_link.py'
--- bin/addons/analytic_override/dest_cc_link.py 1970-01-01 00:00:00 +0000
+++ bin/addons/analytic_override/dest_cc_link.py 2021-05-07 16:01:07 +0000
@@ -0,0 +1,174 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2021 MSF, TeMPO Consulting.
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from osv import osv
23from osv import fields
24from tools.translate import _
25
26
27class dest_cc_link(osv.osv):
28 _name = "dest.cc.link"
29 _description = "Destination / Cost Center Combination"
30 _rec_name = "cc_id"
31 _trace = True
32
33 def _get_current_id(self, cr, uid, ids, field_name, args, context=None):
34 """
35 Returns a dict with key = value = current DB id.
36
37 current_id is an internal field used to make the CC read-only except for new lines (= without DB id).
38 The goal is to prevent the edition of a Dest CC Link with a CC linked to a different coordo than the previous CC.
39 """
40 res = {}
41 for i in ids:
42 res[i] = i
43 return res
44
45 def _get_cc_code(self, cr, uid, ids, name, args, context=None):
46 """
47 Returns a dict with key = Dest CC Link id, and value = related Cost Center code.
48 """
49 if context is None:
50 context = {}
51 if isinstance(ids, (int, long)):
52 ids = [ids]
53 res = {}
54 for dcl in self.browse(cr, uid, ids, fields_to_fetch=['cc_id'], context=context):
55 res[dcl.id] = dcl.cc_id.code or ''
56 return res
57
58 def _get_dest_cc_link_to_update(self, cr, uid, analytic_acc_ids, context=None):
59 """
60 Returns the list of Dest CC Links for which the CC code should be updated.
61 """
62 if context is None:
63 context = {}
64 if isinstance(analytic_acc_ids, (int, long)):
65 analytic_acc_ids = [analytic_acc_ids]
66 return self.pool.get('dest.cc.link').search(cr, uid, [('cc_id', 'in', analytic_acc_ids)], order='NO_ORDER', context=context)
67
68 _columns = {
69 'dest_id': fields.many2one('account.analytic.account', string="Destination", required=True,
70 domain="[('category', '=', 'DEST'), ('type', '!=', 'view')]", ondelete='cascade', select=1),
71 'cc_id': fields.many2one('account.analytic.account', string="Cost Center", required=True, sort_column='cc_code',
72 domain="[('category', '=', 'OC'), ('type', '!=', 'view')]", ondelete='cascade', select=1),
73 'cc_code': fields.function(_get_cc_code, method=True, string="Cost Center Code", type='char', size=24,
74 readonly=True,
75 store={
76 'account.analytic.account': (_get_dest_cc_link_to_update, ['code'], 10),
77 'dest.cc.link': (lambda self, cr, uid, ids, c=None: ids, ['cc_id'], 20),
78 }),
79 'cc_name': fields.related('cc_id', 'name', type="char", string="Cost Center Name", readonly=True, write_relate=False, store=False),
80 'active_from': fields.date('Activation Combination Dest / CC from', required=False),
81 'inactive_from': fields.date('Inactivation Combination Dest / CC from', required=False),
82 'current_id': fields.function(_get_current_id, method=1, type='integer', internal=1, string="DB Id (used by the UI)"),
83 }
84
85 _order = 'dest_id, cc_code, id'
86
87 _sql_constraints = [
88 ('dest_cc_uniq', 'UNIQUE(dest_id, cc_id)', 'Each Cost Center can only be added once to the same Destination.'),
89 ('dest_cc_date_check', 'CHECK(active_from < inactive_from)', 'The Activation date of the "Combination Dest / CC" '
90 'must be before the Inactivation date.')
91 ]
92
93 def _check_analytic_lines(self, cr, uid, ids, context=None):
94 """
95 Displays a non-blocking message on the top of the page in case some AJI using the Dest/CC link have been booked
96 outside its activation dates.
97 """
98 if context is None:
99 context = {}
100 if not context.get('sync_update_execution'):
101 aal_obj = self.pool.get('account.analytic.line')
102 if isinstance(ids, (int, long)):
103 ids = [ids]
104 for dcl in self.browse(cr, uid, ids, context=context):
105 if dcl.active_from or dcl.inactive_from:
106 dcl_dom = [('cost_center_id', '=', dcl.cc_id.id), ('destination_id', '=', dcl.dest_id.id)]
107 if dcl.active_from and dcl.inactive_from:
108 dcl_dom.append('|')
109 if dcl.active_from:
110 dcl_dom.append(('date', '<', dcl.active_from))
111 if dcl.inactive_from:
112 dcl_dom.append(('date', '>=', dcl.inactive_from))
113 if aal_obj.search_exist(cr, uid, dcl_dom, context=context):
114 self.log(cr, uid, dcl.id, _('At least one Analytic Journal Item using the combination \"%s - %s\" '
115 'has a Posting Date outside the activation dates selected.') %
116 (dcl.dest_id.code or '', dcl.cc_id.code or ''))
117
118 def create(self, cr, uid, vals, context=None):
119 """
120 Creates the Dest CC Combination, and:
121 - displays an informative message on the top of the page if existing AJIs are using the combination outside its activation interval.
122 - unticks the box "Allow all Cost Centers" from the related Dest.
123 (UC: edit a Dest. having the box ticked, untick the box, add a CC and click on Cancel.
124 CC isn't removed by the Cancel button as it is a o2m, so the box should remain unticked.)
125 """
126 if context is None:
127 context = {}
128 analytic_acc_obj = self.pool.get('account.analytic.account')
129 res = super(dest_cc_link, self).create(cr, uid, vals, context=context)
130 self._check_analytic_lines(cr, uid, res, context=context)
131 dest_id = self.read(cr, uid, res, ['dest_id'], context=context)['dest_id'][0]
132 if analytic_acc_obj.search_exist(cr, uid, [('id', '=', dest_id), ('allow_all_cc', '=', True)], context=context):
133 analytic_acc_obj.write(cr, uid, dest_id, {'allow_all_cc': False}, context=context)
134 return res
135
136 def write(self, cr, uid, ids, vals, context=None):
137 """
138 See _check_analytic_lines
139 """
140 res = super(dest_cc_link, self).write(cr, uid, ids, vals, context=context)
141 self._check_analytic_lines(cr, uid, ids, context=context)
142 return res
143
144 def is_inactive_dcl(self, cr, uid, dest_id, cc_id, posting_date, context=None):
145 """
146 Returns True if the Dest CC Link with the dest_id and cc_id exists and that the posting_date
147 is outside its validity date range.
148 """
149 if context is None:
150 context = {}
151 inactive_dcl = False
152 if dest_id and cc_id and posting_date:
153 dcl_ids = self.search(cr, uid, [('dest_id', '=', dest_id), ('cc_id', '=', cc_id)], limit=1, context=context)
154 if dcl_ids:
155 dcl = self.browse(cr, uid, dcl_ids[0], fields_to_fetch=['active_from', 'inactive_from'], context=context)
156 inactive_dcl = (dcl.active_from and posting_date < dcl.active_from) or (dcl.inactive_from and posting_date >= dcl.inactive_from)
157 return inactive_dcl
158
159 def on_change_cc_id(self, cr, uid, ids, cc_id):
160 """
161 Fills in the CC Name as soon as a CC is selected
162 """
163 res = {}
164 analytic_acc_obj = self.pool.get('account.analytic.account')
165 if cc_id:
166 name = analytic_acc_obj.read(cr, uid, cc_id, ['name'])['name']
167 else:
168 name = False
169 res['value'] = {'cc_name': name, }
170 return res
171
172
173dest_cc_link()
174# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
0175
=== added file 'bin/addons/analytic_override/dest_cc_link.xml'
--- bin/addons/analytic_override/dest_cc_link.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/analytic_override/dest_cc_link.xml 2021-05-07 16:01:07 +0000
@@ -0,0 +1,49 @@
1<?xml version="1.0"?>
2<openerp>
3 <data>
4 <!-- DEST CC LINK - FORM VIEW -->
5 <record id="view_dest_cc_link_form" model="ir.ui.view">
6 <field name="name">dest.cc.link.form</field>
7 <field name="model">dest.cc.link</field>
8 <field name="type">form</field>
9 <field name="arch" type="xml">
10 <form noteditable="1">
11 <field name="cc_id"/>
12 <newline/>
13 <field name="active_from"/>
14 <field name="inactive_from"/>
15 </form>
16 </field>
17 </record>
18
19 <!-- DEST CC LINK - TREE VIEW -->
20 <record id="view_dest_cc_link_tree" model="ir.ui.view">
21 <field name="name">dest.cc.link.tree</field>
22 <field name="model">dest.cc.link</field>
23 <field name="type">tree</field>
24 <field name="arch" type="xml">
25 <tree>
26 <field name="cc_id"/>
27 <field name="active_from"/>
28 <field name="inactive_from"/>
29 </tree>
30 </field>
31 </record>
32
33 <!-- DEST CC LINK - SEARCH VIEW -->
34 <record id="view_dest_cc_link_search" model="ir.ui.view">
35 <field name="name">dest.cc.link.search</field>
36 <field name="model">dest.cc.link</field>
37 <field name="type">search</field>
38 <field name="arch" type="xml">
39 <search>
40 <group>
41 <field name="cc_id"/>
42 <field name="active_from"/>
43 <field name="inactive_from"/>
44 </group>
45 </search>
46 </field>
47 </record>
48 </data>
49</openerp>
050
=== added file 'bin/addons/analytic_override/multiple_cc_selection_wizard.py'
--- bin/addons/analytic_override/multiple_cc_selection_wizard.py 1970-01-01 00:00:00 +0000
+++ bin/addons/analytic_override/multiple_cc_selection_wizard.py 2021-05-07 16:01:07 +0000
@@ -0,0 +1,56 @@
1# -*- coding: utf-8 -*-
2##############################################################################
3#
4# OpenERP, Open Source Management Solution
5# Copyright (C) 2021 MSF, TeMPO Consulting.
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20##############################################################################
21
22from osv import fields
23from osv import osv
24
25
26class multiple_cc_selection_wizard(osv.osv_memory):
27 _name = 'multiple.cc.selection.wizard'
28
29 _columns = {
30 'dest_id': fields.many2one('account.analytic.account', string="Destination", required=True,
31 domain="[('category', '=', 'DEST'), ('type', '!=', 'view')]"),
32 'cc_ids': fields.many2many('account.analytic.account', 'multiple_cc_wiz_rel', 'wizard_id', 'cost_center_id',
33 string="Cost Centers", domain="[('category', '=', 'OC'), ('type', '!=', 'view')]"),
34 }
35
36 def multiple_cc_add(self, cr, uid, ids, context=None):
37 """
38 Adds the Cost Centers selected in the wizard to the current destination
39 without filling in the activation and inactivation dates of the related combinations.
40 """
41 if context is None:
42 context = {}
43 if isinstance(ids, (int, long)):
44 ids = [ids]
45 dest_cc_link_obj = self.pool.get('dest.cc.link')
46 analytic_acc_obj = self.pool.get('account.analytic.account')
47 wiz = self.browse(cr, uid, ids[0], context=context)
48 if wiz.cc_ids:
49 for cc in wiz.cc_ids:
50 # note: this automatically unticks the box "Allow all Cost Centers" (as for a manual CC addition)
51 dest_cc_link_obj.create(cr, uid, {'dest_id': wiz.dest_id.id, 'cc_id': cc.id}, context=context)
52 return {'type': 'ir.actions.act_window_close'}
53
54
55multiple_cc_selection_wizard()
56# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
057
=== added file 'bin/addons/analytic_override/multiple_cc_selection_wizard.xml'
--- bin/addons/analytic_override/multiple_cc_selection_wizard.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/analytic_override/multiple_cc_selection_wizard.xml 2021-05-07 16:01:07 +0000
@@ -0,0 +1,30 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4
5 <!-- MULTIPLE CC SELECTION WIZARD - FORM VIEW -->
6 <record id="multiple_cc_selection_wizard_form_view" model="ir.ui.view">
7 <field name="name">multiple.cc.selection.wizard.form</field>
8 <field name="model">multiple.cc.selection.wizard</field>
9 <field name="type">form</field>
10 <field name="arch" type="xml">
11 <form string="Add several Cost Centers">
12 <label nolabel="1" colspan="6"
13 string="This wizard enables you to select several Cost Centers at once to be added to the Destination. The activation and inactivation dates of the related Dest / CC combinations will remain empty."/>
14 <field name="cc_ids" nolabel="1" colspan="6">
15 <tree string="Cost Centers">
16 <field name="code"/>
17 <field name="name"/>
18 </tree>
19 </field>
20 <group colspan="6" col="4">
21 <label string="" colspan="2"/>
22 <button icon="gtk-cancel" string="Cancel" special="cancel" colspan="1"/>
23 <button icon="gtk-add" string="Add" name="multiple_cc_add" type="object" colspan="1"/>
24 </group>
25 </form>
26 </field>
27 </record>
28
29 </data>
30</openerp>
031
=== modified file 'bin/addons/financing_contract/financing_contract_account_quadruplet.py'
--- bin/addons/financing_contract/financing_contract_account_quadruplet.py 2020-11-20 15:52:12 +0000
+++ bin/addons/financing_contract/financing_contract_account_quadruplet.py 2021-05-07 16:01:07 +0000
@@ -89,14 +89,15 @@
89 funding_pool_associated_destinations fpad,89 funding_pool_associated_destinations fpad,
90 account_destination_link lnk,90 account_destination_link lnk,
91 account_analytic_account dest91 account_analytic_account dest
92 LEFT JOIN destination_cost_center_rel dest_cc_rel ON dest_cc_rel.destination_id = dest.id92 LEFT JOIN dest_cc_link ON dest_cc_link.dest_id = dest.id
93
93 WHERE94 WHERE
94 fpacc.funding_pool_id = fp.id AND95 fpacc.funding_pool_id = fp.id AND
95 fpacc.cost_center_id = cc.id AND96 fpacc.cost_center_id = cc.id AND
96 lnk.id = fpad.tuple_id AND97 lnk.id = fpad.tuple_id AND
97 fp.id = fpad.funding_pool_id AND98 fp.id = fpad.funding_pool_id AND
98 lnk.destination_id = dest.id AND99 lnk.destination_id = dest.id AND
99 (dest.allow_all_cc = 't' or dest_cc_rel.cost_center_id = cc.id)100 (dest.allow_all_cc = 't' or dest_cc_link.cc_id = cc.id)
100101
101 UNION102 UNION
102103
@@ -111,7 +112,7 @@
111 account_destination_link lnk,112 account_destination_link lnk,
112 account_account gl_account,113 account_account gl_account,
113 account_analytic_account dest114 account_analytic_account dest
114 LEFT JOIN destination_cost_center_rel dest_cc_rel ON dest_cc_rel.destination_id = dest.id115 LEFT JOIN dest_cc_link ON dest_cc_link.dest_id = dest.id
115 where116 where
116 fp.allow_all_cc_with_fp = 't' and117 fp.allow_all_cc_with_fp = 't' and
117 cc.type != 'view' and118 cc.type != 'view' and
@@ -123,7 +124,7 @@
123 fp_account_rel.account_id= gl_account.id and124 fp_account_rel.account_id= gl_account.id and
124 lnk.account_id = gl_account.id and125 lnk.account_id = gl_account.id and
125 lnk.destination_id = dest.id and126 lnk.destination_id = dest.id and
126 (dest.allow_all_cc = 't' or dest_cc_rel.cost_center_id = cc.id)127 (dest.allow_all_cc = 't' or dest_cc_link.cc_id = cc.id)
127128
128 UNION129 UNION
129130
@@ -138,7 +139,7 @@
138 account_destination_link lnk,139 account_destination_link lnk,
139 account_account gl_account,140 account_account gl_account,
140 account_analytic_account dest141 account_analytic_account dest
141 LEFT JOIN destination_cost_center_rel dest_cc_rel ON dest_cc_rel.destination_id = dest.id142 LEFT JOIN dest_cc_link ON dest_cc_link.dest_id = dest.id
142 where143 where
143 fp.allow_all_cc_with_fp = 'f' and144 fp.allow_all_cc_with_fp = 'f' and
144 fpacc.funding_pool_id = fp.id and145 fpacc.funding_pool_id = fp.id and
@@ -148,7 +149,7 @@
148 fp_account_rel.account_id= gl_account.id and149 fp_account_rel.account_id= gl_account.id and
149 lnk.account_id = gl_account.id and150 lnk.account_id = gl_account.id and
150 lnk.destination_id = dest.id and151 lnk.destination_id = dest.id and
151 (dest.allow_all_cc = 't' or dest_cc_rel.cost_center_id = cc.id)152 (dest.allow_all_cc = 't' or dest_cc_link.cc_id = cc.id)
152153
153 UNION154 UNION
154155
@@ -162,7 +163,7 @@
162 account_target_costcenter target,163 account_target_costcenter target,
163 account_destination_link lnk,164 account_destination_link lnk,
164 account_analytic_account dest165 account_analytic_account dest
165 LEFT JOIN destination_cost_center_rel dest_cc_rel ON dest_cc_rel.destination_id = dest.id166 LEFT JOIN dest_cc_link ON dest_cc_link.dest_id = dest.id
166 where167 where
167 fp.allow_all_cc_with_fp = 't' and168 fp.allow_all_cc_with_fp = 't' and
168 cc.type != 'view' and169 cc.type != 'view' and
@@ -173,7 +174,7 @@
173 lnk.id = fpad.tuple_id and174 lnk.id = fpad.tuple_id and
174 fp.id = fpad.funding_pool_id and175 fp.id = fpad.funding_pool_id and
175 lnk.destination_id = dest.id and176 lnk.destination_id = dest.id and
176 (dest.allow_all_cc = 't' or dest_cc_rel.cost_center_id = cc.id)177 (dest.allow_all_cc = 't' or dest_cc_link.cc_id = cc.id)
177 ) AS combinations178 ) AS combinations
178 )""")179 )""")
179 return res180 return res
180181
=== modified file 'bin/addons/msf_audittrail/__openerp__.py'
--- bin/addons/msf_audittrail/__openerp__.py 2021-05-05 16:04:38 +0000
+++ bin/addons/msf_audittrail/__openerp__.py 2021-05-07 16:01:07 +0000
@@ -57,7 +57,11 @@
57 'data/audittrail_account_account.yml',57 'data/audittrail_account_account.yml',
58 'data/audittrail_account_tax.yml',58 'data/audittrail_account_tax.yml',
59 'data/audittrail_res_company.yml',59 'data/audittrail_res_company.yml',
60<<<<<<< TREE
60 'data/audittrail_hq_entry.yml',61 'data/audittrail_hq_entry.yml',
62=======
63 'data/audittrail_dest_cc_link.yml',
64>>>>>>> MERGE-SOURCE
61 'audittrail_report.xml',65 'audittrail_report.xml',
62 'audittrail_invoice_data.yml',66 'audittrail_invoice_data.yml',
63 ],67 ],
6468
=== added file 'bin/addons/msf_audittrail/data/audittrail_dest_cc_link.yml'
--- bin/addons/msf_audittrail/data/audittrail_dest_cc_link.yml 1970-01-01 00:00:00 +0000
+++ bin/addons/msf_audittrail/data/audittrail_dest_cc_link.yml 2021-05-07 16:01:07 +0000
@@ -0,0 +1,39 @@
1-
2 For Dest CC Links (dest.cc.link), track the changes on the Destinations
3-
4 !python {model: audittrail.rule}: |
5 name = 'Dest / CC Combinations'
6 object_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'dest.cc.link')], context=context)
7 rule_id = self.search(cr, uid, [('object_id', 'in', object_ids)], context=context)
8 if object_ids:
9 # Create the rule
10 fields = ['cc_id', 'active_from', 'inactive_from']
11 fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=', 'dest.cc.link'), ('name', 'in', fields)], context=context)
12 field_name = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=', 'dest.cc.link'), ('name', '=', 'cc_id')], context=context)
13 field_parent = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=', 'dest.cc.link'), ('name', '=', 'dest_id')], context=context)
14
15 name_id = False
16 parent_id = False
17
18 if field_parent:
19 parent_id = field_parent[0]
20 if field_name:
21 name_id = field_name[0]
22
23 vals = {
24 'name': name,
25 'object_id': object_ids[0],
26 'log_write': True,
27 'log_unlink': True,
28 'log_create': True,
29 'field_ids': [(6, 0, fields_ids)],
30 'parent_field_id': parent_id,
31 'name_get_field_id': name_id,
32 }
33
34 if not rule_id:
35 rule_id = self.create(cr, uid, vals, context=context)
36 elif rule_id:
37 self.write(cr, uid, rule_id, vals, context=context)
38 # Subscribe to the rule
39 self.subscribe(cr, uid, rule_id)
040
=== modified file 'bin/addons/msf_doc_import/msf_import_export.py'
--- bin/addons/msf_doc_import/msf_import_export.py 2019-05-27 10:02:18 +0000
+++ bin/addons/msf_doc_import/msf_import_export.py 2021-05-07 16:01:07 +0000
@@ -32,6 +32,7 @@
32from tempfile import TemporaryFile32from tempfile import TemporaryFile
33from lxml import etree33from lxml import etree
34from lxml.etree import XMLSyntaxError34from lxml.etree import XMLSyntaxError
35from datetime import datetime
3536
36from msf_doc_import.wizard.abstract_wizard_import import ImportHeader37from msf_doc_import.wizard.abstract_wizard_import import ImportHeader
37from msf_doc_import.msf_import_export_conf import MODEL_DICT38from msf_doc_import.msf_import_export_conf import MODEL_DICT
@@ -626,6 +627,58 @@
626 # thread.join(wait_time)627 # thread.join(wait_time)
627 return self.bg_import(cr, uid, wiz, expected_headers, rows, raise_on_error=raise_on_error, context=context)628 return self.bg_import(cr, uid, wiz, expected_headers, rows, raise_on_error=raise_on_error, context=context)
628629
630 def _handle_dest_cc_dates(self, cr, uid, data, dest_cc_list, dest_cc_tuple_list, context=None):
631 """
632 Gets and checks the dest_cc_link_active_from and dest_cc_link_inactive_from dates.
633 Updates the dest_cc_tuple_list with tuples containing (cost_center, active_date, inactive_date)
634 """
635 if context is None:
636 context = {}
637 dest_cc_active_date_list = []
638 dest_cc_inactive_date_list = []
639 active_from = (True, 'dest_cc_link_active_from', _("Activation Combination Dest / CC from"))
640 inactive_from = (False, 'dest_cc_link_inactive_from', _("Inactivation Combination Dest / CC from"))
641 for t in [active_from, inactive_from]:
642 active = t[0]
643 col_name = t[1]
644 col_str = t[2]
645 dest_cc_date_list = []
646 if data.get(col_name):
647 split_char = ';'
648 if split_char not in data.get(col_name):
649 split_char = ','
650 for cost_center_date in data.get(col_name).split(split_char):
651 cc_date = cost_center_date.strip()
652 if cc_date:
653 cc_date = cc_date.replace(' 00:00:00.00', '') # can be if there is only one date in the cell
654 try:
655 cc_date = datetime.strptime(cc_date, "%Y-%m-%d")
656 except ValueError:
657 raise Exception(_('The dates in the column "%s" should use the format YYYY-MM-DD.') % col_str)
658 else:
659 cc_date = False # the related Dest/CC combination has no activation/inactivation date
660 dest_cc_date_list.append(cc_date)
661 del data[col_name]
662 if len(dest_cc_date_list) > len(dest_cc_list):
663 raise Exception(_('The number of dates in the column "%s" exceeds the number of Cost Centers indicated.') % col_str)
664 if active:
665 dest_cc_active_date_list = dest_cc_date_list[:]
666 else:
667 dest_cc_inactive_date_list = dest_cc_date_list[:]
668 for num, cc in enumerate(dest_cc_list):
669 try:
670 dest_cc_active_date = dest_cc_active_date_list[num]
671 except IndexError:
672 dest_cc_active_date = False
673 try:
674 dest_cc_inactive_date = dest_cc_inactive_date_list[num]
675 except IndexError:
676 dest_cc_inactive_date = False
677 if dest_cc_active_date and dest_cc_inactive_date and dest_cc_active_date >= dest_cc_inactive_date:
678 cc_code = self.pool.get('account.analytic.account').read(cr, uid, cc, ['code'], context=context)['code'] or ''
679 raise Exception(_('The activation date related to the Cost Center %s must be before the inactivation date.') % cc_code)
680 dest_cc_tuple_list.append((cc, dest_cc_active_date, dest_cc_inactive_date))
681
629 def bg_import(self, cr, uid, import_brw, headers, rows, raise_on_error=False, context=None):682 def bg_import(self, cr, uid, import_brw, headers, rows, raise_on_error=False, context=None):
630 """683 """
631 Run the import of lines in background684 Run the import of lines in background
@@ -647,6 +700,7 @@
647 acc_obj = self.pool.get('account.account')700 acc_obj = self.pool.get('account.account')
648 acc_analytic_obj = self.pool.get('account.analytic.account')701 acc_analytic_obj = self.pool.get('account.analytic.account')
649 acc_dest_obj = self.pool.get('account.destination.link')702 acc_dest_obj = self.pool.get('account.destination.link')
703 dest_cc_link_obj = self.pool.get('dest.cc.link')
650704
651 cost_centers_cache = {}705 cost_centers_cache = {}
652 gl_account_cache = {}706 gl_account_cache = {}
@@ -731,7 +785,7 @@
731 # custom process to retrieve CC, Destination_ids785 # custom process to retrieve CC, Destination_ids
732 custom_m2m = []786 custom_m2m = []
733 if import_brw.model_list_selection == 'destinations':787 if import_brw.model_list_selection == 'destinations':
734 custom_m2m = ['dest_cc_ids', 'destination_ids']788 custom_m2m = ['destination_ids']
735 elif import_brw.model_list_selection == 'funding_pools':789 elif import_brw.model_list_selection == 'funding_pools':
736 custom_m2m = ['cost_center_ids', 'tuple_destination_account_ids']790 custom_m2m = ['cost_center_ids', 'tuple_destination_account_ids']
737 for c_m2m in custom_m2m:791 for c_m2m in custom_m2m:
@@ -1025,6 +1079,7 @@
1025 data['tuple_destination_account_ids'] = [(6, 0, [])]1079 data['tuple_destination_account_ids'] = [(6, 0, [])]
10261080
1027 # Destinations1081 # Destinations
1082 dest_cc_tuple_list = []
1028 if import_brw.model_list_selection == 'destinations':1083 if import_brw.model_list_selection == 'destinations':
1029 context['from_import_menu'] = True1084 context['from_import_menu'] = True
1030 data['category'] = 'DEST'1085 data['category'] = 'DEST'
@@ -1042,14 +1097,14 @@
1042 if data['type'] not in ['normal', 'view']:1097 if data['type'] not in ['normal', 'view']:
1043 raise Exception(_('The Type must be either "Normal" or "View".'))1098 raise Exception(_('The Type must be either "Normal" or "View".'))
1044 # Cost Centers1099 # Cost Centers
1045 if data.get('dest_cc_ids'):1100 dest_cc_list = []
1101 if data.get('dest_cc_link_ids'):
1046 if data.get('allow_all_cc'):1102 if data.get('allow_all_cc'):
1047 raise Exception(_("Please either list the Cost Centers to allow, or allow all Cost Centers."))1103 raise Exception(_("Please either list the Cost Centers to allow, or allow all Cost Centers."))
1048 dest_cc_list = []
1049 split_char = ';'1104 split_char = ';'
1050 if split_char not in data.get('dest_cc_ids'):1105 if split_char not in data.get('dest_cc_link_ids'):
1051 split_char = ','1106 split_char = ','
1052 for cost_center in data.get('dest_cc_ids').split(split_char):1107 for cost_center in data.get('dest_cc_link_ids').split(split_char):
1053 cc = cost_center.strip()1108 cc = cost_center.strip()
1054 if cc not in cost_centers_cache:1109 if cc not in cost_centers_cache:
1055 cc_dom = [('category', '=', 'OC'), ('type', '=', 'normal'), ('code', '=', cc)]1110 cc_dom = [('category', '=', 'OC'), ('type', '=', 'normal'), ('code', '=', cc)]
@@ -1059,9 +1114,7 @@
1059 dest_cc_list.append(cc_ids[0])1114 dest_cc_list.append(cc_ids[0])
1060 else:1115 else:
1061 raise Exception(_('Cost Center "%s" not found.') % cc)1116 raise Exception(_('Cost Center "%s" not found.') % cc)
1062 data['dest_cc_ids'] = [(6, 0, dest_cc_list)]1117 self._handle_dest_cc_dates(cr, uid, data, dest_cc_list, dest_cc_tuple_list, context=context)
1063 else:
1064 data['dest_cc_ids'] = [(6, 0, [])]
1065 # Accounts1118 # Accounts
1066 if data.get('destination_ids'): # "destinations_ids" corresponds to G/L accounts...1119 if data.get('destination_ids'): # "destinations_ids" corresponds to G/L accounts...
1067 acc_list = []1120 acc_list = []
@@ -1089,8 +1142,6 @@
1089 # in case of empty columns on non-required fields, existing values should be deleted1142 # in case of empty columns on non-required fields, existing values should be deleted
1090 if 'date' not in data:1143 if 'date' not in data:
1091 data['date'] = False1144 data['date'] = False
1092 if 'dest_cc_ids' not in data:
1093 data['dest_cc_ids'] = [(6, 0, [])]
1094 if 'allow_all_cc' not in data:1145 if 'allow_all_cc' not in data:
1095 data['allow_all_cc'] = False1146 data['allow_all_cc'] = False
1096 if 'destination_ids' not in data:1147 if 'destination_ids' not in data:
@@ -1164,6 +1215,7 @@
11641215
1165 data.update(forced_values)1216 data.update(forced_values)
11661217
1218 id_created = False
1167 if data.get('comment') == '[DELETE]':1219 if data.get('comment') == '[DELETE]':
1168 impobj.unlink(cr, uid, ids_to_update, context=context)1220 impobj.unlink(cr, uid, ids_to_update, context=context)
1169 nb_lines_deleted += len(ids_to_update)1221 nb_lines_deleted += len(ids_to_update)
@@ -1182,11 +1234,66 @@
1182 line_created = impobj.create(cr, uid, data, context=context)1234 line_created = impobj.create(cr, uid, data, context=context)
1183 lines_already_updated.append(line_created)1235 lines_already_updated.append(line_created)
1184 else:1236 else:
1185 impobj.create(cr, uid, data, context=context)1237 id_created = impobj.create(cr, uid, data, context=context)
1186 nb_succes += 11238 nb_succes += 1
1187 processed.append((row_index+1, line_data))1239 processed.append((row_index+1, line_data))
1188 if allow_partial:1240 if allow_partial:
1189 cr.commit()1241 cr.commit()
1242 # For Dest CC Links: create, update or delete the links if necessary
1243 if import_brw.model_list_selection == 'destinations':
1244 if isinstance(ids_to_update, (int, long)):
1245 ids_to_update = [ids_to_update]
1246 if not dest_cc_tuple_list and ids_to_update:
1247 # UC1: Dest CC Link column empty => delete all current Dest/CC combinations attached to the Dest
1248 old_dcl_ids = dest_cc_link_obj.search(cr, uid, [('dest_id', 'in', ids_to_update)], order='NO_ORDER', context=context)
1249 if old_dcl_ids:
1250 dest_cc_link_obj.unlink(cr, uid, old_dcl_ids, context=context)
1251 else:
1252 # UC2: new dest
1253 if id_created:
1254 for cc, active_date, inactive_date in dest_cc_tuple_list:
1255 dest_cc_link_obj.create(cr, uid, {'cc_id': cc, 'dest_id': id_created,
1256 'active_from': active_date, 'inactive_from': inactive_date},
1257 context=context)
1258 elif ids_to_update:
1259 for dest_id in ids_to_update:
1260 dest = acc_analytic_obj.browse(cr, uid, dest_id, fields_to_fetch=['dest_cc_link_ids'], context=context)
1261 current_cc_ids = [dest_cc_link.cc_id.id for dest_cc_link in dest.dest_cc_link_ids]
1262 new_cc_ids = []
1263 for cc, active_date, inactive_date in dest_cc_tuple_list:
1264 new_cc_ids.append(cc)
1265 # UC3: new combinations in existing Destinations
1266 if cc not in current_cc_ids:
1267 dest_cc_link_obj.create(cr, uid, {'cc_id': cc, 'dest_id': dest_id,
1268 'active_from': active_date, 'inactive_from': inactive_date},
1269 context=context)
1270 else:
1271 # UC4: combinations to be updated with new dates
1272 dcl_ids = dest_cc_link_obj.search(cr, uid,
1273 [('dest_id', '=', dest_id), ('cc_id', '=', cc)],
1274 limit=1, context=context)
1275 if dcl_ids:
1276 dest_cc_link = dest_cc_link_obj.read(cr, uid, dcl_ids[0],
1277 ['active_from', 'inactive_from'], context=context)
1278 if dest_cc_link['active_from']:
1279 current_active_dt = datetime.strptime(dest_cc_link['active_from'], "%Y-%m-%d")
1280 else:
1281 current_active_dt = False
1282 if dest_cc_link['inactive_from']:
1283 current_inactive_dt = datetime.strptime(dest_cc_link['inactive_from'], "%Y-%m-%d")
1284 else:
1285 current_inactive_dt = False
1286 if (current_active_dt != active_date) or (current_inactive_dt != inactive_date):
1287 dest_cc_link_obj.write(cr, uid, dest_cc_link['id'],
1288 {'active_from': active_date, 'inactive_from': inactive_date},
1289 context=context)
1290 # UC5: combinations to be deleted in existing Destinations
1291 cc_to_be_deleted = [c for c in current_cc_ids if c not in new_cc_ids]
1292 if cc_to_be_deleted:
1293 dcl_to_be_deleted = dest_cc_link_obj.search(cr, uid,
1294 [('dest_id', '=', dest_id), ('cc_id', 'in', cc_to_be_deleted)],
1295 order='NO_ORDER', context=context)
1296 dest_cc_link_obj.unlink(cr, uid, dcl_to_be_deleted, context=context)
1190 except (osv.except_osv, orm.except_orm) , e:1297 except (osv.except_osv, orm.except_orm) , e:
1191 logging.getLogger('import data').info('Error %s' % e.value)1298 logging.getLogger('import data').info('Error %s' % e.value)
1192 if raise_on_error:1299 if raise_on_error:
11931300
=== modified file 'bin/addons/msf_doc_import/msf_import_export_conf.py'
--- bin/addons/msf_doc_import/msf_import_export_conf.py 2019-05-27 09:37:55 +0000
+++ bin/addons/msf_doc_import/msf_import_export_conf.py 2021-05-07 16:01:07 +0000
@@ -549,7 +549,9 @@
549 'type',549 'type',
550 'date_start',550 'date_start',
551 'date', # "inactive from"551 'date', # "inactive from"
552 'dest_cc_ids',552 'dest_cc_link_ids',
553 'dest_cc_link_active_from',
554 'dest_cc_link_inactive_from',
553 'destination_ids',555 'destination_ids',
554 'allow_all_cc',556 'allow_all_cc',
555 ],557 ],
556558
=== modified file 'bin/addons/msf_homere_interface/hr_payroll.py'
--- bin/addons/msf_homere_interface/hr_payroll.py 2020-10-13 10:14:53 +0000
+++ bin/addons/msf_homere_interface/hr_payroll.py 2021-05-07 16:01:07 +0000
@@ -43,6 +43,7 @@
43 # Prepare some values43 # Prepare some values
44 res = {}44 res = {}
45 ad_obj = self.pool.get('analytic.distribution')45 ad_obj = self.pool.get('analytic.distribution')
46 dest_cc_link_obj = self.pool.get('dest.cc.link')
46 # Search MSF Private Fund element, because it's valid with all accounts47 # Search MSF Private Fund element, because it's valid with all accounts
47 try:48 try:
48 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',49 fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution',
@@ -84,6 +85,10 @@
84 if dest and dest.filter_active is False:85 if dest and dest.filter_active is False:
85 res[line.id] = 'invalid'86 res[line.id] = 'invalid'
86 continue87 continue
88 if line.destination_id and line.cost_center_id:
89 if dest_cc_link_obj.is_inactive_dcl(cr, uid, line.destination_id.id, line.cost_center_id.id, line.date, context=context):
90 res[line.id] = 'invalid'
91 continue
87 # G Check92 # G Check
88 if line.funding_pool_id:93 if line.funding_pool_id:
89 fp = self.pool.get('account.analytic.account').browse(cr, uid, line.funding_pool_id.id, context={'date': line.document_date})94 fp = self.pool.get('account.analytic.account').browse(cr, uid, line.funding_pool_id.id, context={'date': line.document_date})
@@ -202,6 +207,26 @@
202 ])207 ])
203 return to_update208 return to_update
204209
210 def _get_trigger_state_dest_cc_link(self, cr, uid, ids, context=None):
211 """
212 Returns the list of Payroll Entries for which the AD state should be re-computed
213 """
214 if context is None:
215 context = {}
216 if isinstance(ids, (int, long)):
217 ids = [ids]
218 cc_ids = []
219 dest_ids = []
220 payroll_obj = self.pool.get('hr.payroll.msf')
221 for dest_cc_link in self.browse(cr, uid, ids, context=context):
222 cc_ids.append(dest_cc_link.cc_id.id)
223 dest_ids.append(dest_cc_link.dest_id.id)
224 payroll_ids = payroll_obj.search(cr, uid, [('state', '=', 'draft'),
225 '|',
226 ('cost_center_id', 'in', cc_ids),
227 ('destination_id', 'in', dest_ids)], order='NO_ORDER', context=context)
228 return payroll_ids
229
205 def _has_third_party(self, cr, uid, ids, name, arg, context=None):230 def _has_third_party(self, cr, uid, ids, name, arg, context=None):
206 """231 """
207 Returns True if the Payroll entry is linked to either an Employee or a Supplier232 Returns True if the Payroll entry is linked to either an Employee or a Supplier
@@ -242,12 +267,14 @@
242 'hr.payroll.msf': (lambda self, cr, uid, ids, c=None: ids, ['account_id', 'cost_center_id', 'funding_pool_id', 'destination_id'], 10),267 'hr.payroll.msf': (lambda self, cr, uid, ids, c=None: ids, ['account_id', 'cost_center_id', 'funding_pool_id', 'destination_id'], 10),
243 'account.account': (_get_trigger_state_account, ['user_type_code', 'destination_ids'], 20),268 'account.account': (_get_trigger_state_account, ['user_type_code', 'destination_ids'], 20),
244 'account.analytic.account': (_get_trigger_state_ana, ['date', 'date_start', 'allow_all_cc',269 'account.analytic.account': (_get_trigger_state_ana, ['date', 'date_start', 'allow_all_cc',
245 'dest_cc_ids', 'allow_all_cc_with_fp',270 'allow_all_cc_with_fp',
246 'cost_center_ids', 'select_accounts_only',271 'cost_center_ids', 'select_accounts_only',
247 'fp_account_ids',272 'fp_account_ids',
248 'tuple_destination_account_ids'],273 'tuple_destination_account_ids'],
249 20),274 20),
250 'account.destination.link': (_get_trigger_state_dest_link, ['account_id', 'destination_id'], 30),275 'account.destination.link': (_get_trigger_state_dest_link, ['account_id', 'destination_id'], 30),
276 'dest.cc.link': (_get_trigger_state_dest_cc_link,
277 ['cc_id', 'dest_id', 'active_from', 'inactive_from'], 40),
251 }278 }
252 ),279 ),
253 'partner_type': fields.function(_get_third_parties, type='reference', method=True, string="Third Parties", readonly=True,280 'partner_type': fields.function(_get_third_parties, type='reference', method=True, string="Third Parties", readonly=True,
254281
=== modified file 'bin/addons/msf_profile/data/patches.xml'
--- bin/addons/msf_profile/data/patches.xml 2021-02-25 15:48:01 +0000
+++ bin/addons/msf_profile/data/patches.xml 2021-05-07 16:01:07 +0000
@@ -647,9 +647,17 @@
647 <field name="method">us_6796_hide_prod_status_inconsistencies</field>647 <field name="method">us_6796_hide_prod_status_inconsistencies</field>
648 </record>648 </record>
649649
650<<<<<<< TREE
650 <record id="us_8166_hide_consolidated_sm_report" model="patch.scripts">651 <record id="us_8166_hide_consolidated_sm_report" model="patch.scripts">
651 <field name="method">us_8166_hide_consolidated_sm_report</field>652 <field name="method">us_8166_hide_consolidated_sm_report</field>
652 </record>653 </record>
653654
655=======
656 <!-- UF21.0 -->
657 <record id="us_7295_update_new_dest_cc_link" model="patch.scripts">
658 <field name="method">us_7295_update_new_dest_cc_link</field>
659 </record>
660
661>>>>>>> MERGE-SOURCE
654 </data>662 </data>
655</openerp>663</openerp>
656664
=== modified file 'bin/addons/msf_profile/i18n/fr_MF.po'
--- bin/addons/msf_profile/i18n/fr_MF.po 2021-05-06 15:52:15 +0000
+++ bin/addons/msf_profile/i18n/fr_MF.po 2021-05-07 16:01:07 +0000
@@ -21113,6 +21113,7 @@
21113#: report:kitting.order.report:021113#: report:kitting.order.report:0
21114#: field:replenishment.inventory.review.line.exp,name:021114#: field:replenishment.inventory.review.line.exp,name:0
21115#: field:replenishment.segment.line.amc.month_exp,name:021115#: field:replenishment.segment.line.amc.month_exp,name:0
21116#: view:account.analytic.account:0
21116#, python-format21117#, python-format
21117msgid "Name"21118msgid "Name"
21118msgstr "Nom"21119msgstr "Nom"
@@ -34812,6 +34813,7 @@
34812#: field:products.situation.report,p_code:034813#: field:products.situation.report,p_code:0
34813#: report:addons/msf_tools/report/report_stock_pipe_per_product_instance_xls.mako:8634814#: report:addons/msf_tools/report/report_stock_pipe_per_product_instance_xls.mako:86
34814#: field:account.target.costcenter,cost_center_code:034815#: field:account.target.costcenter,cost_center_code:0
34816#: view:account.analytic.account:0
34815#, python-format34817#, python-format
34816msgid "Code"34818msgid "Code"
34817msgstr "Code"34819msgstr "Code"
@@ -44531,9 +44533,10 @@
44531msgid "No donation account found for this line: %s. (product: %s)"44533msgid "No donation account found for this line: %s. (product: %s)"
44532msgstr "No donation account found for this line: %s. (product: %s)"44534msgstr "No donation account found for this line: %s. (product: %s)"
4453344535
44534#. modules: account, base44536#. modules: account, base, analytic_override
44535#: view:account.addtmpl.wizard:044537#: view:account.addtmpl.wizard:0
44536#: view:res.widget.wizard:044538#: view:res.widget.wizard:0
44539#: view:multiple.cc.selection.wizard:0
44537msgid "Add"44540msgid "Add"
44538msgstr "Ajouter"44541msgstr "Ajouter"
4453944542
@@ -47155,7 +47158,7 @@
47155#: code:addons/analytic_distribution/wizard/commitment_analytic_reallocation.py:14047158#: code:addons/analytic_distribution/wizard/commitment_analytic_reallocation.py:140
47156#, python-format47159#, python-format
47157msgid "Non compatible entries found: %s"47160msgid "Non compatible entries found: %s"
47158msgstr "Non compatible entries found: %s"47161msgstr "Ecritures non compatibles trouvées : %s"
4715947162
47160#. modules: delivery_mechanism, tender_flow, analytic_distribution_supply, product_nomenclature, return_claim, sync_client, res_currency_tables, supplier_catalogue, import_data, stock_batch_recall, stock, product, reason_types_moves, consumption_calculation, register_accounting, specific_rules, kit, base, account_period_closing_level, account_subscription, msf_cross_docking, purchase, account, msf_outgoing, resource, procurement_auto, msf_config_locations, sale, account_override, sourcing, sync_so, res_currency_functional, account_hq_entries47163#. modules: delivery_mechanism, tender_flow, analytic_distribution_supply, product_nomenclature, return_claim, sync_client, res_currency_tables, supplier_catalogue, import_data, stock_batch_recall, stock, product, reason_types_moves, consumption_calculation, register_accounting, specific_rules, kit, base, account_period_closing_level, account_subscription, msf_cross_docking, purchase, account, msf_outgoing, resource, procurement_auto, msf_config_locations, sale, account_override, sourcing, sync_so, res_currency_functional, account_hq_entries
47161#: code:addons/account/account.py:44547164#: code:addons/account/account.py:445
@@ -66095,6 +66098,9 @@
66095#: report:addons/account/report/free_allocation_report.mako:17666098#: report:addons/account/report/free_allocation_report.mako:176
66096#: field:free.allocation.wizard,cost_center_ids:066099#: field:free.allocation.wizard,cost_center_ids:0
66097#: field:account.analytic.account,dest_cc_ids:066100#: field:account.analytic.account,dest_cc_ids:0
66101#: field:account.analytic.account,dest_cc_link_ids:0
66102#: view:multiple.cc.selection.wizard:0
66103#: field:multiple.cc.selection.wizard,cc_ids:0
66098msgid "Cost Centers"66104msgid "Cost Centers"
66099msgstr "Centres de Coût"66105msgstr "Centres de Coût"
6610066106
@@ -80576,6 +80582,7 @@
80576#: code:addons/register_accounting/wizard/wizard_register_import.py:57080582#: code:addons/register_accounting/wizard/wizard_register_import.py:570
80577#: report:addons/account/report/free_allocation_report.mako:20580583#: report:addons/account/report/free_allocation_report.mako:205
80578#: code:addons/msf_doc_import/wizard/wizard_po_simulation_screen.py:60680584#: code:addons/msf_doc_import/wizard/wizard_po_simulation_screen.py:606
80585#: field:dest.cc.link,cc_id:0
80579#, python-format80586#, python-format
80580msgid "Cost Center"80587msgid "Cost Center"
80581msgstr "Centre de Coût"80588msgstr "Centre de Coût"
@@ -94527,6 +94534,8 @@
94527#: code:addons/stock_override/report/report_stock_move.py:75994534#: code:addons/stock_override/report/report_stock_move.py:759
94528#: report:addons/account/report/invoice_excel_export.mako:7594535#: report:addons/account/report/invoice_excel_export.mako:75
94529#: field:financing.contract.account.quadruplet,account_destination_id:094536#: field:financing.contract.account.quadruplet,account_destination_id:0
94537#: field:dest.cc.link,dest_id:0
94538#: field:multiple.cc.selection.wizard,dest_id:0
94530#, python-format94539#, python-format
94531msgid "Destination"94540msgid "Destination"
94532msgstr "Destination"94541msgstr "Destination"
@@ -95899,7 +95908,7 @@
95899msgid "Search Uninvoiced Lines"95908msgid "Search Uninvoiced Lines"
95900msgstr "Rechercher Lignes non-facturées"95909msgstr "Rechercher Lignes non-facturées"
9590195910
95902#. modules: res_currency_functional, financing_contract, msf_tools, account_hq_entries, account_override, product_attributes, base_report_designer, register_accounting, procurement_cycle, msf_accrual, finance, sync_client, purchase_followup, account_mcdb, res_currency_tables, supplier_catalogue, procurement_request, purchase_compare_rfq, board, stock_override, msf_doc_import, analytic_distribution, threshold_value, msf_homere_interface, msf_instance, account_reconciliation, consumption_calculation, purchase_override, specific_rules, kit, base, account_period_closing_level, msf_currency_revaluation, msf_supply_doc_export, product_list, procurement_report, msf_budget, account_corrections, account, msf_outgoing, stock_move_tracking, purchase_allocation_report, procurement_auto, documents_done, sale, msf_config_locations, sales_followup, vertical_integration, procurement, sourcing, purchase, msf_audittrail, tender_flow, stock, msf_profile95911#. modules: res_currency_functional, financing_contract, msf_tools, account_hq_entries, account_override, product_attributes, base_report_designer, register_accounting, procurement_cycle, msf_accrual, finance, sync_client, purchase_followup, account_mcdb, res_currency_tables, supplier_catalogue, procurement_request, purchase_compare_rfq, board, stock_override, msf_doc_import, analytic_distribution, threshold_value, msf_homere_interface, msf_instance, account_reconciliation, consumption_calculation, purchase_override, specific_rules, kit, base, account_period_closing_level, msf_currency_revaluation, msf_supply_doc_export, product_list, procurement_report, msf_budget, account_corrections, account, msf_outgoing, stock_move_tracking, purchase_allocation_report, procurement_auto, documents_done, sale, msf_config_locations, sales_followup, vertical_integration, procurement, sourcing, purchase, msf_audittrail, tender_flow, stock, msf_profile, analytic_override
95903#: view:account.addtmpl.wizard:095912#: view:account.addtmpl.wizard:0
95904#: view:account.aged.trial.balance:095913#: view:account.aged.trial.balance:0
95905#: view:account.analytic.Journal.report:095914#: view:account.analytic.Journal.report:0
@@ -96133,6 +96142,7 @@
96133#: view:product.stock_out:096142#: view:product.stock_out:0
96134#: view:replenishment.parent.segment:096143#: view:replenishment.parent.segment:0
96135#: view:view.expired.expiring.stock:096144#: view:view.expired.expiring.stock:0
96145#: view:multiple.cc.selection.wizard:0
96136#, python-format96146#, python-format
96137msgid "Cancel"96147msgid "Cancel"
96138msgstr "Annuler"96148msgstr "Annuler"
@@ -99911,6 +99921,12 @@
99911msgstr "%s: FP inactif (%s)"99921msgstr "%s: FP inactif (%s)"
9991299922
99913#. module: account_hq_entries99923#. module: account_hq_entries
99924#: code:addons/account_hq_entries/hq_entries.py:94
99925#, python-format
99926msgid "%s: inactive combination (%s - %s)"
99927msgstr "%s: combinaison inactive (%s - %s)"
99928
99929#. module: account_hq_entries
99914#: field:hq.entries,is_account_partner_compatible:099930#: field:hq.entries,is_account_partner_compatible:0
99915msgid "Account and partner compatible ?"99931msgid "Account and partner compatible ?"
99916msgstr "Compte et partenaire compatibles ?"99932msgstr "Compte et partenaire compatibles ?"
@@ -100222,11 +100238,23 @@
100222msgstr "Date du CC"100238msgstr "Date du CC"
100223100239
100224#. module: analytic_distribution100240#. module: analytic_distribution
100241#: code:addons/analytic_distribution/analytic_line.py:563
100242#, python-format
100243msgid "DEST/CC combination date"
100244msgstr "Date de la combinaison DEST/CC"
100245
100246#. module: analytic_distribution
100225#: code:addons/analytic_distribution/analytic_line.py:548100247#: code:addons/analytic_distribution/analytic_line.py:548
100226#, python-format100248#, python-format
100227msgid "FP date"100249msgid "FP date"
100228msgstr "Date du FP"100250msgstr "Date du FP"
100229100251
100252#. module: analytic_distribution
100253#: code:addons/analytic_distribution/analytic_distribution.py:237
100254#, python-format
100255msgid "Inactive DEST/CC combination"
100256msgstr "Combinaison DEST/CC inactive"
100257
100230#. module: msf_printed_documents100258#. module: msf_printed_documents
100231#: code:addons/msf_printed_documents/report/report_reception.py:80100259#: code:addons/msf_printed_documents/report/report_reception.py:80
100232#, python-format100260#, python-format
@@ -112023,3 +112051,124 @@
112023#, python-format112051#, python-format
112024msgid "You must have at least one line to create the Internal Move"112052msgid "You must have at least one line to create the Internal Move"
112025msgstr "Vous devez avoir au moins une ligne pour pouvoir créer le Mouvement Interne"112053msgstr "Vous devez avoir au moins une ligne pour pouvoir créer le Mouvement Interne"
112054
112055#. module: analytic_distribution
112056#: view:account.analytic.account:0
112057msgid "Remove all"
112058msgstr "Supprimer tout"
112059
112060#. module: analytic_distribution
112061#: view:account.analytic.account:0
112062msgid "Do you really want to remove all the Cost Centers selected?"
112063msgstr "Voulez-vous vraiment supprimer tous les Centres de Coût sélectionnés ?"
112064
112065#. modules: analytic_override, analytic_distribution
112066#: view:account.analytic.account:0
112067#: view:multiple.cc.selection.wizard:0
112068msgid "Add several Cost Centers"
112069msgstr "Ajouter plusieurs Centres de Coût"
112070
112071#. modules: msf_doc_import, analytic_override
112072#: field:account.analytic.account,dest_cc_link_active_from:0
112073#: field:dest.cc.link,active_from:0
112074#: code:addons/msf_doc_import/msf_import_export.py:639
112075#, python-format
112076msgid "Activation Combination Dest / CC from"
112077msgstr "Activation Combinaison Dest / CC à partir du"
112078
112079#. modules: msf_doc_import, analytic_override
112080#: field:account.analytic.account,dest_cc_link_inactive_from:0
112081#: field:dest.cc.link,inactive_from:0
112082#: code:addons/msf_doc_import/msf_import_export.py:640
112083#, python-format
112084msgid "Inactivation Combination Dest / CC from"
112085msgstr "Inactivation Combinaison Dest / CC à partir du"
112086
112087#. module: analytic_override
112088#: help:account.analytic.account,dest_cc_link_active_from:0
112089#: help:account.analytic.account,dest_cc_link_inactive_from:0
112090msgid "Technical field used for Import Tools only"
112091msgstr "Champ technique utilisé pour les Outils d'Import uniquement"
112092
112093#. module: analytic_override
112094#: field:account.analytic.account,selected_in_dest:0
112095msgid "Selected in Destination"
112096msgstr "Sélectionné dans la Destination"
112097
112098#. module: analytic_override
112099#: model:ir.model,name:analytic_override.model_dest_cc_link
112100msgid "Destination / Cost Center Combination"
112101msgstr "Combinaison Destination / Centre de Coût"
112102
112103#. module: analytic_override
112104#: field:dest.cc.link,cc_code:0
112105msgid "Cost Center Code"
112106msgstr "Code du Centre de Coût"
112107
112108#. module: analytic_override
112109#: field:dest.cc.link,cc_name:0
112110msgid "Cost Center Name"
112111msgstr "Nom du Centre de Coût"
112112
112113#. module: analytic_override
112114#: sql_constraint:dest.cc.link:0
112115msgid "Each Cost Center can only be added once to the same Destination."
112116msgstr "Chaque Centre de Coût ne peut être ajouté qu'une seule fois à une même Destination."
112117
112118#. module: analytic_override
112119#: sql_constraint:dest.cc.link:0
112120msgid "The Activation date of the \"Combination Dest / CC\" must be before the Inactivation date."
112121msgstr "La date d'Activation de la \"Combinaison Dest / CC\" doit précéder la date d'Inactivation."
112122
112123#. module: msf_doc_import
112124#: code:addons/msf_doc_import/msf_import_export.py:663
112125#, python-format
112126msgid "The number of dates in the column \"%s\" exceeds the number of Cost Centers indicated."
112127msgstr "Le nombre de dates dans la colonne \"%s\" dépasse le nombre de Centres de Coût indiqués."
112128
112129#. module: msf_doc_import
112130#: code:addons/msf_doc_import/msf_import_export.py:679
112131#, python-format
112132msgid "The activation date related to the Cost Center %s must be before the inactivation date."
112133msgstr "La date d'activation associée au Centre de Coût %s doit précéder la date d'inactivation."
112134
112135#. module: msf_doc_import
112136#: code:addons/msf_doc_import/msf_import_export.py:657
112137#, python-format
112138msgid "The dates in the column \"%s\" should use the format YYYY-MM-DD."
112139msgstr "Les dates dans la colonne \"%s\" doivent utiliser le format AAAA-MM-JJ."
112140
112141#. module: analytic_override
112142#: view:multiple.cc.selection.wizard:0
112143msgid "This wizard enables you to select several Cost Centers at once to be added to the Destination. The activation and inactivation dates of the related Dest / CC combinations will remain empty."
112144msgstr "Cet assistant vous permet de sélectionner en une fois plusieurs Centres de Coût à ajouter à la Destination. Les dates d'activation et d'inactivation des Combinaisons Dest / CC associées resteront vides."
112145
112146#. module: analytic_override
112147#: field:dest.cc.link,current_id:0
112148msgid "DB Id (used by the UI)"
112149msgstr "Id BD (utilisé par l'UI)"
112150
112151#. module: analytic_override
112152#: code:addons/analytic_override/analytic_line.py:192
112153#, python-format
112154msgid "The combination \"%s - %s\" is not active."
112155msgstr "La combinaison \"%s - %s\" n'est pas active."
112156
112157#. module: analytic_distribution
112158#: code:addons/analytic_distribution/wizard/analytic_distribution_wizard.py:1024
112159#: code:addons/analytic_distribution/account_commitment.py:204
112160#, python-format
112161msgid "The combination \"%s - %s\" is not active at this date: %s"
112162msgstr "La combinaison \"%s - %s\" n'est pas active à cette date : %s"
112163
112164#. module: analytic_distribution
112165#: code:addons/analytic_distribution/analytic_distribution.py:264
112166#, python-format
112167msgid "%sThe combination \"%s - %s\" is not active at this date: %s"
112168msgstr "%sLa combinaison \"%s - %s\" n'est pas active à cette date : %s"
112169
112170#. module: analytic_override
112171#: code:addons/analytic_override/dest_cc_link.py:72
112172#, python-format
112173msgid "At least one Analytic Journal Item using the combination \"%s - %s\" has a Posting Date outside the activation dates selected."
112174msgstr "Au moins une Ligne d'Ecriture Analytique utilisant la combinaison \"%s - %s\" a une Date de Comptabilisation en dehors des dates d'activation sélectionnées."
112026112175
=== modified file 'bin/addons/msf_profile/msf_profile.py'
--- bin/addons/msf_profile/msf_profile.py 2021-05-05 07:42:20 +0000
+++ bin/addons/msf_profile/msf_profile.py 2021-05-07 16:01:07 +0000
@@ -52,6 +52,7 @@
52 'model': lambda *a: 'patch.scripts',52 'model': lambda *a: 'patch.scripts',
53 }53 }
5454
55<<<<<<< TREE
55 # UF21.056 # UF21.0
56 def us_8166_hide_consolidated_sm_report(self, cr, uid, *a, **b):57 def us_8166_hide_consolidated_sm_report(self, cr, uid, *a, **b):
57 instance = self.pool.get('res.users').browse(cr, uid, uid, fields_to_fetch=['company_id']).company_id.instance_id58 instance = self.pool.get('res.users').browse(cr, uid, uid, fields_to_fetch=['company_id']).company_id.instance_id
@@ -61,6 +62,22 @@
61 self.pool.get('ir.ui.menu').write(cr, uid, consolidated_sm_report_menu_id, {'active': instance.level == 'coordo'}, context={})62 self.pool.get('ir.ui.menu').write(cr, uid, consolidated_sm_report_menu_id, {'active': instance.level == 'coordo'}, context={})
62 return True63 return True
6364
65=======
66 # UF21.0
67 def us_7295_update_new_dest_cc_link(self, cr, uid, *a, **b):
68 """
69 CC Tab of the Destinations: replaces the old field "dest_cc_ids" by the new field "dest_cc_link_ids"
70 => recreates the links without activation/inactivation dates
71 """
72 cr.execute("""
73 INSERT INTO dest_cc_link(dest_id, cc_id)
74 SELECT destination_id, cost_center_id FROM destination_cost_center_rel
75 """)
76 cr.execute("DELETE FROM destination_cost_center_rel")
77 self._logger.warn('Destinations: %s Dest CC Links generated.', cr.rowcount)
78 return True
79
80>>>>>>> MERGE-SOURCE
64 # UF20.081 # UF20.0
65 def us_7866_fill_in_target_cc_code(self, cr, uid, *a, **b):82 def us_7866_fill_in_target_cc_code(self, cr, uid, *a, **b):
66 """83 """
6784
=== 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 2021-04-12 09:46:40 +0000
+++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2021-05-07 16:01:07 +0000
@@ -34,6 +34,7 @@
34msf_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,,13034msf_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
35msf_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,,14035msf_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
36msf_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,,14536msf_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
37msf_sync_data_server.dest_cc_link,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional-Private,"[]","['dest_id/id', 'cc_id/id', 'active_from', 'inactive_from']",OC,dest.cc.link,cc_id,Destination / Cost Center Combinations,Valid,,146
37msf_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,,15038msf_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
38msf_sync_data_server.analytic_distribution,FALSE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],['name'],HQ + MISSION,analytic.distribution,,Analytic Distribution,Valid,,20039msf_sync_data_server.analytic_distribution,FALSE,TRUE,FALSE,FALSE,bidirectional,Bidirectional,[],['name'],HQ + MISSION,analytic.distribution,,Analytic Distribution,Valid,,200
39msf_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,,20140msf_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
4041
=== modified file 'bin/addons/sync_client/update.py'
--- bin/addons/sync_client/update.py 2021-01-18 14:22:04 +0000
+++ bin/addons/sync_client/update.py 2021-05-07 16:01:07 +0000
@@ -58,6 +58,7 @@
58 'account.mcdb',58 'account.mcdb',
59 'wizard.template',59 'wizard.template',
60 'account.analytic.account',60 'account.analytic.account',
61 'dest.cc.link',
61]62]
6263
6364
6465
=== modified file 'bin/addons/sync_common/common.py'
--- bin/addons/sync_common/common.py 2020-10-20 07:15:50 +0000
+++ bin/addons/sync_common/common.py 2021-05-07 16:01:07 +0000
@@ -168,6 +168,7 @@
168 'cash.request.liquidity.total',168 'cash.request.liquidity.total',
169 'hr.payment.method',169 'hr.payment.method',
170 'wizard.template',170 'wizard.template',
171 'dest.cc.link',
171]172]
172173
173OC_LIST = ['OCA', 'OCB', 'OCBA', 'OCG', 'OCP']174OC_LIST = ['OCA', 'OCB', 'OCBA', 'OCG', 'OCP']
174175
=== modified file 'bin/addons/sync_so/specific_xml_id.py'
--- bin/addons/sync_so/specific_xml_id.py 2020-11-09 10:40:25 +0000
+++ bin/addons/sync_so/specific_xml_id.py 2021-05-07 16:01:07 +0000
@@ -307,6 +307,30 @@
307307
308account_analytic_account()308account_analytic_account()
309309
310class dest_cc_link(osv.osv):
311 _inherit = 'dest.cc.link'
312
313 def get_destination_name(self, cr, uid, ids, dest_field, context=None):
314 '''
315 same destination as CC
316 '''
317 if not ids:
318 return []
319
320 if isinstance(ids, (long, int)):
321 ids = [ids]
322 res = dict.fromkeys(ids, False)
323 mapping = {}
324 uniq_cc_ids = {}
325 for dest_cc_link in self.browse(cr, uid, ids, fields_to_fetch=['cc_id'], context=context):
326 mapping[dest_cc_link.id] = dest_cc_link.cc_id.id
327 uniq_cc_ids[dest_cc_link.cc_id.id] = True
328 cc_destination = self.pool.get('account.analytic.account').get_destination_name(cr, uid, uniq_cc_ids.keys(), 'category', context)
329 for dest_cc_link_id in mapping:
330 res[dest_cc_link_id] = cc_destination.get(mapping[dest_cc_link_id], [])
331 return res
332
333dest_cc_link()
310334
311#US-113: Sync only to the mission with attached prop instance335#US-113: Sync only to the mission with attached prop instance
312class financing_contract_contract(osv.osv):336class financing_contract_contract(osv.osv):
313337
=== modified file 'bin/osv/orm.py'
--- bin/osv/orm.py 2021-02-02 10:20:51 +0000
+++ bin/osv/orm.py 2021-05-07 16:01:07 +0000
@@ -733,12 +733,21 @@
733733
734 if no_data:734 if no_data:
735 dt = ''735 dt = ''
736 rel_table_name = r[0]._table_name
737 name_relation = self.pool.get(rel_table_name)._rec_name
738 if isinstance(r[0][name_relation], browse_record):
739 rel = True
740 rel_table_name = r[0][name_relation]._table_name
741 all_rr = [rr[name_relation].id for rr in r]
742 else:
743 rel = False
744 all_rr = [rr.id for rr in r]
745 all_name_get = dict(self.pool.get(rel_table_name).name_get(cr, uid, all_rr, context=context))
736 for rr in r:746 for rr in r:
737 name_relation = self.pool.get(rr._table_name)._rec_name747 if not rel:
738 if isinstance(rr[name_relation], browse_record):748 rr_name = all_name_get.get(rr.id, '')
739 rr = rr[name_relation]749 else:
740 rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id], context=context)750 rr_name = all_name_get.get(rr[name_relation].id, '')
741 rr_name = rr_name and rr_name[0] and rr_name[0][1] or ''
742 dt += tools.ustr(rr_name or '') + ','751 dt += tools.ustr(rr_name or '') + ','
743 data[fpos] = dt[:-1]752 data[fpos] = dt[:-1]
744 break753 break

Subscribers

People subscribed via source and target branches