Prevent to select reconciled payment or group on draft statement line
issue11175 review381781002
This commit is contained in:
1 parent
03148cc871
commit
bb398854c0
9 files changed
+164
-3
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Prevent to select reconciled payment or group on draft statement line
|
||||
* Reconcile payment clearing line with all statement lines
|
||||
* Use reference field for statement line relation
|
||||
* Add support for Python 3.10
|
||||
|
||||
@@ -12,6 +12,7 @@ def register():
|
||||
payment.Group,
|
||||
payment.SucceedStart,
|
||||
account.Move,
|
||||
account.MoveReconciliation,
|
||||
ir.Cron,
|
||||
module='account_payment_clearing', type_='model')
|
||||
Pool.register(
|
||||
|
||||
+46
-1
@@ -1,6 +1,7 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.pool import PoolMeta
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.tools import grouped_slice
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
@@ -10,3 +11,47 @@ class Move(metaclass=PoolMeta):
|
||||
def _get_origin(cls):
|
||||
models = super(Move, cls)._get_origin()
|
||||
return models + ['account.payment']
|
||||
|
||||
|
||||
def _payments_to_update(reconciliations):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
Reconciliation = pool.get('account.move.reconciliation')
|
||||
|
||||
moves = set()
|
||||
others = set()
|
||||
for reconciliation in reconciliations:
|
||||
for line in reconciliation.lines:
|
||||
moves.add(line.move)
|
||||
others.update(line.reconciliations_delegated)
|
||||
|
||||
payments = set()
|
||||
for sub_moves in grouped_slice(moves):
|
||||
payments.update(Payment.search([
|
||||
('clearing_move', 'in', [m.id for m in sub_moves]),
|
||||
], order=[]))
|
||||
if others:
|
||||
payments.update(_payments_to_update(Reconciliation.browse(others)))
|
||||
|
||||
return payments
|
||||
|
||||
|
||||
class MoveReconciliation(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.reconciliation'
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
reconciliations = super().create(vlist)
|
||||
Payment.__queue__.update_reconciled(
|
||||
list(_payments_to_update(reconciliations)))
|
||||
return reconciliations
|
||||
|
||||
@classmethod
|
||||
def delete(cls, reconciliations):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
payments = _payments_to_update(reconciliations)
|
||||
super().delete(reconciliations)
|
||||
Payment.__queue__.update_reconciled(list(payments))
|
||||
+66
@@ -3,9 +3,13 @@
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
|
||||
from sql.aggregate import BoolAnd, Min
|
||||
|
||||
from trytond import backend
|
||||
from trytond.model import ModelView, Workflow, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Bool, Eval
|
||||
from trytond.tools import grouped_slice, reduce_ids
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import Button, StateTransition, StateView, Wizard
|
||||
|
||||
@@ -94,6 +98,7 @@ def cancel_clearing_move(func):
|
||||
for party in to_reconcile:
|
||||
for lines in to_reconcile[party].values():
|
||||
Line.reconcile(lines)
|
||||
cls.update_reconciled(payments)
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -120,6 +125,12 @@ class Payment(metaclass=PoolMeta):
|
||||
help="Define the account to use for clearing move.")
|
||||
clearing_move = fields.Many2One('account.move', 'Clearing Move',
|
||||
readonly=True)
|
||||
clearing_reconciled = fields.Boolean(
|
||||
"Clearing Reconciled", select=True,
|
||||
states={
|
||||
'invisible': ~Eval('clearing_move'),
|
||||
},
|
||||
help="Checked if clearing line is reconciled.")
|
||||
|
||||
@property
|
||||
def amount_line_paid(self):
|
||||
@@ -190,6 +201,7 @@ class Payment(metaclass=PoolMeta):
|
||||
to_reconcile.append(lines)
|
||||
Line.reconcile(*to_reconcile)
|
||||
cls.reconcile_clearing(payments)
|
||||
cls.update_reconciled(payments)
|
||||
|
||||
@property
|
||||
def clearing_account(self):
|
||||
@@ -276,6 +288,16 @@ class Payment(metaclass=PoolMeta):
|
||||
def fail(cls, payments):
|
||||
super(Payment, cls).fail(payments)
|
||||
|
||||
@classmethod
|
||||
def update_reconciled(cls, payments):
|
||||
for payment in payments:
|
||||
if payment.clearing_move:
|
||||
payment.clearing_reconciled = all(
|
||||
l.reconciliation for l in payment.clearing_lines)
|
||||
else:
|
||||
payment.clearing_reconciled = False
|
||||
cls.save(payments)
|
||||
|
||||
@classmethod
|
||||
def reconcile_clearing(cls, payments):
|
||||
pool = Pool()
|
||||
@@ -317,6 +339,50 @@ class Payment(metaclass=PoolMeta):
|
||||
class Group(metaclass=PoolMeta):
|
||||
__name__ = 'account.payment.group'
|
||||
|
||||
clearing_reconciled = fields.Function(fields.Boolean(
|
||||
"Clearing Reconciled",
|
||||
help="All payments in the group are reconciled."),
|
||||
'get_reconciled', searcher='search_reconciled')
|
||||
|
||||
@classmethod
|
||||
def get_reconciled(cls, groups, name):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
payment = Payment.__table__()
|
||||
cursor = Transaction().connection.cursor()
|
||||
result = defaultdict()
|
||||
if backend.name == 'sqlite':
|
||||
column = Min(payment.clearing_reconciled)
|
||||
else:
|
||||
column = BoolAnd(payment.clearing_reconciled)
|
||||
for sub_groups in grouped_slice(groups):
|
||||
cursor.execute(*payment.select(
|
||||
payment.group, column,
|
||||
where=reduce_ids(payment.group, sub_groups),
|
||||
group_by=payment.group))
|
||||
result.update(cursor)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def search_reconciled(cls, name, clause):
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
payment = Payment.__table__()
|
||||
|
||||
_, operator, value = clause
|
||||
Operator = fields.SQL_OPERATORS[operator]
|
||||
|
||||
if backend.name == 'sqlite':
|
||||
column = Min(payment.clearing_reconciled)
|
||||
else:
|
||||
column = BoolAnd(payment.clearing_reconciled)
|
||||
|
||||
query = payment.select(
|
||||
payment.group,
|
||||
having=Operator(column, value),
|
||||
group_by=payment.group)
|
||||
return [('id', 'in', query)]
|
||||
|
||||
@classmethod
|
||||
def reconcile_clearing(cls, groups):
|
||||
pool = Pool()
|
||||
|
||||
@@ -22,6 +22,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="name">payment_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_group_view_list">
|
||||
<field name="model">account.payment.group</field>
|
||||
<field name="inherit" ref="account_payment.payment_group_view_list"/>
|
||||
<field name="name">payment_group_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_succeed">
|
||||
<field name="name">Succeed Payments</field>
|
||||
<field name="wiz_name">account.payment.succeed</field>
|
||||
|
||||
+11
-2
@@ -4,7 +4,7 @@ from collections import defaultdict
|
||||
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
@@ -98,9 +98,18 @@ class StatementLine(metaclass=PoolMeta):
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(StatementLine, cls).__setup__()
|
||||
cls.related_to.domain['account.payment'] = [
|
||||
cls.related_to.domain.get('account.payment', []),
|
||||
If(Eval('statement_state') == 'draft',
|
||||
('clearing_reconciled', '!=', True),
|
||||
()),
|
||||
]
|
||||
cls.related_to.domain['account.payment.group'] = [
|
||||
('company', '=', Eval('company', -1)),
|
||||
('currency', '=', Eval('currency', -1))
|
||||
('currency', '=', Eval('currency', -1)),
|
||||
If(Eval('statement_state') == 'draft',
|
||||
('clearing_reconciled', '!=', True),
|
||||
()),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -119,6 +119,8 @@ Succeed payment::
|
||||
True
|
||||
>>> payment.clearing_move.state
|
||||
'draft'
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
False
|
||||
>>> payable.reload()
|
||||
>>> payable.balance
|
||||
Decimal('-20.00')
|
||||
@@ -133,6 +135,8 @@ Fail payment::
|
||||
>>> payment.state
|
||||
'failed'
|
||||
>>> payment.clearing_move
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
False
|
||||
>>> payment.line.reconciliation
|
||||
>>> payable.reload()
|
||||
>>> payable.balance
|
||||
@@ -214,6 +218,8 @@ Fail payment with posted clearing::
|
||||
>>> payment.state
|
||||
'failed'
|
||||
>>> payment.clearing_move
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
False
|
||||
>>> payment.line.reconciliation
|
||||
>>> clearing_move.reload()
|
||||
>>> line, = [l for l in clearing_move.lines
|
||||
@@ -284,6 +290,20 @@ Validate statement::
|
||||
>>> bank_clearing.reload()
|
||||
>>> bank_clearing.balance
|
||||
Decimal('0.00')
|
||||
>>> payment.reload()
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
True
|
||||
>>> bool(payment.group.clearing_reconciled)
|
||||
True
|
||||
|
||||
Unreconcile payment clearing to allow reimbursement::
|
||||
|
||||
>>> move_line.reconciliation.delete()
|
||||
>>> payment.reload()
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
False
|
||||
>>> bool(payment.group.clearing_reconciled)
|
||||
False
|
||||
|
||||
Create a statement that reimburse the payment group::
|
||||
|
||||
@@ -460,6 +480,8 @@ Validate statement and check the payment is confirmed::
|
||||
>>> payment.reload()
|
||||
>>> payment.state
|
||||
'succeeded'
|
||||
>>> bool(payment.clearing_reconciled)
|
||||
True
|
||||
>>> debit_line, = [l for l in payment.clearing_move.lines if l.debit > 0]
|
||||
>>> debit_line.debit
|
||||
Decimal('50.00')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='payment_complete']" position="after">
|
||||
<field name="clearing_reconciled"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -2,6 +2,9 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='state']" position="after">
|
||||
<field name="clearing_reconciled"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='succeed']" position="replace_attributes">
|
||||
<button name="succeed_wizard"/>
|
||||
</xpath>
|
||||
|
||||
Reference in new issue
Block a user