##############################################################################
import time
-from operator import itemgetter
-import decimal_precision as dp
from lxml import etree
+import decimal_precision as dp
import netsvc
-from osv import fields, osv, orm
import pooler
-from tools import config
+from osv import fields, osv, orm
from tools.translate import _
class account_invoice(osv.osv):
('company_id', '=', company_id),
('refund_journal', '=', refund_journal.get(type_inv, False))],
limit=1)
- if res:
- return res[0]
- else:
- return False
+ return res and res[0] or False
def _get_currency(self, cr, uid, context=None):
- user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid])[0]
+ user = pooler.get_pool(cr.dbname).get('res.users').browse(cr, uid, [uid], context=context)[0]
if user.company_id:
return user.company_id.currency_id.id
- else:
- return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=',1.0)])[0]
+ return pooler.get_pool(cr.dbname).get('res.currency').search(cr, uid, [('rate','=', 1.0)])[0]
def _get_journal_analytic(self, cr, uid, type_inv, context=None):
type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
return [('none', _('Free Reference'))]
def _amount_residual(self, cr, uid, ids, name, args, context=None):
- res = {}
- cur_obj = self.pool.get('res.currency')
- data_inv = self.browse(cr, uid, ids)
- if context is None:
- context = {}
-
- for inv in data_inv:
- debit = credit = 0.0
- context.update({'date':inv.date_invoice})
- context_unreconciled=context.copy()
- for lines in inv.move_lines:
- debit_tmp = lines.debit
- credit_tmp = lines.credit
- # If currency conversion needed
- if inv.company_id.currency_id.id <> inv.currency_id.id:
- # If invoice paid, compute currency amount according to invoice date
- # otherwise, take the line date
- if not inv.reconciled:
- context.update({'date':lines.date})
- context_unreconciled.update({'date':lines.date})
- # If amount currency setted, compute for debit and credit in company currency
- if lines.amount_currency < 0:
- credit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False, context=context_unreconciled))
- elif lines.amount_currency > 0:
- debit_tmp=abs(cur_obj.compute(cr, uid, lines.currency_id.id, inv.company_id.currency_id.id, lines.amount_currency, round=False, context=context_unreconciled))
- # Then, recomput into invoice currency to avoid rounding trouble !
- debit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, debit_tmp, round=False, context=context)
- credit += cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id, credit_tmp, round=False, context=context)
- else:
- debit+=debit_tmp
- credit+=credit_tmp
-
- if not inv.amount_total:
- result = 0.0
- elif inv.type in ('out_invoice','in_refund'):
- amount = credit-debit
- result = inv.amount_total - amount
- else:
- amount = debit-credit
- result = inv.amount_total - amount
- # Use is_zero function to avoid rounding trouble => should be fixed into ORM
- res[inv.id] = not self.pool.get('res.currency').is_zero(cr, uid, inv.company_id.currency_id, result) and result or 0.0
-
- return res
+ result = {}
+ for invoice in self.browse(cr, uid, ids, context=context):
+ result[invoice.id] = 0.0
+ if invoice.move_id:
+ for m in invoice.move_id.line_id:
+ if m.account_id.type in ('receivable','payable'):
+ result[invoice.id] += m.amount_residual_currency
+ return result
# Give Journal Items related to the payment reconciled to this invoice
# Return ids of partial and total payments related to the selected invoices
def _compute_lines(self, cr, uid, ids, name, args, context=None):
result = {}
- for invoice in self.browse(cr, uid, ids, context):
+ for invoice in self.browse(cr, uid, ids, context=context):
src = []
lines = []
if invoice.move_id:
def _get_invoice_from_line(self, cr, uid, ids, context=None):
move = {}
- for line in self.pool.get('account.move.line').browse(cr, uid, ids):
+ for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context):
if line.reconcile_partial_id:
for line2 in line.reconcile_partial_id.line_partial_ids:
move[line2.move_id.id] = True
def _get_invoice_from_reconcile(self, cr, uid, ids, context=None):
move = {}
- for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids):
+ for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids, context=context):
for line in r.line_partial_ids:
move[line.move_id.id] = True
for line in r.line_id:
\n* The \'Open\' state is used when user create invoice,a invoice number is generated.Its in open state till user does not pay invoice. \
\n* The \'Paid\' state is set automatically when invoice is paid.\
\n* The \'Cancelled\' state is used when user cancel invoice.'),
- 'date_invoice': fields.date('Date Invoiced', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, help="Keep empty to use the current date"),
- 'date_due': fields.date('Due Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]},
+ 'date_invoice': fields.date('Invoice Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, select=True, help="Keep empty to use the current date"),
+ 'date_due': fields.date('Due Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, select=True,
help="If you use payment terms, the due date will be computed automatically at the generation "\
"of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."),
'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}),
'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}),
'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}),
- 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, help="Link to the automatically generated Journal Items."),
+ 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."),
'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Untaxed',
store={
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
},
multi='all'),
'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Tax',
store={
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
},
multi='all'),
'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Total',
store={
'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20),
'account.invoice.tax': (_get_invoice_tax, None, 20),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20),
},
multi='all'),
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
}, help="The Journal Entry of the invoice have been totally reconciled with one or several Journal Entries of payment."),
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account',
help='Bank Account Number, Company bank account if Invoice is customer or supplier refund, otherwise Partner bank account number.', readonly=True, states={'draft':[('readonly',False)]}),
- 'move_lines':fields.function(_get_lines , method=True, type='many2many', relation='account.move.line', string='Entry Lines'),
+ 'move_lines':fields.function(_get_lines, method=True, type='many2many', relation='account.move.line', string='Entry Lines'),
'residual': fields.function(_amount_residual, method=True, digits_compute=dp.get_precision('Account'), string='Residual',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
+ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','move_id'], 50),
'account.invoice.tax': (_get_invoice_tax, None, 50),
- 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 50),
'account.move.line': (_get_invoice_from_line, None, 50),
'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
},
'payment_ids': fields.function(_compute_lines, method=True, relation='account.move.line', type="many2many", string='Payments'),
'move_name': fields.char('Journal Entry', size=64, readonly=True, states={'draft':[('readonly',False)]}),
'user_id': fields.many2one('res.users', 'Salesman', readonly=True, states={'draft':[('readonly',False)]}),
- 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Mapping', readonly=True, states={'draft':[('readonly',False)]})
+ 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]})
}
_defaults = {
'type': _get_type,
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
journal_obj = self.pool.get('account.journal')
- if context.get('active_model','') in ['res.partner']:
- partner = self.pool.get(context['active_model']).read(cr,uid,context['active_ids'],['supplier','customer'])[0]
+ if context is None:
+ context = {}
+
+ if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']:
+ partner = self.pool.get(context['active_model']).read(cr, uid, context['active_ids'], ['supplier','customer'])[0]
if not view_type:
- view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name','=','account.invoice.tree')])[0]
+ view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')])
view_type = 'tree'
if view_type == 'form':
if partner['supplier'] and not partner['customer']:
- view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name','=','account.invoice.supplier.form')])[0]
+ view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')])
else:
- view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name','=','account.invoice.form')])[0]
+ view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')])
+ if view_id and isinstance(view_id, (list, tuple)):
+ view_id = view_id[0]
res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
+
type = context.get('journal_type', 'sale')
for field in res['fields']:
if field == 'journal_id':
journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1)
res['fields'][field]['selection'] = journal_select
+
+ if view_type == 'tree':
+ doc = etree.XML(res['arch'])
+ nodes = doc.xpath("//field[@name='partner_id']")
+ partner_string = _('Customer')
+ if context.get('type', 'out_invoice') in ('in_invoice', 'in_refund'):
+ partner_string = _('Supplier')
+ for node in nodes:
+ node.set('string', partner_string)
+ res['arch'] = etree.tostring(doc)
return res
+ def get_log_context(self, cr, uid, context=None):
+ if context is None:
+ context = {}
+ res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form')
+ view_id = res and res[1] or False
+ context.update({'view_id': view_id})
+ return context
+
def create(self, cr, uid, vals, context=None):
+ if context is None:
+ context = {}
try:
res = super(account_invoice, self).create(cr, uid, vals, context)
for inv_id, name in self.name_get(cr, uid, [res], context=context):
- message = _('Invoice ') + " '" + name + "' "+ _("is waiting for validation.")
- self.log(cr, uid, inv_id, message)
+ ctx = context.copy()
+ if vals.get('type', 'in_invoice') in ('out_invoice', 'out_refund'):
+ ctx = self.get_log_context(cr, uid, context=ctx)
+ message = _("Invoice '%s' is waiting for validation.") % name
+ self.log(cr, uid, inv_id, message, context=ctx)
return res
except Exception, e:
if '"journal_id" viol' in e.args[0]:
raise orm.except_orm(_('Configuration Error!'),
_('There is no Accounting Journal of type Sale/Purchase defined!'))
else:
- raise orm.except_orm(_('UnknownError'), str(e))
+ raise orm.except_orm(_('Unknown Error'), str(e))
def confirm_paid(self, cr, uid, ids, context=None):
+ if context is None:
+ context = {}
self.write(cr, uid, ids, {'state':'paid'}, context=context)
+ for inv_id, name in self.name_get(cr, uid, ids, context=context):
+ message = _("Invoice '%s' is paid.") % name
+ self.log(cr, uid, inv_id, message)
return True
def unlink(self, cr, uid, ids, context=None):
- invoices = self.read(cr, uid, ids, ['state'])
+ if context is None:
+ context = {}
+ invoices = self.read(cr, uid, ids, ['state'], context=context)
unlink_ids = []
for t in invoices:
if t['state'] in ('draft', 'cancel'):
result['value'].update(to_update['value'])
return result
- def onchange_currency_id(self, cr, uid, ids, curr_id, company_id):
- if curr_id and company_id:
- currency = self.pool.get('res.currency').browse(cr, uid, curr_id)
- if currency.company_id.id != company_id:
- raise osv.except_osv(_('Configuration Error !'),
- _('Can not select currency that is not related to current company.\nPlease select accordingly !.'))
- return {}
-
def onchange_journal_id(self, cr, uid, ids, journal_id=False):
result = {}
if journal_id:
res = {'value':{'date_due': pterm_list[-1]}}
else:
raise osv.except_osv(_('Data Insufficient !'), _('The Payment Term of Supplier does not have Payment Term Lines(Computation) defined !'))
-
return res
def onchange_invoice_line(self, cr, uid, ids, lines):
val = {}
dom = {}
obj_journal = self.pool.get('account.journal')
+ account_obj = self.pool.get('account.account')
+ inv_line_obj = self.pool.get('account.invoice.line')
if company_id and part_id and type:
acc_id = False
partner_obj = self.pool.get('res.partner').browse(cr,uid,part_id)
else:
acc_id = pay_res_id
val= {'account_id': acc_id}
- account_obj = self.pool.get('account.account')
if ids:
if company_id:
inv_obj = self.browse(cr,uid,ids)
if not result_id:
raise osv.except_osv(_('Configuration Error !'),
_('Can not find account chart for this company in invoice line account, Please Create account.'))
- r_id = self.pool.get('account.invoice.line').write(cr, uid, [line.id], {'account_id': result_id[0]})
+ inv_line_obj.write(cr, uid, [line.id], {'account_id': result_id[0]})
else:
if invoice_line:
for inv_line in invoice_line:
journal_ids = obj_journal.search(cr, uid, [('company_id','=',company_id), ('type', '=', journal_type)])
if journal_ids:
val['journal_id'] = journal_ids[0]
- else:
- raise osv.except_osv(_('Configuration Error !'), _('Can\'t find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Financial Accounting\Accounts\Journals.' % (journal_type)))
+ res_journal_default = self.pool.get('ir.values').get(cr, uid, 'default', 'type=%s' % (type), ['account.invoice'])
+ for r in res_journal_default:
+ if r[1] == 'journal_id' and r[2] in journal_ids:
+ val['journal_id'] = r[2]
+ if not val.get('journal_id', False):
+ raise osv.except_osv(_('Configuration Error !'), (_('Can\'t find any account journal of %s type for this company.\n\nYou can create one in the menu: \nConfiguration\Financial Accounting\Accounts\Journals.') % (journal_type)))
dom = {'journal_id': [('id', 'in', journal_ids)]}
else:
journal_ids = obj_journal.search(cr, uid, [])
val['currency_id'] = False
else:
val['currency_id'] = company.currency_id.id
-
- return {'value' : val, 'domain': dom }
+ return {'value': val, 'domain': dom}
# go from canceled state to draft state
def action_cancel_draft(self, cr, uid, ids, *args):
self.write(cr, uid, ids, {'state':'draft'})
wf_service = netsvc.LocalService("workflow")
for inv_id in ids:
+ wf_service.trg_delete(uid, 'account.invoice', inv_id, cr)
wf_service.trg_create(uid, 'account.invoice', inv_id, cr)
return True
- def finalize_invoice_move_lines(self, cr, uid, invoice_browse, move_lines):
- """finalize_invoice_move_lines(cr, uid, invoice, move_lines) -> move_lines
- Hook method to be overridden in additional modules to verify and possibly alter the
- move lines to be created by an invoice, for special cases.
- :param invoice_browse: browsable record of the invoice that is generating the move lines
- :param move_lines: list of dictionaries with the account.move.lines (as for create())
- :return: the (possibly updated) final move_lines to create for this invoice
- """
- return move_lines
-
# Workflow stuff
#################
return res
def copy(self, cr, uid, id, default={}, context=None):
+ if context is None:
+ context = {}
default.update({
'state':'draft',
'number':False,
'move_id':False,
'move_name':False,
'internal_number': False,
+ 'period_id': False
})
if 'date_invoice' not in default:
default.update({
ctx = context.copy()
ait_obj = self.pool.get('account.invoice.tax')
for id in ids:
- cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s", (id,))
+ cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (id,))
partner = self.browse(cr, uid, id, context=ctx).partner_id
if partner.lang:
ctx.update({'lang': partner.lang})
def button_compute(self, cr, uid, ids, context=None, set_total=False):
self.button_reset_taxes(cr, uid, ids, context)
- for inv in self.browse(cr, uid, ids):
+ for inv in self.browse(cr, uid, ids, context=context):
if set_total:
self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total})
return True
return (ref or '').replace('/','')
def _get_analytic_lines(self, cr, uid, id):
- inv = self.browse(cr, uid, [id])[0]
+ inv = self.browse(cr, uid, id)
cur_obj = self.pool.get('res.currency')
company_currency = inv.company_id.currency_id.id
ref = inv.reference
else:
ref = self._convert_ref(cr, uid, inv.number)
+ if not inv.journal_id.analytic_journal_id:
+ raise osv.except_osv(_('No Analytic Journal !'),_("You have to define an analytic journal on the '%s' journal!") % (inv.journal_id.name,))
il['analytic_lines'] = [(0,0, {
'name': il['name'],
'date': inv['date_invoice'],
'product_id': il['product_id'],
'product_uom_id': il['uos_id'],
'general_account_id': il['account_id'],
- 'journal_id': self._get_journal_analytic(cr, uid, inv.type),
+ 'journal_id': inv.journal_id.analytic_journal_id.id,
'ref': ref,
})]
return iml
line = []
for key, val in line2.items():
line.append((0,0,val))
-
return line
def action_move_create(self, cr, uid, ids, *args):
self.write(cr, uid, [inv.id], {'date_invoice':time.strftime('%Y-%m-%d')})
company_currency = inv.company_id.currency_id.id
# create the analytical lines
- line_ids = self.read(cr, uid, [inv.id], ['invoice_line'])[0]['invoice_line']
# one move line per invoice line
iml = self._get_analytic_lines(cr, uid, inv.id)
# check if taxes are all computed
-
- context.update({'lang': inv.partner_id.lang})
- compute_taxes = ait_obj.compute(cr, uid, inv.id, context=context)
+ ctx = context.copy()
+ ctx.update({'lang': inv.partner_id.lang})
+ compute_taxes = ait_obj.compute(cr, uid, inv.id, context=ctx)
self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj)
if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0):
total_fixed += line.value_amount
if line.value == 'procent':
total_percent += line.value_amount
- total_fixed = (total_fixed * 100) / inv.amount_total
+ total_fixed = (total_fixed * 100) / (inv.amount_total or 1.0)
if (total_fixed + total_percent) > 100:
raise osv.except_osv(_('Error !'), _("Cannot create the invoice !\nThe payment term defined gives a computed amount greater than the total invoiced amount."))
'name': name,
'price': total,
'account_id': acc_id,
- 'date_maturity' : inv.date_due or False,
+ 'date_maturity': inv.date_due or False,
'amount_currency': diff_currency_p \
and total_currency or False,
'currency_id': diff_currency_p \
date = inv.date_invoice or time.strftime('%Y-%m-%d')
part = inv.partner_id.id
- line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})) ,iml)
+ line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part, date, context={})),iml)
line = self.group_lines(cr, uid, iml, line, inv)
}
period_id = inv.period_id and inv.period_id.id or False
if not period_id:
- period_ids = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d'))])
- if len(period_ids):
+ period_ids = self.pool.get('account.period').search(cr, uid, [('date_start','<=',inv.date_invoice or time.strftime('%Y-%m-%d')),('date_stop','>=',inv.date_invoice or time.strftime('%Y-%m-%d')), ('company_id', '=', inv.company_id.id)])
+ if period_ids:
period_id = period_ids[0]
if period_id:
move['period_id'] = period_id
def line_get_convert(self, cr, uid, x, part, date, context=None):
return {
'date_maturity': x.get('date_maturity', False),
- 'partner_id':part,
- 'name':x['name'][:64],
+ 'partner_id': part,
+ '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', []),
- '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),
+ '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', []),
+ '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),
+ '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 action_number(self, cr, uid, ids, *args):
+ def action_number(self, cr, uid, ids, context=None):
+ if context is None:
+ context = {}
#TODO: not correct fix but required a frech values before reading it.
self.write(cr, uid, ids, {})
- for obj_inv in self.browse(cr, uid, ids):
+ for obj_inv in self.browse(cr, uid, ids, context=context):
id = obj_inv.id
invtype = obj_inv.type
number = obj_inv.number
self.write(cr, uid, ids, {'internal_number':number})
if invtype in ('in_invoice', 'in_refund'):
- ref = reference
+ if not reference:
+ ref = self._convert_ref(cr, uid, number)
+ else:
+ ref = reference
else:
ref = self._convert_ref(cr, uid, number)
(ref, move_id))
for inv_id, name in self.name_get(cr, uid, [id]):
+ ctx = context.copy()
+ if obj_inv.type in ('out_invoice', 'out_refund'):
+ ctx = self.get_log_context(cr, uid, context=ctx)
message = _('Invoice ') + " '" + name + "' "+ _("is validated.")
- self.log(cr, uid, inv_id, message)
-
+ self.log(cr, uid, inv_id, message, context=ctx)
return True
def action_cancel(self, cr, uid, ids, *args):
+ context = {} # TODO: Use context from arguments
account_move_obj = self.pool.get('account.move')
invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
+ move_ids = [] # ones that we will need to remove
for i in invoices:
if i['move_id']:
- account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
- # delete the move this invoice was pointing to
- # Note that the corresponding move_lines and move_reconciles
- # will be automatically deleted too
- account_move_obj.unlink(cr, uid, [i['move_id'][0]])
+ move_ids.append(i['move_id'][0])
if i['payment_ids']:
account_move_line_obj = self.pool.get('account.move.line')
- pay_ids = account_move_line_obj.browse(cr, uid , i['payment_ids'])
+ pay_ids = account_move_line_obj.browse(cr, uid, i['payment_ids'])
for move_line in pay_ids:
if move_line.reconcile_partial_id and move_line.reconcile_partial_id.line_partial_ids:
raise osv.except_osv(_('Error !'), _('You cannot cancel the Invoice which is Partially Paid! You need to unreconcile concerned payment entries!'))
+ # First, set the invoices as cancelled and detach the move ids
self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
+ if move_ids:
+ # second, invalidate the move(s)
+ account_move_obj.button_cancel(cr, uid, move_ids, context=context)
+ # delete the move this invoice was pointing to
+ # Note that the corresponding move_lines and move_reconciles
+ # will be automatically deleted too
+ account_move_obj.unlink(cr, uid, move_ids, context=context)
self._log_event(cr, uid, ids, -1.0, 'Cancel Invoice')
return True
return True
def name_get(self, cr, uid, ids, context=None):
- if not len(ids):
+ if not ids:
return []
types = {
'out_invoice': 'CI: ',
'out_refund': 'OR: ',
'in_refund': 'SR: ',
}
- return [(r['id'], types[r['type']]+(r['number'] or '')+' '+(r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
+ return [(r['id'], (r['number']) or types[r['type']] + (r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')]
def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100):
if not args:
context = {}
ids = []
if name:
- ids = self.search(cr, user, [('number','=',name)]+ args, limit=limit, context=context)
+ ids = self.search(cr, user, [('number','=',name)] + args, limit=limit, context=context)
if not ids:
- ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit, context=context)
+ ids = self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context)
return self.name_get(cr, user, ids, context)
def _refund_cleanup_lines(self, cr, uid, lines):
def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None):
invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'address_contact_id', 'address_invoice_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id'])
-
+ obj_invoice_line = self.pool.get('account.invoice.line')
+ obj_invoice_tax = self.pool.get('account.invoice.tax')
+ obj_journal = self.pool.get('account.journal')
new_ids = []
for invoice in invoices:
del invoice['id']
'in_refund': 'in_invoice', # Supplier Refund
}
- invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
+ invoice_lines = obj_invoice_line.read(cr, uid, invoice['invoice_line'])
invoice_lines = self._refund_cleanup_lines(cr, uid, invoice_lines)
- tax_lines = self.pool.get('account.invoice.tax').read(cr, uid, invoice['tax_line'])
+ tax_lines = obj_invoice_tax.read(cr, uid, invoice['tax_line'])
tax_lines = filter(lambda l: l['manual'], tax_lines)
tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
if journal_id:
refund_journal_ids = [journal_id]
elif invoice['type'] == 'in_invoice':
- refund_journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','purchase_refund')])
+ refund_journal_ids = obj_journal.search(cr, uid, [('type','=','purchase_refund')])
else:
- refund_journal_ids = self.pool.get('account.journal').search(cr, uid, [('type','=','sale_refund')])
+ refund_journal_ids = obj_journal.search(cr, uid, [('type','=','sale_refund')])
- if not date :
+ if not date:
date = time.strftime('%Y-%m-%d')
invoice.update({
'type': type_dict[invoice['type']],
'tax_line': tax_lines,
'journal_id': refund_journal_ids
})
- if period_id :
+ if period_id:
invoice.update({
'period_id': period_id,
})
- if description :
+ if description:
invoice.update({
'name': description,
})
invoice[field] = invoice[field] and invoice[field][0]
# create the new invoice
new_ids.append(self.create(cr, uid, invoice))
+
return new_ids
def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
context = {}
#TODO check if we can use different period for payment and the writeoff line
assert len(ids)==1, "Can only pay one invoice at a time"
- invoice = self.browse(cr, uid, ids[0])
+ invoice = self.browse(cr, uid, ids[0], context=context)
src_account_id = invoice.account_id.id
# Take the seq as name for move
types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1}
inv_id, name = self.name_get(cr, uid, [invoice.id], context=context)[0]
if (not round(total,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))) or writeoff_acc_id:
- self.log(cr, uid, inv_id, _('Invoice ') + " '" + name + "' "+ _("is totally paid."))
self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
else:
- code = invoice.currency_id.code
- amt = str(pay_amount) + code + ' on ' + str(invoice.amount_total) + code + ' (' + str(total) + code + ' remaining)'
- self.log(cr, uid, inv_id, _('Invoice ') + " '" + name + "' "+ _("is paid partially: ") + amt)
+ code = invoice.currency_id.symbol
+ # TODO: use currency's formatting function
+ msg = _("Invoice '%s' is paid partially: %s%s of %s%s (%s%s remaining)") % \
+ (name, pay_amount, code, invoice.amount_total, code, total, code)
+ self.log(cr, uid, inv_id, msg)
self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
# Update the stored value (fields.function), so we write to trigger recompute
self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
return True
+
account_invoice()
class account_invoice_line(osv.osv):
t = t - (p * l[2].get('quantity'))
taxes = l[2].get('invoice_line_tax_id')
if len(taxes[0]) >= 3 and taxes[0][2]:
- taxes = tax_obj.browse(cr, uid, taxes[0][2])
+ taxes = tax_obj.browse(cr, uid, list(taxes[0][2]))
for tax in tax_obj.compute_all(cr, uid, taxes, p,l[2].get('quantity'), context.get('address_invoice_id', False), l[2].get('product_id', False), context.get('partner_id', False))['taxes']:
t = t - tax['amount']
return t
'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]),
'note': fields.text('Notes'),
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
- 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
+ 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True)
}
_defaults = {
'price_unit': _price_unit_default,
}
- def product_id_change_unit_price_inv(self, cr, uid, tax_id, price_unit, qty, address_invoice_id, product, partner_id, context=None):
- tax_obj = self.pool.get('account.tax')
- if price_unit:
- taxes = tax_obj.browse(cr, uid, tax_id)
- for tax in tax_obj.compute_inv(cr, uid, taxes, price_unit, qty, address_invoice_id, product, partner_id):
- price_unit = price_unit - tax['amount']
- return {'price_unit': price_unit,'invoice_line_tax_id': tax_id}
-
def product_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
if context is None:
context = {}
if not partner_id:
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
if not product:
- if type in ('in_invoice', 'in_refund'):
- return {'value': {'categ_id': False}, 'domain':{'product_uom':[]}}
- else:
- return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}}
- part = self.pool.get('res.partner').browse(cr, uid, partner_id)
+ return {'value': {'price_unit': 0.0, 'categ_id': False}, 'domain':{'product_uom':[]}}
+ part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
fpos_obj = self.pool.get('account.fiscal.position')
- fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id) or False
+ fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False
if part.lang:
context.update({'lang': part.lang})
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
- if company_id:
- property_obj = self.pool.get('ir.property')
- account_obj = self.pool.get('account.account')
- in_pro_id = property_obj.search(cr, uid, [('name','=','property_account_income'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
- if not in_pro_id:
- in_pro_id = property_obj.search(cr, uid, [('name','=','property_account_income_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
- exp_pro_id = property_obj.search(cr, uid, [('name','=','property_account_expense'),('res_id','=','product.template,'+str(res.product_tmpl_id.id)+''),('company_id','=',company_id)])
- if not exp_pro_id:
- exp_pro_id = property_obj.search(cr, uid, [('name','=','property_account_expense_categ'),('res_id','=','product.template,'+str(res.categ_id.id)+''),('company_id','=',company_id)])
-
- if not in_pro_id:
- in_acc = res.product_tmpl_id.property_account_income
- in_acc_cate = res.categ_id.property_account_income_categ
- if in_acc:
- app_acc_in = in_acc
- else:
- app_acc_in = in_acc_cate
- else:
- # Get the fields from the ir.property record
- my_value = property_obj.read(cr,uid,in_pro_id,['name','value_reference','res_id'])
- # Parse the value_reference field to get the ID of the account.account record
- account_id = int (my_value[0]["value_reference"].split(",")[1])
- # Use the ID of the account.account record in the browse for the account.account record
- app_acc_in = account_obj.browse(cr, uid, [account_id])[0]
- if not exp_pro_id:
- ex_acc = res.product_tmpl_id.property_account_expense
- ex_acc_cate = res.categ_id.property_account_expense_categ
- if ex_acc:
- app_acc_exp = ex_acc
- else:
- app_acc_exp = ex_acc_cate
- else:
- app_acc_exp = account_obj.browse(cr, uid, exp_pro_id)[0]
- if not in_pro_id and not exp_pro_id:
- in_acc = res.product_tmpl_id.property_account_income
- in_acc_cate = res.categ_id.property_account_income_categ
- ex_acc = res.product_tmpl_id.property_account_expense
- ex_acc_cate = res.categ_id.property_account_expense_categ
- if in_acc or ex_acc:
- app_acc_in = in_acc
- app_acc_exp = ex_acc
- else:
- app_acc_in = in_acc_cate
- app_acc_exp = ex_acc_cate
- if app_acc_in.company_id.id != company_id and app_acc_exp.company_id.id != company_id:
- in_res_id = account_obj.search(cr, uid, [('name','=',app_acc_in.name),('company_id','=',company_id)])
- exp_res_id = account_obj.search(cr, uid, [('name','=',app_acc_exp.name),('company_id','=',company_id)])
- if not in_res_id and not exp_res_id:
- raise osv.except_osv(_('Configuration Error !'),
- _('Can not find account chart for this company, Please Create account.'))
- in_obj_acc = account_obj.browse(cr, uid, in_res_id)
- exp_obj_acc = account_obj.browse(cr, uid, exp_res_id)
- if in_acc or ex_acc:
- res.product_tmpl_id.property_account_income = in_obj_acc[0]
- res.product_tmpl_id.property_account_expense = exp_obj_acc[0]
- else:
- res.categ_id.property_account_income_categ = in_obj_acc[0]
- res.categ_id.property_account_expense_categ = exp_obj_acc[0]
-
if type in ('out_invoice','out_refund'):
a = res.product_tmpl_id.property_account_income.id
if not a:
a = res.product_tmpl_id.property_account_expense.id
if not a:
a = res.categ_id.property_account_expense_categ.id
-
a = fpos_obj.map_account(cr, uid, fpos, a)
if a:
result['account_id'] = a
- taxep = None
- tax_obj = self.pool.get('account.tax')
if type in ('out_invoice', 'out_refund'):
- taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a).tax_ids or False)
- tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
+ taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False)
else:
- taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a).tax_ids or False)
- tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes)
+ taxes = res.supplier_taxes_id and res.supplier_taxes_id or (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 type in ('in_invoice', 'in_refund'):
- to_update = self.product_id_change_unit_price_inv(cr, uid, tax_id, price_unit or res.standard_price, qty, address_invoice_id, product, partner_id, context=context)
- result.update(to_update)
+ result.update( {'price_unit': price_unit or res.standard_price,'invoice_line_tax_id': tax_id} )
else:
result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
-
-# if not name:
result['name'] = res.partner_ref
domain = {}
result['uos_id'] = res.uom_id.id or uom or False
+ result['note'] = res.description
if result['uos_id']:
res2 = res.uom_id.category_id.id
- if res2 :
+ if res2:
domain = {'uos_id':[('category_id','=',res2 )]}
result['categ_id'] = res.categ_id.id
if not company_id or not currency_id:
return res_final
- company = self.pool.get('res.company').browse(cr, uid, company_id)
- currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
+ company = self.pool.get('res.company').browse(cr, uid, company_id, context=context)
+ currency = self.pool.get('res.currency').browse(cr, uid, currency_id, context=context)
if company.currency_id.id != currency.id:
new_price = res_final['value']['price_unit'] * currency.rate
return res_final
def uos_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, address_invoice_id=False, currency_id=False, context=None):
- res = self.product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, address_invoice_id, currency_id, context)
+ warning = {}
+ res = self.product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, address_invoice_id, currency_id, context=context)
if 'uos_id' in res['value']:
del res['value']['uos_id']
if not uom:
res['value']['price_unit'] = 0.0
+ if product and uom:
+ prod = self.pool.get('product.product').browse(cr, uid, product, context=context)
+ prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context)
+ if prod.uom_id.category_id.id != prod_uom.category_id.id:
+ warning = {
+ 'title': _('Warning!'),
+ 'message': _('You selected an Unit of Measure which is not compatible with the product.')
+ }
+ return {'value': res['value'], 'warning': warning}
return res
def move_line_get(self, cr, uid, invoice_id, context=None):
res = []
- tax_grouped = {}
tax_obj = self.pool.get('account.tax')
cur_obj = self.pool.get('res.currency')
- ait_obj = self.pool.get('account.invoice.tax')
- inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id)
+ if context is None:
+ context = {}
+ inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context)
company_currency = inv.company_id.currency_id.id
- cur = inv.currency_id
for line in inv.invoice_line:
mres = self.move_line_get_item(cr, uid, line, context)
taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids
fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
res = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes)
- r = {'value':{'invoice_line_tax_id': res}}
- return r
+ return {'value':{'invoice_line_tax_id': res}}
+
account_invoice_line()
class account_invoice_tax(osv.osv):
'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')),
'manual': fields.boolean('Manual'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."),
-
'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."),
'base_amount': fields.float('Base Code Amount', digits_compute=dp.get_precision('Account')),
'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."),
'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Account')),
- 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True),
- 'factor_base' : fields.function(_count_factor, method=True, string='Multipication factor for Base code', type='float', multi="all"),
- 'factor_tax' : fields.function(_count_factor, method=True, string='Multipication factor Tax code', type='float', multi="all")
+ 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
+ 'factor_base': fields.function(_count_factor, method=True, string='Multipication factor for Base code', type='float', multi="all"),
+ 'factor_tax': fields.function(_count_factor, method=True, string='Multipication factor Tax code', type='float', multi="all")
}
def base_change(self, cr, uid, ids, base, currency_id=False, company_id=False, date_invoice=False):
_order = 'sequence'
_defaults = {
- 'manual': lambda *a: 1,
- 'base_amount': lambda *a: 0.0,
- 'tax_amount': lambda *a: 0.0,
+ 'manual': 1,
+ 'base_amount': 0.0,
+ 'tax_amount': 0.0,
}
- def compute(self, cr, uid, invoice_id, context={}):
+ def compute(self, cr, uid, invoice_id, context=None):
tax_grouped = {}
tax_obj = self.pool.get('account.tax')
cur_obj = self.pool.get('res.currency')
- inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context)
+ inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context)
cur = inv.currency_id
company_currency = inv.company_id.currency_id.id
tax_grouped[key]['tax_amount'] += val['tax_amount']
for t in tax_grouped.values():
+ t['base'] = cur_obj.round(cr, uid, cur, t['base'])
t['amount'] = cur_obj.round(cr, uid, cur, t['amount'])
t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount'])
t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount'])
'tax_amount': t['tax_amount']
})
return res
+
account_invoice_tax()