# 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), 'company': Transaction().context.get('company') } 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 is 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) journal = fields.Many2One('account.statement.journal', 'Journal', required=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': Eval('transfer', True)}) @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_currency(): Company = Pool().get('company.company') company = Transaction().context.get('company') if company: return Company(company).currency.id @fields.depends('end_balance', 'real_cash', 'mismatch') def on_change_real_cash(self): if self.real_cash and self.end_balance: 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) journal = fields.Many2One('account.statement.journal', 'Journal', required=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") currency = fields.Many2One( 'currency.currency', 'Currency', required=True) @staticmethod def default_company(): return Transaction().context.get('company') @classmethod def default_currency(cls, **pattern): pool = Pool() Company = pool.get('company.company') company = pattern.get('company') if not company: company = cls.default_company() if company: return Company(company).currency.id @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]