[REF] hr_expense, creation of accounting entries from hr.expense: a lot of code refactoring. Still a huge work to be done in order to factorize some code with other objects (like account.invoice, account.voucher, account.asset...)

bzr revid: qdp-launchpad@openerp.com-20130306140554-fhs6fhkyb779t6re
This commit is contained in:
Quentin (OpenERP) 2013-03-06 15:05:54 +01:00
parent 804bc3a916
commit cdabf79cf2
4 changed files with 79 additions and 211 deletions

View File

@ -1150,6 +1150,29 @@ class account_move(osv.osv):
_description = "Account Entry" _description = "Account Entry"
_order = 'id desc' _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): 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} Returns a list of tupples containing id, name, as internally it is called {def name_get}

View File

@ -46,7 +46,7 @@ This module also uses analytic accounting and is compatible with the invoice on
'author': 'OpenERP SA', 'author': 'OpenERP SA',
'website': 'http://www.openerp.com', 'website': 'http://www.openerp.com',
'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'], 'images': ['images/hr_expenses_analysis.jpeg', 'images/hr_expenses.jpeg'],
'depends': ['hr', 'account_voucher'], 'depends': ['hr', 'account_voucher, account_accountant'],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'hr_expense_data.xml', 'hr_expense_data.xml',

View File

@ -145,8 +145,7 @@ class hr_expense_expense(osv.osv):
def expense_canceled(self, cr, uid, ids, context=None): def expense_canceled(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context) return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
def account_move_get(self, cr, uid, expense_id, context=None):
def account_move_get(self, cr, uid, expense_id, journal_id, context=None):
''' '''
This method prepare the creation of the account move related to the given expense. This method prepare the creation of the account move related to the given expense.
@ -154,53 +153,27 @@ class hr_expense_expense(osv.osv):
:return: mapping between fieldname and value of account move to create :return: mapping between fieldname and value of account move to create
:rtype: dict :rtype: dict
''' '''
journal_obj = self.pool.get('account.journal')
expense = self.browse(cr, uid, expense_id, context=context)
#Search for the period corresponding with confirmation date company_id = expense.company_id.id
expense_brw = self.browse(cr,uid,expense_id,context) date = expense.date_confirm
period_obj = self.pool.get('account.period') ref = expense.name
company_id = expense_brw.company_id.id journal_id = False
ctx = context if expense.journal_id:
ctx.update({'company_id': company_id}) journal_id = expense.journal_id.id
date = expense_brw.date_confirm
pids = period_obj.find(cr, uid, date, context=ctx)
try:
period_id = pids[0]
except:
raise osv.except_osv(_('Error! '),
_('Please define periods!'))
period = period_obj.browse(cr, uid, period_id, context=context)
seq_obj = self.pool.get('ir.sequence')
journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
if journal.sequence_id:
if not journal.sequence_id.active:
raise osv.except_osv(_('Configuration Error !'),
_('Please activate the sequence of selected journal !'))
c = dict(context)
c.update({'fiscalyear_id': period.fiscalyear_id.id})
name = seq_obj.next_by_id(cr, uid, journal.sequence_id.id, context=c)
else: else:
raise osv.except_osv(_('Error!'), journal_id = journal_obj.search(cr, uid, [('type', '=', 'purchase'), ('company_id', '=', company_id)])
_('Please define a sequence on the journal.')) if not journal_id:
#Look for the next expense number raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
ref = seq_obj.get(cr, uid, 'hr.expense.invoice') journal_id = journal_id[0]
return self.pool.get('account_move').account_move_prepare(cr, uid, journal_id, date=date, ref=ref, company_id=company_id, context=context)
move = {
'name': name,
'journal_id': journal_id,
'narration': '',
'date': expense_brw.date_confirm,
'ref': ref,
'period_id': period_id,
}
return move
def line_get_convert(self, cr, uid, x, part, date, context=None): def line_get_convert(self, cr, uid, x, part, date, context=None):
#partner_id = self.pool.get('res.partner')._find_partner(part)
partner_id = part.id
return { return {
'date_maturity': x.get('date_maturity', False), 'date_maturity': x.get('date_maturity', False),
'partner_id': part.id, 'partner_id': partner_id,
'name': x['name'][:64], 'name': x['name'][:64],
'date': date, 'date': date,
'debit': x['price']>0 and x['price'], 'debit': x['price']>0 and x['price'],
@ -218,108 +191,65 @@ class hr_expense_expense(osv.osv):
'analytic_account_id': x.get('account_analytic_id', False), 'analytic_account_id': x.get('account_analytic_id', False),
} }
def compute_expense_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None): def compute_expense_totals(self, cr, uid, exp, company_currency, ref, account_move_lines, context=None):
'''
internal method used for computation of total amount of an expense in the company currency and
in the expense currency, given the account_move_lines that will be created. It also do some small
transformations at these account_move_lines (for multi-currency purposes)
:param account_move_lines: list of dict
:rtype: tuple of 3 elements (a, b ,c)
a: total in company currency
b: total in hr.expense currency
c: account_move_lines potentially modified
'''
cur_obj = self.pool.get('res.currency')
if context is None: if context is None:
context={} context={}
total = 0 context.update({'date': exp.date_confirm or time.strftime('%Y-%m-%d')})
total_currency = 0 total = 0.0
cur_obj = self.pool.get('res.currency') total_currency = 0.0
for i in invoice_move_lines: for i in account_move_lines:
if inv.currency_id.id != company_currency: if exp.currency_id.id != company_currency:
context.update({'date': inv.date_confirm or time.strftime('%Y-%m-%d')}) i['currency_id'] = exp.currency_id.id
i['currency_id'] = inv.currency_id.id
i['amount_currency'] = i['price'] i['amount_currency'] = i['price']
i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, i['price'] = cur_obj.compute(cr, uid, exp.currency_id.id,
company_currency, i['price'], company_currency, i['price'],
context=context) context=context)
else: else:
i['amount_currency'] = False i['amount_currency'] = False
i['currency_id'] = False i['currency_id'] = False
i['ref'] = ref
total -= i['price'] total -= i['price']
total_currency -= i['amount_currency'] or i['price'] total_currency -= i['amount_currency'] or i['price']
return total, total_currency, invoice_move_lines return total, total_currency, account_move_lines
def action_move_create(self, cr, uid, ids, context=None): def action_move_create(self, cr, uid, ids, context=None):
property_obj = self.pool.get('ir.property')
sequence_obj = self.pool.get('ir.sequence')
analytic_journal_obj = self.pool.get('account.analytic.journal')
account_journal = self.pool.get('account.journal')
voucher_obj = self.pool.get('account.voucher')
currency_obj = self.pool.get('res.currency')
ait_obj = self.pool.get('account.invoice.tax')
move_obj = self.pool.get('account.move') move_obj = self.pool.get('account.move')
if context is None: if context is None:
context = {} context = {}
for exp in self.browse(cr, uid, ids, context=context): for exp in self.browse(cr, uid, ids, context=context):
company_id = exp.company_id.id
lines = []
total = 0.0
ctx = context.copy()
ctx.update({'date': exp.date})
journal = False
if exp.journal_id:
journal = exp.journal_id
else:
journal_id = voucher_obj._get_journal(cr, uid, context={'type': 'purchase', 'company_id': company_id})
if journal_id:
journal = account_journal.browse(cr, uid, journal_id, context=context)
if not journal:
raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
if not journal.sequence_id:
raise osv.except_osv(_('Error!'), _('Please define sequence on the journal related to this invoice.'))
company_currency = exp.company_id.currency_id.id
current_currency = exp.currency_id
# for line in exp.line_ids:
# if line.product_id:
# acc = line.product_id.property_account_expense
# if not acc:
# acc = line.product_id.categ_id.property_account_expense_categ
# else:
# acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id})
# if not acc:
# raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'))
# total_amount = line.total_amount
# if journal.currency:
# if exp.currency_id != journal.currency:
# total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, journal.currency.id, total_amount, context=ctx)
# elif exp.currency_id != exp.company_id.currency_id:
# total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, exp.company_id.currency_id.id, total_amount, context=ctx)
# lines.append((0, False, {
# 'name': line.name,
# 'account_id': acc.id,
# 'account_analytic_id': line.analytic_account.id,
# 'amount': total_amount,
# 'type': 'dr'
# }))
# total += total_amount
if not exp.employee_id.address_home_id: if not exp.employee_id.address_home_id:
raise osv.except_osv(_('Error!'), _('The employee must have a home address.')) raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
acc = exp.employee_id.address_home_id.property_account_payable.id company_currency = exp.company_id.currency_id.id
diff_currency_p = exp.currency_id.id <> company_currency
#From action_move_line_create of voucher #create the move that will contain the accounting entries
move_id = move_obj.create(cr, uid, self.account_move_get(cr, uid, exp.id, journal.id, context=context), context=context) move_id = move_obj.create(cr, uid, self.account_move_get(cr, uid, exp.id, context=context), context=context)
move = move_obj.browse(cr, uid, move_id, context=context)
#iml = self._get_analytic_lines #iml = self._get_analytic_lines
# within: iml = self.pool.get('account.invoice.line').move_line_get # within: iml = self.pool.get('account.invoice.line').move_line_get
iml = self.move_line_get(cr, uid, exp.id, context=context) iml = self.move_line_get(cr, uid, exp.id, context=context)
diff_currency_p = exp.currency_id.id <> company_currency
# create one move line for the total # create one move line for the total
total = 0 total, total_currency, iml = self.compute_expense_totals(cr, uid, exp, company_currency, exp.name, iml, context=context)
total_currency = 0
total, total_currency, iml = self.compute_expense_totals(cr, uid, exp, company_currency, exp.name, iml, context=ctx)
#Need to have counterline:
#counterline with the total on payable account for the employee
acc = exp.employee_id.address_home_id.property_account_payable.id
iml.append({ iml.append({
'type':'dest', 'type': 'dest',
'name':'/', 'name': '/',
'price':total, 'price': total,
'account_id': acc, 'account_id': acc,
'date_maturity': exp.date_confirm, 'date_maturity': exp.date_confirm,
'amount_currency': diff_currency_p and total_currency or False, 'amount_currency': diff_currency_p and total_currency or False,
@ -327,27 +257,10 @@ class hr_expense_expense(osv.osv):
'ref': exp.name 'ref': exp.name
}) })
line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.user_id.partner_id, exp.date_confirm, context=ctx)),iml) lines = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.user_id.partner_id, exp.date_confirm, context=context)),iml)
move_obj.write(cr, uid, [move_id], {'line_id':line}, context=ctx) move_obj.write(cr, uid, [move_id], {'line_id': lines}, context=context)
self.write(cr, uid, ids, {'account_move_id':move_id, 'state':'done'}, context=context) self.write(cr, uid, ids, {'account_move_id': move_id, 'state': 'done'}, context=context)
return True
#compute_taxes = ait_obj.compute(cr, uid, , context=context)
# return {
# 'type':'src',
# 'name':line.name.split('\n')[0][:64],
# 'price_unit':line.price_unit,
# 'quantity':line.quantity,
# 'price':line.price_subtotal,
# 'account_id':line.account_id.id,
# 'product_id':line.product_id.id,
# 'uos_id':line.uos_id.id,
# 'account_analytic_id':line.account_analytic_id.id,
# 'taxes':line.invoice_line_tax_id}
def move_line_get(self, cr, uid, expense_id, context=None): def move_line_get(self, cr, uid, expense_id, context=None):
res = [] res = []
@ -442,80 +355,11 @@ class hr_expense_expense(osv.osv):
} }
def action_receipt_create(self, cr, uid, ids, context=None): def action_receipt_create(self, cr, uid, ids, context=None):
property_obj = self.pool.get('ir.property') raise osv.except_osv(_('Error!'), _('Deprecated function used'))
sequence_obj = self.pool.get('ir.sequence')
analytic_journal_obj = self.pool.get('account.analytic.journal')
account_journal = self.pool.get('account.journal')
voucher_obj = self.pool.get('account.voucher')
currency_obj = self.pool.get('res.currency')
wkf_service = netsvc.LocalService("workflow")
if context is None:
context = {}
for exp in self.browse(cr, uid, ids, context=context):
company_id = exp.company_id.id
lines = []
total = 0.0
ctx = context.copy()
ctx.update({'date': exp.date})
journal = False
if exp.journal_id:
journal = exp.journal_id
else:
journal_id = voucher_obj._get_journal(cr, uid, context={'type': 'purchase', 'company_id': company_id})
if journal_id:
journal = account_journal.browse(cr, uid, journal_id, context=context)
if not journal:
raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
for line in exp.line_ids:
if line.product_id:
acc = line.product_id.property_account_expense
if not acc:
acc = line.product_id.categ_id.property_account_expense_categ
else:
acc = property_obj.get(cr, uid, 'property_account_expense_categ', 'product.category', context={'force_company': company_id})
if not acc:
raise osv.except_osv(_('Error!'), _('Please configure Default Expense account for Product purchase: `property_account_expense_categ`.'))
total_amount = line.total_amount
if journal.currency:
if exp.currency_id != journal.currency:
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, journal.currency.id, total_amount, context=ctx)
elif exp.currency_id != exp.company_id.currency_id:
total_amount = currency_obj.compute(cr, uid, exp.currency_id.id, exp.company_id.currency_id.id, total_amount, context=ctx)
lines.append((0, False, {
'name': line.name,
'account_id': acc.id,
'account_analytic_id': line.analytic_account.id,
'amount': total_amount,
'type': 'dr'
}))
total += total_amount
if not exp.employee_id.address_home_id:
raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
acc = exp.employee_id.address_home_id.property_account_payable.id
voucher = {
'name': exp.name or '/',
'reference': sequence_obj.get(cr, uid, 'hr.expense.invoice'),
'account_id': acc,
'type': 'purchase',
'partner_id': exp.employee_id.address_home_id.id,
'company_id': company_id,
'line_ids': lines,
'amount': total,
'journal_id': journal.id,
}
if journal and not journal.analytic_journal_id:
analytic_journal_ids = analytic_journal_obj.search(cr, uid, [('type','=','purchase')], context=context)
if analytic_journal_ids:
account_journal.write(cr, uid, [journal.id], {'analytic_journal_id': analytic_journal_ids[0]}, context=context)
voucher_id = voucher_obj.create(cr, uid, voucher, context=context)
self.write(cr, uid, [exp.id], {'voucher_id': voucher_id, 'state': 'done'}, context=context)
return True
def action_view_receipt(self, cr, uid, ids, context=None): def action_view_receipt(self, cr, uid, ids, context=None):
raise osv.except_osv(_('Error!'), _('Deprecated function used'))
''' '''
This function returns an action that display existing receipt of given expense ids. This function returns an action that display existing receipt of given expense ids.
''' '''

View File

@ -133,6 +133,7 @@
<group> <group>
<group string="Accounting Data"> <group string="Accounting Data">
<field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/> <field name="journal_id" widget="selection" domain="[('type', '=', 'purchase')]"/>
<field name="account_move_id"/>
<field name="voucher_id" context="{'form_view_ref': 'account_voucher.view_purchase_receipt_form'}"/> <field name="voucher_id" context="{'form_view_ref': 'account_voucher.view_purchase_receipt_form'}"/>
</group> </group>
</group> </group>