Release v6.0

This commit is contained in:
wilson gomez 2021-05-31 12:35:17 -05:00
parent 17e545a821
commit ddb248fd0c
18 changed files with 382 additions and 231 deletions

View file

@ -2,11 +2,11 @@
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
from trytond.pool import Pool
import voucher
import account
import configuration
import invoice
import multirevenue
from . import voucher
from . import account
from . import configuration
from . import invoice
from . import multirevenue
def register():

View file

@ -4,11 +4,9 @@
from trytond.pool import PoolMeta
from trytond.model import (ModelSQL, ModelView, fields, MatchMixin,
sequence_ordered)
from trytond.pyson import Eval
from trytond.pyson import Eval, Id
from trytond.transaction import Transaction
__all__ = ['Move', 'VoucherSequencePeriod', 'FiscalYear']
class Move(metaclass=PoolMeta):
__name__ = 'account.move'
@ -35,19 +33,22 @@ class VoucherSequencePeriod(sequence_ordered(), ModelSQL, ModelView, MatchMixin)
'Voucher Payment Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.voucher')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
])
receipt_sequence = fields.Many2One('ir.sequence',
'Voucher Receipt Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.voucher')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
])
notes_sequence = fields.Many2One('ir.sequence',
'Voucher Notes Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.voucher')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
])
# multirevenue_sequence = fields.Many2One('ir.sequence',
# 'Multi-Revenue Sequence', required=False,
@ -59,7 +60,8 @@ class VoucherSequencePeriod(sequence_ordered(), ModelSQL, ModelView, MatchMixin)
'Multi-Payment Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.voucher')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
])
@classmethod

View file

@ -48,6 +48,7 @@ the full copyright notices and license terms. -->
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.rule.group" id="rule_group_voucher_sequence">
<field name="name">User in companies</field>
<field name="model"
search="[('model', '=', 'account.voucher.sequence_period')]"/>
<field name="global_p" eval="True"/>

View file

@ -1,46 +1,47 @@
# 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.model import ModelSQL, ModelView, fields, Unique
from trytond.pyson import Eval
from trytond.pyson import Eval, Id
from trytond.transaction import Transaction
__all__ = ['VoucherConfiguration', 'VoucherSequence', 'ResUserAuthorizedVoucher']
from trytond.model.exceptions import AccessError
from trytond.i18n import gettext
class VoucherConfiguration(ModelSQL, ModelView):
'Voucher Configuration'
__name__ = 'account.voucher_configuration'
company = fields.Many2One('company.company', 'Company', required=True)
default_journal_note = fields.Many2One('account.journal', 'Default Journal Note')
default_journal_note = fields.Many2One('account.payment.journal', 'Default Journal Note')
default_payment_mode = fields.Many2One('account.voucher.paymode',
'Default Payment Mode', domain=[
('company', 'in', [Eval('company', -1), None]),
])
account_adjust_expense = fields.Many2One('account.account',
'Account Adjustment Zero for Expense', domain=[
('type', '!=', 'view'),
('type', '!=', None),
])
account_adjust_income = fields.Many2One('account.account',
'Account Adjustment Zero for Income', domain=[
('type', '!=', 'view'),
('type', '!=', None),
])
voucher_notes_sequence = fields.Many2One('ir.sequence',
'Voucher Notes Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.voucher')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
])
multirevenue_sequence = fields.Many2One('ir.sequence',
'Multi-Revenue Sequence', required=False,
domain=[
('company', 'in', [Eval('company', -1), None]),
('code', '=', 'account.multirevenue')
('sequence_type', '=',
Id('account_voucher', 'sequence_type_multirevenue')),
])
account_prepayment = fields.Many2One('account.account', 'Account Prepayment',
required=True, domain=[
('reconcile', '=', True),
('kind', 'in', ['receivable', 'payable']),
('type.statement', 'in', ['balance']),
])
prepayment_description = fields.Char('Prepayment Description',
help="Default descriptor for advances")
@ -52,10 +53,6 @@ class VoucherConfiguration(ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(VoucherConfiguration, cls).__setup__()
cls._error_messages.update({
'missing_default_configuration': (
'Missing the configuration for current company!'),
})
@staticmethod
def default_company():
@ -69,7 +66,8 @@ class VoucherConfiguration(ModelSQL, ModelView):
if res:
return res[0]
else:
cls.raise_user_error('missing_default_configuration')
raise AccessError(
gettext('account.msg_missing_default_configuration'))
class VoucherSequence(ModelSQL, ModelView):
@ -77,19 +75,34 @@ class VoucherSequence(ModelSQL, ModelView):
__name__ = 'account.voucher.sequence'
voucher_receipt_sequence = fields.MultiValue(fields.Many2One('ir.sequence',
'Voucher Receipt Sequence', required=True,
domain=[('code', '=', 'account.voucher')]))
domain=[
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
]))
voucher_payment_sequence = fields.MultiValue(fields.Many2One('ir.sequence',
'Voucher Payment Sequence', required=True,
domain=[('code', '=', 'account.voucher')]))
domain=[
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
]))
voucher_notes_sequence = fields.MultiValue(fields.Many2One('ir.sequence',
'Voucher Notes Sequence', required=True,
domain=[('code', '=', 'account.voucher')]))
domain=[
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
]))
voucher_multipayment_sequence = fields.MultiValue(fields.Many2One('ir.sequence',
'Voucher Multipayment Sequence', required=True,
domain=[('code', '=', 'account.voucher')]))
domain=[
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
]))
voucher_multirevenue_sequence = fields.MultiValue(fields.Many2One('ir.sequence',
'Voucher Multirevenue Sequence', required=True,
domain=[('code', '=', 'account.multirevenue')]))
domain=[
('sequence_type', '=',
Id('account_voucher', 'sequence_type_multirevenue')),
]))
class ResUserAuthorizedVoucher(ModelSQL, ModelView):

View file

@ -6,7 +6,6 @@ the full copyright notices and license terms. -->
<data>
<record model="ir.sequence.type" id="sequence_type_voucher">
<field name="name">Account Voucher</field>
<field name="code">account.voucher</field>
</record>
<record model="ir.sequence.type-res.group"
@ -22,23 +21,23 @@ the full copyright notices and license terms. -->
<record model="ir.sequence" id="sequence_voucher_receipt">
<field name="name">Voucher Receipt</field>
<field name="code">account.voucher</field>
<field name="sequence_type" ref="sequence_type_voucher"/>
</record>
<record model="ir.sequence" id="sequence_voucher_payment">
<field name="name">Voucher Payment</field>
<field name="code">account.voucher</field>
<field name="sequence_type" ref="sequence_type_voucher"/>
</record>
<record model="ir.sequence" id="sequence_voucher_multipayment">
<field name="name">Voucher Multipayment</field>
<field name="code">account.voucher</field>
<field name="sequence_type" ref="sequence_type_voucher"/>
</record>
<record model="ir.sequence" id="sequence_voucher_notes">
<field name="name">Voucher Notes</field>
<field name="code">account.voucher</field>
<field name="sequence_type" ref="sequence_type_voucher"/>
</record>
<menuitem name="Voucher" parent="account.menu_account"
@ -80,7 +79,6 @@ the full copyright notices and license terms. -->
<!-- Sequence Multi-Revenue -->
<record model="ir.sequence.type" id="sequence_type_multirevenue">
<field name="name">Multirevenue</field>
<field name="code">account.multirevenue</field>
</record>
<record model="ir.sequence.type-res.group"
id="sequence_type_multirevenue_admin">
@ -94,7 +92,7 @@ the full copyright notices and license terms. -->
</record>
<record model="ir.sequence" id="sequence_multirevenue">
<field name="name">Multirevenue</field>
<field name="code">account.multirevenue</field>
<field name="sequence_type" ref="sequence_type_multirevenue"/>
</record>
<record model="ir.ui.view" id="voucher_user_authorized_view_form">
<field name="model">account.voucher_configuration.user_authorized</field>

89
exceptions.py Normal file
View file

@ -0,0 +1,89 @@
# 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.exceptions import UserError, UserWarning
from trytond.model.exceptions import ValidationError
# class PeriodNotFoundError(UserError):
# pass
#
#
# class PeriodValidationError(ValidationError):
# pass
#
#
# class ClosePeriodError(PeriodValidationError):
# pass
#
#
# class PeriodDatesError(PeriodValidationError):
# pass
#
#
# class PeriodSequenceError(PeriodValidationError):
# pass
#
#
# class FiscalYearNotFoundError(UserError):
# pass
#
#
# class FiscalYearValidationError(ValidationError):
# pass
#
#
# class FiscalYearDatesError(FiscalYearValidationError):
# pass
#
#
# class FiscalYearSequenceError(FiscalYearValidationError):
# pass
#
#
# class FiscalYearCloseError(UserError):
# pass
#
#
# class FiscalYearReOpenError(UserError):
# pass
#
#
# class AccountMissing(UserError):
# pass
#
#
# class SecondCurrencyError(ValidationError):
# pass
#
#
# class ChartWarning(UserWarning):
# pass
#
#
# class PostError(UserError):
# pass
#
#
# class MoveDatesError(ValidationError):
# pass
#
#
# class CancelWarning(UserWarning):
# pass
#
#
# class ReconciliationError(ValidationError):
# pass
#
#
# class DeleteDelegatedWarning(UserWarning):
# pass
#
#
# class CancelDelegatedWarning(UserWarning):
# pass
#
#
# class GroupLineError(UserError):
# pass

View file

@ -8,11 +8,9 @@ from trytond.pool import Pool, PoolMeta
from trytond.wizard import Wizard, StateTransition, StateView, Button
from trytond.transaction import Transaction
from trytond.pyson import Eval
from trytond.model.exceptions import AccessError
from trytond.i18n import gettext
__all__ = [
'Invoice', 'AdvancePaymentAsk', 'AdvancePayment', 'CrossPaymentAsk',
'CrossPayment'
]
_ZERO = Decimal('0.0')
@ -40,15 +38,17 @@ class Invoice(metaclass=PoolMeta):
for voucher in vouchers:
if self.invoice.type == 'in':
kind = 'payable'
account_type_payable = True
else:
kind = 'receivable'
account_type_receivable = True
if voucher.state in ['posted'] and voucher.move:
move_lines = [
line.id for line in voucher.move.lines
if l.account.kind == kind
]
move_lines = []
for line in voucher.move.lines:
if account_type_payable and line.account.type.payable:
move_lines.append(line.id)
elif account_type_receivable and line.account.type.receivable:
move_lines.append(line.id)
lines_to_create = []
accounts_to_reconcile = []
@ -156,7 +156,8 @@ class Invoice(metaclass=PoolMeta):
if values:
payment_mode = values[0]
if not payment_mode:
return Voucher.raise_user_error('missing_paymode')
raise AccessError(
gettext('account_voucher.msg_missing_paymode'))
total_amount_to_pay = sum([i.amount_to_pay for i in invoices])
if not invoices:
@ -258,10 +259,14 @@ class AdvancePaymentAsk(ModelView):
('party', '=', party_id),
('account', 'in', advance_account_ids),
('reconciliation', '=', None),
('account.kind', '=', account.kind),
['OR',
('account.type.payable', '=', account.type.payable),
('account.type.receivable', '=', account.type.receivable),
],
]
if account.kind == 'payable':
if account.type.payable:
debit_credit = ('debit', '>', 0)
else:
debit_credit = ('credit', '>', 0)
@ -285,11 +290,6 @@ class AdvancePayment(Wizard):
@classmethod
def __setup__(cls):
super(AdvancePayment, cls).__setup__()
cls._error_messages.update({
'invalid_total_advance': 'Total payment advances can not '
'be greater than Amount to Pay on invoice!',
'invoice_dont_posted': 'The invoice must posted!',
})
def transition_search_advance(self):
pool = Pool()
@ -297,12 +297,13 @@ class AdvancePayment(Wizard):
Advance = pool.get('account.invoice.advance_payment.ask')
invoice = Invoice(Transaction().context.get('active_id'))
if invoice.state != 'posted':
return self.raise_user_error('invoice_dont_posted')
return AccessError(
gettext('account_voucher.msg_invoice_dont_posted'))
if invoice.type == 'in':
account_types = ['receivable']
account_types = ('account.type.receivable', '=', True)
debit_credit = ('debit', '>', 0)
elif invoice.type == 'out':
account_types = ['payable']
account_types = ('account.type.payable', '=', True)
debit_credit = ('credit', '>', 0)
else:
return 'end'
@ -311,8 +312,8 @@ class AdvancePayment(Wizard):
('move.state', '=', 'posted'),
('party', '=', invoice.party.id),
('state', '=', 'valid'),
('account.kind', 'in', account_types),
]
domain.append(account_types)
domain.append(debit_credit)
Advance.lines.domain = domain
return 'start'
@ -330,7 +331,8 @@ class AdvancePayment(Wizard):
return 'end'
if not config.default_journal_note:
Note.raise_user_error('missing_journal_note')
raise AccessError(
gettext('account_voucher.msg_missing_journal_note'))
lines_to_create = []
reconcile_advance = []
@ -486,10 +488,6 @@ class CrossPayment(Wizard):
@classmethod
def __setup__(cls):
super(CrossPayment, cls).__setup__()
cls._error_messages.update({
'invalid_total_cross': 'Total payment crosss can not '
'be greater than Amount to Pay on invoice!',
})
def do_add_payment(self, action):
data = {
@ -512,7 +510,8 @@ class CrossPayment(Wizard):
config = Config.get_configuration()
if not config.default_journal_note:
Note.raise_user_error('missing_journal_note')
raise AccessError(
gettext('account_voucher.msg_missing_journal_note'))
lines_to_create = []
accounts_to_reconcile = []

90
message.xml Normal file
View file

@ -0,0 +1,90 @@
<?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. -->
<tryton>
<data grouped="1">
<record model="ir.message" id="msg_missing_default_configuration">
<field name="text">Missing the configuration for current company!.</field>
</record>
<record model="ir.message" id="msg_missing_pay_lines">
<field name="text">You have to enter lines to pay!.</field>
</record>
<record model="ir.message" id="msg_delete_voucher">
<field name="text">You can not delete a voucher that is posted!.</field>
</record>
<record model="ir.message" id="msg_missing_journal_account">
<field name="text">Missing debit or credit account on journal!</field>
</record>
<record model="ir.message" id="msg_invalid_party_line">
<field name="text">Invalid party "%(party)" on line!.</field>
</record>
<record model="ir.message" id="msg_missing_party_line">
<field name="text">Missing party on line!.</field>
</record>
<record model="ir.message" id="msg_missing_party_voucher">
<field name="text">Missing party on voucher!.</field>
</record>
<record model="ir.message" id="msg_missing_approve">
<field name="text">Missing configuration users authorized or select user to authorized</field>
</record>
<record model="ir.message" id="msg_missing_paymode">
<field name="text">Missing party on voucher!.</field>
</record>
<record model="ir.message" id="msg_fail_reconcile_invoice">
<field name="text">Fail reconcile the invoice "%(invoice)"!...maybe is twice in voucher or is already paid.. </field>
</record>
<record model="ir.message" id="msg_invalid_total_advance">
<field name="text">Total payment advances can not
be greater than Amount to Pay on invoice!</field>
</record>
<record model="ir.message" id="msg_invoice_dont_posted">
<field name="text">The invoice must posted!</field>
</record>
<record model="ir.message" id="msg_missing_journal_note">
<field name="text">Missing journal on note!.</field>
</record>
<record model="ir.message" id="msg_invalid_total_cross">
<field name="text">Total payment crosss can not be greater than Amount to Pay on invoice!</field>
</record>
<record model="ir.message" id="msg_exist_voucher">
<field name="text">You cannot perform the operation because already exist vouchers generated!</field>
</record>
<record model="ir.message" id="msg_total_error">
<field name="text">The total transactions is diferent to total lines to pay!</field>
</record>
<record model="ir.message" id="msg_without_account">
<field name="text">Not exist account in line!</field>
</record>
<record model="ir.message" id="msg_post_empty_note">
<field name="text">You can not post note "%(s)" because it is empty.</field>
</record>
<record model="ir.message" id="msg_post_unbalanced_note">
<field name="text">You can not post note "%(s)" because it is an unbalanced.</field>
</record>
<record model="ir.message" id="msg_missing_lines">
<field name="text">You have to enter lines!</field>
</record>
<record model="ir.message" id="msg_delete_note">
<field name="text">You can not delete a note that is posted!</field>
</record>
<record model="ir.message" id="msg_missing_voucher_configuration_company">
<field name="text">Missing configuration of voucher for company!</field>
</record>
<record model="ir.message" id="msg_note_created">
<field name="text">Note "%(s)" created. \n</field>
</record>
<record model="ir.message" id="msg_line_party_required">
<field name="text">Note Line with account require "%(s)" party.</field>
</record>
<record model="ir.message" id="msg_tax_account_no_reconcile">
<field name="text">Account "%(account)" assigned to tax "%(tax)" is not reconcilable.</field>
</record>
<record model="ir.message" id="msg_moves_in_draft">
<field name="text">There are moves account in draft state for selected periods.</field>
</record>
<record model="ir.message" id="msg_state_voucher_draft">
<field name="text">Purchase number "%(number)" is in state "%(state)".</field>
</record>
</data>
</tryton>

View file

@ -7,6 +7,8 @@ from trytond.transaction import Transaction
from trytond.pyson import Eval, Bool
from trytond.pool import Pool
from trytond.report import Report
from trytond.model.exceptions import AccessError
from trytond.i18n import gettext
conversor = None
@ -17,11 +19,6 @@ except:
print("Warning: Does not possible import numword module, please install it...!")
__all__ = [
'MultiRevenue', 'PaymentLines', 'MultiRevenueTransaction',
'MultiRevenueLine'
]
STATES = {
'readonly': Eval('state') != 'draft',
}
@ -51,11 +48,6 @@ class MultiRevenue(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(MultiRevenue, cls).__setup__()
cls._error_messages.update({
'exist_voucher': 'You cannot perform the operation because already exist vouchers generated!',
'total_error': 'The total transactions is diferent to total lines to pay!',
'without_account': 'Not exist account in line!',
})
cls._buttons.update({
'draft': {
'invisible': Eval('state') == 'draft',
@ -110,7 +102,7 @@ class MultiRevenue(Workflow, ModelSQL, ModelView):
def process(cls, records):
for record in records:
if not record.validate_total_amount():
cls.raise_user_error('total_error')
raise AccessError(gettext('account_voucher.msg_total_error'))
if not record.code:
cls.set_number(record)
@ -142,7 +134,7 @@ class MultiRevenue(Workflow, ModelSQL, ModelView):
('party', '=', record.party.id),
('state', '=', 'valid'),
('account.reconcile', '=', True),
('account.kind', '=', 'receivable'),
('account.type.receivable', '=', True),
('reconciliation', '=', None),
], order=[('move.date', 'ASC')])
lines_to_create = []
@ -189,7 +181,7 @@ class MultiRevenue(Workflow, ModelSQL, ModelView):
def validate_vouchers(self):
for line in self.lines:
if line.vouchers:
self.raise_user_error('exist_voucher')
raise AccessError(gettext('account_voucher.msg_exist_voucher'))
def get_total_transaction(self, name):
amount_transaction = []
@ -330,7 +322,7 @@ class MultiRevenue(Workflow, ModelSQL, ModelView):
detail_ = line.reference_document
account_id = line.account.id if line.account else None
if not account_id:
self.raise_user_error('without_account')
raise AccessError(gettext('account_voucher.msg_without_account'))
if line.is_prepayment:
detail_ = 'ANTICIPO'
@ -481,7 +473,7 @@ class MultiRevenueOtherConcepts(ModelSQL, ModelView):
required=True, select=True)
account = fields.Many2One('account.account', 'Account', required=True, domain=[
('company', '=', Eval('context', {}).get('company', -1)),
('kind', '!=', 'view'),
('type', '!=', None),
])
amount = fields.Numeric('Amount', required=True)
description = fields.Char('Description')
@ -505,7 +497,7 @@ class MultiRevenueLine(ModelSQL, ModelView):
('move.company', '=', Eval('context', {}).get('company', -1)),
('state', '=', 'valid'),
('account.reconcile', '=', True),
('account.kind', '=', 'receivable'),
('account.type.receivable', '=', True),
('reconciliation', '=', None),
], depends=['party'])
date_document = fields.Function(fields.Date('Date Document', readonly=True), 'get_data')
@ -525,7 +517,7 @@ class MultiRevenueLine(ModelSQL, ModelView):
'invisible': Bool(Eval('move_line')),
}, domain=[
('company', '=', Eval('context', {}).get('company', -1)),
('kind', '!=', 'view'),
('type', '!=', None),
])
vouchers = fields.One2Many('account.multirevenue.line.vouchers',
'multirevenue_line', 'Vouchers', readonly=True)

View file

@ -122,6 +122,7 @@ this repository contains the full copyright notices and license terms. -->
</record>
<record model="ir.rule.group" id="rule_group_multirevenue">
<field name="name">User in companies</field>
<field name="model" search="[('model', '=', 'account.multirevenue')]"/>
<field name="global_p" eval="True"/>
</record>

View file

@ -1,10 +1,12 @@
[tryton]
version=5.0.3
version=6.0.0
depends:
account
account_invoice
bank
account_bank_statement
email
account_payment
xml:
configuration.xml
voucher.xml

View file

@ -13,7 +13,7 @@ the full copyright notices and license terms. -->
<field name="date"/>
<label name="check_number"/>
<field name="check_number"/>
<label name="reference"/>
<label name="reference"/>
<field name="reference"/>
<field name="payment_type" invisible="1"/>
<newline/>

View file

@ -4,7 +4,7 @@ this repository contains the full copyright notices and license terms. -->
<tree>
<field name="number"/>
<field name="date"/>
<field name="description"/>
<!-- <field name="description"/>
<field name="amount_to_pay"/>
<field name="payment_mode"/>
<field name="party"/>
@ -14,5 +14,5 @@ this repository contains the full copyright notices and license terms. -->
<field name="state"/>
<button name="draft" string="Draft" tree_invisible="1"/>
<button name="processed" string="Process" tree_invisible="1"/>
<button name="posted" string="Post" tree_invisible="1"/>
<button name="posted" string="Post" tree_invisible="1"/> -->
</tree>

View file

@ -1,7 +1,7 @@
<?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. -->
<tree editable="top">
<tree editable="1">
<field name="description"/>
<field name="party"/>
<field name="reference"/>

View file

@ -2,8 +2,8 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<form>
<label name="include_account_kind"/>
<field name="include_account_kind"/>
<!-- <label name="include_account_kind"/>
<field name="include_account_kind"/> -->
<label name="is_multipayment"/>
<field name="is_multipayment"/>
<field name="parties" colspan="4"/>

View file

@ -1,7 +1,7 @@
<?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. -->
<tree editable="top">
<tree >
<field name="account"/>
<field name="detail"/>
<field name="amount"/>

View file

@ -2,6 +2,6 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree editable="bottom">
<tree editable="1">
<field name="user"/>
</tree>

View file

@ -7,9 +7,11 @@ from sql import Table
from trytond.model import ModelView, ModelSQL, fields, Workflow
from trytond.wizard import Wizard, StateView, StateTransition, Button, StateReport
from trytond.transaction import Transaction
from trytond.pyson import Eval, In, Or, Bool
from trytond.pyson import Eval, In, Or, Bool, Id
from trytond.pool import Pool
from trytond.report import Report
from trytond.model.exceptions import AccessError
from trytond.i18n import gettext
conversor = None
try:
@ -20,20 +22,6 @@ except:
print("Please install it...!")
__all__ = [
'VoucherPayMode', 'Voucher', 'VoucherLine', 'AdvanceVoucherStart',
'SelectLinesAsk', 'SelectLines', 'VoucherReport', 'Note', 'AdvanceVoucher',
'NoteLine', 'NoteReport', 'FilteredVouchersReport', 'VoucherFixNumber',
'VoucherFixNumberStart', 'NoteFixNumberStart', 'NoteFixNumber',
'VoucherMoveReport', 'SelectMoveLinesAsk', 'SelectMoveLines',
'VoucherTemplate', 'VoucherTemplateAccount', 'CreateVoucher',
'VoucherTemplateParty', 'CreateVoucherStart', 'VoucherSheetStart',
'VoucherSheet', 'VoucherSheetReport', 'TemplateLine',
'AddZeroAdjustmentStart', 'AddZeroAdjustment', 'TaxesConsolidationDone',
'TaxesConsolidationStart', 'TaxesConsolidation', 'ReceiptRelation',
'ForwardVoucherMail', 'PreapprovedVoucherStart', 'PreapprovedVoucher'
]
_STATES = {
'readonly': Eval('state') != 'draft',
}
@ -92,7 +80,7 @@ class TemplateLine(ModelSQL, ModelView):
account = fields.Many2One('account.account', 'Account',
required=True, domain=[
('company', '=', Eval('context', {}).get('company', -1)),
('kind', '!=', 'view'),
('type', '!=', None),
])
amount = fields.Numeric('Amount', digits=(16, 2))
@ -120,7 +108,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
'required': Eval('payment_type') == 'check',
}, depends=['payment_mode'])
payment_mode = fields.Many2One('account.voucher.paymode', 'Payment Mode',
select=True, required=True, states={
required=True, states={
'readonly': Eval('state') != 'draft',
})
payment_type = fields.Char('Payment Type', depends=['payment_mode'])
@ -132,7 +120,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
}, depends=['state'])
voucher_type_string = voucher_type.translated('voucher_type')
date = fields.Date('Date', required=True, states=_STATES)
journal = fields.Many2One('account.journal', 'Journal', required=True,
journal = fields.Many2One('account.payment.journal', 'Journal', required=True,
depends=['voucher_type'], states=_STATES)
currency = fields.Many2One('currency.currency', 'Currency', required=True,
states=_STATES)
@ -172,7 +160,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
account = fields.Many2One('account.account', 'Account', required=True,
states=_STATES, domain=[
('company', '=', Eval('context', {}).get('company', -1)),
('kind', '!=', 'view'),
('type', '!=', None),
])
amount_to_pay_words = fields.Char('Amount to Pay (Words)',
states={'readonly': True}, depends=['lines'])
@ -197,25 +185,12 @@ class Voucher(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(Voucher, cls).__setup__()
cls._error_messages.update({
'missing_pay_lines': 'You have to enter lines to pay!',
'delete_voucher': 'You can not delete a voucher that is posted!',
'missing_journal_account': 'Missing debit or credit account on journal!',
'invalid_party_line': 'Invalid party %s on line!',
'missing_party_line': 'Missing party on line!',
'missing_party_voucher': 'Missing party on voucher!',
'missing_approve': 'Missing configuration users authorized or select user to authorized',
'missing_paymode': 'Missing party on voucher!',
'fail_reconcile_invoice': 'Fail reconcile the invoice %s!...' +
' maybe is twice in voucher or is already paid.',
})
cls._buttons.update({
'draft': {
'invisible': Eval('state') == 'draft',
},
'post': {
'invisible': (~Eval('state').in_(['processed', 'approved'])
& ~(Eval('state') == 'posted')),
'invisible': ~Eval('state').in_(['processed', 'approved']),
'help': 'Cancel the invoice',
'depends': ['state'],
},
@ -223,7 +198,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
'invisible': Eval('state') != 'draft',
},
'cancel': {
'invisible': Eval('state') == 'cancel',
'invisible': Eval('state').in_(['cancel', 'posted'])
},
'select_lines': {
'invisible': Eval('state') != 'draft',
@ -260,8 +235,8 @@ class Voucher(Workflow, ModelSQL, ModelView):
def get_rec_name(self, name):
rec_name = self.number or ' '
detail = self.reference or ' '
self._rec_name = rec_name + '['+detail+']'
return (self._rec_name)
# self._rec_name = rec_name + '['+detail+']'
return rec_name + '['+detail+']'
@classmethod
def validate(cls, vouchers):
@ -275,7 +250,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
def default_method_counterpart():
return 'one_line'
@fields.depends('party', 'bank_account')
@fields.depends('party', 'bank')
def on_change_with_bank_account_number(self):
if self.party and self.party.bank_accounts:
return self.party.bank_accounts[0].numbers[0].id
@ -311,18 +286,16 @@ class Voucher(Workflow, ModelSQL, ModelView):
new_vouchers.append(new_voucher)
return new_vouchers
@fields.depends('payment_mode', 'account' 'journal', 'payment_type', 'voucher_type')
@fields.depends('payment_mode', 'account', 'journal', 'payment_type', 'voucher_type')
def on_change_payment_mode(self):
if not self.payment_mode:
self.payment_type = None,
self.bank = None
self.journal = self.payment_mode.journal.id
self.payment_type = self.payment_mode.payment_type
self.account = Voucher.get_account(self.voucher_type, self.payment_mode)
if self.payment_mode.payment_type != 'cash':
self.bank = self.payment_mode.bank_account.bank.id
else:
self.bank = None
if self.payment_mode:
self.journal = self.payment_mode.journal.id
self.payment_type = self.payment_mode.payment_type
self.account = Voucher.get_account(self.voucher_type, self.payment_mode)
if self.payment_mode.payment_type != 'cash':
self.bank = self.payment_mode.bank_account.bank.id
else:
self.bank = None
@classmethod
def get_account(cls, voucher_type, payment_mode):
@ -330,7 +303,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
if payment_mode.account:
account = payment_mode.account.id
else:
cls.raise_user_error('missing_journal_account')
raise AccessError(gettext('account_voucher.msg_missing_journal_account'))
return account
def get_amount2words(self, value):
@ -393,7 +366,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
sequence = getattr(self.payment_mode, 'sequence_%s' % voucher_type)
with Transaction().set_context(date=accounting_date):
return Sequence.get_id(sequence.id)
return sequence.get()
def get_target_account_bank(self, name):
if self.party and self.party.bank_accounts:
@ -418,14 +391,13 @@ class Voucher(Workflow, ModelSQL, ModelView):
)
config = Configuration(1)
user_id = Transaction().user
print('pasa por aqui')
if config and self.approved_by:
authorized_user = ResUserAuthorizedVoucher.search([
('configuration', '=', config.id),
('user', '=', user_id),
])
if not authorized_user:
self.raise_user_error('user_not_authorized')
raise AccessError(gettext('account_voucher.msg_user_not_authorized'))
print(False, 'result check')
return False
else:
@ -440,7 +412,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
# else:
# return False
else:
self.raise_user_error('missing_approve')
raise AccessError(gettext('account_voucher.msg_missing_approve'))
@classmethod
@ModelView.button
@ -508,9 +480,9 @@ class Voucher(Workflow, ModelSQL, ModelView):
for voucher in vouchers:
to_reconcile = None
if not voucher.payment_mode.account or not voucher.payment_mode.account:
voucher.raise_user_error('missing_journal_account')
raise AccessError(gettext('account_voucher.msg_missing_journal_account'))
if voucher.amount_to_pay <= Decimal("0.0"):
voucher.raise_user_error('missing_pay_lines')
raise AccessError(gettext('account_voucher.msg_missing_pay_lines'))
if not voucher.move:
to_reconcile = voucher.create_move()
if voucher.move.state == 'posted':
@ -618,7 +590,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
if self.party:
to_create['party'] = self.party.id
else:
self.raise_user_error('missing_party_voucher')
raise AccessError(gettext('account_voucher.msg_missing_party_voucher'))
move_lines.append(MoveLine.create([to_create]))
for line in self.lines:
@ -652,7 +624,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
try:
pending_reconcile, remainder = invoice.get_reconcile_lines_for_amount(amount)
except:
self.raise_user_error('fail_reconcile_invoice', invoice.number)
raise AccessError(gettext('account_voucher.msg_fail_reconcile_invoice', invoice= invoice.number))
if remainder == _ZERO:
to_reconcile_lines = [sl, ml]
to_reconcile_lines_ids = [sl.id, ml.id]
@ -679,7 +651,7 @@ class Voucher(Workflow, ModelSQL, ModelView):
Move = pool.get('account.move')
for voucher in vouchers:
if voucher.state == 'posted':
cls.raise_user_error('delete_voucher')
raise AccessError(gettext('account_voucher.msg_delete_voucher'))
if voucher.move:
Move.delete([voucher.move])
Line.delete([l for v in vouchers for l in v.lines])
@ -755,7 +727,7 @@ class VoucherLine(ModelSQL, ModelView):
account = fields.Many2One('account.account', 'Account', required=True,
domain=[
('company', '=', Eval('context', {}).get('company', -1)),
('kind', '!=', 'view'),
('type', '!=', None),
])
party = fields.Many2One('party.party', 'Party')
amount = fields.Numeric('Amount', digits=(16, 2), required=True)
@ -764,7 +736,7 @@ class VoucherLine(ModelSQL, ModelView):
('move.state', '=', 'posted'),
('state', '=', 'valid'),
('reconciliation', '=', None),
('account.kind', 'in', ['payable', 'receivable', 'other']),
('account.type.statement', 'in', ['balance', 'off-balance']),
], depends=['party'])
amount_original = fields.Numeric('Original Amount', digits=(16, 2),
readonly=True)
@ -773,8 +745,7 @@ class VoucherLine(ModelSQL, ModelView):
def get_rec_name(self, name):
rec_name = self.voucher.number or ' '
detail = self.detail or ' '
self._rec_name = rec_name + '['+detail+']'
return (self._rec_name)
return rec_name + '['+detail+']'
@classmethod
def search_rec_name(cls, name, clause):
@ -845,7 +816,8 @@ class VoucherLine(ModelSQL, ModelView):
line_party_id = self.party.id
else:
if self.account.party_required:
self.voucher.raise_user_error('missing_party_line')
raise AccessError(
gettext('account_voucher.msg_missing_party_line'))
line_party_id = None
if self.account.party_required:
@ -900,11 +872,11 @@ class SelectLinesAsk(ModelView):
is_multipayment = fields.Boolean('Is Multipayment', states={
'readonly': True,
})
include_account_kind = fields.Selection([
('other', 'Other'),
('', ''),
], 'Include Account Kind',
help='The kind account selected will be included on preview.')
# include_account_kind = fields.Selection([
# ('other', 'Other'),
# ('', ''),
# ], 'Include Account Kind',
# help='The kind account selected will be included on preview.')
@staticmethod
def default_is_multipayment():
@ -920,9 +892,9 @@ class SelectLinesAsk(ModelView):
if voucher.voucher_type != 'multipayment' and voucher.party:
return [voucher.party.id]
@staticmethod
def default_account_kind_other():
return ''
# @staticmethod
# def default_account_kind_other():
# return ''
class SelectLines(Wizard):
@ -942,29 +914,32 @@ class SelectLines(Wizard):
Voucher = pool.get('account.voucher')
Select = pool.get('account.voucher.select_lines.ask')
voucher = Voucher(Transaction().context.get('active_id'))
account_kind = [Eval('include_account_kind')]
account_type = []
if voucher.voucher_type == 'receipt':
account_kind.append('receivable')
account_type.append(('account.type.receivable', '=', True),)
debit_credit = ('debit', '>', 0)
elif voucher.voucher_type == 'multipayment':
account_kind.append('receivable')
account_kind.append('payable')
account_type.append(
['OR',
('account.type.receivable', '=', True),
('account.type.payable', '=', True)])
debit_credit = ('debit', '>', 0)
debit_credit = ('credit', '>', 0)
else:
account_kind.append('payable')
account_type.append(('account.type.payable', '=', True))
debit_credit = ('credit', '>', 0)
line_domain = [
('account.kind', 'in', account_kind),
('account.reconcile', '=', True),
('state', '=', 'valid'),
('reconciliation', '=', None),
('move.state', '=', 'posted'),
('party', 'in', Eval('parties')),
]
if 'other' not in account_kind:
line_domain.append(debit_credit)
line_domain.append(debit_credit)
if account_type:
line_domain.append(account_type)
Select.lines.domain = line_domain
return 'start'
@ -982,8 +957,8 @@ class VoucherReport(Report):
__name__ = 'account.voucher.report'
@classmethod
def get_context(cls, records, data):
report_context = super(VoucherReport, cls).get_context(records, data)
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
Company = Pool().get('company.company')
company_id = Transaction().context.get('company')
report_context['company'] = Company(company_id)
@ -995,8 +970,8 @@ class VoucherMoveReport(Report):
__name__ = 'account.voucher_move.report'
@classmethod
def get_context(cls, records, data):
report_context = super(VoucherMoveReport, cls).get_context(records, data)
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
Company = Pool().get('company.company')
company_id = Transaction().context.get('company')
report_context['company'] = Company(company_id)
@ -1028,7 +1003,7 @@ class VoucherPayMode(ModelSQL, ModelView):
'invisible': Eval('payment_type') == 'cash',
'required': Eval('payment_type') != 'cash'
})
journal = fields.Many2One('account.journal', 'Journal', required=True)
journal = fields.Many2One('account.payment.journal', 'Journal', required=True)
kind = fields.Selection([
('payment', 'Payments'),
('receipt', 'Receipts'),
@ -1038,19 +1013,22 @@ class VoucherPayMode(ModelSQL, ModelView):
'Voucher Sequence Payment', domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'account.voucher'),
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
], required=True)
sequence_multipayment = fields.Many2One('ir.sequence',
'Voucher Sequence Multipayment', domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'account.voucher'),
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
], required=True)
sequence_receipt = fields.Many2One('ir.sequence',
'Voucher Sequence Receipt', domain=[
('company', 'in',
[Eval('context', {}).get('company', -1), None]),
('code', '=', 'account.voucher'),
('sequence_type', '=',
Id('account_voucher', 'sequence_type_voucher')),
], required=True)
account = fields.Many2One('account.account',
'Account', domain=[
@ -1082,7 +1060,7 @@ class Note(Workflow, ModelSQL, ModelView):
number = fields.Char('Number', readonly=True, help="Voucher Number")
description = fields.Char('Description', states=_STATES_NOTE)
date = fields.Date('Date', required=True, states=_STATES_NOTE)
journal = fields.Many2One('account.journal', 'Journal', required=True,
journal = fields.Many2One('account.payment.journal', 'Journal', required=True,
states=_STATES_NOTE)
currency = fields.Many2One('currency.currency', 'Currency',
required=True, states=_STATES_NOTE)
@ -1105,16 +1083,6 @@ class Note(Workflow, ModelSQL, ModelView):
@classmethod
def __setup__(cls):
super(Note, cls).__setup__()
cls._error_messages.update({
'post_empty_note': ('You can not post note "%s" because it is '
'empty.'),
'post_unbalanced_note': ('You can not post note "%s" because '
'it is an unbalanced.'),
'missing_lines': 'You have to enter lines!',
'delete_note': 'You can not delete a note that is posted!',
'missing_journal_note': 'Missing journal note!',
'missing_voucher_configuration_company': 'Missing configuration of voucher for company!',
})
cls._buttons.update({
'draft': {
'invisible': Eval('state') == 'draft',
@ -1241,24 +1209,24 @@ class Note(Workflow, ModelSQL, ModelView):
('company', '=', Transaction().context.get('company'))
])
if not config or not config[0].voucher_notes_sequence:
return self.raise_user_error('missing_voucher_configuration_company')
raise AccessError(gettext('account_voucher.msg_missing_voucher_configuration_company'))
sequence = getattr(config[0], 'voucher_notes_sequence')
with Transaction().set_context(date=accounting_date):
return Sequence.get_id(sequence.id)
return sequence.get()
@classmethod
def _post_note(cls, note):
values = {'state': 'posted'}
amount = Decimal('0.0')
if not note.lines:
cls.raise_user_error('post_empty_note', (note.rec_name,))
raise AccessError(gettext('account_voucher.msg_post_empty_note', s=note.rec_name))
company = None
for line in note.lines:
amount += line.debit - line.credit
if not company:
company = line.account.company
if not company.currency.is_zero(amount):
cls.raise_user_error('post_unbalanced_note', (note.rec_name,))
raise AccessError(gettext('account_voucher.msg_post_unbalanced_note', s=note.rec_name))
cls.write([note], values)
def create_moves(self):
@ -1268,7 +1236,7 @@ class Note(Workflow, ModelSQL, ModelView):
move_lines_to_create = []
if not self.lines:
self.raise_user_error('missing_lines')
raise AccessError(gettext('account_voucher.msg_missing_lines'))
if not self.period:
period_id = Period.find(self.company.id, date=self.date)
@ -1331,7 +1299,7 @@ class NoteLine(ModelSQL, ModelView):
credit = fields.Numeric('Credit', digits=(16, Eval('currency_digits', 2)),
required=True, depends=['currency_digits', 'debit'])
account = fields.Many2One('account.account', 'Account', select=True,
domain=[('kind', '!=', 'view')], required=True)
domain=[('type', '!=', None)], required=True)
description = fields.Char('Description')
reference = fields.Char('Reference')
party = fields.Many2One('party.party', 'Party', select=True)
@ -1422,8 +1390,8 @@ class NoteReport(Report):
__name__ = 'account.note.report'
@classmethod
def get_context(cls, records, data):
report_context = super(NoteReport, cls).get_context(records, data)
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
sum_credit = 0
sum_debit = 0
for obj in records:
@ -1441,8 +1409,8 @@ class FilteredVouchersReport(Report):
__name__ = 'account.voucher.filtered_vouchers_report'
@classmethod
def get_context(cls, records, data):
report_context = super(FilteredVouchersReport, cls).get_context(records, data)
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
user = Pool().get('res.user')(Transaction().user)
report_context['company'] = user.company
return report_context
@ -1753,8 +1721,8 @@ class VoucherSheetReport(Report):
__name__ = 'account_voucher.sheet_report'
@classmethod
def get_context(cls, records, data):
report_context = super(VoucherSheetReport, cls).get_context(records, data)
def get_context(cls, records, header, data):
report_context = super().get_context(records, header, data)
pool = Pool()
Voucher = pool.get('account.voucher')
Company = pool.get('company.company')
@ -1839,7 +1807,7 @@ class TaxesConsolidationStart(ModelView):
__name__ = 'account_voucher.taxes_consolidation.start'
company = fields.Many2One('company.company', 'Company',
required=True)
journal = fields.Many2One('account.journal', 'Journal',
journal = fields.Many2One('account.payment.journal', 'Journal',
required=True)
fiscalyear = fields.Many2One('account.fiscalyear',
'Fiscal Year', required=True, domain=[
@ -1852,7 +1820,7 @@ class TaxesConsolidationStart(ModelView):
], depends=['fiscalyear'])
payoff_account = fields.Many2One('account.account',
'Payoff Account', required=True, domain=[
('kind', '!=', 'view'),
('type', '==', None),
])
description = fields.Char('Description', required=True)
party = fields.Many2One('party.party', 'Party',
@ -1872,7 +1840,7 @@ class TaxesConsolidationStart(ModelView):
@staticmethod
def default_journal():
Journal = Pool().get('account.journal')
Journal = Pool().get('account.payment.journal')
journals = Journal.search([
('type', '=', 'general')
])
@ -1897,12 +1865,6 @@ class TaxesConsolidation(Wizard):
@classmethod
def __setup__(cls):
super(TaxesConsolidation, cls).__setup__()
cls._error_messages.update({
'note_created': 'Note %s created. \n',
'line_party_required': 'Note Line with account require %s party.',
'tax_account_no_reconcile': 'Account %s assigned to tax %s is not reconcilable.',
'moves_in_draft': 'There are moves account in draft state for selected periods.',
})
def default_done(self, fields):
return {'result': self.result}
@ -1917,15 +1879,15 @@ class TaxesConsolidation(Wizard):
taxes_accounts = []
for tax in self.start.taxes:
if not tax.invoice_account.reconcile:
self.raise_user_error(
'tax_account_no_reconcile',
tax.invoice_account.name,
tax.name)
raise AccessError(
gettext('account_voucher.msg_tax_account_no_reconcile',
invoice=tax.invoice_account.name,
tax=tax.name))
if not tax.credit_note_account.reconcile:
self.raise_user_error(
'tax_account_no_reconcile',
tax.credit_note_account.name,
tax.name)
raise AccessError(
gettext('account_voucher.msg_tax_account_no_reconcile',
invoice=tax.invoice_account.name,
tax=tax.name))
taxes_accounts.extend([tax.invoice_account.id, tax.credit_note_account.id])
periods_ids = [p.id for p in self.start.periods]
@ -1936,7 +1898,8 @@ class TaxesConsolidation(Wizard):
])
if moves_draft:
self.raise_user_error('moves_in_draft')
raise AccessError(
gettext('account_voucher.msg_moves_in_draft'))
move_lines = MoveLine.search([
('move.period', 'in', periods_ids),
@ -1976,8 +1939,9 @@ class TaxesConsolidation(Wizard):
'move_line': line.id,
})
if line.account.party_required and not line.party:
self.raise_user_error('line_party_required',
line.account.code or '[-]',)
raise AccessError(
gettext('account_voucher.msg_line_party_required',
s=line.account.code or '[-]'))
balance.append(line.debit - line.credit)
NoteLine.create(lines_to_create)
@ -1997,9 +1961,9 @@ class TaxesConsolidation(Wizard):
NoteLine.create([payable_line])
note.set_number()
self.result = self.raise_user_error('note_created',
error_args=(note.number,),
raise_exception=False)
self.result = AccessError(
gettext('account_voucher.msg_note_created',
s=note.number))
return 'done'
@ -2115,9 +2079,6 @@ class ForwardVoucherMail(Wizard):
@classmethod
def __setup__(cls):
super(ForwardVoucherMail, cls).__setup__()
cls._error_messages.update({
'state_voucher_draft': ('Purchase number "%s" is in state .'),
})
def transition_forward_mail(self):
pool = Pool()
@ -2128,7 +2089,9 @@ class ForwardVoucherMail(Wizard):
if voucher.state == 'quotation':
voucher.send_byapprove_emails()
else:
self.raise_user_error('state_purchase_draft', voucher.number, voucher.state)
raise AccessError(
gettext('account_voucher.msg_state_purchase_draft',
number=voucher.number, state=voucher.state))
return 'end'
@ -2196,6 +2159,7 @@ class PreapprovedVoucher(Wizard):
voucher.state = 'approved'
voucher.save()
else:
return voucher.raise_user_error('user_not_authorized')
return AccessError(
gettext('account_voucher.msg_user_not_authorized'))
return 'end'