pass
return result
+ def _prepare_invoice(self, cr, uid, contract, context=None):
+ context = context or {}
+
+ inv_obj = self.pool.get('account.invoice')
+ journal_obj = self.pool.get('account.journal')
+ fpos_obj = self.pool.get('account.fiscal.position')
+
+ if not contract.partner_id:
+ raise osv.except_osv(_('No Customer Defined !'),_("You must first select a Customer for Contract %s!") % contract.name )
+
+ fpos = contract.partner_id.property_account_position.id or False
+ journal_ids = journal_obj.search(cr, uid, [('type', '=','sale'),('company_id', '=', contract.company_id.id or False)], limit=1)
+ if not journal_ids:
+ raise osv.except_osv(_('Error!'),
+ _('Please define a sale journal for the company "%s".') % (contract.company_id.name or '', ))
+
+ partner_payment_term = contract.partner_id.property_payment_term and contract.partner_id.property_payment_term.id or False
+
+
+ inv_data = {
+ 'reference': contract.code or False,
+ 'account_id': contract.partner_id.property_account_receivable.id,
+ 'type': 'out_invoice',
+ 'partner_id': contract.partner_id.id,
+ 'currency_id': contract.partner_id.property_product_pricelist.id or False,
+ 'journal_id': len(journal_ids) and journal_ids[0] or False,
+ 'date_invoice': contract.recurring_next_date,
+ 'origin': contract.name,
+ 'fiscal_position': fpos,
+ 'payment_term': partner_payment_term,
+ 'company_id': contract.company_id.id or False,
+ }
+ invoice_id = inv_obj.create(cr, uid, inv_data, context=context)
+
+ for line in contract.recurring_invoice_line_ids:
+
+ res = line.product_id
+ account_id = res.property_account_income.id
+ if not account_id:
+ account_id = res.categ_id.property_account_income_categ.id
+ account_id = fpos_obj.map_account(cr, uid, fpos, account_id)
+
+ taxes = res.taxes_id and res.taxes_id or False
+ tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
+
+ invoice_line_vals = {
+ 'name': line.name,
+ 'account_id': account_id,
+ 'account_analytic_id': contract.id,
+ 'price_unit': line.price_unit or 0.0,
+ 'quantity': line.quantity,
+ 'uos_id': line.uom_id.id or False,
+ 'product_id': line.product_id.id or False,
+ 'invoice_id' : invoice_id,
+ 'invoice_line_tax_id': [(6, 0, tax_id)],
+ }
+ self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context)
+
+ inv_obj.button_compute(cr, uid, [invoice_id], context=context)
+ return invoice_id
+
+ def recurring_create_invoice(self, cr, uid, automatic=False, context=None):
+ context = context or {}
+ current_date = time.strftime('%Y-%m-%d')
+
+ contract_ids = self.search(cr, uid, [('recurring_next_date','<=', current_date), ('state','=', 'open'), ('recurring_invoices','=', True)])
+ for contract in self.browse(cr, uid, contract_ids, context=context):
+ invoice_id = self._prepare_invoice(cr, uid, contract, context=context)
+
+ next_date = datetime.datetime.strptime(contract.recurring_next_date or current_date, "%Y-%m-%d")
+ interval = contract.recurring_interval
+ if contract.recurring_rule_type == 'daily':
+ new_date = next_date+relativedelta(days=+interval)
+ elif contract.recurring_rule_type == 'weekly':
+ new_date = next_date+relativedelta(weeks=+interval)
+ else:
+ new_date = next_date+relativedelta(months=+interval)
+ self.write(cr, uid, [contract.id], {'recurring_next_date': new_date.strftime('%Y-%m-%d')}, context=context)
+ return True
+
++ def onchange_invoice_on_timesheets(self, cr, uid, ids, invoice_on_timesheets, context=None):
++ if not invoice_on_timesheets:
++ return {}
++ result = {'value': {'use_timesheets': True}}
++ try:
++ to_invoice = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'hr_timesheet_invoice', 'timesheet_invoice_factor1')
++ result['value']['to_invoice'] = to_invoice[1]
++ except ValueError:
++ pass
++ return result
++
class account_analytic_account_summary_user(osv.osv):
_name = "account_analytic_analysis.summary.user"
_description = "Hours Summary by User"
def expense_canceled(self, cr, uid, ids, context=None):
return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
- def action_receipt_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')
+ def account_move_get(self, cr, uid, expense_id, context=None):
+ '''
+ This method prepare the creation of the account move related to the given expense.
+
+ :param expense_id: Id of voucher for which we are creating account_move.
+ :return: mapping between fieldname and value of account move to create
+ :rtype: dict
+ '''
+ journal_obj = self.pool.get('account.journal')
+ expense = self.browse(cr, uid, expense_id, context=context)
+ company_id = expense.company_id.id
+ date = expense.date_confirm
+ ref = expense.name
+ journal_id = False
+ if expense.journal_id:
+ journal_id = expense.journal_id.id
+ else:
+ journal_id = journal_obj.search(cr, uid, [('type', '=', 'purchase'), ('company_id', '=', company_id)])
+ if not journal_id:
+ raise osv.except_osv(_('Error!'), _("No expense journal found. Please make sure you have a journal with type 'purchase' configured."))
+ 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)
+
+ def line_get_convert(self, cr, uid, x, part, date, context=None):
+ partner_id = self.pool.get('res.partner')._find_accounting_partner(part).id
+ return {
+ 'date_maturity': x.get('date_maturity', False),
+ 'partner_id': partner_id,
+ 'name': x['name'][:64],
+ 'date': date,
+ 'debit': x['price']>0 and x['price'],
+ 'credit': x['price']<0 and -x['price'],
+ 'account_id': x['account_id'],
+ 'analytic_lines': x.get('analytic_lines', False),
+ 'amount_currency': x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)),
+ 'currency_id': x.get('currency_id', False),
+ 'tax_code_id': x.get('tax_code_id', False),
+ 'tax_amount': x.get('tax_amount', False),
+ 'ref': x.get('ref', False),
+ 'quantity': x.get('quantity',1.00),
+ 'product_id': x.get('product_id', False),
+ 'product_uom_id': x.get('uos_id', False),
+ 'analytic_account_id': x.get('account_analytic_id', False),
+ }
+
+ 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:
- 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
+ context={}
+ context.update({'date': exp.date_confirm or time.strftime('%Y-%m-%d')})
+ total = 0.0
+ total_currency = 0.0
+ for i in account_move_lines:
+ if exp.currency_id.id != company_currency:
+ i['currency_id'] = exp.currency_id.id
+ i['amount_currency'] = i['price']
+ i['price'] = cur_obj.compute(cr, uid, exp.currency_id.id,
+ company_currency, i['price'],
+ context=context)
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
+ i['amount_currency'] = False
+ i['currency_id'] = False
+ total -= i['price']
+ total_currency -= i['amount_currency'] or i['price']
+ return total, total_currency, account_move_lines
-
-
- def action_receipt_create(self, cr, uid, ids, context=None):
++
++ def action_move_create(self, cr, uid, ids, context=None):
+ '''
+ main function that is called when trying to create the accounting entries related to an expense
+ '''
+ move_obj = self.pool.get('account.move')
+ for exp in self.browse(cr, uid, ids, context=context):
if not exp.employee_id.address_home_id:
raise osv.except_osv(_('Error!'), _('The employee must have a home address.'))
+ company_currency = exp.company_id.currency_id.id
+ diff_currency_p = exp.currency_id.id <> company_currency
+
+ #create the move that will contain the accounting entries
+ move_id = move_obj.create(cr, uid, self.account_move_get(cr, uid, exp.id, context=context), context=context)
+
+ #one account.move.line per expense line (+taxes..)
+ eml = self.move_line_get(cr, uid, exp.id, context=context)
+
+ #create one more move line, a counterline for the total on payable account
+ total, total_currency, eml = self.compute_expense_totals(cr, uid, exp, company_currency, exp.name, eml, context=context)
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)
+ eml.append({
+ 'type': 'dest',
+ 'name': '/',
+ 'price': total,
+ 'account_id': acc,
+ 'date_maturity': exp.date_confirm,
+ 'amount_currency': diff_currency_p and total_currency or False,
+ 'currency_id': diff_currency_p and exp.currency_id.id or False,
+ 'ref': exp.name
+ })
+
+ #convert eml into an osv-valid format
+ lines = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, exp.employee_id.address_home_id, exp.date_confirm, context=context)), eml)
+ 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)
return True
-
- def action_view_receipt(self, cr, uid, ids, context=None):
+
+ def move_line_get(self, cr, uid, expense_id, context=None):
+ res = []
+ tax_obj = self.pool.get('account.tax')
+ cur_obj = self.pool.get('res.currency')
+ if context is None:
+ context = {}
+ exp = self.browse(cr, uid, expense_id, context=context)
+ company_currency = exp.company_id.currency_id.id
+
+ for line in exp.line_ids:
+ mres = self.move_line_get_item(cr, uid, line, context)
+ if not mres:
+ continue
+ res.append(mres)
+ tax_code_found= False
+
+ #Calculate tax according to default tax on product
+
+ taxes = []
+ #Taken from product_id_onchange in account.invoice
+ if line.product_id:
+ fposition_id = False
+ fpos_obj = self.pool.get('account.fiscal.position')
+ fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False
+ product = line.product_id
+ taxes = product.supplier_taxes_id
+ #If taxes are not related to the product, maybe they are in the account
+ if not taxes:
+ a = product.property_account_expense.id #Why is not there a check here?
+ if not a:
+ a = product.categ_id.property_account_expense_categ.id
+ a = fpos_obj.map_account(cr, uid, fpos, a)
+ taxes = a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False
+ tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
+ if not taxes:
+ continue
+ #Calculating tax on the line and creating move?
+ for tax in tax_obj.compute_all(cr, uid, taxes,
+ line.unit_amount ,
+ line.unit_quantity, line.product_id,
+ exp.user_id.partner_id)['taxes']:
+ tax_code_id = tax['base_code_id']
+ tax_amount = line.total_amount * tax['base_sign']
+ if tax_code_found:
+ if not tax_code_id:
+ continue
+ res.append(self.move_line_get_item(cr, uid, line, context))
+ res[-1]['price'] = 0.0
+ res[-1]['account_analytic_id'] = False
+ elif not tax_code_id:
+ continue
+ tax_code_found = True
+ res[-1]['tax_code_id'] = tax_code_id
+ res[-1]['tax_amount'] = cur_obj.compute(cr, uid, exp.currency_id.id, company_currency, tax_amount, context={'date': exp.date_confirm})
+
+ #Will create the tax here as we don't have the access
+ assoc_tax = {
+ 'type':'tax',
+ 'name':tax['name'],
+ 'price_unit': tax['price_unit'],
+ 'quantity': 1,
+ 'price': tax['amount'] * tax['base_sign'] or 0.0,
+ 'account_id': tax['account_collected_id'],
+ 'tax_code_id': tax['tax_code_id'],
+ 'tax_amount': tax['amount'] * tax['base_sign'],
+ }
+ res.append(assoc_tax)
+ return res
+
+ def move_line_get_item(self, cr, uid, line, context=None):
+ company = line.expense_id.company_id
+ property_obj = self.pool.get('ir.property')
+ 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`.'))
+ return {
+ 'type':'src',
+ 'name': line.name.split('\n')[0][:64],
+ 'price_unit':line.unit_amount,
+ 'quantity':line.unit_quantity,
+ 'price':line.total_amount,
+ 'account_id':acc.id,
+ 'product_id':line.product_id.id,
+ 'uos_id':line.uom_id.id,
+ 'account_analytic_id':line.analytic_account.id,
+ }
+
- def action_view_receipt(self, cr, uid, ids, context=None):
++ def action_view_move(self, cr, uid, ids, context=None):
'''
- This function returns an action that display existing receipt of given expense ids.
+ This function returns an action that display existing account.move of given expense ids.
'''
assert len(ids) == 1, 'This option should only be used for a single id at a time'
- voucher_id = self.browse(cr, uid, ids[0], context=context).voucher_id.id
- res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_voucher', 'view_purchase_receipt_form')
+ expense = self.browse(cr, uid, ids[0], context=context)
+ assert expense.account_move_id
+ try:
+ dummy, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'view_move_form')
+ except ValueError, e:
+ view_id = False
result = {
- 'name': _('Expense Receipt'),
+ 'name': _('Expense Account Move'),
'view_type': 'form',
'view_mode': 'form',
- 'view_id': res and res[1] or False,
- 'res_model': 'account.voucher',
+ 'view_id': view_id,
+ 'res_model': 'account.move',
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',