Merge lp:~elbati/account-consolidation/adding_account_parallel_currency_7 into lp:~account-core-editors/account-consolidation/7.0

Proposed by Lorenzo Battistini
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
Reviewer Review Type Date Requested Status
Maxime Chambreuil (http://www.savoirfairelinux.com) code review Approve
Stefan Rijnhart (Opener) Approve
Review via email: mp+151723@code.launchpad.net

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

To post a comment you must log in.
Revision history for this message
Frederic Clementi - Camptocamp (frederic-clementi) 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 ?

Thanks

Frederic

Revision history for this message
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

Revision history for this message
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://www.altenburger.ch/uploads/tx_altenburger/mc_2013_Revisione_del_diritto_contabile_svizzero.pdf>
(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

Revision history for this message
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?

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Forgot to assign review status, make that 'needs information'

review: Needs Information
Revision history for this message
Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) wrote :

Thanks Lorenzo.

Can you please run autopep8 on your module ?

review: Needs Fixing (code review)
Revision history for this message
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

Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Thanks for the info, and for updating the code style a little bit.

review: Approve
Revision history for this message
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..

Revision history for this message
Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) :
review: Approve (code review)
Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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>

Subscribers

People subscribed via source and target branches