##############################################################################
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 _
context = {}
type_inv = context.get('type', 'out_invoice')
type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
+ refund_journal = {'out_invoice': False, 'in_invoice': False, 'out_refund': True, 'in_refund': True}
journal_obj = self.pool.get('account.journal')
- res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale'))], limit=1)
+ res = journal_obj.search(cr, uid, [('type', '=', type2journal.get(type_inv, 'sale')), ('refund_journal', '=', refund_journal.get(type_inv, False))], limit=1)
if res:
return res[0]
else:
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:
- paid_amt = 0.0
- to_pay = inv.amount_total
- for lines in inv.move_lines:
- paid_amt = paid_amt - lines.credit + lines.debit
- res[inv.id] = to_pay - abs(paid_amt)
+ 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:
+ 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:
if not move_lines:
res[id] = []
continue
+ res[id] = []
data_lines = self.pool.get('account.move.line').browse(cr,uid,move_lines)
+ partial_ids = []# Keeps the track of ids where partial payments are done with payment terms
for line in data_lines:
ids_line = []
if line.reconcile_id:
elif line.reconcile_partial_id:
ids_line = line.reconcile_partial_id.line_partial_ids
l = map(lambda x: x.id, ids_line)
- res[id]=[x for x in l if x <> line.id]
+ partial_ids.append(line.id)
+ res[id] =[x for x in l if x <> line.id and x not in partial_ids]
return res
def _get_invoice_line(self, cr, uid, ids, context=None):
src = []
lines = []
for m in self.pool.get('account.move.line').browse(cr, uid, moves, context):
+ temp_lines = []#Added temp list to avoid duplicate records
if m.reconcile_id:
- lines += map(lambda x: x.id, m.reconcile_id.line_id)
+ temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
elif m.reconcile_partial_id:
- lines += map(lambda x: x.id, m.reconcile_partial_id.line_partial_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
('cancel','Cancelled')
],'State', select=True, readonly=True),
- 'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}),
+ 'date_invoice': fields.date('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
'date_due': fields.date('Due Date', states={'open':[('readonly',True)],'close':[('readonly',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."),
+ "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)]}),
'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True, states={'draft':[('readonly',False)]}),
'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True, required=True, states={'draft':[('readonly',False)]}),
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."),
- 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation date."),
+ 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}),
'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."),
'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', 'Invoice Movement', readonly=True, help="Link to the automatically generated account moves."),
- 'amount_untaxed': fields.function(_amount_all, method=True, digits=(16,2),string='Untaxed',
+ 'amount_untaxed': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])),string='Untaxed',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+ '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, None, 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
},
multi='all'),
- 'amount_tax': fields.function(_amount_all, method=True, digits=(16,2), string='Tax',
+ 'amount_tax': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Tax',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+ '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, None, 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
},
multi='all'),
- 'amount_total': fields.function(_amount_all, method=True, digits=(16,2), string='Total',
+ 'amount_total': fields.function(_amount_all, method=True, digits=(16, int(config['price_accuracy'])), string='Total',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 20),
+ '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, None, 20),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 20),
},
multi='all'),
'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}),
'journal_id': fields.many2one('account.journal', 'Journal', required=True,readonly=True, states={'draft':[('readonly',False)]}),
'company_id': fields.many2one('res.company', 'Company', required=True),
- 'check_total': fields.float('Total', digits=(16,2), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
+ 'check_total': fields.float('Total', digits=(16, int(config['price_accuracy'])), states={'open':[('readonly',True)],'close':[('readonly',True)]}),
'reconciled': fields.function(_reconciled, method=True, string='Paid/Reconciled', type='boolean',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50),
+ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ?
'account.move.line': (_get_invoice_from_line, None, 50),
'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
}, 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,2),string='Residual',
+ 'residual': fields.function(_amount_residual, method=True, digits=(16, int(config['price_accuracy'])),string='Residual',
store={
- 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50),
+ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 50),
'account.invoice.tax': (_get_invoice_tax, None, 50),
- 'account.invoice.line': (_get_invoice_line, None, 50),
+ 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount'], 50),
'account.move.line': (_get_invoice_from_line, None, 50),
'account.move.reconcile': (_get_invoice_from_reconcile, None, 50),
},
self.pool.get('res.users').browse(cr, uid, uid,
context=context).company_id.id,
'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):
- for inv in self.browse(cr, uid, ids, context):
- print inv.name, inv.amount_total
- for line in inv.invoice_line:
- print line.price_subtotal
-# invoices = self.read(cr, uid, ids, ['state'])
-# unlink_ids = []
-# for t in invoices:
-# if t['state'] in ('draft', 'cancel'):
-# unlink_ids.append(t['id'])
-# else:
-# raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
-# osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
-# return True
-
- #def unlink(self, cr, uid, ids, context=None):
- # invoices = self.read(cr, uid, ids, ['state'])
- # unlink_ids = []
- # for t in invoices:
- # if t['state'] in ('draft', 'cancel'):
- # unlink_ids.append(t['id'])
- # else:
- # raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
- # osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
- # return True
+ invoices = self.read(cr, uid, ids, ['state'])
+ unlink_ids = []
+ for t in invoices:
+ if t['state'] in ('draft', 'cancel'):
+ unlink_ids.append(t['id'])
+ else:
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete invoice(s) that are already opened or paid !'))
+ osv.osv.unlink(self, cr, uid, unlink_ids, context=context)
+ return True
# def get_invoice_address(self, cr, uid, ids):
# res = self.pool.get('res.partner').address_get(cr, uid, [part], ['invoice'])
# 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 payment_term != partner_payment_term:
- if partner_payment_term:
- to_update = self.onchange_payment_term_date_invoice(
- cr,uid,ids,partner_payment_term,date_invoice)
- result['value'].update(to_update['value'])
- else:
- result['value']['date_due'] = False
-
- 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
return {}
res={}
pt_obj= self.pool.get('account.payment.term')
-
if not date_invoice :
date_invoice = time.strftime('%Y-%m-%d')
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):
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
# return the ids of the move lines which has the same account than the invoice
# whose id is in ids
def move_line_id_payment_get(self, cr, uid, ids, *args):
- ml = self.pool.get('account.move.line')
res = []
- for inv in self.read(cr, uid, ids, ['move_id','account_id']):
- if inv['move_id']:
- move_line_ids = ml.search(cr, uid, [('move_id', '=', inv['move_id'][0])])
- for line in ml.read(cr, uid, move_line_ids, ['account_id']):
- if line['account_id']==inv['account_id']:
- res.append(line['id'])
+ 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 %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).partner_id
- context.update({'lang': partner.lang})
- for taxe in ait_obj.compute(cr, uid, id, context=context).values():
+ partner = self.browse(cr, uid, id,context=ctx).partner_id
+ if partner.lang:
+ 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, {}, 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
})]
return iml
+ def action_date_assign(self, cr, uid, ids, *args):
+ for inv in self.browse(cr, uid, ids):
+ res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice)
+ 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')
cur_obj = self.pool.get('res.currency')
# 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)
line2 = {}
for x, y, l in line:
tmp = str(l['account_id'])
- tmp += '-'+str('tax_code_id' in l and l['tax_code_id'] or "False")
- tmp += '-'+str('product_id' in l and l['product_id'] or "False")
- tmp += '-'+str('analytic_account_id' in l and l['analytic_account_id'] or "False")
-
+ tmp += '-'+str(l.get('tax_code_id',"False"))
+ tmp += '-'+str(l.get('product_id',"False"))
+ tmp += '-'+str(l.get('analytic_account_id',"False"))
+ tmp += '-'+str(l.get('date_maturity',"False"))
+
if tmp in line2:
am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit'])
line2[tmp]['debit'] = (am > 0) and am or 0.0
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:
for i in line:
i[2]['period_id'] = period_id
- move_id = self.pool.get('account.move').create(cr, uid, move)
+ move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
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:
def action_cancel(self, cr, uid, ids, *args):
account_move_obj = self.pool.get('account.move')
- invoices = self.read(cr, uid, ids, ['move_id'])
+ invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids'])
for i in invoices:
if i['move_id']:
account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
# Note that the corresponding move_lines and move_reconciles
# will be automatically deleted too
account_move_obj.unlink(cr, uid, [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'])
+ 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
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, lines):
+ def _refund_cleanup_lines(self, cr, uid, lines):
for line in lines:
del line['id']
del line['invoice_id']
invoice_lines = self.pool.get('account.invoice.line').read(cr, uid, invoice['invoice_line'])
- invoice_lines = self._refund_cleanup_lines(invoice_lines)
+ 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 = filter(lambda l: l['manual'], tax_lines)
- tax_lines = self._refund_cleanup_lines(tax_lines)
+ tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines)
if not date :
date = time.strftime('%Y-%m-%d')
invoice.update({
date=context['date_p']
else:
date=time.strftime('%Y-%m-%d')
+
+ # Take the amount in currency and the currency of the payment
+ if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']:
+ amount_currency = context['amount_currency']
+ currency_id = context['currency_id']
+ else:
+ amount_currency = False
+ currency_id = False
+
+ if invoice.type in ('in_invoice', 'in_refund'):
+ ref = invoice.reference
+ else:
+ ref = self._convert_ref(cr, uid, invoice.number)
+ # Pay attention to the sign for both debit/credit AND amount_currency
l1 = {
'debit': direction * pay_amount>0 and direction * pay_amount,
'credit': direction * pay_amount<0 and - direction * pay_amount,
'account_id': src_account_id,
'partner_id': invoice.partner_id.id,
- 'ref':invoice.number,
+ 'ref':ref,
+ 'date': date,
+ 'currency_id':currency_id,
+ 'amount_currency':amount_currency and direction * amount_currency or 0.0,
}
l2 = {
'debit': direction * pay_amount<0 and - direction * pay_amount,
'credit': direction * pay_amount>0 and direction * pay_amount,
'account_id': pay_account_id,
'partner_id': invoice.partner_id.id,
- 'ref':invoice.number,
+ 'ref':ref,
+ 'date': date,
+ 'currency_id':currency_id,
+ 'amount_currency':amount_currency and - direction * amount_currency or 0.0,
}
if not name:
l2['name'] = name
lines = [(0, 0, l1), (0, 0, l2)]
- move = {'ref': invoice.number, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
- move_id = self.pool.get('account.move').create(cr, uid, move)
+ move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date}
+ move_id = self.pool.get('account.move').create(cr, uid, move, context=context)
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 total) or writeoff_acc_id:
+
+ 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:
self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context)
class account_invoice_line(osv.osv):
def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict):
res = {}
+ cur_obj=self.pool.get('res.currency')
for line in self.browse(cr, uid, ids):
- res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),2)
+ if line.invoice_id:
+ res[line.id] = line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0)
+ cur = line.invoice_id.currency_id
+ res[line.id] = cur_obj.round(cr, uid, cur, res[line.id])
+ else:
+ res[line.id] = round(line.price_unit * line.quantity * (1-(line.discount or 0.0)/100.0),int(config['price_accuracy']))
return res
+
def _price_unit_default(self, cr, uid, context=None):
if context is None:
context = {}
if 'check_total' in context:
t = context['check_total']
for l in context.get('invoice_line', {}):
- if len(l) >= 3 and l[2]:
+ if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]:
tax_obj = self.pool.get('account.tax')
p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0)
t = t - (p * l[2].get('quantity'))
'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
'price_unit': fields.float('Unit Price', required=True, digits=(16, int(config['price_accuracy']))),
- 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True),
+ 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal',store=True, type="float", digits=(16, int(config['price_accuracy']))),
'quantity': fields.float('Quantity', required=True),
- 'discount': fields.float('Discount (%)', digits=(16,2)),
+ 'discount': fields.float('Discount (%)', digits=(16, int(config['price_accuracy']))),
'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'),
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)
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 = self.pool.get('account.fiscal.position').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, qty, address_invoice_id, product, partner_id, context=context)
+ 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)
else:
result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id})
if not name:
- result['name'] = res.name
+ result['name'] = res.partner_ref
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 :
'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True),
'name': fields.char('Tax Description', size=64, required=True),
'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]),
- 'base': fields.float('Base', digits=(16,2)),
- 'amount': fields.float('Amount', digits=(16,2)),
+ 'base': fields.float('Base', digits=(16,int(config['price_accuracy']))),
+ 'amount': fields.float('Amount', digits=(16,int(config['price_accuracy']))),
'manual': fields.boolean('Manual'),
'sequence': fields.integer('Sequence'),
'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=(16,2)),
+ 'base_amount': fields.float('Base Code Amount', digits=(16,int(config['price_accuracy']))),
'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=(16,2)),
+ 'tax_amount': fields.float('Tax Code Amount', digits=(16,int(config['price_accuracy']))),
}
- def base_change(self, cr, uid, ids, base):
+
+ def base_change(self, cr, uid, ids, base,currency_id=False,company_id=False,date_invoice=False):
+ cur_obj = self.pool.get('res.currency')
+ company_obj = self.pool.get('res.company')
+ company_currency=False
+ if company_id:
+ company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
+ if currency_id and company_currency:
+ base = cur_obj.compute(cr, uid, currency_id, company_currency, base, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
return {'value': {'base_amount':base}}
- def amount_change(self, cr, uid, ids, amount):
+
+ def amount_change(self, cr, uid, ids, amount,currency_id=False,company_id=False,date_invoice=False):
+ cur_obj = self.pool.get('res.currency')
+ company_obj = self.pool.get('res.company')
+ company_currency=False
+ if company_id:
+ company_currency = company_obj.read(cr,uid,[company_id],['currency_id'])[0]['currency_id'][0]
+ if currency_id and company_currency:
+ amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False)
return {'value': {'tax_amount':amount}}
+
_order = 'sequence'
_defaults = {
'manual': lambda *a: 1,
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'])