1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Business Applications
5 # Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
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.
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.
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/>.
20 ##############################################################################
24 from dateutil.relativedelta import relativedelta
25 from operator import itemgetter
26 from os.path import join as opj
28 from tools.translate import _
29 from osv import osv, fields
32 class account_config_settings(osv.osv_memory):
33 _name = 'account.config.settings'
34 _inherit = 'res.config.settings'
37 'company_id': fields.many2one('res.company', 'Company', required=True),
38 'has_default_company': fields.boolean('Has default company', readonly=True),
39 'expects_chart_of_accounts': fields.related('company_id', 'expects_chart_of_accounts', type='boolean',
40 string='This Company Has its Own Chart of Accounts',
41 help="""Check this box if this company is a legal entity."""),
42 'currency_id': fields.related('company_id', 'currency_id', type='many2one', relation='res.currency', required=True,
43 string='Default Company Currency', help="Main currency of the company."),
44 'paypal_account': fields.related('company_id', 'paypal_account', type='char', size=128,
45 string='Paypal Account', help="Paypal account (email) for receiving online payments (credit card, etc.) If you set a paypal account, the customer will be able to pay your invoices or quotations with a button \"Pay with Paypal\" in automated emails or through the OpenERP portal."),
46 'company_footer': fields.related('company_id', 'rml_footer2', type='char', size=250, readonly=True,
47 string='Bank Accounts on Reports', help="Bank accounts as printed in the footer of each customer document. This is for information purpose only, you should configure these bank accounts through the above button \"Configure Bank Accounts\"."),
49 'has_chart_of_accounts': fields.boolean('Company has a chart of accounts'),
50 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', domain="[('visible','=', True)]"),
51 'code_digits': fields.integer('# of Digits', help="No. of Digits to use for account code"),
52 'sale_tax': fields.many2one("account.tax.template", "Default Sale Tax"),
53 'purchase_tax': fields.many2one("account.tax.template", "Default Purchase Tax"),
54 'sale_tax_rate': fields.float('Sales Tax (%)'),
55 'purchase_tax_rate': fields.float('Purchase Tax (%)'),
56 'complete_tax_set': fields.boolean('Complete Set of Taxes', help='This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete'),
58 'has_fiscal_year': fields.boolean('Company has a fiscal year'),
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),
63 'sale_journal_id': fields.many2one('account.journal', 'Sale Journal'),
64 'sale_sequence_prefix': fields.related('sale_journal_id', 'sequence_id', 'prefix', type='char', string='Invoice Sequence'),
65 'sale_sequence_next': fields.related('sale_journal_id', 'sequence_id', 'number_next', type='integer', string='Next Invoice Number'),
66 'sale_refund_journal_id': fields.many2one('account.journal', 'Sale Refund Journal'),
67 'sale_refund_sequence_prefix': fields.related('sale_refund_journal_id', 'sequence_id', 'prefix', type='char', string='Credit Note Sequence'),
68 'sale_refund_sequence_next': fields.related('sale_refund_journal_id', 'sequence_id', 'number_next', type='integer', string='Next Credit Note Number'),
69 'purchase_journal_id': fields.many2one('account.journal', 'Purchase Journal'),
70 'purchase_sequence_prefix': fields.related('purchase_journal_id', 'sequence_id', 'prefix', type='char', string='Supplier Invoice Sequence'),
71 'purchase_sequence_next': fields.related('purchase_journal_id', 'sequence_id', 'number_next', type='integer', string='Next Supplier Invoice Number'),
72 'purchase_refund_journal_id': fields.many2one('account.journal', 'Purchase Refund Journal'),
73 'purchase_refund_sequence_prefix': fields.related('purchase_refund_journal_id', 'sequence_id', 'prefix', type='char', string='Supplier Credit Note Sequence'),
74 'purchase_refund_sequence_next': fields.related('purchase_refund_journal_id', 'sequence_id', 'number_next', type='integer', string='Next Supplier Credit Note Number'),
76 'module_account_check_writing': fields.boolean('Check Writing',
77 help="""This allows you to check writing and printing.
78 This installs the module account_check_writing."""),
79 'module_account_accountant': fields.boolean('Accountant Features',
80 help="""If you do not check this box, you will be able to do invoicing & payments, but not accounting (Journal Items, Chart of Accounts, ...)"""),
81 'module_account_asset': fields.boolean('Assets Management',
82 help="""This allows you to manage the assets owned by a company or a person.
83 It keeps track of the depreciation occurred on those assets, and creates account move for those depreciation lines.
84 This installs the module account_asset. If you do not check this box, you will be able to do invoicing & payments,
85 but not accounting (Journal Items, Chart of Accounts, ...)"""),
86 'module_account_budget': fields.boolean('Budget Management',
87 help="""This allows accountants to manage analytic and crossovered budgets.
88 Once the master budgets and the budgets are defined,
89 the project managers can set the planned amount on each analytic account.
90 This installs the module account_budget."""),
91 'module_account_payment': fields.boolean('Manage Payment Orders',
92 help="""This allows you to create and manage your payment orders, with purposes to
93 * serve as base for an easy plug-in of various automated payment mechanisms, and
94 * provide a more efficient way to manage invoice payments.
95 This installs the module account_payment."""),
96 'module_account_voucher': fields.boolean('Manage Customer Payments',
97 help="""This includes all the basic requirements of voucher entries for bank, cash, sales, purchase, expense, contra, etc.
98 This installs the module account_voucher."""),
99 'module_account_followup': fields.boolean('Manage Customer Payment Follow-Ups',
100 help="""This allows to automate letters for unpaid invoices, with multi-level recalls.
101 This installs the module account_followup."""),
102 'module_account_invoice_layout': fields.boolean('Allow Notes and Subtotals',
103 help="""This provides some features to improve the layout of invoices.
104 It gives you the possibility to:
105 * order all the lines of an invoice
106 * add titles, comment lines, sub total lines
107 * draw horizontal lines and put page breaks.
108 This installs the module account_invoice_layout."""),
110 'group_proforma_invoices': fields.boolean('Allow Pro-forma Invoices',
111 implied_group='account.group_proforma_invoices',
112 help="Allows you to put invoices in pro-forma state."),
113 'default_sale_tax': fields.many2one('account.tax', 'Default Sale Tax',
114 help="This sale tax will be assigned by default on new products."),
115 'default_purchase_tax': fields.many2one('account.tax', 'Default Purchase Tax',
116 help="This purchase tax will be assigned by default on new products."),
117 'decimal_precision': fields.integer('Decimal Precision on Journal Entries',
118 help="""As an example, a decimal precision of 2 will allow journal entries like: 9.99 EUR, whereas a decimal precision of 4 will allow journal entries like: 0.0231 EUR."""),
121 def _default_company(self, cr, uid, context=None):
122 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
123 return user.company_id.id
125 def _default_has_default_company(self, cr, uid, context=None):
126 count = self.pool.get('res.company').search_count(cr, uid, [], context=context)
127 return bool(count == 1)
130 'company_id': _default_company,
131 'has_default_company': _default_has_default_company,
132 'date_start': lambda *a: time.strftime('%Y-01-01'),
133 'date_stop': lambda *a: time.strftime('%Y-12-31'),
137 def create(self, cr, uid, values, context=None):
138 id = super(account_config_settings, self).create(cr, uid, values, context)
139 # Hack: to avoid some nasty bug, related fields are not written upon record creation.
140 # Hence we write on those fields here.
142 for fname, field in self._columns.iteritems():
143 if isinstance(field, fields.related) and fname in values:
144 vals[fname] = values[fname]
145 self.write(cr, uid, [id], vals, context)
148 def onchange_company_id(self, cr, uid, ids, company_id):
149 # update related fields
150 company = self.pool.get('res.company').browse(cr, uid, company_id)
151 has_chart_of_accounts = company_id not in self.pool.get('account.installer').get_unconfigured_cmp(cr, uid)
152 fiscalyear_count = self.pool.get('account.fiscalyear').search_count(cr, uid,
153 [('date_start', '<=', time.strftime('%Y-%m-%d')), ('date_stop', '>=', time.strftime('%Y-%m-%d')),
154 ('company_id', '=', company_id)])
156 'expects_chart_of_accounts': company.expects_chart_of_accounts,
157 'currency_id': company.currency_id.id,
158 'paypal_account': company.paypal_account,
159 'company_footer': company.rml_footer2,
160 'has_chart_of_accounts': has_chart_of_accounts,
161 'has_fiscal_year': bool(fiscalyear_count),
162 'chart_template_id': False,
164 # update journals and sequences
165 for journal_type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
166 for suffix in ('_journal_id', '_sequence_prefix', '_sequence_next'):
167 values[journal_type + suffix] = False
168 journal_obj = self.pool.get('account.journal')
169 journal_ids = journal_obj.search(cr, uid, [('company_id', '=', company_id)])
170 for journal in journal_obj.browse(cr, uid, journal_ids):
171 if journal.type in ('sale', 'sale_refund', 'purchase', 'purchase_refund'):
173 journal.type + '_journal_id': journal.id,
174 journal.type + '_sequence_prefix': journal.sequence_id.prefix,
175 journal.type + '_sequence_next': journal.sequence_id.number_next,
178 ir_values = self.pool.get('ir.values')
179 taxes_id = ir_values.get_default(cr, uid, 'product.product', 'taxes_id', company_id=company_id)
180 supplier_taxes_id = ir_values.get_default(cr, uid, 'product.product', 'supplier_taxes_id', company_id=company_id)
182 'default_sale_tax': isinstance(taxes_id, list) and taxes_id[0] or taxes_id,
183 'default_purchase_tax': isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id,
185 return {'value': values}
187 def onchange_chart_template_id(self, cr, uid, ids, chart_template_id, context=None):
188 tax_templ_obj = self.pool.get('account.tax.template')
190 'complete_tax_set': False, 'sale_tax': False, 'purchase_tax': False,
191 'sale_tax_rate': 15, 'purchase_tax_rate': 15,
193 if chart_template_id:
194 # update complete_tax_set, sale_tax and purchase_tax
195 chart_template = self.pool.get('account.chart.template').browse(cr, uid, chart_template_id, context=context)
196 res['value'].update({'complete_tax_set': chart_template.complete_tax_set})
197 if chart_template.complete_tax_set:
198 # default tax is given by the lowest sequence. For same sequence we will take the latest created as it will be the case for tax created while isntalling the generic chart of account
199 sale_tax_ids = tax_templ_obj.search(cr, uid,
200 [("chart_template_id", "=", chart_template_id), ('type_tax_use', 'in', ('sale','all'))],
201 order="sequence, id desc")
202 purchase_tax_ids = tax_templ_obj.search(cr, uid,
203 [("chart_template_id", "=", chart_template_id), ('type_tax_use', 'in', ('purchase','all'))],
204 order="sequence, id desc")
205 res['value']['sale_tax'] = sale_tax_ids and sale_tax_ids[0] or False
206 res['value']['purchase_tax'] = purchase_tax_ids and purchase_tax_ids[0] or False
207 if chart_template.code_digits:
208 res['value']['code_digits'] = chart_template.code_digits
211 def onchange_tax_rate(self, cr, uid, ids, rate, context=None):
212 return {'value': {'purchase_tax_rate': rate or False}}
214 def onchange_start_date(self, cr, uid, id, start_date):
216 start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")
217 end_date = (start_date + relativedelta(months=12)) - relativedelta(days=1)
218 return {'value': {'date_stop': end_date.strftime('%Y-%m-%d')}}
221 def set_default_taxes(self, cr, uid, ids, context=None):
222 """ set default sale and purchase taxes for products """
223 ir_values = self.pool.get('ir.values')
224 config = self.browse(cr, uid, ids[0], context)
225 ir_values.set_default(cr, uid, 'product.product', 'taxes_id',
226 config.default_sale_tax and [config.default_sale_tax.id] or False, company_id=config.company_id.id)
227 ir_values.set_default(cr, uid, 'product.product', 'supplier_taxes_id',
228 config.default_purchase_tax and [config.default_purchase_tax.id] or False, company_id=config.company_id.id)
230 def set_chart_of_accounts(self, cr, uid, ids, context=None):
231 """ install a chart of accounts for the given company (if required) """
232 config = self.browse(cr, uid, ids[0], context)
233 if config.chart_template_id:
234 assert config.expects_chart_of_accounts and not config.has_chart_of_accounts
235 wizard = self.pool.get('wizard.multi.charts.accounts')
236 wizard_id = wizard.create(cr, uid, {
237 'company_id': config.company_id.id,
238 'chart_template_id': config.chart_template_id.id,
239 'code_digits': config.code_digits or 6,
240 'sale_tax': config.sale_tax.id,
241 'purchase_tax': config.purchase_tax.id,
242 'sale_tax_rate': config.sale_tax_rate,
243 'purchase_tax_rate': config.purchase_tax_rate,
244 'complete_tax_set': config.complete_tax_set,
246 wizard.execute(cr, uid, [wizard_id], context)
248 def set_fiscalyear(self, cr, uid, ids, context=None):
249 """ create a fiscal year for the given company (if necessary) """
250 config = self.browse(cr, uid, ids[0], context)
251 if config.has_chart_of_accounts or config.chart_template_id:
252 fiscalyear = self.pool.get('account.fiscalyear')
253 fiscalyear_count = fiscalyear.search_count(cr, uid,
254 [('date_start', '<=', config.date_start), ('date_stop', '>=', config.date_stop),
255 ('company_id', '=', config.company_id.id)],
257 if not fiscalyear_count:
258 name = code = config.date_start[:4]
259 if int(name) != int(config.date_stop[:4]):
260 name = config.date_start[:4] +'-'+ config.date_stop[:4]
261 code = config.date_start[2:4] +'-'+ config.date_stop[2:4]
265 'date_start': config.date_start,
266 'date_stop': config.date_stop,
267 'company_id': config.company_id.id,
269 fiscalyear_id = fiscalyear.create(cr, uid, vals, context=context)
270 if config.period == 'month':
271 fiscalyear.create_period(cr, uid, [fiscalyear_id])
272 elif config.period == '3months':
273 fiscalyear.create_period3(cr, uid, [fiscalyear_id])
275 def get_default_dp(self, cr, uid, fields, context=None):
276 dp = self.pool.get('ir.model.data').get_object(cr, uid, 'product','decimal_account')
277 return {'decimal_precision': dp.digits}
279 def set_default_dp(self, cr, uid, ids, context=None):
280 config = self.browse(cr, uid, ids[0], context)
281 dp = self.pool.get('ir.model.data').get_object(cr, uid, 'product','decimal_account')
282 dp.write({'digits': config.decimal_precision})
284 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: