mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[RFR] Split off payment functionality in a different addon
This commit is contained in:
@@ -43,7 +43,6 @@
|
||||
'data/account_banking_data.xml',
|
||||
'wizard/bank_import_view.xml',
|
||||
'account_banking_view.xml',
|
||||
'account_banking_workflow.xml',
|
||||
'wizard/banking_transaction_wizard.xml',
|
||||
'workflow/account_invoice.xml',
|
||||
],
|
||||
|
||||
@@ -272,68 +272,6 @@ class account_banking_imported_file(osv.osv):
|
||||
}
|
||||
account_banking_imported_file()
|
||||
|
||||
class payment_mode_type(osv.osv):
|
||||
_name= 'payment.mode.type'
|
||||
_description= 'Payment Mode Type'
|
||||
_columns= {
|
||||
'name': fields.char(
|
||||
'Name', size=64, required=True,
|
||||
help='Payment Type'
|
||||
),
|
||||
'code': fields.char(
|
||||
'Code', size=64, required=True,
|
||||
help='Specify the Code for Payment Type'
|
||||
),
|
||||
# Setting suitable_bank_types to required pending
|
||||
# https://bugs.launchpad.net/openobject-addons/+bug/786845
|
||||
'suitable_bank_types': fields.many2many(
|
||||
'res.partner.bank.type',
|
||||
'bank_type_payment_type_rel',
|
||||
'pay_type_id','bank_type_id',
|
||||
'Suitable bank types', required=True),
|
||||
'ir_model_id': fields.many2one(
|
||||
'ir.model', 'Payment wizard',
|
||||
help=('Select the Payment Wizard for payments of this type. '
|
||||
'Leave empty for manual processing'),
|
||||
domain=[('osv_memory', '=', True)],
|
||||
),
|
||||
'payment_order_type': fields.selection(
|
||||
[('payment', 'Payment'),('debit', 'Direct debit')],
|
||||
'Payment order type', required=True,
|
||||
),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'payment_order_type': lambda *a: 'payment',
|
||||
}
|
||||
|
||||
payment_mode_type()
|
||||
|
||||
class payment_mode(osv.osv):
|
||||
''' Restoring the payment type from version 5,
|
||||
used to select the export wizard (if any) '''
|
||||
_inherit = "payment.mode"
|
||||
|
||||
def suitable_bank_types(self, cr, uid, payment_mode_id=None, context=None):
|
||||
""" Reinstates functional code for suitable bank type filtering.
|
||||
Current code in account_payment is disfunctional.
|
||||
"""
|
||||
res = []
|
||||
payment_mode = self.browse(
|
||||
cr, uid, payment_mode_id, context)
|
||||
if (payment_mode and payment_mode.type and
|
||||
payment_mode.type.suitable_bank_types):
|
||||
res = [type.code for type in payment_mode.type.suitable_bank_types]
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'type': fields.many2one(
|
||||
'payment.mode.type', 'Payment type',
|
||||
help='Select the Payment Type for the Payment Mode.'
|
||||
),
|
||||
}
|
||||
payment_mode()
|
||||
|
||||
class account_bank_statement(osv.osv):
|
||||
'''
|
||||
Extensions from account_bank_statement:
|
||||
@@ -785,419 +723,6 @@ class account_bank_statement_line(osv.osv):
|
||||
|
||||
account_bank_statement_line()
|
||||
|
||||
class payment_line(osv.osv):
|
||||
'''
|
||||
Add extra export_state and date_done fields; make destination bank account
|
||||
mandatory, as it makes no sense to send payments into thin air.
|
||||
Edit: Payments can be by cash too, which is prohibited by mandatory bank
|
||||
accounts.
|
||||
'''
|
||||
_inherit = 'payment.line'
|
||||
_columns = {
|
||||
# New fields
|
||||
'export_state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('open','Confirmed'),
|
||||
('cancel','Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done','Done'),
|
||||
], 'State', select=True
|
||||
),
|
||||
'msg': fields.char('Message', size=255, required=False, readonly=True),
|
||||
|
||||
# Redefined fields: added states
|
||||
'date_done': fields.datetime('Date Confirmed', select=True,
|
||||
readonly=True),
|
||||
'name': fields.char(
|
||||
'Your Reference', size=64, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'communication': fields.char(
|
||||
'Communication', size=64, required=False,
|
||||
help=("Used as the message between ordering customer and current "
|
||||
"company. Depicts 'What do you want to say to the recipient"
|
||||
" about this order ?'"
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'communication2': fields.char(
|
||||
'Communication 2', size=128,
|
||||
help='The successor message of Communication.',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'move_line_id': fields.many2one(
|
||||
'account.move.line', 'Entry line',
|
||||
domain=[('reconcile_id','=', False),
|
||||
('account_id.type', '=','payable')
|
||||
],
|
||||
help=('This Entry Line will be referred for the information of '
|
||||
'the ordering customer.'
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'amount_currency': fields.float(
|
||||
'Amount in Partner Currency', digits=(16,2),
|
||||
required=True,
|
||||
help='Payment amount in the partner currency',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'currency': fields.many2one(
|
||||
'res.currency', 'Partner Currency', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'bank_id': fields.many2one(
|
||||
'res.partner.bank', 'Destination Bank account',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'order_id': fields.many2one(
|
||||
'payment.order', 'Order', required=True,
|
||||
ondelete='cascade', select=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'partner_id': fields.many2one(
|
||||
'res.partner', string="Partner", required=True,
|
||||
help='The Ordering Customer',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'date': fields.date(
|
||||
'Payment Date',
|
||||
help=("If no payment date is specified, the bank will treat this "
|
||||
"payment line directly"
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'state': fields.selection([
|
||||
('normal','Free'),
|
||||
('structured','Structured')
|
||||
], 'Communication Type', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
}
|
||||
_defaults = {
|
||||
'export_state': lambda *a: 'draft',
|
||||
'date_done': lambda *a: False,
|
||||
'msg': lambda *a: '',
|
||||
}
|
||||
|
||||
def fields_get(self, cr, uid, fields=None, context=None):
|
||||
res = super(payment_line, self).fields_get(cr, uid, fields, context)
|
||||
if 'communication' in res:
|
||||
res['communication'].setdefault('states', {})
|
||||
res['communication']['states']['structured'] = [('required', True)]
|
||||
if 'communication2' in res:
|
||||
res['communication2'].setdefault('states', {})
|
||||
res['communication2']['states']['structured'] = [('readonly', True)]
|
||||
res['communication2']['states']['normal'] = [('readonly', False)]
|
||||
|
||||
return res
|
||||
|
||||
"""
|
||||
Hooks for processing direct debit orders, such as implemented in
|
||||
account_direct_debit module.
|
||||
"""
|
||||
def get_storno_account_id(self, cr, uid, payment_line_id, amount,
|
||||
currency_id, context=None):
|
||||
"""
|
||||
Hook for verifying a match of the payment line with the amount.
|
||||
Return the account associated with the storno.
|
||||
Used in account_banking interactive mode
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (signed) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:return: an account if there is a full match, False otherwise
|
||||
:rtype: database id of an account.account resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
def debit_storno(self, cr, uid, payment_line_id, amount,
|
||||
currency_id, storno_retry=True, context=None):
|
||||
"""
|
||||
Hook for handling a canceled item of a direct debit order.
|
||||
Presumably called from a bank statement import routine.
|
||||
|
||||
Decide on the direction that the invoice's workflow needs to take.
|
||||
You may optionally return an incomplete reconcile for the caller
|
||||
to reconcile the now void payment.
|
||||
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (negative) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:param boolean storno_retry: whether the storno is considered fatal \
|
||||
or not.
|
||||
:return: an incomplete reconcile for the caller to fill
|
||||
:rtype: database id of an account.move.reconcile resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
payment_line()
|
||||
|
||||
class payment_order(osv.osv):
|
||||
'''
|
||||
Enable extra states for payment exports
|
||||
'''
|
||||
_inherit = 'payment.order'
|
||||
|
||||
_columns = {
|
||||
'date_scheduled': fields.date(
|
||||
'Scheduled date if fixed',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select a date if you have chosen Preferred Date to be fixed.'
|
||||
),
|
||||
'reference': fields.char(
|
||||
'Reference', size=128, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'mode': fields.many2one(
|
||||
'payment.mode', 'Payment mode', select=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select the Payment Mode to be applied.',
|
||||
),
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('open','Confirmed'),
|
||||
('cancel','Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done','Done'),
|
||||
], 'State', select=True
|
||||
),
|
||||
'line_ids': fields.one2many(
|
||||
'payment.line', 'order_id', 'Payment lines',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'user_id': fields.many2one(
|
||||
'res.users','User', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'date_prefered': fields.selection([
|
||||
('now', 'Directly'),
|
||||
('due', 'Due date'),
|
||||
('fixed', 'Fixed date')
|
||||
], "Preferred date", change_default=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help=("Choose an option for the Payment Order:'Fixed' stands for a "
|
||||
"date specified by you.'Directly' stands for the direct "
|
||||
"execution.'Due date' stands for the scheduled date of "
|
||||
"execution."
|
||||
)
|
||||
),
|
||||
'payment_order_type': fields.selection(
|
||||
[('payment', 'Payment'),('debit', 'Direct debit')],
|
||||
'Payment order type', required=True,
|
||||
),
|
||||
'date_sent': fields.date('Send date', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'payment_order_type': lambda *a: 'payment',
|
||||
}
|
||||
|
||||
def launch_wizard(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Search for a wizard to launch according to the type.
|
||||
If type is manual. just confirm the order.
|
||||
Previously (pre-v6) in account_payment/wizard/wizard_pay.py
|
||||
"""
|
||||
if context == None:
|
||||
context = {}
|
||||
result = {}
|
||||
orders = self.browse(cr, uid, ids, context)
|
||||
order = orders[0]
|
||||
# check if a wizard is defined for the first order
|
||||
if order.mode.type and order.mode.type.ir_model_id:
|
||||
context['active_ids'] = ids
|
||||
wizard_model = order.mode.type.ir_model_id.model
|
||||
wizard_obj = self.pool.get(wizard_model)
|
||||
wizard_id = wizard_obj.create(cr, uid, {}, context)
|
||||
result = {
|
||||
'name': wizard_obj._description or 'Payment Order Export',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': wizard_model,
|
||||
'domain': [],
|
||||
'context': context,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'res_id': wizard_id,
|
||||
'nodestroy': True,
|
||||
}
|
||||
else:
|
||||
# should all be manual orders without type or wizard model
|
||||
for order in orders[1:]:
|
||||
if order.mode.type and order.mode.type.ir_model_id:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('You can only combine payment orders of the same type')
|
||||
)
|
||||
# process manual payments
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for order_id in ids:
|
||||
wf_service.trg_validate(uid, 'payment.order', order_id, 'sent', cr)
|
||||
return result
|
||||
|
||||
def _write_payment_lines(self, cursor, uid, ids, **kwargs):
|
||||
'''
|
||||
ORM method for setting attributes of corresponding payment.line objects.
|
||||
Note that while this is ORM compliant, it is also very ineffecient due
|
||||
to the absence of filters on writes and hence the requirement to
|
||||
filter on the client(=OpenERP server) side.
|
||||
'''
|
||||
if not hasattr(ids, '__iter__'):
|
||||
ids = [ids]
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cursor, uid, [
|
||||
('order_id', 'in', ids)
|
||||
])
|
||||
payment_line_obj.write(cursor, uid, line_ids, kwargs)
|
||||
|
||||
def set_to_draft(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'draft'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='draft')
|
||||
return super(payment_order, self).set_to_draft(
|
||||
cursor, uid, ids, *args
|
||||
)
|
||||
|
||||
def action_sent(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'sent'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='sent')
|
||||
self.write(cursor, uid, ids, {'state':'sent',
|
||||
'date_sent': time.strftime('%Y-%m-%d')})
|
||||
return True
|
||||
|
||||
def action_rejected(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'rejected'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='rejected')
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for id in ids:
|
||||
wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cursor)
|
||||
return True
|
||||
|
||||
def set_done(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Extend standard transition to update children as well.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids,
|
||||
export_state='done',
|
||||
date_done=time.strftime('%Y-%m-%d')
|
||||
)
|
||||
return super(payment_order, self).set_done(
|
||||
cursor, uid, ids, *args
|
||||
)
|
||||
|
||||
def get_wizard(self, type):
|
||||
'''
|
||||
Intercept manual bank payments to include 'sent' state. Default
|
||||
'manual' payments are flagged 'done' immediately.
|
||||
'''
|
||||
if type == 'BANKMAN':
|
||||
# Note that self._module gets overwritten by inheriters, so make
|
||||
# the module name hard coded.
|
||||
return 'account_banking', 'wizard_account_banking_payment_manual'
|
||||
return super(payment_order, self).get_wizard(type)
|
||||
|
||||
"""
|
||||
Hooks for processing direct debit orders, such as implemented in
|
||||
account_direct_debit module.
|
||||
"""
|
||||
def debit_reconcile_transfer(
|
||||
self, cr, uid, payment_order_id, amount, currency, context=None):
|
||||
"""
|
||||
Reconcile the payment order if the amount is correct. Return the
|
||||
id of the reconciliation.
|
||||
"""
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile debit order: "+
|
||||
"Not implemented."))
|
||||
|
||||
def debit_unreconcile_transfer(
|
||||
self, cr, uid, payment_order_id, reconcile_id, amount, currency,
|
||||
context=None):
|
||||
""" Unreconcile the payment_order if at all possible """
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile debit order: "+
|
||||
"Not implemented."))
|
||||
|
||||
payment_order()
|
||||
|
||||
class res_partner_bank(osv.osv):
|
||||
'''
|
||||
|
||||
@@ -330,45 +330,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Make buttons on payment order sensitive for extra states,
|
||||
restore wizard functionality when making payments
|
||||
-->
|
||||
|
||||
<record id="view_banking_payment_order_form_1" model="ir.ui.view">
|
||||
<field name="name">account.payment.order.form.banking-1</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_order_form" />
|
||||
<field name="model">payment.order</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="/form/group/button[@string='Select Invoices to Pay']"
|
||||
position="replace">
|
||||
<button colspan="2" name="%(account_payment.action_create_payment_order)s"
|
||||
string="Select Invoices to Pay" type="action"
|
||||
attrs="{'invisible':[('state','!=','draft')]}"
|
||||
icon="gtk-find"
|
||||
/>
|
||||
</xpath>
|
||||
<xpath expr="/form/group/button[@string='Make Payments']"
|
||||
position="replace">
|
||||
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_banking_payment_order_tree_1" model="ir.ui.view">
|
||||
<field name="name">account.payment.order.tree.banking-1</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_order_tree" />
|
||||
<field name="model">payment.order</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<button string="Make Payments" position="replace">
|
||||
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Set trigger on IBAN and acc_number fields in res_partner_bank form -->
|
||||
<!--record id="view_partner_bank_account_banking_form_1" model="ir.ui.view">
|
||||
<field name="name">res.partner.bank.form.banking-1</field>
|
||||
@@ -459,54 +420,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Insert payment_mode.type -->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_mode_form"/>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="type"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_payment_mode_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.tree.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_mode_tree"/>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="type"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- basic view for payment mode type -->
|
||||
<record model="ir.ui.view" id="view_payment_mode_type_form">
|
||||
<field name="name">view.payment.mode.type.form</field>
|
||||
<field name="model">payment.mode.type</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="name" />
|
||||
<field name="code" />
|
||||
<field name="suitable_bank_types"/>
|
||||
<field name="payment_order_type"/>
|
||||
<field name="ir_model_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- fixes https://bugs.launchpad.net/openobject-addons/+bug/903156 for 6.0
|
||||
Note that 6.1 does not suffer from the problem
|
||||
-->
|
||||
<record id="account_payment.action_create_payment_order" model="ir.actions.act_window">
|
||||
<field name="view_id" ref="account_payment.view_create_payment_order"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record model="ir.ui.view" id="view_bank_statement_line_tree">
|
||||
<field name="name">Bank statement line tree view</field>
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
|
||||
@@ -123,43 +123,6 @@ class banking_import_transaction(osv.osv):
|
||||
# return move_lines to mix with the rest
|
||||
return [x for x in invoice.move_id.line_id if x.account_id.reconcile]
|
||||
|
||||
def _match_debit_order(
|
||||
self, cr, uid, trans, log, context=None):
|
||||
|
||||
def is_zero(total):
|
||||
return self.pool.get('res.currency').is_zero(
|
||||
cr, uid, trans.statement_id.currency, total)
|
||||
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
order_ids = payment_order_obj.search(
|
||||
cr, uid, [('payment_order_type', '=', 'debit'),
|
||||
('state', '=', 'sent'),
|
||||
('date_sent', '<=', str2date(trans.execution_date,
|
||||
'%Y-%m-%d'))
|
||||
],
|
||||
limit=0, context=context)
|
||||
orders = payment_order_obj.browse(cr, uid, order_ids, context)
|
||||
candidates = [x for x in orders if
|
||||
is_zero(x.total - trans.transferred_amount)]
|
||||
if len(candidates) > 0:
|
||||
# retrieve the common account_id, if any
|
||||
account_id = False
|
||||
for line in candidates[0].line_ids[0].debit_move_line_id.move_id.line_id:
|
||||
if line.account_id.type == 'other':
|
||||
account_id = line.account_id.id
|
||||
break
|
||||
return dict(
|
||||
move_line_ids = False,
|
||||
match_type = 'payment_order',
|
||||
payment_order_ids = [x.id for x in candidates],
|
||||
account_id = account_id,
|
||||
partner_id = False,
|
||||
partner_bank_id = False,
|
||||
reference = False,
|
||||
type='general',
|
||||
)
|
||||
return False
|
||||
|
||||
def _match_invoice(self, cr, uid, trans, move_lines,
|
||||
partner_ids, bank_account_ids,
|
||||
log, linked_invoices,
|
||||
@@ -542,101 +505,6 @@ class banking_import_transaction(osv.osv):
|
||||
{'voucher_id': voucher_id}, context=context)
|
||||
transaction.refresh()
|
||||
|
||||
def _confirm_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_pool = self.pool.get('payment.line')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_line_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot link with storno"),
|
||||
_("No direct debit order item"))
|
||||
reconcile_id = payment_line_pool.debit_storno(
|
||||
cr, uid,
|
||||
transaction.payment_line_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
transaction.storno_retry,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
transaction.refresh()
|
||||
|
||||
def _confirm_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Reconcile payment order not implemented"))
|
||||
reconcile_id = payment_order_obj.debit_reconcile_transfer(
|
||||
cr, uid,
|
||||
transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
|
||||
def _confirm_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Do some housekeeping on the payment line
|
||||
then pass on to _reconcile_move
|
||||
"""
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id, {
|
||||
'export_state': 'done',
|
||||
'date_done': transaction.statement_line_id.date,
|
||||
}
|
||||
)
|
||||
self._confirm_move(cr, uid, transaction_id, context=context)
|
||||
|
||||
def _cancel_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: this operation is not yet supported for "
|
||||
"match type 'payment'"))
|
||||
|
||||
def _cancel_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Unreconcile payment order not implemented"))
|
||||
return payment_order_obj.debit_unreconcile_transfer(
|
||||
cr, uid, transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.reconcile_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency)
|
||||
|
||||
def _legacy_do_move_unreconcile(self, cr, uid, move_line_ids, currency, context=None):
|
||||
"""
|
||||
Legacy method. Allow for canceling bank statement lines that
|
||||
@@ -768,70 +636,10 @@ class banking_import_transaction(osv.osv):
|
||||
|
||||
return True
|
||||
|
||||
def _cancel_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
TODO: delegate unreconciliation to the direct debit module,
|
||||
to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
reconcile_obj = self.pool.get('account.move.reconcile')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
|
||||
if not transaction.payment_line_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("No direct debit order item"))
|
||||
if not transaction.payment_line_id.storno:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("The direct debit order item is not marked for storno"))
|
||||
|
||||
journal = transaction.statement_line_id.statement_id.journal_id
|
||||
if transaction.statement_line_id.amount >= 0:
|
||||
account_id = journal.default_credit_account_id.id
|
||||
else:
|
||||
account_id = journal.default_debit_account_id.id
|
||||
cancel_line = False
|
||||
move_lines = []
|
||||
for move in transaction.statement_line_id.move_ids:
|
||||
# There should usually be just one move, I think
|
||||
move_lines += move.line_id
|
||||
for line in move_lines:
|
||||
if line.account_id.id != account_id:
|
||||
cancel_line = line
|
||||
break
|
||||
if not cancel_line:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("Line id not found"))
|
||||
reconcile = cancel_line.reconcile_id or cancel_line.reconcile_partial_id
|
||||
lines_reconcile = reconcile.line_id or reconcile.line_partial_ids
|
||||
if len(lines_reconcile) < 3:
|
||||
# delete the full reconciliation
|
||||
reconcile_obj.unlink(cr, uid, reconcile.id, context)
|
||||
else:
|
||||
# we are left with a partial reconciliation
|
||||
reconcile_obj.write(
|
||||
cr, uid, reconcile.id,
|
||||
{'line_partial_ids':
|
||||
[(6, 0, [x.id for x in lines_reconcile if x.id != cancel_line.id])],
|
||||
'line_id': [(6, 0, [])],
|
||||
}, context)
|
||||
# redo the original payment line reconciliation with the invoice
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id,
|
||||
{'storno': False}, context)
|
||||
payment_line_obj.debit_reconcile(
|
||||
cr, uid, transaction.payment_line_id.id, context)
|
||||
|
||||
cancel_map = {
|
||||
'storno': _cancel_storno,
|
||||
'invoice': _cancel_voucher,
|
||||
'manual': _cancel_voucher,
|
||||
'move': _cancel_voucher,
|
||||
'payment_order': _cancel_payment_order,
|
||||
'payment': _cancel_payment,
|
||||
}
|
||||
|
||||
def cancel(self, cr, uid, ids, context=None):
|
||||
@@ -850,11 +658,8 @@ class banking_import_transaction(osv.osv):
|
||||
return True
|
||||
|
||||
confirm_map = {
|
||||
'storno': _confirm_storno,
|
||||
'invoice': _confirm_move,
|
||||
'manual': _confirm_move,
|
||||
'payment_order': _confirm_payment_order,
|
||||
'payment': _confirm_payment,
|
||||
'move': _confirm_move,
|
||||
}
|
||||
|
||||
@@ -892,66 +697,6 @@ class banking_import_transaction(osv.osv):
|
||||
"""
|
||||
return True
|
||||
|
||||
def _match_storno(
|
||||
self, cr, uid, trans, log, context=None):
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id.payment_order_type', '=', 'debit'),
|
||||
('order_id.state', 'in', ['sent', 'done']),
|
||||
('communication', '=', trans.reference)
|
||||
], context=context)
|
||||
# stornos MUST have an exact match
|
||||
if len(line_ids) == 1:
|
||||
account_id = payment_line_obj.get_storno_account_id(
|
||||
cr, uid, line_ids[0], trans.transferred_amount,
|
||||
trans.statement_id.currency, context=None)
|
||||
if account_id:
|
||||
return dict(
|
||||
account_id = account_id,
|
||||
match_type = 'storno',
|
||||
payment_line_id = line_ids[0],
|
||||
move_line_ids=False,
|
||||
partner_id=False,
|
||||
partner_bank_id=False,
|
||||
reference=False,
|
||||
type='customer',
|
||||
)
|
||||
# TODO log the reason why there is no result for transfers marked
|
||||
# as storno
|
||||
return False
|
||||
|
||||
def _match_payment(self, cr, uid, trans, payment_lines,
|
||||
partner_ids, bank_account_ids, log, linked_payments):
|
||||
'''
|
||||
Find the payment order belonging to this reference - if there is one
|
||||
This is the easiest part: when sending payments, the returned bank info
|
||||
should be identical to ours.
|
||||
This also means that we do not allow for multiple candidates.
|
||||
'''
|
||||
# TODO: Not sure what side effects are created when payments are done
|
||||
# for credited customer invoices, which will be matched later on too.
|
||||
digits = dp.get_precision('Account')(cr)[1]
|
||||
candidates = [x for x in payment_lines
|
||||
if x.communication == trans.reference
|
||||
and round(x.amount, digits) == -round(trans.transferred_amount, digits)
|
||||
and trans.remote_account in (x.bank_id.acc_number,
|
||||
x.bank_id.acc_number_domestic)
|
||||
]
|
||||
if len(candidates) == 1:
|
||||
candidate = candidates[0]
|
||||
# Check cache to prevent multiple matching of a single payment
|
||||
if candidate.id not in linked_payments:
|
||||
linked_payments[candidate.id] = True
|
||||
move_info = self._get_move_info(cr, uid, [candidate.move_line_id.id])
|
||||
move_info.update({
|
||||
'match_type': 'payment',
|
||||
'payment_line_id': candidate.id,
|
||||
})
|
||||
return move_info
|
||||
|
||||
return False
|
||||
|
||||
signal_duplicate_keys = [
|
||||
# does not include float values
|
||||
# such as transferred_amount
|
||||
@@ -1059,6 +804,22 @@ class banking_import_transaction(osv.osv):
|
||||
retval['invoice_ids'] = [x.invoice.id for x in move_lines]
|
||||
retval['type'] = type_map[move_lines[0].invoice.type]
|
||||
return retval
|
||||
|
||||
def move_info2values(move_info):
|
||||
vals = {}
|
||||
vals['match_type'] = move_info['match_type']
|
||||
vals['move_line_ids'] = [(6, 0, move_info.get('move_line_ids') or [])]
|
||||
vals['invoice_ids'] = [(6, 0, move_info.get('invoice_ids') or [])]
|
||||
vals['move_line_id'] = (move_info.get('move_line_ids', False) and
|
||||
len(move_info['move_line_ids']) == 1 and
|
||||
move_info['move_line_ids'][0]
|
||||
)
|
||||
if move_info['match_type'] == 'invoice':
|
||||
vals['invoice_id'] = (move_info.get('invoice_ids', False) and
|
||||
len(move_info['invoice_ids']) == 1 and
|
||||
move_info['invoice_ids'][0]
|
||||
)
|
||||
return vals
|
||||
|
||||
def match(self, cr, uid, ids, results=None, context=None):
|
||||
if not ids:
|
||||
@@ -1069,6 +830,7 @@ class banking_import_transaction(osv.osv):
|
||||
journal_obj = self.pool.get('account.journal')
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
has_payment = bool(payment_line_obj)
|
||||
statement_line_obj = self.pool.get('account.bank.statement.line')
|
||||
statement_obj = self.pool.get('account.bank.statement')
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
@@ -1098,14 +860,15 @@ class banking_import_transaction(osv.osv):
|
||||
# communication. Most likely there are much less sent payments
|
||||
# than reconciled and open/draft payments.
|
||||
# Strangely, payment_orders still do not have company_id
|
||||
cr.execute("SELECT l.id FROM payment_order o, payment_line l "
|
||||
"WHERE l.order_id = o.id AND "
|
||||
"o.state = 'sent' AND "
|
||||
"l.date_done IS NULL"
|
||||
)
|
||||
payment_line_ids = [x[0] for x in cr.fetchall()]
|
||||
if payment_line_ids:
|
||||
payment_lines = payment_line_obj.browse(cr, uid, payment_line_ids)
|
||||
if has_payment:
|
||||
payment_line_ids = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id.state', '=', 'sent'),
|
||||
('date_done', '=', False)], context=context)
|
||||
payment_lines = payment_line_obj.browse(
|
||||
cr, uid, payment_line_ids)
|
||||
else:
|
||||
payment_lines = False
|
||||
|
||||
# Start the loop over the transactions requested to match
|
||||
transactions = self.browse(cr, uid, ids, context)
|
||||
@@ -1270,10 +1033,10 @@ class banking_import_transaction(osv.osv):
|
||||
# rebrowse the current record after writing
|
||||
transaction = self.browse(cr, uid, transaction.id, context=context)
|
||||
# Match full direct debit orders
|
||||
if transaction.type == bt.DIRECT_DEBIT:
|
||||
if transaction.type == bt.DIRECT_DEBIT and has_payment:
|
||||
move_info = self._match_debit_order(
|
||||
cr, uid, transaction, results['log'], context)
|
||||
if transaction.type == bt.STORNO:
|
||||
if transaction.type == bt.STORNO and has_payment:
|
||||
move_info = self._match_storno(
|
||||
cr, uid, transaction, results['log'], context)
|
||||
# Allow inclusion of generated bank invoices
|
||||
@@ -1340,6 +1103,10 @@ class banking_import_transaction(osv.osv):
|
||||
if (not move_info
|
||||
and transaction.transferred_amount < 0 and payment_lines):
|
||||
# Link open payment - if any
|
||||
# Note that _match_payment is defined in the
|
||||
# account_banking_payment module which should be installed
|
||||
# automatically if account_payment is. And if account_payment
|
||||
# is not installed, then payment_lines will be empty.
|
||||
move_info = self._match_payment(
|
||||
cr, uid, transaction,
|
||||
payment_lines, partner_ids,
|
||||
@@ -1385,28 +1152,12 @@ class banking_import_transaction(osv.osv):
|
||||
self_values = {}
|
||||
if move_info:
|
||||
results['trans_matched_cnt'] += 1
|
||||
self_values['match_type'] = move_info['match_type']
|
||||
self_values['payment_line_id'] = move_info.get('payment_line_id', False)
|
||||
self_values['move_line_ids'] = [(6, 0, move_info.get('move_line_ids') or [])]
|
||||
self_values['invoice_ids'] = [(6, 0, move_info.get('invoice_ids') or [])]
|
||||
self_values['payment_order_ids'] = [(6, 0, move_info.get('payment_order_ids') or [])]
|
||||
self_values['payment_order_id'] = (move_info.get('payment_order_ids', False) and
|
||||
len(move_info['payment_order_ids']) == 1 and
|
||||
move_info['payment_order_ids'][0]
|
||||
)
|
||||
self_values['move_line_id'] = (move_info.get('move_line_ids', False) and
|
||||
len(move_info['move_line_ids']) == 1 and
|
||||
move_info['move_line_ids'][0]
|
||||
)
|
||||
if move_info['match_type'] == 'invoice':
|
||||
self_values['invoice_id'] = (move_info.get('invoice_ids', False) and
|
||||
len(move_info['invoice_ids']) == 1 and
|
||||
move_info['invoice_ids'][0]
|
||||
)
|
||||
self_values.update(
|
||||
self.move_info2values(move_info))
|
||||
# values['match_type'] = move_info['match_type']
|
||||
values['partner_id'] = move_info['partner_id']
|
||||
values['partner_bank_id'] = move_info['partner_bank_id']
|
||||
values['type'] = move_info['type']
|
||||
# values['match_type'] = move_info['match_type']
|
||||
else:
|
||||
values['partner_id'] = values['partner_bank_id'] = False
|
||||
if not values['partner_id'] and partner_ids and len(partner_ids) == 1:
|
||||
@@ -1446,34 +1197,6 @@ class banking_import_transaction(osv.osv):
|
||||
statement_obj.button_dummy(
|
||||
cr, uid, imported_statement_ids, context=context)
|
||||
|
||||
if payment_lines:
|
||||
# As payments lines are treated as individual transactions, the
|
||||
# batch as a whole is only marked as 'done' when all payment lines
|
||||
# have been reconciled.
|
||||
cr.execute(
|
||||
"SELECT DISTINCT o.id "
|
||||
"FROM payment_order o, payment_line l "
|
||||
"WHERE o.state = 'sent' "
|
||||
"AND o.id = l.order_id "
|
||||
"AND o.id NOT IN ("
|
||||
"SELECT DISTINCT order_id AS id "
|
||||
"FROM payment_line "
|
||||
"WHERE date_done IS NULL "
|
||||
"AND id IN (%s)"
|
||||
")" % (','.join([str(x) for x in payment_line_ids]))
|
||||
)
|
||||
order_ids = [x[0] for x in cr.fetchall()]
|
||||
if order_ids:
|
||||
# Use workflow logics for the orders. Recode logic from
|
||||
# account_payment, in order to increase efficiency.
|
||||
payment_order_obj.set_done(cr, uid, order_ids,
|
||||
{'state': 'done'}
|
||||
)
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for id in order_ids:
|
||||
wf_service.trg_validate(
|
||||
uid, 'payment.order', id, 'done', cr)
|
||||
|
||||
def _get_residual(self, cr, uid, ids, name, args, context=None):
|
||||
"""
|
||||
Calculate the residual against the candidate reconciliation.
|
||||
@@ -1518,10 +1241,6 @@ class banking_import_transaction(osv.osv):
|
||||
elif transaction.match_type == 'invoice':
|
||||
if transaction.invoice_ids and not transaction.invoice_id:
|
||||
res[transaction.id] = True
|
||||
elif transaction.match_type == 'payment_order':
|
||||
if (transaction.payment_order_ids and not
|
||||
transaction.payment_order_id):
|
||||
res[transaction.id] = True
|
||||
return res
|
||||
|
||||
def clear_and_write(self, cr, uid, ids, vals=None, context=None):
|
||||
@@ -1535,12 +1254,10 @@ class banking_import_transaction(osv.osv):
|
||||
'invoice_id',
|
||||
'manual_invoice_id',
|
||||
'manual_move_line_id',
|
||||
'payment_line_id',
|
||||
]] +
|
||||
[(x, [(6, 0, [])]) for x in [
|
||||
'move_line_ids',
|
||||
'invoice_ids',
|
||||
'payment_order_ids',
|
||||
]]))
|
||||
write_vals.update(vals or {})
|
||||
return self.write(cr, uid, ids, write_vals, context=context)
|
||||
@@ -1645,23 +1362,15 @@ class banking_import_transaction(osv.osv):
|
||||
# match fields
|
||||
'match_type': fields.selection(
|
||||
[('manual', 'Manual'), ('move','Move'), ('invoice', 'Invoice'),
|
||||
('payment', 'Payment'), ('payment_order', 'Payment order'),
|
||||
('storno', 'Storno')],
|
||||
'Match type'),
|
||||
], 'Match type'),
|
||||
'match_multi': fields.function(
|
||||
_get_match_multi, method=True, string='Multi match',
|
||||
type='boolean'),
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order', 'banking_transaction_payment_order_rel',
|
||||
'order_id', 'transaction_id', 'Payment orders'),
|
||||
'payment_order_id': fields.many2one(
|
||||
'payment.order', 'Payment order to reconcile'),
|
||||
'move_line_ids': fields.many2many(
|
||||
'account.move.line', 'banking_transaction_move_line_rel',
|
||||
'move_line_id', 'transaction_id', 'Matching entries'),
|
||||
'move_line_id': fields.many2one(
|
||||
'account.move.line', 'Entry to reconcile'),
|
||||
'payment_line_id': fields.many2one('payment.line', 'Payment line'),
|
||||
'invoice_ids': fields.many2many(
|
||||
'account.invoice', 'banking_transaction_invoice_rel',
|
||||
'invoice_id', 'transaction_id', 'Matching invoices'),
|
||||
@@ -1722,9 +1431,8 @@ class account_bank_statement_line(osv.osv):
|
||||
'match_type': fields.related(
|
||||
'import_transaction_id', 'match_type', type='selection',
|
||||
selection=[('manual', 'Manual'), ('move','Move'),
|
||||
('invoice', 'Invoice'), ('payment', 'Payment'),
|
||||
('payment_order', 'Payment order'),
|
||||
('storno', 'Storno')],
|
||||
('invoice', 'Invoice'),
|
||||
],
|
||||
string='Match type', readonly=True,),
|
||||
'state': fields.selection(
|
||||
[('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
|
||||
|
||||
@@ -20,12 +20,5 @@
|
||||
<record id="base_iban.bank_swift_field" model="res.partner.bank.type.field">
|
||||
<field eval="False" name="required"/>
|
||||
</record>
|
||||
<!-- Add manual bank transfer as default payment option -->
|
||||
<record model="payment.mode.type" id="account_banking.manual_bank_tranfer">
|
||||
<field name="name">Manual Bank Transfer</field>
|
||||
<field name="code">BANKMAN</field>
|
||||
<field name="suitable_bank_types"
|
||||
eval="[(6,0,[ref('base.bank_normal'),ref('base_iban.bank_iban'),])]" />
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -2,6 +2,5 @@
|
||||
"access_account_banking_settings","account.banking.account.settings","model_account_banking_account_settings","account.group_account_manager",1,1,1,1
|
||||
"access_account_banking_settings_user","account.banking.account.settings user","model_account_banking_account_settings","account.group_account_user",1,0,0,0
|
||||
"access_account_banking_import","account.bankimport","model_account_banking_imported_file","account.group_account_user",1,1,1,1
|
||||
"access_payment_mode_type","payment.mode.type","model_payment_mode_type","account_payment.group_account_payment",1,1,1,1
|
||||
"access_banking_import_transaction","Banking addons - Bank import transaction","model_banking_import_transaction","account.group_account_user",1,1,1,1
|
||||
"access_banking_transaction_wizard","Banking addons - Transaction wizard","model_banking_transaction_wizard","account.group_account_user",1,1,1,1
|
||||
|
||||
|
@@ -19,8 +19,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
import bank_import
|
||||
import bank_payment_manual
|
||||
import account_payment_order
|
||||
import banking_transaction_wizard
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -85,13 +85,10 @@ class banking_import_line(osv.osv_memory):
|
||||
'invoice_ids': fields.many2many(
|
||||
'account.invoice', 'banking_import_line_invoice_rel',
|
||||
'line_id', 'invoice_id'),
|
||||
'payment_order_id': fields.many2one('payment.order', 'Payment order'),
|
||||
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account'),
|
||||
'transaction_type': fields.selection([
|
||||
# TODO: payment terminal etc...
|
||||
('invoice', 'Invoice payment'),
|
||||
('payment_order_line', 'Payment from a payment order'),
|
||||
('payment_order', 'Aggregate payment order'),
|
||||
('storno', 'Canceled debit order'),
|
||||
('bank_costs', 'Bank costs'),
|
||||
('unknown', 'Unknown'),
|
||||
|
||||
@@ -320,15 +320,6 @@ class banking_transaction_wizard(osv.osv_memory):
|
||||
'import_transaction_id', 'writeoff_journal_id',
|
||||
type='many2one', relation='account.journal',
|
||||
string='Write-off journal'),
|
||||
'payment_line_id': fields.related(
|
||||
'import_transaction_id', 'payment_line_id', string="Matching payment or storno",
|
||||
type='many2one', relation='payment.line', readonly=True),
|
||||
'payment_order_ids': fields.related(
|
||||
'import_transaction_id', 'payment_order_ids', string="Matching payment orders",
|
||||
type='many2many', relation='payment.order'),
|
||||
'payment_order_id': fields.related(
|
||||
'import_transaction_id', 'payment_order_id', string="Payment order to reconcile",
|
||||
type='many2one', relation='payment.order'),
|
||||
'invoice_ids': fields.related(
|
||||
'import_transaction_id', 'invoice_ids', string="Matching invoices",
|
||||
type='many2many', relation='account.invoice'),
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Match transaction">
|
||||
<!-- fields used for form logic -->
|
||||
<field name="payment_order_ids" invisible="True"/>
|
||||
<field name="invoice_ids" invisible="True"/>
|
||||
<field name="move_line_ids" invisible="True"/>
|
||||
<field name="match_multi" invisible="True"/>
|
||||
@@ -36,9 +35,6 @@
|
||||
<separator string="Multiple matches" colspan="2"/>
|
||||
<label colspan="2" string="Multiple matches were found for this bank transfer. You must pick one of the matches or select a match manually below." />
|
||||
</group>
|
||||
<field name='payment_line_id'
|
||||
attrs="{'invisible': [('match_type', '!=', 'storno'),('match_type', '!=', 'payment')]}"
|
||||
/>
|
||||
<group attrs="{'readonly': [('match_multi', '!=', True)]}" col="8">
|
||||
<!-- show if we have an invoice type match (but the user may need to select from multiple options)
|
||||
or whenever there is an invoice_id (e.g. in case of a manual match)
|
||||
@@ -53,10 +49,6 @@
|
||||
attrs="{'readonly': [('match_multi', '=', False)], 'invisible': [('match_type', '!=', 'move'),('invoice_id', '=', False)]}"
|
||||
domain="[('id', 'in', move_line_ids[0][2])]"
|
||||
/>
|
||||
<field name='payment_order_id'
|
||||
attrs="{'readonly': [('match_multi', '=', False)], 'invisible': [('match_type', '!=', 'payment_order')]}"
|
||||
domain="[('id', 'in', payment_order_ids[0][2])]"
|
||||
/>
|
||||
<field name='analytic_account_id' />
|
||||
</group>
|
||||
<button colspan="1"
|
||||
|
||||
1
account_banking_payment/__init__.py
Normal file
1
account_banking_payment/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
import model
|
||||
55
account_banking_payment/__openerp__.py
Normal file
55
account_banking_payment/__openerp__.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'Account Banking - Payments',
|
||||
'version': '0.1.164',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Banking addons community',
|
||||
'website': 'https://launchpad.net/banking-addons',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_banking',
|
||||
'account_payment',
|
||||
],
|
||||
'data': [
|
||||
'view/account_payment.xml',
|
||||
'view/payment_mode_type.xml',
|
||||
'data/payment_mode_type.xml',
|
||||
'workflow/account_payment.xml',
|
||||
'security/ir.model.access.csv',
|
||||
],
|
||||
'description': '''
|
||||
This addon adds payment infrastructure to the Banking Addons.
|
||||
|
||||
* Extends payments for digital banking:
|
||||
+ Adapted workflow in payments to reflect banking operations
|
||||
+ Relies on account_payment mechanics to extend with export generators.
|
||||
- ClieOp3 (NL) payment and direct debit orders files available as
|
||||
account_banking_nl_clieop
|
||||
''',
|
||||
'auto_install': True,
|
||||
'installable': False,
|
||||
}
|
||||
45
account_banking_payment/banking_import_line.py
Normal file
45
account_banking_payment/banking_import_line.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class banking_import_line(orm.TransientModel):
|
||||
_inherit = 'banking.import.line'
|
||||
_columns = {
|
||||
'payment_order_id': fields.many2one(
|
||||
'payment.order', 'Payment order'),
|
||||
'transaction_type': fields.selection([
|
||||
# Add payment order related transaction types
|
||||
('invoice', 'Invoice payment'),
|
||||
('payment_order_line', 'Payment from a payment order'),
|
||||
('payment_order', 'Aggregate payment order'),
|
||||
('storno', 'Canceled debit order'),
|
||||
('bank_costs', 'Bank costs'),
|
||||
('unknown', 'Unknown'),
|
||||
], 'Transaction type'),
|
||||
}
|
||||
|
||||
|
||||
12
account_banking_payment/data/payment_mode_type.xml
Normal file
12
account_banking_payment/data/payment_mode_type.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Add manual bank transfer as default payment option -->
|
||||
<record model="payment.mode.type" id="account_banking.manual_bank_tranfer">
|
||||
<field name="name">Manual Bank Transfer</field>
|
||||
<field name="code">BANKMAN</field>
|
||||
<field name="suitable_bank_types"
|
||||
eval="[(6,0,[ref('base.bank_normal'),ref('base_iban.bank_iban'),])]" />
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
10
account_banking_payment/model/__init__.py
Normal file
10
account_banking_payment/model/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import account_payment
|
||||
import payment_line
|
||||
import payment_mode
|
||||
import payment_mode_type
|
||||
import payment_order_create
|
||||
import banking_import_transaction
|
||||
import account_bank_statement_line
|
||||
import banking_transaction_wizard
|
||||
import bank_payment_manual
|
||||
|
||||
15
account_banking_payment/model/account_bank_statement_line.py
Normal file
15
account_banking_payment/model/account_bank_statement_line.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class account_bank_statement_line(osv.osv):
|
||||
_inherit = 'account.bank.statement.line'
|
||||
_columns = {
|
||||
'match_type': fields.related(
|
||||
# Add payment and storno types
|
||||
'import_transaction_id', 'match_type', type='selection',
|
||||
selection=[('manual', 'Manual'), ('move','Move'),
|
||||
('invoice', 'Invoice'), ('payment', 'Payment'),
|
||||
('payment_order', 'Payment order'),
|
||||
('storno', 'Storno')],
|
||||
string='Match type', readonly=True,),
|
||||
}
|
||||
246
account_banking_payment/model/account_payment.py
Normal file
246
account_banking_payment/model/account_payment.py
Normal file
@@ -0,0 +1,246 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class payment_order(orm.Model):
|
||||
'''
|
||||
Enable extra states for payment exports
|
||||
'''
|
||||
_inherit = 'payment.order'
|
||||
|
||||
_columns = {
|
||||
'date_scheduled': fields.date(
|
||||
'Scheduled date if fixed',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select a date if you have chosen Preferred Date to be fixed.'
|
||||
),
|
||||
'reference': fields.char(
|
||||
'Reference', size=128, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'mode': fields.many2one(
|
||||
'payment.mode', 'Payment mode', select=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select the Payment Mode to be applied.',
|
||||
),
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('open','Confirmed'),
|
||||
('cancel','Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done','Done'),
|
||||
], 'State', select=True
|
||||
),
|
||||
'line_ids': fields.one2many(
|
||||
'payment.line', 'order_id', 'Payment lines',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'user_id': fields.many2one(
|
||||
'res.users','User', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'date_prefered': fields.selection([
|
||||
('now', 'Directly'),
|
||||
('due', 'Due date'),
|
||||
('fixed', 'Fixed date')
|
||||
], "Preferred date", change_default=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help=("Choose an option for the Payment Order:'Fixed' stands for a "
|
||||
"date specified by you.'Directly' stands for the direct "
|
||||
"execution.'Due date' stands for the scheduled date of "
|
||||
"execution."
|
||||
)
|
||||
),
|
||||
'payment_order_type': fields.selection(
|
||||
[('payment', 'Payment'),('debit', 'Direct debit')],
|
||||
'Payment order type', required=True,
|
||||
),
|
||||
'date_sent': fields.date('Send date', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'payment_order_type': lambda *a: 'payment',
|
||||
}
|
||||
|
||||
def launch_wizard(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Search for a wizard to launch according to the type.
|
||||
If type is manual. just confirm the order.
|
||||
Previously (pre-v6) in account_payment/wizard/wizard_pay.py
|
||||
"""
|
||||
if context == None:
|
||||
context = {}
|
||||
result = {}
|
||||
orders = self.browse(cr, uid, ids, context)
|
||||
order = orders[0]
|
||||
# check if a wizard is defined for the first order
|
||||
if order.mode.type and order.mode.type.ir_model_id:
|
||||
context['active_ids'] = ids
|
||||
wizard_model = order.mode.type.ir_model_id.model
|
||||
wizard_obj = self.pool.get(wizard_model)
|
||||
wizard_id = wizard_obj.create(cr, uid, {}, context)
|
||||
result = {
|
||||
'name': wizard_obj._description or 'Payment Order Export',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': wizard_model,
|
||||
'domain': [],
|
||||
'context': context,
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'res_id': wizard_id,
|
||||
'nodestroy': True,
|
||||
}
|
||||
else:
|
||||
# should all be manual orders without type or wizard model
|
||||
for order in orders[1:]:
|
||||
if order.mode.type and order.mode.type.ir_model_id:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('You can only combine payment orders of the same type')
|
||||
)
|
||||
# process manual payments
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for order_id in ids:
|
||||
wf_service.trg_validate(uid, 'payment.order', order_id, 'sent', cr)
|
||||
return result
|
||||
|
||||
def _write_payment_lines(self, cursor, uid, ids, **kwargs):
|
||||
'''
|
||||
ORM method for setting attributes of corresponding payment.line objects.
|
||||
Note that while this is ORM compliant, it is also very ineffecient due
|
||||
to the absence of filters on writes and hence the requirement to
|
||||
filter on the client(=OpenERP server) side.
|
||||
'''
|
||||
if not hasattr(ids, '__iter__'):
|
||||
ids = [ids]
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cursor, uid, [
|
||||
('order_id', 'in', ids)
|
||||
])
|
||||
payment_line_obj.write(cursor, uid, line_ids, kwargs)
|
||||
|
||||
def set_to_draft(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'draft'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='draft')
|
||||
return super(payment_order, self).set_to_draft(
|
||||
cursor, uid, ids, *args
|
||||
)
|
||||
|
||||
def action_sent(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'sent'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='sent')
|
||||
self.write(cursor, uid, ids, {'state':'sent',
|
||||
'date_sent': time.strftime('%Y-%m-%d')})
|
||||
return True
|
||||
|
||||
def action_rejected(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'rejected'.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids, export_state='rejected')
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for id in ids:
|
||||
wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cursor)
|
||||
return True
|
||||
|
||||
def set_done(self, cursor, uid, ids, *args):
|
||||
'''
|
||||
Extend standard transition to update children as well.
|
||||
'''
|
||||
self._write_payment_lines(cursor, uid, ids,
|
||||
export_state='done',
|
||||
date_done=time.strftime('%Y-%m-%d')
|
||||
)
|
||||
return super(payment_order, self).set_done(
|
||||
cursor, uid, ids, *args
|
||||
)
|
||||
|
||||
def get_wizard(self, type):
|
||||
'''
|
||||
Intercept manual bank payments to include 'sent' state. Default
|
||||
'manual' payments are flagged 'done' immediately.
|
||||
'''
|
||||
if type == 'BANKMAN':
|
||||
# Note that self._module gets overwritten by inheriters, so make
|
||||
# the module name hard coded.
|
||||
return 'account_banking', 'wizard_account_banking_payment_manual'
|
||||
return super(payment_order, self).get_wizard(type)
|
||||
|
||||
"""
|
||||
Hooks for processing direct debit orders, such as implemented in
|
||||
account_direct_debit module.
|
||||
"""
|
||||
def debit_reconcile_transfer(
|
||||
self, cr, uid, payment_order_id, amount, currency, context=None):
|
||||
"""
|
||||
Reconcile the payment order if the amount is correct. Return the
|
||||
id of the reconciliation.
|
||||
"""
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile debit order: "+
|
||||
"Not implemented."))
|
||||
|
||||
def debit_unreconcile_transfer(
|
||||
self, cr, uid, payment_order_id, reconcile_id, amount, currency,
|
||||
context=None):
|
||||
""" Unreconcile the payment_order if at all possible """
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile debit order: "+
|
||||
"Not implemented."))
|
||||
385
account_banking_payment/model/banking_import_transaction.py
Normal file
385
account_banking_payment/model/banking_import_transaction.py
Normal file
@@ -0,0 +1,385 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.addons.account_banking.parsers.convert import str2date
|
||||
|
||||
|
||||
class banking_import_transaction(orm.Model):
|
||||
_inherit = 'banking.import.transaction'
|
||||
|
||||
confirm_map = {
|
||||
# Add storno and payment types
|
||||
'storno': _confirm_storno,
|
||||
'invoice': _confirm_move,
|
||||
'manual': _confirm_move,
|
||||
'payment_order': _confirm_payment_order,
|
||||
'payment': _confirm_payment,
|
||||
'move': _confirm_move,
|
||||
}
|
||||
|
||||
cancel_map = {
|
||||
'storno': _cancel_storno,
|
||||
'invoice': _cancel_voucher,
|
||||
'manual': _cancel_voucher,
|
||||
'move': _cancel_voucher,
|
||||
'payment_order': _cancel_payment_order,
|
||||
'payment': _cancel_payment,
|
||||
}
|
||||
|
||||
def _match_debit_order(
|
||||
self, cr, uid, trans, log, context=None):
|
||||
|
||||
def is_zero(total):
|
||||
return self.pool.get('res.currency').is_zero(
|
||||
cr, uid, trans.statement_id.currency, total)
|
||||
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
|
||||
order_ids = payment_order_obj.search(
|
||||
cr, uid, [('payment_order_type', '=', 'debit'),
|
||||
('state', '=', 'sent'),
|
||||
('date_sent', '<=', str2date(trans.execution_date,
|
||||
'%Y-%m-%d'))
|
||||
],
|
||||
limit=0, context=context)
|
||||
orders = payment_order_obj.browse(cr, uid, order_ids, context)
|
||||
candidates = [x for x in orders if
|
||||
is_zero(x.total - trans.transferred_amount)]
|
||||
if len(candidates) > 0:
|
||||
# retrieve the common account_id, if any
|
||||
account_id = False
|
||||
for line in candidates[0].line_ids[0].debit_move_line_id.move_id.line_id:
|
||||
if line.account_id.type == 'other':
|
||||
account_id = line.account_id.id
|
||||
break
|
||||
return dict(
|
||||
move_line_ids = False,
|
||||
match_type = 'payment_order',
|
||||
payment_order_ids = [x.id for x in candidates],
|
||||
account_id = account_id,
|
||||
partner_id = False,
|
||||
partner_bank_id = False,
|
||||
reference = False,
|
||||
type='general',
|
||||
)
|
||||
return False
|
||||
|
||||
def _match_storno(
|
||||
self, cr, uid, trans, log, context=None):
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id.payment_order_type', '=', 'debit'),
|
||||
('order_id.state', 'in', ['sent', 'done']),
|
||||
('communication', '=', trans.reference)
|
||||
], context=context)
|
||||
# stornos MUST have an exact match
|
||||
if len(line_ids) == 1:
|
||||
account_id = payment_line_obj.get_storno_account_id(
|
||||
cr, uid, line_ids[0], trans.transferred_amount,
|
||||
trans.statement_id.currency, context=None)
|
||||
if account_id:
|
||||
return dict(
|
||||
account_id = account_id,
|
||||
match_type = 'storno',
|
||||
payment_line_id = line_ids[0],
|
||||
move_line_ids=False,
|
||||
partner_id=False,
|
||||
partner_bank_id=False,
|
||||
reference=False,
|
||||
type='customer',
|
||||
)
|
||||
# TODO log the reason why there is no result for transfers marked
|
||||
# as storno
|
||||
return False
|
||||
|
||||
def _match_payment(self, cr, uid, trans, payment_lines,
|
||||
partner_ids, bank_account_ids, log, linked_payments):
|
||||
'''
|
||||
Find the payment order belonging to this reference - if there is one
|
||||
This is the easiest part: when sending payments, the returned bank info
|
||||
should be identical to ours.
|
||||
This also means that we do not allow for multiple candidates.
|
||||
'''
|
||||
# TODO: Not sure what side effects are created when payments are done
|
||||
# for credited customer invoices, which will be matched later on too.
|
||||
digits = dp.get_precision('Account')(cr)[1]
|
||||
candidates = [x for x in payment_lines
|
||||
if x.communication == trans.reference
|
||||
and round(x.amount, digits) == -round(trans.transferred_amount, digits)
|
||||
and trans.remote_account in (x.bank_id.acc_number,
|
||||
x.bank_id.acc_number_domestic)
|
||||
]
|
||||
if len(candidates) == 1:
|
||||
candidate = candidates[0]
|
||||
# Check cache to prevent multiple matching of a single payment
|
||||
if candidate.id not in linked_payments:
|
||||
linked_payments[candidate.id] = True
|
||||
move_info = self._get_move_info(cr, uid, [candidate.move_line_id.id])
|
||||
move_info.update({
|
||||
'match_type': 'payment',
|
||||
'payment_line_id': candidate.id,
|
||||
})
|
||||
return move_info
|
||||
|
||||
return False
|
||||
|
||||
def _confirm_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_pool = self.pool.get('payment.line')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_line_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot link with storno"),
|
||||
_("No direct debit order item"))
|
||||
reconcile_id = payment_line_pool.debit_storno(
|
||||
cr, uid,
|
||||
transaction.payment_line_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
transaction.storno_retry,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
transaction.refresh()
|
||||
|
||||
def _confirm_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Reconcile payment order not implemented"))
|
||||
reconcile_id = payment_order_obj.debit_reconcile_transfer(
|
||||
cr, uid,
|
||||
transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
|
||||
def _confirm_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Do some housekeeping on the payment line
|
||||
then pass on to _reconcile_move
|
||||
"""
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id, {
|
||||
'export_state': 'done',
|
||||
'date_done': transaction.statement_line_id.date,
|
||||
}
|
||||
)
|
||||
self._confirm_move(cr, uid, transaction_id, context=context)
|
||||
|
||||
def _cancel_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: this operation is not yet supported for "
|
||||
"match type 'payment'"))
|
||||
|
||||
def _cancel_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Unreconcile payment order not implemented"))
|
||||
return payment_order_obj.debit_unreconcile_transfer(
|
||||
cr, uid, transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.reconcile_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency)
|
||||
|
||||
def _cancel_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
TODO: delegate unreconciliation to the direct debit module,
|
||||
to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
reconcile_obj = self.pool.get('account.move.reconcile')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
|
||||
if not transaction.payment_line_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("No direct debit order item"))
|
||||
if not transaction.payment_line_id.storno:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("The direct debit order item is not marked for storno"))
|
||||
|
||||
journal = transaction.statement_line_id.statement_id.journal_id
|
||||
if transaction.statement_line_id.amount >= 0:
|
||||
account_id = journal.default_credit_account_id.id
|
||||
else:
|
||||
account_id = journal.default_debit_account_id.id
|
||||
cancel_line = False
|
||||
move_lines = []
|
||||
for move in transaction.statement_line_id.move_ids:
|
||||
# There should usually be just one move, I think
|
||||
move_lines += move.line_id
|
||||
for line in move_lines:
|
||||
if line.account_id.id != account_id:
|
||||
cancel_line = line
|
||||
break
|
||||
if not cancel_line:
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("Line id not found"))
|
||||
reconcile = cancel_line.reconcile_id or cancel_line.reconcile_partial_id
|
||||
lines_reconcile = reconcile.line_id or reconcile.line_partial_ids
|
||||
if len(lines_reconcile) < 3:
|
||||
# delete the full reconciliation
|
||||
reconcile_obj.unlink(cr, uid, reconcile.id, context)
|
||||
else:
|
||||
# we are left with a partial reconciliation
|
||||
reconcile_obj.write(
|
||||
cr, uid, reconcile.id,
|
||||
{'line_partial_ids':
|
||||
[(6, 0, [x.id for x in lines_reconcile if x.id != cancel_line.id])],
|
||||
'line_id': [(6, 0, [])],
|
||||
}, context)
|
||||
# redo the original payment line reconciliation with the invoice
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id,
|
||||
{'storno': False}, context)
|
||||
payment_line_obj.debit_reconcile(
|
||||
cr, uid, transaction.payment_line_id.id, context)
|
||||
|
||||
_columns = {
|
||||
'match_type': fields.selection(
|
||||
# Add payment and storno types
|
||||
[
|
||||
('manual', 'Manual'),
|
||||
('move','Move'),
|
||||
('invoice', 'Invoice'),
|
||||
('payment', 'Payment'),
|
||||
('payment_order', 'Payment order'),
|
||||
('storno', 'Storno'),
|
||||
],
|
||||
'Match type'),
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order', 'banking_transaction_payment_order_rel',
|
||||
'order_id', 'transaction_id', 'Payment orders'),
|
||||
'payment_order_id': fields.many2one(
|
||||
'payment.order', 'Payment order to reconcile'),
|
||||
'payment_line_id': fields.many2one('payment.line', 'Payment line'),
|
||||
}
|
||||
|
||||
def _get_match_multi(self, cr, uid, ids, name, args, context=None):
|
||||
if not ids:
|
||||
return {}
|
||||
res = super(banking_import_transaction, self)._get_match_multi(
|
||||
cr, uid, ids, name, args, context=context)
|
||||
for transaction in self.browse(cr, uid, ids, context):
|
||||
if transaction.match_type == 'payment_order':
|
||||
if (transaction.payment_order_ids and not
|
||||
transaction.payment_order_id):
|
||||
res[transaction.id] = True
|
||||
return res
|
||||
|
||||
def clear_and_write(self, cr, uid, ids, vals=None, context=None):
|
||||
super(banking_import_transaction, self).clear_and_write(
|
||||
cr, uid, ids, vals=vals, context=context)
|
||||
return self.write(
|
||||
cr, uid, ids, {
|
||||
'payment_line_id': False,
|
||||
'payment_order_ids': [(6, 0, [])],
|
||||
},
|
||||
context=context)
|
||||
|
||||
def move_info2values(move_info):
|
||||
vals = super(banking_import_transaction, self).move_info2values
|
||||
vals['payment_line_id'] = move_info.get('payment_line_id', False)
|
||||
vals['payment_order_ids'] = [
|
||||
(6, 0, move_info.get('payment_order_ids') or [])]
|
||||
vals['payment_order_id'] = (
|
||||
move_info.get('payment_order_ids', False) and
|
||||
len(move_info['payment_order_ids']) == 1 and
|
||||
move_info['payment_order_ids'][0]
|
||||
)
|
||||
return vals
|
||||
|
||||
def match(self, cr, uid, ids, results=None, context=None):
|
||||
res = super(banking_import_transaction, self).match(
|
||||
cr, uid, ids, results=results, context=context)
|
||||
|
||||
# As payments lines are treated as individual transactions, the
|
||||
# batch as a whole is only marked as 'done' when all payment lines
|
||||
# have been reconciled.
|
||||
cr.execute(
|
||||
"SELECT DISTINCT o.id "
|
||||
"FROM payment_order o, payment_line l "
|
||||
"WHERE o.state = 'sent' "
|
||||
"AND o.id = l.order_id "
|
||||
"AND o.id NOT IN ("
|
||||
"SELECT DISTINCT order_id AS id "
|
||||
"FROM payment_line "
|
||||
"WHERE date_done IS NULL "
|
||||
"AND id IN (%s)"
|
||||
")" % (','.join([str(x) for x in payment_line_ids]))
|
||||
)
|
||||
order_ids = [x[0] for x in cr.fetchall()]
|
||||
if order_ids:
|
||||
# Use workflow logics for the orders. Recode logic from
|
||||
# account_payment, in order to increase efficiency.
|
||||
payment_order_obj.set_done(
|
||||
cr, uid, order_ids, {'state': 'done'})
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for id in order_ids:
|
||||
wf_service.trg_validate(
|
||||
uid, 'payment.order', id, 'done', cr)
|
||||
return res
|
||||
41
account_banking_payment/model/banking_transaction_wizard.py
Normal file
41
account_banking_payment/model/banking_transaction_wizard.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class banking_transaction_wizard(orm.TransientModel):
|
||||
_inherit = 'banking.transaction.wizard'
|
||||
_columns = {
|
||||
'payment_line_id': fields.related(
|
||||
'import_transaction_id', 'payment_line_id', string="Matching payment or storno",
|
||||
type='many2one', relation='payment.line', readonly=True),
|
||||
'payment_order_ids': fields.related(
|
||||
'import_transaction_id', 'payment_order_ids', string="Matching payment orders",
|
||||
type='many2many', relation='payment.order'),
|
||||
'payment_order_id': fields.related(
|
||||
'import_transaction_id', 'payment_order_id', string="Payment order to reconcile",
|
||||
type='many2one', relation='payment.order'),
|
||||
}
|
||||
221
account_banking_payment/model/payment_line.py
Normal file
221
account_banking_payment/model/payment_line.py
Normal file
@@ -0,0 +1,221 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class payment_line(osv.osv):
|
||||
'''
|
||||
Add extra export_state and date_done fields; make destination bank account
|
||||
mandatory, as it makes no sense to send payments into thin air.
|
||||
Edit: Payments can be by cash too, which is prohibited by mandatory bank
|
||||
accounts.
|
||||
'''
|
||||
_inherit = 'payment.line'
|
||||
_columns = {
|
||||
# New fields
|
||||
'export_state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('open','Confirmed'),
|
||||
('cancel','Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done','Done'),
|
||||
], 'State', select=True
|
||||
),
|
||||
'msg': fields.char('Message', size=255, required=False, readonly=True),
|
||||
|
||||
# Redefined fields: added states
|
||||
'date_done': fields.datetime('Date Confirmed', select=True,
|
||||
readonly=True),
|
||||
'name': fields.char(
|
||||
'Your Reference', size=64, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'communication': fields.char(
|
||||
'Communication', size=64, required=False,
|
||||
help=("Used as the message between ordering customer and current "
|
||||
"company. Depicts 'What do you want to say to the recipient"
|
||||
" about this order ?'"
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'communication2': fields.char(
|
||||
'Communication 2', size=128,
|
||||
help='The successor message of Communication.',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'move_line_id': fields.many2one(
|
||||
'account.move.line', 'Entry line',
|
||||
domain=[('reconcile_id','=', False),
|
||||
('account_id.type', '=','payable')
|
||||
],
|
||||
help=('This Entry Line will be referred for the information of '
|
||||
'the ordering customer.'
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'amount_currency': fields.float(
|
||||
'Amount in Partner Currency', digits=(16,2),
|
||||
required=True,
|
||||
help='Payment amount in the partner currency',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'currency': fields.many2one(
|
||||
'res.currency', 'Partner Currency', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'bank_id': fields.many2one(
|
||||
'res.partner.bank', 'Destination Bank account',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'order_id': fields.many2one(
|
||||
'payment.order', 'Order', required=True,
|
||||
ondelete='cascade', select=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'partner_id': fields.many2one(
|
||||
'res.partner', string="Partner", required=True,
|
||||
help='The Ordering Customer',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'date': fields.date(
|
||||
'Payment Date',
|
||||
help=("If no payment date is specified, the bank will treat this "
|
||||
"payment line directly"
|
||||
),
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'state': fields.selection([
|
||||
('normal','Free'),
|
||||
('structured','Structured')
|
||||
], 'Communication Type', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
}
|
||||
_defaults = {
|
||||
'export_state': lambda *a: 'draft',
|
||||
'date_done': lambda *a: False,
|
||||
'msg': lambda *a: '',
|
||||
}
|
||||
|
||||
def fields_get(self, cr, uid, fields=None, context=None):
|
||||
res = super(payment_line, self).fields_get(cr, uid, fields, context)
|
||||
if 'communication' in res:
|
||||
res['communication'].setdefault('states', {})
|
||||
res['communication']['states']['structured'] = [('required', True)]
|
||||
if 'communication2' in res:
|
||||
res['communication2'].setdefault('states', {})
|
||||
res['communication2']['states']['structured'] = [('readonly', True)]
|
||||
res['communication2']['states']['normal'] = [('readonly', False)]
|
||||
|
||||
return res
|
||||
|
||||
"""
|
||||
Hooks for processing direct debit orders, such as implemented in
|
||||
account_direct_debit module.
|
||||
"""
|
||||
def get_storno_account_id(self, cr, uid, payment_line_id, amount,
|
||||
currency_id, context=None):
|
||||
"""
|
||||
Hook for verifying a match of the payment line with the amount.
|
||||
Return the account associated with the storno.
|
||||
Used in account_banking interactive mode
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (signed) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:return: an account if there is a full match, False otherwise
|
||||
:rtype: database id of an account.account resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
def debit_storno(self, cr, uid, payment_line_id, amount,
|
||||
currency_id, storno_retry=True, context=None):
|
||||
"""
|
||||
Hook for handling a canceled item of a direct debit order.
|
||||
Presumably called from a bank statement import routine.
|
||||
|
||||
Decide on the direction that the invoice's workflow needs to take.
|
||||
You may optionally return an incomplete reconcile for the caller
|
||||
to reconcile the now void payment.
|
||||
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (negative) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:param boolean storno_retry: whether the storno is considered fatal \
|
||||
or not.
|
||||
:return: an incomplete reconcile for the caller to fill
|
||||
:rtype: database id of an account.move.reconcile resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
payment_line()
|
||||
|
||||
52
account_banking_payment/model/payment_mode.py
Normal file
52
account_banking_payment/model/payment_mode.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class payment_mode(orm.Model):
|
||||
''' Restoring the payment type from version 5,
|
||||
used to select the export wizard (if any) '''
|
||||
_inherit = "payment.mode"
|
||||
|
||||
def suitable_bank_types(self, cr, uid, payment_mode_id=None, context=None):
|
||||
""" Reinstates functional code for suitable bank type filtering.
|
||||
Current code in account_payment is disfunctional.
|
||||
"""
|
||||
res = []
|
||||
payment_mode = self.browse(
|
||||
cr, uid, payment_mode_id, context)
|
||||
if (payment_mode and payment_mode.type and
|
||||
payment_mode.type.suitable_bank_types):
|
||||
res = [type.code for type in payment_mode.type.suitable_bank_types]
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'type': fields.many2one(
|
||||
'payment.mode.type', 'Payment type',
|
||||
help='Select the Payment Type for the Payment Mode.'
|
||||
),
|
||||
}
|
||||
payment_mode()
|
||||
62
account_banking_payment/model/payment_mode_type.py
Normal file
62
account_banking_payment/model/payment_mode_type.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class payment_mode_type(orm.Model):
|
||||
_name= 'payment.mode.type'
|
||||
_description= 'Payment Mode Type'
|
||||
_columns= {
|
||||
'name': fields.char(
|
||||
'Name', size=64, required=True,
|
||||
help='Payment Type'
|
||||
),
|
||||
'code': fields.char(
|
||||
'Code', size=64, required=True,
|
||||
help='Specify the Code for Payment Type'
|
||||
),
|
||||
# Setting suitable_bank_types to required pending
|
||||
# https://bugs.launchpad.net/openobject-addons/+bug/786845
|
||||
'suitable_bank_types': fields.many2many(
|
||||
'res.partner.bank.type',
|
||||
'bank_type_payment_type_rel',
|
||||
'pay_type_id','bank_type_id',
|
||||
'Suitable bank types', required=True),
|
||||
'ir_model_id': fields.many2one(
|
||||
'ir.model', 'Payment wizard',
|
||||
help=('Select the Payment Wizard for payments of this type. '
|
||||
'Leave empty for manual processing'),
|
||||
domain=[('osv_memory', '=', True)],
|
||||
),
|
||||
'payment_order_type': fields.selection(
|
||||
[('payment', 'Payment'),('debit', 'Direct debit')],
|
||||
'Payment order type', required=True,
|
||||
),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'payment_order_type': lambda *a: 'payment',
|
||||
}
|
||||
@@ -2,25 +2,29 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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.
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import datetime
|
||||
from osv import osv
|
||||
from openerp.osv import orm
|
||||
from account_banking.struct import struct
|
||||
from account_banking.parsers import convert
|
||||
|
||||
@@ -30,7 +34,7 @@ def str2date(str):
|
||||
dt = convert.str2date(str, '%Y-%m-%d')
|
||||
return datetime.date(dt.year, dt.month, dt.day)
|
||||
|
||||
class payment_order_create(osv.osv_memory):
|
||||
class payment_order_create(orm.TransientModel):
|
||||
_inherit = 'payment.order.create'
|
||||
|
||||
def create_payment(self, cr, uid, ids, context=None):
|
||||
@@ -123,5 +127,3 @@ class payment_order_create(osv.osv_memory):
|
||||
'currency': line.invoice and line.invoice.currency_id.id or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id,
|
||||
}, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
payment_order_create()
|
||||
2
account_banking_payment/security/ir.model.access.csv
Normal file
2
account_banking_payment/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
|
||||
"access_payment_mode_type","payment.mode.type","model_payment_mode_type","account_payment.group_account_payment",1,1,1,1
|
||||
|
45
account_banking_payment/view/account_payment.xml
Normal file
45
account_banking_payment/view/account_payment.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Make buttons on payment order sensitive for extra states,
|
||||
restore wizard functionality when making payments
|
||||
-->
|
||||
|
||||
<record id="view_banking_payment_order_form_1" model="ir.ui.view">
|
||||
<field name="name">account.payment.order.form.banking-1</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_order_form" />
|
||||
<field name="model">payment.order</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="/form/group/button[@string='Select Invoices to Pay']"
|
||||
position="replace">
|
||||
<button colspan="2" name="%(account_payment.action_create_payment_order)s"
|
||||
string="Select Invoices to Pay" type="action"
|
||||
attrs="{'invisible':[('state','!=','draft')]}"
|
||||
icon="gtk-find"
|
||||
/>
|
||||
</xpath>
|
||||
<xpath expr="/form/group/button[@string='Make Payments']"
|
||||
position="replace">
|
||||
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_banking_payment_order_tree_1" model="ir.ui.view">
|
||||
<field name="name">account.payment.order.tree.banking-1</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_order_tree" />
|
||||
<field name="model">payment.order</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<button string="Make Payments" position="replace">
|
||||
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
35
account_banking_payment/view/banking_transaction_wizard.xml
Normal file
35
account_banking_payment/view/banking_transaction_wizard.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="transaction_wizard">
|
||||
<field name="name">transaction.wizard</field>
|
||||
<field name="model">banking.transaction.wizard</field>
|
||||
<field name="inherit_id"
|
||||
ref="account_banking.transaction_wizard_first" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="invoice_ids" position="before">
|
||||
<field name="payment_order_ids" invisible="True"/>
|
||||
</field>
|
||||
<xpath expr="//group/separator[@string='Multiple matches']/.."
|
||||
position="after">
|
||||
<field name='payment_line_id'
|
||||
attrs="{'invisible': [
|
||||
('match_type', '!=', 'storno'),
|
||||
('match_type', '!=', 'payment')]
|
||||
}" />
|
||||
</xpath>
|
||||
<field name="move_line_id" position="after">
|
||||
<field name='payment_order_id'
|
||||
attrs="{'readonly': [
|
||||
('match_multi', '=', False)],
|
||||
'invisible': [
|
||||
('match_type', '!=', 'payment_order')]}"
|
||||
domain="[('id', 'in', payment_order_ids[0][2])]"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
|
||||
47
account_banking_payment/view/payment_mode_type.xml
Normal file
47
account_banking_payment/view/payment_mode_type.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Add the payment mode type to the payment mode views -->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_mode_form"/>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="type"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_payment_mode_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.tree.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_mode_tree"/>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="type"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- basic view for payment mode type -->
|
||||
<record model="ir.ui.view" id="view_payment_mode_type_form">
|
||||
<field name="name">view.payment.mode.type.form</field>
|
||||
<field name="model">payment.mode.type</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="name" />
|
||||
<field name="code" />
|
||||
<field name="suitable_bank_types"/>
|
||||
<field name="payment_order_type"/>
|
||||
<field name="ir_model_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -1,20 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) EduSense BV <http://www.edusense.nl>
|
||||
All rights reserved.
|
||||
The licence is in the file __terp__.py
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- New activity for workflow payment order: sent -->
|
||||
<record id="act_sent" model="workflow.activity">
|
||||
<record id="account_banking.act_sent" model="workflow.activity">
|
||||
<field name="name">sent</field>
|
||||
<field name="wkf_id" ref="account_payment.wkf_payment_order"/>
|
||||
<field name="action">action_sent()</field>
|
||||
<field name="kind">function</field>
|
||||
</record>
|
||||
<!-- New activity for workflow payment order: rejected -->
|
||||
<record id="act_rejected" model="workflow.activity">
|
||||
<record id="account_banking.act_rejected" model="workflow.activity">
|
||||
<field name="name">rejected</field>
|
||||
<field name="wkf_id" ref="account_payment.wkf_payment_order"/>
|
||||
<field name="action">action_rejected()
|
||||
@@ -22,21 +17,21 @@ write({'state':'rejected'})</field>
|
||||
<field name="kind">function</field>
|
||||
</record>
|
||||
<!-- Add new transition sent -> done -->
|
||||
<record id="trans_sent_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent"/>
|
||||
<record id="account_banking.trans_sent_done" model="workflow.transition">
|
||||
<field name="act_from" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="account_payment.act_done"/>
|
||||
<field name="signal">done</field>
|
||||
</record>
|
||||
<!-- Add new transition sent -> rejected -->
|
||||
<record id="trans_sent_rejected" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent"/>
|
||||
<field name="act_to" ref="act_rejected"/>
|
||||
<record id="account_banking.trans_sent_rejected" model="workflow.transition">
|
||||
<field name="act_from" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="account_banking.act_rejected"/>
|
||||
<field name="signal">rejected</field>
|
||||
</record>
|
||||
<!-- Rewrite existing open -> done transition to include 'sent' -->
|
||||
<record id="account_payment.trans_open_done" model="workflow.transition">
|
||||
<field name="act_from" ref="account_payment.act_open"/>
|
||||
<field name="act_to" ref="act_sent"/>
|
||||
<field name="act_to" ref="account_banking.act_sent"/>
|
||||
<field name="signal">sent</field>
|
||||
</record>
|
||||
</data>
|
||||
Reference in New Issue
Block a user