106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
import functools
|
|
|
|
from trytond.i18n import gettext
|
|
from trytond.model import ModelView, Workflow, fields
|
|
from trytond.model.exceptions import AccessError
|
|
from trytond.pool import PoolMeta
|
|
from trytond.pyson import Bool, Eval, If
|
|
from trytond.transaction import Transaction
|
|
|
|
|
|
def no_payment(error):
|
|
def decorator(func):
|
|
@functools.wraps(func)
|
|
def wrapper(cls, sales, *args, **kwargs):
|
|
for sale in sales:
|
|
if not all((p.state == 'failed' for p in sale.payments)):
|
|
raise AccessError(gettext(error, sale=sale.rec_name))
|
|
return func(cls, sales, *args, **kwargs)
|
|
return wrapper
|
|
return decorator
|
|
|
|
|
|
class Sale(metaclass=PoolMeta):
|
|
__name__ = 'sale.sale'
|
|
payments = fields.One2Many(
|
|
'account.payment', 'origin', "Payments",
|
|
domain=[
|
|
('company', '=', Eval('company', -1)),
|
|
['OR',
|
|
('party', '=', If(Bool(Eval('invoice_party')),
|
|
Eval('invoice_party', -1), Eval('party', -1))),
|
|
('state', '!=', 'draft'),
|
|
],
|
|
('currency', '=', Eval('currency', -1)),
|
|
],
|
|
states={
|
|
'readonly': Eval('state') != 'quotation',
|
|
})
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('cancelled')
|
|
@no_payment('sale_payment.msg_sale_cancel_payment')
|
|
def cancel(cls, sales):
|
|
super().cancel(sales)
|
|
|
|
@classmethod
|
|
@ModelView.button
|
|
@Workflow.transition('draft')
|
|
@no_payment('sale_payment.msg_sale_draft_payment')
|
|
def draft(cls, sales):
|
|
super().draft(sales)
|
|
|
|
@classmethod
|
|
def copy(cls, sales, default=None):
|
|
if default is None:
|
|
default = {}
|
|
else:
|
|
default = default.copy()
|
|
default.setdefault('payments', None)
|
|
return super().copy(sales, default=default)
|
|
|
|
@property
|
|
def payment_amount_authorized(self):
|
|
"Total amount of the authorized payments"
|
|
return (sum(
|
|
p.amount for p in self.payments
|
|
if p.kind == 'receivable' and p.is_authorized)
|
|
- sum(p.amount for p in self.payments if p.kind == 'payable'))
|
|
|
|
@property
|
|
def amount_to_pay(self):
|
|
"Amount to pay to confirm the sale"
|
|
return self.total_amount
|
|
|
|
@classmethod
|
|
def payment_confirm(cls, sales=None):
|
|
"Confirm the sale based on payment authorization"
|
|
if sales is None:
|
|
context = Transaction().context
|
|
sales = cls.search([
|
|
('state', '=', 'quotation'),
|
|
('payments', '!=', None),
|
|
('company', '=', context.get('company')),
|
|
])
|
|
|
|
def cover(authorized, amount):
|
|
return (
|
|
abs(authorized) >= abs(amount)
|
|
and (authorized * amount >= 0))
|
|
|
|
to_confirm = []
|
|
for sale in sales:
|
|
if cover(sale.payment_amount_authorized, sale.amount_to_pay):
|
|
to_confirm.append(sale)
|
|
if to_confirm:
|
|
to_confirm = cls.browse(to_confirm) # optimize cache
|
|
cls.confirm(to_confirm)
|
|
|
|
@property
|
|
def credit_limit_amount(self):
|
|
amount = super().credit_limit_amount
|
|
return max(0, amount - self.payment_amount_authorized)
|