Merge lp:~elbati/account-consolidation/adding_account_parallel_currency_7 into lp:~account-core-editors/account-consolidation/7.0
- adding_account_parallel_currency_7
- Merge into 7.0
Status: | Merged |
---|---|
Merged at revision: | 18 |
Proposed branch: | lp:~elbati/account-consolidation/adding_account_parallel_currency_7 |
Merge into: | lp:~account-core-editors/account-consolidation/7.0 |
Diff against target: |
1601 lines (+1527/-0) 14 files modified
account_parallel_currency/AUTHORS.txt (+1/-0) account_parallel_currency/__init__.py (+23/-0) account_parallel_currency/__openerp__.py (+56/-0) account_parallel_currency/account.py (+505/-0) account_parallel_currency/account_demo.xml (+504/-0) account_parallel_currency/account_view.xml (+64/-0) account_parallel_currency/company_view.xml (+16/-0) account_parallel_currency/res_company.py (+33/-0) account_parallel_currency/security/security.xml (+11/-0) account_parallel_currency/test/customer_invoice.yml (+143/-0) account_parallel_currency/test/mapping_parallel_accounts.yml (+25/-0) account_parallel_currency/wizard/__init__.py (+22/-0) account_parallel_currency/wizard/do_mapping.py (+92/-0) account_parallel_currency/wizard/do_mapping.xml (+32/-0) |
To merge this branch: | bzr merge lp:~elbati/account-consolidation/adding_account_parallel_currency_7 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Maxime Chambreuil (http://www.savoirfairelinux.com) | code review | Approve | |
Stefan Rijnhart (Opener) | Approve | ||
Review via email:
|
Commit message
Description of the change
This module handles parallel accounting entries based on different currencies.
It is useful for companies who have to manage accounting with more than one currency at the same time. For instance, companies who have to produce balances on different currencies.
In order to use the module, you have to define one company for each parallel chart of accounts. Then you have to map parallel accounts and parallel journals through the related forms.
A 'Parallel Account Mapping' wizard is provided. It is intended to be run when the same chart of account is used for the parallel companies. It allows to automatically map the 'master' account to 'parallel' accounts, based on account code.
When posting new journal entries, the system checks the configured parallel accounts and automatically generates the parallel entries.
For each user, it is possible to configure a 'parallel user' (that should be associated to a dummy parent company), used to carry out the parallel registrations. This allows to keep the companies separate, so that users of the master company don't see secondary company data (e.g. currencies and journals) but the system uses his parallel user in order to perform the parallel registrations

Frederic Clementi - Camptocamp (frederic-clementi) wrote : | # |

Nhomar - Vauxoo (nhomar) wrote : | # |
Hi Lorenzo,
We have same question, we frequently use Consolidated accounts to build consolidation reports, some specific or generical reason or accounting Good practice to support the development?
For sure i had some times this idea in my mind because when you have some customers you think in several options to solve the problem, but i never developed because Consolidated acounts works for me.
Regards, and Kudos for you contribution

Lorenzo Battistini (elbati) wrote : | # |
On 03/18/2013 01:15 PM, Frederic Clementi - Camptocamp.com wrote:
> Hi Lorenzzo,
>
> Sorry for the question : this looks very interesting but I cannot find any cases/example in real situation where this could be a must have...
> I mean, why would you duplicate the entire accounting books of the company instead of simply do a report or a view which convert account balances ?
> Any legal reason ? unless a segregation of duties issue maybe ?
Hi Frederic and Nhomar,
simply converting balances can be insufficient if you have to consider
historical rates.
Just found a recent publication
<http://
(Italian) about Swiss accounting law.
The interesting part:
"To facilitate the international companies, moreover, the accounts can
be held in one of the national languages (German, French, Italian)
and, in addition to the CHF, other currencies are accepted - crucial for
the company activities - such as Euro or USD, with an indication,
however, of the values in CHF too"
- 22. By Lorenzo Battistini
-
[FIX] using _logger.warning
- 23. By Lorenzo Battistini
-
[fix] string formatting
- 24. By Lorenzo Battistini
-
[add] sync_parallel_
accounts method: automatically write/create parallel accounts when writing account - 25. By Lorenzo Battistini
-
[ADD] tests
- 26. By Lorenzo Battistini
-
[MERGE] tax codes handling
- 27. By Lorenzo Battistini
-
[add] Parallel accounts summary
- 28. By Lorenzo Battistini
-
[ref] create_
parallel_ accounts - 29. By Lorenzo Battistini
-
[fix] useless code
- 30. By Lorenzo Battistini
-
[fix] allow mapping for normal users
- 31. By Lorenzo Battistini
-
[add] runtime tax codes sync
- 32. By Lorenzo Battistini
-
[add] comment
- 33. By Lorenzo Battistini
-
[add] parallel accounts at creation
- 34. By Lorenzo Battistini
-
[imp]messages
- 35. By Lorenzo Battistini
-
[imp] error message
- 36. By Lorenzo Battistini
-
[imp] error message
- 37. By Lorenzo Battistini
-
[fix] impossible to change parent account
- 38. By Lorenzo Battistini
-
[FIX] searching parallel period by code

Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
This has been the oldest proposal on the community projects for some time now, maybe because of the relatively rare business case, and the large amount of code impacting the core of the accounting system. So I'll have ago. I think I can understand the particular business case of this module, and the code does look good. Tests are provided too, so I think this should be fine.
It would be nice to know whether this module has already been used in production by now?
One nit:
l.174..185 + l.535..536 is a bit tiresome. Can you make this a list comprehension or at least iterate over a list of fields?

Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
Forgot to assign review status, make that 'needs information'

Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) wrote : | # |
Thanks Lorenzo.
Can you please run autopep8 on your module ?

Lorenzo Battistini (elbati) wrote : | # |
On 09/08/2013 08:17 AM, Stefan Rijnhart (Therp) wrote:
> It would be nice to know whether this module has already been used in production by now?
Hello Stefan.
The module has been used in production from this winter.
Unfortunately, we could not verify deeply the data accuracy yet. We will
probably do it at the end the year.
- 39. By Lorenzo Battistini
-
[IMP] some PEP8

Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
Thanks for the info, and for updating the code style a little bit.

Lorenzo Battistini (elbati) wrote : | # |
On 09/29/2013 08:30 PM, Stefan Rijnhart (Therp) wrote:
> and for updating the code style a little bit
I had no time to complete it for now..

Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) : | # |

Stefan Rijnhart (Opener) (stefan-opener) wrote : | # |
> I had no time to complete it for now..
That's quite alright by me for now. I think this proposal has been waiting long enough.
Preview Diff
1 | === added directory 'account_parallel_currency' |
2 | === added file 'account_parallel_currency/AUTHORS.txt' |
3 | --- account_parallel_currency/AUTHORS.txt 1970-01-01 00:00:00 +0000 |
4 | +++ account_parallel_currency/AUTHORS.txt 2013-09-27 07:51:38 +0000 |
5 | @@ -0,0 +1,1 @@ |
6 | +Lorenzo Battistini <lorenzo.battistini@agilebg.com> |
7 | |
8 | === added file 'account_parallel_currency/__init__.py' |
9 | --- account_parallel_currency/__init__.py 1970-01-01 00:00:00 +0000 |
10 | +++ account_parallel_currency/__init__.py 2013-09-27 07:51:38 +0000 |
11 | @@ -0,0 +1,23 @@ |
12 | +# -*- coding: utf-8 -*- |
13 | +############################################################################## |
14 | +# |
15 | +# Copyright (C) 2012 Agile Business Group sagl (<http://www.agilebg.com>) |
16 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
17 | +# |
18 | +# This program is free software: you can redistribute it and/or modify |
19 | +# it under the terms of the GNU Affero General Public License as published |
20 | +# by the Free Software Foundation, either version 3 of the License, or |
21 | +# (at your option) any later version. |
22 | +# |
23 | +# This program is distributed in the hope that it will be useful, |
24 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
26 | +# GNU General Public License for more details. |
27 | +# |
28 | +# You should have received a copy of the GNU Affero General Public License |
29 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
30 | +# |
31 | +############################################################################## |
32 | +import account |
33 | +import res_company |
34 | +import wizard |
35 | |
36 | === added file 'account_parallel_currency/__openerp__.py' |
37 | --- account_parallel_currency/__openerp__.py 1970-01-01 00:00:00 +0000 |
38 | +++ account_parallel_currency/__openerp__.py 2013-09-27 07:51:38 +0000 |
39 | @@ -0,0 +1,56 @@ |
40 | +# -*- coding: utf-8 -*- |
41 | +############################################################################## |
42 | +# |
43 | +# Copyright (C) 2012-2013 Agile Business Group sagl |
44 | +# (<http://www.agilebg.com>) |
45 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
46 | +# |
47 | +# This program is free software: you can redistribute it and/or modify |
48 | +# it under the terms of the GNU Affero General Public License as published |
49 | +# by the Free Software Foundation, either version 3 of the License, or |
50 | +# (at your option) any later version. |
51 | +# |
52 | +# This program is distributed in the hope that it will be useful, |
53 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
54 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
55 | +# GNU Affero General Public License for more details. |
56 | +# |
57 | +# You should have received a copy of the GNU Affero General Public License |
58 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
59 | +# |
60 | +############################################################################## |
61 | +{ |
62 | + 'name': "Account Parallel Currency", |
63 | + 'version': '0.2', |
64 | + 'category': 'Generic Modules/Accounting', |
65 | + 'description': """ |
66 | +This module handles parallel accounting entries based on different currencies. |
67 | +It is useful for companies who have to manage accounting with more than one currency at the same time. For instance, companies who have to produce balances on different currencies. |
68 | + |
69 | +In order to use the module, you have to define one company for each parallel chart of accounts. Then you have to map parallel accounts and parallel journals through the related forms. |
70 | + |
71 | +A 'Parallel Account Mapping' wizard is provided. It is intended to be run when the same chart of account is used for the parallel companies. It allows to automatically map the 'master' account to 'parallel' accounts, based on account code. |
72 | + |
73 | +When posting new journal entries, the system checks the configured parallel accounts and automatically generates the parallel entries. |
74 | +This is achieved keeping the companies separate, so that users of the master company don't see secondary company data (e.g. currencies and journals) but the system uses the super user in order to perform the parallel registrations. |
75 | +""", |
76 | + 'author': 'Agile Business Group', |
77 | + 'website': 'http://www.agilebg.com', |
78 | + 'license': 'AGPL-3', |
79 | + "depends" : ['account'], |
80 | + "data" : [ |
81 | + 'account_view.xml', |
82 | + 'company_view.xml', |
83 | + 'wizard/do_mapping.xml', |
84 | + 'security/security.xml', |
85 | + ], |
86 | + "demo" : [ |
87 | + 'account_demo.xml', |
88 | + ], |
89 | + 'test': [ |
90 | + 'test/mapping_parallel_accounts.yml', |
91 | + 'test/customer_invoice.yml', |
92 | + ], |
93 | + "active": False, |
94 | + "installable": True |
95 | +} |
96 | |
97 | === added file 'account_parallel_currency/account.py' |
98 | --- account_parallel_currency/account.py 1970-01-01 00:00:00 +0000 |
99 | +++ account_parallel_currency/account.py 2013-09-27 07:51:38 +0000 |
100 | @@ -0,0 +1,505 @@ |
101 | +# -*- coding: utf-8 -*- |
102 | +############################################################################## |
103 | +# |
104 | +# Copyright (C) 2012-2013 Agile Business Group sagl |
105 | +# (<http://www.agilebg.com>) |
106 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
107 | +# |
108 | +# This program is free software: you can redistribute it and/or modify |
109 | +# it under the terms of the GNU Affero General Public License as published |
110 | +# by the Free Software Foundation, either version 3 of the License, or |
111 | +# (at your option) any later version. |
112 | +# |
113 | +# This program is distributed in the hope that it will be useful, |
114 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
115 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
116 | +# GNU Affero General Public License for more details. |
117 | +# |
118 | +# You should have received a copy of the GNU Affero General Public License |
119 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
120 | +# |
121 | +############################################################################## |
122 | + |
123 | +from openerp.osv import fields, orm |
124 | +from tools.translate import _ |
125 | +import logging |
126 | +from openerp import SUPERUSER_ID |
127 | + |
128 | +_logger = logging.getLogger(__name__) |
129 | + |
130 | + |
131 | +class account_account(orm.Model): |
132 | + _inherit = "account.account" |
133 | + |
134 | + def _get_parallel_accounts_summary(self, cr, uid, ids, field_name, arg, context=None): |
135 | + res = {} |
136 | + for account in self.browse(cr, SUPERUSER_ID, ids, context): |
137 | + text = 'Configured parallel accounts:\n' |
138 | + text2 = '' |
139 | + for parallel_account in account.parallel_account_ids: |
140 | + text2 += _('Account code: %s. Company: %s\n') % ( |
141 | + parallel_account.code, parallel_account.company_id.name) |
142 | + if text2: |
143 | + res[account.id] = text + text2 |
144 | + else: |
145 | + res[account.id] = _("No parallel accounts found") |
146 | + return res |
147 | + |
148 | + _columns = { |
149 | + 'parallel_account_ids': fields.many2many('account.account', |
150 | + 'parallel_account_rel', 'parent_id', |
151 | + 'child_id', 'Parallel Currency Accounts', |
152 | + Help="""Set here the accounts you want to automatically move when |
153 | + registering entries in this account"""), |
154 | + 'master_parallel_account_ids': fields.many2many('account.account', |
155 | + 'parallel_account_rel', 'child_id', |
156 | + 'parent_id', 'Master Parallel Currency Accounts', |
157 | + Help="You can see here the accounts that automatically move this account", |
158 | + readonly=True), |
159 | + 'parallel_accounts_summary': fields.function( |
160 | + _get_parallel_accounts_summary, type='text', |
161 | + string='Parallel accounts summary'), |
162 | + } |
163 | + |
164 | + def _search_parallel_account(self, cr, uid, account_code, parallel_company, |
165 | + context=None): |
166 | + parallel_acc_ids = self.search(cr, uid, [ |
167 | + ('company_id', '=', parallel_company.id), |
168 | + ('code', '=', account_code), |
169 | + ], context=context) |
170 | + if len(parallel_acc_ids) > 1: |
171 | + raise orm.except_orm(_('Error'), _('Too many accounts %s for company %s') |
172 | + % (account_code, parallel_company.name)) |
173 | + return parallel_acc_ids and parallel_acc_ids[0] or False |
174 | + |
175 | + def _build_account_vals(self, cr, uid, account_vals, parallel_company, context=None): |
176 | + # build only fields I need |
177 | + vals = {} |
178 | + if 'name' in account_vals: |
179 | + vals['name'] = account_vals['name'] |
180 | + if 'code' in account_vals: |
181 | + vals['code'] = account_vals['code'] |
182 | + if 'type' in account_vals: |
183 | + vals['type'] = account_vals['type'] |
184 | + if 'user_type' in account_vals: |
185 | + vals['user_type'] = account_vals['user_type'] |
186 | + if 'active' in account_vals: |
187 | + vals['active'] = account_vals['active'] |
188 | + if 'centralized' in account_vals: |
189 | + vals['centralized'] = account_vals['centralized'] |
190 | + if 'parent_id' in account_vals: |
191 | + parent_account = self.browse(cr, uid, account_vals['parent_id'], context) |
192 | + parent_parallel_acc_id = self._search_parallel_account( |
193 | + cr, uid, parent_account.code, parallel_company, context=context) |
194 | + if not parent_parallel_acc_id: |
195 | + raise orm.except_orm(_('Error'), |
196 | + _('No parent account %s found in company %s') % |
197 | + (parent_account.code, parallel_company.name)) |
198 | + vals['parent_id'] = parent_parallel_acc_id |
199 | + return vals |
200 | + |
201 | + def create_parallel_accounts(self, cr, uid, ids, context=None): |
202 | + for account in self.browse(cr, SUPERUSER_ID, ids, context): |
203 | + for parallel_company in account.company_id.parallel_company_ids: |
204 | + parent_parallel_acc_id = self._search_parallel_account( |
205 | + cr, SUPERUSER_ID, account.parent_id.code, parallel_company, |
206 | + context=context) |
207 | + new_id = self.create(cr, SUPERUSER_ID, { |
208 | + 'company_id': parallel_company.id, |
209 | + 'parent_id': parent_parallel_acc_id, |
210 | + 'name': account.name, |
211 | + 'code': account.code, |
212 | + 'type': account.type, |
213 | + 'user_type': account.user_type and account.user_type.id or False, |
214 | + 'active': account.active, |
215 | + 'centralized': account.centralized, |
216 | + }) |
217 | + cr.execute("insert into parallel_account_rel(parent_id,child_id) values (%d,%d)" |
218 | + % (account.id, new_id)) |
219 | + return True |
220 | + |
221 | + ''' |
222 | + def sync_parallel_accounts(self, cr, uid, ids, vals={}, context=None): |
223 | + for account in self.browse(cr, uid, ids, context): |
224 | + new_parallel_acc_ids = [] |
225 | + company_id = vals.get('company_id') or account.company_id.id |
226 | + code = vals.get('code') or account.code |
227 | + company = self.pool.get('res.company').browse(cr, uid, company_id, context) |
228 | + for parallel_company in company.parallel_company_ids: |
229 | + parallel_acc_id = self._search_parallel_account( |
230 | + cr, uid, code, parallel_company, context=context) |
231 | + if not parallel_acc_id: |
232 | + # Then I create it, linked to parent account |
233 | + #parallel_acc_id = self.create(cr, uid, |
234 | + #self._build_account_vals(cr, uid, |
235 | + #vals, parallel_company, context=context), context=context) |
236 | + pass |
237 | + else: |
238 | + super(account_account,self).write(cr, uid, [parallel_acc_id], |
239 | + self._build_account_vals(cr, uid, |
240 | + vals, parallel_company, context=context), context) |
241 | + _logger.info( |
242 | + _("Parallel account %s (company %s) written") % |
243 | + (code, parallel_company.name)) |
244 | + new_parallel_acc_ids.append(parallel_acc_id) |
245 | + return new_parallel_acc_ids |
246 | + ''' |
247 | + |
248 | + def write(self, cr, uid, ids, vals, context=None): |
249 | + if 'parallel_account_ids' not in vals: |
250 | + # write/create parallel accounts only if 'parallel_account_ids' not explicity written |
251 | + for acc_id in ids: |
252 | + account = self.browse(cr, SUPERUSER_ID, acc_id, context) |
253 | + for parallel_account in account.parallel_account_ids: |
254 | + parallel_vals = self._build_account_vals( |
255 | + cr, SUPERUSER_ID, vals, parallel_account.company_id, |
256 | + context=context) |
257 | + parallel_account.write(parallel_vals) |
258 | + res=super(account_account,self).write(cr, uid, ids, vals, context=context) |
259 | + return res |
260 | + |
261 | + def create(self, cr, uid, vals, context=None): |
262 | + res=super(account_account,self).create(cr, uid, vals, context) |
263 | + self.create_parallel_accounts(cr, uid, [res], context=None) |
264 | + return res |
265 | + |
266 | + def unlink(self, cr, uid, ids, context=None): |
267 | + for account in self.browse(cr, SUPERUSER_ID, ids, context): |
268 | + for parallel_account in account.parallel_account_ids: |
269 | + parallel_account.unlink() |
270 | + res=super(account_account,self).unlink(cr, uid, ids, context=context) |
271 | + return res |
272 | + |
273 | +class account_move(orm.Model): |
274 | + _inherit = "account.move" |
275 | + |
276 | + _columns = { |
277 | + 'parallel_move_ids': fields.one2many('account.move', 'master_parallel_move_id', 'Parallel Entries', |
278 | + readonly=True), |
279 | + 'master_parallel_move_id': fields.many2one('account.move', 'Master Parallel Entry'), |
280 | + } |
281 | + |
282 | + def button_cancel(self, cr, uid, ids, context=None): |
283 | + res = super(account_move, self).button_cancel(cr, uid, ids, context=context) |
284 | + for move in self.browse(cr, SUPERUSER_ID, ids, context=context): |
285 | + for parallel_move in move.parallel_move_ids: |
286 | + parallel_move.button_cancel(context=context) |
287 | + parallel_move.unlink(context=context) |
288 | + return res |
289 | + |
290 | + def lines_balance(self, move_lines): |
291 | + """ |
292 | + returns 0 if lines are balanced, difference if unbalanced |
293 | + """ |
294 | + balance = 0.0 |
295 | + for line_tuple in move_lines: |
296 | + balance += line_tuple[2]['debit'] or (- line_tuple[2]['credit']) or 0.0 |
297 | + return balance |
298 | + |
299 | + def balance_lines(self, cr, uid, move_lines, currency_id, context=None): |
300 | + """ |
301 | + Balance move lines that were unbalanced due to roundings. |
302 | + """ |
303 | + balance = self.lines_balance(move_lines) |
304 | + acc_pool = self.pool.get('account.account') |
305 | + curr_pool = self.pool.get('res.currency') |
306 | + if curr_pool.is_zero(cr, uid, curr_pool.browse(cr, uid, currency_id), balance): |
307 | + return move_lines |
308 | + else: |
309 | + found = False |
310 | + for line_tuple in move_lines: |
311 | + account = acc_pool.browse(cr, uid, line_tuple[2]['account_id'], context) |
312 | + # search for liquidity, receivable or payable accounts |
313 | + # beacause usually they are the result of operations (invoice total). |
314 | + # So, if we made the invoice in parallel currency, we'd get that different invoice total |
315 | + if account.type == 'liquidity' or account.type == 'receivable' or account.type == 'payable': |
316 | + if line_tuple[2]['debit']: |
317 | + line_tuple[2]['debit'] -= balance |
318 | + found = True |
319 | + break |
320 | + elif line_tuple[2]['credit']: |
321 | + line_tuple[2]['credit'] += balance |
322 | + found = True |
323 | + break |
324 | + if not found: |
325 | + # if no liquidity, receivable or payable accounts are present, we use the first line (randomly). |
326 | + # TODO check if this makes sense |
327 | + if move_lines[0][2]['debit']: |
328 | + move_lines[0][2]['debit'] -= balance |
329 | + elif move_lines[0][2]['credit']: |
330 | + move_lines[0][2]['credit'] += balance |
331 | + return move_lines |
332 | + |
333 | + def post(self, cr, uid, ids, context=None): |
334 | + res = super(account_move, self).post(cr, uid, ids, context=context) |
335 | + if context is None: |
336 | + context = {} |
337 | + curr_pool = self.pool.get('res.currency') |
338 | + company_pool = self.pool.get('res.company') |
339 | + uid = SUPERUSER_ID |
340 | + for move in self.browse(cr, uid, ids, context=context): |
341 | + if move.state == 'posted' and not move.parallel_move_ids: |
342 | + # avoid double post in case of 'Skip Draft State for Manual Entries' |
343 | + new_move_lines = [] |
344 | + parallel_data = {} |
345 | + for line in move.line_id: |
346 | + for parallel_account in line.account_id.parallel_account_ids: |
347 | + parallel_data[parallel_account.company_id.id] = {} |
348 | + parallel_data[parallel_account.company_id.id]['move_name'] = line.move_id.name |
349 | + parallel_data[parallel_account.company_id.id]['ref'] = line.move_id.ref |
350 | + parallel_data[parallel_account.company_id.id]['date'] = line.date |
351 | + parallel_data[parallel_account.company_id.id]['move_id'] = line.move_id.id |
352 | + |
353 | + # search period by code and parallel company |
354 | + period_ids = self.pool.get('account.period').search(cr, uid, [ |
355 | + ('code','=',line.period_id.code), |
356 | + ('company_id', '=', parallel_account.company_id.id), |
357 | + ]) |
358 | + |
359 | + if len(period_ids) == 0: |
360 | + raise orm.except_orm(_('Error !'), _('Period %s does not exist in company %s !') |
361 | + % (line.period_id.code, parallel_account.company_id.name)) |
362 | + if len(period_ids) > 1: |
363 | + raise orm.except_orm(_('Error !'), _('Too many periods %s for company %s !') |
364 | + % (line.period_id.code, parallel_account.company_id.name)) |
365 | + |
366 | + parallel_data[parallel_account.company_id.id]['period_id'] = period_ids[0] |
367 | + |
368 | + # search parallel journals for the parallel company |
369 | + parallel_journal_ids = [] |
370 | + for journal in line.journal_id.parallel_journal_ids: |
371 | + if journal.company_id.id == parallel_account.company_id.id: |
372 | + parallel_journal_ids.append(journal.id) |
373 | + |
374 | + if len(parallel_journal_ids) == 0: |
375 | + raise orm.except_orm(_('Error !'), _('Journal %s does not exist in company %s !') |
376 | + % (line.journal_id.name, parallel_account.company_id.name)) |
377 | + if len(parallel_journal_ids) > 1: |
378 | + raise orm.except_orm(_('Error !'), _('Too many journals %s for company %s !') |
379 | + % (line.journal_id.name, parallel_account.company_id.name)) |
380 | + |
381 | + parallel_data[parallel_account.company_id.id]['journal_id'] = parallel_journal_ids[0] |
382 | + |
383 | + new_line_values = { |
384 | + 'name': line.name, |
385 | + 'date_maturity': line.date_maturity or False, |
386 | + 'account_id': parallel_account.id, |
387 | + 'period_id': period_ids[0], |
388 | + 'journal_id': parallel_journal_ids[0], |
389 | + 'company_id': parallel_account.company_id.id, |
390 | + 'partner_id': line.partner_id and line.partner_id.id or False, |
391 | + } |
392 | + |
393 | + if line.currency_id and line.amount_currency: |
394 | + parallel_sec_curr_iso_code = line.currency_id.name |
395 | + amount = line.amount_currency |
396 | + else: |
397 | + parallel_sec_curr_iso_code = line.company_id.currency_id.name |
398 | + amount = line.debit or ( - line.credit) |
399 | + |
400 | + parallel_base_amount = amount |
401 | + if parallel_sec_curr_iso_code != parallel_account.company_id.currency_id.name: |
402 | + # only if parallel company currency is != master move currency |
403 | + # search parallel currency by ISO code and parallel company |
404 | + parallel_secondary_curr_ids = curr_pool.search(cr, uid, [ |
405 | + ('name', '=', parallel_sec_curr_iso_code), |
406 | + ('company_id', '=', parallel_account.company_id.id), |
407 | + ], context=context) |
408 | + |
409 | + if len(parallel_secondary_curr_ids) == 0: |
410 | + raise orm.except_orm(_('Error !'), _('Currency %s does not exist in company %s !') |
411 | + % (parallel_sec_curr_iso_code, parallel_account.company_id.name)) |
412 | + if len(parallel_secondary_curr_ids) > 1: |
413 | + raise orm.except_orm(_('Error !'), _('Too many currencies %s for company %s !') |
414 | + % (parallel_sec_curr_iso_code, parallel_account.company_id.name)) |
415 | + |
416 | + # compute parallel base amount from document currency, using move date |
417 | + context.update({'date': line.date}) |
418 | + parallel_base_amount = curr_pool.compute(cr, uid, parallel_secondary_curr_ids[0], |
419 | + parallel_account.company_id.currency_id.id, amount, |
420 | + context=context) |
421 | + |
422 | + new_line_values['amount_currency'] = amount |
423 | + new_line_values['currency_id'] = parallel_secondary_curr_ids[0] |
424 | + |
425 | + new_line_values['debit'] = 0.0 |
426 | + new_line_values['credit'] = 0.0 |
427 | + if parallel_base_amount > 0: |
428 | + new_line_values['debit'] = abs(parallel_base_amount) |
429 | + elif parallel_base_amount < 0: |
430 | + new_line_values['credit'] = abs(parallel_base_amount) |
431 | + |
432 | + if line.tax_code_id and line.tax_amount: |
433 | + # search parallel tax codes for the parallel company |
434 | + parallel_tax_code_ids = [] |
435 | + for tax_code in line.tax_code_id.parallel_tax_code_ids: |
436 | + if tax_code.company_id.id == parallel_account.company_id.id: |
437 | + parallel_tax_code_ids.append(tax_code.id) |
438 | + |
439 | + if len(parallel_tax_code_ids) == 0: |
440 | + raise orm.except_orm(_('Error !'), _('Tax code %s does not exist in company %s !') |
441 | + % (line.tax_code_id.name, parallel_account.company_id.name)) |
442 | + if len(parallel_tax_code_ids) > 1: |
443 | + raise orm.except_orm(_('Error !'), _('Too many tax_codes %s for company %s !') |
444 | + % (line.tax_code_id.name, parallel_account.company_id.name)) |
445 | + |
446 | + new_line_values['tax_code_id'] = parallel_tax_code_ids[0] |
447 | + total_tax = new_line_values['debit'] - new_line_values['credit'] |
448 | + new_line_values['tax_amount'] = line.tax_amount < 0 \ |
449 | + and - abs(total_tax) \ |
450 | + or line.tax_amount > 0 \ |
451 | + and abs(total_tax) \ |
452 | + or 0.0 |
453 | + |
454 | + new_move_lines.append((parallel_account.company_id.id, (0,0,new_line_values))) |
455 | + #parallel_data[parallel_account.company_id.id]['move_lines'].append((0,0,new_line_values)) |
456 | + |
457 | + for company_id in parallel_data: |
458 | + move_lines = [] |
459 | + for new_move_line in new_move_lines: |
460 | + if new_move_line[0] == company_id: |
461 | + move_lines.append(new_move_line[1]) |
462 | + move_lines=self.balance_lines(cr, uid, move_lines, company_pool.browse(cr, uid, company_id).currency_id.id, context=context) |
463 | + move_values = { |
464 | + 'name': parallel_data[company_id]['move_name'], |
465 | + 'period_id': parallel_data[company_id]['period_id'], |
466 | + 'journal_id': parallel_data[company_id]['journal_id'], |
467 | + 'date': parallel_data[company_id]['date'], |
468 | + 'company_id': company_id, |
469 | + 'line_id': move_lines, |
470 | + 'master_parallel_move_id': parallel_data[company_id]['move_id'], |
471 | + 'ref': parallel_data[company_id]['ref'], |
472 | + } |
473 | + self.create(cr, uid, move_values, context=context) |
474 | + # self.post(cr, uid, [move_id], context=context) |
475 | + |
476 | + return res |
477 | + |
478 | + |
479 | +class account_journal(orm.Model): |
480 | + _inherit = "account.journal" |
481 | + |
482 | + _columns = { |
483 | + 'parallel_journal_ids': fields.many2many('account.journal', |
484 | + 'parallel_journal_rel', 'parent_id', |
485 | + 'child_id', 'Parallel Currency Journals', |
486 | + Help="Set here the journals you want to automatically move when registering entries in this journal"), |
487 | + 'master_parallel_journal_ids': fields.many2many('account.journal', |
488 | + 'parallel_journal_rel', 'child_id', |
489 | + 'parent_id', 'Master Parallel Currency Journals', |
490 | + Help="You can see here the journals that automatically move this journal", readonly=True), |
491 | + } |
492 | + |
493 | +class account_tax_code(orm.Model): |
494 | + _inherit = "account.tax.code" |
495 | + |
496 | + def _get_parallel_tax_codes_summary(self, cr, uid, ids, field_name, arg, context=None): |
497 | + res={} |
498 | + for tax_code in self.browse(cr, SUPERUSER_ID, ids, context): |
499 | + text='Configured parallel tax codes:\n' |
500 | + text2='' |
501 | + for parallel_tax_code in tax_code.parallel_tax_code_ids: |
502 | + text2+= _('Tax code: %s. Company: %s\n') % ( |
503 | + parallel_tax_code.code, parallel_tax_code.company_id.name) |
504 | + if text2: |
505 | + res[tax_code.id] = text + text2 |
506 | + else: |
507 | + res[tax_code.id] = _("No parallel tax codes found") |
508 | + return res |
509 | + |
510 | + _columns = { |
511 | + 'parallel_tax_code_ids': fields.many2many('account.tax.code', |
512 | + 'parallel_tax_code_rel', 'parent_id', |
513 | + 'child_id', 'Parallel Currency Tax Codes', |
514 | + Help="Set here the tax codes you want to automatically move when registering entries in this tax code"), |
515 | + 'master_parallel_tax_code_ids': fields.many2many('account.tax.code', |
516 | + 'parallel_tax_code_rel', 'child_id', |
517 | + 'parent_id', 'Master Parallel Currency Tax Codes', |
518 | + Help="You can see here the tax codes that automatically move this journal", readonly=True), |
519 | + 'parallel_tax_codes_summary': fields.function(_get_parallel_tax_codes_summary, type='text', string='Parallel tax codes summary'), |
520 | + } |
521 | + |
522 | + def _search_parallel_tax_code(self, cr, uid, tax_code, parallel_company, |
523 | + context=None): |
524 | + parallel_tax_code_ids = self.search(cr, uid, [ |
525 | + ('company_id','=', parallel_company.id), |
526 | + ('code','=', tax_code), |
527 | + ], context=context) |
528 | + if len(parallel_tax_code_ids) > 1: |
529 | + raise orm.except_orm(_('Error'), _('Too many tax codes %s for company %s') |
530 | + % (tax_code,parallel_company.name)) |
531 | + return parallel_tax_code_ids and parallel_tax_code_ids[0] or False |
532 | + |
533 | + def _build_tax_code_vals(self, cr, uid, tax_code_vals, parallel_company, context=None): |
534 | + # build only fields I need |
535 | + vals={} |
536 | + if tax_code_vals.has_key('name'): |
537 | + vals['name'] = tax_code_vals['name'] |
538 | + if tax_code_vals.has_key('code'): |
539 | + vals['code'] = tax_code_vals['code'] |
540 | + if tax_code_vals.has_key('type'): |
541 | + vals['type'] = tax_code_vals['type'] |
542 | + if tax_code_vals.has_key('user_type'): |
543 | + vals['user_type'] = tax_code_vals['user_type'] |
544 | + if tax_code_vals.has_key('active'): |
545 | + vals['active'] = tax_code_vals['active'] |
546 | + if tax_code_vals.has_key('centralized'): |
547 | + vals['centralized'] = tax_code_vals['centralized'] |
548 | + if tax_code_vals.has_key('parent_id'): |
549 | + parent_tax_code = self.browse(cr, uid, tax_code_vals['parent_id'], context) |
550 | + parent_parallel_acc_id = self._search_parallel_tax_code( |
551 | + cr, uid, parent_tax_code.code, parallel_company, context=context) |
552 | + if not parent_parallel_acc_id: |
553 | + raise orm.except_orm(_('Error'), |
554 | + _('No parent tax code %s found in company %s') % |
555 | + (parent_tax_code.code, parallel_company.name)) |
556 | + vals['parent_id'] = parent_parallel_acc_id |
557 | + return vals |
558 | + |
559 | + def create_parallel_tax_codes(self, cr, uid, ids, context=None): |
560 | + for tax_code in self.browse(cr, SUPERUSER_ID, ids, context): |
561 | + for parallel_company in tax_code.company_id.parallel_company_ids: |
562 | + if not tax_code.parent_id: |
563 | + raise orm.except_orm(_('Error'),_('Tax code %s does not have parent') |
564 | + % tax_code.code) |
565 | + existing_ids = self.search(cr, SUPERUSER_ID, [ |
566 | + ('code', '=', tax_code.code), |
567 | + ('company_id', '=', parallel_company.id), |
568 | + ]) |
569 | + if existing_ids: |
570 | + raise orm.except_orm(_('Error'), |
571 | + _('Tax code %s already exists for company %s') |
572 | + % (tax_code.code, parallel_company.name)) |
573 | + parent_parallel_tax_code_id = self._search_parallel_tax_code( |
574 | + cr, SUPERUSER_ID, tax_code.parent_id.code, parallel_company, |
575 | + context=context) |
576 | + new_id = self.create(cr, SUPERUSER_ID,{ |
577 | + 'company_id': parallel_company.id, |
578 | + 'parent_id': parent_parallel_tax_code_id, |
579 | + 'name': tax_code.name, |
580 | + 'code': tax_code.code, |
581 | + 'notprintable': tax_code.notprintable, |
582 | + 'sign': tax_code.sign, |
583 | + 'info': tax_code.info, |
584 | + }) |
585 | + cr.execute( |
586 | + "insert into parallel_tax_code_rel(parent_id,child_id) values (%d,%d)" |
587 | + % (tax_code.id,new_id)) |
588 | + return True |
589 | + |
590 | + def write(self, cr, uid, ids, vals, context=None): |
591 | + if not vals.has_key('parallel_tax_code_ids'): |
592 | + # write/create parallel tax codes only if 'parallel_tax_code_ids' not explicity written |
593 | + for tax_code_id in ids: |
594 | + tax_code = self.browse(cr, SUPERUSER_ID, tax_code_id, context) |
595 | + for parallel_tax_code in tax_code.parallel_tax_code_ids: |
596 | + parallel_vals = self._build_tax_code_vals( |
597 | + cr, SUPERUSER_ID, vals, parallel_tax_code.company_id, context=context) |
598 | + parallel_tax_code.write(parallel_vals) |
599 | + res=super(account_tax_code,self).write(cr, uid, ids, vals, context=context) |
600 | + return res |
601 | + |
602 | + def create(self, cr, uid, vals, context=None): |
603 | + res=super(account_tax_code,self).create(cr, uid, vals, context) |
604 | + self.create_parallel_tax_codes(cr, uid, [res], context=None) |
605 | + return res |
606 | |
607 | === added file 'account_parallel_currency/account_demo.xml' |
608 | --- account_parallel_currency/account_demo.xml 1970-01-01 00:00:00 +0000 |
609 | +++ account_parallel_currency/account_demo.xml 2013-09-27 07:51:38 +0000 |
610 | @@ -0,0 +1,504 @@ |
611 | +<?xml version="1.0" encoding="utf-8"?> |
612 | +<openerp> |
613 | + <data> |
614 | + |
615 | + <!-- |
616 | + Parallel company |
617 | + --> |
618 | + |
619 | + <record id="parallel_company" model="res.company"> |
620 | + <field name="name" >Parallel company</field> |
621 | + <field name="currency_id" ref="base.CHF"></field> |
622 | + </record> |
623 | + |
624 | + <record id="base.main_company" model="res.company"> |
625 | + <field name="parallel_company_ids" eval="[(6,0,[ref('parallel_company')])]"></field> |
626 | + </record> |
627 | + |
628 | + <!-- |
629 | + currencies |
630 | + --> |
631 | + |
632 | + <record id="USD" model="res.currency"> |
633 | + <field name="name">USD</field> |
634 | + <field name="symbol">$</field> |
635 | + <field name="rounding">0.01</field> |
636 | + <field name="accuracy">4</field> |
637 | + <field name="position">before</field> |
638 | + <field name="company_id" ref="parallel_company"/> |
639 | + </record> |
640 | + <record id="rateUSD" model="res.currency.rate"> |
641 | + <field name="rate">1.2834</field> |
642 | + <field name="currency_id" ref="USD"/> |
643 | + <field eval="time.strftime('%Y-01-01')" name="name"/> |
644 | + </record> |
645 | + <record id="CHF" model="res.currency"> |
646 | + <field name="name">CHF</field> |
647 | + <field name="symbol">CHF</field> |
648 | + <field name="rounding">0.01</field> |
649 | + <field name="accuracy">4</field> |
650 | + <field name="company_id" ref="parallel_company"/> |
651 | + </record> |
652 | + <record id="rateCHF" model="res.currency.rate"> |
653 | + <field name="rate">1.3086</field> |
654 | + <field name="currency_id" ref="CHF"/> |
655 | + <field eval="time.strftime('%Y-01-01')" name="name"/> |
656 | + </record> |
657 | + <record id="EUR" model="res.currency"> |
658 | + <field name="name">EUR</field> |
659 | + <field name="symbol">€</field> |
660 | + <field name="rounding">0.01</field> |
661 | + <field name="accuracy">4</field> |
662 | + <field name="company_id" ref="parallel_company"/> |
663 | + </record> |
664 | + <record id="rateEUR" model="res.currency.rate"> |
665 | + <field name="currency_id" ref="EUR" /> |
666 | + <field eval="time.strftime('%Y-01-01')" name="name"/> |
667 | + <field name="rate">1.0</field> |
668 | + </record> |
669 | + |
670 | + <!-- |
671 | + Journal |
672 | + --> |
673 | + |
674 | + <record id="parallel_sales_journal" model="account.journal"> |
675 | + <field name="name">Parallel Sales Journal - (test)</field> |
676 | + <field name="code">PSAJ</field> |
677 | + <field name="type">sale</field> |
678 | + <field name="company_id" ref="parallel_company"/> |
679 | + </record> |
680 | + <record id="account.sales_journal" model="account.journal"> |
681 | + <field name="parallel_journal_ids" eval="[(6,0,[ref('parallel_sales_journal')])]"></field> |
682 | + </record> |
683 | + |
684 | + <!-- |
685 | + Fiscal year |
686 | + --> |
687 | + |
688 | + <record id="data_fiscalyear" model="account.fiscalyear"> |
689 | + <field eval="'Fiscal Year X '+time.strftime('%Y')" name="name"/> |
690 | + <field eval="'FY'+time.strftime('%Y')" name="code"/> |
691 | + <field eval="time.strftime('%Y')+'-01-01'" name="date_start"/> |
692 | + <field eval="time.strftime('%Y')+'-12-31'" name="date_stop"/> |
693 | + <field name="company_id" ref="parallel_company"/> |
694 | + </record> |
695 | + |
696 | + <!-- |
697 | + Fiscal Periods |
698 | + --> |
699 | + |
700 | + <record id="period_1" model="account.period"> |
701 | + <field eval="'01/'+time.strftime('%Y')" name="code"/> |
702 | + <field eval="'X 01/'+time.strftime('%Y')" name="name"/> |
703 | + <field eval="True" name="special"/> |
704 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
705 | + <field eval="time.strftime('%Y')+'-01-01'" name="date_start"/> |
706 | + <field eval="time.strftime('%Y')+'-01-31'" name="date_stop"/> |
707 | + <field name="company_id" ref="parallel_company"/> |
708 | + </record> |
709 | + <record id="period_2" model="account.period"> |
710 | + <field eval="'02/'+time.strftime('%Y')" name="code"/> |
711 | + <field eval="'X 02/'+time.strftime('%Y')" name="name"/> |
712 | + <field eval="True" name="special"/> |
713 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
714 | + <field eval="time.strftime('%Y')+'-02-01'" name="date_start"/> |
715 | + <!-- for the last day of February, we have to compute the day before March 1st --> |
716 | + <field eval="(DateTime.today().replace(month=3, day=1) - timedelta(days=1)).strftime('%Y-%m-%d')" name="date_stop"/> |
717 | + <field name="company_id" ref="parallel_company"/> |
718 | + </record> |
719 | + <record id="period_3" model="account.period"> |
720 | + <field eval="'03/'+time.strftime('%Y')" name="code"/> |
721 | + <field eval="'X 03/'+time.strftime('%Y')" name="name"/> |
722 | + <field eval="True" name="special"/> |
723 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
724 | + <field eval="time.strftime('%Y')+'-03-01'" name="date_start"/> |
725 | + <field eval="time.strftime('%Y')+'-03-31'" name="date_stop"/> |
726 | + <field name="company_id" ref="parallel_company"/> |
727 | + </record> |
728 | + <record id="period_4" model="account.period"> |
729 | + <field eval="'04/'+time.strftime('%Y')" name="code"/> |
730 | + <field eval="'X 04/'+time.strftime('%Y')" name="name"/> |
731 | + <field eval="True" name="special"/> |
732 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
733 | + <field eval="time.strftime('%Y')+'-04-01'" name="date_start"/> |
734 | + <field eval="time.strftime('%Y')+'-04-30'" name="date_stop"/> |
735 | + <field name="company_id" ref="parallel_company"/> |
736 | + </record> |
737 | + <record id="period_5" model="account.period"> |
738 | + <field eval="'05/'+time.strftime('%Y')" name="code"/> |
739 | + <field eval="'X 05/'+time.strftime('%Y')" name="name"/> |
740 | + <field eval="True" name="special"/> |
741 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
742 | + <field eval="time.strftime('%Y')+'-05-01'" name="date_start"/> |
743 | + <field eval="time.strftime('%Y')+'-05-31'" name="date_stop"/> |
744 | + <field name="company_id" ref="parallel_company"/> |
745 | + </record> |
746 | + <record id="period_6" model="account.period"> |
747 | + <field eval="'06/'+time.strftime('%Y')" name="code"/> |
748 | + <field eval="'X 06/'+time.strftime('%Y')" name="name"/> |
749 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
750 | + <field eval="True" name="special"/> |
751 | + <field eval="time.strftime('%Y')+'-06-01'" name="date_start"/> |
752 | + <field eval="time.strftime('%Y')+'-06-30'" name="date_stop"/> |
753 | + <field name="company_id" ref="parallel_company"/> |
754 | + </record> |
755 | + <record id="period_7" model="account.period"> |
756 | + <field eval="'07/'+time.strftime('%Y')" name="code"/> |
757 | + <field eval="'X 07/'+time.strftime('%Y')" name="name"/> |
758 | + <field eval="True" name="special"/> |
759 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
760 | + <field eval="time.strftime('%Y')+'-07-01'" name="date_start"/> |
761 | + <field eval="time.strftime('%Y')+'-07-31'" name="date_stop"/> |
762 | + <field name="company_id" ref="parallel_company"/> |
763 | + </record> |
764 | + <record id="period_8" model="account.period"> |
765 | + <field eval="'08/'+time.strftime('%Y')" name="code"/> |
766 | + <field eval="'X 08/'+time.strftime('%Y')" name="name"/> |
767 | + <field eval="True" name="special"/> |
768 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
769 | + <field eval="time.strftime('%Y')+'-08-01'" name="date_start"/> |
770 | + <field eval="time.strftime('%Y')+'-08-31'" name="date_stop"/> |
771 | + <field name="company_id" ref="parallel_company"/> |
772 | + </record> |
773 | + <record id="period_9" model="account.period"> |
774 | + <field eval="'09/'+time.strftime('%Y')" name="code"/> |
775 | + <field eval="'X 09/'+time.strftime('%Y')" name="name"/> |
776 | + <field eval="True" name="special"/> |
777 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
778 | + <field eval="time.strftime('%Y')+'-09-01'" name="date_start"/> |
779 | + <field eval="time.strftime('%Y')+'-09-30'" name="date_stop"/> |
780 | + <field name="company_id" ref="parallel_company"/> |
781 | + </record> |
782 | + <record id="period_10" model="account.period"> |
783 | + <field eval="'10/'+time.strftime('%Y')" name="code"/> |
784 | + <field eval="'X 10/'+time.strftime('%Y')" name="name"/> |
785 | + <field eval="True" name="special"/> |
786 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
787 | + <field eval="time.strftime('%Y')+'-10-01'" name="date_start"/> |
788 | + <field eval="time.strftime('%Y')+'-10-31'" name="date_stop"/> |
789 | + <field name="company_id" ref="parallel_company"/> |
790 | + </record> |
791 | + <record id="period_11" model="account.period"> |
792 | + <field eval="'11/'+time.strftime('%Y')" name="code"/> |
793 | + <field eval="'X 11/'+time.strftime('%Y')" name="name"/> |
794 | + <field eval="True" name="special"/> |
795 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
796 | + <field eval="time.strftime('%Y')+'-11-01'" name="date_start"/> |
797 | + <field eval="time.strftime('%Y')+'-11-30'" name="date_stop"/> |
798 | + <field name="company_id" ref="parallel_company"/> |
799 | + </record> |
800 | + <record id="period_12" model="account.period"> |
801 | + <field eval="'12/'+time.strftime('%Y')" name="code"/> |
802 | + <field eval="'X 12/'+time.strftime('%Y')" name="name"/> |
803 | + <field eval="True" name="special"/> |
804 | + <field name="fiscalyear_id" ref="data_fiscalyear"/> |
805 | + <field eval="time.strftime('%Y')+'-12-01'" name="date_start"/> |
806 | + <field eval="time.strftime('%Y')+'-12-31'" name="date_stop"/> |
807 | + <field name="company_id" ref="parallel_company"/> |
808 | + </record> |
809 | + |
810 | + <!-- |
811 | + Chart of Accounts |
812 | + --> |
813 | + |
814 | + <record id="chart0" model="account.account"> |
815 | + <field name="company_id" ref="parallel_company"/> |
816 | + <field name="code">X0</field> |
817 | + <field name="name">Chart For Automated Tests</field> |
818 | + <field name="type">view</field> |
819 | + <field name="user_type" ref="account.data_account_type_view"/> |
820 | + </record> |
821 | + |
822 | + <!-- Balance Sheet --> |
823 | + |
824 | + <record id="bal" model="account.account"> |
825 | + <field name="company_id" ref="parallel_company"/> |
826 | + <field name="code">X1</field> |
827 | + <field name="name">Balance Sheet - (test)</field> |
828 | + <field ref="chart0" name="parent_id"/> |
829 | + <field name="type">view</field> |
830 | + <field name="user_type" ref="account.data_account_type_view"/> |
831 | + </record> |
832 | + |
833 | + <record model="account.account" id="assets_view"> |
834 | + <field name="company_id" ref="parallel_company"/> |
835 | + <field name="name">Assets - (test)</field> |
836 | + <field name="code">X10</field> |
837 | + <field name="type">view</field> |
838 | + <field name="user_type" ref="account.data_account_type_asset"/> |
839 | + <field name="reconcile" eval="False"/> |
840 | + <field name="parent_id" ref="bal"/> |
841 | + </record> |
842 | + |
843 | + <record id="fas" model="account.account"> |
844 | + <field name="company_id" ref="parallel_company"/> |
845 | + <field name="code">X100</field> |
846 | + <field name="name">Fixed Assets - (test)</field> |
847 | + <field ref="assets_view" name="parent_id"/> |
848 | + <field name="type">view</field> |
849 | + <field name="user_type" ref="account.data_account_type_asset"/> |
850 | + </record> |
851 | + |
852 | + <record id="xfa" model="account.account"> |
853 | + <field name="company_id" ref="parallel_company"/> |
854 | + <field name="code">X1000</field> |
855 | + <field name="name">Fixed Asset Account - (test)</field> |
856 | + <field ref="fas" name="parent_id"/> |
857 | + <field name="type">other</field> |
858 | + <field name="user_type" ref="account.data_account_type_asset"/> |
859 | + </record> |
860 | + |
861 | + <record id="nca" model="account.account"> |
862 | + <field name="company_id" ref="parallel_company"/> |
863 | + <field name="code">X101</field> |
864 | + <field name="name">Net Current Assets - (test)</field> |
865 | + <field ref="assets_view" name="parent_id"/> |
866 | + <field name="type">view</field> |
867 | + <field name="user_type" ref="account.data_account_type_asset"/> |
868 | + </record> |
869 | + |
870 | + <record id="cas" model="account.account"> |
871 | + <field name="company_id" ref="parallel_company"/> |
872 | + <field name="code">X1100</field> |
873 | + <field name="name">Current Assets - (test)</field> |
874 | + <field ref="nca" name="parent_id"/> |
875 | + <field name="type">view</field> |
876 | + <field name="user_type" ref="account.data_account_type_asset"/> |
877 | + </record> |
878 | + |
879 | + <record id="stk" model="account.account"> |
880 | + <field name="company_id" ref="parallel_company"/> |
881 | + <field name="code">X11001</field> |
882 | + <field name="name">Purchased Stocks - (test)</field> |
883 | + <field ref="cas" name="parent_id"/> |
884 | + <field name="type">other</field> |
885 | + <field name="user_type" ref="account.data_account_type_asset"/> |
886 | + </record> |
887 | + |
888 | + <record id="a_recv" model="account.account"> |
889 | + <field name="company_id" ref="parallel_company"/> |
890 | + <field name="code">X11002</field> |
891 | + <field name="name">Debtors - (test)</field> |
892 | + <field ref="cas" name="parent_id"/> |
893 | + <field name="type">receivable</field> |
894 | + <field eval="True" name="reconcile"/> |
895 | + <field name="user_type" ref="account.data_account_type_receivable"/> |
896 | + </record> |
897 | + |
898 | + <record id="ova" model="account.account"> |
899 | + <field name="company_id" ref="parallel_company"/> |
900 | + <field name="code">X11003</field> |
901 | + <field name="name">Output VAT - (test)</field> |
902 | + <field ref="cas" name="parent_id"/> |
903 | + <field name="type">other</field> |
904 | + <field name="user_type" ref="account.data_account_type_asset"/> |
905 | + </record> |
906 | + |
907 | + <record id="bnk" model="account.account"> |
908 | + <field name="company_id" ref="parallel_company"/> |
909 | + <field name="code">X11004</field> |
910 | + <field name="name">Bank Current Account - (test)</field> |
911 | + <field ref="cas" name="parent_id"/> |
912 | + <field name="type">liquidity</field> |
913 | + <field name="user_type" ref="account.data_account_type_asset"/> |
914 | + </record> |
915 | + |
916 | + <record id="cash" model="account.account"> |
917 | + <field name="company_id" ref="parallel_company"/> |
918 | + <field name="code">X11005</field> |
919 | + <field name="name">Cash - (test)</field> |
920 | + <field ref="cas" name="parent_id"/> |
921 | + <field name="type">liquidity</field> |
922 | + <field name="user_type" ref="account.data_account_type_asset"/> |
923 | + </record> |
924 | + |
925 | + <record id="o_income" model="account.account"> |
926 | + <field name="company_id" ref="parallel_company"/> |
927 | + <field name="code">X11006</field> |
928 | + <field name="name">Opening Income - (test)</field> |
929 | + <field ref="cas" name="parent_id"/> |
930 | + <field name="type">other</field> |
931 | + <field name="user_type" ref="account.data_account_type_income"/> |
932 | + </record> |
933 | + <record id="usd_bnk" model="account.account"> |
934 | + <field name="company_id" ref="parallel_company"/> |
935 | + <field name="code">X11007</field> |
936 | + <field name="name">USD Bank Account - (test)</field> |
937 | + <field ref="cas" name="parent_id"/> |
938 | + <field name="type">liquidity</field> |
939 | + <field name="user_type" ref="account.data_account_type_asset"/> |
940 | + <field name="currency_id" ref="base.USD"/> |
941 | + </record> |
942 | + |
943 | + <record model="account.account" id="liabilities_view"> |
944 | + <field name="company_id" ref="parallel_company"/> |
945 | + <field name="name">Liabilities - (test)</field> |
946 | + <field name="code">X11</field> |
947 | + <field name="type">view</field> |
948 | + <field name="user_type" ref="account.data_account_type_liability"/> |
949 | + <field name="reconcile" eval="False"/> |
950 | + <field name="parent_id" ref="bal"/> |
951 | + </record> |
952 | + |
953 | + <record id="cli" model="account.account"> |
954 | + <field name="company_id" ref="parallel_company"/> |
955 | + <field name="code">X110</field> |
956 | + <field name="name">Current Liabilities - (test)</field> |
957 | + <field ref="liabilities_view" name="parent_id"/> |
958 | + <field name="type">view</field> |
959 | + <field name="user_type" ref="account.data_account_type_liability"/> |
960 | + </record> |
961 | + |
962 | + <record id="a_pay" model="account.account"> |
963 | + <field name="company_id" ref="parallel_company"/> |
964 | + <field name="code">X1111</field> |
965 | + <field name="name">Creditors - (test)</field> |
966 | + <field ref="cli" name="parent_id"/> |
967 | + <field name="type">payable</field> |
968 | + <field eval="True" name="reconcile"/> |
969 | + <field name="user_type" ref="account.data_account_type_payable"/> |
970 | + </record> |
971 | + |
972 | + <record id="iva" model="account.account"> |
973 | + <field name="company_id" ref="parallel_company"/> |
974 | + <field name="code">X1112</field> |
975 | + <field name="name">Input VAT - (test)</field> |
976 | + <field ref="cli" name="parent_id"/> |
977 | + <field name="type">other</field> |
978 | + <field name="user_type" ref="account.data_account_type_liability"/> |
979 | + </record> |
980 | + |
981 | + <record id="rsa" model="account.account"> |
982 | + <field name="company_id" ref="parallel_company"/> |
983 | + <field name="code">X1113</field> |
984 | + <field name="name">Reserve and Profit/Loss - (test)</field> |
985 | + <field ref="cli" name="parent_id"/> |
986 | + <field name="type">other</field> |
987 | + <field name="user_type" ref="account.data_account_type_liability"/> |
988 | + </record> |
989 | + |
990 | + <record id="o_expense" model="account.account"> |
991 | + <field name="company_id" ref="parallel_company"/> |
992 | + <field name="code">X1114</field> |
993 | + <field name="name">Opening Expense - (test)</field> |
994 | + <field ref="cli" name="parent_id"/> |
995 | + <field name="type">other</field> |
996 | + <field name="user_type" ref="account.data_account_type_expense"/> |
997 | + </record> |
998 | + |
999 | + <!-- Profit and Loss --> |
1000 | + |
1001 | + <record id="gpf" model="account.account"> |
1002 | + <field name="company_id" ref="parallel_company"/> |
1003 | + <field name="code">X2</field> |
1004 | + <field name="name">Profit and Loss - (test)</field> |
1005 | + <field ref="chart0" name="parent_id"/> |
1006 | + <field name="type">view</field> |
1007 | + <field name="user_type" ref="account.data_account_type_view"/> |
1008 | + </record> |
1009 | + |
1010 | + <record model="account.account" id="income_view"> |
1011 | + <field name="company_id" ref="parallel_company"/> |
1012 | + <field name="name">Income - (test)</field> |
1013 | + <field name="code">X20</field> |
1014 | + <field name="type">view</field> |
1015 | + <field name="user_type" ref="account.data_account_type_income"/> |
1016 | + <field name="reconcile" eval="False"/> |
1017 | + <field name="parent_id" ref="gpf"/> |
1018 | + </record> |
1019 | + |
1020 | + <record model="account.account" id="income_fx_income"> |
1021 | + <field name="company_id" ref="parallel_company"/> |
1022 | + <field name="name">Foreign Exchange Gain - (test)</field> |
1023 | + <field name="code">X201</field> |
1024 | + <field name="type">other</field> |
1025 | + <field name="user_type" ref="account.data_account_type_income"/> |
1026 | + <field name="reconcile" eval="False"/> |
1027 | + <field name="parent_id" ref="income_view"/> |
1028 | + </record> |
1029 | + |
1030 | + <record id="rev" model="account.account"> |
1031 | + <field name="company_id" ref="parallel_company"/> |
1032 | + <field name="code">X200</field> |
1033 | + <field name="name">Revenue - (test)</field> |
1034 | + <field ref="income_view" name="parent_id"/> |
1035 | + <field name="type">view</field> |
1036 | + <field name="user_type" ref="account.data_account_type_income"/> |
1037 | + </record> |
1038 | + |
1039 | + <record id="a_sale" model="account.account"> |
1040 | + <field name="company_id" ref="parallel_company"/> |
1041 | + <field name="code">X2001</field> |
1042 | + <field name="name">Product Sales - (test)</field> |
1043 | + <field ref="rev" name="parent_id"/> |
1044 | + <field name="type">other</field> |
1045 | + <field name="user_type" ref="account.data_account_type_income"/> |
1046 | + </record> |
1047 | + |
1048 | + <record model="account.account" id="expense_view"> |
1049 | + <field name="company_id" ref="parallel_company"/> |
1050 | + <field name="name">Expense - (test)</field> |
1051 | + <field name="code">X21</field> |
1052 | + <field name="type">view</field> |
1053 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1054 | + <field name="reconcile" eval="False"/> |
1055 | + <field name="parent_id" ref="gpf"/> |
1056 | + </record> |
1057 | + |
1058 | + |
1059 | + <record id="cos" model="account.account"> |
1060 | + <field name="company_id" ref="parallel_company"/> |
1061 | + <field name="code">X210</field> |
1062 | + <field name="name">Cost of Sales - (test)</field> |
1063 | + <field ref="expense_view" name="parent_id"/> |
1064 | + <field name="type">view</field> |
1065 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1066 | + </record> |
1067 | + |
1068 | + <record id="cog" model="account.account"> |
1069 | + <field name="company_id" ref="parallel_company"/> |
1070 | + <field name="code">X2100</field> |
1071 | + <field name="name">Cost of Goods Sold - (test)</field> |
1072 | + <field ref="cos" name="parent_id"/> |
1073 | + <field name="type">other</field> |
1074 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1075 | + </record> |
1076 | + |
1077 | + <record id="ovr" model="account.account"> |
1078 | + <field name="company_id" ref="parallel_company"/> |
1079 | + <field name="code">X211</field> |
1080 | + <field name="name">Overheads - (test)</field> |
1081 | + <field ref="expense_view" name="parent_id"/> |
1082 | + <field name="type">view</field> |
1083 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1084 | + </record> |
1085 | + |
1086 | + <record id="a_expense" model="account.account"> |
1087 | + <field name="company_id" ref="parallel_company"/> |
1088 | + <field name="code">X2110</field> |
1089 | + <field name="name">Expenses - (test)</field> |
1090 | + <field ref="ovr" name="parent_id"/> |
1091 | + <field name="type">other</field> |
1092 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1093 | + </record> |
1094 | + |
1095 | + <record model="account.account" id="income_fx_expense"> |
1096 | + <field name="company_id" ref="parallel_company"/> |
1097 | + <field name="name">Foreign Exchange Loss - (test)</field> |
1098 | + <field name="code">X2111</field> |
1099 | + <field name="type">other</field> |
1100 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1101 | + <field name="reconcile" eval="False"/> |
1102 | + <field name="parent_id" ref="ovr"/> |
1103 | + </record> |
1104 | + |
1105 | + <record id="a_salary_expense" model="account.account"> |
1106 | + <field name="company_id" ref="parallel_company"/> |
1107 | + <field name="code">X2112</field> |
1108 | + <field name="name">Salary Expenses - (test)</field> |
1109 | + <field ref="ovr" name="parent_id"/> |
1110 | + <field name="type">other</field> |
1111 | + <field name="user_type" ref="account.data_account_type_expense"/> |
1112 | + </record> |
1113 | + </data> |
1114 | +</openerp> |
1115 | |
1116 | === added file 'account_parallel_currency/account_view.xml' |
1117 | --- account_parallel_currency/account_view.xml 1970-01-01 00:00:00 +0000 |
1118 | +++ account_parallel_currency/account_view.xml 2013-09-27 07:51:38 +0000 |
1119 | @@ -0,0 +1,64 @@ |
1120 | +<?xml version="1.0" encoding="utf-8"?> |
1121 | +<openerp> |
1122 | + <data> |
1123 | + <record id="view_account_form" model="ir.ui.view"> |
1124 | + <field name="name">account.account.form</field> |
1125 | + <field name="model">account.account</field> |
1126 | + <field name="inherit_id" ref="account.view_account_form"></field> |
1127 | + <field name="arch" type="xml"> |
1128 | + <field name="note" position="after"> |
1129 | + <group colspan="4" string="Parallel Currency" groups="account.group_account_manager"> |
1130 | + <field name="parallel_accounts_summary" /> |
1131 | + <button name="create_parallel_accounts" type="object" string="Create parallel accounts"></button> |
1132 | + <separator string="Parallel Currency Accounts" colspan="4"/> |
1133 | + <field colspan="4" name="parallel_account_ids" nolabel="1" domain="[('company_id', '!=', company_id)]"/> |
1134 | + <separator string="Master Parallel Currency Accounts" colspan="4"/> |
1135 | + <field colspan="4" name="master_parallel_account_ids" nolabel="1"/> |
1136 | + </group> |
1137 | + </field> |
1138 | + </field> |
1139 | + </record> |
1140 | + <record id="view_account_journal_form" model="ir.ui.view"> |
1141 | + <field name="name">view_account_journal_form</field> |
1142 | + <field name="model">account.journal</field> |
1143 | + <field name="inherit_id" ref="account.view_account_journal_form"></field> |
1144 | + <field name="arch" type="xml"> |
1145 | + <page string="Entry Controls" position="after"> |
1146 | + <page string="Parallel Currency" groups="account.group_account_manager"> |
1147 | + <separator string="Parallel Currency Journals" colspan="4"/> |
1148 | + <field colspan="4" name="parallel_journal_ids" nolabel="1" domain="[('company_id', '!=', company_id)]"/> |
1149 | + <separator string="Master Parallel Currency Journals" colspan="4"/> |
1150 | + <field colspan="4" name="master_parallel_journal_ids" nolabel="1"/> |
1151 | + </page> |
1152 | + </page> |
1153 | + </field> |
1154 | + </record> |
1155 | + <record id="view_tax_code_form" model="ir.ui.view"> |
1156 | + <field name="name">view_tax_code_form</field> |
1157 | + <field name="model">account.tax.code</field> |
1158 | + <field name="inherit_id" ref="account.view_tax_code_form"></field> |
1159 | + <field name="arch" type="xml"> |
1160 | + <field name="info" position="after"> |
1161 | + <group colspan="4" col="1" groups="account.group_account_manager"> |
1162 | + <field name="parallel_tax_codes_summary" /> |
1163 | + <button name="create_parallel_tax_codes" type="object" string="Create parallel tax codes"></button> |
1164 | + <field name="parallel_tax_code_ids" domain="[('company_id', '!=', company_id)]"/> |
1165 | + <field name="master_parallel_tax_code_ids"/> |
1166 | + </group> |
1167 | + </field> |
1168 | + </field> |
1169 | + </record> |
1170 | + <record id="view_move_form" model="ir.ui.view"> |
1171 | + <field name="name">view_move_form</field> |
1172 | + <field name="model">account.move</field> |
1173 | + <field name="inherit_id" ref="account.view_move_form"></field> |
1174 | + <field name="arch" type="xml"> |
1175 | + <page string="Journal Items" position="after"> |
1176 | + <page string="Parallel Entries" groups="account.group_account_manager"> |
1177 | + <field colspan="4" name="parallel_move_ids" nolabel="1" /> |
1178 | + </page> |
1179 | + </page> |
1180 | + </field> |
1181 | + </record> |
1182 | + </data> |
1183 | +</openerp> |
1184 | |
1185 | === added file 'account_parallel_currency/company_view.xml' |
1186 | --- account_parallel_currency/company_view.xml 1970-01-01 00:00:00 +0000 |
1187 | +++ account_parallel_currency/company_view.xml 2013-09-27 07:51:38 +0000 |
1188 | @@ -0,0 +1,16 @@ |
1189 | +<?xml version="1.0" encoding="UTF-8"?> |
1190 | +<openerp> |
1191 | + <data> |
1192 | + <record id="view_company_form" model="ir.ui.view"> |
1193 | + <field name="inherit_id" ref="base.view_company_form"/> |
1194 | + <field name="name">view.company.form</field> |
1195 | + <field name="model">res.company</field> |
1196 | + <field name="arch" type="xml"> |
1197 | + <page string="Configuration" position="inside"> |
1198 | + <separator string="Parallel Companies" colspan="4"/> |
1199 | + <field name="parallel_company_ids" nolabel="1" colspan="4"/> |
1200 | + </page> |
1201 | + </field> |
1202 | + </record> |
1203 | + </data> |
1204 | +</openerp> |
1205 | |
1206 | === added directory 'account_parallel_currency/i18n' |
1207 | === added file 'account_parallel_currency/res_company.py' |
1208 | --- account_parallel_currency/res_company.py 1970-01-01 00:00:00 +0000 |
1209 | +++ account_parallel_currency/res_company.py 2013-09-27 07:51:38 +0000 |
1210 | @@ -0,0 +1,33 @@ |
1211 | +# -*- coding: utf-8 -*- |
1212 | +############################################################################## |
1213 | +# |
1214 | +# Copyright (C) 2012-2013 Agile Business Group sagl |
1215 | +# (<http://www.agilebg.com>) |
1216 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
1217 | +# |
1218 | +# This program is free software: you can redistribute it and/or modify |
1219 | +# it under the terms of the GNU Affero General Public License as published |
1220 | +# by the Free Software Foundation, either version 3 of the License, or |
1221 | +# (at your option) any later version. |
1222 | +# |
1223 | +# This program is distributed in the hope that it will be useful, |
1224 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1225 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1226 | +# GNU Affero General Public License for more details. |
1227 | +# |
1228 | +# You should have received a copy of the GNU Affero General Public License |
1229 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1230 | +# |
1231 | +############################################################################## |
1232 | + |
1233 | +from openerp.osv import fields, orm |
1234 | +from osv import fields |
1235 | + |
1236 | +class res_company(orm.Model): |
1237 | + _inherit = "res.company" |
1238 | + |
1239 | + _columns = { |
1240 | + 'parallel_company_ids': fields.many2many('res.company', 'parallel_companies_rel', 'master_id', |
1241 | + 'parallel_id', 'Parallel Companies'), |
1242 | + } |
1243 | + |
1244 | |
1245 | === added directory 'account_parallel_currency/security' |
1246 | === added file 'account_parallel_currency/security/security.xml' |
1247 | --- account_parallel_currency/security/security.xml 1970-01-01 00:00:00 +0000 |
1248 | +++ account_parallel_currency/security/security.xml 2013-09-27 07:51:38 +0000 |
1249 | @@ -0,0 +1,11 @@ |
1250 | +<?xml version="1.0" encoding="utf-8"?> |
1251 | +<openerp> |
1252 | +<data noupdate="0"> |
1253 | + <record model="ir.rule" id="res_currency_comp_rule"> |
1254 | + <field name="name">Currency multi-company</field> |
1255 | + <field name="model_id" ref="base.model_res_currency"/> |
1256 | + <field name="global" eval="True"/> |
1257 | + <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> |
1258 | + </record> |
1259 | +</data> |
1260 | +</openerp> |
1261 | |
1262 | === added directory 'account_parallel_currency/test' |
1263 | === added file 'account_parallel_currency/test/customer_invoice.yml' |
1264 | --- account_parallel_currency/test/customer_invoice.yml 1970-01-01 00:00:00 +0000 |
1265 | +++ account_parallel_currency/test/customer_invoice.yml 2013-09-27 07:51:38 +0000 |
1266 | @@ -0,0 +1,143 @@ |
1267 | +- |
1268 | + In order to test account invoice I create a new customer invoice |
1269 | +- |
1270 | + I will create bank detail |
1271 | +- |
1272 | + !record {model: res.partner.bank, id: res_partner_bank_0}: |
1273 | + state: bank |
1274 | + company_id: base.main_company |
1275 | + partner_id: base.main_partner |
1276 | + acc_number: 123456789 |
1277 | + footer: True |
1278 | + bank: base.res_bank_1 |
1279 | + bank_name: Reserve |
1280 | +- |
1281 | + I create a customer invoice |
1282 | +- |
1283 | + !record {model: account.invoice, id: account_invoice_customer0, view: account.invoice_form}: |
1284 | + payment_term: account.account_payment_term_advance |
1285 | + partner_bank_id: res_partner_bank_0 |
1286 | + journal_id: account.sales_journal |
1287 | + partner_id: base.res_partner_3 |
1288 | + reference_type: none |
1289 | + name: 'Test Customer Invoice' |
1290 | + invoice_line: |
1291 | + - product_id: product.product_product_5 |
1292 | + quantity: 10.0 |
1293 | +- |
1294 | + I manually assign tax on invoice |
1295 | +- |
1296 | + !python {model: account.invoice.tax}: | |
1297 | + amt = self.amount_change(cr, uid, [], 50.0, ref('base.EUR'), ref('base.main_company'), False) |
1298 | + base_amt = self.base_change(cr, uid, [], 9000.0, ref('base.EUR'), ref('base.main_company'), False) |
1299 | + invoice_tax_line = { |
1300 | + 'name': 'Test Tax for Customer Invoice', |
1301 | + 'manual': 1, |
1302 | + 'base': base_amt['value']['base_amount'], |
1303 | + 'amount': amt['value']['tax_amount'], |
1304 | + 'account_id': ref('account.ova'), |
1305 | + 'invoice_id': ref("account_invoice_customer0"), |
1306 | + 'tax_code_id': ref('tax_code'), |
1307 | + 'tax_amount': amt['value']['tax_amount'], |
1308 | + } |
1309 | + tax = self.create(cr, uid, invoice_tax_line) |
1310 | + assert tax, "Tax has not been assigned correctly" |
1311 | + |
1312 | +- |
1313 | + I check that Initially customer invoice is in the "Draft" state |
1314 | +- |
1315 | + !assert {model: account.invoice, id: account_invoice_customer0}: |
1316 | + - state == 'draft' |
1317 | +- |
1318 | + I confirm invoice by clicking on validate button |
1319 | +- |
1320 | + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_customer0} |
1321 | +- |
1322 | + I check that the invoice state is "Open" |
1323 | +- |
1324 | + !assert {model: account.invoice, id: account_invoice_customer0}: |
1325 | + - state == 'open' |
1326 | + |
1327 | +- |
1328 | + I check moves attached to the invoice |
1329 | +- |
1330 | + !python {model: account.invoice}: | |
1331 | + acc_id=self.browse(cr, uid, ref("account_invoice_customer0")) |
1332 | + assert acc_id.move_id, "Move not created for open invoice" |
1333 | + assert acc_id.move_id.parallel_move_ids, "Parallel move not created for open invoice" |
1334 | + for parallel_move in acc_id.move_id.parallel_move_ids: |
1335 | + for line in parallel_move.line_id: |
1336 | + if line.account_id.id == ref("a_sale"): |
1337 | + assert line.credit == 11777.40, "CHF Product Sales must be 11777.40" |
1338 | + assert line.amount_currency == -9000.0, "EUR Product Sales must be -9000.0" |
1339 | + assert line.currency_id.id == ref("EUR"), "Secondary currency must be EUR" |
1340 | + if line.account_id.id == ref("ova"): |
1341 | + assert line.tax_amount == 65.43, "CHF Tax amount must be 65.43" |
1342 | + assert line.credit == 65.43, "CHF Tax amount must be 65.43" |
1343 | + |
1344 | +- |
1345 | + I create a customer CHF invoice |
1346 | +- |
1347 | + !record {model: account.invoice, id: account_invoice_customer1, view: account.invoice_form}: |
1348 | + partner_bank_id: res_partner_bank_0 |
1349 | + journal_id: account.sales_journal |
1350 | + partner_id: base.res_partner_3 |
1351 | + reference_type: none |
1352 | + name: 'Test Customer CHF Invoice' |
1353 | + currency_id: base.CHF |
1354 | + invoice_line: |
1355 | + - product_id: product.product_product_5 |
1356 | + quantity: 1.0 |
1357 | + price_unit: 100.0 |
1358 | + |
1359 | +- |
1360 | + I confirm invoice by clicking on validate button |
1361 | +- |
1362 | + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_customer1} |
1363 | + |
1364 | +- |
1365 | + I check moves attached to the invoice |
1366 | +- |
1367 | + !python {model: account.invoice}: | |
1368 | + acc_id=self.browse(cr, uid, ref("account_invoice_customer1")) |
1369 | + assert acc_id.move_id, "Move not created for open invoice" |
1370 | + assert acc_id.move_id.parallel_move_ids, "Parallel move not created for open invoice" |
1371 | + for parallel_move in acc_id.move_id.parallel_move_ids: |
1372 | + for line in parallel_move.line_id: |
1373 | + if line.account_id.id == ref("a_sale"): |
1374 | + assert line.credit == 100.0, "CHF Product Sales must be 100.0" |
1375 | + assert not line.amount_currency, "amount_currency must be empty" |
1376 | + assert not line.currency_id, "currency_id must be empty" |
1377 | + |
1378 | +- |
1379 | + I create a customer USD invoice |
1380 | +- |
1381 | + !record {model: account.invoice, id: account_invoice_customer2, view: account.invoice_form}: |
1382 | + journal_id: account.sales_journal |
1383 | + partner_id: base.res_partner_3 |
1384 | + reference_type: none |
1385 | + name: 'Test Customer USD Invoice' |
1386 | + currency_id: base.USD |
1387 | + invoice_line: |
1388 | + - product_id: product.product_product_5 |
1389 | + quantity: 1.0 |
1390 | + price_unit: 100.0 |
1391 | + |
1392 | +- |
1393 | + I confirm invoice by clicking on validate button |
1394 | +- |
1395 | + !workflow {model: account.invoice, action: invoice_open, ref: account_invoice_customer2} |
1396 | + |
1397 | +- |
1398 | + I check moves attached to the invoice |
1399 | +- |
1400 | + !python {model: account.invoice}: | |
1401 | + acc_id=self.browse(cr, uid, ref("account_invoice_customer2")) |
1402 | + assert acc_id.move_id, "Move not created for open invoice" |
1403 | + assert acc_id.move_id.parallel_move_ids, "Parallel move not created for open invoice" |
1404 | + for parallel_move in acc_id.move_id.parallel_move_ids: |
1405 | + for line in parallel_move.line_id: |
1406 | + if line.account_id.id == ref("a_sale"): |
1407 | + assert line.credit == 101.96, "USD Product Sales must be 101.96" |
1408 | + assert line.amount_currency == -100.0, "USD Product Sales must be -100.0" |
1409 | + assert line.currency_id.id == ref("USD"), "Secondary currency must be USD" |
1410 | |
1411 | === added file 'account_parallel_currency/test/mapping_parallel_accounts.yml' |
1412 | --- account_parallel_currency/test/mapping_parallel_accounts.yml 1970-01-01 00:00:00 +0000 |
1413 | +++ account_parallel_currency/test/mapping_parallel_accounts.yml 2013-09-27 07:51:38 +0000 |
1414 | @@ -0,0 +1,25 @@ |
1415 | +- |
1416 | + I create Tax Codes |
1417 | +- |
1418 | + !record {model: account.tax.code, id: tax_code}: |
1419 | + name: tax_code |
1420 | + code: 1 |
1421 | + company_id: base.main_company |
1422 | + sign: 1 |
1423 | +- |
1424 | + !record {model: account.tax.code, id: parallel_tax_code}: |
1425 | + name: parallel_tax_code |
1426 | + code: 1 |
1427 | + company_id: parallel_company |
1428 | + sign: 1 |
1429 | +- |
1430 | + I create the mapping wizard |
1431 | +- |
1432 | + !record {model: account.parallel.mapping, id: account_parallel_mapping_0}: |
1433 | + remove_old_mapping: True |
1434 | + |
1435 | +- |
1436 | + I click on do mapping |
1437 | +- |
1438 | + !python {model: account.parallel.mapping}: | |
1439 | + self.do_mapping(cr, uid, [ref("account_parallel_mapping_0")]) |
1440 | |
1441 | === added directory 'account_parallel_currency/wizard' |
1442 | === added file 'account_parallel_currency/wizard/__init__.py' |
1443 | --- account_parallel_currency/wizard/__init__.py 1970-01-01 00:00:00 +0000 |
1444 | +++ account_parallel_currency/wizard/__init__.py 2013-09-27 07:51:38 +0000 |
1445 | @@ -0,0 +1,22 @@ |
1446 | +# -*- coding: utf-8 -*- |
1447 | +############################################################################## |
1448 | +# |
1449 | +# Copyright (C) 2012-2013 Agile Business Group sagl |
1450 | +# (<http://www.agilebg.com>) |
1451 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
1452 | +# |
1453 | +# This program is free software: you can redistribute it and/or modify |
1454 | +# it under the terms of the GNU Affero General Public License as published |
1455 | +# by the Free Software Foundation, either version 3 of the License, or |
1456 | +# (at your option) any later version. |
1457 | +# |
1458 | +# This program is distributed in the hope that it will be useful, |
1459 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1460 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1461 | +# GNU Affero General Public License for more details. |
1462 | +# |
1463 | +# You should have received a copy of the GNU Affero General Public License |
1464 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1465 | +# |
1466 | +############################################################################## |
1467 | +import do_mapping |
1468 | |
1469 | === added file 'account_parallel_currency/wizard/do_mapping.py' |
1470 | --- account_parallel_currency/wizard/do_mapping.py 1970-01-01 00:00:00 +0000 |
1471 | +++ account_parallel_currency/wizard/do_mapping.py 2013-09-27 07:51:38 +0000 |
1472 | @@ -0,0 +1,92 @@ |
1473 | +# -*- coding: utf-8 -*- |
1474 | +############################################################################## |
1475 | +# |
1476 | +# Copyright (C) 2012-2013 Agile Business Group sagl |
1477 | +# (<http://www.agilebg.com>) |
1478 | +# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>) |
1479 | +# |
1480 | +# This program is free software: you can redistribute it and/or modify |
1481 | +# it under the terms of the GNU Affero General Public License as published |
1482 | +# by the Free Software Foundation, either version 3 of the License, or |
1483 | +# (at your option) any later version. |
1484 | +# |
1485 | +# This program is distributed in the hope that it will be useful, |
1486 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1487 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1488 | +# GNU Affero General Public License for more details. |
1489 | +# |
1490 | +# You should have received a copy of the GNU Affero General Public License |
1491 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1492 | +# |
1493 | +############################################################################## |
1494 | + |
1495 | +from openerp.osv import fields, orm |
1496 | +from tools.translate import _ |
1497 | +import logging |
1498 | +from openerp import SUPERUSER_ID |
1499 | + |
1500 | +_logger = logging.getLogger(__name__) |
1501 | + |
1502 | +class account_parallel_mapping(orm.TransientModel): |
1503 | + |
1504 | + _name = "account.parallel.mapping" |
1505 | + |
1506 | + _columns = { |
1507 | + 'message': fields.text('Message'), |
1508 | + 'remove_old_mapping': fields.boolean('Remove Previous Mapping'), |
1509 | + } |
1510 | + |
1511 | + _defaults = { |
1512 | + 'remove_old_mapping': True, |
1513 | + } |
1514 | + |
1515 | + def do_mapping(self, cr, uid, ids, context=None): |
1516 | + company_pool = self.pool.get('res.company') |
1517 | + account_pool = self.pool.get('account.account') |
1518 | + tax_code_pool = self.pool.get('account.tax.code') |
1519 | + company_ids = company_pool.search(cr, SUPERUSER_ID, []) |
1520 | + wizard =self.browse(cr, uid, ids[0]) |
1521 | + if wizard.remove_old_mapping: |
1522 | + account_ids = account_pool.search(cr, SUPERUSER_ID, []) |
1523 | + account_pool.write(cr, SUPERUSER_ID, account_ids, {'parallel_account_ids': [(6,0,[])]}) |
1524 | + for company_id in company_ids: |
1525 | + company = company_pool.browse(cr, SUPERUSER_ID, company_id) |
1526 | + if company.parallel_company_ids: |
1527 | + master_account_ids = account_pool.search(cr, SUPERUSER_ID, [('company_id', '=', company.id)]) |
1528 | + master_tax_code_ids = tax_code_pool.search(cr, SUPERUSER_ID, [('company_id', '=', company.id)]) |
1529 | + # account mapping |
1530 | + for master_account_id in master_account_ids: |
1531 | + master_account = account_pool.browse(cr, SUPERUSER_ID, master_account_id) |
1532 | + for parallel_company in company.parallel_company_ids: |
1533 | + parallel_account_ids = account_pool.search(cr, SUPERUSER_ID, [ |
1534 | + ('code', '=', master_account.code), |
1535 | + ('company_id', '=', parallel_company.id), |
1536 | + ]) |
1537 | + if len(parallel_account_ids) > 1: |
1538 | + raise orm.except_orm(_('Error'), _('Duplicated account %s for company %s') |
1539 | + % (master_account.code,parallel_company.name)) |
1540 | + elif not parallel_account_ids: |
1541 | + raise orm.except_orm(_('Error'), _('No account %s for company %s') |
1542 | + % (master_account.code,parallel_company.name)) |
1543 | + elif len(parallel_account_ids) == 1: |
1544 | + master_account.write({'parallel_account_ids': |
1545 | + [(4,parallel_account_ids[0])]}) |
1546 | + # tax code mapping |
1547 | + for master_tax_code_id in master_tax_code_ids: |
1548 | + master_tax_code = tax_code_pool.browse(cr, SUPERUSER_ID, master_tax_code_id) |
1549 | + for parallel_company in company.parallel_company_ids: |
1550 | + parallel_tax_code_ids = tax_code_pool.search(cr, SUPERUSER_ID, [ |
1551 | + ('code', '=', master_tax_code.code), |
1552 | + ('company_id', '=', parallel_company.id), |
1553 | + ]) |
1554 | + if len(parallel_tax_code_ids) > 1: |
1555 | + raise orm.except_orm(_('Error'), _('Duplicated tax code %s for company %s') |
1556 | + % (master_tax_code.code,parallel_company.name)) |
1557 | + elif not parallel_tax_code_ids: |
1558 | + raise orm.except_orm(_('Error'), _('No tax code %s for company %s') |
1559 | + % (master_tax_code.code,parallel_company.name)) |
1560 | + elif len(parallel_tax_code_ids) == 1: |
1561 | + master_tax_code.write({'parallel_tax_code_ids': |
1562 | + [(4,parallel_tax_code_ids[0])]}) |
1563 | + self.write(cr, uid, ids, {'message': _('Done')}) |
1564 | + return True |
1565 | |
1566 | === added file 'account_parallel_currency/wizard/do_mapping.xml' |
1567 | --- account_parallel_currency/wizard/do_mapping.xml 1970-01-01 00:00:00 +0000 |
1568 | +++ account_parallel_currency/wizard/do_mapping.xml 2013-09-27 07:51:38 +0000 |
1569 | @@ -0,0 +1,32 @@ |
1570 | +<?xml version="1.0" encoding="utf-8"?> |
1571 | +<openerp> |
1572 | + <data> |
1573 | + |
1574 | + <record id="account_parallel_mapping" model="ir.ui.view"> |
1575 | + <field name="name">account.parallel.mapping</field> |
1576 | + <field name="model">account.parallel.mapping</field> |
1577 | + <field name="arch" type="xml"> |
1578 | + <form string="Parallel Mapping" > |
1579 | + <group col="2"> |
1580 | + <label string="This wizard will create the parallel account mapping. Please make sure you configured the 'Parallel Companies' field on your company. The wizard will search for matching account and tax codes and write the result in the 'Parallel Currency Accounts' and 'Parallel Currency Tax Codes' fields" colspan="2"/> |
1581 | + <field name="remove_old_mapping" colspan="2"/> |
1582 | + <button icon="gtk-cancel" special="cancel" string="Close" /> |
1583 | + <button icon="gtk-ok" name="do_mapping" string="Run" type="object" /> |
1584 | + <field name="message" colspan="2" nolabel="1" readonly="1"/> |
1585 | + </group> |
1586 | + </form> |
1587 | + </field> |
1588 | + </record> |
1589 | + |
1590 | + <record id="action_account_parallel_mapping" model="ir.actions.act_window"> |
1591 | + <field name="name">Parallel Accounting Mapping</field> |
1592 | + <field name="res_model">account.parallel.mapping</field> |
1593 | + <field name="view_type">form</field> |
1594 | + <field name="view_mode">form</field> |
1595 | + <field name="view_id" ref="account_parallel_mapping"/> |
1596 | + <field name="target">new</field> |
1597 | + </record> |
1598 | + |
1599 | + <menuitem name="Parallel Accounting Mapping" action="action_account_parallel_mapping" id="menu_action_account_parallel_mapping" parent="account.menu_finance_accounting"/> |
1600 | + </data> |
1601 | +</openerp> |
Hi Lorenzzo,
Sorry for the question : this looks very interesting but I cannot find any cases/example in real situation where this could be a must have...
I mean, why would you duplicate the entire accounting books of the company instead of simply do a report or a view which convert account balances ?
Any legal reason ? unless a segregation of duties issue maybe ?
Thanks
Frederic