Merge lp:~julie-w/unifield-server/US-7244 into lp:unifield-server
- US-7244
- Merge into trunk
Proposed by
jftempo
Status: | Merged |
---|---|
Merged at revision: | 5874 |
Proposed branch: | lp:~julie-w/unifield-server/US-7244 |
Merge into: | lp:unifield-server |
Diff against target: |
3342 lines (+1352/-694) 41 files modified
bin/addons/account/account.py (+19/-0) bin/addons/account/account_view.xml (+51/-0) bin/addons/account_corrections/wizard/analytic_distribution_wizard.py (+5/-0) bin/addons/account_hq_entries/hq_entries.py (+10/-35) bin/addons/account_hq_entries/wizard/hq_entries_split.py (+0/-30) bin/addons/account_hq_entries/wizard/hq_reallocation.py (+6/-28) bin/addons/account_hq_entries/wizard/wizard_view.xml (+5/-2) bin/addons/account_mcdb/mass_reallocation_search.py (+4/-7) bin/addons/account_override/account.py (+85/-0) bin/addons/analytic_distribution/account.py (+6/-3) bin/addons/analytic_distribution/account_commitment_view.xml (+1/-1) bin/addons/analytic_distribution/account_move_line.py (+11/-2) bin/addons/analytic_distribution/analytic_account.py (+0/-5) bin/addons/analytic_distribution/analytic_account_view.xml (+29/-6) bin/addons/analytic_distribution/analytic_distribution.py (+101/-26) bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml (+1/-1) bin/addons/analytic_distribution/analytic_line.py (+25/-39) bin/addons/analytic_distribution/report/funding_pool.py (+1/-0) bin/addons/analytic_distribution/report/funding_pool.rml (+26/-2) bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py (+15/-52) bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py (+5/-26) bin/addons/analytic_override/analytic_account.py (+213/-13) bin/addons/financing_contract/contract.py (+9/-12) bin/addons/financing_contract/financing_contract_account_quadruplet.py (+191/-85) bin/addons/financing_contract/financing_contract_view.xml (+52/-9) bin/addons/financing_contract/format.py (+5/-27) bin/addons/financing_contract/format_line.py (+228/-81) bin/addons/financing_contract/report/financing_contract.py (+4/-0) bin/addons/financing_contract/report/report_project_expenses.py (+33/-54) bin/addons/msf_audittrail/audittrail_invoice_data.yml (+1/-1) bin/addons/msf_doc_import/account.py (+9/-21) bin/addons/msf_homere_interface/hr.py (+16/-31) bin/addons/msf_homere_interface/hr_payroll.py (+26/-41) bin/addons/msf_homere_interface/hr_payroll_wizard.xml (+3/-1) bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py (+3/-31) bin/addons/msf_instance/account_target_costcenter.py (+2/-2) bin/addons/msf_profile/data/patches.xml (+4/-0) bin/addons/msf_profile/i18n/fr_MF.po (+121/-12) bin/addons/msf_profile/msf_profile.py (+20/-4) bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv (+3/-3) bin/addons/register_accounting/wizard/wizard_cash_return.py (+3/-1) |
To merge this branch: | bzr merge lp:~julie-w/unifield-server/US-7244 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
UniField Reviewer Team | Pending | ||
Review via email: mp+393179@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/addons/account/account.py' |
2 | --- bin/addons/account/account.py 2020-08-07 12:54:32 +0000 |
3 | +++ bin/addons/account/account.py 2020-11-02 12:53:09 +0000 |
4 | @@ -517,6 +517,25 @@ |
5 | res['value'] = {'prevent_multi_curr_rec': False} |
6 | return res |
7 | |
8 | + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
9 | + """ |
10 | + Displays specific views when G/L accounts are selected from a Funding Pool, a Financing Contract or a Donor |
11 | + """ |
12 | + if context is None: |
13 | + context = {} |
14 | + ir_model_obj = self.pool.get('ir.model.data') |
15 | + if context.get('from_fp') or context.get('from_grant_management'): |
16 | + view = False |
17 | + module = 'account' |
18 | + if view_type == 'search': |
19 | + search_view_name = context.get('from_grant_management') and 'view_account_contract_search' or 'view_account_fp_search' |
20 | + view = ir_model_obj.get_object_reference(cr, uid, module, search_view_name) |
21 | + elif view_type == 'tree': |
22 | + view = ir_model_obj.get_object_reference(cr, uid, module, 'view_account_fp_tree') |
23 | + if view: |
24 | + view_id = view[1] |
25 | + return super(account_account, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu) |
26 | + |
27 | |
28 | account_account() |
29 | |
30 | |
31 | === modified file 'bin/addons/account/account_view.xml' |
32 | --- bin/addons/account/account_view.xml 2020-09-08 16:37:35 +0000 |
33 | +++ bin/addons/account/account_view.xml 2020-11-02 12:53:09 +0000 |
34 | @@ -239,6 +239,57 @@ |
35 | </field> |
36 | </record> |
37 | |
38 | + <!-- G/L account Search View to be displayed from FP --> |
39 | + <record id="view_account_fp_search" model="ir.ui.view"> |
40 | + <field name="name">account.account.fp.search</field> |
41 | + <field name="model">account.account</field> |
42 | + <field name="type">search</field> |
43 | + <field name="priority" eval="90"/> |
44 | + <field name="arch" type="xml"> |
45 | + <search> |
46 | + <group> |
47 | + <field name="code"/> |
48 | + <field name="name"/> |
49 | + </group> |
50 | + </search> |
51 | + </field> |
52 | + </record> |
53 | + |
54 | + <!-- G/L account Search View to be displayed from contracts/donors --> |
55 | + <record id="view_account_contract_search" model="ir.ui.view"> |
56 | + <field name="name">account.account.contract.search</field> |
57 | + <field name="model">account.account</field> |
58 | + <field name="type">search</field> |
59 | + <field name="priority" eval="91"/> |
60 | + <field name="arch" type="xml"> |
61 | + <search> |
62 | + <group> |
63 | + <filter name="active" string="Active" icon="terp-check" domain="[('filter_active', '=', True)]" /> |
64 | + <filter name="inactive" string="Inactive" icon="gtk-dialog-error" domain="[('filter_active', '=', False)]" /> |
65 | + <field name="code"/> |
66 | + <field name="name"/> |
67 | + </group> |
68 | + </search> |
69 | + </field> |
70 | + </record> |
71 | + |
72 | + <!-- G/L account Tree View to be displayed from FP and contracts/donors --> |
73 | + <record id="view_account_fp_tree" model="ir.ui.view"> |
74 | + <field name="name">account.account.fp.tree</field> |
75 | + <field name="model">account.account</field> |
76 | + <field name="type">tree</field> |
77 | + <field name="priority" eval="90"/> |
78 | + <field name="arch" type="xml"> |
79 | + <tree string="G/L Accounts" colors="grey:selected_in_fp or selected_in_contract" |
80 | + notselectable="selected_in_fp or selected_in_contract" > |
81 | + <field name="code"/> |
82 | + <field name="name"/> |
83 | + <field name="selected_in_fp" invisible="1"/> |
84 | + <field name="selected_in_contract" invisible="1"/> |
85 | + </tree> |
86 | + </field> |
87 | + </record> |
88 | + |
89 | <record id="view_account_list" model="ir.ui.view"> |
90 | <field name="name">account.account.list</field> |
91 | <field name="model">account.account</field> |
92 | |
93 | === modified file 'bin/addons/account_corrections/wizard/analytic_distribution_wizard.py' |
94 | --- bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2020-02-06 16:52:43 +0000 |
95 | +++ bin/addons/account_corrections/wizard/analytic_distribution_wizard.py 2020-11-02 12:53:09 +0000 |
96 | @@ -269,6 +269,11 @@ |
97 | _('The Cost Center %s is not compatible with the Destination %s.') % |
98 | (wiz_line.cost_center_id.code or '', wiz_line.destination_id.code or '')) |
99 | |
100 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, wiz_line.analytic_id.id, wiz_line.cost_center_id.id, context=context): |
101 | + raise osv.except_osv(_('Error'), |
102 | + _('The Cost Center %s is not compatible with the Funding Pool %s.') % |
103 | + (wiz_line.cost_center_id.code or '', wiz_line.analytic_id.code or '')) |
104 | + |
105 | if not wiz_line.distribution_line_id or wiz_line.distribution_line_id.id not in old_line_ids: |
106 | # new distribution line |
107 | #if self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [wiz_line.analytic_id.id]): |
108 | |
109 | === modified file 'bin/addons/account_hq_entries/hq_entries.py' |
110 | --- bin/addons/account_hq_entries/hq_entries.py 2019-03-28 13:35:11 +0000 |
111 | +++ bin/addons/account_hq_entries/hq_entries.py 2020-11-02 12:53:09 +0000 |
112 | @@ -106,7 +106,7 @@ |
113 | continue |
114 | if line.analytic_id and not line.destination_id: # CASE 2/ |
115 | # D Check, except B check |
116 | - if line.cost_center_id.id not in [x.id for x in line.analytic_id.cost_center_ids] and line.analytic_id.id != fp_id: |
117 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, line.analytic_id.id, line.cost_center_id.id, context=context): |
118 | res[line.id] = 'invalid' |
119 | logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: CC (%s) not found in FP (%s)') % (line.id or '', line.cost_center_id.code or '', line.analytic_id.code or '')) |
120 | continue |
121 | @@ -119,12 +119,13 @@ |
122 | continue |
123 | else: # CASE 4/ |
124 | # C Check, except B |
125 | - if (line.account_id.id, line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in line.analytic_id.tuple_destination_account_ids if not x.disabled] and line.analytic_id.id != fp_id: |
126 | + if not ad_obj.check_fp_acc_dest_compatibility(cr, uid, line.analytic_id.id, line.account_id.id, |
127 | + line.destination_id.id, context=context): |
128 | res[line.id] = 'invalid' |
129 | logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: Tuple Account/DEST (%s/%s) not found in FP (%s)') % (line.id or '', line.account_id.code or '', line.destination_id.code or '', line.analytic_id.code or '')) |
130 | continue |
131 | # D Check, except B check |
132 | - if line.cost_center_id.id not in [x.id for x in line.analytic_id.cost_center_ids] and line.analytic_id.id != fp_id: |
133 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, line.analytic_id.id, line.cost_center_id.id, context=context): |
134 | res[line.id] = 'invalid' |
135 | logger.notifyChannel('account_hq_entries', netsvc.LOG_WARNING, _('%s: CC (%s) not found in FP (%s)') % (line.id or '', line.cost_center_id.code or '', line.analytic_id.code or '')) |
136 | continue |
137 | @@ -458,7 +459,7 @@ |
138 | |
139 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
140 | """ |
141 | - Change funding pool domain in order to include MSF Private fund |
142 | + Adapts domain for AD fields |
143 | """ |
144 | if context is None: |
145 | context = {} |
146 | @@ -466,11 +467,9 @@ |
147 | arch = etree.fromstring(view['arch']) |
148 | fields = arch.xpath('field[@name="analytic_id"]') |
149 | if fields: |
150 | - try: |
151 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
152 | - except ValueError: |
153 | - fp_id = 0 |
154 | - fields[0].set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id) |
155 | + fields[0].set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
156 | + "('fp_compatible_with_cc_ids', '=', cost_center_id), " |
157 | + "('fp_compatible_with_acc_dest_ids', '=', (account_id, destination_id))]") |
158 | # Change Destination field |
159 | dest_fields = arch.xpath('field[@name="destination_id"]') |
160 | for field in dest_fields: |
161 | @@ -479,32 +478,8 @@ |
162 | return view |
163 | |
164 | def onchange_destination(self, cr, uid, ids, destination_id=False, funding_pool_id=False, account_id=False): |
165 | - """ |
166 | - Check given funding pool with destination |
167 | - """ |
168 | - # Prepare some values |
169 | - res = {} |
170 | - # If all elements given, then search FP compatibility |
171 | - if destination_id and funding_pool_id and account_id: |
172 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id) |
173 | - # Search MSF Private Fund element, because it's valid with all accounts |
174 | - try: |
175 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
176 | - 'analytic_account_msf_private_funds')[1] |
177 | - except ValueError: |
178 | - fp_id = 0 |
179 | - # Delete funding_pool_id if not valid with tuple "account_id/destination_id". |
180 | - # but do an exception for MSF Private FUND analytic account |
181 | - if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.tuple_destination_account_ids if not x.disabled] and funding_pool_id != fp_id: |
182 | - res = {'value': {'analytic_id': False}} |
183 | - # If no destination, do nothing |
184 | - elif not destination_id: |
185 | - res = {} |
186 | - # Otherway: delete FP |
187 | - else: |
188 | - res = {'value': {'analytic_id': False}} |
189 | - # If destination given, search if given |
190 | - return res |
191 | + return self.pool.get('analytic.distribution').\ |
192 | + onchange_ad_destination(cr, uid, ids, destination_id=destination_id, funding_pool_id=funding_pool_id, account_id=account_id) |
193 | |
194 | def _check_cc(self, cr, uid, ids, context=None): |
195 | """ |
196 | |
197 | === modified file 'bin/addons/account_hq_entries/wizard/hq_entries_split.py' |
198 | --- bin/addons/account_hq_entries/wizard/hq_entries_split.py 2018-08-17 08:28:25 +0000 |
199 | +++ bin/addons/account_hq_entries/wizard/hq_entries_split.py 2020-11-02 12:53:09 +0000 |
200 | @@ -271,36 +271,6 @@ |
201 | return super(hq_entries_split, self).create(cr, uid, vals, |
202 | context=context) |
203 | |
204 | - # UFTP-200: Add the correct funding pool domain to the split line based on the account_id and cost_center |
205 | - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
206 | - """ |
207 | - Change funding pool domain in order to include MSF Private fund |
208 | - """ |
209 | - if context is None: |
210 | - context = {} |
211 | - view = super(hq_entries_split, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu) |
212 | - fields = view['fields'] |
213 | - if view_type=='form' and fields: |
214 | - if fields.get('line_ids') and fields.get('line_ids')['views']: |
215 | - # get the default PF and include into the domain for analytic_id |
216 | - try: |
217 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
218 | - except ValueError: |
219 | - fp_id = 0 |
220 | - |
221 | - viewtemp = fields.get('line_ids')['views'] |
222 | - arch = etree.fromstring(viewtemp['tree']['arch']) # the analytic_id is found in the line_ids, one level down |
223 | - fields = arch.xpath('field[@name="analytic_id"]') |
224 | - if fields: |
225 | - fields[0].set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id) |
226 | - |
227 | - # Change Destination field |
228 | - dest_fields = arch.xpath('field[@name="destination_id"]') |
229 | - for field in dest_fields: |
230 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'DEST'), ('destination_ids', '=', account_id)]") |
231 | - viewtemp['tree']['arch'] = etree.tostring(arch) |
232 | - return view |
233 | - |
234 | def button_validate(self, cr, uid, ids, context=None): |
235 | """ |
236 | Validate wizard lines and create new split HQ lines. |
237 | |
238 | === modified file 'bin/addons/account_hq_entries/wizard/hq_reallocation.py' |
239 | --- bin/addons/account_hq_entries/wizard/hq_reallocation.py 2019-02-06 09:25:31 +0000 |
240 | +++ bin/addons/account_hq_entries/wizard/hq_reallocation.py 2020-11-02 12:53:09 +0000 |
241 | @@ -51,7 +51,7 @@ |
242 | |
243 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
244 | """ |
245 | - Change funding pool domain in order to include MSF Private fund |
246 | + Adapts domain for AD fields |
247 | """ |
248 | if not context: |
249 | context = {} |
250 | @@ -68,39 +68,17 @@ |
251 | for field in fields: |
252 | field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id) |
253 | # Change FP field |
254 | - try: |
255 | - fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
256 | - except ValueError: |
257 | - fp_id = 0 |
258 | fp_fields = form.xpath('//field[@name="analytic_id"]') |
259 | - # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE! |
260 | + # no restrictions are related to the G/L accounts because the wizard isn't linked to one single line with a specific account_id |
261 | for field in fp_fields: |
262 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id) |
263 | - # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE! |
264 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
265 | + "('fp_compatible_with_cc_ids', '=', cost_center_id)]") |
266 | view['arch'] = etree.tostring(form) |
267 | return view |
268 | |
269 | def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, analytic_id=False): |
270 | - """ |
271 | - Check given cost_center with funding pool |
272 | - """ |
273 | - # Prepare some values |
274 | - res = {} |
275 | - if cost_center_id and analytic_id: |
276 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, analytic_id) |
277 | - # Search MSF Private Fund element, because it's valid with all accounts |
278 | - try: |
279 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
280 | - 'analytic_account_msf_private_funds')[1] |
281 | - except ValueError: |
282 | - fp_id = 0 |
283 | - if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and analytic_id != fp_id: |
284 | - res = {'value': {'analytic_id': False}} |
285 | - elif not cost_center_id: |
286 | - res = {} |
287 | - else: |
288 | - res = {'value': {'analytic_id': False}} |
289 | - return res |
290 | + return self.pool.get('analytic.distribution').\ |
291 | + onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=analytic_id, fp_field_name='analytic_id') |
292 | |
293 | def button_validate(self, cr, uid ,ids, context=None): |
294 | """ |
295 | |
296 | === modified file 'bin/addons/account_hq_entries/wizard/wizard_view.xml' |
297 | --- bin/addons/account_hq_entries/wizard/wizard_view.xml 2019-01-08 11:20:04 +0000 |
298 | +++ bin/addons/account_hq_entries/wizard/wizard_view.xml 2020-11-02 12:53:09 +0000 |
299 | @@ -48,10 +48,13 @@ |
300 | domain="[('restricted_area', '=', 'hq_lines_correction'), ('filter_active', '=', True)]" |
301 | /> |
302 | <field name="amount"/> |
303 | - <field name="destination_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}"/> |
304 | + <field name="destination_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}" |
305 | + domain="[('category', '=', 'DEST'), ('type', '!=', 'view'), ('destination_ids', '=', account_id)]"/> |
306 | <field name="cost_center_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}"/> |
307 | <field name="analytic_id" attrs="{'readonly': [('is_not_ad_correctable', '=', True)]}" |
308 | - domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]" |
309 | + domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), |
310 | + ('fp_compatible_with_cc_ids', '=', cost_center_id), |
311 | + ('fp_compatible_with_acc_dest_ids', '=', (account_id, destination_id))]" |
312 | string="Funding Pool" |
313 | context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/> |
314 | <field name="state"/> |
315 | |
316 | === modified file 'bin/addons/account_mcdb/mass_reallocation_search.py' |
317 | --- bin/addons/account_mcdb/mass_reallocation_search.py 2013-10-04 14:54:39 +0000 |
318 | +++ bin/addons/account_mcdb/mass_reallocation_search.py 2020-11-02 12:53:09 +0000 |
319 | @@ -37,8 +37,9 @@ |
320 | context = {} |
321 | if isinstance(ids, (int, long)): |
322 | ids = [ids] |
323 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
324 | # Only process first id |
325 | - account = self.pool.get('account.analytic.account').browse(cr, uid, ids, context=context)[0] |
326 | + account = analytic_acc_obj.browse(cr, uid, ids, context=context)[0] |
327 | if account.category != 'FUNDING': |
328 | raise osv.except_osv(_('Error'), _('This action only works for Funding Pool accounts!')) |
329 | # Take all elements to create a domain |
330 | @@ -53,16 +54,12 @@ |
331 | except ValueError: |
332 | fp_id = 0 |
333 | if account.id != fp_id: |
334 | - if account.tuple_destination_account_ids: |
335 | + if account.tuple_destination_account_ids or account.fp_account_ids: |
336 | + # note: this includes restrictions on Cost Centers |
337 | search.append(('is_fp_compat_with', '=', account.id)) |
338 | else: |
339 | # trick to avoid problem with FP that have NO destination link. So we need to search a "False" Destination. |
340 | search.append(('destination_id', '=', 0)) |
341 | - if account.cost_center_ids: |
342 | - search.append(('cost_center_id', 'in', [x.id for x in account.cost_center_ids])) |
343 | - else: |
344 | - # trick to avoid problem with FP that have NO CC. |
345 | - search.append(('cost_center_id', '=', 0)) |
346 | for criterium in [('account_id', '!=', account.id), ('journal_id.type', '!=', 'engagement'), ('is_reallocated', '=', False), ('is_reversal', '=', False)]: |
347 | search.append(criterium) |
348 | search.append(('contract_open','=', True)) |
349 | |
350 | === modified file 'bin/addons/account_override/account.py' |
351 | --- bin/addons/account_override/account.py 2020-03-05 17:13:57 +0000 |
352 | +++ bin/addons/account_override/account.py 2020-11-02 12:53:09 +0000 |
353 | @@ -363,6 +363,83 @@ |
354 | ret[link['account_id'][0]] = True |
355 | return ret |
356 | |
357 | + def _get_selected_in_fp(self, cr, uid, account_ids, name=False, args=False, context=None): |
358 | + """ |
359 | + Returns True for the G/L accounts already selected in the Funding Pool: |
360 | + they will be displayed in grey in the list and won't be re-selectable. |
361 | + """ |
362 | + if context is None: |
363 | + context = {} |
364 | + if isinstance(account_ids, (int, long)): |
365 | + account_ids = [account_ids] |
366 | + selected = [] |
367 | + acc = context.get('accounts_selected') |
368 | + if acc and isinstance(acc, list) and len(acc) == 1 and len(acc[0]) == 3: |
369 | + selected = acc[0][2] |
370 | + res = {} |
371 | + for account_id in account_ids: |
372 | + res[account_id] = account_id in selected |
373 | + return res |
374 | + |
375 | + def _get_false(self, cr, uid, ids, *a, **b): |
376 | + """ |
377 | + Returns False for all ids |
378 | + """ |
379 | + return {}.fromkeys(ids, False) |
380 | + |
381 | + def _search_selectable_in_contract(self, cr, uid, ids, field_name, arg, context=None): |
382 | + """ |
383 | + Returns a domain with the G/L accounts selectable in the contract in context. |
384 | + The accounts must appear either in the G/L accounts or in the Account/Destination combinations linked to the |
385 | + Funding Pools selected in the contract. |
386 | + """ |
387 | + if context is None: |
388 | + context = {} |
389 | + contract_obj = self.pool.get('financing.contract.contract') |
390 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
391 | + acc_ids = set() |
392 | + if context.get('contract_id'): |
393 | + contract = contract_obj.browse(cr, uid, context['contract_id'], fields_to_fetch=['funding_pool_ids'], context=context) |
394 | + for contract_fp_line in contract.funding_pool_ids: |
395 | + acc_ids.update([t[0] for t in |
396 | + analytic_acc_obj.get_acc_dest_linked_to_fp(cr, uid, contract_fp_line.funding_pool_id.id, context=context)]) |
397 | + return [('id', 'in', list(acc_ids))] |
398 | + |
399 | + def _get_selected_in_contract(self, cr, uid, account_ids, name=False, args=False, context=None): |
400 | + """ |
401 | + Returns True for the G/L accounts already selected in the contract or donor in context: |
402 | + they will be displayed in grey in the list and won't be re-selectable. |
403 | + |
404 | + As soon as an account has been selected in either G/L accounts only, acc/dest combinaisons, or quadruplets, |
405 | + it is seen as already used. |
406 | + """ |
407 | + if context is None: |
408 | + context = {} |
409 | + if isinstance(account_ids, (int, long)): |
410 | + account_ids = [account_ids] |
411 | + res = {} |
412 | + selected = {} |
413 | + current_obj = current_id = False |
414 | + if context.get('contract_id'): |
415 | + current_obj = self.pool.get('financing.contract.contract') |
416 | + current_id = context['contract_id'] |
417 | + elif context.get('donor_id'): |
418 | + current_obj = self.pool.get('financing.contract.donor') |
419 | + current_id = context['donor_id'] |
420 | + if current_obj and current_id: |
421 | + active_id = context.get('active_id', False) |
422 | + for line in current_obj.browse(cr, uid, current_id, fields_to_fetch=['actual_line_ids'], context=context).actual_line_ids: |
423 | + if not active_id or line.id != active_id: # skip the current reporting line |
424 | + for account_destination in line.account_destination_ids: |
425 | + selected[account_destination.account_id.id] = True |
426 | + for account_quadruplet in line.account_quadruplet_ids: |
427 | + selected[account_quadruplet.account_id.id] = True |
428 | + for account in line.reporting_account_ids: |
429 | + selected[account.id] = True |
430 | + for account_id in account_ids: |
431 | + res[account_id] = account_id in selected |
432 | + return res |
433 | + |
434 | _columns = { |
435 | 'name': fields.char('Name', size=128, required=True, select=True, translate=True), |
436 | 'activation_date': fields.date('Active from', required=True), |
437 | @@ -400,6 +477,14 @@ |
438 | 'has_partner_type_empty': fields.boolean('Empty'), # US-1307 empty |
439 | |
440 | 'inactivated_for_dest': fields.function(_get_inactivated_for_dest, method=True, type='boolean', string='Is inactive for destination given in context'), |
441 | + |
442 | + 'selected_in_fp': fields.function(_get_selected_in_fp, string='Selected in Funding Pool', method=True, store=False, type='boolean'), |
443 | + # G/L acc. which CAN BE selected in the Financing Contract: |
444 | + 'selectable_in_contract': fields.function(_get_false, string='Selectable in Contract', method=True, store=False, |
445 | + type='boolean', fnct_search=_search_selectable_in_contract), |
446 | + # G/L acc. which ARE currently selected in the Financing Contract or Donor: |
447 | + 'selected_in_contract': fields.function(_get_selected_in_contract, string='Selected in Contract or Donor', method=True, |
448 | + store=False, type='boolean'), |
449 | } |
450 | |
451 | _defaults = { |
452 | |
453 | === modified file 'bin/addons/analytic_distribution/account.py' |
454 | --- bin/addons/analytic_distribution/account.py 2019-05-13 09:18:37 +0000 |
455 | +++ bin/addons/analytic_distribution/account.py 2020-11-02 12:53:09 +0000 |
456 | @@ -81,8 +81,8 @@ |
457 | return ret |
458 | |
459 | _columns = { |
460 | - 'account_id': fields.many2one('account.account', "G/L Account", required=True, domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]", readonly=True), |
461 | - 'destination_id': fields.many2one('account.analytic.account', "Analytical Destination Account", required=True, domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", readonly=True), |
462 | + 'account_id': fields.many2one('account.account', "G/L Account", required=True, domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]", readonly=True, select=1), |
463 | + 'destination_id': fields.many2one('account.analytic.account', "Analytical Destination Account", required=True, domain="[('type', '!=', 'view'), ('category', '=', 'DEST')]", readonly=True, select=1), |
464 | 'funding_pool_ids': fields.many2many('account.analytic.account', 'funding_pool_associated_destinations', 'tuple_id', 'funding_pool_id', "Funding Pools"), |
465 | 'name': fields.function(_get_tuple_name, method=True, type='char', size=254, string="Name", readonly=True, |
466 | store={ |
467 | @@ -183,6 +183,8 @@ |
468 | # Prepare some values |
469 | if not ids: |
470 | return True |
471 | + if isinstance(ids, (int, long)): |
472 | + ids = [ids] |
473 | if context is None: |
474 | context = {} |
475 | # Check default destination presence |
476 | @@ -196,7 +198,8 @@ |
477 | all_ids.append(dd_id) |
478 | super(account_account, self).write(cr, uid, [a.id], {'destination_ids': [(6, 0, all_ids)]}) |
479 | link_obj = self.pool.get('account.destination.link') |
480 | - link_ids = link_obj.search(cr, uid, [('account_id', 'in', ids), ('disabled', '=', True)], context=context) |
481 | + link_ids = link_obj.search(cr, uid, [('account_id', 'in', ids), ('destination_id', '=', dd_id), ('disabled', '=', True)], |
482 | + context=context) |
483 | if link_ids: |
484 | link_obj.write(cr, uid, link_ids, {'disabled': False}, context=context) |
485 | return res |
486 | |
487 | === modified file 'bin/addons/analytic_distribution/account_commitment_view.xml' |
488 | --- bin/addons/analytic_distribution/account_commitment_view.xml 2020-05-07 08:28:47 +0000 |
489 | +++ bin/addons/analytic_distribution/account_commitment_view.xml 2020-11-02 12:53:09 +0000 |
490 | @@ -204,8 +204,8 @@ |
491 | <field name="arch" type="xml"> |
492 | <form string="Intl Commitments Analytic Reallocation"> |
493 | <group colspan="6" col="6"> |
494 | + <field name="cost_center_id" required="0" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
495 | <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
496 | - <field name="cost_center_id" required="0" on_change="onchange_cost_center(cost_center_id, funding_pool_id)" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
497 | <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
498 | </group> |
499 | <newline/> |
500 | |
501 | === modified file 'bin/addons/analytic_distribution/account_move_line.py' |
502 | --- bin/addons/analytic_distribution/account_move_line.py 2020-02-24 17:11:37 +0000 |
503 | +++ bin/addons/analytic_distribution/account_move_line.py 2020-11-02 12:53:09 +0000 |
504 | @@ -412,6 +412,7 @@ |
505 | context = {} |
506 | if isinstance(ids, (int, long)): |
507 | ids = [ids] |
508 | + ad_obj = self.pool.get('analytic.distribution') |
509 | aml_duplication = '__copy_data_seen' in context and 'account.move.line' in context['__copy_data_seen'] or False |
510 | from_duplication = context.get('copy', False) or aml_duplication |
511 | if context.get('from_je_import', False) or from_duplication: |
512 | @@ -432,7 +433,15 @@ |
513 | vals.update({'destination_id': l.account_id.default_destination_id.id}) |
514 | if l.employee_id.funding_pool_id: |
515 | vals.update({'analytic_id': l.employee_id.funding_pool_id.id}) |
516 | - if vals.get('cost_center_id') not in [cc.id for cc in l.employee_id.funding_pool_id.cost_center_ids]: |
517 | + use_default_pf = False |
518 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, l.employee_id.funding_pool_id.id, l.employee_id.cost_center_id.id, |
519 | + context=context): |
520 | + use_default_pf = True |
521 | + elif 'destination_id' in vals and not ad_obj.check_fp_acc_dest_compatibility(cr, uid, l.employee_id.funding_pool_id.id, |
522 | + l.account_id.id, vals['destination_id'], |
523 | + context=context): |
524 | + use_default_pf = True |
525 | + if use_default_pf: |
526 | # Fetch default funding pool: MSF Private Fund |
527 | try: |
528 | msf_fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
529 | @@ -458,7 +467,7 @@ |
530 | to_change = True |
531 | |
532 | if to_change: |
533 | - distrib_id = self.pool.get('analytic.distribution').create(cr, uid, {'name': 'check_employee_analytic_distribution'}) |
534 | + distrib_id = ad_obj.create(cr, uid, {'name': 'check_employee_analytic_distribution'}, context=context) |
535 | vals.update({'distribution_id': distrib_id, 'percentage': 100.0, 'currency_id': l.currency_id.id}) |
536 | # Create funding pool lines |
537 | self.pool.get('funding.pool.distribution.line').create(cr, uid, vals) |
538 | |
539 | === modified file 'bin/addons/analytic_distribution/analytic_account.py' |
540 | --- bin/addons/analytic_distribution/analytic_account.py 2017-02-10 08:51:30 +0000 |
541 | +++ bin/addons/analytic_distribution/analytic_account.py 2020-11-02 12:53:09 +0000 |
542 | @@ -38,11 +38,6 @@ |
543 | if not default: |
544 | default = {} |
545 | |
546 | - # US-348: Reset some values when duplicating an analytic account |
547 | - # But: duplication of funding pool should carry over the account codes (US-723) |
548 | - account = self.browse(cr, uid, a_id, context=context) |
549 | - if account.category != 'FUNDING': |
550 | - default['tuple_destination_account_ids'] = [] |
551 | default['destination_ids'] = [] |
552 | |
553 | # Copy analytic distribution |
554 | |
555 | === modified file 'bin/addons/analytic_distribution/analytic_account_view.xml' |
556 | --- bin/addons/analytic_distribution/analytic_account_view.xml 2020-01-30 10:18:45 +0000 |
557 | +++ bin/addons/analytic_distribution/analytic_account_view.xml 2020-11-02 12:53:09 +0000 |
558 | @@ -83,19 +83,42 @@ |
559 | <field name="date" select="2"/> |
560 | </page> |
561 | <page string="Cost centers" attrs="{'invisible': [('category', '!=', 'FUNDING')]}"> |
562 | - <button name="button_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="1"/> |
563 | + <field name="allow_all_cc_with_fp" colspan="4" |
564 | + on_change="on_change_allow_all_cc_with_fp(allow_all_cc_with_fp, cost_center_ids)"/> |
565 | + <button name="button_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/> |
566 | <separator/> |
567 | - <field name="cost_center_ids" nolabel="1" domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"> |
568 | + <field name="cost_center_ids" nolabel="1" colspan="4" |
569 | + domain="[('type', '!=', 'view'), ('category', '=', 'OC')]" |
570 | + on_change="on_change_cc_with_fp(cost_center_ids)"> |
571 | <tree string="Cost Centers" > |
572 | <field name="code"/> |
573 | <field name="name"/> |
574 | </tree> |
575 | </field> |
576 | </page> |
577 | - <page string="Destinations" attrs="{'invisible': [('category', '!=', 'FUNDING')]}"> |
578 | - <button name="button_dest_clear" type="object" string="Remove all" icon="gtk-clear" colspan="1"/> |
579 | + <page string="Accounts/Destinations" attrs="{'invisible': [('category', '!=', 'FUNDING')]}"> |
580 | + <field name="select_accounts_only"/> |
581 | + <button name="button_dest_clear" type="object" string="Remove all accounts/destinations" |
582 | + icon="gtk-clear" colspan="4" |
583 | + attrs="{'invisible': [('select_accounts_only', '=', True)]}" |
584 | + /> |
585 | + <button name="button_fp_account_clear" type="object" string="Remove all accounts" |
586 | + icon="gtk-clear" colspan="4" |
587 | + attrs="{'invisible': [('select_accounts_only', '=', False)]}" |
588 | + /> |
589 | <separator/> |
590 | - <field name="tuple_destination_account_ids" nolabel="1" context="{'dest_in_use':tuple_destination_account_ids}"/> |
591 | + <field name="tuple_destination_account_ids" nolabel="1" colspan="4" |
592 | + context="{'dest_in_use': tuple_destination_account_ids}" |
593 | + attrs="{'invisible': [('select_accounts_only', '=', True)]}" |
594 | + /> |
595 | + <field name="fp_account_ids" colspan="4" nolabel="1" |
596 | + context="{'from_fp': True, 'accounts_selected': fp_account_ids}" |
597 | + attrs="{'invisible': [('select_accounts_only', '=', False)]}"> |
598 | + <tree string="G/L Accounts"> |
599 | + <field name="code"/> |
600 | + <field name="name"/> |
601 | + </tree> |
602 | + </field> |
603 | </page> |
604 | <page string="Expense accounts" attrs="{'invisible': [('category', '!=', 'DEST')]}"> |
605 | <field name="destination_ids" nolabel="1" domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True)]" context="{'destination_id': record_id}"> |
606 | @@ -112,7 +135,7 @@ |
607 | <field name="allow_all_cc" colspan="4" on_change="on_change_allow_all_cc(allow_all_cc, dest_cc_ids)"/> |
608 | <button name="button_dest_cc_clear" type="object" string="Remove all" icon="gtk-clear" colspan="4"/> |
609 | <separator/> |
610 | - <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_dest_cc_ids(dest_cc_ids)"> |
611 | + <field name="dest_cc_ids" nolabel="1" colspan="4" on_change="on_change_cc_ids(dest_cc_ids)"> |
612 | <tree string="Cost Centers"> |
613 | <field name="code"/> |
614 | <field name="name"/> |
615 | |
616 | === modified file 'bin/addons/analytic_distribution/analytic_distribution.py' |
617 | --- bin/addons/analytic_distribution/analytic_distribution.py 2020-01-30 17:08:29 +0000 |
618 | +++ bin/addons/analytic_distribution/analytic_distribution.py 2020-11-02 12:53:09 +0000 |
619 | @@ -44,6 +44,98 @@ |
620 | return False |
621 | return True |
622 | |
623 | + def check_fp_cc_compatibility(self, cr, uid, fp_id, cost_center_id, context=None): |
624 | + """ |
625 | + Checks the compatibility between the FP and the Cost Center (cf. CC tab in the FP form). |
626 | + Returns False if they aren't compatible. |
627 | + |
628 | + If "Allow all Cost Centers" is ticked: only CC linked to the prop. instance of the FP are allowed. |
629 | + """ |
630 | + if context is None: |
631 | + context = {} |
632 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
633 | + ir_model_data_obj = self.pool.get('ir.model.data') |
634 | + res = True |
635 | + if fp_id and cost_center_id: |
636 | + # The Funding Pool PF is compatible with every CC |
637 | + try: |
638 | + pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
639 | + except ValueError: |
640 | + pf_id = 0 |
641 | + if fp_id != pf_id: |
642 | + fp = analytic_acc_obj.browse(cr, uid, fp_id, |
643 | + fields_to_fetch=['category', 'allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'], |
644 | + context=context) |
645 | + cc = analytic_acc_obj.browse(cr, uid, cost_center_id, fields_to_fetch=['category', 'type', 'cc_instance_ids'], context=context) |
646 | + if fp and cc and fp.category == 'FUNDING' and cc.category == 'OC': |
647 | + if fp.allow_all_cc_with_fp and cc.type != 'view' and fp.instance_id and \ |
648 | + fp.instance_id.id in [inst.id for inst in cc.cc_instance_ids]: |
649 | + res = True |
650 | + elif cc.id in [c.id for c in fp.cost_center_ids]: |
651 | + res = True |
652 | + else: |
653 | + res = False |
654 | + return res |
655 | + |
656 | + def onchange_ad_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False, fp_field_name='funding_pool_id'): |
657 | + """ |
658 | + Resets the FP in case the CC selected isn't compatible with it. |
659 | + """ |
660 | + res = {} |
661 | + if cost_center_id and funding_pool_id and not self.check_fp_cc_compatibility(cr, uid, funding_pool_id, cost_center_id): |
662 | + res = {'value': {fp_field_name: False}} |
663 | + return res |
664 | + |
665 | + def check_fp_acc_dest_compatibility(self, cr, uid, fp_id, account_id, dest_id, context=None): |
666 | + """ |
667 | + Checks the compatibility between the FP and the "G/L Account/Destination" combination. |
668 | + Returns False if they aren't compatible. |
669 | + """ |
670 | + if context is None: |
671 | + context = {} |
672 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
673 | + account_obj = self.pool.get('account.account') |
674 | + ir_model_data_obj = self.pool.get('ir.model.data') |
675 | + res = True |
676 | + if fp_id and account_id and dest_id: |
677 | + # The Funding Pool PF is compatible with every combination |
678 | + try: |
679 | + pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
680 | + except ValueError: |
681 | + pf_id = 0 |
682 | + if fp_id != pf_id: |
683 | + fp = analytic_acc_obj.browse(cr, uid, fp_id, |
684 | + fields_to_fetch=['category', 'select_accounts_only', 'fp_account_ids', |
685 | + 'tuple_destination_account_ids'], |
686 | + context=context) |
687 | + if fp and fp.category == 'FUNDING': |
688 | + # continue only if the account and destination selected are compatible with one another |
689 | + account_selected = account_obj.browse(cr, uid, account_id, fields_to_fetch=['destination_ids'], context=context) |
690 | + if dest_id not in [d.id for d in account_selected.destination_ids]: |
691 | + res = False |
692 | + else: |
693 | + # when the link is made to G/L accounts only: all Destinations compatible with the acc. are allowed |
694 | + if fp.select_accounts_only and account_id in [a.id for a in fp.fp_account_ids]: |
695 | + res = True |
696 | + # otherwise the combination "account + dest" must be checked |
697 | + elif not fp.select_accounts_only and (account_id, dest_id) in \ |
698 | + [(t.account_id.id, t.destination_id.id) for t in fp.tuple_destination_account_ids if not t.disabled]: |
699 | + res = True |
700 | + else: |
701 | + res = False |
702 | + return res |
703 | + |
704 | + def onchange_ad_destination(self, cr, uid, ids, destination_id=False, funding_pool_id=False, account_id=False, |
705 | + fp_field_name='funding_pool_id'): |
706 | + """ |
707 | + Resets the FP in case the Dest/Acc combination selected isn't compatible with it. |
708 | + """ |
709 | + res = {} |
710 | + if destination_id and funding_pool_id and account_id and \ |
711 | + not self.check_fp_acc_dest_compatibility(cr, uid, funding_pool_id, account_id, destination_id): |
712 | + res = {'value': {fp_field_name: False}} |
713 | + return res |
714 | + |
715 | def _get_distribution_state(self, cr, uid, distrib_id, parent_id, account_id, context=None, |
716 | doc_date=False, posting_date=False, manual=False, amount=False): |
717 | """ |
718 | @@ -70,12 +162,6 @@ |
719 | if amount is not None and amount is not False and abs(amount) <= 1: |
720 | if not all(len(d) <= 1 for d in [distrib.funding_pool_lines, distrib.free_1_lines, distrib.free_2_lines]): |
721 | return 'invalid_small_amount' |
722 | - # Search MSF Private Fund element, because it's valid with all accounts |
723 | - try: |
724 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
725 | - 'analytic_account_msf_private_funds')[1] |
726 | - except ValueError: |
727 | - fp_id = 0 |
728 | account = self.pool.get('account.account').read(cr, uid, account_id, ['destination_ids']) |
729 | # Check Cost Center lines regarding destination/account and destination/CC links |
730 | for cc_line in distrib.cost_center_lines: |
731 | @@ -105,12 +191,10 @@ |
732 | return 'invalid' |
733 | if not fp_line.analytic_id: |
734 | return 'invalid' |
735 | - # If fp_line is MSF Private Fund, all is ok |
736 | - if fp_line.analytic_id.id == fp_id: |
737 | - continue |
738 | - if (account_id, fp_line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.analytic_id.tuple_destination_account_ids if not x.disabled]: |
739 | + if not self.check_fp_acc_dest_compatibility(cr, uid, fp_line.analytic_id.id, account_id, |
740 | + fp_line.destination_id.id, context=context): |
741 | return 'invalid' |
742 | - if fp_line.cost_center_id.id not in [x.id for x in fp_line.analytic_id.cost_center_ids]: |
743 | + if not self.check_fp_cc_compatibility(cr, uid, fp_line.analytic_id.id, fp_line.cost_center_id.id, context=context): |
744 | return 'invalid' |
745 | # Check the date validity of the free accounts used in manual entries |
746 | if manual and doc_date: |
747 | @@ -135,14 +219,6 @@ |
748 | info = '' |
749 | ana_obj = self.pool.get('account.analytic.account') |
750 | account = self.pool.get('account.account').browse(cr, uid, account_id, context=context) |
751 | - fp = ana_obj.browse(cr, uid, analytic_id, context=context) |
752 | - try: |
753 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
754 | - except ValueError: |
755 | - fp_id = 0 |
756 | - is_private_fund = False |
757 | - if analytic_id == fp_id: |
758 | - is_private_fund = True |
759 | # DISTRIBUTION VERIFICATION |
760 | # Check that destination is compatible with account |
761 | if destination_id not in [x.id for x in account.destination_ids]: |
762 | @@ -150,13 +226,12 @@ |
763 | # Check that Destination and Cost Center are compatible |
764 | if not self.check_dest_cc_compatibility(cr, uid, destination_id, cost_center_id, context=context): |
765 | return 'invalid', _('Cost Center not compatible with destination') |
766 | - if not is_private_fund: |
767 | - # Check that cost center is compatible with FP (except if FP is MSF Private Fund) |
768 | - if cost_center_id not in [x.id for x in fp.cost_center_ids]: |
769 | - return 'invalid', _('Cost Center not compatible with FP') |
770 | - # Check that tuple account/destination is compatible with FP (except if FP is MSF Private Fund): |
771 | - if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp.tuple_destination_account_ids if not x.disabled]: |
772 | - return 'invalid', _('account/destination tuple not compatible with given FP analytic account') |
773 | + # Check that cost center is compatible with FP |
774 | + if not self.check_fp_cc_compatibility(cr, uid, analytic_id, cost_center_id, context=context): |
775 | + return 'invalid', _('Cost Center not compatible with FP') |
776 | + # Check that tuple account/destination is compatible with FP |
777 | + if not self.check_fp_acc_dest_compatibility(cr, uid, analytic_id, account_id, destination_id, context=context): |
778 | + return 'invalid', _('account/destination tuple not compatible with given FP analytic account') |
779 | return res, info |
780 | |
781 | def check_cc_distrib_active(self, cr, uid, distrib_br, posting_date=False, prefix='', from_supply=False): |
782 | |
783 | === modified file 'bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml' |
784 | --- bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2020-02-10 14:51:45 +0000 |
785 | +++ bin/addons/analytic_distribution/analytic_distribution_wizard_view.xml 2020-11-02 12:53:09 +0000 |
786 | @@ -32,7 +32,7 @@ |
787 | <field name="destination_id" on_change="onchange_destination(destination_id, analytic_id, parent.account_id)" |
788 | context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('posting_date')}"/> |
789 | <field name="analytic_id" |
790 | - domain="[('type', '!=', 'view'), ('category', '=', 'FUNDING'), ('state', '=', 'open'), ('cost_center_ids', '=', cost_center_id)]" |
791 | + domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('fp_compatible_with_cc_ids', '=', cost_center_id)]" |
792 | string="Funding Pool" context="{'search_default_active': 1, 'hide_inactive': 1, 'date': context.get('document_date')}"/> |
793 | <field name="percentage" sum="Total Percentage" digits="(16,2)"/> |
794 | <field name="amount" sum="Total Amount"/> |
795 | |
796 | === modified file 'bin/addons/analytic_distribution/analytic_line.py' |
797 | --- bin/addons/analytic_distribution/analytic_line.py 2019-12-12 14:07:37 +0000 |
798 | +++ bin/addons/analytic_distribution/analytic_line.py 2020-11-02 12:53:09 +0000 |
799 | @@ -46,6 +46,7 @@ |
800 | if not args: |
801 | return [] |
802 | res = [] |
803 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
804 | # We just support '=' operator |
805 | for arg in args: |
806 | if not arg[1]: |
807 | @@ -54,9 +55,9 @@ |
808 | raise osv.except_osv(_('Warning'), _('This filter is not implemented yet!')) |
809 | if not arg[2]: |
810 | raise osv.except_osv(_('Warning'), _('Some search args are missing!')) |
811 | - analytic_account = self.pool.get('account.analytic.account').browse(cr, uid, arg[2]) |
812 | - tuple_list = [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in analytic_account.tuple_destination_account_ids if not x.disabled] |
813 | - cost_center_ids = [x and x.id for x in analytic_account.cost_center_ids] |
814 | + fp_id = arg[2] |
815 | + tuple_list = analytic_acc_obj.get_acc_dest_linked_to_fp(cr, uid, fp_id, context=context) |
816 | + cost_center_ids = [c.id for c in analytic_acc_obj.get_cc_linked_to_fp(cr, uid, fp_id, context=context)] |
817 | for cc in cost_center_ids: |
818 | for t in tuple_list: |
819 | if res: |
820 | @@ -417,11 +418,6 @@ |
821 | res = [] |
822 | if not account_type: |
823 | return res |
824 | - try: |
825 | - msf_private_fund = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
826 | - 'analytic_account_msf_private_funds')[1] |
827 | - except ValueError: |
828 | - msf_private_fund = 0 |
829 | expired_date_ids = [] |
830 | date_start = account and account.get('date_start', False) or False |
831 | date_stop = account and account.get('date', False) or False |
832 | @@ -455,16 +451,9 @@ |
833 | continue |
834 | if ad_obj.check_dest_cc_compatibility(cr, uid, aline.destination_id and aline.destination_id.id or False, |
835 | account_id, context=context): |
836 | - if aline.account_id and aline.account_id.id == msf_private_fund: |
837 | + if ad_obj.check_fp_cc_compatibility(cr, uid, aline.account_id.id, account_id, context=context): |
838 | res.append(aline.id) |
839 | - elif aline.account_id and aline.cost_center_id and aline.account_id.cost_center_ids: |
840 | - if account_id in [x and x.id for x in aline.account_id.cost_center_ids] or aline.account_id.id == msf_private_fund: |
841 | - res.append(aline.id) |
842 | elif account_type == 'FUNDING': |
843 | - fp = self.pool.get('account.analytic.account').read(cr, uid, account_id, ['cost_center_ids', 'tuple_destination_account_ids'], context=context) |
844 | - cc_ids = fp and fp.get('cost_center_ids', []) or [] |
845 | - tuple_destination_account_ids = fp and fp.get('tuple_destination_account_ids', []) or [] |
846 | - tuple_list = [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in self.pool.get('account.destination.link').browse(cr, uid, tuple_destination_account_ids) if not x.disabled] |
847 | # Browse all analytic line to verify them |
848 | for aline in self.browse(cr, uid, ids): |
849 | # Verify that: |
850 | @@ -472,21 +461,24 @@ |
851 | check_accounts = self.pool.get('account.analytic.account').is_blocked_by_a_contract(cr, uid, [aline.account_id.id]) |
852 | if check_accounts and aline.account_id.id in check_accounts: |
853 | continue |
854 | - # No verification if account is MSF Private Fund because of its compatibility with all elements. |
855 | - if account_id == msf_private_fund: |
856 | - res.append(aline.id) |
857 | - continue |
858 | # Verify that: |
859 | # - the line have a cost_center_id field (we expect it's a line with a funding pool account) |
860 | # - the cost_center is in compatible cost center from the new funding pool |
861 | # - the general account is in compatible account/destination tuple |
862 | # - the destination is in compatible account/destination tuple |
863 | - if aline.cost_center_id and aline.cost_center_id.id in cc_ids and aline.general_account_id and aline.destination_id and (aline.general_account_id.id, aline.destination_id.id) in tuple_list: |
864 | + if aline.cost_center_id and aline.destination_id and \ |
865 | + ad_obj.check_fp_cc_compatibility(cr, uid, account_id, aline.cost_center_id.id, context=context) and \ |
866 | + ad_obj.check_fp_acc_dest_compatibility(cr, uid, account_id, aline.general_account_id.id, |
867 | + aline.destination_id.id, context=context): |
868 | res.append(aline.id) |
869 | elif account_type == "DEST": |
870 | for aline in self.browse(cr, uid, ids, context=context): |
871 | - if ad_obj.check_dest_cc_compatibility(cr, uid, account_id, aline.cost_center_id and aline.cost_center_id.id or False, context=context) and \ |
872 | - aline.general_account_id and account_id in [x.id for x in aline.general_account_id.destination_ids]: |
873 | + # the following check is included into check_fp_acc_dest_compatibility: |
874 | + # account_id in [x.id for x in aline.general_account_id.destination_ids] |
875 | + if ad_obj.check_dest_cc_compatibility(cr, uid, account_id, aline.cost_center_id and aline.cost_center_id.id or False, |
876 | + context=context) and \ |
877 | + ad_obj.check_fp_acc_dest_compatibility(cr, uid, aline.account_id.id, aline.general_account_id.id, |
878 | + account_id, context=context): |
879 | res.append(aline.id) |
880 | else: |
881 | # Case of FREE1 and FREE2 lines |
882 | @@ -538,23 +530,17 @@ |
883 | res.append((id, entry_sequence, _('CC/DEST'))) |
884 | return False |
885 | |
886 | - # check funding pool (expect for MSF Private Fund) |
887 | - if not new_fp_id == msf_pf_id: # all OK for MSF Private Fund |
888 | - # - cost center and funding pool compatibility |
889 | - cc_ids = [cc.id for cc in new_fp_br.cost_center_ids] |
890 | - if not new_cc_id in cc_ids: |
891 | - # not compatible with CC |
892 | - res.append((id, entry_sequence, _('CC'))) |
893 | - return False |
894 | + # - cost center and funding pool compatibility |
895 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, new_fp_id, new_cc_id, context=context): |
896 | + # not compatible with CC |
897 | + res.append((id, entry_sequence, _('CC'))) |
898 | + return False |
899 | |
900 | - # - destination / account |
901 | - acc_dest = (general_account_br.id, new_dest_id) |
902 | - if acc_dest not in [x.account_id and x.destination_id and \ |
903 | - (x.account_id.id, x.destination_id.id) \ |
904 | - for x in new_fp_br.tuple_destination_account_ids if not x.disabled]: |
905 | - # not compatible with dest/account |
906 | - res.append((id, entry_sequence, _('account/dest'))) |
907 | - return False |
908 | + # - destination / account |
909 | + if not ad_obj.check_fp_acc_dest_compatibility(cr, uid, new_fp_id, general_account_br.id, new_dest_id, context=context): |
910 | + # not compatible with account/dest |
911 | + res.append((id, entry_sequence, _('account/dest'))) |
912 | + return False |
913 | |
914 | # check active date |
915 | if not check_date(new_dest_br, posting_date): |
916 | |
917 | === modified file 'bin/addons/analytic_distribution/report/funding_pool.py' |
918 | --- bin/addons/analytic_distribution/report/funding_pool.py 2019-04-03 13:47:21 +0000 |
919 | +++ bin/addons/analytic_distribution/report/funding_pool.py 2020-11-02 12:53:09 +0000 |
920 | @@ -29,6 +29,7 @@ |
921 | self.localcontext.update({ |
922 | 'locale': locale, |
923 | 'today': self.today, |
924 | + 'all_cc': lambda f: self.pool.get('account.analytic.account').get_cc_linked_to_fp(cr, uid, f, context=context), |
925 | }) |
926 | |
927 | def today(self): |
928 | |
929 | === modified file 'bin/addons/analytic_distribution/report/funding_pool.rml' |
930 | --- bin/addons/analytic_distribution/report/funding_pool.rml 2019-04-17 10:00:14 +0000 |
931 | +++ bin/addons/analytic_distribution/report/funding_pool.rml 2020-11-02 12:53:09 +0000 |
932 | @@ -206,7 +206,7 @@ |
933 | </td> |
934 | </tr> |
935 | <tr> |
936 | - <para style="P17">[[repeatIn(o.cost_center_ids,'line')]]</para> |
937 | + <para style="P17">[[repeatIn(all_cc(o.id), 'line')]]</para> |
938 | <td> |
939 | <para style="P7">[[ line.code or '' ]]</para> |
940 | </td> |
941 | @@ -222,9 +222,11 @@ |
942 | <para style="P8"> |
943 | <font color="white"> </font> |
944 | </para> |
945 | - <para style="P6">Account/Destination:</para> |
946 | + <para style="P6">[[ o.select_accounts_only and translate("G/L Accounts:") or translate("Account/Destination:") ]]</para> |
947 | |
948 | + <!-- Account/Destination --> |
949 | <blockTable repeatRows="1" style="Table4" colWidths="95.0,165.0,95.0,165.0"> |
950 | + [[ not o.select_accounts_only or removeParentNode('blockTable') ]] |
951 | <tr> |
952 | <td> |
953 | <para style="P8">Account code</para> |
954 | @@ -257,6 +259,28 @@ |
955 | </tr> |
956 | </blockTable> |
957 | |
958 | + <!-- G/L Accounts --> |
959 | + <blockTable repeatRows="1" style="Table4" colWidths="260.0,260.0"> |
960 | + [[ o.select_accounts_only or removeParentNode('blockTable') ]] |
961 | + <tr> |
962 | + <td> |
963 | + <para style="P8">Account code</para> |
964 | + </td> |
965 | + <td> |
966 | + <para style="P8">Account name</para> |
967 | + </td> |
968 | + </tr> |
969 | + <tr> |
970 | + [[ repeatIn(o.fp_account_ids, 'acc') ]] |
971 | + <td> |
972 | + <para style="P7">[[ acc.code ]]</para> |
973 | + </td> |
974 | + <td> |
975 | + <para style="P7">[[ acc.name ]]</para> |
976 | + </td> |
977 | + </tr> |
978 | + </blockTable> |
979 | + |
980 | <para style="P5"> |
981 | <font color="white"> </font> |
982 | </para> |
983 | |
984 | === modified file 'bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py' |
985 | --- bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2020-07-16 15:42:42 +0000 |
986 | +++ bin/addons/analytic_distribution/wizard/analytic_distribution_wizard.py 2020-11-02 12:53:09 +0000 |
987 | @@ -237,12 +237,17 @@ |
988 | elif (context.get('from_invoice', False) and isinstance(context.get('from_invoice'), int)) or (context.get('from_commitment', False) and isinstance(context.get('from_commitment'), int)) \ |
989 | or (context.get('from_model', False) and isinstance(context.get('from_model'), int)) \ |
990 | or (context.get('from_move', False) and isinstance(context.get('from_move'), int)) \ |
991 | - or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int)): |
992 | - # Filter is only on cost_center and MSF Private Fund on invoice header |
993 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), ('hide_closed_fp', '=', True), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id) |
994 | + or (context.get('from_cash_return', False) and isinstance(context.get('from_cash_return'), int))\ |
995 | + or (context.get('direct_invoice_id', False) and isinstance(context.get('direct_invoice_id'), int)): |
996 | + # Filter is only on cost_centers on invoice header |
997 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
998 | + "('hide_closed_fp', '=', True), ('fp_compatible_with_cc_ids', '=', cost_center_id)]") |
999 | else: |
1000 | # Add account_id constraints for invoice lines |
1001 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), ('hide_closed_fp', '=', True), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (parent.account_id, destination_id)), ('id', '=', %s)]" % fp_id) |
1002 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
1003 | + "('hide_closed_fp', '=', True), " |
1004 | + "('fp_compatible_with_cc_ids', '=', cost_center_id), " |
1005 | + "('fp_compatible_with_acc_dest_ids', '=', (parent.account_id, destination_id))]") |
1006 | # Change Destination field |
1007 | dest_fields = tree.xpath('/tree/field[@name="destination_id"]') |
1008 | for field in dest_fields: |
1009 | @@ -404,60 +409,18 @@ |
1010 | } |
1011 | |
1012 | def onchange_destination(self, cr, uid, ids, destination_id=False, analytic_id=False, account_id=False): |
1013 | - """ |
1014 | - Check given funding pool with destination |
1015 | - """ |
1016 | - # Prepare some values |
1017 | - res = {} |
1018 | - # Search MSF Private Fund element, because it's valid with all accounts |
1019 | - try: |
1020 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
1021 | - 'analytic_account_msf_private_funds')[1] |
1022 | - except ValueError: |
1023 | - fp_id = 0 |
1024 | - |
1025 | - # If all elements given, then search FP compatibility |
1026 | - if destination_id and analytic_id and account_id: |
1027 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, analytic_id) |
1028 | - # Delete analytic_id if not valid with tuple "account_id/destination_id". |
1029 | - # but do an exception for MSF Private FUND analytic account |
1030 | - if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.tuple_destination_account_ids if not x.disabled] and analytic_id != fp_id: |
1031 | - res = {'value': {'analytic_id': False}} |
1032 | - # If no destination, do nothing |
1033 | - elif not destination_id \ |
1034 | - or analytic_id == fp_id: # PF always compatible |
1035 | - res = {} |
1036 | - # Otherway: delete FP |
1037 | - else: |
1038 | - res = {'value': {'analytic_id': False}} |
1039 | - return res |
1040 | + return self.pool.get('analytic.distribution').onchange_ad_destination(cr, uid, ids, destination_id=destination_id, |
1041 | + funding_pool_id=analytic_id, account_id=account_id, |
1042 | + fp_field_name='analytic_id') |
1043 | |
1044 | def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, analytic_id=False): |
1045 | - """ |
1046 | - Check given cost_center with funding pool |
1047 | - """ |
1048 | - # Prepare some values |
1049 | - res = {} |
1050 | - # Search MSF Private Fund element, because it's valid with all accounts |
1051 | - try: |
1052 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
1053 | - 'analytic_account_msf_private_funds')[1] |
1054 | - except ValueError: |
1055 | - fp_id = 0 |
1056 | + return self.pool.get('analytic.distribution').\ |
1057 | + onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=analytic_id, fp_field_name='analytic_id') |
1058 | |
1059 | - if cost_center_id and analytic_id: |
1060 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, analytic_id) |
1061 | - if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and analytic_id != fp_id: |
1062 | - res = {'value': {'analytic_id': False}} |
1063 | - elif not cost_center_id \ |
1064 | - or analytic_id == fp_id: # PF always compatible: |
1065 | - res = {} |
1066 | - else: |
1067 | - res = {'value': {'analytic_id': False}} |
1068 | - return res |
1069 | |
1070 | analytic_distribution_wizard_fp_lines() |
1071 | |
1072 | + |
1073 | class analytic_distribution_wizard_f1_lines(osv.osv_memory): |
1074 | _name = 'analytic.distribution.wizard.f1.lines' |
1075 | _description = 'analytic.distribution.wizard.lines' |
1076 | |
1077 | === modified file 'bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py' |
1078 | --- bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py 2015-02-09 12:53:11 +0000 |
1079 | +++ bin/addons/analytic_distribution/wizard/commitment_analytic_reallocation.py 2020-11-02 12:53:09 +0000 |
1080 | @@ -38,7 +38,7 @@ |
1081 | |
1082 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
1083 | """ |
1084 | - Change funding pool domain in order to include MSF Private fund |
1085 | + Adapts domain for AD fields |
1086 | """ |
1087 | if context is None: |
1088 | context = {} |
1089 | @@ -55,40 +55,19 @@ |
1090 | for field in fields: |
1091 | field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id) |
1092 | # Change FP field |
1093 | - try: |
1094 | - fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
1095 | - except ValueError: |
1096 | - fp_id = 0 |
1097 | fp_fields = form.xpath('//field[@name="funding_pool_id"]') |
1098 | # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE! |
1099 | for field in fp_fields: |
1100 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id) |
1101 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
1102 | + "('fp_compatible_with_cc_ids', '=', cost_center_id)]") |
1103 | # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE! |
1104 | # Apply changes |
1105 | view['arch'] = etree.tostring(form) |
1106 | return view |
1107 | |
1108 | def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False): |
1109 | - """ |
1110 | - Check given cost_center with funding pool |
1111 | - """ |
1112 | - # Prepare some values |
1113 | - res = {} |
1114 | - if cost_center_id and funding_pool_id: |
1115 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id) |
1116 | - # Search MSF Private Fund element, because it's valid with all accounts |
1117 | - try: |
1118 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
1119 | - 'analytic_account_msf_private_funds')[1] |
1120 | - except ValueError: |
1121 | - fp_id = 0 |
1122 | - if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and funding_pool_id != fp_id: |
1123 | - res = {'value': {'funding_pool_id': False}} |
1124 | - elif not cost_center_id: |
1125 | - res = {} |
1126 | - else: |
1127 | - res = {'value': {'funding_pool_id': False}} |
1128 | - return res |
1129 | + return self.pool.get('analytic.distribution').\ |
1130 | + onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id) |
1131 | |
1132 | def button_validate(self, cr, uid ,ids, context=None): |
1133 | """ |
1134 | |
1135 | === modified file 'bin/addons/analytic_override/analytic_account.py' |
1136 | --- bin/addons/analytic_override/analytic_account.py 2020-10-09 14:34:01 +0000 |
1137 | +++ bin/addons/analytic_override/analytic_account.py 2020-11-02 12:53:09 +0000 |
1138 | @@ -290,6 +290,108 @@ |
1139 | dom.append(('id', 'in', compatible_dest_ids)) |
1140 | return dom |
1141 | |
1142 | + def _search_fp_compatible_with_cc_ids(self, cr, uid, obj, name, args, context=None): |
1143 | + """ |
1144 | + Returns a domain with all funding pools compatible with the selected Cost Center |
1145 | + E.g.: to get the FPs compatible with the CC 2, use the dom [('fp_compatible_with_cc_ids', '=', 2)] |
1146 | + """ |
1147 | + dom = [] |
1148 | + if context is None: |
1149 | + context = {} |
1150 | + ir_model_data_obj = self.pool.get('ir.model.data') |
1151 | + for arg in args: |
1152 | + if arg[0] == 'fp_compatible_with_cc_ids': |
1153 | + operator = arg[1] |
1154 | + cc_id = arg[2] |
1155 | + if operator != '=': |
1156 | + raise osv.except_osv(_('Error'), _('Filter not implemented on Funding Pools.')) |
1157 | + cc = False |
1158 | + if cc_id and isinstance(cc_id, (int, long)): |
1159 | + cc = self.browse(cr, uid, cc_id, fields_to_fetch=['category', 'type', 'cc_instance_ids'], context=context) |
1160 | + if cc.category != 'OC' or cc.type == 'view': |
1161 | + raise osv.except_osv(_('Error'), _('Filter only compatible with a normal-type Cost Center.')) |
1162 | + compatible_fp_ids = [] |
1163 | + # The Funding Pool PF is compatible with every CC |
1164 | + try: |
1165 | + pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
1166 | + except ValueError: |
1167 | + pf_id = 0 |
1168 | + compatible_fp_ids.append(pf_id) |
1169 | + if cc: |
1170 | + other_fp_ids = self.search(cr, uid, [('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('id', '!=', pf_id)], |
1171 | + context=context) |
1172 | + cc_inst_ids = [inst.id for inst in cc.cc_instance_ids] |
1173 | + for fp in self.browse(cr, uid, other_fp_ids, |
1174 | + fields_to_fetch=['allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'], |
1175 | + context=context): |
1176 | + if fp.allow_all_cc_with_fp and fp.instance_id and fp.instance_id.id in cc_inst_ids: |
1177 | + compatible = True |
1178 | + elif cc.id in [c.id for c in fp.cost_center_ids]: |
1179 | + compatible = True |
1180 | + else: |
1181 | + compatible = False |
1182 | + if compatible: |
1183 | + compatible_fp_ids.append(fp.id) |
1184 | + dom.append(('id', 'in', compatible_fp_ids)) |
1185 | + return dom |
1186 | + |
1187 | + def _search_fp_compatible_with_acc_dest_ids(self, cr, uid, obj, name, args, context=None): |
1188 | + """ |
1189 | + Returns a domain with all funding pools compatible with the selected Account/Destination combination. |
1190 | + It requires a tuple with (account, destination), e.g.: to get the FPs compatible with the G/L account 20 and the |
1191 | + destination 30, use the dom [('fp_compatible_with_acc_dest_ids', '=', (20, 30))] |
1192 | + """ |
1193 | + fp_ids = [] |
1194 | + if context is None: |
1195 | + context = {} |
1196 | + ir_model_data_obj = self.pool.get('ir.model.data') |
1197 | + account_obj = self.pool.get('account.account') |
1198 | + for arg in args: |
1199 | + if arg[0] == 'fp_compatible_with_acc_dest_ids': |
1200 | + operator = arg[1] |
1201 | + if operator != '=': |
1202 | + raise osv.except_osv(_('Error'), _('Filter not implemented on Funding Pools.')) |
1203 | + acc_dest = arg[2] |
1204 | + acc_id = dest_id = False |
1205 | + if acc_dest and isinstance(acc_dest, tuple) and len(acc_dest) == 2: |
1206 | + acc_id = acc_dest[0] |
1207 | + dest_id = acc_dest[1] |
1208 | + # The Funding Pool PF is compatible with everything and must always be displayed |
1209 | + try: |
1210 | + pf_id = ir_model_data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
1211 | + except ValueError: |
1212 | + pf_id = 0 |
1213 | + fp_ids.append(pf_id) |
1214 | + if acc_id and dest_id: |
1215 | + account_selected = account_obj.browse(cr, uid, acc_id, fields_to_fetch=['destination_ids'], context=context) |
1216 | + # search for compatible FPs only if the account and destination selected are compatible with one another |
1217 | + if dest_id in [d.id for d in account_selected.destination_ids]: |
1218 | + # note: when the link is made to G/L accounts only, all Destinations compatible with the acc. are allowed |
1219 | + cr.execute(''' |
1220 | + SELECT fp.id |
1221 | + FROM |
1222 | + account_analytic_account fp |
1223 | + LEFT JOIN fp_account_rel ON fp_account_rel.fp_id = fp.id |
1224 | + LEFT JOIN funding_pool_associated_destinations ON funding_pool_associated_destinations.funding_pool_id = fp.id |
1225 | + LEFT JOIN account_destination_link link ON link.id = funding_pool_associated_destinations.tuple_id |
1226 | + WHERE |
1227 | + fp.category = 'FUNDING' AND |
1228 | + fp.type != 'view' AND |
1229 | + fp.id != %(pf_id)s AND |
1230 | + ( |
1231 | + fp.select_accounts_only = 't' AND |
1232 | + fp_account_rel.account_id = %(acc_id)s |
1233 | + OR |
1234 | + fp.select_accounts_only = 'f' AND |
1235 | + link.account_id = %(acc_id)s AND |
1236 | + link.destination_id = %(dest_id)s AND |
1237 | + link.disabled = 'f' |
1238 | + ) |
1239 | + ''', {'pf_id': pf_id, 'acc_id': acc_id, 'dest_id': dest_id}) |
1240 | + other_fp_ids = [x[0] for x in cr.fetchall()] |
1241 | + fp_ids.extend(other_fp_ids) |
1242 | + return [('id', 'in', fp_ids)] |
1243 | + |
1244 | def _get_cc_instance_ids(self, cr, uid, ids, fields, arg, context=None): |
1245 | """ |
1246 | Computes the values for fields.function fields, retrieving: |
1247 | @@ -299,7 +401,8 @@ |
1248 | ...as Cost centre picked for PO/FO reference => po_fo_cc_instance_ids |
1249 | (Note that those fields should theoretically always be linked to one single instance, |
1250 | but they are set as one2many in order to be consistent with the type of fields used in the related object.) |
1251 | - - the Missions where the Cost Center is added to => cc_missions |
1252 | + - the Instances where the Cost Center is added to => cc_instance_ids |
1253 | + - the related Missions => cc_missions |
1254 | """ |
1255 | if context is None: |
1256 | context = {} |
1257 | @@ -311,6 +414,7 @@ |
1258 | top_instance_ids = [] |
1259 | target_instance_ids = [] |
1260 | po_fo_instance_ids = [] |
1261 | + all_instance_ids = [] |
1262 | missions = set() |
1263 | missions_str = "" |
1264 | target_cc_ids = acc_target_cc_obj.search(cr, uid, [('cost_center_id', '=', analytic_acc_id)], context=context) |
1265 | @@ -318,6 +422,7 @@ |
1266 | field_list = ['instance_id', 'is_target', 'is_po_fo_cost_center', 'is_top_cost_center'] |
1267 | for target_cc in acc_target_cc_obj.browse(cr, uid, target_cc_ids, fields_to_fetch=field_list, context=context): |
1268 | instance = target_cc.instance_id |
1269 | + all_instance_ids.append(instance.id) |
1270 | if instance.mission: |
1271 | missions.add(instance.mission) |
1272 | if target_cc.is_top_cost_center: |
1273 | @@ -333,6 +438,7 @@ |
1274 | 'is_target_cc_instance_ids': target_instance_ids, |
1275 | 'po_fo_cc_instance_ids': po_fo_instance_ids, |
1276 | 'cc_missions': missions_str, |
1277 | + 'cc_instance_ids': all_instance_ids, |
1278 | } |
1279 | return res |
1280 | |
1281 | @@ -357,13 +463,22 @@ |
1282 | 'dest_cc_ids': fields.many2many('account.analytic.account', 'destination_cost_center_rel', |
1283 | 'destination_id', 'cost_center_id', string='Cost Centers', |
1284 | domain="[('type', '!=', 'view'), ('category', '=', 'OC')]"), |
1285 | - 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"), |
1286 | + 'allow_all_cc': fields.boolean(string="Allow all Cost Centers"), # for the Destinations |
1287 | + 'allow_all_cc_with_fp': fields.boolean(string="Allow all Cost Centers"), # for the Funding Pools |
1288 | 'dest_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False, |
1289 | string='Destinations compatible with the Cost Center', |
1290 | type='many2many', relation='account.analytic.account', |
1291 | fnct_search=_search_dest_compatible_with_cc_ids), |
1292 | 'dest_without_cc': fields.function(_get_dest_without_cc, type='boolean', method=True, store=False, |
1293 | string="Destination allowing no Cost Center",), |
1294 | + 'fp_compatible_with_cc_ids': fields.function(_get_fake, method=True, store=False, |
1295 | + string='Funding Pools compatible with the Cost Center', |
1296 | + type='many2many', relation='account.analytic.account', |
1297 | + fnct_search=_search_fp_compatible_with_cc_ids), |
1298 | + 'fp_compatible_with_acc_dest_ids': fields.function(_get_fake, method=True, store=False, |
1299 | + string='Funding Pools compatible with the Account/Destination combination', |
1300 | + type='many2many', relation='account.analytic.account', |
1301 | + fnct_search=_search_fp_compatible_with_acc_dest_ids), |
1302 | 'top_cc_instance_ids': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True, |
1303 | string="Instances having the CC as Top CC", |
1304 | type="one2many", relation="msf.instance", multi="cc_instances"), |
1305 | @@ -376,12 +491,21 @@ |
1306 | 'cc_missions': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True, |
1307 | string="Missions where the CC is added to", |
1308 | type='char', multi="cc_instances"), |
1309 | + 'cc_instance_ids': fields.function(_get_cc_instance_ids, method=True, store=False, readonly=True, |
1310 | + string="Instances where the CC is added to", |
1311 | + type="one2many", relation="msf.instance", multi="cc_instances"), |
1312 | + 'select_accounts_only': fields.boolean(string="Select Accounts Only"), |
1313 | + 'fp_account_ids': fields.many2many('account.account', 'fp_account_rel', 'fp_id', 'account_id', string='G/L Accounts', |
1314 | + domain="[('type', '!=', 'view'), ('is_analytic_addicted', '=', True), ('active', '=', 't')]", |
1315 | + help="G/L accounts linked to the Funding Pool", order_by='code'), |
1316 | } |
1317 | |
1318 | _defaults ={ |
1319 | 'date_start': lambda *a: (datetime.today() + relativedelta(months=-3)).strftime('%Y-%m-%d'), |
1320 | 'for_fx_gain_loss': lambda *a: False, |
1321 | 'allow_all_cc': lambda *a: False, |
1322 | + 'allow_all_cc_with_fp': lambda *a: False, |
1323 | + 'select_accounts_only': lambda *a: False, |
1324 | } |
1325 | |
1326 | def _check_code_unicity(self, cr, uid, ids, context=None): |
1327 | @@ -453,30 +577,40 @@ |
1328 | res['domain']['parent_id'] = [('category', '=', category), ('type', '=', 'view')] |
1329 | return res |
1330 | |
1331 | - def on_change_allow_all_cc(self, cr, uid, ids, allow_all_cc, dest_cc_ids, context=None): |
1332 | + 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): |
1333 | """ |
1334 | If the user tries to tick the box "Allow all Cost Centers" whereas CC are selected, |
1335 | informs him that he has to remove the CC first |
1336 | + (acc_type = name of the Analytic Account Type to which the CC are linked, displayed in the warning msg) |
1337 | """ |
1338 | res = {} |
1339 | - if allow_all_cc and dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])] |
1340 | + if allow_all_cc and cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])] |
1341 | + # NOTE: the msg is stored in a variable on purpose, otherwise the ".po" translation files would wrongly contain Python code |
1342 | + msg = 'Please remove the Cost Centers linked to the %s before ticking this box.' % acc_type.title() |
1343 | warning = { |
1344 | 'title': _('Warning!'), |
1345 | - 'message': _('Please remove the Cost Centers linked to the Destination before ticking this box.') |
1346 | + 'message': _(msg) |
1347 | } |
1348 | res['warning'] = warning |
1349 | - res['value'] = {'allow_all_cc': False, } |
1350 | + res['value'] = {field_name: False, } |
1351 | return res |
1352 | |
1353 | - def on_change_dest_cc_ids(self, cr, uid, ids, dest_cc_ids, context=None): |
1354 | + def on_change_allow_all_cc_with_fp(self, cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, context=None): |
1355 | + return self.on_change_allow_all_cc(cr, uid, ids, allow_all_cc_with_fp, cost_center_ids, acc_type='funding pool', |
1356 | + field_name='allow_all_cc_with_fp', context=context) |
1357 | + |
1358 | + def on_change_cc_ids(self, cr, uid, ids, cc_ids, field_name='allow_all_cc', context=None): |
1359 | """ |
1360 | If at least a CC is selected, unticks the box "Allow all Cost Centers" |
1361 | """ |
1362 | res = {} |
1363 | - if dest_cc_ids and dest_cc_ids[0][2]: # e.g. [(6, 0, [1, 2])] |
1364 | - res['value'] = {'allow_all_cc': False, } |
1365 | + if cc_ids and cc_ids[0][2]: # e.g. [(6, 0, [1, 2])] |
1366 | + res['value'] = {field_name: False, } |
1367 | return res |
1368 | |
1369 | + def on_change_cc_with_fp(self, cr, uid, ids, cost_center_ids, context=None): |
1370 | + return self.on_change_cc_ids(cr, uid, ids, cost_center_ids, field_name='allow_all_cc_with_fp', context=context) |
1371 | + |
1372 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
1373 | if not context: |
1374 | context = {} |
1375 | @@ -539,12 +673,12 @@ |
1376 | vals['parent_id'] = funding_pool_parent |
1377 | |
1378 | def remove_inappropriate_links(self, vals, context=None): |
1379 | - ''' |
1380 | - Remove relations that are incoherent regarding the category selected. For instance an account with |
1381 | - category "Funding Pool" can have associated cost centers, whereas a "Destination" shouldn't. |
1382 | + """ |
1383 | + Removes relations that are inconsistent regarding the category selected. For instance an account with the |
1384 | + category "Funding Pool" can have associated cost centers, whereas a "Cost Center" shouldn't. |
1385 | (That would happen if the category is modified after that the relations have been created). |
1386 | :return: corrected vals |
1387 | - ''' |
1388 | + """ |
1389 | if context is None: |
1390 | context = {} |
1391 | if 'category' in vals: |
1392 | @@ -555,6 +689,15 @@ |
1393 | if vals['category'] != 'FUNDING': |
1394 | vals['tuple_destination_account_ids'] = [(6, 0, [])] |
1395 | vals['cost_center_ids'] = [(6, 0, [])] |
1396 | + vals['allow_all_cc_with_fp'] = False # default value |
1397 | + vals['select_accounts_only'] = False |
1398 | + vals['fp_account_ids'] = [(6, 0, [])] |
1399 | + # Funding Pools: either "Account/Destination combinations" or "G/L accounts only" must be stored |
1400 | + if vals['category'] == 'FUNDING' and 'select_accounts_only' in vals: |
1401 | + if vals['select_accounts_only']: |
1402 | + vals['tuple_destination_account_ids'] = [(6, 0, [])] |
1403 | + else: |
1404 | + vals['fp_account_ids'] = [(6, 0, [])] |
1405 | return vals |
1406 | |
1407 | def _check_date(self, vals): |
1408 | @@ -720,6 +863,56 @@ |
1409 | 'target': 'current', |
1410 | } |
1411 | |
1412 | + def get_cc_linked_to_fp(self, cr, uid, fp_id, context=None): |
1413 | + """ |
1414 | + Returns a browse record list of all Cost Centers compatible with the Funding Pool in parameter: |
1415 | + - if "Allow all Cost Centers" is ticked: all CC linked to the prop. instance of the FP |
1416 | + - else all CC selected in the FP form. |
1417 | + |
1418 | + Note: this method matches with what has been selected in the Cost centers tab of the FP form. |
1419 | + It returns an empty list for PF. |
1420 | + """ |
1421 | + if context is None: |
1422 | + context = {} |
1423 | + cc_list = [] |
1424 | + fp = self.browse(cr, uid, fp_id, |
1425 | + fields_to_fetch=['category', 'allow_all_cc_with_fp', 'instance_id', 'cost_center_ids'], |
1426 | + context=context) |
1427 | + if fp.category == 'FUNDING': |
1428 | + if fp.allow_all_cc_with_fp and fp.instance_id: |
1429 | + # inactive CC are included on purpose, to match with selectable CC in FP form |
1430 | + for cc_id in self.search(cr, uid, [('category', '=', 'OC'), ('type', '!=', 'view')], order='code', context=context): |
1431 | + cc = self.browse(cr, uid, cc_id, context=context) |
1432 | + if fp.instance_id.id in [inst.id for inst in cc.cc_instance_ids]: |
1433 | + cc_list.append(cc) |
1434 | + else: |
1435 | + cc_list = fp.cost_center_ids or [] |
1436 | + return cc_list |
1437 | + |
1438 | + def get_acc_dest_linked_to_fp(self, cr, uid, fp_id, context=None): |
1439 | + """ |
1440 | + Returns a tuple of all combinations of (account_id, destination_id) compatible with the FP in parameter: |
1441 | + - if "Select Accounts Only" is ticked: the accounts selected and the Destinations compatible with them |
1442 | + - else the Account/Destination combinations selected. |
1443 | + |
1444 | + Note: this method matches with what has been selected in the Accounts/Destinations tab of the FP form. |
1445 | + It returns an empty list for PF. |
1446 | + """ |
1447 | + if context is None: |
1448 | + context = {} |
1449 | + combinations = [] |
1450 | + fp = self.browse(cr, uid, fp_id, |
1451 | + fields_to_fetch=['category', 'select_accounts_only', 'fp_account_ids', 'tuple_destination_account_ids'], |
1452 | + context=context) |
1453 | + if fp.category == 'FUNDING': |
1454 | + if fp.select_accounts_only: |
1455 | + for account in fp.fp_account_ids: |
1456 | + for dest in account.destination_ids: |
1457 | + combinations.append((account.id, dest.id)) |
1458 | + else: |
1459 | + combinations = [(t.account_id.id, t.destination_id.id) for t in fp.tuple_destination_account_ids if not t.disabled] |
1460 | + return combinations |
1461 | + |
1462 | def button_cc_clear(self, cr, uid, ids, context=None): |
1463 | self.write(cr, uid, ids, {'cost_center_ids':[(6, 0, [])]}, context=context) |
1464 | return True |
1465 | @@ -735,6 +928,13 @@ |
1466 | self.write(cr, uid, ids, {'tuple_destination_account_ids':[(6, 0, [])]}, context=context) |
1467 | return True |
1468 | |
1469 | + def button_fp_account_clear(self, cr, uid, ids, context=None): |
1470 | + """ |
1471 | + Removes all G/L accounts selected in the Funding Pool view |
1472 | + """ |
1473 | + self.write(cr, uid, ids, {'fp_account_ids': [(6, 0, [])]}, context=context) |
1474 | + return True |
1475 | + |
1476 | def get_destinations_by_accounts(self, cr, uid, ids, context=None): |
1477 | """ |
1478 | Returns a view with the Destinations by accounts (for the FP selected if any, otherwise for all the FP) |
1479 | |
1480 | === modified file 'bin/addons/financing_contract/contract.py' |
1481 | --- bin/addons/financing_contract/contract.py 2019-10-10 16:20:57 +0000 |
1482 | +++ bin/addons/financing_contract/contract.py 2020-11-02 12:53:09 +0000 |
1483 | @@ -62,6 +62,10 @@ |
1484 | return True |
1485 | |
1486 | def create(self, cr, uid, vals, context=None): |
1487 | + if context is None: |
1488 | + context = {} |
1489 | + analytic_acc_obj = self.pool.get('account.analytic.account') |
1490 | + format_obj = self.pool.get('financing.contract.format') |
1491 | # US-113: Check if the call is from sync update |
1492 | if context.get('sync_update_execution') and vals.get('contract_id', False): |
1493 | # US-113: and if there is any financing contract existed for this format, if no, then ignore this call |
1494 | @@ -75,26 +79,18 @@ |
1495 | |
1496 | #US-345: the following block cannot be executed in the sync context, because it would then reset all costcenters from the funding pools! |
1497 | # making that the deleted costcenters from the sender were not taken into account |
1498 | - if not context.get('sync_update_execution') and 'contract_id' in vals and 'funding_pool_id' in vals: |
1499 | - # get the cc ids from for this funding pool |
1500 | - quad_obj = self.pool.get('financing.contract.account.quadruplet') |
1501 | - quad_ids = quad_obj.search(cr, uid, [('funding_pool_id','=',vals['funding_pool_id'])],context=context) |
1502 | - quad_rows = quad_obj.browse(cr, uid, quad_ids,context=context) |
1503 | - quad_cc_ids = [] |
1504 | - for quad in quad_rows: |
1505 | - cc_id_temp = quad.cost_center_id.id |
1506 | - if cc_id_temp not in quad_cc_ids: |
1507 | - quad_cc_ids.append(cc_id_temp) |
1508 | + if not context.get('sync_update_execution') and vals.get('contract_id') and vals.get('funding_pool_id'): |
1509 | + # get the Cost Centers linked to the Funding Pool |
1510 | + fp_cc_ids = [c.id for c in analytic_acc_obj.get_cc_linked_to_fp(cr, uid, vals['funding_pool_id'], context=context)] |
1511 | |
1512 | # get the format instance |
1513 | - format_obj = self.pool.get('financing.contract.format') |
1514 | cc_rows = format_obj.browse(cr, uid, vals['contract_id'], context=context).cost_center_ids |
1515 | cc_ids = [] |
1516 | for cc in cc_rows: |
1517 | cc_ids.append(cc.id) |
1518 | |
1519 | # append the ccs from the fp only if not already there |
1520 | - cc_ids = list(set(cc_ids).union(quad_cc_ids)) |
1521 | + cc_ids = list(set(cc_ids).union(fp_cc_ids)) |
1522 | # replace the associated cc list -NOT WORKING |
1523 | format_obj.write(cr, uid, vals['contract_id'],{'cost_center_ids':[(6,0,cc_ids)]}, context=context) |
1524 | # UFTP-121: Check that FP is not used yet. |
1525 | @@ -337,6 +333,7 @@ |
1526 | ondelete="cascade", required=True), |
1527 | 'fp_added_flag': fields.boolean('Flag when new FP is added'), |
1528 | 'instance_level': fields.function(_get_instance_level, method=True, string="Current instance level", type="char", readonly=True), # UFTP-343 |
1529 | + 'quad_gen_date': fields.datetime('Date of last generation of quad'), |
1530 | } |
1531 | |
1532 | _defaults = { |
1533 | |
1534 | === modified file 'bin/addons/financing_contract/financing_contract_account_quadruplet.py' |
1535 | --- bin/addons/financing_contract/financing_contract_account_quadruplet.py 2017-10-02 15:51:29 +0000 |
1536 | +++ bin/addons/financing_contract/financing_contract_account_quadruplet.py 2020-11-02 12:53:09 +0000 |
1537 | @@ -21,18 +21,48 @@ |
1538 | |
1539 | from osv import fields, osv |
1540 | from tools import sql |
1541 | +import time |
1542 | |
1543 | class financing_contract_account_quadruplet(osv.osv): |
1544 | _name = 'financing.contract.account.quadruplet' |
1545 | _rec_name = 'cost_center_id' |
1546 | _description = 'FP / CC / destination valid values view' |
1547 | - _auto = False |
1548 | - |
1549 | + _log_access = False |
1550 | + _auto = True |
1551 | + |
1552 | + def migrate_old_quad(self, cr, uid, ids, context=None): |
1553 | + ''' |
1554 | + ids: list of record in the old view |
1555 | + return list of new record |
1556 | + ''' |
1557 | + |
1558 | + new_ids = [] |
1559 | + for old_id in ids: |
1560 | + # DO UPDATE: to return an id |
1561 | + cr.execute(''' |
1562 | + INSERT INTO financing_contract_account_quadruplet |
1563 | + (account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id) |
1564 | + ( select |
1565 | + account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id |
1566 | + from |
1567 | + financing_contract_account_quadruplet_old |
1568 | + where |
1569 | + id=%s |
1570 | + ) ON CONFLICT ON CONSTRAINT financing_contract_account_quadruplet_check_unique DO UPDATE SET disabled=EXCLUDED.disabled |
1571 | + RETURNING id ''', (old_id,)) |
1572 | + ret = cr.fetchone() |
1573 | + if ret: |
1574 | + new_ids.append(ret[0]) |
1575 | + return new_ids |
1576 | |
1577 | def _auto_init(self, cr, context=None): |
1578 | + sql.drop_view_if_exists(cr, 'financing_contract_account_quadruplet') |
1579 | + sql.drop_view_if_exists(cr, 'financing_contract_account_quadruplet_view') |
1580 | res = super(financing_contract_account_quadruplet, self)._auto_init(cr, context) |
1581 | - sql.drop_view_if_exists(cr, 'financing_contract_account_quadruplet') |
1582 | - cr.execute("""CREATE OR REPLACE VIEW financing_contract_account_quadruplet AS ( |
1583 | + sql.drop_view_if_exists(cr, 'financing_contract_account_quadruplet_old') |
1584 | + |
1585 | + # old sql view kept to manage migration and old sync updates |
1586 | + cr.execute('''CREATE OR REPLACE VIEW financing_contract_account_quadruplet_old AS ( |
1587 | SELECT abs(('x'||substr(md5(fp.code || cc.code || lnk.name),1,16))::bit(32)::int) as id, |
1588 | lnk.destination_id AS account_destination_id, cc.id AS cost_center_id, fp.id AS funding_pool_id, lnk.name AS account_destination_name, lnk.account_id, lnk.disabled, lnk.id as account_destination_link_id |
1589 | FROM account_analytic_account fp, |
1590 | @@ -44,9 +74,133 @@ |
1591 | AND fpacc.cost_center_id = cc.id |
1592 | AND lnk.id = fpad.tuple_id |
1593 | AND fp.id = fpad.funding_pool_id |
1594 | - ORDER BY lnk.name, cc.code DESC)""") |
1595 | + ORDER BY lnk.name, cc.code DESC) |
1596 | + ''') |
1597 | + |
1598 | + cr.execute("""CREATE OR REPLACE VIEW financing_contract_account_quadruplet_view AS ( |
1599 | + SELECT account_destination_id, cost_center_id, funding_pool_id, account_destination_name, account_id, disabled, account_destination_link_id FROM |
1600 | + ( |
1601 | + -- all cc = f, G/L = f |
1602 | + SELECT |
1603 | + lnk.destination_id AS account_destination_id, cc.id AS cost_center_id, fp.id AS funding_pool_id, lnk.name AS account_destination_name, lnk.account_id, lnk.disabled, lnk.id as account_destination_link_id |
1604 | + FROM account_analytic_account fp, |
1605 | + account_analytic_account cc, |
1606 | + funding_pool_associated_cost_centers fpacc, |
1607 | + funding_pool_associated_destinations fpad, |
1608 | + account_destination_link lnk |
1609 | + WHERE |
1610 | + fpacc.funding_pool_id = fp.id AND |
1611 | + fpacc.cost_center_id = cc.id AND |
1612 | + lnk.id = fpad.tuple_id AND |
1613 | + fp.id = fpad.funding_pool_id |
1614 | + |
1615 | + UNION |
1616 | + |
1617 | + -- all cc = t, G/L = t |
1618 | + select |
1619 | + lnk.destination_id AS account_destination_id, cc.id AS cost_center_id, fp.id AS funding_pool_id, lnk.name AS account_destination_name, lnk.account_id, lnk.disabled, lnk.id as account_destination_link_id |
1620 | + FROM |
1621 | + account_analytic_account fp, |
1622 | + account_analytic_account cc, |
1623 | + fp_account_rel, |
1624 | + account_target_costcenter target, |
1625 | + account_destination_link lnk, |
1626 | + account_account gl_account |
1627 | + where |
1628 | + fp.allow_all_cc_with_fp = 't' and |
1629 | + cc.type != 'view' and |
1630 | + cc.category = 'OC' and |
1631 | + target.cost_center_id = cc.id and |
1632 | + target.instance_id = fp.instance_id and |
1633 | + fp.select_accounts_only = 't' and |
1634 | + fp_account_rel.fp_id = fp.id and |
1635 | + fp_account_rel.account_id= gl_account.id and |
1636 | + lnk.account_id = gl_account.id |
1637 | + |
1638 | + UNION |
1639 | + |
1640 | + -- all cc = f, G/L = t |
1641 | + select |
1642 | + lnk.destination_id AS account_destination_id, cc.id AS cost_center_id, fp.id AS funding_pool_id, lnk.name AS account_destination_name, lnk.account_id, lnk.disabled, lnk.id as account_destination_link_id |
1643 | + FROM |
1644 | + account_analytic_account fp, |
1645 | + account_analytic_account cc, |
1646 | + funding_pool_associated_cost_centers fpacc, |
1647 | + fp_account_rel, |
1648 | + account_destination_link lnk, |
1649 | + account_account gl_account |
1650 | + where |
1651 | + fp.allow_all_cc_with_fp = 'f' and |
1652 | + fpacc.funding_pool_id = fp.id and |
1653 | + fpacc.cost_center_id = cc.id and |
1654 | + fp.select_accounts_only = 't' and |
1655 | + fp_account_rel.fp_id = fp.id and |
1656 | + fp_account_rel.account_id= gl_account.id and |
1657 | + lnk.account_id = gl_account.id |
1658 | + |
1659 | + UNION |
1660 | + |
1661 | + -- all cc = t , G/L = f |
1662 | + select |
1663 | + lnk.destination_id AS account_destination_id, cc.id AS cost_center_id, fp.id AS funding_pool_id, lnk.name AS account_destination_name, lnk.account_id, lnk.disabled, lnk.id as account_destination_link_id |
1664 | + FROM |
1665 | + account_analytic_account fp, |
1666 | + account_analytic_account cc, |
1667 | + funding_pool_associated_destinations fpad, |
1668 | + account_target_costcenter target, |
1669 | + account_destination_link lnk |
1670 | + where |
1671 | + fp.allow_all_cc_with_fp = 't' and |
1672 | + cc.type != 'view' and |
1673 | + cc.category = 'OC' and |
1674 | + target.cost_center_id = cc.id and |
1675 | + target.instance_id = fp.instance_id and |
1676 | + fp.select_accounts_only = 'f' and |
1677 | + lnk.id = fpad.tuple_id and |
1678 | + fp.id = fpad.funding_pool_id |
1679 | + ) AS combinations |
1680 | + )""") |
1681 | return res |
1682 | |
1683 | + def gen_quadruplet(self, cr, uid, context=None): |
1684 | + ''' |
1685 | + triggered by unifield-web to generate the list of quadruplets for this contract |
1686 | + record the last generation date to refresh only if the contract, a dest link or a ana. acccount is modified |
1687 | + ''' |
1688 | + if context is None: |
1689 | + context = {} |
1690 | + contract_id = context.get('contract_id', False) |
1691 | + if contract_id: |
1692 | + ctr_obj = self.pool.get('financing.contract.contract') |
1693 | + contract = ctr_obj.browse(cr, uid, context['contract_id'], fields_to_fetch=['funding_pool_ids', 'cost_center_ids', 'quad_gen_date'], context=context) |
1694 | + cr.execute('''select max(last_modification) from ir_model_data where module='sd' and ( |
1695 | + model in ('account.analytic.account', 'account.destination.link') or (model = 'financing.contract.contract' and res_id = %s) |
1696 | + )''', (contract_id,)) |
1697 | + last_obj_modified = cr.fetchone()[0] |
1698 | + if not contract.quad_gen_date or last_obj_modified > contract.quad_gen_date or contract.quad_gen_date > time.strftime('%Y-%m-%d %H:%M:%S'): |
1699 | + # ignore quad_gen_date in the future |
1700 | + cc_ids = [cc.id for cc in contract.cost_center_ids] |
1701 | + fp_ids = [fp.funding_pool_id.id for fp in contract.funding_pool_ids] |
1702 | + if not cc_ids: |
1703 | + # do not traceback if cc / fp not set on contract |
1704 | + cc_ids = [0] |
1705 | + if not fp_ids: |
1706 | + fp_ids = [0] |
1707 | + cr.execute(''' |
1708 | + INSERT INTO financing_contract_account_quadruplet |
1709 | + (account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id) |
1710 | + (select |
1711 | + account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id |
1712 | + from |
1713 | + financing_contract_account_quadruplet_view |
1714 | + where |
1715 | + funding_pool_id in %s and |
1716 | + cost_center_id in %s |
1717 | + ) |
1718 | + ON CONFLICT ON CONSTRAINT financing_contract_account_quadruplet_check_unique DO UPDATE SET disabled=EXCLUDED.disabled''', (tuple(fp_ids), tuple(cc_ids))) |
1719 | + cr.execute('update financing_contract_contract set quad_gen_date=%s where id=%s', (last_obj_modified, contract_id)) |
1720 | + |
1721 | + return True |
1722 | |
1723 | # The result set with {ID:Flag} if Flag=True, the line will be grey, otherwise, it is selectable |
1724 | def _get_used_in_contract(self, cr, uid, ids, field_name, arg, context=None): |
1725 | @@ -67,8 +221,8 @@ |
1726 | # TODO this should be renamed format_id |
1727 | cr.execute('''select account_quadruplet_id |
1728 | from financing_contract_actual_account_quadruplets |
1729 | - where actual_line_id in (select id from financing_contract_format_line |
1730 | - where format_id = %s and is_quadruplet is true)''', (contract.format_id.id,)) |
1731 | + where account_quadruplet_id in %s and actual_line_id in (select id from financing_contract_format_line |
1732 | + where format_id = %s and is_quadruplet is true)''', (tuple(ids), contract.format_id.id,)) |
1733 | rows = cr.fetchall() |
1734 | for id in [x[0] for x in rows]: |
1735 | exclude[id] = True |
1736 | @@ -78,9 +232,13 @@ |
1737 | if not active_id or line.id != active_id: |
1738 | for account_destination in line.account_destination_ids: |
1739 | # search the quadruplet to exclude |
1740 | - quadruplet_ids_to_exclude = self.search(cr, uid, [('account_id', '=', account_destination.account_id.id),('account_destination_id','=',account_destination.destination_id.id)]) |
1741 | + quadruplet_ids_to_exclude = self.search(cr, uid, [('id', 'in', ids), ('account_id', '=', account_destination.account_id.id),('account_destination_id','=',account_destination.destination_id.id)]) |
1742 | for item in quadruplet_ids_to_exclude: |
1743 | exclude[item] = True |
1744 | + for account in line.reporting_account_ids: |
1745 | + # exclude the quadruplets when the account has been selected in lines with "accounts only" |
1746 | + for quad in self.search(cr, uid, [('account_id', '=', account.id)], order='NO_ORDER', context=context): |
1747 | + exclude[quad] = True |
1748 | |
1749 | for id in ids: |
1750 | ids_to_exclude[id] = id in exclude |
1751 | @@ -90,35 +248,20 @@ |
1752 | res = {} |
1753 | if context is None: |
1754 | context = {} |
1755 | - exclude = {} |
1756 | + res = {} |
1757 | |
1758 | if not context.get('contract_id'): |
1759 | for id in ids: |
1760 | res[id] = False |
1761 | return res |
1762 | |
1763 | - ctr_obj = self.pool.get('financing.contract.contract') |
1764 | - contract = ctr_obj.browse(cr, uid, context['contract_id']) |
1765 | - # financing_contract_funding_pool_line.contract_id is a FK for financing_contract_format.id |
1766 | - # TODO this should be renamed format_id during refactoring |
1767 | - exclude = {} |
1768 | - cr.execute('''select id from financing_contract_account_quadruplet |
1769 | - where funding_pool_id in |
1770 | - (select funding_pool_id |
1771 | - from financing_contract_funding_pool_line |
1772 | - where contract_id = %s) |
1773 | - and exists (select 'X' |
1774 | - from financing_contract_cost_center cc |
1775 | - where cc.contract_id = %s |
1776 | - and cc.cost_center_id = |
1777 | - financing_contract_account_quadruplet.cost_center_id)''', (contract.format_id.id,contract.format_id.id)) |
1778 | - for id in [x[0] for x in cr.fetchall()]: |
1779 | - exclude[id] = True |
1780 | - for id in ids: |
1781 | - res[id] = id in exclude |
1782 | + for _id in ids: |
1783 | + res[_id] = False |
1784 | + |
1785 | + for _id in self.search(cr, uid, [('can_be_used', '=', True)], context=context): |
1786 | + res[_id] = True |
1787 | return res |
1788 | |
1789 | - |
1790 | def _search_can_be(self, cr, uid, ids, field_name, arg, context=None): |
1791 | res = {} |
1792 | if context is None: |
1793 | @@ -130,67 +273,30 @@ |
1794 | return res |
1795 | |
1796 | ctr_obj = self.pool.get('financing.contract.contract') |
1797 | - contract = ctr_obj.browse(cr, uid, context['contract_id']) |
1798 | - cr.execute('''select id from financing_contract_account_quadruplet |
1799 | - where funding_pool_id in |
1800 | - (select funding_pool_id |
1801 | - from financing_contract_funding_pool_line |
1802 | - where contract_id = %s) |
1803 | - and exists (select 'X' |
1804 | - from financing_contract_cost_center cc |
1805 | - where cc.contract_id = %s |
1806 | - and cc.cost_center_id = |
1807 | - financing_contract_account_quadruplet.cost_center_id)''', (contract.format_id.id,contract.format_id.id)) |
1808 | - someids = [] |
1809 | - someids += [x[0] for x in cr.fetchall()] |
1810 | - return [('id','in',someids)] |
1811 | - |
1812 | - |
1813 | - |
1814 | - def _search_used_in_contract(self, cr, uid, obj, name, args, context=None): |
1815 | - if not args: |
1816 | - return [] |
1817 | - if context is None: |
1818 | - context = {} |
1819 | - assert args[0][1] == '=' and args[0][2], 'Filter not implemented' |
1820 | - if not context.get('contract_id'): |
1821 | - return [] |
1822 | - |
1823 | - ctr_obj = self.pool.get('financing.contract.contract') |
1824 | - contract = ctr_obj.browse(cr, uid, context['contract_id']) |
1825 | - |
1826 | - exclude = [] |
1827 | - for line in contract.actual_line_ids: |
1828 | - if context.get('active_id', False) and line.id != context['active_id']: |
1829 | - for account_destination in line.account_destination_ids: |
1830 | - cr.execute('''select account_quadruplet_id |
1831 | - from financing_contract_actual_account_quadruplets |
1832 | - where actual_line_id in (select l.id from financing_contract_contract c, |
1833 | - financing_contract_format f, |
1834 | - financing_contract_format_line l |
1835 | - where c.id = %s |
1836 | - and f.id = c.format_id |
1837 | - and l.format_id = f.id)''', (contract.format_id.id,)) |
1838 | - exclude += [x[0] for x in cr.fetchall()] |
1839 | - for account_quadruplet in line.account_quadruplet_ids: |
1840 | - exclude.append(account_quadruplet.id) |
1841 | - return [('id', 'not in', exclude)] |
1842 | - |
1843 | - #columns for view |
1844 | + contract = ctr_obj.browse(cr, uid, context['contract_id'], fields_to_fetch=['funding_pool_ids', 'cost_center_ids']) |
1845 | + cc_ids = [cc.id for cc in contract.cost_center_ids] |
1846 | + fp_ids = [fp.funding_pool_id.id for fp in contract.funding_pool_ids] |
1847 | + return [('cost_center_id', 'in', cc_ids), ('funding_pool_id', 'in', fp_ids)] |
1848 | + |
1849 | _columns = { |
1850 | - 'account_destination_id': fields.many2one('account.destination.link', 'Account/Destination', relate=True, readonly=True), |
1851 | - 'cost_center_id': fields.many2one('account.analytic.account', 'Cost Centre', relate=True, readonly=True), |
1852 | - 'funding_pool_id': fields.many2one('account.analytic.account', 'Funding Pool', relate=True, readonly=True), |
1853 | - 'account_destination_name': fields.char('Account', size=64, readonly=True), |
1854 | - 'used_in_contract': fields.function(_get_used_in_contract, method=True, type='boolean', string='Used', fnct_search=_search_used_in_contract), |
1855 | + 'account_destination_id': fields.many2one('account.analytic.account', 'Destination', relate=True, readonly=True, select=1), |
1856 | + 'cost_center_id': fields.many2one('account.analytic.account', 'Cost Centre', relate=True, readonly=True, select=1), |
1857 | + 'funding_pool_id': fields.many2one('account.analytic.account', 'Funding Pool', relate=True, readonly=True, select=1), |
1858 | + 'account_destination_name': fields.char('Account', size=64, readonly=True, select=1), |
1859 | + 'used_in_contract': fields.function(_get_used_in_contract, method=True, type='boolean', string='Used'), |
1860 | 'can_be_used': fields.function(_can_be_used_in_contract, method=True, type='boolean', string='Can', fnct_search=_search_can_be), |
1861 | - 'account_id': fields.many2one('account.destination.link', 'Account ID', relate=True, readonly=True), |
1862 | - 'account_destination_link_id': fields.many2one('account.destination.link', 'Link id', readonly=True), |
1863 | + 'account_id': fields.many2one('account.account', 'Account ID', relate=True, readonly=True, select=1), |
1864 | + 'account_destination_link_id': fields.many2one('account.destination.link', 'Link id', readonly=True, select=1), |
1865 | 'disabled': fields.boolean('Disabled'), |
1866 | } |
1867 | |
1868 | - _order = 'account_destination_name asc, funding_pool_id asc, cost_center_id asc' |
1869 | + _sql_constraints = { |
1870 | + ('check_unique', |
1871 | + 'unique (account_destination_id, cost_center_id, funding_pool_id, account_id, account_destination_link_id)', |
1872 | + 'not unique!') |
1873 | + } |
1874 | + _order = 'account_destination_name asc, funding_pool_id asc, cost_center_id asc, id' |
1875 | + |
1876 | |
1877 | financing_contract_account_quadruplet() |
1878 | # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
1879 | - |
1880 | |
1881 | === modified file 'bin/addons/financing_contract/financing_contract_view.xml' |
1882 | --- bin/addons/financing_contract/financing_contract_view.xml 2020-01-30 10:18:45 +0000 |
1883 | +++ bin/addons/financing_contract/financing_contract_view.xml 2020-11-02 12:53:09 +0000 |
1884 | @@ -105,20 +105,54 @@ |
1885 | <field name="overhead_type" colspan="2" attrs="{'required': [('line_type', '=', 'overhead')]}"/> |
1886 | <field name="overhead_percentage" colspan="2" attrs="{'required': [('line_type', '=', 'overhead')]}"/> |
1887 | </group> |
1888 | - <button name="button_delete_all_quads" type="object" string="Remove all quads" icon="gtk-clear" colspan="1" |
1889 | - attrs="{'invisible': [('is_quadruplet', '=', False)]}" /> |
1890 | - <button name="button_delete_all_couples" type="object" string="Remove all couples" icon="gtk-clear" colspan="1" |
1891 | - attrs="{'invisible': [('is_quadruplet', '=', True)]}" /> |
1892 | - <field name="is_quadruplet" attrs="{'invisible': [('line_type', '=', 'view')]}"/> |
1893 | + <group colspan="6" col="20"> |
1894 | + <button name="button_delete_all_quads" type="object" string="Remove all quads" |
1895 | + icon="gtk-clear" colspan="4" |
1896 | + attrs="{'invisible': ['|', |
1897 | + ('is_quadruplet', '=', False), |
1898 | + ('line_type', '=', 'view')]}" /> |
1899 | + <button name="button_delete_all_couples" type="object" string="Remove all couples" |
1900 | + icon="gtk-clear" colspan="4" |
1901 | + attrs="{'invisible': ['|', '|', |
1902 | + ('is_quadruplet', '=', True), |
1903 | + ('reporting_select_accounts_only', '=', True), |
1904 | + ('line_type', '=', 'view')]}" |
1905 | + /> |
1906 | + <button name="button_remove_all_accounts" type="object" string="Remove all accounts" |
1907 | + icon="gtk-clear" colspan="4" |
1908 | + attrs="{'invisible': ['|', |
1909 | + ('reporting_select_accounts_only', '=', False), |
1910 | + ('line_type', '=', 'view')]}" /> |
1911 | + <label string="" colspan="1"/> |
1912 | + <field name="is_quadruplet" attrs="{'invisible': [('line_type', '=', 'view')]}" |
1913 | + colspan="2" |
1914 | + on_change="on_change_is_quadruplet(is_quadruplet)" |
1915 | + /> |
1916 | + <field name="reporting_select_accounts_only" attrs="{'invisible': [('line_type', '=', 'view')]}" |
1917 | + colspan="2" |
1918 | + on_change="on_change_reporting_select_accounts_only(reporting_select_accounts_only)" |
1919 | + /> |
1920 | + <label string="" colspan="3"/> |
1921 | + </group> |
1922 | <field name="account_destination_ids" colspan="4" string="Account/Destination" nolabel="1" |
1923 | context="{'search_default_active': 1}" |
1924 | - attrs="{'invisible': [('|'), ('line_type', '=', 'view'), ('is_quadruplet', '=', True)]}"> |
1925 | - </field> |
1926 | + attrs="{'invisible': ['|', '|', |
1927 | + ('line_type', '=', 'view'), |
1928 | + ('is_quadruplet', '=', True), |
1929 | + ('reporting_select_accounts_only', '=', True)]}" |
1930 | + /> |
1931 | <field name="account_quadruplet_ids" colspan="4" string="Account/Destination/Funding Pool/Cost Centre" nolabel="1" |
1932 | context="{'search_default_active': 1}" |
1933 | attrs="{'invisible': [('|'), ('line_type', '=', 'view'), ('is_quadruplet', '!=', True)]}" |
1934 | domain="[('can_be_used','=',True)]"> |
1935 | </field> |
1936 | + <field name="reporting_account_ids" colspan="4" string="G/L Accounts" nolabel="1" |
1937 | + context="{'from_grant_management': True, 'search_default_active': 1}" |
1938 | + attrs="{'invisible': ['|', |
1939 | + ('line_type', '=', 'view'), |
1940 | + ('reporting_select_accounts_only', '=', False)]}" |
1941 | + domain="[('selectable_in_contract', '=', True)]" |
1942 | + /> |
1943 | </form> |
1944 | </field> |
1945 | </page> |
1946 | @@ -175,7 +209,6 @@ |
1947 | <field name="type">tree</field> |
1948 | <field name="arch" type="xml"> |
1949 | <tree string="Account/Destination/Funding Pool/Cost Centre" colors="grey:used_in_contract;red:disabled" notselectable="used_in_contract" editable="top" noteditable="1"> |
1950 | - <field name="can_be_used" invisible="1"/> |
1951 | <field name="account_destination_name"/> |
1952 | <field name="funding_pool_id"/> |
1953 | <field name="cost_center_id"/> |
1954 | @@ -304,9 +337,19 @@ |
1955 | <field name="overhead_type" colspan="2" attrs="{'required': [('line_type', '=', 'overhead')]}"/> |
1956 | <field name="overhead_percentage" colspan="2" attrs="{'required': [('line_type', '=', 'overhead')]}"/> |
1957 | </group> |
1958 | + <field name="reporting_select_accounts_only" attrs="{'invisible': [('line_type', '=', 'view')]}" |
1959 | + colspan="2"/> |
1960 | <field name="account_destination_ids" colspan="4" string="Account/Destination" |
1961 | context="context" |
1962 | - attrs="{'invisible': [('line_type', '=', 'view')]}"/> |
1963 | + attrs="{'invisible': ['|', |
1964 | + ('line_type', '=', 'view'), |
1965 | + ('reporting_select_accounts_only', '=', True)]}"/> |
1966 | + <field name="reporting_account_ids" colspan="4" string="G/L Accounts" |
1967 | + context="{'from_grant_management': True, 'search_default_active': 1}" |
1968 | + attrs="{'invisible': ['|', |
1969 | + ('line_type', '=', 'view'), |
1970 | + ('reporting_select_accounts_only', '=', False)]}" |
1971 | + /> |
1972 | </form> |
1973 | </field> |
1974 | </page> |
1975 | |
1976 | === modified file 'bin/addons/financing_contract/format.py' |
1977 | --- bin/addons/financing_contract/format.py 2015-05-28 08:50:00 +0000 |
1978 | +++ bin/addons/financing_contract/format.py 2020-11-02 12:53:09 +0000 |
1979 | @@ -97,39 +97,17 @@ |
1980 | duplet_ids_to_exclude = self.search(cr, uid, [('account_id', '=', account_quadruplet.account_id.id),('destination_id','=',account_quadruplet.account_destination_id.id)]) |
1981 | for item in duplet_ids_to_exclude: |
1982 | exclude[item] = True |
1983 | + for account in line.reporting_account_ids: |
1984 | + # exclude the acc/dest combinations when the account has been selected in lines with "accounts only" |
1985 | + for acc_dest in self.search(cr, uid, [('account_id', '=', account.id)], order='NO_ORDER', context=context): |
1986 | + exclude[acc_dest] = True |
1987 | |
1988 | for id in ids: |
1989 | ids_to_exclude[id] = id in exclude |
1990 | return ids_to_exclude |
1991 | |
1992 | - def _search_used_in_contract(self, cr, uid, obj, name, args, context=None): |
1993 | - if not args: |
1994 | - return [] |
1995 | - if context is None: |
1996 | - context = {} |
1997 | - assert args[0][1] == '=' and args[0][2], 'Filter not implemented' |
1998 | - if not context.get('contract_id') and not context.get('donor_id'): |
1999 | - return [] |
2000 | - |
2001 | - if context.get('contract_id'): |
2002 | - ctr_obj = self.pool.get('financing.contract.contract') |
2003 | - id_toread = context['contract_id'] |
2004 | - elif context.get('donor_id'): |
2005 | - ctr_obj = self.pool.get('financing.contract.donor') |
2006 | - id_toread = context['donor_id'] |
2007 | - |
2008 | - exclude = {} |
2009 | - for line in ctr_obj.browse(cr, uid, id_toread).actual_line_ids: |
2010 | - if not context.get('active_id', False) or line.id != context['active_id']: |
2011 | - for account_destination in line.account_destination_ids: |
2012 | - exclude[account_destination.id] = True |
2013 | - for account_quadruplet in line.account_quadruplet_ids: |
2014 | - exclude[account_quadruplet.account_destination_id.id] = True |
2015 | - |
2016 | - return [('id', 'not in', exclude.keys())] |
2017 | - |
2018 | _columns = { |
2019 | - 'used_in_contract': fields.function(_get_used_in_contract, method=True, type='boolean', string='Used', fnct_search=_search_used_in_contract), |
2020 | + 'used_in_contract': fields.function(_get_used_in_contract, method=True, type='boolean', string='Used'), |
2021 | } |
2022 | |
2023 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
2024 | |
2025 | === modified file 'bin/addons/financing_contract/format_line.py' |
2026 | --- bin/addons/financing_contract/format_line.py 2019-10-30 16:33:21 +0000 |
2027 | +++ bin/addons/financing_contract/format_line.py 2020-11-02 12:53:09 +0000 |
2028 | @@ -22,7 +22,7 @@ |
2029 | from osv import fields, osv |
2030 | from analytic_distribution.destination_tools import many2many_sorted |
2031 | from account_override import ACCOUNT_RESTRICTED_AREA |
2032 | - |
2033 | +from tools.safe_eval import safe_eval |
2034 | |
2035 | class financing_contract_format_line(osv.osv): |
2036 | |
2037 | @@ -63,50 +63,47 @@ |
2038 | return domain |
2039 | |
2040 | # get list of accounts for duplet format lines |
2041 | - def _create_account_couple_domain(self, account_destination_list, general_domain): |
2042 | - if len(account_destination_list) == 0: |
2043 | - return False # Just make this condition to False |
2044 | - elif len(account_destination_list) == 1: |
2045 | - temp_domain = ['&', |
2046 | - ('general_account_id', '=', account_destination_list[0].account_id.id), |
2047 | - ('destination_id', '=', account_destination_list[0].destination_id.id)] |
2048 | - |
2049 | - return temp_domain |
2050 | - else: |
2051 | - firstElement = self._create_account_couple_domain([account_destination_list[0]], general_domain) |
2052 | - secondElement = self._create_account_couple_domain(account_destination_list[1:], general_domain) |
2053 | - |
2054 | - if firstElement and secondElement: |
2055 | - return ['|'] + firstElement + secondElement |
2056 | - elif firstElement: |
2057 | - return firstElement |
2058 | - return secondElement |
2059 | + def _create_account_couple_domain(self, account_destination_list): |
2060 | + """ |
2061 | + Returns the domain corresponding to the list of acc/dest in param. |
2062 | + """ |
2063 | + dom = [] |
2064 | + if not account_destination_list: |
2065 | + return dom |
2066 | + first = True |
2067 | + for account_dest in account_destination_list: |
2068 | + dom += ['&', ('general_account_id', '=', account_dest.account_id.id), ('destination_id', '=', account_dest.destination_id.id)] |
2069 | + if not first: |
2070 | + dom.insert(0, '|') |
2071 | + else: |
2072 | + first = False |
2073 | + return dom |
2074 | |
2075 | # get list of accounts for quadruplet format lines |
2076 | - def _create_account_quadruplet_domain(self, account_quadruplet_list, funding_pool_ids=False): |
2077 | - if len(account_quadruplet_list) == 0: |
2078 | - return False |
2079 | - elif len(account_quadruplet_list) == 1: |
2080 | - if account_quadruplet_list[0].funding_pool_id.id in funding_pool_ids: |
2081 | - quad_element = account_quadruplet_list[0] |
2082 | - return ['&', |
2083 | - '&', |
2084 | - ('general_account_id', '=', quad_element.account_id.id), |
2085 | - ('destination_id', '=', quad_element.account_destination_id.id), |
2086 | - '&', |
2087 | - ('cost_center_id', '=', quad_element.cost_center_id.id), |
2088 | - ('account_id', '=', quad_element.funding_pool_id.id)] |
2089 | - else: |
2090 | - return False |
2091 | - else: |
2092 | - firstElement = self._create_account_quadruplet_domain([account_quadruplet_list[0]], funding_pool_ids) |
2093 | - secondElement = self._create_account_quadruplet_domain(account_quadruplet_list[1:], funding_pool_ids) |
2094 | - |
2095 | - if firstElement and secondElement: |
2096 | - return ['|'] + firstElement + secondElement |
2097 | - elif firstElement: |
2098 | - return firstElement |
2099 | - return secondElement |
2100 | + def _create_account_quadruplet_domain(self, account_quadruplet_list, funding_pool_ids=None): |
2101 | + """ |
2102 | + Returns the domain corresponding to the list of quadruplets in param. |
2103 | + """ |
2104 | + dom = [] |
2105 | + if not account_quadruplet_list: |
2106 | + return dom |
2107 | + if funding_pool_ids is None: |
2108 | + funding_pool_ids = [] |
2109 | + first = True |
2110 | + for quad in account_quadruplet_list: |
2111 | + if quad.funding_pool_id.id in funding_pool_ids: |
2112 | + dom += ['&', |
2113 | + '&', |
2114 | + '&', |
2115 | + ('general_account_id', '=', quad.account_id.id), |
2116 | + ('destination_id', '=', quad.account_destination_id.id), |
2117 | + ('cost_center_id', '=', quad.cost_center_id.id), |
2118 | + ('account_id', '=', quad.funding_pool_id.id)] |
2119 | + if not first: |
2120 | + dom.insert(0, '|') |
2121 | + else: |
2122 | + first = False |
2123 | + return dom |
2124 | |
2125 | def _get_number_of_childs(self, cr, uid, ids, field_name=None, arg=None, context=None): |
2126 | # Verifications |
2127 | @@ -150,14 +147,27 @@ |
2128 | self.write(cr, uid, ids, {'account_destination_ids':[(6, 0, [])]}, context=context ) |
2129 | return True |
2130 | |
2131 | + def button_remove_all_accounts(self, cr, uid, ids, context=None): |
2132 | + """ |
2133 | + Removes all G/L accounts selected in the Reporting lines wizard |
2134 | + """ |
2135 | + self.write(cr, uid, ids, {'reporting_account_ids': [(6, 0, [])]}, context=context) |
2136 | + return True |
2137 | + |
2138 | # Get the list of accounts for both duplet and quadruplet |
2139 | def _get_accounts_couple_and_quadruplets(self, browse_line): |
2140 | + """ |
2141 | + Returns a dict with a list of browse records for acc/dest, quadruplets and "accounts only" |
2142 | + """ |
2143 | account_destination_result = [] |
2144 | account_quadruplet_result = [] |
2145 | - |
2146 | + account_gl_result = [] |
2147 | if browse_line.line_type != 'view': |
2148 | if browse_line.is_quadruplet: |
2149 | account_quadruplet_result = [account_quadruplet for account_quadruplet in browse_line.account_quadruplet_ids] |
2150 | + elif browse_line.reporting_select_accounts_only: |
2151 | + # this syntax's goal is to get a list of browse records instead of a browse_record_list |
2152 | + account_gl_result = [a for a in browse_line.reporting_account_ids] |
2153 | else: |
2154 | account_destination_result = [account_destination for account_destination in browse_line.account_destination_ids] |
2155 | else: |
2156 | @@ -165,8 +175,12 @@ |
2157 | temp = self._get_accounts_couple_and_quadruplets(child_line) |
2158 | account_destination_result += temp['account_destination_list'] |
2159 | account_quadruplet_result += temp['account_quadruplet_list'] |
2160 | - return {'account_destination_list': account_destination_result, |
2161 | - 'account_quadruplet_list': account_quadruplet_result} |
2162 | + account_gl_result += temp['account_gl_list'] |
2163 | + return { |
2164 | + 'account_destination_list': account_destination_result, |
2165 | + 'account_quadruplet_list': account_quadruplet_result, |
2166 | + 'account_gl_list': account_gl_result, |
2167 | + } |
2168 | |
2169 | def _get_general_domain(self, cr, uid, browse_format, domain_type, context=None): |
2170 | # Method to get the domain (allocated or project) of a line |
2171 | @@ -205,6 +219,8 @@ |
2172 | funding_pool_ids = [x for x in funding_pool_ids if x not in fp_ids] |
2173 | funding_pool_domain = self._create_domain('account_id', funding_pool_ids) |
2174 | gen_domain['funding_pool_domain'] = funding_pool_domain |
2175 | + else: |
2176 | + gen_domain['funding_pool_domain'] = "('account_id', 'in', [])" |
2177 | |
2178 | gen_domain['funding_pool_ids'] = [x.id for x in funding_pool_ids] |
2179 | return gen_domain |
2180 | @@ -221,23 +237,35 @@ |
2181 | if format.eligibility_from_date and format.eligibility_to_date: |
2182 | #### DUY US-385: MOVE THIS TO OUTSIDE OF THE ALL THE LOOPS |
2183 | general_domain = self._get_general_domain(cr, uid, format, domain_type, context=context) |
2184 | + accounts_criteria = ['&', '&', ] + non_corrected_domain |
2185 | + acc_domains = [] |
2186 | |
2187 | # Account + destination domain |
2188 | account_destination_quadruplet_ids = self._get_accounts_couple_and_quadruplets(browse_line) |
2189 | - account_couple_domain = self._create_account_couple_domain(account_destination_quadruplet_ids['account_destination_list'], False) |
2190 | + account_couple_domain = self._create_account_couple_domain(account_destination_quadruplet_ids['account_destination_list']) |
2191 | + if account_couple_domain: |
2192 | + acc_domains += [account_couple_domain] |
2193 | # get the criteria for accounts of quadruplet mode |
2194 | account_quadruplet_domain = self._create_account_quadruplet_domain(account_destination_quadruplet_ids['account_quadruplet_list'], general_domain['funding_pool_ids']) |
2195 | - |
2196 | - if not account_couple_domain and not account_quadruplet_domain: |
2197 | - return [('id', '=', '-1')] |
2198 | - |
2199 | - accounts_criteria = ['&', '&', ] + non_corrected_domain |
2200 | - if account_couple_domain and account_quadruplet_domain: |
2201 | - accounts_criteria += ['|'] + account_couple_domain + account_quadruplet_domain |
2202 | - elif account_couple_domain: |
2203 | - accounts_criteria += account_couple_domain |
2204 | - elif account_quadruplet_domain: |
2205 | - accounts_criteria += account_quadruplet_domain |
2206 | + if account_quadruplet_domain: |
2207 | + acc_domains += [account_quadruplet_domain] |
2208 | + # "Accounts Only" Domain |
2209 | + account_only_domain = [] |
2210 | + if account_destination_quadruplet_ids['account_gl_list']: |
2211 | + acc_ids = [a.id for a in account_destination_quadruplet_ids['account_gl_list']] |
2212 | + account_only_domain = [('general_account_id', 'in', acc_ids)] |
2213 | + if account_only_domain: |
2214 | + acc_domains += [account_only_domain] |
2215 | + |
2216 | + # note: it's possible to have more than one domain in case several lines are grouped into a view |
2217 | + if len(acc_domains) == 1: |
2218 | + accounts_criteria += acc_domains[0] |
2219 | + elif len(acc_domains) == 2: |
2220 | + accounts_criteria += ['|'] + acc_domains[0] + acc_domains[1] |
2221 | + elif len(acc_domains) == 3: |
2222 | + accounts_criteria += ['|'] + ['|'] + acc_domains[0] + acc_domains[1] + acc_domains[2] |
2223 | + else: |
2224 | + return [('id', '=', -1)] |
2225 | |
2226 | return accounts_criteria |
2227 | else: |
2228 | @@ -404,6 +432,67 @@ |
2229 | |
2230 | return res |
2231 | |
2232 | + def _get_quadruplet_sync_list(self, cr, uid, ids, field_name=None, arg=None, context=None): |
2233 | + tmp_quad = {} |
2234 | + link_ids = set() |
2235 | + aa_ids = set() |
2236 | + |
2237 | + for line in self.browse(cr, uid, ids, fields_to_fetch=['account_quadruplet_ids'], context=context): |
2238 | + for quad in line.account_quadruplet_ids: |
2239 | + link_ids.add(quad.account_destination_link_id.id) |
2240 | + aa_ids.add(quad.funding_pool_id.id) |
2241 | + aa_ids.add(quad.cost_center_id.id) |
2242 | + tmp_quad.setdefault(line.id, []).append([quad.account_destination_link_id.id, quad.funding_pool_id.id, quad.cost_center_id.id]) |
2243 | + |
2244 | + if link_ids: |
2245 | + link_sdref = self.pool.get('account.destination.link').get_sd_ref(cr, uid, list(link_ids), context=context) |
2246 | + if aa_ids: |
2247 | + aa_sdref = self.pool.get('account.analytic.account').get_sd_ref(cr, uid, list(aa_ids), context=context) |
2248 | + |
2249 | + ret = {} |
2250 | + for _id in ids: |
2251 | + ret[_id] = [] |
2252 | + for quad_list in tmp_quad.get(_id, []): |
2253 | + ret[_id].append([link_sdref.get(quad_list[0]), aa_sdref.get(quad_list[1]), aa_sdref.get(quad_list[2])]) |
2254 | + ret[_id] = '%s' % ret[_id] |
2255 | + return ret |
2256 | + |
2257 | + |
2258 | + def _set_quadruplet_sync_list(self, cr, uid, id, name, value, arg, context): |
2259 | + quad_obj = self.pool.get('financing.contract.account.quadruplet') |
2260 | + |
2261 | + value_list = safe_eval(value) |
2262 | + link_ids = set() |
2263 | + aa_ids = set() |
2264 | + |
2265 | + cr.execute('delete from financing_contract_actual_account_quadruplets where actual_line_id = %s', (id, )) |
2266 | + for data in value_list: |
2267 | + link_ids.add(data[0]) |
2268 | + aa_ids.add(data[1]) |
2269 | + aa_ids.add(data[2]) |
2270 | + |
2271 | + if link_ids: |
2272 | + link_sdref = self.pool.get('account.destination.link').find_sd_ref(cr, uid, list(link_ids), context=context) |
2273 | + if aa_ids: |
2274 | + aa_sdref = self.pool.get('account.analytic.account').find_sd_ref(cr, uid, list(aa_ids), context=context) |
2275 | + |
2276 | + for data in value_list: |
2277 | + quad_id = quad_obj.search(cr, uid, [('account_destination_link_id', '=', link_sdref.get(data[0])), ('funding_pool_id', '=', aa_sdref.get(data[1])), ('cost_center_id', '=', aa_sdref.get(data[2]))], context=context) |
2278 | + if not quad_id: |
2279 | + cr.execute('''INSERT INTO financing_contract_account_quadruplet (account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id) (select |
2280 | + account_destination_name, account_id, cost_center_id, disabled, account_destination_link_id, funding_pool_id, account_destination_id |
2281 | + from |
2282 | + financing_contract_account_quadruplet_view |
2283 | + where |
2284 | + account_destination_link_id = %s and |
2285 | + funding_pool_id = %s and |
2286 | + cost_center_id = %s |
2287 | + ) RETURNING id |
2288 | + ''', (link_sdref.get(data[0]), aa_sdref.get(data[1]), aa_sdref.get(data[2]))) |
2289 | + quad_id = cr.fetchone() |
2290 | + cr.execute('insert into financing_contract_actual_account_quadruplets (actual_line_id, account_quadruplet_id) values (%s, %s)', (id, quad_id[0])) |
2291 | + |
2292 | + return True |
2293 | |
2294 | _columns = { |
2295 | 'name': fields.char('Name', size=64, required=True), |
2296 | @@ -411,7 +500,7 @@ |
2297 | 'format_id': fields.many2one('financing.contract.format', 'Format'), |
2298 | 'is_quadruplet': fields.boolean('Input CC/FP at line level?'), |
2299 | 'account_destination_ids': many2many_sorted('account.destination.link', 'financing_contract_actual_account_destinations', 'actual_line_id', 'account_destination_id', string='Accounts/Destinations', domain=ACCOUNT_RESTRICTED_AREA['contract_reporting_lines']), |
2300 | - 'account_quadruplet_ids': many2many_sorted('financing.contract.account.quadruplet', 'financing_contract_actual_account_quadruplets', 'actual_line_id', 'account_quadruplet_id', string='Accounts/Destinations/Funding Pools/Cost Centres'), |
2301 | + 'account_quadruplet_ids': fields.many2many('financing.contract.account.quadruplet', 'financing_contract_actual_account_quadruplets', 'actual_line_id', 'account_quadruplet_id', string='Accounts/Destinations/Funding Pools/Cost Centres', order_by='account_destination_name asc, funding_pool_id asc, cost_center_id asc, id'), |
2302 | 'parent_id': fields.many2one('financing.contract.format.line', 'Parent line'), |
2303 | 'child_ids': fields.one2many('financing.contract.format.line', 'parent_id', 'Child lines'), |
2304 | 'line_type': fields.selection([('view','View'), |
2305 | @@ -431,12 +520,23 @@ |
2306 | |
2307 | 'allocated_real': fields.function(_get_actual_amount, method=True, store=False, string="Funded - Actuals", type="float", readonly=True), |
2308 | 'project_real': fields.function(_get_actual_amount, method=True, store=False, string="Total project - Actuals", type="float", readonly=True), |
2309 | - 'quadruplet_update': fields.text('Internal Use Only'), |
2310 | + 'quadruplet_update': fields.text('Internal Use Only (deprecated - kept to manage old sync update)'), |
2311 | + 'quadruplet_sync_list': fields.function(_get_quadruplet_sync_list, method=True, string='Used to sync quad', type='text', fnct_inv=_set_quadruplet_sync_list), |
2312 | 'instance_id': fields.many2one('msf.instance','Proprietary Instance'), |
2313 | + 'reporting_select_accounts_only': fields.boolean(string="Select Accounts Only"), |
2314 | + 'reporting_account_ids': fields.many2many('account.account', 'contract_format_line_account_rel', 'format_line_id', 'account_id', |
2315 | + string='G/L Accounts', |
2316 | + domain="[('type', '!=', 'view')," |
2317 | + " ('is_analytic_addicted', '=', True)," |
2318 | + " ('active', 'in', ['t', 'f'])]", |
2319 | + order_by='code'), |
2320 | } |
2321 | |
2322 | + |
2323 | + |
2324 | _defaults = { |
2325 | 'is_quadruplet': False, |
2326 | + 'reporting_select_accounts_only': False, |
2327 | 'line_type': 'actual', |
2328 | 'overhead_type': 'cost_percentage', |
2329 | 'parent_id': lambda *a: False |
2330 | @@ -445,36 +545,58 @@ |
2331 | _order = 'code asc' |
2332 | |
2333 | # UF-2311: Calculate the quadruplet value before writing or creating the format line |
2334 | - def calculate_quaduplet(self, vals, context): |
2335 | + def calculate_quadruplet(self, cr, uid, vals, context): |
2336 | + if context is None: |
2337 | + context = {} |
2338 | + # UC "Account/Dest." selection when... |
2339 | + # ...either no boxes are ticked |
2340 | + if 'is_quadruplet' in vals and 'reporting_select_accounts_only' in vals and \ |
2341 | + not vals['is_quadruplet'] and not vals['reporting_select_accounts_only']: |
2342 | + acc_dest_selected = True |
2343 | + # ...or "Select Accounts Only" isn't ticked in the Donors where no quadruplets are handled |
2344 | + elif context.get('donor_id') and 'reporting_select_accounts_only' in vals and not vals['reporting_select_accounts_only']: |
2345 | + acc_dest_selected = True |
2346 | + else: |
2347 | + acc_dest_selected = False |
2348 | + # View Line Type = no items selected |
2349 | if 'line_type' in vals and vals['line_type'] == 'view': |
2350 | vals['allocated_amount'] = 0.0 |
2351 | vals['project_amount'] = 0.0 |
2352 | vals['account_destination_ids'] = [(6, 0, [])] |
2353 | vals['account_quadruplet_ids'] = [(6, 0, [])] |
2354 | - elif 'is_quadruplet' in vals: # If the vals contains quadruplet value, then check if it is true or false |
2355 | - if vals.get('is_quadruplet', False): |
2356 | - # delete account/destinations |
2357 | - vals['account_destination_ids'] = [(6, 0, [])] |
2358 | - if context.get('sync_update_execution'): |
2359 | - quads_list = [] |
2360 | - if vals.get('quadruplet_update', False): |
2361 | - quadrup_str = vals['quadruplet_update'] |
2362 | - quads_list = map(int, quadrup_str.split(',')) |
2363 | - vals['account_quadruplet_ids'] = [(6, 0, quads_list)] |
2364 | - else: |
2365 | - temp = vals['account_quadruplet_ids'] |
2366 | - if temp[0]: |
2367 | - vals['quadruplet_update'] = str(temp[0][2]).strip('[]') |
2368 | - else: |
2369 | - vals['account_quadruplet_ids'] = [(6, 0, [])] |
2370 | - vals['quadruplet_update'] = '' # delete quadruplets |
2371 | + vals['quadruplet_update'] = '' |
2372 | + vals['reporting_account_ids'] = [(6, 0, [])] |
2373 | + vals['is_quadruplet'] = False |
2374 | + vals['reporting_select_accounts_only'] = False |
2375 | + # "Input CC/FP at line level" = quadruplets selected |
2376 | + elif vals.get('is_quadruplet'): |
2377 | + # reset the acc/dest and G/L accounts which might have been selected before ticking the box "Input CC/FP at line level" |
2378 | + vals['account_destination_ids'] = [(6, 0, [])] |
2379 | + vals['reporting_account_ids'] = [(6, 0, [])] |
2380 | + if context.get('sync_update_execution') and vals.get('quadruplet_update', False) and 'quadruplet_sync_list' not in vals: |
2381 | + # old sync update received |
2382 | + quadrup_str = vals['quadruplet_update'] |
2383 | + quads_list = map(int, quadrup_str.split(',')) |
2384 | + vals['account_quadruplet_ids'] = [(6, 0, self.pool.get('financing.contract.account.quadruplet').migrate_old_quad(cr, uid, quads_list))] |
2385 | + # "Select Accounts Only" = only G/L accounts selected: reset the acc/dest and quadruplets |
2386 | + elif vals.get('reporting_select_accounts_only'): |
2387 | + vals['account_destination_ids'] = [(6, 0, [])] |
2388 | + vals['account_quadruplet_ids'] = [(6, 0, [])] |
2389 | + vals['quadruplet_update'] = '' |
2390 | + # Accounts/Destinations selected: reset the G/L accounts and quadruplets |
2391 | + elif acc_dest_selected: |
2392 | + vals['reporting_account_ids'] = [(6, 0, [])] |
2393 | + vals['account_quadruplet_ids'] = [(6, 0, [])] |
2394 | + vals['quadruplet_update'] = '' |
2395 | + return True |
2396 | + |
2397 | |
2398 | def create(self, cr, uid, vals, context=None): |
2399 | if not context: |
2400 | context = {} |
2401 | |
2402 | # calculate the quadruplet combination |
2403 | - self.calculate_quaduplet(vals, context) |
2404 | + self.calculate_quadruplet(cr, uid, vals, context) |
2405 | return super(financing_contract_format_line, self).create(cr, uid, vals, context=context) |
2406 | |
2407 | def write(self, cr, uid, ids, vals, context=None): |
2408 | @@ -488,12 +610,14 @@ |
2409 | # US-180: Check if it comes from the sync update |
2410 | if context.get('sync_update_execution') and vals.get('format_id', False): |
2411 | # US-180: and if the financing contract of the contract format does not exist, then just ignore this update |
2412 | - exist = self.pool.get('financing.contract.contract').search(cr, uid, [('format_id', '=', vals['format_id'])]) |
2413 | + exist = self.pool.get('financing.contract.contract').search_exists(cr, uid, [('format_id', '=', vals['format_id'])]) |
2414 | + if not exist: |
2415 | + exist = self.pool.get('financing.contract.donor').search_exists(cr, uid, [('format_id', '=', vals['format_id'])]) |
2416 | if not exist: # No contract found for this format line |
2417 | return True |
2418 | |
2419 | # calculate the quadruplet combination |
2420 | - self.calculate_quaduplet(vals, context) |
2421 | + self.calculate_quadruplet(cr, uid, vals, context) |
2422 | return super(financing_contract_format_line, self).write(cr, uid, ids, vals, context=context) |
2423 | |
2424 | def copy_format_line(self, cr, uid, browse_source_line, destination_format_id, parent_id=None, context=None): |
2425 | @@ -505,14 +629,37 @@ |
2426 | 'parent_id': parent_id, |
2427 | 'line_type': browse_source_line.line_type, |
2428 | 'account_quadruplet_ids': [(6, 0, [])], |
2429 | + 'reporting_select_accounts_only': browse_source_line.reporting_select_accounts_only, |
2430 | } |
2431 | account_destination_ids = [account_destination.id for account_destination in browse_source_line.account_destination_ids] |
2432 | format_line_vals['account_destination_ids'] = [(6, 0, account_destination_ids)] |
2433 | + # copy the list of "Accounts Only" |
2434 | + gl_account_ids = [a.id for a in browse_source_line.reporting_account_ids] |
2435 | + format_line_vals['reporting_account_ids'] = [(6, 0, gl_account_ids)] |
2436 | parent_line_id = self.pool.get('financing.contract.format.line').create(cr, uid, format_line_vals, context=context) |
2437 | for child_line in browse_source_line.child_ids: |
2438 | self.copy_format_line(cr, uid, child_line, destination_format_id, parent_line_id, context=context) |
2439 | return |
2440 | |
2441 | + def on_change_is_quadruplet(self, cr, uid, ids, is_quadruplet, context=None): |
2442 | + """ |
2443 | + Ticking "Input CC/FP at line level?" automatically unticks "Select Accounts Only" |
2444 | + """ |
2445 | + res = {} |
2446 | + if is_quadruplet: |
2447 | + res['value'] = {'reporting_select_accounts_only': False, } |
2448 | + return res |
2449 | + |
2450 | + def on_change_reporting_select_accounts_only(self, cr, uid, ids, reporting_select_accounts_only, context=None): |
2451 | + """ |
2452 | + Ticking "Select Accounts Only" automatically unticks "Input CC/FP at line level?" |
2453 | + """ |
2454 | + res = {} |
2455 | + if reporting_select_accounts_only: |
2456 | + res['value'] = {'is_quadruplet': False, } |
2457 | + return res |
2458 | + |
2459 | + |
2460 | financing_contract_format_line() |
2461 | |
2462 | |
2463 | |
2464 | === modified file 'bin/addons/financing_contract/report/financing_contract.py' |
2465 | --- bin/addons/financing_contract/report/financing_contract.py 2016-02-26 10:24:24 +0000 |
2466 | +++ bin/addons/financing_contract/report/financing_contract.py 2020-11-02 12:53:09 +0000 |
2467 | @@ -74,6 +74,10 @@ |
2468 | str(quad.funding_pool_id.code), |
2469 | str(quad.cost_center_id.code)]), |
2470 | account_list, account_list_index) |
2471 | + elif line.reporting_account_ids: |
2472 | + # G/L Accounts Only selected |
2473 | + for account in line.reporting_account_ids: |
2474 | + account_list_index = add_account_list_block_item(str(account.code), account_list, account_list_index) |
2475 | else: |
2476 | # Case where we have some destination_ids |
2477 | for account_destination in line.account_destination_ids: |
2478 | |
2479 | === modified file 'bin/addons/financing_contract/report/report_project_expenses.py' |
2480 | --- bin/addons/financing_contract/report/report_project_expenses.py 2019-10-30 16:33:21 +0000 |
2481 | +++ bin/addons/financing_contract/report/report_project_expenses.py 2020-11-02 12:53:09 +0000 |
2482 | @@ -1,6 +1,8 @@ |
2483 | from report import report_sxw |
2484 | from spreadsheet_xml.spreadsheet_xml_write import SpreadsheetReport |
2485 | from tools.translate import _ |
2486 | +import logging |
2487 | + |
2488 | assert _ # pyflakes check |
2489 | |
2490 | class report_project_expenses2(report_sxw.rml_parse): |
2491 | @@ -17,7 +19,6 @@ |
2492 | self.lines = {} |
2493 | self.totalRptCurrency = 0 |
2494 | self.totalBookAmt = 0 |
2495 | - self.iter = [] |
2496 | self.localcontext.update({ |
2497 | 'getLines':self.getLines, |
2498 | 'getCostCenter':self.getCostCenter, |
2499 | @@ -26,29 +27,12 @@ |
2500 | 'getSub1':self.getSub1, |
2501 | 'getSub2':self.getSub2, |
2502 | 'getLines2':self.getLines2, |
2503 | - 'getFormula':self.getFormula, |
2504 | 'totalRptCurrency': self.totalRptCurrency, |
2505 | 'totalBookAmt':self.totalBookAmt, |
2506 | 'getTotalRptCurrency': self.getTotalRptCurrency, |
2507 | 'getTotalBookAmt': self.getTotalBookAmt, |
2508 | }) |
2509 | |
2510 | - def getFormula(self): |
2511 | - formul = '' |
2512 | - iters = self.iter[1:] |
2513 | - temp = self.iter[1:] |
2514 | - tour = 1 |
2515 | - for i in temp: |
2516 | - tour += 1 |
2517 | - nb = 0 |
2518 | - for x in iters: |
2519 | - nb += x + 1 |
2520 | - rang = nb + 1 |
2521 | - formul += '+R[-'+str(rang)+']C' |
2522 | - iters = self.iter[tour:] |
2523 | - |
2524 | - return self.totalRptCurrency |
2525 | - return formul |
2526 | |
2527 | def getTotalBookAmt(self): |
2528 | return self.totalBookAmt |
2529 | @@ -100,51 +84,46 @@ |
2530 | return [] |
2531 | contract_obj = self.pool.get('financing.contract.contract') |
2532 | format_line_obj = self.pool.get('financing.contract.format.line') |
2533 | + logger = logging.getLogger('contract.report') |
2534 | + |
2535 | contract_domain = contract_obj.get_contract_domain(self.cr, self.uid, contract, reporting_type=self.reporting_type) |
2536 | analytic_line_obj = self.pool.get('account.analytic.line') |
2537 | analytic_lines = analytic_line_obj.search(self.cr, self.uid, contract_domain, context=None) |
2538 | |
2539 | - # list of analytic journal_ids which are in the engagement journals |
2540 | + # list of analytic journal_ids which are in the engagement journals: to be added in get_contract_domain ? |
2541 | exclude_journal_ids = self.pool.get('account.analytic.journal').search(self.cr, self.uid, [('type','=','engagement')]) |
2542 | - exclude_line_ids = [] |
2543 | + |
2544 | + # gen a dict to store aji cond = reporting_line.code, reporting_line.name |
2545 | + line_code_name_by_cond = {} |
2546 | + reporting_lines_id = format_line_obj.search(self.cr, self.uid, [('format_id', '=', contract.format_id.id), ('line_type', '!=', 'view')]) |
2547 | + for report_line in format_line_obj.browse(self.cr, self.uid, reporting_lines_id): |
2548 | + if report_line.is_quadruplet: |
2549 | + for quad in report_line.account_quadruplet_ids: |
2550 | + line_code_name_by_cond[(quad.account_id.id, quad.account_destination_id.id, quad.cost_center_id.id, quad.funding_pool_id.id)] = (report_line.code, report_line.name) |
2551 | + elif not report_line.reporting_select_accounts_only: |
2552 | + for triplet in report_line.account_destination_ids: |
2553 | + line_code_name_by_cond[(triplet.account_id.id, triplet.destination_id.id)] = (report_line.code, report_line.name) |
2554 | + else: |
2555 | + for gl_only in report_line.reporting_account_ids: |
2556 | + line_code_name_by_cond[gl_only.id] = (report_line.code, report_line.name) |
2557 | + |
2558 | + # iterate over aji, to link each aji to its reporting_line |
2559 | for analytic_line in analytic_line_obj.browse(self.cr, self.uid, analytic_lines, context=None): |
2560 | if analytic_line.journal_id.id in exclude_journal_ids: |
2561 | - exclude_line_ids.append(analytic_line.id) |
2562 | - analytic_lines = [x for x in analytic_lines if x not in exclude_line_ids] |
2563 | - |
2564 | - # UFTP-16: First search in the triplet in format line, then in the second block below, search in quadruplet |
2565 | - for analytic_line in analytic_line_obj.browse(self.cr, self.uid, analytic_lines, context=None): |
2566 | - ids_adl = self.pool.get('account.destination.link').search(self.cr, self.uid,[('account_id', '=', analytic_line.general_account_id.id),('destination_id','=',analytic_line.destination_id.id) ]) |
2567 | - ids_fcfl = format_line_obj.search(self.cr, self.uid, [('account_destination_ids','in',ids_adl), ('format_id', '=', contract.format_id.id)]) |
2568 | - for fcfl in format_line_obj.browse(self.cr, self.uid, ids_fcfl): |
2569 | - ana_tuple = (analytic_line, fcfl.code, fcfl.name) |
2570 | - if lines.has_key(fcfl.code): |
2571 | - if not ana_tuple in lines[fcfl.code]: |
2572 | - lines[fcfl.code] += [ana_tuple] |
2573 | - else: |
2574 | - lines[fcfl.code] = [ana_tuple] |
2575 | - |
2576 | - # UFTP-16: First search in the triplet in format line, then in the second block below, search in quadruplet |
2577 | - for analytic_line in analytic_line_obj.browse(self.cr, self.uid, analytic_lines, context=None): |
2578 | - # US-460: Include also the funding pool in the criteria when searching for the quadruplet of the contract line |
2579 | - criteria_for_adl = [('account_id', '=', analytic_line.general_account_id.id), |
2580 | - ('account_destination_id', '=', analytic_line.destination_id and analytic_line.destination_id.id or False), |
2581 | - ('funding_pool_id', '=', analytic_line.account_id.id), |
2582 | - ('cost_center_id', '=', analytic_line.cost_center_id and analytic_line.cost_center_id.id or False)] |
2583 | - ids_adl = self.pool.get('financing.contract.account.quadruplet').search(self.cr, self.uid, criteria_for_adl) |
2584 | - |
2585 | - ids_fcfl = format_line_obj.search(self.cr, self.uid, [('account_quadruplet_ids','in',ids_adl), ('format_id', '=', contract.format_id.id)]) |
2586 | - for fcfl in format_line_obj.browse(self.cr, self.uid, ids_fcfl): |
2587 | - ana_tuple = (analytic_line, fcfl.code, fcfl.name) |
2588 | - if lines.has_key(fcfl.code): |
2589 | - if not ana_tuple in lines[fcfl.code]: |
2590 | - lines[fcfl.code] += [ana_tuple] |
2591 | - else: |
2592 | - lines[fcfl.code] = [ana_tuple] |
2593 | + continue |
2594 | + quad_key = (analytic_line.general_account_id.id, analytic_line.destination_id.id, analytic_line.cost_center_id.id, analytic_line.account_id.id) |
2595 | + if quad_key in line_code_name_by_cond: |
2596 | + lines.setdefault(line_code_name_by_cond[quad_key], []).append((analytic_line, line_code_name_by_cond[quad_key][0], line_code_name_by_cond[quad_key][1])) |
2597 | + elif quad_key[0:2] in line_code_name_by_cond: |
2598 | + triplet_key = quad_key[0:2] |
2599 | + lines.setdefault(line_code_name_by_cond[triplet_key], []).append((analytic_line, line_code_name_by_cond[triplet_key][0], line_code_name_by_cond[triplet_key][1])) |
2600 | + elif quad_key[0] in line_code_name_by_cond: |
2601 | + gl_key = quad_key[0] |
2602 | + lines.setdefault(line_code_name_by_cond[gl_key], []).append((analytic_line, line_code_name_by_cond[gl_key][0], line_code_name_by_cond[gl_key][1])) |
2603 | + else: |
2604 | + logger.warn('AJI id:%s, name: %s does not match any reporting lines on contract %s' % (analytic_line.id, analytic_line.entry_sequence, self.objects[0].code)) |
2605 | |
2606 | self.lines = lines |
2607 | - for x in lines: |
2608 | - self.iter.append(len(lines[x])) |
2609 | return lines |
2610 | |
2611 | |
2612 | |
2613 | === modified file 'bin/addons/msf_audittrail/audittrail_invoice_data.yml' |
2614 | --- bin/addons/msf_audittrail/audittrail_invoice_data.yml 2019-06-26 10:13:00 +0000 |
2615 | +++ bin/addons/msf_audittrail/audittrail_invoice_data.yml 2020-11-02 12:53:09 +0000 |
2616 | @@ -259,7 +259,7 @@ |
2617 | # Create the rule |
2618 | fields = ['state', 'category', 'code', 'complete_name', 'cost_center_ids', 'date', 'date_start', 'name', 'parent_id', |
2619 | 'type', 'for_fx_gain_loss', 'instance_id', 'tuple_destination_account_ids', 'description', 'destination_ids', |
2620 | - 'dest_cc_ids', 'allow_all_cc'] |
2621 | + 'dest_cc_ids', 'allow_all_cc', 'allow_all_cc_with_fp', 'select_accounts_only', 'fp_account_ids'] |
2622 | |
2623 | fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'account.analytic.account'), ('name', 'in', fields)], context=context) |
2624 | |
2625 | |
2626 | === modified file 'bin/addons/msf_doc_import/account.py' |
2627 | --- bin/addons/msf_doc_import/account.py 2020-06-25 08:27:13 +0000 |
2628 | +++ bin/addons/msf_doc_import/account.py 2020-11-02 12:53:09 +0000 |
2629 | @@ -180,10 +180,6 @@ |
2630 | # Prepare some values |
2631 | # Do changes because of YAML tests |
2632 | cr = pooler.get_db(dbname).cursor() |
2633 | - try: |
2634 | - msf_fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
2635 | - except ValueError: |
2636 | - msf_fp_id = 0 |
2637 | created = 0 |
2638 | processed = 0 |
2639 | errors = [] |
2640 | @@ -517,23 +513,15 @@ |
2641 | errors.append(_('Line %s. The Cost Center %s is not compatible with the Destination %s.') % |
2642 | (current_line_num, line[cols['Cost Centre']], line[cols['Destination']])) |
2643 | continue |
2644 | - # if the Fund. Pool used is NOT "PF" check the compatibility with the (account, dest) and the CC |
2645 | - if r_fp != msf_fp_id: |
2646 | - fp_fields = ['tuple_destination_account_ids', 'cost_center_ids'] |
2647 | - fp = self.pool.get('account.analytic.account').browse(cr, uid, r_fp, |
2648 | - fields_to_fetch=fp_fields, context=context) |
2649 | - if (account.id, r_destination) not in \ |
2650 | - [t.account_id and t.destination_id and (t.account_id.id, t.destination_id.id) |
2651 | - for t in fp.tuple_destination_account_ids if not t.disabled]: |
2652 | - errors.append(_('Line %s. The combination "account %s and destination %s" is not ' |
2653 | - 'compatible with the Funding Pool %s.') % |
2654 | - (current_line_num, line[cols['G/L Account']], line[cols['Destination']], |
2655 | - line[cols['Funding Pool']])) |
2656 | - continue |
2657 | - if cc.id not in [c.id for c in fp.cost_center_ids]: |
2658 | - errors.append(_('Line %s. The Cost Center %s is not compatible with the Funding Pool %s.') % |
2659 | - (current_line_num, line[cols['Cost Centre']], line[cols['Funding Pool']])) |
2660 | - continue |
2661 | + if not ad_obj.check_fp_acc_dest_compatibility(cr, uid, r_fp, account.id, r_destination, context=context): |
2662 | + errors.append(_('Line %s. The combination "account %s and destination %s" is not ' |
2663 | + 'compatible with the Funding Pool %s.') % |
2664 | + (current_line_num, line[cols['G/L Account']], line[cols['Destination']], line[cols['Funding Pool']])) |
2665 | + continue |
2666 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, r_fp, cc.id, context=context): |
2667 | + errors.append(_('Line %s. The Cost Center %s is not compatible with the Funding Pool %s.') % |
2668 | + (current_line_num, line[cols['Cost Centre']], line[cols['Funding Pool']])) |
2669 | + continue |
2670 | |
2671 | # US-937: use period of import file |
2672 | if period_name.startswith('Period 16'): |
2673 | |
2674 | === modified file 'bin/addons/msf_homere_interface/hr.py' |
2675 | --- bin/addons/msf_homere_interface/hr.py 2020-09-01 08:22:26 +0000 |
2676 | +++ bin/addons/msf_homere_interface/hr.py 2020-11-02 12:53:09 +0000 |
2677 | @@ -209,21 +209,26 @@ |
2678 | (_check_unicity, "Another employee has the same Identification No.", ['identification_id']), |
2679 | ] |
2680 | |
2681 | - def _check_employe_dest_cc_compatibility(self, cr, uid, employee_id, context=None): |
2682 | + def _check_employee_cc_compatibility(self, cr, uid, employee_id, context=None): |
2683 | """ |
2684 | - Raises an error in case the employee Destination and Cost Center are not compatible |
2685 | + Raises an error in case the employee "Destination and Cost Center" or "Funding Pool and Cost Center" are not compatible. |
2686 | """ |
2687 | if context is None: |
2688 | context = {} |
2689 | ad_obj = self.pool.get('analytic.distribution') |
2690 | - employee_fields = ['destination_id', 'cost_center_id', 'name_resource'] |
2691 | + employee_fields = ['destination_id', 'cost_center_id', 'funding_pool_id', 'name_resource'] |
2692 | employee = self.browse(cr, uid, employee_id, fields_to_fetch=employee_fields, context=context) |
2693 | emp_dest = employee.destination_id |
2694 | emp_cc = employee.cost_center_id |
2695 | + emp_fp = employee.funding_pool_id |
2696 | if emp_dest and emp_cc: |
2697 | if not ad_obj.check_dest_cc_compatibility(cr, uid, emp_dest.id, emp_cc.id, context=context): |
2698 | raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Destination %s.') % |
2699 | (employee.name_resource, emp_cc.code or '', emp_dest.code or '')) |
2700 | + if emp_fp and emp_cc: |
2701 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, emp_fp.id, emp_cc.id, context=context): |
2702 | + raise osv.except_osv(_('Error'), _('Employee %s: the Cost Center %s is not compatible with the Funding Pool %s.') % |
2703 | + (employee.name_resource, emp_cc.code or '', emp_fp.code or '')) |
2704 | |
2705 | def create(self, cr, uid, vals, context=None): |
2706 | """ |
2707 | @@ -246,7 +251,7 @@ |
2708 | if (not context.get('from', False) or context.get('from') not in ['yaml', 'import']) and not context.get('sync_update_execution', False) and not allow_edition: |
2709 | raise osv.except_osv(_('Error'), _('You are not allowed to create a local staff! Please use Import to create local staff.')) |
2710 | employee_id = super(hr_employee, self).create(cr, uid, vals, context) |
2711 | - self._check_employe_dest_cc_compatibility(cr, uid, employee_id, context=context) |
2712 | + self._check_employee_cc_compatibility(cr, uid, employee_id, context=context) |
2713 | return employee_id |
2714 | |
2715 | def write(self, cr, uid, ids, vals, context=None): |
2716 | @@ -298,7 +303,7 @@ |
2717 | employee_id = super(hr_employee, self).write(cr, uid, emp.id, new_vals, context) |
2718 | if employee_id: |
2719 | res.append(employee_id) |
2720 | - self._check_employe_dest_cc_compatibility(cr, uid, emp.id, context=context) |
2721 | + self._check_employee_cc_compatibility(cr, uid, emp.id, context=context) |
2722 | return res |
2723 | |
2724 | def unlink(self, cr, uid, ids, context=None): |
2725 | @@ -328,7 +333,7 @@ |
2726 | |
2727 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
2728 | """ |
2729 | - Change funding pool domain in order to include MSF Private fund |
2730 | + Adapts domain for AD fields |
2731 | """ |
2732 | if not context: |
2733 | context = {} |
2734 | @@ -350,36 +355,16 @@ |
2735 | dest_field.set('domain', "[('category', '=', 'DEST'), ('type', '!=', 'view'), " |
2736 | "('dest_compatible_with_cc_ids', '=', cost_center_id)]") |
2737 | # Change FP field |
2738 | - try: |
2739 | - fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
2740 | - except ValueError: |
2741 | - fp_id = 0 |
2742 | - fp_fields = form.xpath('/' + view_type + '//field[@name="funding_pool_id"]') |
2743 | + fp_fields = form.xpath('/' + view_type + '//field[@name="funding_pool_id"]') |
2744 | for field in fp_fields: |
2745 | - field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), ('state', '=', 'open'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id) |
2746 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
2747 | + "('fp_compatible_with_cc_ids', '=', cost_center_id)]") |
2748 | view['arch'] = etree.tostring(form) |
2749 | return view |
2750 | |
2751 | def onchange_cc(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False): |
2752 | - """ |
2753 | - Update FP or CC regarding both. |
2754 | - """ |
2755 | - # Prepare some values |
2756 | - vals = {} |
2757 | - if not cost_center_id or not funding_pool_id: |
2758 | - return {} |
2759 | - if cost_center_id and funding_pool_id: |
2760 | - fp = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id) |
2761 | - try: |
2762 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
2763 | - except ValueError: |
2764 | - fp_id = 0 |
2765 | - # Exception for MSF Private Fund |
2766 | - if funding_pool_id == fp_id: |
2767 | - return {} |
2768 | - if cost_center_id not in [x.id for x in fp.cost_center_ids]: |
2769 | - vals.update({'funding_pool_id': False}) |
2770 | - return {'value': vals} |
2771 | + return self.pool.get('analytic.distribution').\ |
2772 | + onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id) |
2773 | |
2774 | def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): |
2775 | |
2776 | |
2777 | === modified file 'bin/addons/msf_homere_interface/hr_payroll.py' |
2778 | --- bin/addons/msf_homere_interface/hr_payroll.py 2019-05-14 15:12:35 +0000 |
2779 | +++ bin/addons/msf_homere_interface/hr_payroll.py 2020-11-02 12:53:09 +0000 |
2780 | @@ -103,7 +103,7 @@ |
2781 | continue |
2782 | if line.funding_pool_id and not line.destination_id: # CASE 2/ |
2783 | # D Check, except B check |
2784 | - if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id: |
2785 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, line.funding_pool_id.id, line.cost_center_id.id, context=context): |
2786 | res[line.id] = 'invalid' |
2787 | continue |
2788 | elif not line.funding_pool_id and line.destination_id: # CASE 3/ |
2789 | @@ -114,11 +114,12 @@ |
2790 | continue |
2791 | else: # CASE 4/ |
2792 | # C Check, except B |
2793 | - if (line.account_id.id, line.destination_id.id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in line.funding_pool_id.tuple_destination_account_ids if not x.disabled] and line.funding_pool_id.id != fp_id: |
2794 | + if not ad_obj.check_fp_acc_dest_compatibility(cr, uid, line.funding_pool_id.id, line.account_id.id, |
2795 | + line.destination_id.id, context=context): |
2796 | res[line.id] = 'invalid' |
2797 | continue |
2798 | # D Check, except B check |
2799 | - if line.cost_center_id.id not in [x.id for x in line.funding_pool_id.cost_center_ids] and line.funding_pool_id.id != fp_id: |
2800 | + if not ad_obj.check_fp_cc_compatibility(cr, uid, line.funding_pool_id.id, line.cost_center_id.id, context=context): |
2801 | res[line.id] = 'invalid' |
2802 | continue |
2803 | # E Check |
2804 | @@ -163,9 +164,9 @@ |
2805 | if isinstance(ids, (int, long)): |
2806 | ids = [ids] |
2807 | |
2808 | - fp = [0] |
2809 | - cc = [0] |
2810 | - dest = [0] |
2811 | + fp = [-1] |
2812 | + cc = [-1] |
2813 | + dest = [-1] |
2814 | for ana_account in self.read(cr, uid, ids, ['category']): |
2815 | if ana_account['category'] == 'OC': |
2816 | cc.append(ana_account['id']) |
2817 | @@ -174,8 +175,13 @@ |
2818 | elif ana_account['category'] == 'FUNDING': |
2819 | fp.append(ana_account['id']) |
2820 | if len(fp) > 1 or len(cc) > 1 or len(dest) > 1: |
2821 | - return self.pool.get('hr.payroll.msf').search(cr, uid, [('state', '=', 'draft'), '|', '|', ('funding_pool_id', 'in', fp), ('cost_center_id','in', cc), ('destination_id','in', dest)]) |
2822 | - |
2823 | + return self.pool.get('hr.payroll.msf').search(cr, uid, |
2824 | + [('state', '=', 'draft'), |
2825 | + '|', '|', |
2826 | + ('funding_pool_id', 'in', fp), |
2827 | + ('cost_center_id', 'in', cc), |
2828 | + ('destination_id', 'in', dest)], |
2829 | + order='NO_ORDER') |
2830 | return [] |
2831 | |
2832 | def _get_trigger_state_account(self, cr, uid, ids, context=None): |
2833 | @@ -235,7 +241,12 @@ |
2834 | store={ |
2835 | 'hr.payroll.msf': (lambda self, cr, uid, ids, c=None: ids, ['account_id', 'cost_center_id', 'funding_pool_id', 'destination_id'], 10), |
2836 | 'account.account': (_get_trigger_state_account, ['user_type_code', 'destination_ids'], 20), |
2837 | - 'account.analytic.account': (_get_trigger_state_ana, ['date', 'date_start', 'cost_center_ids', 'tuple_destination_account_ids'], 20), |
2838 | + 'account.analytic.account': (_get_trigger_state_ana, ['date', 'date_start', 'allow_all_cc', |
2839 | + 'dest_cc_ids', 'allow_all_cc_with_fp', |
2840 | + 'cost_center_ids', 'select_accounts_only', |
2841 | + 'fp_account_ids', |
2842 | + 'tuple_destination_account_ids'], |
2843 | + 20), |
2844 | 'account.destination.link': (_get_trigger_state_dest_link, ['account_id', 'destination_id'], 30), |
2845 | } |
2846 | ), |
2847 | @@ -255,7 +266,7 @@ |
2848 | |
2849 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
2850 | """ |
2851 | - Change funding pool domain in order to include MSF Private fund |
2852 | + Adapts domain for AD fields |
2853 | """ |
2854 | if not context: |
2855 | context = {} |
2856 | @@ -272,13 +283,11 @@ |
2857 | for field in fields: |
2858 | field.set('domain', "[('category', '=', 'OC'), ('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id) |
2859 | # Change FP field |
2860 | - try: |
2861 | - fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
2862 | - except ValueError: |
2863 | - fp_id = 0 |
2864 | fp_fields = form.xpath('//field[@name="funding_pool_id"]') |
2865 | for field in fp_fields: |
2866 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', '&', ('cost_center_ids', '=', cost_center_id), ('tuple_destination', '=', (account_id, destination_id)), ('id', '=', %s)]" % fp_id) |
2867 | + field.set('domain', "[('category', '=', 'FUNDING'), ('type', '!=', 'view'), " |
2868 | + "('fp_compatible_with_cc_ids', '=', cost_center_id), " |
2869 | + "('fp_compatible_with_acc_dest_ids', '=', (account_id, destination_id))]") |
2870 | # Change Destination field |
2871 | dest_fields = form.xpath('//field[@name="destination_id"]') |
2872 | for field in dest_fields: |
2873 | @@ -288,32 +297,8 @@ |
2874 | return view |
2875 | |
2876 | def onchange_destination(self, cr, uid, ids, destination_id=False, funding_pool_id=False, account_id=False): |
2877 | - """ |
2878 | - Check given funding pool with destination |
2879 | - """ |
2880 | - # Prepare some values |
2881 | - res = {} |
2882 | - # If all elements given, then search FP compatibility |
2883 | - if destination_id and funding_pool_id and account_id: |
2884 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id) |
2885 | - # Search MSF Private Fund element, because it's valid with all accounts |
2886 | - try: |
2887 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
2888 | - 'analytic_account_msf_private_funds')[1] |
2889 | - except ValueError: |
2890 | - fp_id = 0 |
2891 | - # Delete funding_pool_id if not valid with tuple "account_id/destination_id". |
2892 | - # but do an exception for MSF Private FUND analytic account |
2893 | - if (account_id, destination_id) not in [x.account_id and x.destination_id and (x.account_id.id, x.destination_id.id) for x in fp_line.tuple_destination_account_ids if not x.disabled] and funding_pool_id != fp_id: |
2894 | - res = {'value': {'funding_pool_id': False}} |
2895 | - # If no destination, do nothing |
2896 | - elif not destination_id: |
2897 | - res = {} |
2898 | - # Otherway: delete FP |
2899 | - else: |
2900 | - res = {'value': {'funding_pool_id': False}} |
2901 | - # If destination given, search if given |
2902 | - return res |
2903 | + return self.pool.get('analytic.distribution').\ |
2904 | + onchange_ad_destination(cr, uid, ids, destination_id=destination_id, funding_pool_id=funding_pool_id, account_id=account_id) |
2905 | |
2906 | def create(self, cr, uid, vals, context=None): |
2907 | """ |
2908 | |
2909 | === modified file 'bin/addons/msf_homere_interface/hr_payroll_wizard.xml' |
2910 | --- bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2020-02-07 15:20:55 +0000 |
2911 | +++ bin/addons/msf_homere_interface/hr_payroll_wizard.xml 2020-11-02 12:53:09 +0000 |
2912 | @@ -18,7 +18,9 @@ |
2913 | <field name="destination_id" context="{'search_default_active': 1, 'hide_inactive': 1}" |
2914 | domain="[('category', '=', 'DEST'), ('type', '!=', 'view'), |
2915 | ('dest_compatible_with_cc_ids', '=', cost_center_id)]"/> |
2916 | - <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
2917 | + <field name="funding_pool_id" context="{'search_default_active': 1, 'hide_inactive': 1}" |
2918 | + domain="[('category', '=', 'FUNDING'), ('type', '!=', 'view'), |
2919 | + ('fp_compatible_with_cc_ids', '=', cost_center_id)]"/> |
2920 | </group> |
2921 | <newline/> |
2922 | <field name="free1_id" context="{'search_default_active': 1, 'hide_inactive': 1}"/> |
2923 | |
2924 | === modified file 'bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py' |
2925 | --- bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py 2015-07-22 09:03:35 +0000 |
2926 | +++ bin/addons/msf_homere_interface/wizard/hr_analytic_reallocation.py 2020-11-02 12:53:09 +0000 |
2927 | @@ -40,7 +40,7 @@ |
2928 | |
2929 | def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
2930 | """ |
2931 | - Change funding pool domain in order to include MSF Private fund |
2932 | + Computes the domain for the Cost Center field |
2933 | """ |
2934 | if not context: |
2935 | context = {} |
2936 | @@ -56,41 +56,13 @@ |
2937 | fields = form.xpath('//field[@name="cost_center_id"]') |
2938 | for field in fields: |
2939 | field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('id', 'child_of', [%s])]" % oc_id) |
2940 | - # Change FP field |
2941 | - try: |
2942 | - fp_id = data_obj.get_object_reference(cr, uid, 'analytic_distribution', 'analytic_account_msf_private_funds')[1] |
2943 | - except ValueError: |
2944 | - fp_id = 0 |
2945 | - fp_fields = form.xpath('//field[@name="funding_pool_id"]') |
2946 | - # Do not use line with account_id, because of NO ACCOUNT_ID PRESENCE! |
2947 | - for field in fp_fields: |
2948 | - field.set('domain', "[('type', '!=', 'view'), ('state', '=', 'open'), ('category', '=', 'FUNDING'), '|', ('cost_center_ids', '=', cost_center_id), ('id', '=', %s)]" % fp_id) |
2949 | - # NO NEED TO CHANGE DESTINATION_ID FIELD because NO ACCOUNT_ID PRESENCE! |
2950 | # Apply changes |
2951 | view['arch'] = etree.tostring(form) |
2952 | return view |
2953 | |
2954 | def onchange_cost_center(self, cr, uid, ids, cost_center_id=False, funding_pool_id=False): |
2955 | - """ |
2956 | - Check given cost_center with funding pool |
2957 | - """ |
2958 | - # Prepare some values |
2959 | - res = {} |
2960 | - if cost_center_id and funding_pool_id: |
2961 | - fp_line = self.pool.get('account.analytic.account').browse(cr, uid, funding_pool_id) |
2962 | - # Search MSF Private Fund element, because it's valid with all accounts |
2963 | - try: |
2964 | - fp_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'analytic_distribution', |
2965 | - 'analytic_account_msf_private_funds')[1] |
2966 | - except ValueError: |
2967 | - fp_id = 0 |
2968 | - if cost_center_id not in [x.id for x in fp_line.cost_center_ids] and funding_pool_id != fp_id: |
2969 | - res = {'value': {'funding_pool_id': False}} |
2970 | - elif not cost_center_id: |
2971 | - res = {} |
2972 | - else: |
2973 | - res = {'value': {'funding_pool_id': False}} |
2974 | - return res |
2975 | + return self.pool.get('analytic.distribution').\ |
2976 | + onchange_ad_cost_center(cr, uid, ids, cost_center_id=cost_center_id, funding_pool_id=funding_pool_id) |
2977 | |
2978 | def button_validate(self, cr, uid ,ids, context=None): |
2979 | """ |
2980 | |
2981 | === modified file 'bin/addons/msf_instance/account_target_costcenter.py' |
2982 | --- bin/addons/msf_instance/account_target_costcenter.py 2019-05-14 07:28:06 +0000 |
2983 | +++ bin/addons/msf_instance/account_target_costcenter.py 2020-11-02 12:53:09 +0000 |
2984 | @@ -27,8 +27,8 @@ |
2985 | _trace = True |
2986 | |
2987 | _columns = { |
2988 | - 'instance_id': fields.many2one('msf.instance', 'Instance', required=True), |
2989 | - 'cost_center_id': fields.many2one('account.analytic.account', 'Code', domain=[('category', '=', 'OC')], required=True), |
2990 | + 'instance_id': fields.many2one('msf.instance', 'Instance', required=True, select=1), |
2991 | + 'cost_center_id': fields.many2one('account.analytic.account', 'Code', domain=[('category', '=', 'OC')], required=True, select=1), |
2992 | 'cost_center_name': fields.related('cost_center_id', 'name', string="Name", readonly=True, type="text"), |
2993 | 'is_target': fields.boolean('Is target'), |
2994 | 'is_top_cost_center': fields.boolean('Top cost centre for budget consolidation'), |
2995 | |
2996 | === modified file 'bin/addons/msf_profile/data/patches.xml' |
2997 | --- bin/addons/msf_profile/data/patches.xml 2020-10-09 13:44:47 +0000 |
2998 | +++ bin/addons/msf_profile/data/patches.xml 2020-11-02 12:53:09 +0000 |
2999 | @@ -610,5 +610,9 @@ |
3000 | <field name="method">us_2725_uf_write_date_on_products</field> |
3001 | </record> |
3002 | |
3003 | + <record id="us_7243_migrate_contract_quad" model="patch.scripts"> |
3004 | + <field name="method">us_7243_migrate_contract_quad</field> |
3005 | + </record> |
3006 | + |
3007 | </data> |
3008 | </openerp> |
3009 | |
3010 | === modified file 'bin/addons/msf_profile/i18n/fr_MF.po' |
3011 | --- bin/addons/msf_profile/i18n/fr_MF.po 2020-10-28 17:16:11 +0000 |
3012 | +++ bin/addons/msf_profile/i18n/fr_MF.po 2020-11-02 12:53:09 +0000 |
3013 | @@ -3460,7 +3460,7 @@ |
3014 | #: view:financing.contract.account.quadruplet:0 |
3015 | #: view:financing.contract.contract:0 |
3016 | msgid "Account/Destination/Funding Pool/Cost Centre" |
3017 | -msgstr "Account/Destination/Funding Pool/Cost Centre" |
3018 | +msgstr "Compte/Destination/Funding Pool/Centre de Coût" |
3019 | |
3020 | #. module: sync_client |
3021 | #: field:sync.monitor,data_push_send:0 |
3022 | @@ -8938,7 +8938,7 @@ |
3023 | #. module: financing_contract |
3024 | #: view:financing.contract.contract:0 |
3025 | msgid "Remove all couples" |
3026 | -msgstr "Remove all couples" |
3027 | +msgstr "Supprimer toutes les paires" |
3028 | |
3029 | #. modules: purchase, tender_flow, msf_outgoing, stock |
3030 | #: view:stock.picking:0 |
3031 | @@ -15941,7 +15941,7 @@ |
3032 | #. module: financing_contract |
3033 | #: view:financing.contract.contract:0 |
3034 | msgid "Remove all quads" |
3035 | -msgstr "Remove all quads" |
3036 | +msgstr "Supprimer tous les quadruplets" |
3037 | |
3038 | #. modules: tender_flow, process, account_hq_entries, account_override, procurement_cycle, return_claim, finance, sync_client, account_mcdb, procurement_request, product_asset, board, stock_override, msf_button_access_rights, analytic_distribution, msf_homere_interface, hr, consumption_calculation, register_accounting, kit, base, procurement_report, threshold_value, purchase, account, msf_outgoing, resource, stock_move_tracking, msf_partner, procurement_auto, msf_field_access_rights, sale, transport_mgmt, procurement, sourcing, msf_audittrail, stock |
3039 | #: view:account.account:0 |
3040 | @@ -16349,8 +16349,14 @@ |
3041 | |
3042 | #. module: analytic_distribution |
3043 | #: view:account.analytic.account:0 |
3044 | -msgid "Remove all" |
3045 | -msgstr "Tout supprimer" |
3046 | +msgid "Remove all accounts/destinations" |
3047 | +msgstr "Supprimer tous les comptes/destinations" |
3048 | + |
3049 | +#. modules: analytic_distribution, financing_contract |
3050 | +#: view:account.analytic.account:0 |
3051 | +#: view:financing.contract.contract:0 |
3052 | +msgid "Remove all accounts" |
3053 | +msgstr "Supprimer tous les comptes" |
3054 | |
3055 | #. module: vertical_integration |
3056 | #: code:addons/vertical_integration/report/hq_report_oca.py:176 |
3057 | @@ -28883,8 +28889,9 @@ |
3058 | msgid "This view can be used by accountants in order to quickly record entries in OpenERP. If you want to record a supplier invoice, start by recording the line of the expense account. OpenERP will propose to you automatically the Tax related to this account and the counterpart \"Account Payable\"." |
3059 | msgstr "Cette vue peut être utilisée par des comptables afin d'enregistrer rapidement des écritures comptables dans OpenERP. Si vous voulez enregistrer une facture fournisseur, commencez par enregistrer la ligne du compte de charge. OpenERP vous proposera automatiquement la taxe afférente à ce compte et la contrepartie \"Compte fournisseur\"." |
3060 | |
3061 | -#. module: financing_contract |
3062 | +#. modules: financing_contract, analytic_distribution |
3063 | #: field:financing.contract.format.line,account_destination_ids:0 |
3064 | +#: view:account.analytic.account:0 |
3065 | msgid "Accounts/Destinations" |
3066 | msgstr "Comptes/Destinations" |
3067 | |
3068 | @@ -53406,8 +53413,8 @@ |
3069 | |
3070 | #. module: financing_contract |
3071 | #: field:financing.contract.format.line,quadruplet_update:0 |
3072 | -msgid "Internal Use Only" |
3073 | -msgstr "Internal Use Only" |
3074 | +msgid "Internal Use Only (deprecated - kept to manage old sync update)" |
3075 | +msgstr "Usage Interne Uniquement (déprécié - conservé pour gérer les anciennes mises à jour via synch.)" |
3076 | |
3077 | #. modules: base_setup, unifield_setup, base |
3078 | #: selection:base.setup.company,country_id:0 |
3079 | @@ -60385,6 +60392,62 @@ |
3080 | msgid "G/L Account" |
3081 | msgstr "Compte Grand Livre" |
3082 | |
3083 | +#. modules: analytic_distribution, account, analytic_override, financing_contract |
3084 | +#: view:account.analytic.account:0 |
3085 | +#: view:account.account:0 |
3086 | +#: field:account.analytic.account,fp_account_ids:0 |
3087 | +#: view:financing.contract.contract:0 |
3088 | +#: field:financing.contract.format.line,reporting_account_ids:0 |
3089 | +#: view:financing.contract.donor:0 |
3090 | +msgid "G/L Accounts" |
3091 | +msgstr "Comptes Grand Livre" |
3092 | + |
3093 | +#. module: analytic_distribution |
3094 | +#: report:funding.pool:0 |
3095 | +msgid "G/L Accounts:" |
3096 | +msgstr "Comptes Grand Livre :" |
3097 | + |
3098 | +#. module: analytic_override |
3099 | +#: help:account.analytic.account,fp_account_ids:0 |
3100 | +msgid "G/L accounts linked to the Funding Pool" |
3101 | +msgstr "Comptes Grand Livre liés au Funding Pool" |
3102 | + |
3103 | +#. modules: analytic_override, financing_contract |
3104 | +#: field:account.analytic.account,select_accounts_only:0 |
3105 | +#: field:financing.contract.format.line,reporting_select_accounts_only:0 |
3106 | +msgid "Select Accounts Only" |
3107 | +msgstr "Sélectionner des Comptes Uniquement" |
3108 | + |
3109 | +#. module: account_override |
3110 | +#: field:account.account,selected_in_fp:0 |
3111 | +msgid "Selected in Funding Pool" |
3112 | +msgstr "Sélectionné dans le Funding Pool" |
3113 | + |
3114 | +#. module: account_override |
3115 | +#: field:account.account,selectable_in_contract:0 |
3116 | +msgid "Selectable in Contract" |
3117 | +msgstr "Sélectionnable dans le Contrat" |
3118 | + |
3119 | +#. module: account_override |
3120 | +#: field:account.account,selected_in_contract:0 |
3121 | +msgid "Selected in Contract or Donor" |
3122 | +msgstr "Sélectionné dans le Contrat ou le Bailleur" |
3123 | + |
3124 | +#. module: financing_contract |
3125 | +#: sql_constraint:financing.contract.account.quadruplet:0 |
3126 | +msgid "not unique!" |
3127 | +msgstr "non unique !" |
3128 | + |
3129 | +#. module: financing_contract |
3130 | +#: field:financing.contract.format.line,quadruplet_sync_list:0 |
3131 | +msgid "Used to sync quad" |
3132 | +msgstr "Utilisé pour synch. les quadruplets" |
3133 | + |
3134 | +#. module: financing_contract |
3135 | +#: field:financing.contract.contract,quad_gen_date:0 |
3136 | +msgid "Date of last generation of quad" |
3137 | +msgstr "Date de la dernière génération des quadruplets" |
3138 | + |
3139 | #. module: purchase_override |
3140 | #: view:purchase.order.cancel.wizard:0 |
3141 | msgid "Please confirm that you want to cancel all non-confirmed lines of this PO" |
3142 | @@ -61535,10 +61598,10 @@ |
3143 | msgstr "Le total hors-taxe" |
3144 | |
3145 | #. module: register_accounting |
3146 | -#: code:addons/register_accounting/wizard/wizard_cash_return.py:800 |
3147 | +#: code:addons/register_accounting/wizard/wizard_cash_return.py:824 |
3148 | #, python-format |
3149 | -msgid "All advance lines with account that depends on analytic distribution must have an allocation." |
3150 | -msgstr "All advance lines with account that depends on analytic distribution must have an allocation." |
3151 | +msgid "All advance lines with an account depending on an analytic distribution must have a valid allocation." |
3152 | +msgstr "Toutes les lignes d'avance dont le compte dépend d'une distribution analytique doivent avoir une allocation valide." |
3153 | |
3154 | #. module: stock |
3155 | #: field:product.category,property_stock_account_output_categ:0 |
3156 | @@ -69077,7 +69140,6 @@ |
3157 | msgstr "Canceled End" |
3158 | |
3159 | #. modules: financing_contract, analytic_distribution |
3160 | -#: field:financing.contract.account.quadruplet,account_destination_id:0 |
3161 | #: field:account.analytic.account,tuple_destination_account_ids:0 |
3162 | #: view:account.destination.link:0 |
3163 | #: view:account.destination.link:0 |
3164 | @@ -94241,6 +94303,7 @@ |
3165 | #: view:account.destination.summary:0 |
3166 | #: code:addons/stock_override/report/report_stock_move.py:759 |
3167 | #: report:addons/account/report/invoice_excel_export.mako:75 |
3168 | +#: field:financing.contract.account.quadruplet,account_destination_id:0 |
3169 | #, python-format |
3170 | msgid "Destination" |
3171 | msgstr "Destination" |
3172 | @@ -104790,7 +104853,20 @@ |
3173 | msgstr "Filtre non mis en oeuvre sur les Destinations." |
3174 | |
3175 | #. module: analytic_override |
3176 | +#: code:addons/analytic_override/analytic_account.py:307 |
3177 | +#, python-format |
3178 | +msgid "Filter not implemented on Funding Pools." |
3179 | +msgstr "Filtre non mis en oeuvre sur les Funding Pools." |
3180 | + |
3181 | +#. module: analytic_override |
3182 | +#: code:addons/analytic_override/analytic_account.py:312 |
3183 | +#, python-format |
3184 | +msgid "Filter only compatible with a normal-type Cost Center." |
3185 | +msgstr "Filtre compatible uniquement avec un Centre de Coût de type normal." |
3186 | + |
3187 | +#. module: analytic_override |
3188 | #: field:account.analytic.account,allow_all_cc:0 |
3189 | +#: field:account.analytic.account,allow_all_cc_with_fp:0 |
3190 | msgid "Allow all Cost Centers" |
3191 | msgstr "Autoriser tous les Centres de Coût" |
3192 | |
3193 | @@ -104806,17 +104882,39 @@ |
3194 | msgstr "Destinations compatibles avec le Centre de Coût" |
3195 | |
3196 | #. module: analytic_override |
3197 | +#: field:account.analytic.account,fp_compatible_with_cc_ids:0 |
3198 | +msgid "Funding Pools compatible with the Cost Center" |
3199 | +msgstr "Funding Pools compatibles avec le Centre de Coût" |
3200 | + |
3201 | +#. module: analytic_override |
3202 | +#: field:account.analytic.account,fp_compatible_with_acc_dest_ids:0 |
3203 | +msgid "Funding Pools compatible with the Account/Destination combination" |
3204 | +msgstr "Funding Pools compatibles avec la combinaison Compte/Destination" |
3205 | + |
3206 | +#. module: analytic_override |
3207 | #: code:addons/analytic_override/analytic_account.py:347 |
3208 | #, python-format |
3209 | msgid "Please remove the Cost Centers linked to the Destination before ticking this box." |
3210 | msgstr "Veuillez supprimer les Centres de Coût liés à la Destination avant de cocher cette case." |
3211 | |
3212 | +#. module: analytic_override |
3213 | +#: code:addons/analytic_override/analytic_account.py:347 |
3214 | +#, python-format |
3215 | +msgid "Please remove the Cost Centers linked to the Funding Pool before ticking this box." |
3216 | +msgstr "Veuillez supprimer les Centres de Coût liés au Funding Pool avant de cocher cette case." |
3217 | + |
3218 | #. module: account_corrections |
3219 | #: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:246 |
3220 | #, python-format |
3221 | msgid "The Cost Center %s is not compatible with the Destination %s." |
3222 | msgstr "Le Centre de Coût %s n'est pas compatible avec la Destination %s." |
3223 | |
3224 | +#. module: account_corrections |
3225 | +#: code:addons/account_corrections/wizard/analytic_distribution_wizard.py:274 |
3226 | +#, python-format |
3227 | +msgid "The Cost Center %s is not compatible with the Funding Pool %s." |
3228 | +msgstr "Le Centre de Coût %s n'est pas compatible avec le Funding Pool %s." |
3229 | + |
3230 | #. module: msf_doc_import |
3231 | #: code:addons/msf_doc_import/account.py:490 |
3232 | #, python-format |
3233 | @@ -105166,6 +105264,12 @@ |
3234 | msgid "Employee %s: the Cost Center %s is not compatible with the Destination %s." |
3235 | msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec la Destination %s." |
3236 | |
3237 | +#. module: msf_homere_interface |
3238 | +#: code:addons/msf_homere_interface/hr.py:229 |
3239 | +#, python-format |
3240 | +msgid "Employee %s: the Cost Center %s is not compatible with the Funding Pool %s." |
3241 | +msgstr "Employé %s : le Centre de Coût %s n'est pas compatible avec le Funding Pool %s." |
3242 | + |
3243 | #. module: msf_supply_doc_export |
3244 | #: report:po.follow.up_rml:0 |
3245 | msgid "Status:" |
3246 | @@ -109907,6 +110011,11 @@ |
3247 | msgid "Missions where the CC is added to" |
3248 | msgstr "Missions dans lesquelles le CC est ajouté" |
3249 | |
3250 | +#. module: analytic_override |
3251 | +#: field:account.analytic.account,cc_instance_ids:0 |
3252 | +msgid "Instances where the CC is added to" |
3253 | +msgstr "Instances dans lesquelles le CC est ajouté" |
3254 | + |
3255 | #. module: sync_client |
3256 | #: field:sync_client.survey.user,nb_displayed:0 |
3257 | msgid "# Display" |
3258 | |
3259 | === modified file 'bin/addons/msf_profile/msf_profile.py' |
3260 | --- bin/addons/msf_profile/msf_profile.py 2020-10-09 13:44:47 +0000 |
3261 | +++ bin/addons/msf_profile/msf_profile.py 2020-11-02 12:53:09 +0000 |
3262 | @@ -53,6 +53,25 @@ |
3263 | } |
3264 | |
3265 | # UF19.0 |
3266 | + def us_7243_migrate_contract_quad(self, cr, uid, *a, **b): |
3267 | + quad_obj = self.pool.get('financing.contract.account.quadruplet') |
3268 | + if not cr.table_exists('financing_contract_actual_account_quadruplets_old'): |
3269 | + cr.execute("create table financing_contract_actual_account_quadruplets_old as (select * from financing_contract_actual_account_quadruplets)") |
3270 | + already_migrated = {} |
3271 | + cr.execute('truncate financing_contract_actual_account_quadruplets') |
3272 | + cr.execute('select actual_line_id, account_quadruplet_id from financing_contract_actual_account_quadruplets_old') |
3273 | + nb_mig = 0 |
3274 | + for x in cr.fetchall(): |
3275 | + if x[1] not in already_migrated: |
3276 | + new_id = quad_obj.migrate_old_quad(cr, uid, [x[1]]) |
3277 | + already_migrated[x[1]] = new_id and new_id[0] |
3278 | + if already_migrated.get(x[1]): |
3279 | + nb_mig += 1 |
3280 | + cr.execute('insert into financing_contract_actual_account_quadruplets (actual_line_id, account_quadruplet_id) values (%s, %s)', (x[0], already_migrated[x[1]])) |
3281 | + |
3282 | + self._logger.warn('%d quad migrated' % (nb_mig,)) |
3283 | + return True |
3284 | + |
3285 | def us_2725_uf_write_date_on_products(self, cr, uid, *a, **b): |
3286 | ''' |
3287 | Set the uf_write_date of products which don't have one to the date of creation |
3288 | @@ -3910,10 +3929,7 @@ |
3289 | except Exception as e: |
3290 | err_msg = 'Error with the patch scripts %s.%s :: %s' % (ps['model'], ps['method'], e) |
3291 | self._logger.error(err_msg) |
3292 | - raise osv.except_osv( |
3293 | - 'Error', |
3294 | - err_msg, |
3295 | - ) |
3296 | + raise |
3297 | |
3298 | patch_scripts() |
3299 | |
3300 | |
3301 | === modified file 'bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv' |
3302 | --- bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-09-01 08:22:26 +0000 |
3303 | +++ bin/addons/msf_sync_data_server/data/sync_server.sync_rule.csv 2020-11-02 12:53:09 +0000 |
3304 | @@ -26,7 +26,7 @@ |
3305 | msf_sync_data_server.analytical_journal_project,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional,"[('instance_id.level', '=', 'project'),('code','!=','ENGI')]","['code', 'name', 'type','instance_id/id']",MISSION,account.analytic.journal,,Analytical Journal (Project),Valid,,121 |
3306 | msf_sync_data_server.analytical_journal,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,[],"['code', 'name', 'type','instance_id/id']",OC,account.analytic.journal,instance_id,Analytical Journal,Valid,,122 |
3307 | msf_sync_data_server.link_accounts_destination,TRUE,TRUE,TRUE,TRUE,bidirectional,Down,[],"['account_id/id', 'destination_id/id', 'disabled']",OC,account.destination.link,,Link accounts Destination,Valid,,123 |
3308 | -msf_sync_data_server.funding_pool_to_coordo,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,"[('category' , '=' , 'FUNDING')]","['category', 'code', 'cost_center_ids/id', 'date', 'date_start', 'description', 'instance_id/id', 'name', 'tuple_destination_account_ids/id', 'type']",HQ + MISSION,account.analytic.account,instance_id,Funding Pool linked to a special coordo,Valid,,124 |
3309 | +msf_sync_data_server.funding_pool_to_coordo,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,"[('category' , '=' , 'FUNDING')]","['category', 'code', 'cost_center_ids/id', 'date', 'date_start', 'description', 'instance_id/id', 'name', 'tuple_destination_account_ids/id', 'type', 'allow_all_cc_with_fp', 'select_accounts_only', 'fp_account_ids/id']",HQ + MISSION,account.analytic.account,instance_id,Funding Pool linked to a special coordo,Valid,,124 |
3310 | msf_sync_data_server.fp_tree0,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('category', 'in', ['FREE1', 'FREE2', 'DEST']),('parent_id', '!=', '')]","['parent_id/id', 'instance_id/id']",OC,account.analytic.account,instance_id,"DestF1F2 tree (only F1, F2 and Dest)",Valid,,126 |
3311 | msf_sync_data_server.fp_tree,FALSE,TRUE,TRUE,FALSE,bidirectional,Bidirectional,"[('category', 'in', ['FREE1', 'FREE2', 'DEST']),('parent_id', '!=', '')]",['parent_id/id'],MISSION,account.analytic.account,,"Analytic account tree (only F1, F2 and Dest) – Coordo/Projects only",Valid,,127 |
3312 | msf_sync_data_server.currency_rate,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,"[('rate' , '!=' , ''),('currency_id', '!=', ''), ('currency_id', 'in', ('res.currency', 'id', [('active', 'in', ['t', 'f']), ('currency_table_id', '=', '')]))]","['currency_id/id', 'name', 'rate']",OC,res.currency.rate,,Currency Rate,Valid,,128 |
3313 | @@ -103,12 +103,12 @@ |
3314 | msf_sync_data_server.mission_stock_report_line_OC,TRUE,TRUE,FALSE,FALSE,bidirectional,Up,"[('mission_report_id.full_view', '=', False), ('mission_report_id.local_report', '=', True), ('international_status','in',['ITC', 'UniData', 'ESC', 'HQ'])]","['central_qty', 'central_val', 'cross_qty', 'cross_val', 'cu_qty', 'cu_val', 'in_pipe_coor_qty', 'in_pipe_coor_val', 'in_pipe_qty', 'in_pipe_val', 'internal_qty', 'internal_val', 'mission_report_id/id', 'product_id/id', 'secondary_qty', 'secondary_val', 'stock_qty', 'stock_val', 'xmlid_code', 'product_state', 'product_active', 'state_ud', 'international_status_code', 'product_amc', 'product_consumption']",OC,stock.mission.report.line,,Mission Stock Report Line OC,Valid,,442 |
3315 | msf_sync_data_server.financing_contract_formats_fc,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,[],"['cost_center_ids/id', 'eligibility_from_date', 'eligibility_to_date', 'format_name', 'overhead_percentage', 'overhead_type', 'reporting_type', 'hidden_instance_id/id']",HQ + MISSION,financing.contract.format,hidden_instance_id,Financing Contract Formats FC,Valid,,450 |
3316 | msf_sync_data_server.financing_contract_formats,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional,"[('hidden_instance_id','=',False)]","['hidden_instance_id/id','cost_center_ids/id', 'eligibility_from_date', 'eligibility_to_date', 'format_name', 'overhead_percentage', 'overhead_type', 'reporting_type']",HQ + MISSION,financing.contract.format,,Financing Contract Formats,Valid,,451 |
3317 | -msf_sync_data_server.financing_contract_format_lines,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional,"[('instance_id', '=', False)]","['account_destination_ids/id', 'instance_id/id','allocated_budget_value', 'allocated_real_value', 'code', 'format_id/id', 'line_type', 'name', 'overhead_percentage', 'overhead_type', 'project_budget_value', 'project_real_value', 'is_quadruplet','quadruplet_update']",HQ + MISSION,financing.contract.format.line,,Financing Contract Format Lines,Valid,,452 |
3318 | +msf_sync_data_server.financing_contract_format_lines,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional,"[('instance_id', '=', False)]","['account_destination_ids/id', 'instance_id/id','allocated_budget_value', 'allocated_real_value', 'code', 'format_id/id', 'line_type', 'name', 'overhead_percentage', 'overhead_type', 'project_budget_value', 'project_real_value', 'is_quadruplet', 'quadruplet_sync_list', 'reporting_select_accounts_only', 'reporting_account_ids/id']",HQ + MISSION,financing.contract.format.line,,Financing Contract Format Lines,Valid,,452 |
3319 | msf_sync_data_server.financing_contract_format_lines_tree,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional,"[('parent_id', '!=', ''), ('instance_id', '=', False)]",['parent_id/id'],HQ + MISSION,financing.contract.format.line,,Financing Contract Format Lines Tree,Valid,,453 |
3320 | msf_sync_data_server.donors,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional,[],"['active', 'code', 'format_id/id', 'name', 'reporting_currency/id']",HQ + MISSION,financing.contract.donor,,Donors,Valid,,454 |
3321 | msf_sync_data_server.financing_contract,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional-Private,[],"['code', 'currency_table_id/id', 'donor_grant_reference', 'donor_id/id', 'eligibility_from_date', 'eligibility_to_date', 'format_id/id', 'grant_amount', 'hard_closed_date', 'hq_grant_reference', 'name', 'notes', 'open_date', 'reporting_currency/id', 'soft_closed_date', 'state', 'instance_id/id']",HQ + MISSION,financing.contract.contract,instance_id,Financing Contract,Valid,,455 |
3322 | msf_sync_data_server.financing_contract_fp_line,TRUE,TRUE,TRUE,TRUE,bidirectional,Bidirectional-Private,[],"['contract_id/id', 'funded', 'funding_pool_id/id', 'total_project', 'instance_id/id']",HQ + MISSION,financing.contract.funding.pool.line,instance_id,Financing Contract FP Line,Valid,,456 |
3323 | -msf_sync_data_server.financing_contract_format_lines_fc,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,[],"['account_destination_ids/id', 'allocated_budget_value', 'allocated_real_value', 'code', 'format_id/id', 'instance_id/id', 'line_type', 'name', 'overhead_percentage', 'overhead_type', 'project_budget_value', 'project_real_value', 'is_quadruplet','quadruplet_update']",HQ + MISSION,financing.contract.format.line,instance_id,Financing Contract Format Lines FC,Valid,,457 |
3324 | +msf_sync_data_server.financing_contract_format_lines_fc,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,[],"['account_destination_ids/id', 'allocated_budget_value', 'allocated_real_value', 'code', 'format_id/id', 'instance_id/id', 'line_type', 'name', 'overhead_percentage', 'overhead_type', 'project_budget_value', 'project_real_value', 'is_quadruplet', 'quadruplet_sync_list', 'reporting_select_accounts_only', 'reporting_account_ids/id']",HQ + MISSION,financing.contract.format.line,instance_id,Financing Contract Format Lines FC,Valid,,457 |
3325 | msf_sync_data_server.financing_contract_format_lines_tree_fc,TRUE,TRUE,FALSE,TRUE,bidirectional,Bidirectional-Private,"[('parent_id','!=','')]","['parent_id/id', 'instance_id/id']",HQ + MISSION,financing.contract.format.line,instance_id,Financing Contract Format Lines Tree FC,Valid,,458 |
3326 | msf_sync_data_server.country,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],"['code', 'name']",OC,res.country,,Country,Valid,,500 |
3327 | msf_sync_data_server.state,TRUE,TRUE,FALSE,TRUE,bidirectional,Down,[],"['code', 'country_id/id', 'name']",OC,res.country.state,,State,Valid,,501 |
3328 | |
3329 | === modified file 'bin/addons/register_accounting/wizard/wizard_cash_return.py' |
3330 | --- bin/addons/register_accounting/wizard/wizard_cash_return.py 2020-02-04 13:20:04 +0000 |
3331 | +++ bin/addons/register_accounting/wizard/wizard_cash_return.py 2020-11-02 12:53:09 +0000 |
3332 | @@ -820,7 +820,9 @@ |
3333 | # check if any line with an analytic-a-holic account missing the distribution_id value |
3334 | for st_line in wizard.advance_line_ids: |
3335 | if st_line.account_id.is_analytic_addicted and st_line.analytic_distribution_state != 'valid': |
3336 | - raise osv.except_osv(_('Warning'), _('All advance lines with account that depends on analytic distribution must have an allocation.')) |
3337 | + raise osv.except_osv(_('Warning'), |
3338 | + _('All advance lines with an account depending on an analytic distribution ' |
3339 | + 'must have a valid allocation.')) |
3340 | |
3341 | # Do computation of total_amount of advance return lines |
3342 | self.compute_total_amount(cr, uid, ids, context=context) |