trytondo-sale_payment/statement.py

505 lines
19 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 trytond.model import fields, ModelView
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateAction, StateView, Wizard
from decimal import Decimal
from trytond.i18n import gettext
from trytond.modules.currency.fields import Monetary
from trytond.pyson import Bool, Eval, If
from trytond.exceptions import UserError
from datetime import datetime
__all__ = ['Journal', 'Statement', 'Line', 'OpenStatementStart',
'OpenStatementDone', 'OpenStatement', 'CloseStatementStart',
'CloseStatementDone', 'CloseStatement']
class Journal(metaclass=PoolMeta):
__name__ = 'account.statement.journal'
devices = fields.One2Many('sale.device', 'journal', 'Devices')
class Statement(metaclass=PoolMeta):
__name__ = 'account.statement'
users = fields.Function(fields.One2Many('res.user', None, 'Users'),
'get_users', searcher='search_users')
@classmethod
def get_users(cls, statements, names):
return {'users': {s.id: [u.id
for j in s.journal
for d in j.devices
for u in d.users
]
} for s in statements}
@classmethod
def search_users(cls, name, clause):
pool = Pool()
Journal = pool.get('account.statement.journal')
Device = pool.get('sale.device')
DeviceJournal = pool.get('sale.device.account.statement.journal')
User = pool.get('res.user')
statement = cls.__table__()
journal = Journal.__table__()
device = Device.__table__()
device_journal = DeviceJournal.__table__()
user = User.__table__()
query = statement.join(
journal, condition=statement.journal == journal.id).join(
device_journal,
condition=journal.id == device_journal.journal).join(
device, condition=device_journal.device == device.id).join(
user, condition=device.id == user.sale_device).select(
statement.id,
where=user.id == clause[2])
return [('id', 'in', query)]
class Line(metaclass=PoolMeta):
__name__ = 'account.statement.line'
sale = fields.Many2One('sale.sale', 'Sale', ondelete='RESTRICT')
def create_move(self):
'''
Create move for the statement line and return move if created.
Redefined method to allow amounts in statement lines greater than the
invoice amount.
'''
pool = Pool()
Move = pool.get('account.move')
Period = pool.get('account.period')
Invoice = pool.get('account.invoice')
Currency = pool.get('currency.currency')
MoveLine = pool.get('account.move.line')
if self.move:
return
period_id = Period.find(self.statement.company.id, date=self.date)
move_lines = self._get_move_lines()
move = Move(
period=period_id,
journal=self.statement.journal.journal,
date=self.date,
origin=self,
lines=move_lines,
)
move.save()
self.write([self], {
'move': move.id,
})
if self.invoice:
with Transaction().set_context(date=self.invoice.currency_date):
amount = Currency.compute(self.statement.journal.currency,
self.amount, self.statement.company.currency)
reconcile_lines = self.invoice.get_reconcile_lines_for_amount(
abs(amount))
for move_line in move.lines:
if move_line.account == self.invoice.account:
Invoice.write([self.invoice], {
'payment_lines': [('add', [move_line.id])],
})
break
if reconcile_lines[1] == Decimal('0.0'):
lines = reconcile_lines[0] + [move_line]
MoveLine.reconcile(lines)
return move
class OpenStatementStart(ModelView):
'Open Statement'
__name__ = 'open.statement.start'
class OpenStatementDone(ModelView):
'Open Statement'
__name__ = 'open.statement.done'
result = fields.Text('Result', readonly=True)
class OpenStatement(Wizard):
'Open Statement'
__name__ = 'open.statement'
start = StateView('open.statement.start',
'sale_payment.open_statement_start', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'create_', 'tryton-ok', default=True),
])
create_ = StateTransition()
done = StateView('open.statement.done',
'sale_payment.open_statement_done', [
Button('Done', 'end', 'tryton-ok', default=True),
])
def default_done(self, fields):
return {
'result': self.result,
}
def transition_create_(self):
pool = Pool()
User = pool.get('res.user')
Statement = pool.get('account.statement')
user = Transaction().user
user = User(user)
device = user.sale_device
if device:
journals = [j.id for j in device.journals]
statements = Statement.search([
('journal', 'in', journals),
], order=[
('date', 'ASC'),
])
journals_of_draft_statements = [s.journal for s in statements
if s.state == 'draft']
start_balances = {
s.journal.id: s.end_balance or Decimal('0.0')
for s in statements
}
vlist = []
results = []
for journal in device.journals:
if journal not in journals_of_draft_statements:
values = {
'name': '%s - %s' % (device.rec_name, journal.rec_name),
'journal': journal.id,
'company': user.company.id,
'start_balance': start_balances.get(journal.id,
Decimal('0.0')),
'end_balance': Decimal('0.0'),
'total_amount': Decimal('0.0'),
'number_of_lines': 0,
}
vlist.append(values)
results.append(gettext('sale_payment.open_statement',
statement=journal.rec_name))
else:
results.append(gettext('sale_payment.statement_already_opened',
statement=journal.rec_name))
statements.extend(Statement.create(vlist))
self.result = '\n'.join(results)
else:
self.result = gettext('sale_payment.user_without_device',
user=user.rec_name)
return 'done'
class CloseStatementStart(ModelView):
'Close Statement'
__name__ = 'close.statement.start'
statementLines = fields.One2Many('statement.line', None, 'Lines Statement',
states={'readonly': True})
class CloseStatementDone(ModelView):
'Close Statement'
__name__ = 'close.statement.done'
result = fields.Text('Result', readonly=True)
class CloseStatement(Wizard):
'Close Statement'
__name__ = 'close.statement'
start = StateView('close.statement.start',
'sale_payment.close_statement_start', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Ok', 'validate', 'tryton-ok', default=True),
])
validate = StateTransition()
done = StateView('close.statement.done',
'sale_payment.close_statement_done', [
Button('Done', 'end', 'tryton-ok', default=True),
])
def default_done(self, fields):
return {
'result': self.result,
}
def default_start(self, fields):
pool = Pool()
User = pool.get('res.user')
StatementLine = pool.get('statement.line')
Statement = pool.get('account.statement')
statementLines = []
user = Transaction().user
user = User(user)
device = user.sale_device
if device:
journals = [j.id for j in device.journals]
draft_statements = {
s.journal: s for s in Statement.search([
('journal', 'in', journals),
('state', '=', 'draft'),
], order=[
('create_date', 'ASC'),
])}
for s in draft_statements.values():
end_balance = Decimal('0.0')
for line in s.lines:
end_balance += line.amount
line = {
'journal': s.journal.id,
'start_balance': s.start_balance,
'balance': end_balance,
'end_balance':end_balance+s.start_balance
}
statementLines.append(line)
default = {'statementLines': statementLines}
return default
def transition_validate(self):
pool = Pool()
User = pool.get('res.user')
Statement = pool.get('account.statement')
StatementLine = pool.get('account.statement.line')
ConfigurationClosure = pool.get('sale.cash_closures')
config = ConfigurationClosure(8)
user = Transaction().user
user = User(user)
employee_party = user.employee.party.id if user.employee else None
device = user.sale_device
if device:
journals = [j.id for j in device.journals]
draft_statements = {
s.journal: s for s in Statement.search([
('journal', 'in', journals),
('state', '=', 'draft')
], order=[
('create_date', 'ASC'),
])}
results = []
statements = []
for journal in self.start.statementLines:
account = journal.account
transfer = journal.transfer
mismatch = journal.mismatch
end_balance = journal.end_balance
journal = journal.journal
statement = draft_statements.get(journal)
if statement and statement.state == 'draft':
if not statement.start_balance:
statement.start_balance = Decimal(0)
if account and transfer:
end_balance = abs(end_balance) - abs(transfer)
statement.end_balance = end_balance
lines = statement.lines
conciliation = tuple([StatementLine(
date=datetime.today().date(),
amount= abs(transfer)*-1,
account=account.id)]
)
statement.lines = statement.lines + conciliation
if mismatch and mismatch.real > 0:
pass
if (config.mismatch_limit and config.account_mismatch_charge):
if mismatch and (abs(mismatch) >= abs(config.mismatch_limit.real)):
lines = statement.lines
if employee_party == None:
raise UserError(str("Debe definir un Empleado para el Usuario."))
conciliation_mismatch = tuple([StatementLine(
date=datetime.today().date(),
amount=mismatch,
account=config.account_mismatch_charge.id)]
)
statement.lines = statement.lines + conciliation_mismatch
end_balance = abs(end_balance) - abs(mismatch)
statement.end_balance = end_balance
statement.save()
statements.append(statement)
results.append(gettext('sale_payment.close_statement',
statement=statement.rec_name))
elif statement:
results.append(gettext('sale_payment.statement_already_closed',
statement=statement.rec_name))
else:
results.append(gettext('sale_payment.not_statement_found',
journal=journal.rec_name))
if statements:
Statement.validate_statement(statements)
self.result = '\n'.join(results)
else:
self.result = gettext('sale_payment.user_without_device',
user=user.rec_name)
return 'done'
class StatementLine(ModelView):
"statement Line"
__name__ = 'statement.line'
_states = {'readonly': True}
company = fields.Many2One(
'company.company', "Company", required=True, select=True)
journal = fields.Many2One('account.statement.journal', 'Journal',
required=True, select=True,
states=_states)
currency = fields.Many2One(
'currency.currency', "Currency")
start_balance = Monetary(
"Start Balance", currency='currency', digits='currency', states=_states)
balance = Monetary(
"Balance", currency='currency', digits='currency', states=_states)
end_balance = Monetary(
"End Balance", currency='currency', digits='currency', readonly=True)
transfer = Monetary(
"Transfer", currency='currency', digits='currency')
real_cash = Monetary(
"Real Cash", currency='currency', digits='currency')
mismatch = Monetary(
"Mismatch", currency='currency', digits='currency', readonly=True)
account = fields.Many2One('account.account', "Account",
domain=[
('company', '=', Eval('company', 0)),
('type', '!=', None),
('closed', '!=', True),
],
states={'required': If(Eval('transfer', True), True)})
@staticmethod
def default_currency():
Company = Pool().get('company.company')
company = Transaction().context.get('company')
if company:
return Company(company).currency.id
@staticmethod
def default_company():
return Transaction().context.get('company')
@fields.depends('end_balance', 'real_cash', 'mismatch')
def on_change_real_cash(self):
self.mismatch = self.real_cash - self.end_balance
class PayInvoiceSupplierStart(ModelView):
'Payment Invoice To Supplier'
__name__ = 'pay_invoice.statement.start'
invoice_to_pay = fields.One2Many('line_invoice.pay', None, "Invoice To Pay")
class PayInvoiceSupplier(Wizard):
'Payment Invoice To Supplier'
__name__ = 'pay_invoice.statement'
start = StateView('pay_invoice.statement.start',
'sale_payment.pay_invoice_statement_start', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Pay', 'pay', 'tryton-ok', default=True),
])
pay = StateAction('account_statement.act_statement_form')
def do_pay(self, action):
pool = Pool()
User = pool.get('res.user')
Statement = pool.get('account.statement')
StatementLine = pool.get('account.statement.line')
user = Transaction().user
user = User(user)
device = user.sale_device
if device:
journals = [j.id for j in device.journals]
draft_statements = {
s.journal: s for s in Statement.search([
('journal', 'in', journals),
], order=[
('create_date', 'ASC'),
])}
for pay in self.start.invoice_to_pay:
journal = pay.journal
account = pay.account
invoice = pay.invoice
party = pay.party
amount = pay.amount
statement = draft_statements.get(journal)
lines = statement.lines
pay_to_add = tuple([StatementLine(
date=datetime.today().date(),
party=party,
related_to=invoice,
amount= abs(amount)*-1,
account=account.id)])
statement.lines = statement.lines + pay_to_add
statement.save()
class LinesInvoiceToPay(ModelView):
"Lines Invoice To Pay"
__name__ = 'line_invoice.pay'
company = fields.Many2One(
'company.company', "Company", required=True, select=True)
journal = fields.Many2One('account.statement.journal', 'Journal',
required=True, select=True)
amount = Monetary("Amount", currency='currency', digits='currency', required=True)
party = fields.Many2One('party.party', "Party", required=True,
context={'company': Eval('company', -1)},)
invoice = fields.Reference(
"Related To", 'get_relations', required=True,
domain={
'account.invoice': [
('company', '=', Eval('company', -1)),
('type', '=', 'in'),
If(Bool(Eval('party')),
('party', '=', Eval('party')),
()),
If(Bool(Eval('account')),
('account', '=', Eval('account')),
()),
If(Eval('statement_state') == 'draft',
('state', '=', 'posted'),
('state', '!=', '')),
],},
context={'with_payment': False})
account = fields.Many2One('account.account', "Account",
domain=[
('company', '=', Eval('company', 0)),
('type', '!=', None),
('closed', '!=', True),
],)
description = fields.Char("Description")
@staticmethod
def default_company():
return Transaction().context.get('company')
@classmethod
def _get_relations(cls):
"Return a list of Model names for related_to Reference"
return ['account.invoice']
@classmethod
def get_relations(cls):
Model = Pool().get('ir.model')
get_name = Model.get_name
models = cls._get_relations()
return [(m, get_name(m)) for m in models]