##############################################################################
import time
+from operator import itemgetter
+
import netsvc
from osv import fields, osv
-import ir
+from osv.orm import except_orm
import pooler
-import mx.DateTime
-from mx.DateTime import RelativeDateTime
from tools import config
from tools.translate import _
return [('none', _('Free Reference'))]
def _amount_residual(self, cr, uid, ids, name, args, context=None):
+ if context is None:
+ context = {}
res = {}
data_inv = self.browse(cr, uid, ids)
cur_obj = self.pool.get('res.currency')
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)
+ if inv.reconciled:
+ res[inv.id] = 0.0
+ continue
+ inv_total = inv.amount_total
+ context_unreconciled = context.copy()
+ for lines in inv.payment_ids:
+ if lines.amount_currency and lines.currency_id.id == inv.currency_id.id:
+ if inv.type in ('out_invoice','in_refund'):
+ inv_total += lines.amount_currency
+ else:
+ inv_total -= lines.amount_currency
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
-
+ context_unreconciled.update({'date': lines.date})
+ amount_in_invoice_currency = cur_obj.compute(cr, uid, inv.company_id.currency_id.id, inv.currency_id.id,abs(lines.debit-lines.credit),round=False,context=context_unreconciled)
+ inv_total -= amount_in_invoice_currency
+
+ result = inv_total
+ res[inv.id] = self.pool.get('res.currency').round(cr, uid, inv.currency_id, result)
return res
+ #This function is called by the fields.function move_lines, which is probably unused now.
+ #This function is also wrongly computed: you should use the one on the field payment_ids instead
def _get_lines(self, cr, uid, ids, name, arg, context=None):
res = {}
for id in ids:
temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
lines += [x for x in temp_lines if x not in lines]
src.append(m.id)
-
lines = filter(lambda x: x not in src, lines)
result[invoice.id] = lines
return result
}, help="The account moves of the invoice have been reconciled with account moves of the payment(s)."),
'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',
help='The bank account to pay to or to be paid from'),
+ #this field is probably unused, and wrongly computed. Use payment_ids instead.
'move_lines':fields.function(_get_lines , method=True,type='many2many' , relation='account.move.line',string='Move Lines'),
'residual': fields.function(_amount_residual, method=True, digits=(16, int(config['price_accuracy'])),string='Residual',
store={
'reference_type': lambda *a: 'none',
'check_total': lambda *a: 0.0,
}
-
+
+ def create(self, cr, uid, vals, context={}):
+ try:
+ res = super(account_invoice, self).create(cr, uid, vals, context)
+ return res
+ except Exception,e:
+ if '"journal_id" viol' in e.args[0]:
+ raise except_orm(_('Configuration Error!'),
+ _('There is no Accounting Journal of type Sale/Purchase defined!'))
+ else:
+ raise except_orm(_('UnknownError'), str(e))
+
def unlink(self, cr, uid, ids, context=None):
invoices = self.read(cr, uid, ids, ['state'])
unlink_ids = []
# return [{}]
def onchange_partner_id(self, cr, uid, ids, type, partner_id,
- date_invoice=False, payment_term=False, partner_bank_id=False):
+ date_invoice=False, payment_term=False, partner_bank=False):
invoice_addr_id = False
contact_addr_id = False
partner_payment_term = False
if type in ('in_invoice', 'in_refund'):
result['value']['partner_bank'] = bank_id
- if partner_bank_id != bank_id:
+ if partner_bank != bank_id:
to_update = self.onchange_partner_bank(cr, uid, ids, bank_id)
result['value'].update(to_update['value'])
return result
def onchange_invoice_line(self, cr, uid, ids, lines):
return {}
- def onchange_partner_bank(self, cursor, user, ids, partner_bank_id):
+ def onchange_partner_bank(self, cursor, user, ids, partner_bank):
return {'value': {}}
# go from canceled state to draft state
def move_line_id_payment_get(self, cr, uid, ids, *args):
res = []
if not ids: return res
- cr.execute('select \
- l.id \
- from account_move_line l \
- left join account_invoice i on (i.move_id=l.move_id) \
- where i.id in ('+','.join(map(str,ids))+') and l.account_id=i.account_id')
- res = map(lambda x: x[0], cr.fetchall())
+ cr.execute('SELECT l.id '\
+ 'FROM account_move_line l '\
+ 'LEFT JOIN account_invoice i ON (i.move_id=l.move_id) '\
+ 'WHERE i.id IN %s '\
+ 'AND l.account_id=i.account_id',
+ (tuple(ids),))
+ res = map(itemgetter(0), cr.fetchall())
return res
def copy(self, cr, uid, id, default=None, context=None):
def button_reset_taxes(self, cr, uid, ids, context=None):
if not context:
context = {}
+ 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,))
- partner = self.browse(cr, uid, id,context=context).partner_id
+ partner = self.browse(cr, uid, id,context=ctx).partner_id
if partner.lang:
- context.update({'lang': partner.lang})
- for taxe in ait_obj.compute(cr, uid, id, context=context).values():
+ ctx.update({'lang': partner.lang})
+ for taxe in ait_obj.compute(cr, uid, id, context=ctx).values():
ait_obj.create(cr, uid, taxe)
# Update the stored value (fields.function), so we write to trigger recompute
- self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=context)
+ self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=ctx)
# self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context)
return True
if res and res['value']:
self.write(cr, uid, [inv.id], res['value'])
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
def action_move_create(self, cr, uid, ids, *args):
ait_obj = self.pool.get('account.invoice.tax')
# 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)
if not inv.tax_line:
for tax in compute_taxes.values():
ait_obj.create(cr, uid, tax)
if journal.centralisation:
raise osv.except_osv(_('UserError'),
_('Cannot create invoice move on centralised journal'))
+
+ line = self.finalize_invoice_move_lines(cr, uid, inv, line)
+
move = {'ref': inv.number, 'line_id': line, 'journal_id': journal_id, 'date': date}
period_id=inv.period_id and inv.period_id.id or False
if not period_id:
new_move_name = self.pool.get('account.move').browse(cr, uid, move_id).name
# make the invoice point to that move
self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name})
- self.pool.get('account.move').post(cr, uid, [move_id])
+ # Pass invoice in context in method post: used if you want to get the same
+ # account move reference when creating the same invoice after a cancelled one:
+ self.pool.get('account.move').post(cr, uid, [move_id], context={'invoice':inv})
self._log_event(cr, uid, ids)
return True
def action_number(self, cr, uid, ids, *args):
cr.execute('SELECT id, type, number, move_id, reference ' \
- 'FROM account_invoice ' \
- 'WHERE id IN ('+','.join(map(str,ids))+')')
+ 'FROM account_invoice ' \
+ 'WHERE id IN %s',
+ (tuple(ids),))
obj_inv = self.browse(cr, uid, ids)[0]
for (id, invtype, number, move_id, reference) in cr.fetchall():
if not number:
+ tmp_context = {
+ 'fiscalyear_id' : obj_inv.period_id.fiscalyear_id.id,
+ }
if obj_inv.journal_id.invoice_sequence_id:
sid = obj_inv.journal_id.invoice_sequence_id.id
- number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', {'fiscalyear_id': obj_inv.period_id.fiscalyear_id.id})
+ number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', context=tmp_context)
else:
- number = self.pool.get('ir.sequence').get(cr, uid,
- 'account.invoice.' + invtype)
+ number = self.pool.get('ir.sequence').get_id(cr, uid,
+ 'account.invoice.' + invtype,
+ 'code=%s',
+ context=tmp_context)
+ if not number:
+ raise osv.except_osv(_('Warning !'), _('There is no active invoice sequence defined for the journal !'))
+
if invtype in ('in_invoice', 'in_refund'):
ref = reference
else:
# will be automatically deleted too
account_move_obj.unlink(cr, uid, [i['move_id'][0]])
if i['payment_ids']:
- self.pool.get('account.move.line').write(cr, uid, i['payment_ids'], {'reconcile_partial_id': False})
+ account_move_line_obj = self.pool.get('account.move.line')
+ 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!'))
+
self.write(cr, uid, ids, {'state':'cancel', 'move_id':False})
self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
return True
line_ids = []
total = 0.0
line = self.pool.get('account.move.line')
- cr.execute('select id from account_move_line where move_id in ('+str(move_id)+','+str(invoice.move_id.id)+')')
+ cr.execute('SELECT id FROM account_move_line '\
+ 'WHERE move_id in %s',
+ ((move_id, invoice.move_id.id),))
lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) )
+
for l in lines+invoice.payment_ids:
if l.account_id.id==src_account_id:
line_ids.append(l.id)
total += (l.debit or 0.0) - (l.credit or 0.0)
+
if (not round(total,int(config['price_accuracy']))) or writeoff_acc_id:
self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context)
else:
raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") )
if not product:
if type in ('in_invoice', 'in_refund'):
- return {'domain':{'product_uom':[]}}
+ return {'value':{}, 'domain':{'product_uom':[]}}
else:
return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
part = self.pool.get('res.partner').browse(cr, uid, partner_id)
fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False
- lang=part.lang
- context.update({'lang': lang})
+ if part.lang:
+ context.update({'lang': part.lang})
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
domain = {}
result['uos_id'] = uom or res.uom_id.id or False
+ result['note'] = res.description
if result['uos_id']:
res2 = res.uom_id.category_id.id
if res2 :
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'])