[REF] hr_expense, creation of accounting entries from hr.expense: a lot of code refac...
[odoo/odoo.git] / addons / account / account.py
index 1588110..2aab34f 100644 (file)
 #
 ##############################################################################
 
-import time
+import logging
 from datetime import datetime
 from dateutil.relativedelta import relativedelta
 from operator import itemgetter
+import time
 
-import logging
-import pooler
-from osv import fields, osv
-import decimal_precision as dp
-from tools.translate import _
-from tools.float_utils import float_round
 from openerp import SUPERUSER_ID
-import tools
+from openerp import pooler, tools
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+from openerp.tools.float_utils import float_round
+
+import openerp.addons.decimal_precision as dp
 
 _logger = logging.getLogger(__name__)
 
@@ -75,8 +75,8 @@ class account_payment_term(osv.osv):
         amount = value
         result = []
         obj_precision = self.pool.get('decimal.precision')
+        prec = obj_precision.precision_get(cr, uid, 'Account')
         for line in pt.line_ids:
-            prec = obj_precision.precision_get(cr, uid, 'Account')
             if line.value == 'fixed':
                 amt = round(line.value_amount, prec)
             elif line.value == 'procent':
@@ -92,19 +92,20 @@ class account_payment_term(osv.osv):
                     next_date += relativedelta(day=line.days2, months=1)
                 result.append( (next_date.strftime('%Y-%m-%d'), amt) )
                 amount -= amt
-        return result
 
-account_payment_term()
+        amount = reduce(lambda x,y: x+y[1], result, 0.0)
+        dist = round(value-amount, prec)
+        if dist:
+            result.append( (time.strftime('%Y-%m-%d'), dist) )
+        return result
 
 class account_payment_term_line(osv.osv):
     _name = "account.payment.term.line"
     _description = "Payment Term Line"
     _columns = {
-        'name': fields.char('Line Name', size=32, required=True),
-        'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order the payment term lines from the lowest sequences to the higher ones"),
         'value': fields.selection([('procent', 'Percent'),
                                    ('balance', 'Balance'),
-                                   ('fixed', 'Fixed Amount')], 'Valuation',
+                                   ('fixed', 'Fixed Amount')], 'Computation',
                                    required=True, help="""Select here the kind of valuation related to this payment term line. Note that you should have your last line with the type 'Balance' to ensure that the whole amount will be treated."""),
 
         'value_amount': fields.float('Amount To Pay', digits_compute=dp.get_precision('Payment Term'), help="For percent enter a ratio between 0-1."),
@@ -115,10 +116,10 @@ class account_payment_term_line(osv.osv):
     }
     _defaults = {
         'value': 'balance',
-        'sequence': 5,
+        'days': 30,
         'days2': 0,
     }
-    _order = "sequence"
+    _order = "value desc,days"
 
     def _check_percent(self, cr, uid, ids, context=None):
         obj = self.browse(cr, uid, ids[0], context=context)
@@ -697,44 +698,6 @@ class account_account(osv.osv):
 
 account_account()
 
-class account_journal_view(osv.osv):
-    _name = "account.journal.view"
-    _description = "Journal View"
-    _columns = {
-        'name': fields.char('Journal View', size=64, required=True, translate=True),
-        'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns')
-    }
-    _order = "name"
-
-account_journal_view()
-
-
-class account_journal_column(osv.osv):
-
-    def _col_get(self, cr, user, context=None):
-        result = []
-        cols = self.pool.get('account.move.line')._columns
-        for col in cols:
-            if col in ('period_id', 'journal_id'):
-                continue
-            result.append( (col, cols[col].string) )
-        result.sort()
-        return result
-
-    _name = "account.journal.column"
-    _description = "Journal Column"
-    _columns = {
-        'name': fields.char('Column Name', size=64, required=True),
-        'field': fields.selection(_col_get, 'Field Name', required=True, size=32),
-        'view_id': fields.many2one('account.journal.view', 'Journal View', select=True),
-        'sequence': fields.integer('Sequence', help="Gives the sequence order to journal column.", readonly=True),
-        'required': fields.boolean('Required'),
-        'readonly': fields.boolean('Readonly'),
-    }
-    _order = "view_id, sequence"
-
-account_journal_column()
-
 class account_journal(osv.osv):
     _name = "account.journal"
     _description = "Journal"
@@ -742,7 +705,7 @@ class account_journal(osv.osv):
         'with_last_closing_balance' : fields.boolean('Opening With Last Closing Balance'),
         'name': fields.char('Journal Name', size=64, required=True),
         'code': fields.char('Code', size=5, required=True, help="The code will be displayed on reports."),
-        'type': fields.selection([('sale', 'Sale'),('sale_refund','Sale Refund'), ('purchase', 'Purchase'), ('purchase_refund','Purchase Refund'), ('cash', 'Cash'), ('bank', 'Bank and Cheques'), ('general', 'General'), ('situation', 'Opening/Closing Situation')], 'Type', size=32, required=True,
+        'type': fields.selection([('sale', 'Sale'),('sale_refund','Sale Refund'), ('purchase', 'Purchase'), ('purchase_refund','Purchase Refund'), ('cash', 'Cash'), ('bank', 'Bank and Checks'), ('general', 'General'), ('situation', 'Opening/Closing Situation')], 'Type', size=32, required=True,
                                  help="Select 'Sale' for customer invoices journals."\
                                  " Select 'Purchase' for supplier invoices journals."\
                                  " Select 'Cash' or 'Bank' for journals that are used in customer or supplier payments."\
@@ -750,10 +713,9 @@ class account_journal(osv.osv):
                                  " Select 'Opening/Closing Situation' for entries generated for new fiscal years."),
         'type_control_ids': fields.many2many('account.account.type', 'account_journal_type_rel', 'journal_id','type_id', 'Type Controls', domain=[('code','<>','view'), ('code', '<>', 'closed')]),
         'account_control_ids': fields.many2many('account.account', 'account_account_type_rel', 'journal_id','account_id', 'Account', domain=[('type','<>','view'), ('type', '<>', 'closed')]),
-        'view_id': fields.many2one('account.journal.view', 'Display Mode', required=True, help="Gives the view used when writing or browsing entries in this journal. The view tells OpenERP which fields should be visible, required or readonly and in which order. You can create your own view for a faster encoding in each journal."),
         'default_credit_account_id': fields.many2one('account.account', 'Default Credit Account', domain="[('type','!=','view')]", help="It acts as a default account for credit amount"),
         'default_debit_account_id': fields.many2one('account.account', 'Default Debit Account', domain="[('type','!=','view')]", help="It acts as a default account for debit amount"),
-        'centralisation': fields.boolean('Centralised Counterpart', help="Check this box to determine that each entry of this journal won't create a new counterpart but will share the same counterpart. This is used in fiscal year closing."),
+        'centralisation': fields.boolean('Centralized Counterpart', help="Check this box to determine that each entry of this journal won't create a new counterpart but will share the same counterpart. This is used in fiscal year closing."),
         'update_posted': fields.boolean('Allow Cancelling Entries', help="Check this box if you want to allow the cancellation the entries related to this journal or of the invoice related to this journal"),
         'group_invoice_lines': fields.boolean('Group Invoice Lines', help="If this box is checked, the system will try to group the accounting lines when generating them from invoices."),
         'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="This field contains the information related to the numbering of the journal entries of this journal.", required=True),
@@ -886,37 +848,6 @@ class account_journal(osv.osv):
 
         return self.name_get(cr, user, ids, context=context)
 
-    def onchange_type(self, cr, uid, ids, type, currency, context=None):
-        obj_data = self.pool.get('ir.model.data')
-        user_pool = self.pool.get('res.users')
-
-        type_map = {
-            'sale':'account_sp_journal_view',
-            'sale_refund':'account_sp_refund_journal_view',
-            'purchase':'account_sp_journal_view',
-            'purchase_refund':'account_sp_refund_journal_view',
-            'cash':'account_journal_bank_view',
-            'bank':'account_journal_bank_view',
-            'general':'account_journal_view',
-            'situation':'account_journal_view'
-        }
-
-        res = {}
-        view_id = type_map.get(type, 'account_journal_view')
-        user = user_pool.browse(cr, uid, uid)
-        if type in ('cash', 'bank') and currency and user.company_id.currency_id.id != currency:
-            view_id = 'account_journal_bank_view_multi'
-        data_id = obj_data.search(cr, uid, [('model','=','account.journal.view'), ('name','=',view_id)])
-        data = obj_data.browse(cr, uid, data_id[0], context=context)
-
-        res.update({
-            'centralisation':type == 'situation',
-            'view_id':data.res_id,
-        })
-        return {
-            'value':res
-        }
-
 account_journal()
 
 class account_fiscalyear(osv.osv):
@@ -1083,10 +1014,15 @@ class account_period(osv.osv):
         else:
             company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
             args.append(('company_id', '=', company_id))
-        ids = self.search(cr, uid, args, context=context)
-        if not ids:
-            raise osv.except_osv(_('Error!'), _('There is no period defined for this date: %s.\nPlease create one.')%dt)
-        return ids
+        result = []
+        if context.get('account_period_prefer_normal'):
+            # look for non-special periods first, and fallback to all if no result is found
+            result = self.search(cr, uid, args + [('special', '=', False)], context=context)
+        if not result:
+            result = self.search(cr, uid, args, context=context)
+        if not result:
+            raise osv.except_osv(_('Error !'), _('There is no period defined for this date: %s.\nPlease create one.')%dt)
+        return result
 
     def action_draft(self, cr, uid, ids, *args):
         mode = 'draft'
@@ -1152,7 +1088,7 @@ class account_journal_period(osv.osv):
         'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade"),
         'period_id': fields.many2one('account.period', 'Period', required=True, ondelete="cascade"),
         'icon': fields.function(_icon_get, string='Icon', type='char', size=32),
-        'active': fields.boolean('Active', required=True, help="If the active field is set to False, it will allow you to hide the journal period without removing it."),
+        'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the journal period without removing it."),
         'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True,
                                   help='When journal period is created. The status is \'Draft\'. If a report is printed it comes to \'Printed\' status. When all transactions are done, it comes in \'Done\' status.'),
         'fiscalyear_id': fields.related('period_id', 'fiscalyear_id', string='Fiscal Year', type='many2one', relation='account.fiscalyear'),
@@ -1214,6 +1150,29 @@ class account_move(osv.osv):
     _description = "Account Entry"
     _order = 'id desc'
 
+    def account_move_prepare(self, cr, uid, journal_id, date=False, ref='', company_id=False, context=None):
+        '''
+        Prepares and returns a dictionary of values, ready to be passed to create() based on the parameters received.
+        '''
+        if not date:
+            date = fields.date.today()
+        period_obj = self.pool.get('account.period')
+        if not company_id:
+            user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+            company_id = user.company_id.id
+        if context is None:
+            context = {}
+        #put the company in context to find the good period
+        ctx = context.copy()
+        ctx.update({'company_id': company_id})
+        return {
+            'journal_id': journal_id,
+            'date': date,
+            'period_id': period_obj.find(cr, uid, date, context=ctx)[0],
+            'ref': ref,
+            'company_id': company_id,
+        }
+
     def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80):
         """
         Returns a list of tupples containing id, name, as internally it is called {def name_get}
@@ -1260,10 +1219,9 @@ class account_move(osv.osv):
         return res
 
     def _get_period(self, cr, uid, context=None):
-        periods = self.pool.get('account.period').find(cr, uid)
-        if periods:
-            return periods[0]
-        return False
+        ctx = dict(context or {}, account_period_prefer_normal=True)
+        period_ids = self.pool.get('account.period').find(cr, uid, context=ctx)
+        return period_ids[0]
 
     def _amount_compute(self, cr, uid, ids, name, args, context, where =''):
         if not ids: return {}
@@ -1396,13 +1354,6 @@ class account_move(osv.osv):
                        'WHERE id IN %s', ('draft', tuple(ids),))
         return True
 
-    def onchange_line_id(self, cr, uid, ids, line_ids, context=None):
-        balance = 0.0
-        for line in line_ids:
-            if line[2]:
-                balance += (line[2].get('debit',0.00)- (line[2].get('credit',0.00)))
-        return {'value': {'balance': balance}}
-
     def write(self, cr, uid, ids, vals, context=None):
         if context is None:
             context = {}
@@ -1451,9 +1402,9 @@ class account_move(osv.osv):
         if 'line_id' in vals:
             c = context.copy()
             c['novalidate'] = True
-            c['period_id'] = vals['period_id']
+            c['period_id'] = vals['period_id'] if 'period_id' in vals else self._get_period(cr, uid, context)
             c['journal_id'] = vals['journal_id']
-            c['date'] = vals['date']
+            if 'date' in vals: c['date'] = vals['date']
             result = super(account_move, self).create(cr, uid, vals, c)
             self.validate(cr, uid, [result], context)
         else:
@@ -1535,6 +1486,7 @@ class account_move(osv.osv):
             line_id = self.pool.get('account.move.line').create(cr, uid, {
                 'name': _(mode.capitalize()+' Centralisation'),
                 'centralisation': mode,
+                'partner_id': False,
                 'account_id': account_id,
                 'move_id': move.id,
                 'journal_id': move.journal_id.id,
@@ -1573,6 +1525,7 @@ class account_move(osv.osv):
                     line_id = self.pool.get('account.move.line').create(cr, uid, {
                         'name': _('Currency Adjustment'),
                         'centralisation': 'currency',
+                        'partner_id': False,
                         'account_id': account_id,
                         'move_id': move.id,
                         'journal_id': move.journal_id.id,
@@ -2381,8 +2334,13 @@ class account_model(osv.osv):
                     if not line.partner_id:
                         raise osv.except_osv(_('Error!'), _("Maturity date of entry line generated by model line '%s' of model '%s' is based on partner payment term!" \
                                                                 "\nPlease define partner on it!")%(line.name, model.name))
-                    if line.partner_id.property_payment_term:
+
+                    payment_term_id = False
+                    if model.journal_id.type in ('purchase', 'purchase_refund') and line.partner_id.property_supplier_payment_term:
+                        payment_term_id = line.partner_id.property_supplier_payment_term.id
+                    elif line.partner_id.property_payment_term:
                         payment_term_id = line.partner_id.property_payment_term.id
+                    if payment_term_id:
                         pterm_list = pt_obj.compute(cr, uid, payment_term_id, value=1, date_ref=date_maturity)
                         if pterm_list:
                             pterm_list = [l[0] for l in pterm_list]
@@ -3208,16 +3166,6 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                     default_account = acc_template_ref.get(template.property_account_income_opening.id)
             return default_account
 
-        def _get_view_id(journal_type):
-            # Get the journal views
-            if journal_type in ('general', 'situation'):
-                data = obj_data.get_object_reference(cr, uid, 'account', 'account_journal_view')
-            elif journal_type in ('sale_refund', 'purchase_refund'):
-                data = obj_data.get_object_reference(cr, uid, 'account', 'account_sp_refund_journal_view')
-            else:
-                data = obj_data.get_object_reference(cr, uid, 'account', 'account_sp_journal_view')
-            return data and data[1] or False
-
         journal_names = {
             'sale': _('Sales Journal'),
             'purchase': _('Purchase Journal'),
@@ -3247,7 +3195,6 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                 'code': journal_codes[journal_type],
                 'company_id': company_id,
                 'centralisation': journal_type == 'situation',
-                'view_id': _get_view_id(journal_type),
                 'analytic_journal_id': _get_analytic_journal(journal_type),
                 'default_credit_account_id': _get_default_account(journal_type, 'credit'),
                 'default_debit_account_id': _get_default_account(journal_type, 'debit'),
@@ -3430,10 +3377,25 @@ class wizard_multi_charts_accounts(osv.osv_memory):
         all the provided information to create the accounts, the banks, the journals, the taxes, the tax codes, the
         accounting properties... accordingly for the chosen company.
         '''
+        obj_data = self.pool.get('ir.model.data')
         ir_values_obj = self.pool.get('ir.values')
         obj_wizard = self.browse(cr, uid, ids[0])
         company_id = obj_wizard.company_id.id
+
         self.pool.get('res.company').write(cr, uid, [company_id], {'currency_id': obj_wizard.currency_id.id})
+
+        # When we install the CoA of first company, set the currency to price types and pricelists
+        if company_id==1:
+            for ref in (('product','list_price'),('product','standard_price'),('product','list0'),('purchase','list0')):
+                try:
+                    tmp2 = obj_data.get_object_reference(cr, uid, *ref)
+                    if tmp2: 
+                        self.pool.get(tmp2[0]).write(cr, uid, tmp2[1], {
+                            'currency_id': obj_wizard.currency_id.id
+                        })
+                except ValueError, e:
+                    pass
+
         # If the floats for sale/purchase rates have been filled, create templates from them
         self._create_tax_templates_from_rates(cr, uid, obj_wizard, company_id, context=context)
 
@@ -3464,11 +3426,7 @@ class wizard_multi_charts_accounts(osv.osv_memory):
         '''
         obj_data = self.pool.get('ir.model.data')
         obj_journal = self.pool.get('account.journal')
-        # Get the id of journal views
-        tmp = obj_data.get_object_reference(cr, uid, 'account', 'account_journal_bank_view_multi')
-        view_id_cur = tmp and tmp[1] or False
-        tmp = obj_data.get_object_reference(cr, uid, 'account', 'account_journal_bank_view')
-        view_id_cash = tmp and tmp[1] or False
+        
 
         # we need to loop again to find next number for journal code
         # because we can't rely on the value current_num as,
@@ -3494,10 +3452,8 @@ class wizard_multi_charts_accounts(osv.osv_memory):
                 'default_debit_account_id': default_account_id,
         }
         if line['currency_id']:
-            vals['view_id'] = view_id_cur
             vals['currency'] = line['currency_id']
-        else:
-            vals['view_id'] = view_id_cash
+        
         return vals
 
     def _prepare_bank_account(self, cr, uid, line, new_code, acc_template_ref, ref_acc_bank, company_id, context=None):
@@ -3588,7 +3544,7 @@ class account_bank_accounts_wizard(osv.osv_memory):
 
     _columns = {
         'acc_name': fields.char('Account Name.', size=64, required=True),
-        'bank_account_id': fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True),
+        'bank_account_id': fields.many2one('wizard.multi.charts.accounts', 'Bank Account', required=True, ondelete='cascade'),
         'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Forces all moves for this account to have this secondary currency."),
         'account_type': fields.selection([('cash','Cash'), ('check','Check'), ('bank','Bank')], 'Account Type', size=32),
     }