194 lines
6.8 KiB
Python
194 lines
6.8 KiB
Python
# This file is part of the sale_payment module for Tryton.
|
|
# The COPYRIGHT file at the top level of this repository contains the full
|
|
# copyright notices and license terms.
|
|
from decimal import Decimal
|
|
from sql.aggregate import Sum
|
|
from sql.conditionals import Coalesce
|
|
|
|
from trytond.model import ModelView, fields
|
|
from trytond.pool import PoolMeta, Pool
|
|
from trytond.pyson import Bool, Eval, Not
|
|
from trytond.transaction import Transaction
|
|
from trytond.wizard import Wizard, StateView, StateTransition, Button
|
|
from trytond.i18n import gettext
|
|
from trytond.exceptions import UserError
|
|
|
|
|
|
class SalePaymentForm(metaclass=PoolMeta):
|
|
'Sale Payment Form'
|
|
__name__ = 'sale.payment.form'
|
|
|
|
received_amount = fields.Numeric('Received amount', required=True,
|
|
digits=(16, Eval('currency_digits',2)),
|
|
depends=['currency_digits'])
|
|
change_amount = fields.Numeric('Change amount', readonly=True,
|
|
digits=(16, Eval('currency_digits',2)),
|
|
depends=['currency_digits'])
|
|
|
|
@fields.depends('payment_amount', 'received_amount')
|
|
def on_change_received_amount(self):
|
|
pool = Pool()
|
|
Sale = pool.get('sale.sale')
|
|
sale = Sale(Transaction().context['active_id'])
|
|
amount = sale.total_amount - sale.paid_amount
|
|
|
|
if self.payment_amount < 0:
|
|
self.received_amount = self.payment_amount
|
|
self.change_amount = 0
|
|
else:
|
|
if self.received_amount < 0:
|
|
self.received_amount = None
|
|
self.payment_amount = amount
|
|
self.change_amount = 0
|
|
elif self.received_amount < amount:
|
|
self.payment_amount = self.received_amount
|
|
self.change_amount = 0
|
|
else:
|
|
self.payment_amount = amount
|
|
self.change_amount = self.received_amount - self.payment_amount
|
|
|
|
@fields.depends('payment_amount', 'received_amount')
|
|
def on_change_payment_amount(self):
|
|
pool = Pool()
|
|
Sale = pool.get('sale.sale')
|
|
sale = Sale(Transaction().context['active_id'])
|
|
amount = sale.total_amount - sale.paid_amount
|
|
|
|
if self.payment_amount < 0:
|
|
self.payment_amount = amount
|
|
self.received_amount = amount
|
|
if self.received_amount:
|
|
if self.payment_amount > self.received_amount:
|
|
self.payment_amount = self.received_amount
|
|
self.change_amount = self.received_amount - self.payment_amount
|
|
if self.payment_amount > amount:
|
|
self.payment_amount = amount
|
|
|
|
|
|
class WizardSalePayment(metaclass=PoolMeta):
|
|
'Wizard Sale Payment'
|
|
__name__ = 'sale.payment'
|
|
start = StateView('sale.payment.form',
|
|
'sale_payment.sale_payment_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Pay', 'pay_', 'tryton-ok', default=True),
|
|
])
|
|
pay_ = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
pool = Pool()
|
|
Sale = pool.get('sale.sale')
|
|
User = pool.get('res.user')
|
|
sale = Sale(Transaction().context['active_id'])
|
|
user = User(Transaction().user)
|
|
sale_device = sale.sale_device or user.sale_device or False
|
|
if user.id != 0 and not sale_device:
|
|
raise UserError(gettext('sale_payment.not_sale_device'))
|
|
return {
|
|
'journal': sale_device.journal.id
|
|
if sale_device.journal else None,
|
|
'journals': [j.id for j in sale_device.journals],
|
|
'payment_amount': sale.total_amount - sale.paid_amount
|
|
if sale.paid_amount else sale.total_amount,
|
|
'currency_digits': sale.currency_digits,
|
|
'party': sale.party.id,
|
|
}
|
|
|
|
def get_statement_line(self, sale):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
Sale = pool.get('sale.sale')
|
|
Statement = pool.get('account.statement')
|
|
StatementLine = pool.get('account.statement.line')
|
|
|
|
form = self.start
|
|
statements = Statement.search([
|
|
('journal', '=', form.journal),
|
|
('state', '=', 'draft'),
|
|
], order=[('date', 'DESC')])
|
|
if not statements:
|
|
raise UserError(gettext('sale_payment.not_draft_statement',
|
|
journal=form.journal.name))
|
|
|
|
if not sale.number:
|
|
Sale.set_number([sale])
|
|
|
|
with Transaction().set_context(date=Date.today()):
|
|
account = sale.party.account_receivable_used
|
|
|
|
if not account:
|
|
raise UserError(gettext(
|
|
'sale_payment.party_without_account_receivable',
|
|
party=sale.party.name))
|
|
if form.payment_amount:
|
|
return StatementLine(
|
|
statement=statements[0],
|
|
date=Date.today(),
|
|
amount=form.payment_amount,
|
|
party=sale.party,
|
|
invoice=sale.invoices[0].id if sale.invoices else None,
|
|
account=account,
|
|
description=sale.number,
|
|
sale=sale,
|
|
)
|
|
|
|
def transition_pay_(self):
|
|
Sale = Pool().get('sale.sale')
|
|
|
|
active_id = Transaction().context.get('active_id', False)
|
|
sale = Sale(active_id)
|
|
|
|
if sale.state != 'draft':
|
|
line = self.get_statement_line(sale)
|
|
if line:
|
|
line.save()
|
|
return 'end'
|
|
else:
|
|
sale.description = sale.reference
|
|
sale.save()
|
|
Sale.workflow_to_end([sale])
|
|
|
|
line = self.get_statement_line(sale)
|
|
if line:
|
|
line.save()
|
|
|
|
if sale.total_amount != sale.paid_amount:
|
|
return 'start'
|
|
|
|
return 'end'
|
|
|
|
|
|
class WizardSaleReconcile(Wizard):
|
|
'Reconcile Sales'
|
|
__name__ = 'sale.reconcile'
|
|
start = StateTransition()
|
|
reconcile = StateTransition()
|
|
|
|
def transition_start(self):
|
|
pool = Pool()
|
|
Sale = pool.get('sale.sale')
|
|
Line = pool.get('account.move.line')
|
|
for sale in Sale.browse(Transaction().context['active_ids']):
|
|
account = sale.party.account_receivable_used
|
|
lines = []
|
|
amount = Decimal('0.0')
|
|
for invoice in sale.invoices:
|
|
for line in invoice.lines_to_pay:
|
|
if not line.reconciliation:
|
|
lines.append(line)
|
|
amount += line.debit - line.credit
|
|
for payment in sale.payments:
|
|
if not payment.move:
|
|
continue
|
|
for line in payment.move.lines:
|
|
if (not line.reconciliation and
|
|
line.account.id == account.id):
|
|
lines.append(line)
|
|
amount += line.debit - line.credit
|
|
if lines and amount == Decimal('0.0'):
|
|
Line.reconcile(lines)
|
|
return 'end'
|
|
|
|
|
|
|