Improve payment, invoice, statement

- Use the right reference from invoice on account move lines
- Add a reference type on invoice
- Payment are now multi-currency
- Add wizard to populate statement
- Make thread safe payment wizard
- Add mod10r in utils
- Add colors on statement lines

bzr revid: ced-35849fea0ca75fec483ba54836f4954c1b26d2c9
This commit is contained in:
ced 2007-10-04 05:59:39 +00:00
parent ddae539d94
commit 1d480a1df3
13 changed files with 635 additions and 227 deletions

View File

@ -507,6 +507,25 @@ class account_bank_statement_line(osv.osv):
currency_id, balance, context=context)
return {'value': {'amount': balance, 'account_id': account_id}}
def _reconcile_amount(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
res_currency_obj = self.pool.get('res.currency')
res_users_obj = self.pool.get('res.users')
res = {}
company_currency_id = res_users_obj.browse(cursor, user, user,
context=context).company_id.currency_id.id
for line in self.browse(cursor, user, ids, context=context):
if line.reconcile_id:
res[line.id] = res_currency_obj.compute(cursor, user,
company_currency_id, line.statement_id.currency.id,
line.reconcile_id.total_entry, context=context)
else:
res[line.id] = 0.0
return res
_order = "date,name desc"
_name = "account.bank.statement.line"
_description = "Bank Statement Line"
@ -514,14 +533,25 @@ class account_bank_statement_line(osv.osv):
'name': fields.char('Name', size=64, required=True),
'date': fields.date('Date', required=True),
'amount': fields.float('Amount'),
'type': fields.selection([('supplier','Supplier'),('customer','Customer'),('general','General')], 'Type', required=True),
'type': fields.selection([
('supplier','Supplier'),
('customer','Customer'),
('general','General')
], 'Type', required=True),
'partner_id': fields.many2one('res.partner', 'Partner'),
'account_id': fields.many2one('account.account','Account', required=True),
'statement_id': fields.many2one('account.bank.statement', 'Statement', select=True, required=True),
'reconcile_id': fields.many2one('account.bank.statement.reconcile', 'Reconcile', states={'confirm':[('readonly',True)]}),
'move_ids': fields.many2many('account.move', 'account_bank_statement_line_move_rel', 'move_id','statement_id', 'Moves'),
'account_id': fields.many2one('account.account','Account',
required=True),
'statement_id': fields.many2one('account.bank.statement', 'Statement',
select=True, required=True),
'reconcile_id': fields.many2one('account.bank.statement.reconcile',
'Reconcile', states={'confirm':[('readonly',True)]}),
'move_ids': fields.many2many('account.move',
'account_bank_statement_line_move_rel', 'move_id','statement_id',
'Moves'),
'ref': fields.char('Ref.', size=32),
'note': fields.text('Notes')
'note': fields.text('Notes'),
'reconcile_amount': fields.function(_reconcile_amount,
string='Amount reconciled', method=True, type='float'),
}
_defaults = {
'name': lambda self,cr,uid,context={}: self.pool.get('ir.sequence').get(cr, uid, 'account.bank.statement.line'),

View File

@ -139,8 +139,10 @@
<field name="name" select="2"/>
<field name="name" select="2" colspan="4"/>
<newline/>
<field name="reference" select="1"/>
<field name="reference_type" select="2"/>
<field name="check_total" required="2"/>
<field name="currency_id" on_change="onchange_currency_id(currency_id)" select="2"/>
@ -261,7 +263,9 @@
<newline/>
<field name="partner_bank_id" domain="[('partner_id','=',partner_id)]"/>
<field name="date_due" select="1"/>
<newline/>
<field name="reference" select="2"/>
<field name="reference_type" select="2"/>
<field name="origin"/>
<field name="address_contact_id" domain="[('partner_id','=',partner_id)]" colspan="4"/>
<field name="move_id"/>

View File

@ -324,7 +324,8 @@
<notebook colspan="4">
<page string="Entry encoding">
<field name="line_ids" colspan="4" nolabel="1">
<tree string="Statement lines" editable="bottom">
<tree string="Statement lines" editable="bottom"
colors="red:amount!=reconcile_amount and reconcile_id">
<field name="date"/>
<field name="ref"/>
<field name="name"/>
@ -335,7 +336,8 @@
domain="[('journal_id','=',parent.journal_id)]"/>
<field name="amount"/>
<field name="reconcile_id"
context="{'partner_id': partner_id, 'amount': amount, 'account_id': account_id, 'currency_id': parent.currency}"/>
context="{'partner_id': partner_id, 'amount': amount, 'account_id': account_id, 'currency_id': parent.currency}"/>
<field name="reconcile_amount" invisible="1"/>
</tree>
<form string="Statement lines">
<field name="date"/>

View File

@ -95,6 +95,9 @@ class account_invoice(osv.osv):
res[id] = self.test_paid(cr, uid, [id])
return res
def _get_reference_type(self, cursor, user, context=None):
return [('none', 'None')]
_name = "account.invoice"
_description = 'Invoice'
_order = "number"
@ -110,6 +113,8 @@ class account_invoice(osv.osv):
'number': fields.char('Invoice Number', size=32, readonly=True),
'reference': fields.char('Invoice Reference', size=64),
'reference_type': fields.selection(_get_reference_type, 'Reference Type',
required=True),
'comment': fields.text('Additionnal Information'),
'state': fields.selection([
@ -152,9 +157,12 @@ class account_invoice(osv.osv):
'state': lambda *a: 'draft',
'journal_id': _get_journal,
'currency_id': _get_currency,
'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id,
'company_id': lambda self, cr, uid, context: \
self.pool.get('res.users').browse(cr, uid, uid,
context=context).company_id.id,
'reference_type': lambda *a: 'none',
}
def unlink(self, cr, uid, ids):
invoices = self.read(cr, uid, ids, ['state'])
unlink_ids = []
@ -317,6 +325,10 @@ class account_invoice(osv.osv):
iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id)
for il in iml:
if il['account_analytic_id']:
if inv.type in ('in_invoice', 'in_refund'):
ref = inv.reference
else:
ref = self._convert_ref(cr, uid, inv.number)
il['analytic_lines'] = [(0,0, {
'name': il['name'],
'date': inv['date_invoice'],
@ -327,7 +339,7 @@ class account_invoice(osv.osv):
'product_uom_id': il['uos_id'],
'general_account_id': il['account_id'],
'journal_id': self._get_journal_analytic(cr, uid, inv.type),
'ref': self._convert_ref(cr, uid, inv['number']),
'ref': ref,
})]
return iml
@ -370,7 +382,12 @@ class account_invoice(osv.osv):
# one move line per tax line
iml += ait_obj.move_line_get(cr, uid, inv.id)
if inv.type in ('in_invoice', 'in_refund'):
ref = inv.reference
else:
ref = self._convert_ref(cr, uid, inv.number)
diff_currency_p = inv.currency_id.id <> company_currency
# create one move line for the total and possibly adjust the other lines amount
total = 0
total_currency = 0
@ -378,11 +395,13 @@ class account_invoice(osv.osv):
if inv.currency_id.id != company_currency:
i['currency_id'] = inv.currency_id.id
i['amount_currency'] = i['price']
i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, i['price'], context={'date': inv.date_invoice})
i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
company_currency, i['price'],
context={'date': inv.date_invoice})
else:
i['amount_currency'] = False
i['currency_id'] = False
i['ref'] = self._convert_ref(cr, uid, inv.number)
i['ref'] = ref
if inv.type in ('out_invoice','in_refund'):
total += i['price']
total_currency += i['amount_currency'] or i['price']
@ -409,9 +428,31 @@ class account_invoice(osv.osv):
if i == len(totlines):
amount_currency += res_amount_currency
iml.append({ 'type':'dest', 'name':name, 'price': t[1], 'account_id':acc_id, 'date_maturity': t[0], 'amount_currency': amount_currency, 'currency_id': inv.currency_id.id, 'ref': inv.number})
iml.append({
'type': 'dest',
'name': name,
'price': t[1],
'account_id': acc_id,
'date_maturity': t[0],
'amount_currency': diff_currency_p \
and amount_currency or False,
'currency_id': diff_currency_p \
and inv.currency_id.id or False,
'ref': ref,
})
else:
iml.append({ 'type':'dest', 'name':name, 'price': total, 'account_id':acc_id, 'date_maturity' : inv.date_due or False, 'amount_currency': total_currency, 'currency_id': inv.currency_id.id, 'ref': inv.number})
iml.append({
'type': 'dest',
'name': name,
'price': total,
'account_id': acc_id,
'date_maturity' : inv.date_due or False,
'amount_currency': diff_currency_p \
and total_currency or False,
'currency_id': diff_currency_p \
and inv.currency_id.id or False,
'ref': ref
})
date = inv.date_invoice
part = inv.partner_id.id
@ -452,13 +493,17 @@ class account_invoice(osv.osv):
}
def action_number(self, cr, uid, ids, *args):
cr.execute('SELECT id, type, number, move_id FROM account_invoice WHERE id IN ('+','.join(map(str,ids))+')')
for (id, invtype, number, move_id) in cr.fetchall():
cr.execute('SELECT id, type, number, move_id, reference FROM account_invoice WHERE id IN ('+','.join(map(str,ids))+')')
for (id, invtype, number, move_id, reference) in cr.fetchall():
if not number:
number = self.pool.get('ir.sequence').get(cr, uid, 'account.invoice.'+invtype)
if type in ('in_invoice', 'in_refund'):
ref = reference
else:
ref = self._convert_ref(cr, uid, number)
cr.execute('UPDATE account_invoice SET number=%s WHERE id=%d', (number, id))
cr.execute('UPDATE account_move_line SET ref=%s WHERE move_id=%d and ref is null', (self._convert_ref(cr, uid, number), move_id))
cr.execute('UPDATE account_analytic_line SET ref=%s FROM account_move_line WHERE account_move_line.move_id=%d AND account_analytic_line.move_id=account_move_line.id', (self._convert_ref(cr, uid, number), move_id))
cr.execute('UPDATE account_move_line SET ref=%s WHERE move_id=%d and ref is null', (ref, move_id))
cr.execute('UPDATE account_analytic_line SET ref=%s FROM account_move_line WHERE account_move_line.move_id=%d AND account_analytic_line.move_id=account_move_line.id', (ref, move_id))
return True
def action_cancel(self, cr, uid, ids, *args):

View File

@ -1,16 +1,18 @@
{
"name" : "Payment Management",
"version" : "1.0",
"author" : "Tiny",
"category" : "Generic Modules/Payment",
"depends" : ["account"],
"init_xml" : [],
"name": "Payment Management",
"version": "1.0",
"author": "Tiny",
"category": "Generic Modules/Payment",
"depends": ["account"],
"init_xml": [],
"description": """
This is the module which Provide an more efficient way to manage invoice payment,
Provide a basic mechanism to easily plug various automated payment.
""",
"demo_xml" : [],
"update_xml" : ["payment_wizard.xml","payment_view.xml","payment_workflow.xml","payment_sequence.xml"],
This module provide :
* a more efficient way to manage invoice payment.
* a basic mechanism to easily plug various automated payment.
""",
"demo_xml": [],
"update_xml": ["payment_wizard.xml", "payment_view.xml",
"payment_workflow.xml", "payment_sequence.xml"],
"active": False,
"installable": True
}

View File

@ -25,8 +25,8 @@
#
##############################################################################
from osv import fields
from osv import osv
from osv import fields, osv
class account_move_line(osv.osv):
_inherit = "account.move.line"
@ -37,8 +37,11 @@ class account_move_line(osv.osv):
if not ids:
return {}
cr.execute("""SELECT ml.id,
ml.credit -
(SELECT coalesce(sum(amount),0)
CASE WHEN ml.amount_currency < 0
THEN - ml.amount_currency
ELSE ml.credit
END -
(SELECT coalesce(sum(amount_currency),0)
FROM payment_line pl
INNER JOIN payment_order po
ON (pl.order_id = po.id)
@ -49,36 +52,42 @@ class account_move_line(osv.osv):
r=dict(cr.fetchall())
return r
def _to_pay_search(self, cr, uid, obj, name, args):
if not len(args):
return []
query = self.pool.get('account.move.line')._query_get(cr, uid, context={})
where = ' and '.join(map(lambda x: '''(select l.credit - coalesce(sum(amount),0)
from payment_line pl
inner join payment_order po on (pl.order_id = po.id)
where move_line_id = l.id and po.state != 'cancel') '''+x[1]+str(x[2])+' ',args))
line_obj = self.pool.get('account.move.line')
query = line_obj._query_get(cr, uid, context={})
where = ' and '.join(map(lambda x: '''(SELECT
CASE WHEN l.amount_currency < 0
THEN - l.amount_currency
ELSE l.credit
END - coalesce(sum(pl.amount_currency), 0)
FROM payment_line pl
INNER JOIN payment_order po ON (pl.order_id = po.id)
WHERE move_line_id = l.id AND po.state != 'cancel')''' \
+ x[1] + str(x[2])+' ',args))
cr.execute(('''select id
from account_move_line l
where account_id in (select id from account_account where type=%s and active)
and reconcile_id is null
and credit > 0
and '''+where+' and '+query), ('payable',) )
from account_move_line l
where account_id in (select id
from account_account
where type=%s and active)
and reconcile_id is null
and credit > 0
and ''' + where + ' and ' + query), ('payable',) )
res = cr.fetchall()
if not len(res):
return [('id','=','0')]
return [('id','in',map(lambda x:x[0], res))]
def line2bank(self,cr,uid,ids,payment_type= 'manual',context=None):
"""
Try to return for each account move line a corresponding bank
account according to the payment type. This work using one of
the bank of the partner defined on the invoice eventually
associated to the line.
Return the first suitable bank for the corresponding partner.
Return the first suitable bank for the corresponding partner.
"""
if not ids: return {}
bank_type= self.pool.get('payment.mode').suitable_bank_types(cr,uid,payment_type,context=context)
@ -102,10 +111,11 @@ class account_move_line(osv.osv):
elif (line not in type_ok) and (t in bank_type) :
line2bank[line]= bank
type_ok.append(line)
return line2bank
_columns = {
'amount_to_pay' : fields.function(amount_to_pay, method=True, type='float', string='Amount to pay', fnct_search=_to_pay_search),
}
'amount_to_pay' : fields.function(amount_to_pay, method=True,
type='float', string='Amount to pay', fnct_search=_to_pay_search),
}
account_move_line()

View File

@ -29,6 +29,8 @@ from osv import fields
from osv import osv
import time
import netsvc
class payment_type(osv.osv):
_name= 'payment.type'
_description= 'Payment type'
@ -36,12 +38,11 @@ class payment_type(osv.osv):
'name': fields.char('Name', size=64, required=True),
'code': fields.char('Code', size=64, required=True),
'suitable_bank_types': fields.many2many('res.partner.bank.type',
'bank_type_payment_type_rel',
'pay_type_id','bank_type_id',
'Suitable bank types')
}
'bank_type_payment_type_rel',
'pay_type_id','bank_type_id',
'Suitable bank types')
}
payment_type()
@ -50,19 +51,23 @@ class payment_mode(osv.osv):
_description= 'Payment mode'
_columns= {
'name': fields.char('Name', size=64, required=True),
'bank_id': fields.many2one('res.partner.bank',"Bank account",required=True),
'journal': fields.many2one('account.journal','Journal',required=True,domain=[('type','=','cash')]),
'bank_id': fields.many2one('res.partner.bank', "Bank account",
required=True),
'journal': fields.many2one('account.journal', 'Journal', required=True,
domain=[('type', '=', 'cash')]),
'type': fields.many2one('payment.type','Payment type',required=True),
'account': fields.many2one('account.account','Default payable account',domain=[('type','=','payable')],required=True),
}
'account': fields.many2one('account.account', 'Default payable account',
domain=[('type', '=', 'payable')], required=True),
}
def suitable_bank_types(self,cr,uid,payment_code= 'manual',context={}):
""" Return the codes of the bank type that are suitable for the given payment type code"""
"""Return the codes of the bank type that are suitable
for the given payment type code"""
cr.execute(""" select t.code
from res_partner_bank_type t
join bank_type_payment_type_rel r on (r.bank_type_id = t.id)
join payment_type pt on (r.pay_type_id = pt.id)
where pt.code = %s """, [payment_code])
from res_partner_bank_type t
join bank_type_payment_type_rel r on (r.bank_type_id = t.id)
join payment_type pt on (r.pay_type_id = pt.id)
where pt.code = %s """, [payment_code])
return [x[0] for x in cr.fetchall()]
payment_mode()
@ -75,18 +80,20 @@ class payment_order(osv.osv):
def get_wizard(self,type):
logger = netsvc.Logger()
logger.notifyChannel("warning", netsvc.LOG_WARNING, "No wizard found for the payment type '%s'."%type)
logger.notifyChannel("warning", netsvc.LOG_WARNING,
"No wizard found for the payment type '%s'." % type)
return None
def _total(self, cr, uid, ids, name, args, context={}):
if not ids: return {}
cr.execute("""SELECT o.id, coalesce(sum(l.amount),0)
FROM payment_order o
LEFT JOIN payment_line l
ON (o.id = l.order_id)
WHERE o.id in (%s)
GROUP BY o.id""" % ','.join(map(str,ids)))
return dict(cr.fetchall())
def _total(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
res = {}
for order in self.browse(cursor, user, ids, context=context):
if order.line_ids:
res[order.id] = reduce(lambda x, y: x + y.amount, order.line_ids, 0.0)
else:
res[order.id] = 0.0
return res
def nb_line(self, cr, uid, ids, name, args, context={}):
if not ids: return {}
@ -102,15 +109,24 @@ class payment_order(osv.osv):
'date_planned': fields.date('Scheduled date if fixed'),
'reference': fields.char('Reference',size=128),
'mode': fields.many2one('payment.mode','Payment mode', select=True),
'state': fields.selection([('draft', 'Draft'),('open','Open'),
('cancel','Cancelled'),('done','Done')], 'State', select=True),
'state': fields.selection([
('draft', 'Draft'),
('open','Open'),
('cancel','Cancelled'),
('done','Done')], 'State', select=True),
'line_ids': fields.one2many('payment.line','order_id','Payment lines'),
'total': fields.function(_total, string="Total", method=True, type='float'),
'total': fields.function(_total, string="Total", method=True,
type='float'),
'user_id': fields.many2one('res.users','User',required=True),
'nb_line': fields.function(nb_line,string='Number of payment',method=True, type='integer'),
'date_prefered': fields.selection([('now','Directly'),('due','Due date'),('fixed','Fixed date')],"Prefered date",required=True),
'date_created': fields.date('Creation date',readonly=True),
'date_done': fields.date('Execution date',readonly=True),
'nb_line': fields.function(nb_line,string='Number of payment',
method=True, type='integer'),
'date_prefered': fields.selection([
('now', 'Directly'),
('due', 'Due date'),
('fixed', 'Fixed date')
], "Prefered date", required=True),
'date_created': fields.date('Creation date', readonly=True),
'date_done': fields.date('Execution date', readonly=True),
}
_defaults = {
@ -118,7 +134,9 @@ class payment_order(osv.osv):
'state': lambda *a: 'draft',
'date_prefered': lambda *a: 'due',
'date_created': lambda *a: time.strftime('%Y-%m-%d'),
}
'reference': lambda self,cr,uid,context: self.pool.get('ir.sequence'
).get(cr, uid, 'payment.order'),
}
def set_to_draft(self, cr, uid, ids, *args):
self.write(cr, uid, ids, {'state':'draft'})
@ -135,70 +153,198 @@ class payment_order(osv.osv):
return True
def set_done(self, cr, uid, id, *args):
self.write(cr,uid,id,{'date_done':time.strftime('%Y-%m-%d'),
'state':'done',})
self.write(cr,uid,id,{'date_done': time.strftime('%Y-%m-%d'),
'state': 'done',})
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'payment.order', id, 'done', cr)
return True
payment_order()
class payment_line(osv.osv):
_name = 'payment.line'
_description = 'Payment Line'
_rec_name = 'move_line_id'
def partner_payable(self, cr, uid, ids, name, args, context={}):
if not ids: return {}
partners= self.read(cr, uid, ids, ['partner_id'], context)
partners= dict(map(lambda x: (x['id'],x['partner_id']),partners))
debit= self.pool.get('res.partner')._debit_get(cr, uid,
partners.values(), name,
args, context)
partners= dict(map(lambda x: (x['id'], x['partner_id'][0]), partners))
debit = self.pool.get('res.partner')._debit_get(cr, uid,
partners.values(), name, args, context)
for i in partners:
partners[i]= debit[partners[i]]
partners[i] = debit[partners[i]]
return partners
def translate(self,orig):
return {"to_pay":"credit",
"due_date":"date_maturity",
"reference":"ref"}.get(orig,orig)
def select_by_name(self, cr, uid, ids, name, args, context={}):
def translate(self, orig):
return {"to_pay": "credit",
"due_date": "date_maturity",
"reference": "ref"}.get(orig, orig)
def select_by_name(self, cr, uid, ids, name, args, context=None):
if not ids: return {}
cr.execute("""SELECT pl.id, ml.%s
from account_move_line ml
inner join payment_line pl on (ml.id= pl.move_line_id)
where pl.id in (%s)"""% (self.translate(name),','.join(map(str,ids))) )
return dict(cr.fetchall())
partner_obj = self.pool.get('res.partner')
cr.execute("""SELECT pl.id, ml.%s
from account_move_line ml
inner join payment_line pl
on (ml.id = pl.move_line_id)
where pl.id in (%s)"""%
(self.translate(name), ','.join(map(str,ids))) )
res = dict(cr.fetchall())
if name == 'partner_id':
partner_name = {}
for p_id, p_name in partner_obj.name_get(cr,uid,
filter(lambda x:x and x != 0,res.values()),context=context):
partner_name[p_id] = p_name
for id in ids:
if id in res:
res[id] = (res[id],partner_name[res[id]])
else:
res[id] = (False,False)
else:
for id in ids:
res.setdefault(id, (False, ""))
return res
def _currency(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
res = {}
currency_obj = self.pool.get('res.currency')
account_obj = self.pool.get('account.account')
cursor.execute('''SELECT pl.id, ml.currency_id, ml.account_id
FROM account_move_line ml
INNER JOIN payment_line pl
ON (ml.id = pl.move_line_id)
WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
res2 = {}
account_ids = []
for payment_line_id, currency_id, account_id in cursor.fetchall():
res2[payment_line_id] = [currency_id, account_id]
account_ids.append(account_id)
account2currency_id = {}
for account in account_obj.browse(cursor, user, account_ids,
context=context):
account2currency_id[account.id] = account.company_currency_id.id
for payment_line_id in ids:
if res2[payment_line_id][0]:
res[payment_line_id] = res2[payment_line_id][0]
else:
res[payment_line_id] = \
account2currency_id[res2[payment_line_id][1]]
currency_names = {}
for currency_id, name in currency_obj.name_get(cursor, user, res.values(),
context=context):
currency_names[currency_id] = name
for payment_line_id in ids:
res[payment_line_id] = (res[payment_line_id],
currency_names[res[payment_line_id]])
return res
def _to_pay_currency(self, cursor, user, ids, name , args, context=None):
if not ids:
return {}
cursor.execute('''SELECT pl.id,
CASE WHEN ml.amount_currency < 0
THEN - ml.amount_currency
ELSE ml.credit
END
FROM account_move_line ml
INNER JOIN payment_line pl
ON (ml.id = pl.move_line_id)
WHERE pl.id in (''' + ','.join([str(x) for x in ids]) + ')')
return dict(cursor.fetchall())
def _amount(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
currency_obj = self.pool.get('res.currency')
if context is None:
context = {}
res = {}
for line in self.browse(cursor, user, ids, context=context):
ctx = context.copy()
ctx['date'] = line.order_id.date_done or time.strftime('%Y-%m-%d')
res[line.id] = currency_obj.compute(cursor, user, line.currency.id,
line.move_line_id.account_id.company_currency_id.id,
line.amount_currency, context=ctx)
return res
def _value_date(self, cursor, user, ids, name, args, context=None):
if not ids:
return {}
res = {}
for line in self.browse(cursor, user, ids, context=context):
if line.order_id.date_prefered == 'fixed':
res[line.id] = line.order_id.date_planned
elif line.order_id.date_prefered == 'due':
res[line.id] = line.due_date or time.strftime('%Y-%m-%d')
else:
res[line.id] = time.strftime('%Y-%m-%d')
return res
_columns = {
'move_line_id': fields.many2one('account.move.line','Entry line',required=True),
'amount': fields.float('Payment Amount', digits=(16,2), required=True),
'bank_id': fields.many2one('res.partner.bank','Bank account'),
'order_id': fields.many2one('payment.order','Order', ondelete='cascade', select=True),
'partner_id': fields.function(select_by_name, string="Partner", method=True, type='many2one', obj='res.partner'),
'to_pay': fields.function(select_by_name, string="To pay", method=True, type='float'),
'due_date': fields.function(select_by_name, string="Due date", method=True, type='date'),
'date_created': fields.function(select_by_name, string="Creation date", method=True, type='date'),
'reference': fields.function(select_by_name, string="Ref", method=True, type='char'),
'partner_payable': fields.function(partner_payable, string="Partner payable", method=True, type='float'),
'name': fields.char('Name', size=64, required=True, readonly=True),
'move_line_id': fields.many2one('account.move.line','Entry line',
required=True),
'amount_currency': fields.float('Amount', digits=(16,2),
required=True, help='Payment amount in the partner currency'),
'to_pay_currency': fields.function(_to_pay_currency, string='To Pay',
method=True, type='float',
help='Amount to pay in the partner currency'),
'currency': fields.function(_currency, string='Currency',
method=True, type='many2one', obj='res.currency'),
'bank_id': fields.many2one('res.partner.bank', 'Bank account'),
'order_id': fields.many2one('payment.order', 'Order', required=True,
ondelete='cascade', select=True),
'partner_id': fields.function(select_by_name, string="Partner",
method=True, type='many2one', obj='res.partner'),
'amount': fields.function(_amount, string='Amount',
method=True, type='float',
help='Payment amount in the company currency'),
'to_pay': fields.function(select_by_name, string="To Pay", method=True,
type='float', help='Amount to pay in the company currency'),
'due_date': fields.function(select_by_name, string="Due date",
method=True, type='date'),
'date_created': fields.function(select_by_name, string="Creation date",
method=True, type='date'),
'reference': fields.function(select_by_name, string="Ref", method=True,
type='char'),
'partner_payable': fields.function(partner_payable,
string="Partner payable", method=True, type='float'),
'value_date': fields.function(_value_date, string='Value Date',
method=True, type='date'),
}
def onchange_move_line(self, cr, uid, id, move_line_id, payment_type,context={}):
_defaults = {
'name': lambda obj, cursor, user, context: obj.pool.get('ir.sequence'
).get(cursor, user, 'payment.line'),
}
_sql_constraints = [
('name_uniq', 'UNIQUE(name)', 'The payment line name must be unique!'),
]
def onchange_move_line(self, cr, uid, id, move_line_id, payment_type,context=None):
if not move_line_id:
return {}
line=self.pool.get('account.move.line').browse(cr,uid,move_line_id)
return {'value': {'amount': line.amount_to_pay,
'to_pay': line.amount_to_pay,
return {'value': {
'amount_currency': line.amount_to_pay,
'to_pay_currency': line.amount_to_pay,
'partner_id': line.partner_id.id,
'reference': line.ref,
'date_created': line.date_created,
'bank_id': self.pool.get('account.move.line').line2bank(cr, uid,
[move_line_id],
payment_type or 'manual',context)[move_line_id]
payment_type or 'manual', context)[move_line_id]
}}
payment_line()

View File

@ -1,16 +1,29 @@
<?xml version="1.0"?>
<terp>
<data noupdate="1">
<record model="ir.sequence.type" id="seq_type_payment_order">
<field name="name">Payment order</field>
<field name="code">payment.order</field>
</record>
<data noupdate="1">
<record model="ir.sequence.type" id="seq_type_payment_order" forcecreate="1">
<field name="name">Payment order</field>
<field name="code">payment.order</field>
</record>
<record model="ir.sequence" id="seq_payment_order">
<field name="name">Payment order</field>
<field name="code">payment.order</field>
<field name="padding" eval="3"/>
<field name="prefix">%(year)s/</field>
</record>
</data>
<record model="ir.sequence" id="seq_payment_order" forcecreate="1">
<field name="name">Payment order</field>
<field name="code">payment.order</field>
<field name="padding" eval="3"/>
<field name="prefix">%(year)s/</field>
</record>
<record model="ir.sequence.type" id="seq_type_payment_line" forcecreate="1">
<field name="name">Payment Line</field>
<field name="code">payment.line</field>
</record>
<record model="ir.sequence" id="seq_payment_line" forcecreate="1">
<field name="name">Payment Line</field>
<field name="code">payment.line</field>
<field name="prefix"></field>
<field name="padding">3</field>
</record>
</data>
</terp>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" ?>
<terp>
<data>
<menuitem name="Financial Management/Payment Management" id="menu_main" />
<menuitem name="Financial Management/Payment Management/Configuration" groups="admin" sequence="1"/>
<menuitem name="Financial Management/Payment" id="menu_main" />
<menuitem name="Financial Management/Configuration" groups="admin" sequence="1"/>
<record model="ir.ui.view" id="view_payment_type_form">
<field name="name">payment.type.form</field>
@ -13,7 +13,7 @@
<field name="name" select="1"/>
<field name="code"/>
<newline/>
<field name="suitable_bank_types" select="1"/>
<field name="suitable_bank_types" select="1" colspan="4"/>
</form>
</field>
</record>
@ -23,7 +23,8 @@
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Financial Management/Payment Management/Configuration/Payment Type" id="menu_action_payment_type_form" action="action_payment_type_form"/>
<menuitem name="Financial Management/Configuration/Payment/Payment Type"
id="menu_action_payment_type_form" action="action_payment_type_form"/>
<record model="ir.ui.view" id="view_payment_mode_tree">
<field name="name">payment.mode.tree</field>
@ -58,10 +59,11 @@
<field name="view_mode">tree,form</field>
</record>
<menuitem
name="Financial Management/Payment Management/Configuration/Payment Mode"
id="menu_action_payment_mode_form" action="action_payment_mode_form"/>
<record model="ir.ui.view" id="view_payment_order_form">
<menuitem
name="Financial Management/Configuration/Payment/Payment Mode"
id="menu_action_payment_mode_form" action="action_payment_mode_form"/>
<record model="ir.ui.view" id="view_payment_order_form">
<field name="name">payment.order.form</field>
<field name="model">payment.order</field>
<field name="type">form</field>
@ -88,7 +90,7 @@
<button name="set_to_draft" states="cancel"
type="object" string="Set to draft"/>
</group>
</form>
</form>
</field>
</record>
@ -115,12 +117,12 @@
<field name="name">Payment order</field>
<field name="res_model">payment.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">form,tree</field>
</record>
<menuitem name="Financial Management/Payment Management/Payment Orders"
id="menu_action_payment_order_form"
action="action_payment_order_tree"
sequence="3"/>
<menuitem name="Financial Management/Payment/Payment Orders"
id="menu_action_payment_order_form"
action="action_payment_order_tree"
sequence="3"/>
<record model="ir.actions.act_window" id="action_payment_order_draft">
<field name="name">Draft payment order</field>
@ -129,10 +131,10 @@
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','draft')]</field>
</record>
<menuitem name="Financial Management/Payment Management/Payment Orders/Draft Payment Orders"
id="menu_action_payment_order_draft"
action="action_payment_order_draft"
sequence="1"/>
<menuitem name="Financial Management/Payment/Payment Orders/Draft Payment Orders"
id="menu_action_payment_order_draft"
action="action_payment_order_draft"
sequence="1"/>
<record model="ir.actions.act_window" id="action_payment_order_open">
<field name="name">Payment orders to validate</field>
@ -141,14 +143,10 @@
<field name="view_mode">tree,form</field>
<field name="domain">[('state','=','open')]</field>
</record>
<menuitem name="Financial Management/Payment Management/Payment Orders/Payment Orders to Validate"
id="menu_action_payment_order_open"
action="action_payment_order_open"
sequence="2"/>
<menuitem name="Financial Management/Payment/Payment Orders/Payment Orders to Validate"
id="menu_action_payment_order_open"
action="action_payment_order_open"
sequence="2"/>
<record model="ir.ui.view" id="view_payment_line_form">
<field name="name">Payment Line</field>
@ -156,15 +154,21 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Payment Line">
<field name="move_line_id"
select="1" on_change="onchange_move_line(move_line_id,parent.mode.type.code)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
<field name="amount" select="1" />
<field name="move_line_id"
select="1"
on_change="onchange_move_line(move_line_id,parent.mode.type.code)"
domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
<newline/>
<field name="amount_currency" select="1"/>
<field name="to_pay_currency"/>
<field name="currency"/>
<field name="bank_id" domain="[('partner_id','=',partner_id)]"/>
<field name="to_pay"/>
<field name="partner_id"/>
<field name="partner_payable"/>
<field name="reference"/>
<field name="due_date"/>
<field name="amount" select="2" />
<field name="to_pay"/>
<field name="partner_payable"/>
<field name="date_created"/>
</form>
</field>
@ -175,16 +179,23 @@
<field name="type">tree</field>
<field name="priority" eval="4"/>
<field name="arch" type="xml">
<tree string="Payment Line" editable="bottom" colors="red:to_pay&lt;amount" >
<field name="move_line_id" select="1" on_change="onchange_move_line(move_line_id,parent.mode)" domain="[('reconcile_id','=', False), ('credit', '>',0),('amount_to_pay','>',0)] "/>
<field name="bank_id" domain="[('partner_id','=',partner_id)]"/>
<field name="amount"/>
<field name="to_pay"/>
<tree string="Payment Line" editable="bottom"
colors="red:to_pay_currency&lt;amount_currency">
<field name="move_line_id" select="1"
on_change="onchange_move_line(move_line_id,parent.mode)"
domain="[('reconcile_id', '=', False), ('credit', '>', 0), ('amount_to_pay', '>', 0)]"/>
<field name="bank_id" domain="[('partner_id', '=', partner_id)]"/>
<field name="amount_currency"/>
<field name="to_pay_currency"/>
<field name="currency"/>
<field name="partner_id"/>
<field name="partner_payable"/>
<field name="reference"/>
<field name="due_date"/>
<field name="amount"/>
<field name="to_pay"/>
<field name="partner_payable"/>
<field name="date_created"/>
<field name="name"/>
</tree>
</field>
</record>
@ -195,6 +206,18 @@
<field name="view_mode">form,tree</field>
</record>
<record model="ir.ui.view" id="view_bank_statement_form">
<field name="name">account.bank.statement.form.inherit</field>
<field name="model">account.bank.statement</field>
<field name="type">form</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml">
<field name="period_id" position="after">
<button name="%(wizard_populate_statement)d"
string="Import payment lines" type="action" colspan="2"/>
</field>
</field>
</record>
</data>
</terp>

View File

@ -2,18 +2,25 @@
<terp>
<data>
<wizard
string="Populate payment"
model="payment.order"
name="populate_payment"
menu="False"
id="wizard_populate_payment"/>
string="Populate payment"
model="payment.order"
name="populate_payment"
menu="False"
id="wizard_populate_payment"/>
<wizard
string="Pay"
model="payment.order"
name="pay_payment"
menu="False"
id="wizard_pay_payment"/>
string="Pay"
model="payment.order"
name="pay_payment"
menu="False"
id="wizard_pay_payment"/>
<wizard
string="Populate Statement with Payment lines"
model="account.bank.statement"
name="populate_statement"
menu="False"
id="wizard_populate_statement"/>
</data>
</terp>

View File

@ -29,3 +29,4 @@
import wizard_payment_order
import wizard_pay
import wizard_populate_statement

View File

@ -25,88 +25,92 @@
#
##############################################################################
import wizard
from osv import osv
import pooler
from osv import fields
import time
from tools.misc import UpdateableStr
ask_form="""<?xml version="1.0"?>
<form string="Populate Payment : ">
<field name="entries"/>
</form>
"""
ask_fields={
'entries': {'string':'Entries', 'type':'many2many',
'relation': 'account.move.line',},
}
FORM = UpdateableStr()
FIELDS = {
'entries': {'string':'Entries', 'type':'many2many',
'relation': 'account.move.line',},
}
def search_entries(self, cr, uid, data, context):
pool = pooler.get_pool(cr.dbname)
line_obj = pool.get('account.move.line')
pool= pooler.get_pool(cr.dbname)
# Search for move line to pay:
mline_ids = pool.get('account.move.line').search(cr, uid,
[('reconcile_id', '=', False),
('amount_to_pay', '>', 0)],
context=context)
line_ids = line_obj.search(cr, uid, [
('reconcile_id', '=', False),
('amount_to_pay', '>', 0)], context=context)
if not mline_ids:
ask_fields['entries']['domain']= [('id', 'in', [])]
return {}
ask_fields['entries']['domain']= [('id', 'in', mline_ids)]
return {'entries': mline_ids}
FORM.string = '''<?xml version="1.0"?>
<form string="Populate Payment:">
<field name="entries" colspan="4" height="300" width="800" nolabel="1"
domain="[('id', 'in', [%s])]"/>
</form>''' % (','.join([str(x) for x in line_ids]))
return {'entries': line_ids}
def create_payment(self, cr, uid, data, context):
mline_ids= data['form']['entries'][0][2]
if not mline_ids: return {}
line_ids= data['form']['entries'][0][2]
if not line_ids: return {}
pool= pooler.get_pool(cr.dbname)
payment = pool.get('payment.order').browse(cr, uid, data['id'],
order_obj = pool.get('payment.order')
line_obj = pool.get('account.move.line')
payment = order_obj.browse(cr, uid, data['id'],
context=context)
t= payment.mode and payment.mode.type.code or 'manual'
line2bank= pool.get('account.move.line').line2bank(cr,uid,
mline_ids,t,context)
t = payment.mode and payment.mode.type.code or 'manual'
line2bank= pool.get('account.move.line').line2bank(cr, uid,
line_ids, t, context)
## Finally populate the current payment with new lines:
for line in pool.get('account.move.line').browse(cr, uid, mline_ids,
context=context):
for line in line_obj.browse(cr, uid, line_ids, context=context):
pool.get('payment.line').create(cr,uid,{
'move_line_id': line.id,
'amount': line.amount_to_pay,
'amount_currency': line.amount_to_pay,
'bank_id': line2bank.get(line.id),
'order_id': payment.id,
})
}, context=context)
return {}
class wizard_payment_order(wizard.interface):
"""
Create a payment object with lines corresponding to the account move line
to pay according to the date and the mode provided by the user.
Hypothesis:
- Small number of non-reconcilied move line , payment mode and bank account type,
- Small number of non-reconcilied move line , payment mode and bank account type,
- Big number of partner and bank account.
If a type is given, unsuitable account move lines are ignored.
"""
states = {'init' : {'actions':[search_entries],
'result': {'type':'form',
'arch':ask_form,
'fields':ask_fields,
'state':[('end','_Cancel'),('create','_Add to payment order')]}
},
'create': {'actions': [],
'result': {'type':'action',
'action':create_payment,
'state':'end'}
},
states = {
'init': {
'actions': [search_entries],
'result': {
'type': 'form',
'arch': FORM,
'fields': FIELDS,
'state': [
('end','_Cancel'),
('create','_Add to payment order', '', True)
]
},
},
'create': {
'actions': [],
'result': {
'type': 'action',
'action': create_payment,
'state': 'end'}
},
}
wizard_payment_order('populate_payment')

View File

@ -0,0 +1,121 @@
##############################################################################
#
# Copyright (c) 2005-2007 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import wizard
import pooler
from tools.misc import UpdateableStr
FORM = UpdateableStr()
FIELDS = {
'lines': {'string': 'Payment Lines', 'type': 'many2many',
'relation': 'payment.line'},
}
def _search_entries(obj, cursor, user, data, context):
pool = pooler.get_pool(cursor.dbname)
line_obj = pool.get('payment.line')
statement_obj = pool.get('account.bank.statement')
statement = statement_obj.browse(cursor, user, data['id'], context=context)
line_ids = line_obj.search(cursor, user, [
('move_line_id.reconcile_id', '=', False),
('order_id.mode.journal.id', '=', statement.journal_id.id)])
line_ids.extend(line_obj.search(cursor, user, [
('move_line_id.reconcile_id', '=', False),
('order_id.mode', '=', False)]))
FORM.string = '''<?xml version="1.0"?>
<form string="Populate Statement:">
<field name="lines" colspan="4" height="300" width="800" nolabel="1"
domain="[('id', 'in', [%s])]"/>
</form>''' % (','.join([str(x) for x in line_ids]))
return {'lines': line_ids}
def _populate_statement(obj, cursor, user, data, context):
line_ids = data['form']['lines'][0][2]
if not line_ids:
return {}
pool = pooler.get_pool(cursor.dbname)
line_obj = pool.get('payment.line')
statement_obj = pool.get('account.bank.statement')
statement_line_obj = pool.get('account.bank.statement.line')
currency_obj = pool.get('res.currency')
statement_reconcile_obj = pool.get('account.bank.statement.reconcile')
statement = statement_obj.browse(cursor, user, data['id'], context=context)
for line in line_obj.browse(cursor, user, line_ids, context=context):
ctx = context.copy()
ctx['date'] = line.value_date
amount = currency_obj.compute(cursor, user, line.currency.id,
statement.currency.id, line.amount_currency, context=ctx)
reconcile_id = statement_reconcile_obj.create(cursor, user, {
'line_ids': [(6, 0, [line.move_line_id.id])]
}, context=context)
statement_line_obj.create(cursor, user, {
'name': line.order_id.reference or '?',
'amount': - amount,
'type': 'supplier',
'partner_id': line.partner_id.id,
'account_id': line.move_line_id.account_id.id,
'statement_id': statement.id,
'ref': line.reference,
'reconcile_id': reconcile_id,
}, context=context)
return {}
class PopulateStatement(wizard.interface):
"""
Populate the current statement with selected payement lines
"""
states = {
'init': {
'actions': [_search_entries],
'result': {
'type': 'form',
'arch': FORM,
'fields': FIELDS,
'state': [
('end', '_Cancel'),
('add', '_Add', '', True)
]
},
},
'add': {
'actions': [],
'result': {
'type': 'action',
'action': _populate_statement,
'state': 'end'
},
},
}
PopulateStatement('populate_statement')