[FIX] account: Fixed default taxes and code.
[odoo/odoo.git] / addons / account / res_config.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import logging
23 import time
24 import datetime
25 from dateutil.relativedelta import relativedelta
26 from operator import itemgetter
27 from os.path import join as opj
28
29 from tools.translate import _
30 from osv import fields, osv
31 import netsvc
32 import tools
33
34 class account_configuration(osv.osv_memory):
35     _name = 'account.installer'
36     _inherit = 'res.config.settings'
37     __logger = logging.getLogger(_name)
38
39     def _get_charts(self, cr, uid, context=None):
40         modules = self.pool.get('ir.module.module')
41         # Looking for the module with the 'Account Charts' category
42         category_name, category_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'module_category_localization_account_charts')
43         ids = modules.search(cr, uid, [('category_id', '=', category_id)], context=context)
44         charts = list(
45             sorted(((m.name, m.shortdesc)
46                     for m in modules.browse(cr, uid, ids, context=context)),
47                    key=itemgetter(1)))
48         charts.insert(0, ('configurable', 'Generic Chart Of Accounts'))
49         return charts
50
51     _columns = {
52             'company_id': fields.many2one('res.company', 'Company',help="Your company."),
53             'currency_id': fields.related('company_id', 'currency_id', type='many2one', relation='res.currency', string='Currency', store=True, help="Currency of your company."),
54             'charts': fields.selection(_get_charts, 'Chart of Accounts',
55                                         required=True,
56                                         help="Installs localized accounting charts to match as closely as "
57                                              "possible the accounting needs of your company based on your "
58                                              "country."),
59             'date_start': fields.date('Start Date', required=True),
60             'date_stop': fields.date('End Date', required=True),
61             'period': fields.selection([('month', 'Monthly'), ('3months','3 Monthly')], 'Periods', required=True),
62             'has_default_company' : fields.boolean('Has Default Company', readonly=True),
63             'chart_template_id': fields.many2one('account.chart.template', 'Chart Template'),
64             'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year'),
65             'default_paypal_account': fields.char("Your Paypal Account", size=128, help="Paypal username (usually email) for receiving online payments.", default_model='res.company'),
66             'company_footer': fields.char("Footer of Reports", size=128, readonly=True, help="Footer of reports based on your bank accounts."),
67             'sale_journal_id': fields.many2one('account.journal','Sale Journal'),
68             'customer_invoice_sequence_prefix': fields.related('sale_journal_id', 'sequence_id', 'prefix', type='char', relation='ir.sequence', string='Invoice Sequence'),
69             'customer_invoice_sequence_next': fields.related('sale_journal_id', 'sequence_id', 'number_next', type='integer', relation='ir.sequence', string='Invoice Sequence Next Number'),
70             'sale_refund_journal_id': fields.many2one('account.journal','Sale Refund Journal'),
71             'customer_refund_sequence_prefix': fields.related('sale_refund_journal_id', 'sequence_id', 'prefix', type='char', relation='ir.sequence', string='Refund Sequence'),
72             'customer_refund_sequence_next': fields.related('sale_refund_journal_id', 'sequence_id', 'number_next', type='integer', relation='ir.sequence', string='Refund Sequence Next Number'),
73             'purchase_journal_id': fields.many2one('account.journal','Purchase Journal'),
74             'supplier_invoice_sequence_prefix': fields.related('purchase_journal_id', 'sequence_id', 'prefix', type='char', relation='ir.sequence', string='Supplier Invoice Sequence'),
75             'supplier_invoice_sequence_next': fields.related('purchase_journal_id', 'sequence_id', 'number_next', type='integer', relation='ir.sequence', string='Supplier Invoice Sequence Next Number'),
76             'purchase_refund_journal_id': fields.many2one('account.journal','Purchase Refund Journal'),
77             'supplier_refund_sequence_prefix': fields.related('purchase_refund_journal_id', 'sequence_id', 'prefix', type='char', relation='ir.sequence', string='Supplier Refund Sequence'),
78             'supplier_refund_sequence_next': fields.related('purchase_refund_journal_id', 'sequence_id', 'number_next', type='integer', relation='ir.sequence', string='Supplier Refund Sequence Next Number'),
79
80             'module_account_check_writing': fields.boolean('Support check writings',
81                                             help="""  This allows you to check writing and printing.
82                                             It installs the account_check_writing module."""),
83             'module_account_accountant': fields.boolean('Accountant Features',
84                                         help="""This allows you to access all the accounting features like the journal items and the chart of accounts.
85                                         It installs the account_accountant module."""),
86             'module_account_asset': fields.boolean('Assets Management',
87                                     help="""This allows you to manages the assets owned by a company or an individual. It will keep track of depreciation's occurred on
88                                     those assets. And it allows to create Move's of the depreciation lines.
89                                     It installs the account_asset module."""),
90             'module_account_budget': fields.boolean('Budgets Management',
91                                     help="""This allows accountants to manage analytic and crossovered budgets.
92                                     Once the Master Budgets and the Budgets are defined (in Accounting/Budgets/),
93                                     the Project Managers can set the planned amount on each Analytic Account.
94                                     It installs the account_budget module."""),
95             'module_account_payment': fields.boolean('Supplier Payment Orders',
96                                     help="""This allows you to create and manage your payment orders, with purposes to
97                                     * serve as base for an easy plug-in of various automated payment mechanisms.
98                                     * provide a more efficient way to manage invoice payment.
99                                     It installs the account_payment module."""),
100             'module_account_voucher': fields.boolean('Manage Customer Payments',
101                                     help="""This includes all the basic requirements of Voucher Entries for Bank, Cash, Sales, Purchase, Expanse, Contra, etc.
102                                     It installs the account_voucher module."""),
103             'module_account_followup': fields.boolean('Customer Follow-Ups',
104                                     help="""This allows to automate letters for unpaid invoices, with multi-level recalls.
105                                     It installs the account_followup module."""),
106             'module_account_analytic_plans': fields.boolean('Support Multiple Analytic Plans',
107                                         help="""This allows to use several analytic plans, according to the general journal.
108                                         It installs the account_analytic_plans module."""),
109             'module_account_analytic_default': fields.boolean('Rules for Analytic Assignation',
110                                             help="""Set default values for your analytic accounts
111                                             Allows to automatically select analytic accounts based on criterias:
112                                             * Product
113                                             * Partner
114                                             * User
115                                             * Company
116                                             * Date.
117                                         It installs the account_analytic_default module."""),
118             'module_account_invoice_layout': fields.boolean('Allow notes and subtotals',
119                                             help="""This provides some features to improve the layout of the invoices.
120                                             It gives you the possibility to:
121                                             * order all the lines of an invoice
122                                             * add titles, comment lines, sub total lines
123                                             * draw horizontal lines and put page breaks.
124                                             It installs the account_invoice_layout module."""),
125
126             'group_analytic_account_for_sales': fields.boolean('Analytic Accounting for Sales', group='base.group_user', implied_group='base.group_analytic_account_for_sales',
127                                                                help="Allows you to set analytic account for sale order. It assigns 'Analytic Accounting for Sales' group to all employees."),
128             'group_analytic_account_for_purchase': fields.boolean('Analytic Accounting for Purchase', group='base.group_user', implied_group='base.group_analytic_account_for_purchase',
129                                                                   help="Allows you to set analytic account for purchase order. It assigns 'Analytic Accounting for Purchase' group to all employees."),
130             'group_dates_periods': fields.boolean('Allow dates/periods', group='base.group_user', implied_group='base.group_dates_periods',
131                                                   help="Allows you to keep the period same as your invoice date when you validate the invoice."\
132                                                        "It will add the group 'Allow dates and periods' for all users."),
133             'group_proforma_invoices': fields.boolean('Allow Pro-forma Invoices', group='base.group_user', implied_group='base.group_proforma_invoices',
134                                                       help="Allows you to put invoice in pro-forma state. It assigns 'Allow Pro-forma Invoices' group to all employees."),
135
136             'multi_charts_id':fields.many2one('wizard.multi.charts.accounts', 'Multi charts accounts'),
137             'taxes_id':fields.many2one('account.tax.template', 'Default Sale Tax', domain="[('type_tax_use','=','sale')]"),
138             'supplier_taxes_id':fields.many2one('account.tax.template', 'Default Purchase Tax', domain="[('type_tax_use','=','purchase')]"),
139             'sale_tax_rate': fields.float('Sales Tax(%)'),
140             'purchase_tax_rate': fields.float('Purchase Tax(%)'),
141             'complete_tax_set': fields.boolean('Complete Set of Taxes'),
142     }
143     def _default_company(self, cr, uid, context=None):
144         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
145         return user.company_id and user.company_id.id or False
146
147     def _default_has_default_company(self, cr, uid, context=None):
148         count = self.pool.get('res.company').search_count(cr, uid, [], context=context)
149         return bool(count == 1)
150
151     _defaults = {
152             'date_start': lambda *a: time.strftime('%Y-01-01'),
153             'date_stop': lambda *a: time.strftime('%Y-12-31'),
154             'period': 'month',
155             'company_id': _default_company,
156             'has_default_company': _default_has_default_company,
157             'charts': 'configurable',
158     }
159
160     def _check_default_tax(self, cr, uid, context=None):
161         ir_values_obj = self.pool.get('ir.values')
162         taxes = {}
163         for tax in ir_values_obj.get(cr, uid, 'default', False, ['product.template']):
164             if tax[1] == 'taxes_id':
165                 taxes.update({'taxes_id': tax[2]})
166             if tax[1] == 'supplier_taxes_id':
167                 taxes.update({'supplier_taxes_id': tax[2]})
168         return taxes
169
170     def set_tax_defaults(self, cr, uid, ids, context=None):
171         ir_values_obj = self.pool.get('ir.values')
172
173         res = {}
174         wizard = self.browse(cr, uid, ids)[0]
175         if wizard.taxes_id.id:
176             ir_values_obj.set_default(cr, uid, 'product.template', 'taxes_id', wizard.taxes_id.id )
177
178         if wizard.supplier_taxes_id.id:
179             ir_values_obj.set_default(cr, uid, 'product.template', 'supplier_taxes_id', wizard.supplier_taxes_id.id )
180
181         return res
182
183     def default_get(self, cr, uid, fields_list, context=None):
184         ir_values_obj = self.pool.get('ir.values')
185         chart_template_obj = self.pool.get('account.chart.template')
186         fiscalyear_obj = self.pool.get('account.fiscalyear')
187         journal_obj = self.pool.get('account.journal')
188         res = super(account_configuration, self).default_get(cr, uid, fields_list, context=context)
189         taxes = self._check_default_tax(cr, uid, context)
190         chart_template_ids = chart_template_obj.search(cr, uid, [('visible', '=', True)], context=context)
191         fiscalyear_ids = fiscalyear_obj.search(cr, uid, [('date_start','=',time.strftime('%Y-01-01')),('date_stop','=',time.strftime('%Y-12-31'))])
192
193         cmp_id = self.pool.get('ir.model.data').get_object(cr, uid, 'base', 'main_company').id
194         company_data = self.pool.get('res.company').browse(cr, uid, cmp_id)
195         res.update({'company_footer': company_data.rml_footer2})
196
197         journal_ids = journal_obj.search(cr, uid, [('company_id', '=', res.get('company_id'))])
198         if journal_ids:
199             for journal in journal_obj.browse(cr, uid, journal_ids, context=context):
200                 if journal.type == 'sale':
201                     res.update({'sale_journal_id': journal.id})
202                 if journal.type == 'sale_refund':
203                     res.update({'sale_refund_journal_id': journal.id})
204                 if journal.type == 'purchase':
205                     res.update({'purchase_journal_id': journal.id})
206                 if journal.type == 'purchase_refund':
207                     res.update({'purchase_refund_journal_id': journal.id})
208
209         if chart_template_ids:
210             res.update({'chart_template_id': chart_template_ids[0]})
211             data = chart_template_obj.browse(cr, uid, chart_template_ids[0], context=context)
212             res.update({'complete_tax_set': data.complete_tax_set})
213             supplier_taxes_id = ir_values_obj.get_default(cr, uid, 'product.template', 'supplier_taxes_id')
214             res.update({'supplier_taxes_id': supplier_taxes_id})
215             taxes_id = ir_values_obj.get_default(cr, uid, 'product.template', 'taxes_id')
216             res.update({'taxes_id': taxes_id})
217
218         if fiscalyear_ids:
219             res.update({'fiscalyear_id': fiscalyear_ids[0]})
220         if taxes:
221             if chart_template_ids:
222                 sale_tax_id = taxes.get('taxes_id')
223                 res.update({'taxes_id': isinstance(sale_tax_id,list) and sale_tax_id[0] or sale_tax_id})
224                 purchase_tax_id = taxes.get('supplier_taxes_id')
225                 res.update({'supplier_taxes_id': isinstance(purchase_tax_id,list) and purchase_tax_id[0] or purchase_tax_id})
226         else:
227             res.update({'sale_tax_rate': 15.0, 'purchase_tax_rate': 15.0})
228         return res
229
230     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
231         ir_values_obj = self.pool.get('ir.values')
232         res = super(account_configuration, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu)
233         cmp_select = []
234         # display in the widget selection only the companies that haven't been configured yet
235         unconfigured_cmp = self.get_unconfigured_cmp(cr, uid, context=context)
236         for field in res['fields']:
237             if field == 'company_id':
238                 res['fields'][field]['domain'] = [('id','in',unconfigured_cmp)]
239                 res['fields'][field]['selection'] = [('', '')]
240                 if unconfigured_cmp:
241                     cmp_select = [(line.id, line.name) for line in self.pool.get('res.company').browse(cr, uid, unconfigured_cmp)]
242                     res['fields'][field]['selection'] = cmp_select
243         return res
244
245     def get_unconfigured_cmp(self, cr, uid, context=None):
246         """ get the list of companies that have not been configured yet
247         but don't care about the demo chart of accounts """
248         cmp_select = []
249         company_ids = self.pool.get('res.company').search(cr, uid, [], context=context)
250         cr.execute("SELECT company_id FROM account_account WHERE active = 't' AND account_account.parent_id IS NULL AND name != %s", ("Chart For Automated Tests",))
251         configured_cmp = [r[0] for r in cr.fetchall()]
252         return list(set(company_ids)-set(configured_cmp))
253
254     def check_unconfigured_cmp(self, cr, uid, context=None):
255         """ check if there are still unconfigured companies """
256         if not self.get_unconfigured_cmp(cr, uid, context=context):
257             raise osv.except_osv(_('No unconfigured company !'), _("There are currently no company without chart of account. The wizard will therefore not be executed."))
258
259     def on_change_start_date(self, cr, uid, id, start_date=False):
260         if start_date:
261             start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")
262             end_date = (start_date + relativedelta(months=12)) - relativedelta(days=1)
263             return {'value': {'date_stop': end_date.strftime('%Y-%m-%d')}}
264         return {}
265
266     def on_change_company_id(self, cr, uid, id, company_id=False):
267         company_obj = self.pool.get('res.company')
268         currency_id = company_obj.browse(cr, uid, company_id).currency_id
269         return {'value': {'currency_id': currency_id.id}}
270
271     def install_chartofaccounts(self, cr, uid, ids, context=None):
272         ir_module = self.pool.get('ir.module.module')
273         multi_chart_obj = self.pool.get('wizard.multi.charts.accounts')
274         chart_template_obj = self.pool.get('account.chart.template')
275         tax_templ_obj = self.pool.get('account.tax.template')
276
277         if context is None:
278             context = {}
279         for res in self.read(cr, uid, ids, context=context):
280             chart = res.get('charts')
281             if chart == 'configurable':
282                 #load generic chart of account
283                 fp = tools.file_open(opj('account', 'configurable_account_chart.xml'))
284                 tools.convert_xml_import(cr, 'account', fp, {}, 'init', True, None)
285                 fp.close()
286             elif chart.startswith('l10n_'):
287                 mod_ids = ir_module.search(cr, uid, [('name','=',chart)])
288                 if mod_ids and ir_module.browse(cr, uid, mod_ids[0], context).state == 'uninstalled':
289                     ir_module.button_immediate_install(cr, uid, mod_ids, context)
290
291             chart_template_ids = chart_template_obj.search(cr, uid, [('visible', '=', True)], context=context)
292             complete_tax_set = chart_template_obj.browse(cr, uid, chart_template_ids[0]).complete_tax_set
293             if not complete_tax_set:
294                 code_digits = multi_chart_obj.onchange_chart_template_id(cr, uid, [], chart_template_ids[0], context=context)['value']['code_digits']
295                 object_id = multi_chart_obj.create(cr, uid, {'code_digits': code_digits , 'sale_tax_rate': res.get('sale_tax_rate'), 'purchase_tax_rate': res.get('purchase_tax_rate')}, context=context)
296                 multi_chart_obj.execute(cr, uid, [object_id], context=context)
297
298     def configure_fiscalyear(self, cr, uid, ids, context=None):
299         if context is None:
300             context = {}
301         fy_obj = self.pool.get('account.fiscalyear')
302         for res in self.read(cr, uid, ids, context=context):
303             if 'date_start' in res and 'date_stop' in res:
304                 f_ids = fy_obj.search(cr, uid, [('date_start', '<=', res['date_start']), ('date_stop', '>=', res['date_stop']), ('company_id', '=', res['company_id'][0])], context=context)
305                 if not f_ids:
306                     name = code = res['date_start'][:4]
307                     if int(name) != int(res['date_stop'][:4]):
308                         name = res['date_start'][:4] +'-'+ res['date_stop'][:4]
309                         code = res['date_start'][2:4] +'-'+ res['date_stop'][2:4]
310                     vals = {
311                         'name': name,
312                         'code': code,
313                         'date_start': res['date_start'],
314                         'date_stop': res['date_stop'],
315                         'company_id': res['company_id'][0]
316                     }
317                     fiscal_id = fy_obj.create(cr, uid, vals, context=context)
318                     if res['period'] == 'month':
319                         fy_obj.create_period(cr, uid, [fiscal_id])
320                     elif res['period'] == '3months':
321                         fy_obj.create_period3(cr, uid, [fiscal_id])
322
323 account_configuration()
324
325 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: