Finalise merge of account_banking_payment_transfer into account_payment_order

Add support for transfer moves
Display transfer moves in a dedicated tab in payment order view
Code cleanup
This commit is contained in:
Alexis de Lattre
2016-05-06 22:14:56 +02:00
committed by Enric Tobella
parent 469af80ee7
commit 8736d5722f
14 changed files with 710 additions and 136 deletions

View File

@@ -4,6 +4,7 @@ from . import account_payment_mode
from . import account_payment_order
from . import account_payment_line
from . import bank_payment_line
from . import account_move
from . import account_move_line
from . import account_invoice
from . import res_bank

View File

@@ -38,7 +38,8 @@ class AccountInvoice(models.Model):
}
if self.payment_mode_id.bank_account_link == 'fixed':
vals['journal_id'] = self.payment_mode_id.fixed_journal_id.id
# TODO : else: no filter on allowed bank accounts, because onchange not played ??
# TODO : else: no filter on allowed bank accounts,
# because onchange not played ??
return vals
@api.multi

View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
class AccountMove(models.Model):
_inherit = 'account.move'
payment_order_id = fields.Many2one(
'account.payment.order', string='Payment Order', copy=False,
readonly=True)

View File

@@ -4,41 +4,17 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
from openerp.tools import float_is_zero
class AccountMoveLine(models.Model):
_inherit = 'account.move.line'
# TODO Should we keep this field ?
# journal_entry_ref = fields.Char(compute='_get_journal_entry_ref',
# string='Journal Entry Ref')
partner_bank_id = fields.Many2one(
'res.partner.bank', string='Partner Bank Account',
help='Bank account on which we should pay the supplier')
# @api.one
# def _get_journal_entry_ref(self):
# if self.move_id.state == 'draft':
# if self.invoice.id:
# self.journal_entry_ref = self.invoice.number
# else:
# self.journal_entry_ref = '*' + str(self.move_id.id)
# else:
# self.journal_entry_ref = self.move_id.name
@api.multi
def get_balance(self):
"""
Return the balance of any set of move lines.
Not to be confused with the 'balance' field on this model, which
returns the account balance that the move line applies to.
"""
total = 0.0
for line in self:
total += (line.debit or 0.0) - (line.credit or 0.0)
return total
bank_payment_line_id = fields.Many2one(
'bank.payment.line', string='Bank Payment Line',
readonly=True)
@api.multi
def _prepare_payment_line_vals(self, payment_order):
@@ -67,8 +43,8 @@ class AccountMoveLine(models.Model):
else:
currency_id = self.company_id.currency_id.id
amount_currency = self.amount_residual
# TODO : check that self.amount_residual_currency is 0 in this case
precision = self.env['decimal.precision'].precision_get('Account')
# TODO : check that self.amount_residual_currency is 0
# in this case
if payment_order.payment_type == 'outbound':
amount_currency *= -1
vals = {

View File

@@ -80,6 +80,9 @@ class AccountPaymentOrder(models.Model):
bank_line_count = fields.Integer(
compute='_bank_line_count', string='Number of Bank Lines',
readonly=True)
move_ids = fields.One2many(
'account.move', 'payment_order_id', string='Transfer Journal Entries',
readonly=True)
@api.multi
@api.constrains('payment_type', 'payment_mode_id')
@@ -271,135 +274,108 @@ class AccountPaymentOrder(models.Model):
# Generation of transfer move
@api.multi
def _prepare_transfer_move(self):
if self.payment_type == 'outbound':
ref = _('Payment order %s') % self.name
else:
ref = _('Debit order %s') % self.name
vals = {
'journal_id': self.mode.transfer_journal_id.id,
'ref': '%s %s' % (
self.payment_order_type[:3].upper(), self.reference)
'journal_id': self.payment_mode_id.transfer_journal_id.id,
'ref': ref,
'payment_order_id': self.id,
'line_ids': [],
}
return vals
@api.multi
def _prepare_move_line_transfer_account(
self, amount, move, bank_payment_lines, labels):
if len(bank_payment_lines) == 1:
partner_id = bank_payment_lines[0].partner_id.id
name = _('%s bank line %s') % (labels[self.payment_order_type],
bank_payment_lines[0].name)
self, amount, bank_payment_lines):
if self.payment_type == 'outbound':
name = _('Payment order %s') % self.name
else:
partner_id = False
name = '%s %s' % (
labels[self.payment_order_type], self.reference)
name = _('Debit order %s') % self.name
date_maturity = bank_payment_lines[0].date
vals = {
'name': name,
'move_id': move.id,
'partner_id': partner_id,
'account_id': self.mode.transfer_account_id.id,
'credit': (self.payment_order_type == 'payment' and
'partner_id': False,
'account_id': self.payment_mode_id.transfer_account_id.id,
'credit': (self.payment_type == 'outbound' and
amount or 0.0),
'debit': (self.payment_order_type == 'debit' and
'debit': (self.payment_type == 'inbound' and
amount or 0.0),
'date_maturity': date_maturity,
}
return vals
@api.multi
def _prepare_move_line_partner_account(self, bank_line, move, labels):
def _prepare_move_line_partner_account(self, bank_line):
# TODO : ALEXIS check don't group if move_line_id.account_id
# is not the same
if bank_line.payment_line_ids[0].move_line_id:
account_id =\
bank_line.payment_line_ids[0].move_line_id.account_id.id
else:
if self.payment_order_type == 'debit':
if self.payment_type == 'inbound':
account_id =\
bank_line.partner_id.property_account_receivable.id
bank_line.partner_id.property_account_receivable_id.id
else:
account_id = bank_line.partner_id.property_account_payable.id
account_id =\
bank_line.partner_id.property_account_payable_id.id
if self.payment_type == 'outbound':
name = _('Payment bank line %s') % bank_line.name
else:
name = _('Debit bank line %s') % bank_line.name
vals = {
'name': _('%s line %s') % (
labels[self.payment_order_type], bank_line.name),
'move_id': move.id,
'name': name,
'bank_payment_line_id': bank_line.id,
'partner_id': bank_line.partner_id.id,
'account_id': account_id,
'credit': (self.payment_order_type == 'debit' and
'credit': (self.payment_type == 'inbound' and
bank_line.amount_currency or 0.0),
'debit': (self.payment_order_type == 'payment' and
'debit': (self.payment_type == 'outbound' and
bank_line.amount_currency or 0.0),
}
return vals
@api.multi
def action_sent_no_move_line_hook(self, pay_line):
"""This function is designed to be inherited"""
return
@api.multi
def _create_move_line_partner_account(self, bank_line, move, labels):
"""This method is designed to be inherited in a custom module"""
# TODO: take multicurrency into account
company_currency = self.env.user.company_id.currency_id
if bank_line.currency != company_currency:
raise UserError(_(
"Cannot generate the transfer move when "
"the currency of the payment (%s) is not the "
"same as the currency of the company (%s). This "
"is not supported for the moment.")
% (bank_line.currency.name, company_currency.name))
aml_obj = self.env['account.move.line']
# create the payment/debit counterpart move line
# on the partner account
partner_ml_vals = self._prepare_move_line_partner_account(
bank_line, move, labels)
partner_move_line = aml_obj.create(partner_ml_vals)
# register the payment/debit move line
# on the payment line and call reconciliation on it
bank_line.write({'transit_move_line_id': partner_move_line.id})
@api.multi
def _reconcile_payment_lines(self, bank_payment_lines):
for bline in bank_payment_lines:
if all([pline.move_line_id for pline in bline.payment_line_ids]):
bline.debit_reconcile()
else:
self.action_sent_no_move_line_hook(bline)
@api.multi
def generate_transfer_move(self):
"""
Create the moves that pay off the move lines from
the debit order.
the payment/debit order.
"""
self.ensure_one()
am_obj = self.env['account.move']
aml_obj = self.env['account.move.line']
labels = {
'outbound': _('Payment'),
'inbound': _('Direct debit'),
}
# prepare a dict "trfmoves" that can be used when
# self.mode.transfer_move_option = date or line
# self.payment_mode_id.transfer_move_option = date or line
# key = unique identifier (date or True or line.id)
# value = [pay_line1, pay_line2, ...]
# value = bank_pay_lines (recordset that can have several entries)
trfmoves = {}
for bline in self.bank_line_ids:
hashcode = bline.move_line_transfer_account_hashcode()
if hashcode in trfmoves:
trfmoves[hashcode].append(bline)
trfmoves[hashcode] += bline
else:
trfmoves[hashcode] = [bline]
trfmoves[hashcode] = bline
company_currency = self.env.user.company_id.currency_id
for hashcode, blines in trfmoves.iteritems():
mvals = self._prepare_transfer_move()
move = am_obj.create(mvals)
total_amount = 0
for bline in blines:
total_amount += bline.amount_currency
self._create_move_line_partner_account(bline, move, labels)
# create the payment/debit move line on the transfer account
if bline.currency_id != company_currency:
raise UserError(_(
"Cannot generate the transfer move when "
"the currency of the payment (%s) is not the "
"same as the currency of the company (%s). This "
"is not supported for the moment.")
% (bline.currency_id.name, company_currency.name))
partner_ml_vals = self._prepare_move_line_partner_account(
bline)
mvals['line_ids'].append((0, 0, partner_ml_vals))
trf_ml_vals = self._prepare_move_line_transfer_account(
total_amount, move, blines, labels)
aml_obj.create(trf_ml_vals)
self._reconcile_payment_lines(blines)
total_amount, blines)
mvals['line_ids'].append((0, 0, trf_ml_vals))
move = am_obj.create(mvals)
blines.reconcile_payment_lines()
move.post()

View File

@@ -2,7 +2,8 @@
# © 2015 Akretion - Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
from openerp import models, fields, api, _
from openerp.exceptions import UserError
class BankPaymentLine(models.Model):
@@ -45,32 +46,10 @@ class BankPaymentLine(models.Model):
related='payment_line_ids.communication_type', readonly=True)
communication = fields.Char(
string='Communication', required=True,
readonly=True) #, states={'draft': [('readonly', False)]})
readonly=True)
company_id = fields.Many2one(
related='order_id.payment_mode_id.company_id', store=True,
readonly=True)
# TODO : not shown in view ?
# why on bank payment line and not on payment line ?
transit_move_line_id = fields.Many2one(
'account.move.line', string='Transfer Move Line', readonly=True,
help="Move line through which the payment/debit order "
"pays the invoice")
transfer_move_line_id = fields.Many2one(
'account.move.line', compute='_get_transfer_move_line',
string='Transfer move line counterpart',
help="Counterpart move line on the transfer account")
@api.multi
def _get_transfer_move_line(self):
for bank_line in self:
if bank_line.transit_move_line_id:
payment_type = bank_line.payment_type
trf_lines = bank_line.transit_move_line_id.move_id.line_id
for move_line in trf_lines:
if payment_type == 'inbound' and move_line.debit > 0:
bank_line.transfer_move_line_id = move_line
elif payment_type == 'outbound' and move_line.credit > 0:
bank_line.transfer_move_line_id = move_line
@api.model
def same_fields_payment_line_and_bank_payment_line(self):
@@ -113,3 +92,54 @@ class BankPaymentLine(models.Model):
hashcode = unicode(self.id)
return hashcode
@api.multi
def reconcile_payment_lines(self):
for bline in self:
if all([pline.move_line_id for pline in bline.payment_line_ids]):
bline.reconcile()
else:
bline.no_reconcile_hook()
@api.multi
def no_reconcile_hook(self):
"""This method is designed to be inherited if needed"""
return
@api.multi
def reconcile(self):
self.ensure_one()
amlo = self.env['account.move.line']
transit_mlines = amlo.search([('bank_payment_line_id', '=', self.id)])
assert len(transit_mlines) == 1, 'We should have only 1 move'
transit_mline = transit_mlines[0]
assert not transit_mline.reconciled,\
'Transit move should not be reconciled'
lines_to_rec = transit_mline
for payment_line in self.payment_line_ids:
if not payment_line.move_line_id:
raise UserError(_(
"Can not reconcile: no move line for "
"payment line %s of partner '%s'.") % (
payment_line.name,
payment_line.partner_id.name))
if payment_line.move_line_id.reconciled:
raise UserError(_(
"Move line '%s' of partner '%s' has already "
"been reconciled") % (
payment_line.move_line_id.name,
payment_line.partner_id.name))
if (
payment_line.move_line_id.account_id !=
transit_mline.account_id):
raise UserError(_(
"For partner '%s', the account of the account "
"move line to pay (%s) is different from the "
"account of of the transit move line (%s).") % (
payment_line.move_line_id.partner_id.name,
payment_line.move_line_id.account_id.code,
transit_mline.account_id.code))
lines_to_rec += payment_line.move_line_id
lines_to_rec.reconcile()

View File

@@ -0,0 +1,28 @@
-
I create a transfer account
-
!record {model: account.account, id: account_account_transfer0}:
code: TRANSF
name: Transfer
user_type: account.data_account_type_liability
type: other
reconcile: True
-
I create a transfer journal
-
!record {model: account.journal, id: transfer_journal0}:
name: Transfer journal
code: TR
type: general
company_id: base.main_company
-
I create a payment mode
-
!record {model: payment.mode, id: payment_mode0}:
name: Payment Mode Test
journal: account.bank_journal
bank_id: account_payment.partner_bank_1
company_id: base.main_company
transfer_account_id: account_account_transfer0
transfer_journal_id: transfer_journal0
type: account_banking_payment_export.manual_bank_tranfer

View File

@@ -0,0 +1,134 @@
-
I create a supplier invoice
-
!record {model: account.invoice, id: account_invoice_supplier_refunded, view: account.invoice_supplier_form}:
check_total: 600.00
partner_id: base.res_partner_12
reference_type: none
type: in_invoice
account_id: account.a_pay
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: 'Some contact lenses'
price_unit: 600.00
quantity: 1.0
journal_id: account.expenses_journal
-
Make sure that the type is in_invoice
-
!python {model: account.invoice}: |
self.write(cr, uid, ref("account_invoice_supplier_refunded"), {'type': 'in_invoice'})
-
I change the state of invoice to open by clicking Validate button
-
!workflow {model: account.invoice, action: invoice_open, ref: account_invoice_supplier_refunded}
-
I create a supplier refund for this invoice
-
!record {model: account.invoice, id: account_refund_supplier_refunded, view: account.invoice_supplier_form}:
check_total: 200.00
partner_id: base.res_partner_12
reference_type: none
type: in_refund
account_id: account.a_pay
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: 'Some contact lenses'
price_unit: 200.00
quantity: 1.0
journal_id: account.expenses_journal
-
Make sure that the type is in_refund
-
!python {model: account.invoice}: |
self.write(cr, uid, ref("account_refund_supplier_refunded"), {'type': 'in_refund'})
-
I change the state of invoice to open by clicking Validate button
-
!workflow {model: account.invoice, action: invoice_open, ref: account_refund_supplier_refunded}
-
I create a payment order
-
!record {model: payment.order, id: partial_payment_order_1}:
mode: account_banking_payment_transfer.payment_mode0
date_prefered: 'now'
-
I run the select move line to pay wizard
-
!python {model: payment.order.create}: |
context = {
"active_model": "payment.order",
"active_ids": [ref("partial_payment_order_1")],
"active_id": ref("partial_payment_order_1"),
}
wiz_id = self.create(cr, uid, {}, context=context)
self.search_entries(cr, uid, [wiz_id], context=context)
mline_ids = []
invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_supplier_refunded"))
for l in invoice.move_id.line_id:
if not l.debit and l.credit:
mline_ids.append(l.id)
break
refund = self.pool.get('account.invoice').browse(cr, uid, ref("account_refund_supplier_refunded"))
for l in refund.move_id.line_id:
if not l.credit and l.debit:
mline_ids.append(l.id)
break
self.write(cr, uid, [wiz_id], {'entries': [(6, 0, mline_ids)]})
self.create_payment(cr, uid, [wiz_id], context=context)
pay_obj = self.pool.get('payment.order')
pay = pay_obj.browse(cr, uid, ref('partial_payment_order_1'))
assert len(pay.line_ids) == 2
-
I confirm the payment order.
-
!workflow {model: payment.order, action: open, ref: partial_payment_order_1}
-
I check that payment order is now "Confirmed".
-
!assert {model: payment.order, id: partial_payment_order_1, severity: error, string: Payment Order should be 'Confirmed'.}:
- state == 'open'
- total == 400.0
-
I create the wizard for paying the payment
-
!record {model: payment.manual, id: payment_manual_partial}:
create_date: !eval time.strftime('%Y-%m-%d')
-
I click OK
-
!python {model: payment.manual}: |
if context is None:
context = {}
context.update({'active_ids': [ref("partial_payment_order_1")]})
self.button_ok(cr, uid, ref("payment_manual_partial"), context)
-
I check that the payment order is now "Sent".
-
!assert {model: payment.order, id: partial_payment_order_1, severity: error, string: Payment Order should be 'Sent'.}:
- state == 'sent'
-
I check that the invoice has payments associated
-
!assert {model: account.invoice, id: account_invoice_supplier_refunded, severity: error, string: payment_ids should be populated}:
- payment_ids
-
I check the content of the payment of the invoice
-
!python {model: account.invoice}: |
inv = self.browse(cr, uid, ref("account_invoice_supplier_refunded"))
payment1, payment2 = sorted(inv.payment_ids, key=lambda line: line.id)
assert payment1.debit == 200
assert payment2.debit == 400
assert inv.payment_ids[0].reconcile_id.id != False
-
I check that the invoice balance (residual) is now 0 and the state is paid
-
!assert {model: account.invoice, id: account_invoice_supplier_refunded, severity: error, string: Invoice residual should be 0.}:
- residual == 0
- amount_total == 600
- state == 'paid'

View File

@@ -0,0 +1,252 @@
-
I create a supplier invoice
-
!record {model: account.invoice, id: account_invoice_supplier_partial, view: account.invoice_supplier_form}:
check_total: 1000.00
partner_id: base.res_partner_12
reference_type: none
type: in_invoice
account_id: account.a_pay
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: 'Some glasses'
price_unit: 1000.00
quantity: 1.0
journal_id: account.expenses_journal
-
Make sure that the type is in_invoice
-
!python {model: account.invoice}: |
self.write(cr, uid, ref("account_invoice_supplier_partial"), {'type': 'in_invoice'})
-
I change the state of invoice to open by clicking Validate button
-
!workflow {model: account.invoice, action: invoice_open, ref: account_invoice_supplier_partial}
-
I create a payment order
-
!record {model: payment.order, id: partial_payment_order_2}:
mode: account_banking_payment_transfer.payment_mode0
date_prefered: 'due'
-
I run the select move line to pay wizard
-
!python {model: payment.order.create}: |
context = {
"active_model": "payment.order",
"active_ids": [ref("partial_payment_order_2")],
"active_id": ref("partial_payment_order_2"),
}
wiz_id = self.create(cr, uid, {}, context=context)
self.search_entries(cr, uid, [wiz_id], context=context)
invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_supplier_partial"))
move_line = invoice.move_id.line_id[0]
self.write(cr, uid, [wiz_id], {'entries': [(6, 0, [move_line.id])]})
self.create_payment(cr, uid, [wiz_id], context=context)
pay_obj = self.pool.get('payment.order')
pay = pay_obj.browse(cr, uid, ref('partial_payment_order_2'))
assert pay.line_ids
assert pay.line_ids[0].amount_currency == 1000.0
-
I change the amount paid to test the partial payment
-
!python {model: payment.order}: |
line_ids = self.browse(cr, uid, ref('partial_payment_order_2')).line_ids
line_to_change = line_ids[0]
assert line_to_change.amount_currency == 1000.00
self.pool.get('payment.line').write(cr, uid, line_to_change.id, {'amount_currency':100})
-
I confirm the payment order.
-
!workflow {model: payment.order, action: open, ref: partial_payment_order_2}
-
I check that payment order is now "Confirmed".
-
!assert {model: payment.order, id: partial_payment_order_2, severity: error, string: Payment Order should be 'Confirmed'.}:
- state == 'open'
-
I assume that the document is sent to the bank and validate.
-
!record {model: payment.manual, id: payment_manual_1}:
create_date: !eval time.strftime('%Y-%m-%d')
-
I click OK
-
!python {model: payment.manual}: |
if context is None:
context = {}
context.update({'active_ids': [ref("partial_payment_order_2")]})
self.button_ok(cr, uid, ref("payment_manual_1"), context)
-
I check that the payment order is now "Sent".
-
!assert {model: payment.order, id: partial_payment_order_2, severity: error, string: Payment Order should be 'Sent'.}:
- state == 'sent'
-
I check that the invoice has payments associated
-
!assert {model: account.invoice, id: account_invoice_supplier_partial, severity: error, string: payment_ids should be populated}:
- payment_ids
-
I check the content of the payment of the invoice
-
!python {model: account.invoice}: |
inv = self.browse(cr, uid, ref("account_invoice_supplier_partial"))
assert round(inv.payment_ids[0].debit, 2) == 100
assert inv.payment_ids[0].credit == 0
assert not inv.payment_ids[0].reconcile_id.id
assert inv.payment_ids[0].reconcile_partial_id
sum_debit = 0.0
sum_credit = 0.0
for line in inv.payment_ids[0].reconcile_partial_id.line_partial_ids:
sum_debit += line.debit
sum_credit += line.credit
assert sum_debit == 100
sum_credit == 1000
assert inv.residual == 900
assert inv.state == 'open'
-
I create a 2nd partial payment
-
!record {model: payment.order, id: partial_partial_payment_order_2}:
mode: account_banking_payment_transfer.payment_mode0
date_prefered: 'due'
-
I search for the invoice entries to make the payment.
-
!python {model: payment.order.create}: |
context = {
"active_model": "payment.order",
"active_ids": [ref("partial_partial_payment_order_2")],
"active_id": ref("partial_partial_payment_order_2"),
}
wiz_id = self.create(cr, uid, {}, context=context)
self.search_entries(cr, uid, [wiz_id], context=context)
invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_supplier_partial"))
for l in invoice.move_id.line_id:
if not l.debit and l.credit:
move_line = l
break
self.write(cr, uid, [wiz_id], {'entries': [(6,0,[move_line.id])]})
self.create_payment(cr, uid, [wiz_id], context=context)
pay_obj = self.pool.get('payment.order')
pay = pay_obj.browse(cr, uid, ref('partial_partial_payment_order_2'))
assert len(pay.line_ids) == 1
assert pay.line_ids[0].amount_currency == 900
-
I change the amount paid to test the partial payment
-
!python {model: payment.order}: |
line_ids = self.browse(cr, uid, ref('partial_partial_payment_order_2')).line_ids
line_to_change = line_ids[0]
self.pool.get('payment.line').write(cr, uid, line_to_change.id, {'amount_currency':200})
-
I confirm the payment order.
-
!workflow {model: payment.order, action: open, ref: partial_partial_payment_order_2}
-
I assume that the document is sent to the bank and validate.
-
!record {model: payment.manual, id: payment_manual_1}:
create_date: !eval time.strftime('%Y-%m-%d')
-
I click OK
-
!python {model: payment.manual}: |
if context is None:
context = {}
context.update({'active_ids': [ref("partial_partial_payment_order_2")]})
self.button_ok(cr, uid, ref("payment_manual_1"), context)
-
I check that the payment order is now "Sent".
-
!assert {model: payment.order, id: partial_partial_payment_order_2, severity: error, string: Payment Order should be 'Sent'.}:
- state == 'sent'
-
I check the content of the payment of the invoice
-
!python {model: account.invoice}: |
inv = self.browse(cr, uid, ref("account_invoice_supplier_partial"))
assert len(inv.payment_ids) == 2
assert inv.payment_ids[0].credit == 0
assert not inv.payment_ids[0].reconcile_id.id
assert inv.payment_ids[0].reconcile_partial_id
sum_debit = 0.0
sum_credit = 0.0
for line in inv.payment_ids[0].reconcile_partial_id.line_partial_ids:
sum_debit += line.debit
sum_credit += line.credit
assert sum_debit == 300
assert sum_credit == 1000
assert inv.residual == 700
assert inv.state == 'open'
-
I create the last partial payment for completing the payment
-
!record {model: payment.order, id: partial_partial_payment_order_3}:
mode: account_banking_payment_transfer.payment_mode0
date_prefered: 'due'
-
I search for the invoice entries to make the payment.
-
!python {model: payment.order.create}: |
context = {
"active_model": "payment.order",
"active_ids": [ref("partial_partial_payment_order_3")],
"active_id": ref("partial_partial_payment_order_3"),
}
wiz_id = self.create(cr, uid, {}, context=context)
self.search_entries(cr, uid, [wiz_id], context=context)
invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_supplier_partial"))
for l in invoice.move_id.line_id:
if not l.debit and l.credit:
move_line = l
break
self.write(cr, uid, [wiz_id], {'entries': [(6, 0, [move_line.id])]})
self.create_payment(cr, uid, [wiz_id], context=context)
pay_obj = self.pool.get('payment.order')
pay = pay_obj.browse(cr, uid, ref('partial_partial_payment_order_3'))
assert len(pay.line_ids) == 1
assert pay.line_ids[0].amount_currency == 700
-
I confirm the payment order.
-
!workflow {model: payment.order, action: open, ref: partial_partial_payment_order_3}
-
I assume that the document is sent to the bank and validate.
-
!record {model: payment.manual, id: payment_manual_3}:
create_date: !eval time.strftime('%Y-%m-%d')
-
I click OK
-
!python {model: payment.manual}: |
if context is None:
context = {}
context.update({'active_ids': [ref("partial_partial_payment_order_3")]})
self.button_ok(cr, uid, ref("payment_manual_3"), context)
-
I check that the payment order is now "Sent".
-
!assert {model: payment.order, id: partial_partial_payment_order_3, severity: error, string: Payment Order should be 'Sent'.}:
- state == 'sent'
-
I check the content of the payment of the invoice
-
!python {model: account.invoice}: |
inv = self.browse(cr, uid, ref("account_invoice_supplier_partial"))
assert len(inv.payment_ids) == 3
assert inv.payment_ids[0].credit == 0
assert inv.payment_ids[0].reconcile_id.id
#assert not inv.payment_ids[0].reconcile_partial_id ?? should we remove it?
sum_debit = 0.0
sum_credit = 0.0
for line in inv.payment_ids:
sum_debit += line.debit
sum_credit += line.credit
assert sum_debit == 1000
assert sum_credit == 0
assert inv.residual == 0
assert inv.state == 'paid'

View File

@@ -0,0 +1,159 @@
-
I create a supplier invoice
-
!record {model: account.invoice, id: account_invoice_supplier0, view: account.invoice_supplier_form}:
check_total: 1005.55
partner_id: base.res_partner_4
reference_type: none
type: in_invoice
account_id: account.a_pay
company_id: base.main_company
currency_id: base.EUR
invoice_line:
- account_id: account.a_expense
name: 'Some expenses'
price_unit: 450.0
quantity: 1.0
- account_id: account.a_expense
name: 'Some other expenses'
price_unit: 555.55
quantity: 1.0
journal_id: account.expenses_journal
-
Make sure that the type is in_invoice
-
!python {model: account.invoice}: |
self.write(cr, uid, ref("account_invoice_supplier0"), {'type': 'in_invoice'})
-
I change the state of invoice to open by clicking Validate button
-
!workflow {model: account.invoice, action: invoice_open, ref: account_invoice_supplier0}
-
I check that the invoice state is now "Open"
-
!assert {model: account.invoice, id: account_invoice_supplier0}:
- state == 'open'
- type == 'in_invoice'
-
I create a payment order
-
!record {model: payment.order, id: payment_order_0}:
mode: account_banking_payment_transfer.payment_mode0
date_prefered: 'due'
-
I run the select move line to pay wizard
-
!python {model: payment.order.create}: |
context = {
"active_model": "payment.order",
"active_ids": [ref("payment_order_0")],
"active_id": ref("payment_order_0"),
}
wiz_id = self.create(cr, uid, {}, context=context)
self.search_entries(cr, uid, [wiz_id], context=context)
invoice = self.pool.get('account.invoice').browse(cr, uid, ref("account_invoice_supplier0"))
entries = []
for move_line in invoice.move_id.line_id:
if move_line.credit and not move_line.debit:
entries.append((6, 0, [move_line.id]))
self.write(cr, uid, [wiz_id], {'entries': entries})
self.create_payment(cr, uid, [wiz_id], context=context)
pay_obj = self.pool.get('payment.order')
pay = pay_obj.browse(cr, uid, ref('payment_order_0'))
for line in pay.line_ids:
assert line.amount != 0.0
-
I confirm the payment order.
-
!workflow {model: payment.order, action: open, ref: payment_order_0}
-
I check that payment order is now "Confirmed".
-
!assert {model: payment.order, id: payment_order_0, severity: error, string: Payment Order should be 'Confirmed'.}:
- state == 'open'
-
I create the wizard for paying the payment
-
!record {model: payment.manual, id: payment_manual_0}:
create_date: !eval time.strftime('%Y-%m-%d')
-
I click OK
-
!python {model: payment.manual}: |
if context is None:
context = {}
context.update({'active_ids': [ref("payment_order_0")]})
self.button_ok(cr, uid, ref("payment_manual_0"), context)
-
I check that the payment order is now "Sent".
-
!assert {model: payment.order, id: payment_order_0, severity: error, string: Payment Order should be 'Sent'.}:
- state == 'sent'
-
I check that the invoice has payments associated
-
!assert {model: account.invoice, id: account_invoice_supplier0, severity: error, string: payment_ids should be populated}:
- payment_ids
-
I check the content of the payment of the invoice
-
!python {model: account.invoice}: |
inv = self.browse(cr, uid, ref("account_invoice_supplier0"))
assert round(inv.payment_ids[0].debit, 2) == 1005.55
assert inv.payment_ids[0].credit == 0
assert inv.payment_ids[0].reconcile_id.id != False
assert inv.payment_ids[0].reconcile_ref != False
assert inv.state == 'paid'
-
I create the bank statement to reconcile the transfer account move
-
!record {model: account.bank.statement, id: bank_statement_0}:
name: BK test
balance_end_real: 0.0
balance_start: 0.0
date: !eval time.strftime('%Y-%m-%d')
journal_id: account.bank_journal
-
I create bank statement line
-
!python {model: account.bank.statement.line}: |
vals = {
'amount': -1005.55,
'partner_id': ref('base.res_partner_4'),
'statement_id': ref('bank_statement_0'),
'name': 'Pay invoice',
'journal_id': ref("account.bank_journal"),
}
line_id = self.create(cr, uid, vals)
assert line_id, "Account bank statement line has not been created"
-
I reconcile the move transfer (not the invoice) with the payment.
-
!python {model: account.bank.statement}: |
inv_obj = self.pool.get('account.invoice')
statement_obj = self.pool.get('account.bank.statement.line')
transfer_entry = inv_obj.browse(cr, uid, ref("account_invoice_supplier0")).payment_ids[0].move_id
for line in transfer_entry.line_id:
if not line.reconcile_id and line.credit:
counterpart_move_line = line
break
browse_payment = self.browse(cr, uid, ref("bank_statement_0"))
for line in browse_payment.line_ids:
statement_obj.process_reconciliation(cr, uid, line.id, [{
'counterpart_move_line_id': counterpart_move_line.id,
'credit':0,
'debit': counterpart_move_line.credit,
'name': line.name,
}])
self.write(cr, uid, ref("bank_statement_0"), {'balance_end_real': -1005.55})
self.button_confirm_bank(cr, uid, ref("bank_statement_0"))
-
I check that the bank statement is confirm
-
!assert {model: account.bank.statement, id: bank_statement_0, severity: error, string: Bank Statement should be confirm}:
- state == 'confirm'
-
I check that the payment is done
-
!assert {model: payment.order, id: payment_order_0, severity: error, string: Payment Order should be done}:
- state == 'done'

View File

@@ -18,6 +18,7 @@
<group name="payments" position="inside">
<field name="partner_bank_id"
domain="[('partner_id', '=', partner_id)]"/>
<field name="bank_payment_line_id"/>
</group>
</field>
</record>

View File

@@ -23,7 +23,7 @@
<field name="transfer_move"/>
<field name="transfer_account_id"
attrs="{'invisible': [('transfer_move', '=', False)], 'required': [('transfer_move', '=', True)]}"
context="{'default_internal_type': 'other', 'default_reconcile': True, 'default_company_id': company_id}"/>
context="{'default_reconcile': True, 'default_company_id': company_id}"/> <!-- We can't put a default vue to user_type_id... -->
<field name="transfer_journal_id"
attrs="{'invisible': [('transfer_move', '=', False)], 'required': [('transfer_move', '=', True)]}"/>
<field name="transfer_move_option"

View File

@@ -59,6 +59,9 @@
<field name="bank_line_ids"
context="{'default_payment_type': payment_type}"/>
</page>
<page name="moves" string="Transfer Journal Entries">
<field name="move_ids"/>
</page>
</notebook>
</sheet>
</form>

View File

@@ -91,11 +91,11 @@ class AccountPaymentLineCreate(models.TransientModel):
# will not be refunded with a payment.
domain += [
('credit', '>', 0),
#'|',
# '|',
('account_id.internal_type', '=', 'payable'),
#'&',
#('account_id.internal_type', '=', 'receivable'),
#('reconcile_partial_id', '=', False), # TODO uncomment
# '&',
# ('account_id.internal_type', '=', 'receivable'),
# ('reconcile_partial_id', '=', False), # TODO uncomment
]
elif self.order_id.payment_type == 'inbound':
domain += [