first commit
This commit is contained in:
2
modules/account_invoice/__init__.py
Normal file
2
modules/account_invoice/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
BIN
modules/account_invoice/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/account.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/account.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/company.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/company.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/exceptions.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/exceptions.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/invoice.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/invoice.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/party.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/party.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/account_invoice/__pycache__/payment_term.cpython-311.pyc
Normal file
BIN
modules/account_invoice/__pycache__/payment_term.cpython-311.pyc
Normal file
Binary file not shown.
441
modules/account_invoice/account.py
Normal file
441
modules/account_invoice/account.py
Normal file
@@ -0,0 +1,441 @@
|
||||
# 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 collections import OrderedDict
|
||||
from itertools import islice
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import (
|
||||
MatchMixin, ModelSQL, ModelView, Workflow, fields, sequence_ordered)
|
||||
from trytond.modules.account.exceptions import ClosePeriodError
|
||||
from trytond.modules.company.model import CompanyValueMixin
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Bool, Eval, Id, If
|
||||
from trytond.tools import grouped_slice
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import CancelInvoiceMoveWarning
|
||||
|
||||
|
||||
class Configuration(metaclass=PoolMeta):
|
||||
__name__ = 'account.configuration'
|
||||
|
||||
default_customer_payment_term = fields.MultiValue(
|
||||
fields.Many2One(
|
||||
'account.invoice.payment_term', "Default Customer Payment Term"))
|
||||
customer_payment_reference_number = fields.MultiValue(
|
||||
fields.Selection('get_customer_payment_references',
|
||||
"Customer Payment Reference Number",
|
||||
help="The number used to generate "
|
||||
"the customer payment reference."))
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in 'default_customer_payment_term':
|
||||
return pool.get('account.configuration.default_payment_term')
|
||||
elif field == 'customer_payment_reference_number':
|
||||
return pool.get('account.configuration.payment_reference')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
@classmethod
|
||||
def get_customer_payment_references(cls):
|
||||
pool = Pool()
|
||||
PaymentReference = pool.get('account.configuration.payment_reference')
|
||||
field = 'customer_payment_reference_number'
|
||||
return PaymentReference.fields_get([field])[field]['selection']
|
||||
|
||||
@classmethod
|
||||
def default_customer_payment_reference_number(cls, **pattern):
|
||||
pool = Pool()
|
||||
PaymentReference = pool.get('account.configuration.payment_reference')
|
||||
return PaymentReference.default_customer_payment_reference_number()
|
||||
|
||||
|
||||
class ConfigurationDefaultPaymentTerm(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'account.configuration.default_payment_term'
|
||||
|
||||
default_customer_payment_term = fields.Many2One(
|
||||
'account.invoice.payment_term', "Default Customer Payment Term")
|
||||
|
||||
|
||||
class ConfigurationPaymentReference(ModelSQL, CompanyValueMixin):
|
||||
__name__ = 'account.configuration.payment_reference'
|
||||
|
||||
customer_payment_reference_number = fields.Selection([
|
||||
('invoice', "Invoice"),
|
||||
('party', "Party"),
|
||||
], "Customer Payment Reference Number")
|
||||
|
||||
@classmethod
|
||||
def default_customer_payment_reference_number(cls):
|
||||
return 'invoice'
|
||||
|
||||
|
||||
class FiscalYear(metaclass=PoolMeta):
|
||||
__name__ = 'account.fiscalyear'
|
||||
invoice_sequences = fields.One2Many(
|
||||
'account.fiscalyear.invoice_sequence', 'fiscalyear',
|
||||
"Invoice Sequences",
|
||||
domain=[
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def default_invoice_sequences():
|
||||
if Transaction().user == 0:
|
||||
return []
|
||||
return [{}]
|
||||
|
||||
|
||||
class Period(metaclass=PoolMeta):
|
||||
__name__ = 'account.period'
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('closed')
|
||||
def close(cls, periods):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
company_ids = list({p.company.id for p in periods})
|
||||
invoices = Invoice.search([
|
||||
('company', 'in', company_ids),
|
||||
('state', '=', 'posted'),
|
||||
('move', '=', None),
|
||||
])
|
||||
if invoices:
|
||||
names = ', '.join(i.rec_name for i in invoices[:5])
|
||||
if len(invoices) > 5:
|
||||
names += '...'
|
||||
raise ClosePeriodError(
|
||||
gettext('account_invoice.msg_close_period_non_posted_invoices',
|
||||
invoices=names))
|
||||
super().close(periods)
|
||||
|
||||
|
||||
class InvoiceSequence(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
|
||||
__name__ = 'account.fiscalyear.invoice_sequence'
|
||||
company = fields.Many2One('company.company', "Company", required=True)
|
||||
fiscalyear = fields.Many2One(
|
||||
'account.fiscalyear', "Fiscal Year", required=True, ondelete='CASCADE',
|
||||
domain=[
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
period = fields.Many2One('account.period', 'Period',
|
||||
domain=[
|
||||
('fiscalyear', '=', Eval('fiscalyear', -1)),
|
||||
('type', '=', 'standard'),
|
||||
])
|
||||
in_invoice_sequence = fields.Many2One('ir.sequence.strict',
|
||||
'Supplier Invoice Sequence', required=True,
|
||||
domain=[
|
||||
('sequence_type', '=',
|
||||
Id('account_invoice', 'sequence_type_account_invoice')),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
in_credit_note_sequence = fields.Many2One('ir.sequence.strict',
|
||||
'Supplier Credit Note Sequence', required=True,
|
||||
domain=[
|
||||
('sequence_type', '=',
|
||||
Id('account_invoice', 'sequence_type_account_invoice')),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
out_invoice_sequence = fields.Many2One('ir.sequence.strict',
|
||||
'Customer Invoice Sequence', required=True,
|
||||
domain=[
|
||||
('sequence_type', '=',
|
||||
Id('account_invoice', 'sequence_type_account_invoice')),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
out_credit_note_sequence = fields.Many2One('ir.sequence.strict',
|
||||
'Customer Credit Note Sequence', required=True,
|
||||
domain=[
|
||||
('sequence_type', '=',
|
||||
Id('account_invoice', 'sequence_type_account_invoice')),
|
||||
('company', '=', Eval('company', -1)),
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order.insert(0, ('fiscalyear', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def default_company(cls):
|
||||
return Transaction().context.get('company')
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'account.move'
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + ['account.invoice']
|
||||
|
||||
|
||||
class MoveLine(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.line'
|
||||
|
||||
invoice_payment = fields.Function(fields.Many2One(
|
||||
'account.invoice', "Invoice Payment",
|
||||
domain=[
|
||||
('account', '=', Eval('account', -1)),
|
||||
If(Bool(Eval('party')),
|
||||
('party', '=', Eval('party', -1)),
|
||||
(),
|
||||
),
|
||||
],
|
||||
states={
|
||||
'invisible': Bool(Eval('reconciliation')),
|
||||
}),
|
||||
'get_invoice_payment',
|
||||
setter='set_invoice_payment',
|
||||
searcher='search_invoice_payment')
|
||||
invoice_payments = fields.Many2Many(
|
||||
'account.invoice-account.move.line', 'line', 'invoice',
|
||||
"Invoice Payments", readonly=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._check_modify_exclude.add('invoice_payment')
|
||||
|
||||
@classmethod
|
||||
def _view_reconciliation_muted(cls):
|
||||
pool = Pool()
|
||||
ModelData = pool.get('ir.model.data')
|
||||
muted = super()._view_reconciliation_muted()
|
||||
muted.add(ModelData.get_id(
|
||||
'account_invoice', 'move_line_view_list_to_pay'))
|
||||
return muted
|
||||
|
||||
@classmethod
|
||||
def _get_origin(cls):
|
||||
return super()._get_origin() + [
|
||||
'account.invoice.line', 'account.invoice.tax']
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
default = {} if default is None else default.copy()
|
||||
default.setdefault('invoice_payments', None)
|
||||
return super().copy(lines, default=default)
|
||||
|
||||
@classmethod
|
||||
def get_invoice_payment(cls, lines, name):
|
||||
pool = Pool()
|
||||
InvoicePaymentLine = pool.get('account.invoice-account.move.line')
|
||||
|
||||
ids = list(map(int, lines))
|
||||
result = dict.fromkeys(ids, None)
|
||||
for sub_ids in grouped_slice(ids):
|
||||
payment_lines = InvoicePaymentLine.search([
|
||||
('line', 'in', list(sub_ids)),
|
||||
])
|
||||
result.update({p.line.id: p.invoice.id for p in payment_lines})
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def set_invoice_payment(cls, lines, name, value):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
Invoice.remove_payment_lines(lines)
|
||||
if value:
|
||||
Invoice.add_payment_lines({Invoice(value): lines})
|
||||
|
||||
@classmethod
|
||||
def search_invoice_payment(cls, name, clause):
|
||||
nested = clause[0][len(name):]
|
||||
return [('invoice_payments' + nested, *clause[1:])]
|
||||
|
||||
@property
|
||||
def product(self):
|
||||
pool = Pool()
|
||||
InvoiceLine = pool.get('account.invoice.line')
|
||||
product = super().product
|
||||
if (isinstance(self.origin, InvoiceLine)
|
||||
and self.origin.product):
|
||||
product = self.origin.product
|
||||
return product
|
||||
|
||||
|
||||
def _invoices_to_process(reconciliations):
|
||||
pool = Pool()
|
||||
Reconciliation = pool.get('account.move.reconciliation')
|
||||
Invoice = pool.get('account.invoice')
|
||||
|
||||
move_ids = set()
|
||||
others = set()
|
||||
for reconciliation in reconciliations:
|
||||
for line in reconciliation.lines:
|
||||
move_ids.add(line.move.id)
|
||||
others.update(line.reconciliations_delegated)
|
||||
|
||||
invoices = set()
|
||||
for sub_ids in grouped_slice(move_ids):
|
||||
sub_ids = list(sub_ids)
|
||||
invoices.update(Invoice.search(['OR',
|
||||
('move', 'in', sub_ids),
|
||||
('additional_moves', 'in', sub_ids),
|
||||
]))
|
||||
if others:
|
||||
invoices.update(_invoices_to_process(Reconciliation.browse(others)))
|
||||
|
||||
return invoices
|
||||
|
||||
|
||||
class Reconciliation(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.reconciliation'
|
||||
|
||||
@classmethod
|
||||
def on_modification(cls, mode, reconciliations, field_names=None):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
transaction = Transaction()
|
||||
context = transaction.context
|
||||
|
||||
super().on_modification(mode, reconciliations, field_names=field_names)
|
||||
|
||||
with transaction.set_context(
|
||||
queue_batch=context.get('queue_batch', True)):
|
||||
Invoice.__queue__.process(
|
||||
list(_invoices_to_process(reconciliations)))
|
||||
|
||||
|
||||
class RenewFiscalYear(metaclass=PoolMeta):
|
||||
__name__ = 'account.fiscalyear.renew'
|
||||
|
||||
def fiscalyear_defaults(self):
|
||||
defaults = super().fiscalyear_defaults()
|
||||
defaults['invoice_sequences'] = None
|
||||
return defaults
|
||||
|
||||
@property
|
||||
def invoice_sequence_fields(self):
|
||||
return ['out_invoice_sequence', 'out_credit_note_sequence',
|
||||
'in_invoice_sequence', 'in_credit_note_sequence']
|
||||
|
||||
def create_fiscalyear(self):
|
||||
pool = Pool()
|
||||
Sequence = pool.get('ir.sequence.strict')
|
||||
InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence')
|
||||
fiscalyear = super().create_fiscalyear()
|
||||
|
||||
def standard_period(period):
|
||||
return period.type == 'standard'
|
||||
|
||||
period_mapping = {}
|
||||
for previous, new in zip(
|
||||
filter(
|
||||
standard_period, self.start.previous_fiscalyear.periods),
|
||||
filter(standard_period, fiscalyear.periods)):
|
||||
period_mapping[previous] = new.id
|
||||
|
||||
InvoiceSequence.copy(
|
||||
self.start.previous_fiscalyear.invoice_sequences,
|
||||
default={
|
||||
'fiscalyear': fiscalyear.id,
|
||||
'period': lambda data: period_mapping.get(data['period']),
|
||||
})
|
||||
|
||||
if not self.start.reset_sequences:
|
||||
return fiscalyear
|
||||
sequences = OrderedDict()
|
||||
for invoice_sequence in fiscalyear.invoice_sequences:
|
||||
for field in self.invoice_sequence_fields:
|
||||
sequence = getattr(invoice_sequence, field, None)
|
||||
sequences[sequence.id] = sequence
|
||||
copies = Sequence.copy(list(sequences.values()), default={
|
||||
'name': lambda data: data['name'].replace(
|
||||
self.start.previous_fiscalyear.name,
|
||||
self.start.name)
|
||||
})
|
||||
Sequence.write(copies, {
|
||||
'number_next': Sequence.default_number_next(),
|
||||
})
|
||||
|
||||
mapping = {}
|
||||
for previous_id, new_sequence in zip(sequences.keys(), copies):
|
||||
mapping[previous_id] = new_sequence.id
|
||||
to_write = []
|
||||
for new_sequence, old_sequence in zip(
|
||||
fiscalyear.invoice_sequences,
|
||||
self.start.previous_fiscalyear.invoice_sequences):
|
||||
values = {}
|
||||
for field in self.invoice_sequence_fields:
|
||||
sequence = getattr(old_sequence, field, None)
|
||||
values[field] = mapping[sequence.id]
|
||||
to_write.extend(([new_sequence], values))
|
||||
if to_write:
|
||||
InvoiceSequence.write(*to_write)
|
||||
return fiscalyear
|
||||
|
||||
|
||||
class RescheduleLines(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.line.reschedule'
|
||||
|
||||
@classmethod
|
||||
def reschedule_lines(cls, lines, journal, terms):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
move, balance_line = super().reschedule_lines(lines, journal, terms)
|
||||
|
||||
move_ids = list({l.move.id for l in lines})
|
||||
invoices = Invoice.search(['OR',
|
||||
('move', 'in', move_ids),
|
||||
('additional_moves', 'in', move_ids),
|
||||
])
|
||||
Invoice.write(invoices, {
|
||||
'additional_moves': [('add', [move.id])],
|
||||
})
|
||||
return move, balance_line
|
||||
|
||||
|
||||
class DelegateLines(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.line.delegate'
|
||||
|
||||
@classmethod
|
||||
def delegate_lines(cls, lines, party, journal, date=None):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
move = super().delegate_lines(lines, party, journal, date=None)
|
||||
|
||||
move_ids = list({l.move.id for l in lines})
|
||||
invoices = Invoice.search(['OR',
|
||||
('move', 'in', move_ids),
|
||||
('additional_moves', 'in', move_ids),
|
||||
])
|
||||
Invoice.write(invoices, {
|
||||
'alternative_payees': [('add', [party.id])],
|
||||
'additional_moves': [('add', [move.id])],
|
||||
})
|
||||
return move
|
||||
|
||||
|
||||
class CancelMoves(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.cancel'
|
||||
|
||||
def transition_cancel(self):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
Warning = pool.get('res.user.warning')
|
||||
|
||||
moves_w_invoices = {
|
||||
m: m.origin for m in self.records
|
||||
if (isinstance(m.origin, Invoice)
|
||||
and m.origin.state not in {'paid', 'cancelled'})}
|
||||
if moves_w_invoices:
|
||||
move_names = ', '.join(m.rec_name
|
||||
for m in islice(moves_w_invoices, None, 5))
|
||||
invoice_names = ', '.join(i.rec_name
|
||||
for i in islice(moves_w_invoices.values(), None, 5))
|
||||
if len(moves_w_invoices) > 5:
|
||||
move_names += '...'
|
||||
invoice_names += '...'
|
||||
key = Warning.format('cancel_invoice_move', moves_w_invoices)
|
||||
if Warning.check(key):
|
||||
raise CancelInvoiceMoveWarning(key,
|
||||
gettext('account_invoice.msg_cancel_invoice_move',
|
||||
moves=move_names, invoices=invoice_names),
|
||||
gettext(
|
||||
'account_invoice.msg_cancel_invoice_move_description'))
|
||||
|
||||
return super().transition_cancel()
|
||||
108
modules/account_invoice/account.xml
Normal file
108
modules/account_invoice/account.xml
Normal file
@@ -0,0 +1,108 @@
|
||||
<?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>
|
||||
<record model="ir.ui.icon" id="invoice_icon">
|
||||
<field name="name">tryton-invoice</field>
|
||||
<field name="path">icons/tryton-invoice.svg</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="configuration_view_form">
|
||||
<field name="model">account.configuration</field>
|
||||
<field name="inherit" ref="account.configuration_view_form"/>
|
||||
<field name="name">configuration_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="fiscalyear_view_form">
|
||||
<field name="model">account.fiscalyear</field>
|
||||
<field name="inherit" ref="account.fiscalyear_view_form"/>
|
||||
<field name="name">fiscalyear_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_form">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit" ref="account.move_line_view_form"/>
|
||||
<field name="name">move_line_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_form_move">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit" ref="account.move_line_view_form_move"/>
|
||||
<field name="name">move_line_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_list">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit" ref="account.move_line_view_tree"/>
|
||||
<field name="name">move_line_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_list_move">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="inherit" ref="account.move_line_view_tree_move"/>
|
||||
<field name="name">move_line_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_list_payment">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">move_line_list_payment</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="move_line_view_list_to_pay">
|
||||
<field name="model">account.move.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">move_line_list_to_pay</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_sequence_view_form">
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">invoice_sequence_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_sequence_view_list">
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">invoice_sequence_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_sequence_view_list_sequence">
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">invoice_sequence_list_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_sequence">
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_invoice_sequence_admin">
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule.group" id="rule_group_invoice_sequence_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.fiscalyear.invoice_sequence</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_invoice_sequence_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_invoice_sequence_companies"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
23
modules/account_invoice/company.py
Normal file
23
modules/account_invoice/company.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# 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 fields
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
|
||||
class Company(metaclass=PoolMeta):
|
||||
__name__ = 'company.company'
|
||||
|
||||
purchase_taxes_expense = fields.Boolean(
|
||||
"Purchase Taxes as Expense",
|
||||
help="Check to book purchase taxes as expense.")
|
||||
cancel_invoice_out = fields.Boolean(
|
||||
"Cancel Customer Invoice",
|
||||
help="Allow cancelling move of customer invoice.")
|
||||
|
||||
@classmethod
|
||||
def default_purchase_taxes_expense(cls):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def default_cancel_invoice_out(cls):
|
||||
return False
|
||||
12
modules/account_invoice/company.xml
Normal file
12
modules/account_invoice/company.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
<record model="ir.ui.view" id="company_view_form">
|
||||
<field name="model">company.company</field>
|
||||
<field name="inherit" ref="company.company_view_form"/>
|
||||
<field name="name">company_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
53
modules/account_invoice/exceptions.py
Normal file
53
modules/account_invoice/exceptions.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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 PaymentTermValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class PaymentTermComputeError(UserError):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceTaxValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceNumberError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceLineValidationError(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class PayInvoiceError(UserError):
|
||||
pass
|
||||
|
||||
|
||||
class InvoicePaymentTermDateWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceFutureWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceTaxesWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceSimilarWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class CancelInvoiceMoveWarning(UserWarning):
|
||||
pass
|
||||
1
modules/account_invoice/icons/tryton-invoice.svg
Normal file
1
modules/account_invoice/icons/tryton-invoice.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M18 17H6v-2h12v2zm0-4H6v-2h12v2zm0-4H6V7h12v2zM3 22l1.5-1.5L6 22l1.5-1.5L9 22l1.5-1.5L12 22l1.5-1.5L15 22l1.5-1.5L18 22l1.5-1.5L21 22V2l-1.5 1.5L18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2 4.5 3.5 3 2v20z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
||||
|
After Width: | Height: | Size: 356 B |
1100
modules/account_invoice/invoice.fodt
Normal file
1100
modules/account_invoice/invoice.fodt
Normal file
File diff suppressed because it is too large
Load Diff
3921
modules/account_invoice/invoice.py
Normal file
3921
modules/account_invoice/invoice.py
Normal file
File diff suppressed because it is too large
Load Diff
523
modules/account_invoice/invoice.xml
Normal file
523
modules/account_invoice/invoice.xml
Normal file
@@ -0,0 +1,523 @@
|
||||
<?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>
|
||||
<menuitem
|
||||
name="Invoices"
|
||||
parent="account.menu_account"
|
||||
sequence="20"
|
||||
id="menu_invoices"/>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_pay">
|
||||
<field name="name">Pay Invoice</field>
|
||||
<field name="wiz_name">account.invoice.pay</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_view_form">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">invoice_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="invoice_view_tree">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">invoice_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_invoice_form">
|
||||
<field name="name">Invoices</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="invoice_view_tree"/>
|
||||
<field name="act_window" ref="act_invoice_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="invoice_view_form"/>
|
||||
<field name="act_window" ref="act_invoice_form"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.act_window" id="act_invoice_out_form">
|
||||
<field name="name">Customer Invoices</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="domain" eval="[('type', '=', 'out')]" pyson="1"/>
|
||||
<field name="context" eval="None"/>
|
||||
<field name="search_value"></field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_out_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="invoice_view_tree"/>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_out_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="invoice_view_form"/>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_out_domain_draft">
|
||||
<field name="name">Draft</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_out_domain_validated">
|
||||
<field name="name">Validated</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('state', '=', 'validated')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_out_domain_posted">
|
||||
<field name="name">Posted</field>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="domain" eval="[('state', '=', 'posted')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_out_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_invoice_out_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_invoices"
|
||||
action="act_invoice_out_form"
|
||||
sequence="10"
|
||||
id="menu_invoice_out_form"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_invoice_in_form">
|
||||
<field name="name">Supplier Invoices</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="domain" eval="[('type', '=', 'in')]" pyson="1"/>
|
||||
<field name="context" eval="None"/>
|
||||
<field name="search_value"></field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_in_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="invoice_view_tree"/>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_in_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="invoice_view_form"/>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_in_domain_draft">
|
||||
<field name="name">Draft</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('state', '=', 'draft')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_in_domain_validated">
|
||||
<field name="name">Validated</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('state', '=', 'validated')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_in_domain_posted">
|
||||
<field name="name">Posted</field>
|
||||
<field name="sequence" eval="30"/>
|
||||
<field name="domain" eval="[('state', '=', 'posted')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_in_domain_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_invoice_in_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_invoices"
|
||||
action="act_invoice_in_form"
|
||||
sequence="10"
|
||||
id="menu_invoice_in_form"/>
|
||||
|
||||
<record model="ir.action.act_window" id="act_invoice_relate">
|
||||
<field name="name">Invoices</field>
|
||||
<field name="res_model">account.invoice</field>
|
||||
<field name="domain"
|
||||
eval="[
|
||||
If(Eval('active_model') == 'party.party',
|
||||
('party', 'in', Eval('active_ids', [])), ()),
|
||||
]"
|
||||
pyson="1"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_relate_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="invoice_view_tree"/>
|
||||
<field name="act_window" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_invoice_relate_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="invoice_view_form"/>
|
||||
<field name="act_window" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_relate_pending">
|
||||
<field name="name">Pending</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="domain" eval="[('state', 'not in', ['paid', 'cancelled'])]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_relate_paid">
|
||||
<field name="name">Paid</field>
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="domain" eval="[('state', '=', 'paid')]" pyson="1"/>
|
||||
<field name="count" eval="True"/>
|
||||
<field name="act_window" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.domain" id="act_invoice_relate_all">
|
||||
<field name="name">All</field>
|
||||
<field name="sequence" eval="9999"/>
|
||||
<field name="domain"></field>
|
||||
<field name="act_window" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="act_invoice_relate_keyword_party">
|
||||
<field name="keyword">form_relate</field>
|
||||
<field name="model">party.party,-1</field>
|
||||
<field name="action" ref="act_invoice_relate"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_invoice_account">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_cancel_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">cancel</field>
|
||||
<field name="string">Cancel</field>
|
||||
<field name="confirm">Are you sure you want to cancel the invoices?</field>
|
||||
<field name="help">Cancel the invoice</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_draft_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">draft</field>
|
||||
<field name="string">Draft</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_validate_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">validate_invoice</field>
|
||||
<field name="string">Validate</field>
|
||||
<field name="help">Also known as Pro Forma</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_post_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">post</field>
|
||||
<field name="string">Post</field>
|
||||
<field name="confirm">Are you sure you want to post the invoices?</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_pay_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">pay</field>
|
||||
<field name="string">Pay</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_reschedule_lines_to_pay_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">reschedule_lines_to_pay</field>
|
||||
<field name="string">Reschedule</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_delegate_lines_to_pay_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">delegate_lines_to_pay</field>
|
||||
<field name="string">Modify Payee</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="invoice_process_button">
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="name">process</field>
|
||||
<field name="string">Process</field>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="invoice_process_button_group_account_admin">
|
||||
<field name="button" ref="invoice_process_button"/>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="refresh_invoice_report_wizard">
|
||||
<field name="name">Invoice (revised)</field>
|
||||
<field name="wiz_name">account.invoice.refresh_invoice_report</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="refresh_invoice_report_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="refresh_invoice_report_wizard"/>
|
||||
</record>
|
||||
<record model="ir.action-res.group" id="refresh_invoice_report-group_account_admin">
|
||||
<field name="action" ref="refresh_invoice_report_wizard"/>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_report_revision_view_list">
|
||||
<field name="model">account.invoice.report.revision</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">invoice_report_revision_list</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="invoice_report_revision_view_form">
|
||||
<field name="model">account.invoice.report.revision</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">invoice_report_revision_form</field>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_invoice_report_revision">
|
||||
<field name="model">account.invoice.report.revision</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.report" id="report_invoice">
|
||||
<field name="name">Invoice</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="report_name">account.invoice</field>
|
||||
<field name="report">account_invoice/invoice.fodt</field>
|
||||
<field name="single" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="report_invoice_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="report_invoice"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="edocument">
|
||||
<field name="name">E-document</field>
|
||||
<field name="wiz_name">account.invoice.edocument</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="active" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="edocument_keyword">
|
||||
<field name="keyword">form_print</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="edocument"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="edocument_start_view_form">
|
||||
<field name="model">account.invoice.edocument.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">edocument_start_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="edocument_result_view_form">
|
||||
<field name="model">account.invoice.edocument.result</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">edocument_result_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.sequence.type" id="sequence_type_account_invoice">
|
||||
<field name="name">Invoice</field>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_account_invoice_group_admin">
|
||||
<field name="sequence_type" ref="sequence_type_account_invoice"/>
|
||||
<field name="group" ref="res.group_admin"/>
|
||||
</record>
|
||||
<record model="ir.sequence.type-res.group"
|
||||
id="sequence_type_account_invoice_group_account_admin">
|
||||
<field name="sequence_type" ref="sequence_type_account_invoice"/>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_line_view_form">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">invoice_line_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="invoice_line_view_tree">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">invoice_line_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_line_view_tree_sequence">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">invoice_line_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_invoice_line">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="perm_read" eval="False"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_invoice_line_account">
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_tax_view_form">
|
||||
<field name="model">account.invoice.tax</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">invoice_tax_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="invoice_tax_view_tree">
|
||||
<field name="model">account.invoice.tax</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">invoice_tax_tree</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="invoice_tax_view_tree_sequence">
|
||||
<field name="model">account.invoice.tax</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">invoice_tax_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="pay_start_view_form">
|
||||
<field name="model">account.invoice.pay.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">pay_start_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="pay_ask_view_form">
|
||||
<field name="model">account.invoice.pay.ask</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">pay_ask_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_method_view_form">
|
||||
<field name="model">account.invoice.payment.method</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">payment_method_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="payment_method_view_list">
|
||||
<field name="model">account.invoice.payment.method</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">payment_method_tree</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_payment_method_form">
|
||||
<field name="name">Invoice Payment Methods</field>
|
||||
<field name="res_model">account.invoice.payment.method</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_payment_method_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="payment_method_view_list"/>
|
||||
<field name="act_window" ref="act_payment_method_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_payment_method_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="payment_method_view_form"/>
|
||||
<field name="act_window" ref="act_payment_method_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="account.menu_journal_configuration"
|
||||
action="act_payment_method_form"
|
||||
sequence="50"
|
||||
id="menu_payment_method_form"/>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_payment_method_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.invoice.payment.method</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_payment_method_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_payment_method_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.access" id="access_payment_method">
|
||||
<field name="model">account.invoice.payment.method</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_payment_method_account_admin">
|
||||
<field name="model">account.invoice.payment.method</field>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="credit_start_view_form">
|
||||
<field name="model">account.invoice.credit.start</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">credit_start_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="credit">
|
||||
<field name="name">Credit</field>
|
||||
<field name="wiz_name">account.invoice.credit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword" id="credit_keyword">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">account.invoice,-1</field>
|
||||
<field name="action" ref="credit"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_invoice_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_invoice_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_invoice_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.rule.group" id="rule_group_invoice_line_companies">
|
||||
<field name="name">User in companies</field>
|
||||
<field name="model">account.invoice.line</field>
|
||||
<field name="global_p" eval="True"/>
|
||||
</record>
|
||||
<record model="ir.rule" id="rule_invoice_line_companies">
|
||||
<field name="domain"
|
||||
eval="[('company', 'in', Eval('companies', []))]"
|
||||
pyson="1"/>
|
||||
<field name="rule_group" ref="rule_group_invoice_line_companies"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="act_reschedule_lines_to_pay_wizard">
|
||||
<field name="name">Reschedule Lines to Pay</field>
|
||||
<field name="wiz_name">account.invoice.lines_to_pay.reschedule</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="act_delegate_lines_to_pay_wizard">
|
||||
<field name="name">Delegate Lines to Pay</field>
|
||||
<field name="wiz_name">account.invoice.lines_to_pay.delegate</field>
|
||||
<field name="model">account.invoice</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
1751
modules/account_invoice/locale/bg.po
Normal file
1751
modules/account_invoice/locale/bg.po
Normal file
File diff suppressed because it is too large
Load Diff
1660
modules/account_invoice/locale/ca.po
Normal file
1660
modules/account_invoice/locale/ca.po
Normal file
File diff suppressed because it is too large
Load Diff
1683
modules/account_invoice/locale/cs.po
Normal file
1683
modules/account_invoice/locale/cs.po
Normal file
File diff suppressed because it is too large
Load Diff
1677
modules/account_invoice/locale/de.po
Normal file
1677
modules/account_invoice/locale/de.po
Normal file
File diff suppressed because it is too large
Load Diff
1659
modules/account_invoice/locale/es.po
Normal file
1659
modules/account_invoice/locale/es.po
Normal file
File diff suppressed because it is too large
Load Diff
1675
modules/account_invoice/locale/es_419.po
Normal file
1675
modules/account_invoice/locale/es_419.po
Normal file
File diff suppressed because it is too large
Load Diff
1729
modules/account_invoice/locale/et.po
Normal file
1729
modules/account_invoice/locale/et.po
Normal file
File diff suppressed because it is too large
Load Diff
1737
modules/account_invoice/locale/fa.po
Normal file
1737
modules/account_invoice/locale/fa.po
Normal file
File diff suppressed because it is too large
Load Diff
1678
modules/account_invoice/locale/fi.po
Normal file
1678
modules/account_invoice/locale/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
1669
modules/account_invoice/locale/fr.po
Normal file
1669
modules/account_invoice/locale/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
1690
modules/account_invoice/locale/hu.po
Normal file
1690
modules/account_invoice/locale/hu.po
Normal file
File diff suppressed because it is too large
Load Diff
1689
modules/account_invoice/locale/id.po
Normal file
1689
modules/account_invoice/locale/id.po
Normal file
File diff suppressed because it is too large
Load Diff
1725
modules/account_invoice/locale/it.po
Normal file
1725
modules/account_invoice/locale/it.po
Normal file
File diff suppressed because it is too large
Load Diff
1764
modules/account_invoice/locale/lo.po
Normal file
1764
modules/account_invoice/locale/lo.po
Normal file
File diff suppressed because it is too large
Load Diff
1697
modules/account_invoice/locale/lt.po
Normal file
1697
modules/account_invoice/locale/lt.po
Normal file
File diff suppressed because it is too large
Load Diff
1663
modules/account_invoice/locale/nl.po
Normal file
1663
modules/account_invoice/locale/nl.po
Normal file
File diff suppressed because it is too large
Load Diff
1699
modules/account_invoice/locale/pl.po
Normal file
1699
modules/account_invoice/locale/pl.po
Normal file
File diff suppressed because it is too large
Load Diff
1679
modules/account_invoice/locale/pt.po
Normal file
1679
modules/account_invoice/locale/pt.po
Normal file
File diff suppressed because it is too large
Load Diff
1654
modules/account_invoice/locale/ro.po
Normal file
1654
modules/account_invoice/locale/ro.po
Normal file
File diff suppressed because it is too large
Load Diff
1760
modules/account_invoice/locale/ru.po
Normal file
1760
modules/account_invoice/locale/ru.po
Normal file
File diff suppressed because it is too large
Load Diff
1691
modules/account_invoice/locale/sl.po
Normal file
1691
modules/account_invoice/locale/sl.po
Normal file
File diff suppressed because it is too large
Load Diff
1676
modules/account_invoice/locale/tr.po
Normal file
1676
modules/account_invoice/locale/tr.po
Normal file
File diff suppressed because it is too large
Load Diff
1610
modules/account_invoice/locale/uk.po
Normal file
1610
modules/account_invoice/locale/uk.po
Normal file
File diff suppressed because it is too large
Load Diff
1661
modules/account_invoice/locale/zh_CN.po
Normal file
1661
modules/account_invoice/locale/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
107
modules/account_invoice/message.xml
Normal file
107
modules/account_invoice/message.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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_payment_term_missing_last_remainder">
|
||||
<field name="text">To save payment term "%(payment_term)s", you must append a last remainder line.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_payment_term_missing_remainder">
|
||||
<field name="text">To compute terms, you must append a remainder line on payment term "%(payment_term)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_payment_term_non_zero_ratio_divisor">
|
||||
<field name="text">The ratio and divisor can not be both equal to zero.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_payment_term_invalid_ratio_divisor">
|
||||
<field name="text">The ratio and divisor are not consistent on line "%(line)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_erase_party_pending_invoice">
|
||||
<field name="text">You cannot erase party "%(party)s" while they have pending invoices with company "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_tax_invalid">
|
||||
<field name="text">The taxes on invoice "%(invoice)s" are not valid, you must save it again to force them to be recalculated.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_no_sequence">
|
||||
<field name="text">To post invoice "%(invoice)s", you must define a sequence on fiscal year "%(fiscalyear)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_number_after">
|
||||
<field name="text">To number the invoice "%(invoice)s", you must set an invoice date after "%(date)s" because the sequence "%(sequence)s" has already been used for invoice "%(after_invoice)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_modify">
|
||||
<field name="text">You cannot modify invoice "%(invoice)s" because it is posted, paid or cancelled.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_delete_cancel">
|
||||
<field name="text">To delete invoice "%(invoice)s" you must cancel it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_delete_numbered">
|
||||
<field name="text">You cannot delete invoice "%(invoice)s" because it has a number.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_customer_cancel_move">
|
||||
<field name="text">You cannot cancel customer invoice "%(invoice)s" because it is posted and company setup does not allow it.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_payment_lines_greater_amount">
|
||||
<field name="text">Payment lines amount on invoice "%(invoice)s" can not be greater than the invoice amount.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_supplier_payment_reference_invalid">
|
||||
<field name="text">The %(type)s "%(reference)s" on invoice "%(invoice)s" is not valid.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_payment_lines_add_remove_paid">
|
||||
<field name="text">You cannot add/remove payment lines on paid invoice "%(invoice)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_credit_refund_not_posted">
|
||||
<field name="text">You cannot refund invoice "%(invoice)s" because it is not posted.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_line_modify">
|
||||
<field name="text">You cannot modify line "%(line)s" because its invoice "%(invoice)s" is posted, paid or cancelled.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_line_create_draft">
|
||||
<field name="text">You cannot add lines to invoice "%(invoice)s" because it is no longer in a draft state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_tax_modify">
|
||||
<field name="text">You cannot modify tax "%(tax)s" because its invoice "%(invoice)s" is posted, paid or cancelled.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_tax_create_draft">
|
||||
<field name="text">You cannot add taxes to invoice "%(invoice)s" because it is no longer in a draft state.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_line_tax_unique">
|
||||
<field name="text">A tax can be added only once to an invoice line.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_pay_amount_greater_amount_to_pay">
|
||||
<field name="text">You cannot add a partial payment on invoice "%(invoice)s" with an amount greater than the amount to pay "%(amount_to_pay)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_overpay_paid">
|
||||
<field name="text">You cannot overpay invoice "%(invoice)s" because there is no more left to pay.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_payment_line_unique">
|
||||
<field name="text">A payment line can be linked to only one invoice.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_payment_term_date_past">
|
||||
<field name="text">The invoice "%(invoice)s" generates a payment date "%(date)s" in the past.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_close_period_non_posted_invoices">
|
||||
<field name="text">To close the periods you must post the invoices "%(invoices)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_date_future">
|
||||
<field name="text">The invoices "%(invoices)s" have an invoice date in the future.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_default_taxes">
|
||||
<field name="text">The invoice "%(invoice)s" does not have the default taxes for %(lines)s.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_similar">
|
||||
<field name="text">The invoice "%(invoice)s" is similar to invoice "%(similar)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_cancel_invoice_move">
|
||||
<field name="text">The moves "%(moves)s" have the posted invoices "%(invoices)s" as origin.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_cancel_invoice_move_description">
|
||||
<field name="text">Cancelling them will reconcile the move lines thus paying the invoices. You might want to cancel the invoices first.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_currency_exchange_credit_account_missing">
|
||||
<field name="text">To post invoice "%(invoice)s", you must define a currency exchange credit account for "%(company)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_currency_exchange_debit_account_missing">
|
||||
<field name="text">To post invoice "%(invoice)s", you must define a currency exchange debit account for "%(company)s".</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
||||
97
modules/account_invoice/party.py
Normal file
97
modules/account_invoice/party.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# 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.i18n import gettext
|
||||
from trytond.model import ModelSQL, ValueMixin, fields
|
||||
from trytond.modules.party.exceptions import EraseError
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
|
||||
customer_payment_term = fields.Many2One(
|
||||
'account.invoice.payment_term', "Customer Payment Term",
|
||||
ondelete='RESTRICT')
|
||||
supplier_payment_term = fields.Many2One(
|
||||
'account.invoice.payment_term', "Supplier Payment Term",
|
||||
ondelete='RESTRICT')
|
||||
|
||||
|
||||
class Address(metaclass=PoolMeta):
|
||||
__name__ = 'party.address'
|
||||
invoice = fields.Boolean('Invoice')
|
||||
|
||||
|
||||
class ContactMechanism(metaclass=PoolMeta):
|
||||
__name__ = 'party.contact_mechanism'
|
||||
invoice = fields.Boolean('Invoice')
|
||||
|
||||
@classmethod
|
||||
def usages(cls, _fields=None):
|
||||
if _fields is None:
|
||||
_fields = []
|
||||
_fields.append('invoice')
|
||||
return super().usages(_fields=_fields)
|
||||
|
||||
|
||||
class Party(metaclass=PoolMeta):
|
||||
__name__ = 'party.party'
|
||||
customer_payment_term = fields.MultiValue(customer_payment_term)
|
||||
supplier_payment_term = fields.MultiValue(supplier_payment_term)
|
||||
payment_terms = fields.One2Many(
|
||||
'party.party.payment_term', 'party', "Payment Terms")
|
||||
|
||||
@classmethod
|
||||
def multivalue_model(cls, field):
|
||||
pool = Pool()
|
||||
if field in {'customer_payment_term', 'supplier_payment_term'}:
|
||||
return pool.get('party.party.payment_term')
|
||||
return super().multivalue_model(field)
|
||||
|
||||
@classmethod
|
||||
def default_customer_payment_term(cls, **pattern):
|
||||
pool = Pool()
|
||||
Configuration = pool.get('account.configuration')
|
||||
config = Configuration(1)
|
||||
payment_term = config.get_multivalue(
|
||||
'default_customer_payment_term', **pattern)
|
||||
return payment_term.id if payment_term else None
|
||||
|
||||
|
||||
class PartyPaymentTerm(ModelSQL, ValueMixin):
|
||||
__name__ = 'party.party.payment_term'
|
||||
party = fields.Many2One(
|
||||
'party.party', "Party", ondelete='CASCADE')
|
||||
customer_payment_term = customer_payment_term
|
||||
supplier_payment_term = supplier_payment_term
|
||||
|
||||
|
||||
class Replace(metaclass=PoolMeta):
|
||||
__name__ = 'party.replace'
|
||||
|
||||
@classmethod
|
||||
def fields_to_replace(cls):
|
||||
return super().fields_to_replace() + [
|
||||
('account.invoice', 'party'),
|
||||
('account.invoice.line', 'party'),
|
||||
('account.invoice.alternative_payee', 'party'),
|
||||
]
|
||||
|
||||
|
||||
class Erase(metaclass=PoolMeta):
|
||||
__name__ = 'party.erase'
|
||||
|
||||
def check_erase_company(self, party, company):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
super().check_erase_company(party, company)
|
||||
|
||||
invoices = Invoice.search([
|
||||
['OR',
|
||||
('party', '=', party.id),
|
||||
('alternative_payees', '=', party.id),
|
||||
],
|
||||
('company', '=', company.id),
|
||||
('state', 'not in', ['paid', 'cancelled']),
|
||||
])
|
||||
if invoices:
|
||||
raise EraseError(
|
||||
gettext('account_invoice.msg_erase_party_pending_invoice',
|
||||
party=party.rec_name,
|
||||
company=company.rec_name))
|
||||
51
modules/account_invoice/party.xml
Normal file
51
modules/account_invoice/party.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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>
|
||||
<record model="ir.ui.view" id="address_view_tree">
|
||||
<field name="model">party.address</field>
|
||||
<field name="inherit" ref="party.address_view_tree"/>
|
||||
<field name="name">address_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="address_view_form">
|
||||
<field name="model">party.address</field>
|
||||
<field name="inherit" ref="party.address_view_form"/>
|
||||
<field name="name">address_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="address_view_form_simple">
|
||||
<field name="model">party.address</field>
|
||||
<field name="inherit" ref="party.address_view_form_simple"/>
|
||||
<field name="name">address_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="address_view_tree_sequence">
|
||||
<field name="model">party.address</field>
|
||||
<field name="inherit" ref="party.address_view_tree_sequence"/>
|
||||
<field name="name">address_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="contact_mechanism_view_tree">
|
||||
<field name="model">party.contact_mechanism</field>
|
||||
<field name="inherit" ref="party.contact_mechanism_view_tree"/>
|
||||
<field name="name">contact_mechanism_tree</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="contact_mechanism_view_form">
|
||||
<field name="model">party.contact_mechanism</field>
|
||||
<field name="inherit" ref="party.contact_mechanism_view_form"/>
|
||||
<field name="name">contact_mechanism_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="contact_mechanism_view_tree_sequence">
|
||||
<field name="model">party.contact_mechanism</field>
|
||||
<field name="inherit" ref="party.contact_mechanism_view_tree_sequence"/>
|
||||
<field name="name">contact_mechanism_tree_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="party_view_form">
|
||||
<field name="model">party.party</field>
|
||||
<field name="inherit" ref="party.party_view_form"/>
|
||||
<field name="name">party_form</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
324
modules/account_invoice/payment_term.py
Normal file
324
modules/account_invoice/payment_term.py
Normal file
@@ -0,0 +1,324 @@
|
||||
# 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 decimal import Decimal
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import (
|
||||
Check, DeactivableMixin, ModelSQL, ModelView, fields, sequence_ordered)
|
||||
from trytond.modules.currency.fields import Monetary
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.wizard import Button, StateView, Wizard
|
||||
|
||||
from .exceptions import PaymentTermComputeError, PaymentTermValidationError
|
||||
|
||||
|
||||
class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
|
||||
__name__ = 'account.invoice.payment_term'
|
||||
name = fields.Char('Name', size=None, required=True, translate=True)
|
||||
description = fields.Text('Description', translate=True)
|
||||
lines = fields.One2Many('account.invoice.payment_term.line', 'payment',
|
||||
'Lines')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls._order.insert(0, ('name', 'ASC'))
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, terms, field_names):
|
||||
super().validate_fields(terms, field_names)
|
||||
cls.check_remainder(terms, field_names)
|
||||
|
||||
@classmethod
|
||||
def check_remainder(cls, terms, field_names=None):
|
||||
if Transaction().user == 0:
|
||||
return
|
||||
if field_names and 'lines' not in field_names:
|
||||
return
|
||||
for term in terms:
|
||||
if not term.lines or not term.lines[-1].type == 'remainder':
|
||||
raise PaymentTermValidationError(gettext(
|
||||
'account_invoice'
|
||||
'.msg_payment_term_missing_last_remainder',
|
||||
payment_term=term.rec_name))
|
||||
|
||||
def compute(self, amount, currency, date):
|
||||
"""Calculate payment terms and return a list of tuples
|
||||
with (date, amount) for each payment term line.
|
||||
|
||||
amount must be a Decimal used for the calculation.
|
||||
"""
|
||||
# TODO implement business_days
|
||||
# http://pypi.python.org/pypi/BusinessHours/
|
||||
sign = 1 if amount >= Decimal(0) else -1
|
||||
res = []
|
||||
remainder = amount
|
||||
for line in self.lines:
|
||||
value = line.get_value(remainder, amount, currency)
|
||||
value_date = line.get_date(date)
|
||||
if value is None or not value_date:
|
||||
continue
|
||||
if ((remainder - value) * sign) < Decimal(0):
|
||||
res.append((value_date, remainder))
|
||||
break
|
||||
if value:
|
||||
res.append((value_date, value))
|
||||
remainder -= value
|
||||
else:
|
||||
# Enforce to have at least one term
|
||||
if not res:
|
||||
res.append((date, Decimal(0)))
|
||||
|
||||
if not currency.is_zero(remainder):
|
||||
raise PaymentTermComputeError(
|
||||
gettext('account_invoice.msg_payment_term_missing_remainder',
|
||||
payment_term=self.rec_name))
|
||||
return res
|
||||
|
||||
|
||||
class PaymentTermLine(sequence_ordered(), ModelSQL, ModelView):
|
||||
__name__ = 'account.invoice.payment_term.line'
|
||||
payment = fields.Many2One('account.invoice.payment_term', 'Payment Term',
|
||||
required=True, ondelete="CASCADE")
|
||||
type = fields.Selection([
|
||||
('fixed', 'Fixed'),
|
||||
('percent', 'Percentage on Remainder'),
|
||||
('percent_on_total', 'Percentage on Total'),
|
||||
('remainder', 'Remainder'),
|
||||
], 'Type', required=True)
|
||||
ratio = fields.Numeric('Ratio', digits=(14, 10),
|
||||
domain=[
|
||||
If(Eval('type').in_(['percent', 'percent_on_total'])
|
||||
& ~Eval('divisor', 0),
|
||||
('ratio', '!=', 0),
|
||||
()),
|
||||
],
|
||||
states={
|
||||
'invisible': ~Eval('type').in_(['percent', 'percent_on_total']),
|
||||
'required': Eval('type').in_(['percent', 'percent_on_total']),
|
||||
})
|
||||
divisor = fields.Numeric('Divisor', digits=(10, 14),
|
||||
states={
|
||||
'invisible': ~Eval('type').in_(['percent', 'percent_on_total']),
|
||||
'required': Eval('type').in_(['percent', 'percent_on_total']),
|
||||
})
|
||||
amount = Monetary(
|
||||
"Amount", currency='currency', digits='currency',
|
||||
states={
|
||||
'invisible': Eval('type') != 'fixed',
|
||||
'required': Eval('type') == 'fixed',
|
||||
})
|
||||
currency = fields.Many2One('currency.currency', 'Currency',
|
||||
states={
|
||||
'invisible': Eval('type') != 'fixed',
|
||||
'required': Eval('type') == 'fixed',
|
||||
})
|
||||
relativedeltas = fields.One2Many(
|
||||
'account.invoice.payment_term.line.delta', 'line', 'Deltas')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
t = cls.__table__()
|
||||
cls._sql_constraints += [
|
||||
('non_zero_ratio_divisor',
|
||||
Check(t,
|
||||
(t.ratio != 0) | (t.divisor != 0)
|
||||
| ~t.type.in_(['percent', 'percent_on_total'])),
|
||||
'account_invoice.msg_payment_term_non_zero_ratio_divisor'),
|
||||
]
|
||||
cls.__access__.add('payment')
|
||||
|
||||
@staticmethod
|
||||
def default_type():
|
||||
return 'remainder'
|
||||
|
||||
@classmethod
|
||||
def default_relativedeltas(cls):
|
||||
if Transaction().user == 0:
|
||||
return []
|
||||
return [{}]
|
||||
|
||||
@fields.depends('type')
|
||||
def on_change_type(self):
|
||||
if self.type != 'fixed':
|
||||
self.amount = Decimal(0)
|
||||
self.currency = None
|
||||
if self.type not in ('percent', 'percent_on_total'):
|
||||
self.ratio = None
|
||||
self.divisor = None
|
||||
|
||||
@fields.depends('ratio')
|
||||
def on_change_ratio(self):
|
||||
if not self.ratio:
|
||||
self.divisor = self.ratio
|
||||
else:
|
||||
self.divisor = self.round(1 / self.ratio,
|
||||
self.__class__.divisor.digits[1])
|
||||
|
||||
@fields.depends('divisor')
|
||||
def on_change_divisor(self):
|
||||
if not self.divisor:
|
||||
self.ratio = self.divisor
|
||||
else:
|
||||
self.ratio = self.round(1 / self.divisor,
|
||||
self.__class__.ratio.digits[1])
|
||||
|
||||
def get_date(self, date):
|
||||
for relativedelta_ in self.relativedeltas:
|
||||
date += relativedelta_.get()
|
||||
return date
|
||||
|
||||
def get_value(self, remainder, amount, currency):
|
||||
Currency = Pool().get('currency.currency')
|
||||
if self.type == 'fixed':
|
||||
fixed = Currency.compute(self.currency, self.amount, currency)
|
||||
return fixed.copy_sign(amount)
|
||||
elif self.type == 'percent':
|
||||
return currency.round(remainder * self.ratio)
|
||||
elif self.type == 'percent_on_total':
|
||||
return currency.round(amount * self.ratio)
|
||||
elif self.type == 'remainder':
|
||||
return currency.round(remainder)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def round(number, digits):
|
||||
quantize = Decimal(10) ** -Decimal(digits)
|
||||
return Decimal(number).quantize(quantize)
|
||||
|
||||
@classmethod
|
||||
def validate_fields(cls, lines, field_names):
|
||||
super().validate_fields(lines, field_names)
|
||||
cls.check_ratio_and_divisor(lines, field_names)
|
||||
|
||||
@classmethod
|
||||
def check_ratio_and_divisor(cls, lines, field_names=None):
|
||||
"Check consistency between ratio and divisor"
|
||||
if field_names and not (field_names & {'type', 'ratio', 'divisor'}):
|
||||
return
|
||||
for line in lines:
|
||||
if line.type not in ('percent', 'percent_on_total'):
|
||||
continue
|
||||
if line.ratio is None or line.divisor is None:
|
||||
raise PaymentTermValidationError(
|
||||
gettext('account_invoice'
|
||||
'.msg_payment_term_invalid_ratio_divisor',
|
||||
line=line.rec_name))
|
||||
if (line.ratio != round(
|
||||
1 / line.divisor, cls.ratio.digits[1])
|
||||
and line.divisor != round(
|
||||
1 / line.ratio, cls.divisor.digits[1])):
|
||||
raise PaymentTermValidationError(
|
||||
gettext('account_invoice'
|
||||
'.msg_payment_term_invalid_ratio_divisor',
|
||||
line=line.rec_name))
|
||||
|
||||
|
||||
class PaymentTermLineRelativeDelta(sequence_ordered(), ModelSQL, ModelView):
|
||||
__name__ = 'account.invoice.payment_term.line.delta'
|
||||
line = fields.Many2One('account.invoice.payment_term.line',
|
||||
'Payment Term Line', required=True, ondelete='CASCADE')
|
||||
day = fields.Integer('Day of Month',
|
||||
domain=['OR',
|
||||
('day', '=', None),
|
||||
[('day', '>=', 1), ('day', '<=', 31)],
|
||||
])
|
||||
month = fields.Many2One('ir.calendar.month', "Month")
|
||||
weekday = fields.Many2One('ir.calendar.day', "Day of Week")
|
||||
months = fields.Integer('Number of Months', required=True)
|
||||
weeks = fields.Integer('Number of Weeks', required=True)
|
||||
days = fields.Integer('Number of Days', required=True)
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super().__setup__()
|
||||
cls.__access__.add('line')
|
||||
|
||||
@staticmethod
|
||||
def default_months():
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def default_weeks():
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def default_days():
|
||||
return 0
|
||||
|
||||
def get(self):
|
||||
"Return the relativedelta"
|
||||
return relativedelta(
|
||||
day=self.day,
|
||||
month=int(self.month.index) if self.month else None,
|
||||
days=self.days,
|
||||
weeks=self.weeks,
|
||||
months=self.months,
|
||||
weekday=int(self.weekday.index) if self.weekday else None,
|
||||
)
|
||||
|
||||
|
||||
class TestPaymentTerm(Wizard):
|
||||
__name__ = 'account.invoice.payment_term.test'
|
||||
start_state = 'test'
|
||||
test = StateView('account.invoice.payment_term.test',
|
||||
'account_invoice.payment_term_test_view_form',
|
||||
[Button('Close', 'end', 'tryton-close', default=True)])
|
||||
|
||||
def default_test(self, fields):
|
||||
default = {}
|
||||
if (self.model
|
||||
and self.model.__name__ == 'account.invoice.payment_term'):
|
||||
default['payment_term'] = self.record.id if self.record else None
|
||||
return default
|
||||
|
||||
|
||||
class TestPaymentTermView(ModelView):
|
||||
__name__ = 'account.invoice.payment_term.test'
|
||||
payment_term = fields.Many2One('account.invoice.payment_term',
|
||||
'Payment Term', required=True)
|
||||
date = fields.Date("Date", required=True)
|
||||
amount = Monetary(
|
||||
"Amount", currency='currency', digits='currency', required=True)
|
||||
currency = fields.Many2One('currency.currency', 'Currency', required=True)
|
||||
result = fields.One2Many('account.invoice.payment_term.test.result',
|
||||
None, 'Result', readonly=True)
|
||||
|
||||
@classmethod
|
||||
def default_date(cls):
|
||||
return Pool().get('ir.date').today()
|
||||
|
||||
@staticmethod
|
||||
def default_currency():
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
company = Transaction().context.get('company')
|
||||
if company is not None and company >= 0:
|
||||
return Company(company).currency.id
|
||||
|
||||
@fields.depends('payment_term', 'date', 'amount', 'currency', 'result')
|
||||
def on_change_with_result(self):
|
||||
pool = Pool()
|
||||
Result = pool.get('account.invoice.payment_term.test.result')
|
||||
result = []
|
||||
if (self.payment_term and self.amount and self.currency and self.date):
|
||||
for date, amount in self.payment_term.compute(
|
||||
self.amount, self.currency, self.date):
|
||||
result.append(Result(
|
||||
date=date,
|
||||
amount=amount,
|
||||
currency=self.currency))
|
||||
return result
|
||||
|
||||
|
||||
class TestPaymentTermViewResult(ModelView):
|
||||
__name__ = 'account.invoice.payment_term.test.result'
|
||||
date = fields.Date('Date', readonly=True)
|
||||
amount = Monetary(
|
||||
"Amount", currency='currency', digits='currency', readonly=True)
|
||||
currency = fields.Many2One('currency.currency', "Currency")
|
||||
251
modules/account_invoice/payment_term.xml
Normal file
251
modules/account_invoice/payment_term.xml
Normal file
@@ -0,0 +1,251 @@
|
||||
<?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>
|
||||
<menuitem
|
||||
name="Payment Terms"
|
||||
parent="account.menu_account_configuration"
|
||||
sequence="50"
|
||||
id="menu_payment_terms_configuration"/>
|
||||
<record model="ir.ui.view" id="payment_term_view_form">
|
||||
<field name="model">account.invoice.payment_term</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">payment_term_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="payment_term_view_tree">
|
||||
<field name="model">account.invoice.payment_term</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">payment_term_tree</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window" id="act_payment_term_form">
|
||||
<field name="name">Payment Terms</field>
|
||||
<field name="res_model">account.invoice.payment_term</field>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_payment_term_form_view1">
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="view" ref="payment_term_view_tree"/>
|
||||
<field name="act_window" ref="act_payment_term_form"/>
|
||||
</record>
|
||||
<record model="ir.action.act_window.view" id="act_payment_term_form_view2">
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view" ref="payment_term_view_form"/>
|
||||
<field name="act_window" ref="act_payment_term_form"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_payment_terms_configuration"
|
||||
action="act_payment_term_form"
|
||||
sequence="10"
|
||||
id="menu_payment_term_form"/>
|
||||
|
||||
<record model="ir.model.access" id="access_payment_term">
|
||||
<field name="model">account.invoice.payment_term</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_delete" eval="False"/>
|
||||
</record>
|
||||
<record model="ir.model.access" id="access_payment_term_account_admin">
|
||||
<field name="model">account.invoice.payment_term</field>
|
||||
<field name="group" ref="account.group_account_admin"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="True"/>
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_delete" eval="True"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_term_line_view_list">
|
||||
<field name="model">account.invoice.payment_term.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">payment_term_line_list</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_term_line_view_list_sequence">
|
||||
<field name="model">account.invoice.payment_term.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">payment_term_line_list_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_term_line_view_form">
|
||||
<field name="model">account.invoice.payment_term.line</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">payment_term_line_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view"
|
||||
id="payment_term_line_relativedelta_view_list">
|
||||
<field name="model">account.invoice.payment_term.line.delta</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="10"/>
|
||||
<field name="name">payment_term_line_relativedelta_list</field>
|
||||
</record>
|
||||
<record model="ir.ui.view"
|
||||
id="payment_term_line_relativedelta_view_list_sequence">
|
||||
<field name="model">account.invoice.payment_term.line.delta</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="name">payment_term_line_relativedelta_list_sequence</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="payment_term_line_relativedelta_view_form">
|
||||
<field name="model">account.invoice.payment_term.line.delta</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">payment_term_line_relativedelta_form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.action.wizard" id="wizard_payment_term_test">
|
||||
<field name="name">Test Payment Term</field>
|
||||
<field name="wiz_name">account.invoice.payment_term.test</field>
|
||||
</record>
|
||||
<record model="ir.action.keyword"
|
||||
id="wizard_payment_term_test_keyword1">
|
||||
<field name="keyword">form_action</field>
|
||||
<field name="model">account.invoice.payment_term,-1</field>
|
||||
<field name="action" ref="wizard_payment_term_test"/>
|
||||
</record>
|
||||
<menuitem
|
||||
parent="menu_payment_terms_configuration"
|
||||
action="wizard_payment_term_test"
|
||||
sequence="90"
|
||||
id="menu_payment_term_test"/>
|
||||
|
||||
<record model="ir.ui.view" id="payment_term_test_view_form">
|
||||
<field name="model">account.invoice.payment_term.test</field>
|
||||
<field name="type">form</field>
|
||||
<field name="name">payment_term_test_form</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="payment_term_test_result_view_list">
|
||||
<field name="model">account.invoice.payment_term.test.result</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="name">payment_term_test_result_list</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
<record model="account.invoice.payment_term" id="payment_term_0">
|
||||
<field name="name">Upon Receipt</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_0_line1">
|
||||
<field name="payment" ref="payment_term_0"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_net10">
|
||||
<field name="name">Net 10 days</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_net10_line1">
|
||||
<field name="payment" ref="payment_term_net10"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_net10_line1_delta1">
|
||||
<field name="line" ref="payment_term_net10_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="days" eval="10"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_net15">
|
||||
<field name="name">Net 15 days</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_net15_line1">
|
||||
<field name="payment" ref="payment_term_net15"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_net15_line1_delta1">
|
||||
<field name="line" ref="payment_term_net15_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="days" eval="15"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_net30">
|
||||
<field name="name">Net 30 days</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_net30_line1">
|
||||
<field name="payment" ref="payment_term_net30"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_net30_line1_delta1">
|
||||
<field name="line" ref="payment_term_net30_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="days" eval="30"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_net60">
|
||||
<field name="name">Net 60 days</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_net60_line1">
|
||||
<field name="payment" ref="payment_term_net60"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_net60_line1_delta1">
|
||||
<field name="line" ref="payment_term_net60_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="days" eval="60"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_eom">
|
||||
<field name="name">End of Month</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_eom_line1">
|
||||
<field name="payment" ref="payment_term_eom"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_eom_line1_delta1">
|
||||
<field name="line" ref="payment_term_eom_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="day" eval="31"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_eom_following">
|
||||
<field name="name">End of Month Following</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_eom_following_line1">
|
||||
<field name="payment" ref="payment_term_eom_following"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_eom_following_line1_delta1">
|
||||
<field name="line" ref="payment_term_eom_following_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="months" eval="1"/>
|
||||
<field name="day" eval="31"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_30_eom">
|
||||
<field name="name">Net 30 days End of Month</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_30_eom_line1">
|
||||
<field name="payment" ref="payment_term_30_eom"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_30_eom_line1_delta1">
|
||||
<field name="line" ref="payment_term_30_eom_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="day" eval="31"/>
|
||||
<field name="days" eval="30"/>
|
||||
</record>
|
||||
|
||||
<record model="account.invoice.payment_term" id="payment_term_60_eom">
|
||||
<field name="name">Net 60 days End of Month</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line" id="payment_term_60_eom_line1">
|
||||
<field name="payment" ref="payment_term_60_eom"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="type">remainder</field>
|
||||
</record>
|
||||
<record model="account.invoice.payment_term.line.delta" id="payment_term_60_eom_line1_delta1">
|
||||
<field name="line" ref="payment_term_60_eom_line1"/>
|
||||
<field name="sequence" eval="10"/>
|
||||
<field name="day" eval="31"/>
|
||||
<field name="days" eval="60"/>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
6
modules/account_invoice/tests/__init__.py
Normal file
6
modules/account_invoice/tests/__init__.py
Normal file
@@ -0,0 +1,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.
|
||||
|
||||
from .test_module import set_invoice_sequences
|
||||
|
||||
__all__ = ['set_invoice_sequences']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
modules/account_invoice/tests/__pycache__/tools.cpython-311.pyc
Normal file
BIN
modules/account_invoice/tests/__pycache__/tools.cpython-311.pyc
Normal file
Binary file not shown.
@@ -0,0 +1,100 @@
|
||||
================================
|
||||
Cancelling Invoice Move Scenario
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.exceptions import CancelInvoiceMoveWarning
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Warning = Model.get('res.user.warning')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.account_revenue = accounts['revenue']
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('40')
|
||||
|
||||
Post invoice and cancel the created move::
|
||||
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> cancel_move = Wizard('account.move.cancel', [invoice.move])
|
||||
>>> cancel_move.form.description = 'Cancel'
|
||||
>>> cancel_move.execute('cancel')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
CancelInvoiceMoveWarning: ...
|
||||
|
||||
Bypass the warning and cancel the move::
|
||||
|
||||
>>> try:
|
||||
... cancel_move.execute('cancel')
|
||||
... except CancelInvoiceMoveWarning as warning:
|
||||
... Warning(user=config.user, name=warning.name).save()
|
||||
>>> cancel_move.execute('cancel')
|
||||
>>> cancel_move.state
|
||||
'end'
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> [bool(l.reconciliation) for l in invoice.move.lines
|
||||
... if l.account == accounts['receivable']]
|
||||
[True]
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
135
modules/account_invoice/tests/scenario_credit_note.rst
Normal file
135
modules/account_invoice/tests/scenario_credit_note.rst
Normal file
@@ -0,0 +1,135 @@
|
||||
====================
|
||||
Credit Note Scenario
|
||||
====================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> receivable = accounts['receivable']
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> account_cash = accounts['cash']
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> TaxCode = Model.get('account.tax.code')
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> Sequence = Model.get('ir.sequence')
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = account_cash
|
||||
>>> payment_method.debit_account = account_cash
|
||||
>>> payment_method.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.customer_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create credit note::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = -5
|
||||
>>> line.unit_price = Decimal('40')
|
||||
>>> invoice.total_amount
|
||||
Decimal('-220.00')
|
||||
>>> invoice.save()
|
||||
|
||||
Post credit note::
|
||||
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
|
||||
Pay credit note::
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('-220.00')
|
||||
>>> pay.form.amount = Decimal('-120.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.form.type = 'partial'
|
||||
>>> pay.form.amount
|
||||
Decimal('-120.00')
|
||||
>>> len(pay.form.lines_to_pay)
|
||||
1
|
||||
>>> len(pay.form.payment_lines)
|
||||
0
|
||||
>>> len(pay.form.lines)
|
||||
1
|
||||
>>> pay.form.amount_writeoff
|
||||
Decimal('-100.00')
|
||||
>>> pay.execute('pay')
|
||||
>>> pay.state
|
||||
'end'
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('-100.00')
|
||||
>>> pay.form.amount = Decimal('-100.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> sorted(l.debit for l in invoice.reconciliation_lines)
|
||||
[Decimal('100.00'), Decimal('120.00')]
|
||||
435
modules/account_invoice/tests/scenario_invoice.rst
Normal file
435
modules/account_invoice/tests/scenario_invoice.rst
Normal file
@@ -0,0 +1,435 @@
|
||||
================
|
||||
Invoice Scenario
|
||||
================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from stdnum import iso11649
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, create_tax_code, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual, assertTrue
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Setup company::
|
||||
|
||||
>>> company = get_company()
|
||||
>>> tax_identifier = company.party.identifiers.new()
|
||||
>>> tax_identifier.type = 'eu_vat'
|
||||
>>> tax_identifier.code = 'BE0897290877'
|
||||
>>> company.party.save()
|
||||
|
||||
Set employee::
|
||||
|
||||
>>> User = Model.get('res.user')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Employee = Model.get('company.employee')
|
||||
>>> employee_party = Party(name="Employee")
|
||||
>>> employee_party.save()
|
||||
>>> employee = Employee(party=employee_party)
|
||||
>>> employee.save()
|
||||
>>> user = User(config.user)
|
||||
>>> user.employees.append(employee)
|
||||
>>> user.employee = employee
|
||||
>>> user.save()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
>>> period_ids = [p.id for p in fiscalyear.periods]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> receivable = accounts['receivable']
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> account_tax = accounts['tax']
|
||||
>>> account_cash = accounts['cash']
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> TaxCode = Model.get('account.tax.code')
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> invoice_base_code = create_tax_code(tax, 'base', 'invoice')
|
||||
>>> invoice_base_code.save()
|
||||
>>> invoice_tax_code = create_tax_code(tax, 'tax', 'invoice')
|
||||
>>> invoice_tax_code.save()
|
||||
>>> credit_note_base_code = create_tax_code(tax, 'base', 'credit')
|
||||
>>> credit_note_base_code.save()
|
||||
>>> credit_note_tax_code = create_tax_code(tax, 'tax', 'credit')
|
||||
>>> credit_note_tax_code.save()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> Sequence = Model.get('ir.sequence')
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = account_cash
|
||||
>>> payment_method.debit_account = account_cash
|
||||
>>> payment_method.save()
|
||||
|
||||
Create Write Off method::
|
||||
|
||||
>>> WriteOff = Model.get('account.move.reconcile.write_off')
|
||||
>>> journal_writeoff, = Journal.find(
|
||||
... [('code', '=', 'EXC'), ('type', '=', 'write-off')], limit=1)
|
||||
>>> writeoff_method = WriteOff()
|
||||
>>> writeoff_method.name = 'Rate loss'
|
||||
>>> writeoff_method.journal = journal_writeoff
|
||||
>>> writeoff_method.credit_account = expense
|
||||
>>> writeoff_method.debit_account = expense
|
||||
>>> writeoff_method.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.customer_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create payment term::
|
||||
|
||||
>>> PaymentTerm = Model.get('account.invoice.payment_term')
|
||||
>>> payment_term = PaymentTerm(name='Term')
|
||||
>>> line = payment_term.lines.new(type='percent', ratio=Decimal('.5'))
|
||||
>>> delta, = line.relativedeltas
|
||||
>>> delta.days = 20
|
||||
>>> line = payment_term.lines.new(type='remainder')
|
||||
>>> delta = line.relativedeltas.new(days=40)
|
||||
>>> payment_term.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> InvoiceLine = Model.get('account.invoice.line')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('40')
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.account = revenue
|
||||
>>> line.description = 'Test'
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(20)
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('220.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('20.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('240.00')
|
||||
>>> invoice.save()
|
||||
>>> bool(invoice.has_report_cache)
|
||||
False
|
||||
|
||||
Test change tax::
|
||||
|
||||
>>> tax_line, = invoice.taxes
|
||||
>>> assertEqual(tax_line.tax, tax)
|
||||
>>> tax_line.tax = None
|
||||
>>> tax_line.tax = tax
|
||||
|
||||
Validate invoice::
|
||||
|
||||
>>> invoice.click('validate_invoice')
|
||||
>>> assertEqual(invoice.validated_by, employee)
|
||||
|
||||
Post invoice::
|
||||
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> assertEqual(invoice.posted_by, employee)
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.tax_identifier.code
|
||||
'BE0897290877'
|
||||
>>> assertTrue(iso11649.validate(invoice.customer_payment_reference))
|
||||
>>> bool(invoice.has_report_cache)
|
||||
True
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('220.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('20.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('240.00')
|
||||
>>> receivable.reload()
|
||||
>>> receivable.debit
|
||||
Decimal('240.00')
|
||||
>>> receivable.credit
|
||||
Decimal('0.00')
|
||||
>>> revenue.reload()
|
||||
>>> revenue.debit
|
||||
Decimal('0.00')
|
||||
>>> revenue.credit
|
||||
Decimal('220.00')
|
||||
>>> account_tax.reload()
|
||||
>>> account_tax.debit
|
||||
Decimal('0.00')
|
||||
>>> account_tax.credit
|
||||
Decimal('20.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_base_code = TaxCode(invoice_base_code.id)
|
||||
... invoice_base_code.amount
|
||||
Decimal('200.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_tax_code = TaxCode(invoice_tax_code.id)
|
||||
... invoice_tax_code.amount
|
||||
Decimal('20.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_base_code = TaxCode(credit_note_base_code.id)
|
||||
... credit_note_base_code.amount
|
||||
Decimal('0.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_tax_code = TaxCode(credit_note_tax_code.id)
|
||||
... credit_note_tax_code.amount
|
||||
Decimal('0.00')
|
||||
|
||||
Credit invoice with refund::
|
||||
|
||||
>>> credit = Wizard('account.invoice.credit', [invoice])
|
||||
>>> credit.form.with_refund = True
|
||||
>>> credit.form.invoice_date = invoice.invoice_date
|
||||
>>> credit.execute('credit')
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'cancelled'
|
||||
>>> bool(invoice.cancel_move)
|
||||
True
|
||||
>>> bool(invoice.reconciled)
|
||||
True
|
||||
>>> credit_note, = Invoice.find([
|
||||
... ('type', '=', 'out'), ('id', '!=', invoice.id)])
|
||||
>>> credit_note.state
|
||||
'paid'
|
||||
>>> for line in credit_note.lines:
|
||||
... assertEqual(line.taxes_date, today)
|
||||
>>> receivable.reload()
|
||||
>>> receivable.debit
|
||||
Decimal('240.00')
|
||||
>>> receivable.credit
|
||||
Decimal('240.00')
|
||||
>>> revenue.reload()
|
||||
>>> revenue.debit
|
||||
Decimal('220.00')
|
||||
>>> revenue.credit
|
||||
Decimal('220.00')
|
||||
>>> account_tax.reload()
|
||||
>>> account_tax.debit
|
||||
Decimal('20.00')
|
||||
>>> account_tax.credit
|
||||
Decimal('20.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_base_code = TaxCode(invoice_base_code.id)
|
||||
... invoice_base_code.amount
|
||||
Decimal('200.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_tax_code = TaxCode(invoice_tax_code.id)
|
||||
... invoice_tax_code.amount
|
||||
Decimal('20.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_base_code = TaxCode(credit_note_base_code.id)
|
||||
... credit_note_base_code.amount
|
||||
Decimal('200.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_tax_code = TaxCode(credit_note_tax_code.id)
|
||||
... credit_note_tax_code.amount
|
||||
Decimal('20.00')
|
||||
|
||||
Unreconcile cancelled invoice::
|
||||
|
||||
>>> unreconcile_lines = Wizard(
|
||||
... 'account.move.unreconcile_lines', invoice.move.lines)
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.cancel_move
|
||||
|
||||
Pay invoice::
|
||||
|
||||
>>> invoice, = invoice.duplicate()
|
||||
>>> invoice.click('post')
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('240.00')
|
||||
>>> pay.form.amount = Decimal('120.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('120.00')
|
||||
>>> pay.form.amount = Decimal('20.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.form.type = 'partial'
|
||||
>>> pay.form.amount
|
||||
Decimal('20.00')
|
||||
>>> len(pay.form.lines_to_pay)
|
||||
1
|
||||
>>> len(pay.form.payment_lines)
|
||||
0
|
||||
>>> len(pay.form.lines)
|
||||
1
|
||||
>>> pay.form.amount_writeoff
|
||||
Decimal('100.00')
|
||||
>>> pay.execute('pay')
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('-20.00')
|
||||
>>> pay.form.amount = Decimal('99.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.form.type = 'writeoff'
|
||||
>>> pay.form.writeoff = writeoff_method
|
||||
>>> pay.form.amount
|
||||
Decimal('99.00')
|
||||
>>> len(pay.form.lines_to_pay)
|
||||
1
|
||||
>>> len(pay.form.payment_lines)
|
||||
1
|
||||
>>> len(pay.form.lines)
|
||||
1
|
||||
>>> pay.form.amount_writeoff
|
||||
Decimal('1.00')
|
||||
>>> pay.execute('pay')
|
||||
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> sorted(l.credit for l in invoice.reconciliation_lines)
|
||||
[Decimal('1.00'), Decimal('20.00'), Decimal('99.00'), Decimal('120.00')]
|
||||
|
||||
Create empty invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
Create some complex invoice and test its taxes base rounding::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('0.0035')
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('0.0035')
|
||||
>>> invoice.save()
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('0.00')
|
||||
>>> assertEqual(invoice.taxes[0].base, invoice.untaxed_amount)
|
||||
>>> empty_invoice, found_invoice = Invoice.find(
|
||||
... [('untaxed_amount', '=', Decimal(0))], order=[('id', 'ASC')])
|
||||
>>> assertEqual(found_invoice, invoice)
|
||||
>>> empty_invoice, found_invoice = Invoice.find(
|
||||
... [('total_amount', '=', Decimal(0))], order=[('id', 'ASC')])
|
||||
>>> assertEqual(found_invoice, invoice)
|
||||
|
||||
Clear company tax_identifier::
|
||||
|
||||
>>> tax_identifier, = company.party.identifiers
|
||||
>>> tax_identifier.type = None
|
||||
>>> tax_identifier.save()
|
||||
|
||||
Create a paid invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('40')
|
||||
>>> invoice.click('post')
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
>>> invoice.tax_identifier
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
The invoice is posted when the reconciliation is deleted::
|
||||
|
||||
>>> invoice.payment_lines[0].reconciliation.delete()
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.tax_identifier
|
||||
|
||||
Credit invoice with non line lines::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('40')
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.type = 'comment'
|
||||
>>> line.description = 'Comment'
|
||||
>>> invoice.click('post')
|
||||
>>> credit = Wizard('account.invoice.credit', [invoice])
|
||||
>>> credit.form.with_refund = True
|
||||
>>> credit.execute('credit')
|
||||
@@ -0,0 +1,194 @@
|
||||
===================================
|
||||
Invoice Scenario Alternate Currency
|
||||
===================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.exceptions import InvoiceTaxesWarning
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> tomorrow = today + dt.timedelta(days=1)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Warning = Model.get('res.user.warning')
|
||||
|
||||
Get currencies::
|
||||
|
||||
>>> currency = get_currency('USD')
|
||||
>>> eur = get_currency('EUR')
|
||||
|
||||
Set alternate currency rates::
|
||||
|
||||
>>> rate = eur.rates.new()
|
||||
>>> rate.date = today
|
||||
>>> rate.rate = eur.rates[0].rate
|
||||
>>> rate = eur.rates.new()
|
||||
>>> rate.date = tomorrow
|
||||
>>> rate.rate = eur.rates[0].rate + Decimal('0.5')
|
||||
>>> eur.save()
|
||||
|
||||
Create fiscal years::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=(today, tomorrow)))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> account_tax = accounts['tax']
|
||||
>>> account_cash = accounts['cash']
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = account_cash
|
||||
>>> payment_method.debit_account = account_cash
|
||||
>>> payment_method.save()
|
||||
|
||||
Create writeoff method::
|
||||
|
||||
>>> WriteOff = Model.get('account.move.reconcile.write_off')
|
||||
>>> journal_writeoff = Journal(name='Write-Off', type='write-off')
|
||||
>>> journal_writeoff.save()
|
||||
>>> writeoff = WriteOff()
|
||||
>>> writeoff.name = 'Rate loss'
|
||||
>>> writeoff.journal = journal_writeoff
|
||||
>>> writeoff.credit_account = expense
|
||||
>>> writeoff.debit_account = expense
|
||||
>>> writeoff.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.customer_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create invoice with alternate currency::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.currency = eur
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('80')
|
||||
>>> line.amount
|
||||
Decimal('400.00')
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = revenue
|
||||
>>> line.description = 'Test'
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(20)
|
||||
>>> line.amount
|
||||
Decimal('20.00')
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('420.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('40.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('460.00')
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('420.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('40.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('460.00')
|
||||
|
||||
Create negative tax::
|
||||
|
||||
>>> negative_tax = create_tax(Decimal('-.10'))
|
||||
>>> negative_tax.save()
|
||||
|
||||
Create invoice with alternate currency and negative taxes::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.currency = eur
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('80')
|
||||
>>> _ = line.taxes.pop(0)
|
||||
>>> line.taxes.append(negative_tax)
|
||||
>>> line.amount
|
||||
Decimal('400.00')
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('400.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('-40.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('360.00')
|
||||
>>> try:
|
||||
... invoice.click('post')
|
||||
... except InvoiceTaxesWarning as warning:
|
||||
... Warning(user=config.user, name=warning.name).save()
|
||||
... raise
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceTaxesWarning: ...
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('400.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('-40.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('360.00')
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
=================================================
|
||||
Invoice Scenario Alternate Currency with Exchange
|
||||
=================================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Configuration = Model.get('account.configuration')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> PaymentTerm = Model.get('account.invoice.payment_term')
|
||||
|
||||
Get currencies::
|
||||
|
||||
>>> currency = get_currency('USD')
|
||||
>>> eur = get_currency('EUR')
|
||||
|
||||
Set alternate currency rates::
|
||||
|
||||
>>> rate, = eur.rates
|
||||
>>> rate.rate = Decimal('0.3')
|
||||
>>> eur.save()
|
||||
|
||||
Create fiscal years::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Configure currency exchange::
|
||||
|
||||
>>> currency_exchange_account, = (
|
||||
... accounts['revenue'].duplicate(
|
||||
... default={'name': "Currency Exchange"}))
|
||||
>>> configuration = Configuration(1)
|
||||
>>> configuration.currency_exchange_credit_account = (
|
||||
... currency_exchange_account)
|
||||
>>> configuration.save()
|
||||
|
||||
Create payment term::
|
||||
|
||||
>>> payment_term = PaymentTerm(name="Payment Term")
|
||||
>>> line = payment_term.lines.new(type='percent', ratio=Decimal('.5'))
|
||||
>>> line = payment_term.lines.new(type='remainder')
|
||||
>>> payment_term.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> invoice = Invoice(party=party)
|
||||
>>> invoice.currency = eur
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('100.0000')
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.total_amount
|
||||
Decimal('100.00')
|
||||
|
||||
Check accounts::
|
||||
|
||||
>>> accounts['receivable'].reload()
|
||||
>>> accounts['receivable'].balance
|
||||
Decimal('333.34')
|
||||
>>> accounts['receivable'].amount_second_currency
|
||||
Decimal('100.00')
|
||||
|
||||
>>> currency_exchange_account.reload()
|
||||
>>> currency_exchange_account.balance
|
||||
Decimal('-0.01')
|
||||
@@ -0,0 +1,96 @@
|
||||
==============================================
|
||||
Invoice Scenario Alternate Currency Lower Rate
|
||||
==============================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Get currencies::
|
||||
|
||||
>>> currency = get_currency('USD')
|
||||
>>> eur = get_currency('EUR')
|
||||
|
||||
Set alternate currency rates::
|
||||
|
||||
>>> rate = eur.rates.new()
|
||||
>>> rate.date = today
|
||||
>>> rate.rate = Decimal('0.5')
|
||||
>>> eur.save()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> Sequence = Model.get('ir.sequence')
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = accounts['cash']
|
||||
>>> payment_method.debit_account = accounts['cash']
|
||||
>>> payment_method.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create invoice with alternate currency::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.currency = eur
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.description = "Line"
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('80')
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.total_amount
|
||||
Decimal('400.00')
|
||||
|
||||
Pay the invoice::
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount
|
||||
Decimal('400.00')
|
||||
>>> assertEqual(pay.form.currency, eur)
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.form.date = today
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
@@ -0,0 +1,117 @@
|
||||
===============================================
|
||||
Invoice Scenario Alternate Currency Rate Change
|
||||
===============================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> tomorrow = today + dt.timedelta(days=1)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Configuration = Model.get('account.configuration')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
|
||||
Get currencies::
|
||||
|
||||
>>> currency = get_currency('USD')
|
||||
>>> eur = get_currency('EUR')
|
||||
|
||||
Set alternate currency rates::
|
||||
|
||||
>>> rate = eur.rates.new()
|
||||
>>> rate.date = today
|
||||
>>> rate.rate = Decimal('1.20')
|
||||
>>> rate = eur.rates.new()
|
||||
>>> rate.date = tomorrow
|
||||
>>> rate.rate = Decimal('1.10')
|
||||
>>> eur.save()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Configure currency exchange::
|
||||
|
||||
>>> currency_exchange_account, = (
|
||||
... accounts['revenue'].duplicate(
|
||||
... default={'name': "Currency Exchange"}))
|
||||
>>> configuration = Configuration(1)
|
||||
>>> configuration.currency_exchange_credit_account = (
|
||||
... currency_exchange_account)
|
||||
>>> configuration.save()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = "Cash"
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = accounts['cash']
|
||||
>>> payment_method.debit_account = accounts['cash']
|
||||
>>> payment_method.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create invoice with alternate currency::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> invoice.currency = eur
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.description = "Line"
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('80')
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.total_amount
|
||||
Decimal('400.00')
|
||||
|
||||
Pay the invoice::
|
||||
|
||||
>>> pay = Wizard('account.invoice.pay', [invoice])
|
||||
>>> pay.form.amount
|
||||
Decimal('400.00')
|
||||
>>> assertEqual(pay.form.currency, eur)
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.form.date = tomorrow
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
>>> accounts['receivable'].reload()
|
||||
>>> abs(accounts['receivable'].balance)
|
||||
Decimal('0.00')
|
||||
>>> currency_exchange_account.reload()
|
||||
>>> currency_exchange_account.balance
|
||||
Decimal('-30.31')
|
||||
@@ -0,0 +1,135 @@
|
||||
==================================
|
||||
Invoice Alternative Payee Scenario
|
||||
==================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
>>> journal_cash, = Journal.find([
|
||||
... ('code', '=', 'CASH'),
|
||||
... ])
|
||||
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = "Cash"
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = accounts['cash']
|
||||
>>> payment_method.debit_account = accounts['cash']
|
||||
>>> payment_method.save()
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party1 = Party(name="Party 1")
|
||||
>>> party1.save()
|
||||
>>> party2 = Party(name="Party 2")
|
||||
>>> party2.save()
|
||||
>>> party3 = Party(name="Party 3")
|
||||
>>> party3.save()
|
||||
|
||||
Post customer invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party1
|
||||
>>> invoice.alternative_payees.append(Party(party2.id))
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(10)
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> len(invoice.lines_to_pay)
|
||||
1
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('10.00')
|
||||
|
||||
>>> party1.reload()
|
||||
>>> party1.receivable
|
||||
Decimal('0')
|
||||
>>> party2.reload()
|
||||
>>> party2.receivable
|
||||
Decimal('10.00')
|
||||
>>> party3.reload()
|
||||
>>> party3.receivable
|
||||
Decimal('0')
|
||||
|
||||
Copying invoice with single alternative payee is kept::
|
||||
|
||||
>>> duplicate_inv, = invoice.duplicate()
|
||||
>>> assertEqual(duplicate_inv.alternative_payees, invoice.alternative_payees)
|
||||
|
||||
Set another payee::
|
||||
|
||||
>>> delegate = Wizard(
|
||||
... 'account.invoice.lines_to_pay.delegate', [invoice])
|
||||
>>> delegate_lines, = delegate.actions
|
||||
>>> delegate_lines.form.party = party3
|
||||
>>> delegate_lines.execute('delegate')
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> len(invoice.lines_to_pay)
|
||||
3
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('10.00')
|
||||
|
||||
>>> party1.reload()
|
||||
>>> party1.receivable
|
||||
Decimal('0')
|
||||
>>> party2.reload()
|
||||
>>> party2.receivable
|
||||
Decimal('0')
|
||||
>>> party3.reload()
|
||||
>>> party3.receivable
|
||||
Decimal('10.00')
|
||||
|
||||
Pay the invoice::
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.payee = party3
|
||||
>>> pay.form.amount = Decimal('10.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> len(invoice.payment_lines)
|
||||
1
|
||||
>>> len(invoice.reconciliation_lines)
|
||||
1
|
||||
|
||||
Copying invoice with many alternative payees remove them::
|
||||
|
||||
>>> duplicate_inv, = invoice.duplicate()
|
||||
>>> duplicate_inv.alternative_payees
|
||||
[]
|
||||
@@ -0,0 +1,86 @@
|
||||
===========================
|
||||
Customer Invoice Sequential
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> past_year = today - dt.timedelta(days=365)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal years::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=past_year))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
>>> renew_fiscalyear = Wizard('account.fiscalyear.renew')
|
||||
>>> renew_fiscalyear.execute('create_')
|
||||
>>> next_fiscalyear, = renew_fiscalyear.actions[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create invoice invoice second period and next year::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
|
||||
>>> invoice = Invoice(type='out')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = fiscalyear.periods[1].start_date
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('10')
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> invoice.click('post')
|
||||
|
||||
>>> invoice = Invoice(type='out')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = next_fiscalyear.periods[0].start_date
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> invoice.click('post')
|
||||
|
||||
Try to post invoice on first period::
|
||||
|
||||
>>> invoice = Invoice(type='out')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = fiscalyear.periods[0].start_date
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('5')
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> invoice.save()
|
||||
|
||||
>>> invoice.click('post')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceNumberError: ...
|
||||
|
||||
Post invoice on the third period::
|
||||
|
||||
>>> invoice.invoice_date = fiscalyear.periods[2].start_date
|
||||
>>> invoice.click('post')
|
||||
148
modules/account_invoice/tests/scenario_invoice_group_line.rst
Normal file
148
modules/account_invoice/tests/scenario_invoice_group_line.rst
Normal file
@@ -0,0 +1,148 @@
|
||||
================
|
||||
Invoice Scenario
|
||||
================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> receivable = accounts['receivable']
|
||||
>>> payable = accounts['payable']
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> account_cash = accounts['cash']
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> journal_cash, = Journal.find([
|
||||
... ('code', '=', 'CASH'),
|
||||
... ])
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Post customer invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = revenue
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(10)
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('10.00')
|
||||
|
||||
Post supplier invoice::
|
||||
|
||||
>>> supplier_invoice = Invoice(type='in')
|
||||
>>> supplier_invoice.party = party
|
||||
>>> supplier_invoice.invoice_date = period.start_date
|
||||
>>> line = supplier_invoice.lines.new()
|
||||
>>> line.account = expense
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(5)
|
||||
>>> supplier_invoice.click('post')
|
||||
>>> supplier_invoice.state
|
||||
'posted'
|
||||
>>> supplier_invoice.amount_to_pay
|
||||
Decimal('5.00')
|
||||
|
||||
Group lines::
|
||||
|
||||
>>> Line = Model.get('account.move.line')
|
||||
>>> lines = Line.find([('account', 'in', [payable.id, receivable.id])])
|
||||
>>> len(lines)
|
||||
2
|
||||
>>> group = Wizard('account.move.line.group', lines)
|
||||
>>> group.form.journal = journal_cash
|
||||
>>> group.execute('group')
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
>>> supplier_invoice.reload()
|
||||
>>> supplier_invoice.state
|
||||
'posted'
|
||||
>>> supplier_invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
|
||||
Receive remaining line::
|
||||
|
||||
>>> Move = Model.get('account.move')
|
||||
>>> move = Move()
|
||||
>>> move.journal = journal_cash
|
||||
>>> move.date = period.start_date
|
||||
>>> line = move.lines.new()
|
||||
>>> line.account = account_cash
|
||||
>>> line.debit = Decimal(5)
|
||||
>>> line = move.lines.new()
|
||||
>>> line.account = receivable
|
||||
>>> line.party = party
|
||||
>>> line.credit = Decimal(5)
|
||||
>>> move.click('post')
|
||||
|
||||
>>> lines = Line.find([
|
||||
... ('account', '=', receivable.id),
|
||||
... ('reconciliation', '=', None),
|
||||
... ])
|
||||
>>> reconcile_lines = Wizard('account.move.reconcile_lines', lines)
|
||||
>>> reconcile_lines.state
|
||||
'end'
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
>>> supplier_invoice.reload()
|
||||
>>> supplier_invoice.state
|
||||
'paid'
|
||||
>>> supplier_invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
|
||||
Remove the created reconciliation::
|
||||
|
||||
>>> Reconciliation = Model.get('account.move.reconciliation')
|
||||
>>> reconciliation, = Reconciliation.find([('lines', '=', lines[0].id)])
|
||||
>>> Reconciliation.delete([reconciliation])
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
>>> supplier_invoice.reload()
|
||||
>>> supplier_invoice.state
|
||||
'posted'
|
||||
>>> supplier_invoice.amount_to_pay
|
||||
Decimal('0')
|
||||
66
modules/account_invoice/tests/scenario_invoice_in_future.rst
Normal file
66
modules/account_invoice/tests/scenario_invoice_in_future.rst
Normal file
@@ -0,0 +1,66 @@
|
||||
=================
|
||||
Invoice in Future
|
||||
=================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> tomorrow = today + dt.timedelta(days=1)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=(today, tomorrow)))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> revenue = accounts['revenue']
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = revenue
|
||||
>>> line.description = 'Test'
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(20)
|
||||
|
||||
Posting an invoice in the future raises a warning::
|
||||
|
||||
>>> invoice.invoice_date = tomorrow
|
||||
>>> invoice.click('post')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceFutureWarning: ...
|
||||
|
||||
Post invoice::
|
||||
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
@@ -0,0 +1,81 @@
|
||||
==================
|
||||
Invoice Manual Tax
|
||||
==================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, create_tax_code, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> TaxCode = Model.get('account.tax.code')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period_ids = [p.id for p in fiscalyear.periods]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> invoice_base_code = create_tax_code(tax, 'base', 'invoice')
|
||||
>>> invoice_base_code.save()
|
||||
>>> invoice_tax_code = create_tax_code(tax, 'tax', 'invoice')
|
||||
>>> invoice_tax_code.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Post a supplier invoice with manual taxes::
|
||||
|
||||
>>> invoice = Invoice(type='in')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> tax_line = invoice.taxes.new()
|
||||
>>> bool(tax_line.manual)
|
||||
True
|
||||
>>> tax_line.tax = tax
|
||||
>>> tax_line.base = Decimal('100')
|
||||
>>> tax_line.amount
|
||||
Decimal('10.00')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('0.00'), Decimal('10.00'), Decimal('10.00'))
|
||||
|
||||
Post invoice and check tax codes::
|
||||
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('0.00'), Decimal('10.00'), Decimal('10.00'))
|
||||
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_base_code = TaxCode(invoice_base_code.id)
|
||||
... invoice_base_code.amount
|
||||
Decimal('100.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_tax_code = TaxCode(invoice_tax_code.id)
|
||||
... invoice_tax_code.amount
|
||||
Decimal('10.00')
|
||||
108
modules/account_invoice/tests/scenario_invoice_overpayment.rst
Normal file
108
modules/account_invoice/tests/scenario_invoice_overpayment.rst
Normal file
@@ -0,0 +1,108 @@
|
||||
============================
|
||||
Invoice Overpayment Scenario
|
||||
============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal years::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create payment method::
|
||||
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> journal_cash, = Journal.find([('type', '=', 'cash')])
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = 'Cash'
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = accounts['cash']
|
||||
>>> payment_method.debit_account = accounts['cash']
|
||||
>>> payment_method.save()
|
||||
|
||||
Create write-off method::
|
||||
|
||||
>>> WriteOff = Model.get('account.move.reconcile.write_off')
|
||||
>>> journal_writeoff = Journal(name='Write-Off', type='write-off')
|
||||
>>> journal_writeoff.save()
|
||||
>>> writeoff = WriteOff()
|
||||
>>> writeoff.name = 'Write-off'
|
||||
>>> writeoff.journal = journal_writeoff
|
||||
>>> writeoff.credit_account = accounts['expense']
|
||||
>>> writeoff.debit_account = accounts['expense']
|
||||
>>> writeoff.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create an invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice(party=party)
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('100')
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> invoice.total_amount
|
||||
Decimal('100.00')
|
||||
|
||||
Overpay the invoice with write-off::
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount = Decimal('110.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.form.type = 'writeoff'
|
||||
>>> pay.form.writeoff = writeoff
|
||||
>>> pay.form.amount_writeoff
|
||||
Decimal('-10.00')
|
||||
>>> pay.execute('pay')
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
>>> accounts['receivable'].reload()
|
||||
>>> accounts['receivable'].balance
|
||||
Decimal('0.00')
|
||||
|
||||
Overpay the invoice without write-off::
|
||||
|
||||
>>> invoice, = invoice.duplicate()
|
||||
>>> invoice.click('post')
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount = Decimal('110.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.form.type = 'overpayment'
|
||||
>>> pay.execute('pay')
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
|
||||
>>> accounts['receivable'].reload()
|
||||
>>> accounts['receivable'].balance
|
||||
Decimal('-10.00')
|
||||
@@ -0,0 +1,83 @@
|
||||
================================
|
||||
Invoice Report Revision Scenario
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> Move = Model.get('account.move')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Sequence = Model.get('ir.sequence')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Post an invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'out'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
|
||||
Check invoice report::
|
||||
|
||||
>>> bool(invoice.invoice_report_cache)
|
||||
True
|
||||
>>> len(invoice.invoice_report_revisions)
|
||||
0
|
||||
>>> invoice.invoice_report_format
|
||||
'odt'
|
||||
|
||||
Execute update invoice report wizard::
|
||||
|
||||
>>> refresh_invoice_report = Wizard(
|
||||
... 'account.invoice.refresh_invoice_report', [invoice])
|
||||
>>> revision, = invoice.invoice_report_revisions
|
||||
>>> bool(revision.invoice_report_cache)
|
||||
True
|
||||
>>> revision.invoice_report_format
|
||||
'odt'
|
||||
>>> revision.filename
|
||||
'Invoice-1.odt'
|
||||
@@ -0,0 +1,102 @@
|
||||
=================================
|
||||
Invoice Reschedule Lines Scenario
|
||||
=================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> PaymentMethod = Model.get('account.invoice.payment.method')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
>>> journal_cash, = Journal.find([
|
||||
... ('code', '=', 'CASH'),
|
||||
... ])
|
||||
|
||||
>>> payment_method = PaymentMethod()
|
||||
>>> payment_method.name = "Cash"
|
||||
>>> payment_method.journal = journal_cash
|
||||
>>> payment_method.credit_account = accounts['cash']
|
||||
>>> payment_method.debit_account = accounts['cash']
|
||||
>>> payment_method.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Post customer invoice::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(10)
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> len(invoice.lines_to_pay)
|
||||
1
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('10.00')
|
||||
|
||||
Reschedule line::
|
||||
|
||||
>>> reschedule = invoice.click('reschedule_lines_to_pay')
|
||||
>>> reschedule_lines, = reschedule.actions
|
||||
>>> reschedule_lines.form.total_amount
|
||||
Decimal('10.00')
|
||||
>>> reschedule_lines.form.start_date = period.end_date
|
||||
>>> reschedule_lines.form.frequency = 'monthly'
|
||||
>>> reschedule_lines.form.number = 2
|
||||
>>> reschedule_lines.execute('preview')
|
||||
>>> reschedule_lines.execute('reschedule')
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> len(invoice.lines_to_pay)
|
||||
4
|
||||
>>> len([l for l in invoice.lines_to_pay if not l.reconciliation])
|
||||
2
|
||||
>>> invoice.amount_to_pay
|
||||
Decimal('10.00')
|
||||
|
||||
Pay the invoice::
|
||||
|
||||
>>> pay = invoice.click('pay')
|
||||
>>> pay.form.amount = Decimal('10.00')
|
||||
>>> pay.form.payment_method = payment_method
|
||||
>>> pay.execute('choice')
|
||||
>>> pay.state
|
||||
'end'
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
>>> len(invoice.reconciliation_lines)
|
||||
1
|
||||
244
modules/account_invoice/tests/scenario_invoice_supplier.rst
Normal file
244
modules/account_invoice/tests/scenario_invoice_supplier.rst
Normal file
@@ -0,0 +1,244 @@
|
||||
=========================
|
||||
Invoice Supplier Scenario
|
||||
=========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, create_tax_code, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period_ids = [p.id for p in fiscalyear.periods]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> payable = accounts['payable']
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
>>> account_tax = accounts['tax']
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> TaxCode = Model.get('account.tax.code')
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> invoice_base_code = create_tax_code(tax, 'base', 'invoice')
|
||||
>>> invoice_base_code.save()
|
||||
>>> invoice_tax_code = create_tax_code(tax, 'tax', 'invoice')
|
||||
>>> invoice_tax_code.save()
|
||||
>>> credit_note_base_code = create_tax_code(tax, 'base', 'credit')
|
||||
>>> credit_note_base_code.save()
|
||||
>>> credit_note_tax_code = create_tax_code(tax, 'tax', 'credit')
|
||||
>>> credit_note_tax_code.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.account_revenue = revenue
|
||||
>>> account_category.supplier_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create payment term::
|
||||
|
||||
>>> PaymentTerm = Model.get('account.invoice.payment_term')
|
||||
>>> payment_term = PaymentTerm(name='Term')
|
||||
>>> line = payment_term.lines.new(type='remainder')
|
||||
>>> payment_term.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> InvoiceLine = Model.get('account.invoice.line')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'in'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.reference = 'FAC001'
|
||||
>>> invoice.supplier_payment_reference_type = 'creditor_reference'
|
||||
>>> invoice.supplier_payment_reference = 'RF18539007547034'
|
||||
>>> invoice.supplier_payment_reference
|
||||
'RF18 5390 0754 7034'
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.account = expense
|
||||
>>> line.description = 'Test'
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(10)
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('110.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('10.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('120.00')
|
||||
>>> invoice.save()
|
||||
>>> invoice.state
|
||||
'draft'
|
||||
>>> bool(invoice.move)
|
||||
False
|
||||
>>> invoice.click('validate_invoice')
|
||||
>>> invoice.state
|
||||
'validated'
|
||||
>>> bool(invoice.move)
|
||||
True
|
||||
>>> invoice.move.state
|
||||
'draft'
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
>>> bool(invoice.move)
|
||||
True
|
||||
>>> invoice.move.state
|
||||
'posted'
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('110.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('10.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('120.00')
|
||||
>>> payable.reload()
|
||||
>>> payable.debit
|
||||
Decimal('0.00')
|
||||
>>> payable.credit
|
||||
Decimal('120.00')
|
||||
>>> expense.reload()
|
||||
>>> expense.debit
|
||||
Decimal('110.00')
|
||||
>>> expense.credit
|
||||
Decimal('0.00')
|
||||
>>> account_tax.reload()
|
||||
>>> account_tax.debit
|
||||
Decimal('10.00')
|
||||
>>> account_tax.credit
|
||||
Decimal('0.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_base_code = TaxCode(invoice_base_code.id)
|
||||
... invoice_base_code.amount
|
||||
Decimal('100.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_tax_code = TaxCode(invoice_tax_code.id)
|
||||
... invoice_tax_code.amount
|
||||
Decimal('10.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_base_code = TaxCode(credit_note_base_code.id)
|
||||
... credit_note_base_code.amount
|
||||
Decimal('0.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_tax_code = TaxCode(credit_note_tax_code.id)
|
||||
... credit_note_tax_code.amount
|
||||
Decimal('0.00')
|
||||
|
||||
Credit invoice::
|
||||
|
||||
>>> credit = Wizard('account.invoice.credit', [invoice])
|
||||
>>> credit.form.with_refund = False
|
||||
>>> credit.execute('credit')
|
||||
>>> credit_note, = Invoice.find(
|
||||
... [('type', '=', 'in'), ('id', '!=', invoice.id)])
|
||||
>>> credit_note.state
|
||||
'draft'
|
||||
>>> credit_note.untaxed_amount
|
||||
Decimal('-110.00')
|
||||
>>> credit_note.tax_amount
|
||||
Decimal('-10.00')
|
||||
>>> credit_note.total_amount
|
||||
Decimal('-120.00')
|
||||
|
||||
A warning is raised when creating an invoice with same reference::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'in'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.reference = 'FAC001'
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> invoice.click('post')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceSimilarWarning: ...
|
||||
>>> invoice.reference = 'FAC002'
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
|
||||
Create a posted and a draft invoice to cancel::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'in'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> invoice.click('post')
|
||||
>>> invoice_draft, = Invoice.duplicate([invoice])
|
||||
|
||||
Cancel draft invoice::
|
||||
|
||||
>>> invoice_draft.click('cancel')
|
||||
>>> invoice_draft.state
|
||||
'cancelled'
|
||||
>>> invoice_draft.move
|
||||
>>> invoice_draft.reconciled
|
||||
|
||||
Cancel posted invoice::
|
||||
|
||||
>>> invoice.click('cancel')
|
||||
>>> invoice.state
|
||||
'cancelled'
|
||||
>>> invoice.cancel_move is not None
|
||||
True
|
||||
>>> bool(invoice.reconciled)
|
||||
True
|
||||
@@ -0,0 +1,117 @@
|
||||
==========================
|
||||
Invoice Supplier Post Paid
|
||||
==========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period = fiscalyear.periods[0]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
>>> payable = accounts['payable']
|
||||
>>> expense = accounts['expense']
|
||||
>>> cash = accounts['cash']
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = expense
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('40')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create validated invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'in'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> invoice.click('validate_invoice')
|
||||
>>> invoice.state
|
||||
'validated'
|
||||
|
||||
Pay invoice::
|
||||
|
||||
>>> Move = Model.get('account.move')
|
||||
>>> Journal = Model.get('account.journal')
|
||||
>>> journal_cash, = Journal.find([
|
||||
... ('code', '=', 'CASH'),
|
||||
... ])
|
||||
>>> move = Move()
|
||||
>>> move.period = period
|
||||
>>> move.journal = journal_cash
|
||||
>>> move.date = period.start_date
|
||||
>>> line = move.lines.new()
|
||||
>>> line.account = payable
|
||||
>>> line.debit = Decimal('100')
|
||||
>>> line.party = party
|
||||
>>> line = move.lines.new()
|
||||
>>> line.account = cash
|
||||
>>> line.credit = Decimal('100')
|
||||
>>> move.save()
|
||||
|
||||
>>> Line = Model.get('account.move.line')
|
||||
>>> lines = Line.find([('account', '=', payable.id)])
|
||||
>>> reconcile = Wizard('account.move.reconcile_lines', lines)
|
||||
|
||||
Check invoice::
|
||||
|
||||
>>> invoice.reload()
|
||||
>>> invoice.state
|
||||
'validated'
|
||||
>>> bool(invoice.reconciled)
|
||||
True
|
||||
|
||||
Post invoice::
|
||||
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'paid'
|
||||
@@ -0,0 +1,120 @@
|
||||
======================
|
||||
Invoice Tax Deductible
|
||||
======================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.exceptions import InvoiceTaxesWarning
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Warning = Model.get('res.user.warning')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(today=today))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> party = Party(name="Party")
|
||||
>>> party.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.supplier_taxes_deductible_rate = Decimal('.5')
|
||||
>>> account_category.supplier_taxes.append(tax)
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.list_price = Decimal('100')
|
||||
>>> template.account_category = account_category
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Post a supplier invoice with 0% deductible::
|
||||
|
||||
>>> invoice = Invoice(type='in')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> line.unit_price = Decimal('50')
|
||||
>>> line.taxes_deductible_rate
|
||||
Decimal('0.5')
|
||||
>>> line.taxes_deductible_rate = Decimal(0)
|
||||
>>> line.amount
|
||||
Decimal('550.00')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('550.00'), Decimal('0.00'), Decimal('550.00'))
|
||||
>>> try:
|
||||
... invoice.click('post')
|
||||
... except InvoiceTaxesWarning as warning:
|
||||
... Warning(user=config.user, name=warning.name).save()
|
||||
... raise
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceTaxesWarning: ...
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('550.00'), Decimal('0.00'), Decimal('550.00'))
|
||||
>>> len(invoice.taxes)
|
||||
0
|
||||
|
||||
Post a supplier invoice with 50% deductible rate::
|
||||
|
||||
>>> invoice = Invoice(type='in')
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> line.unit_price = Decimal('50')
|
||||
>>> line.amount
|
||||
Decimal('525.00')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('525.00'), Decimal('25.00'), Decimal('550.00'))
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
|
||||
(Decimal('525.00'), Decimal('25.00'), Decimal('550.00'))
|
||||
>>> len(invoice.taxes)
|
||||
1
|
||||
102
modules/account_invoice/tests/scenario_invoice_with_credit.rst
Normal file
102
modules/account_invoice/tests/scenario_invoice_with_credit.rst
Normal file
@@ -0,0 +1,102 @@
|
||||
============================
|
||||
Invoice with credit Scenario
|
||||
============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, create_tax_code, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> period_ids = [p.id for p in fiscalyear.periods]
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> TaxCode = Model.get('account.tax.code')
|
||||
>>> Tax = Model.get('account.tax')
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
>>> invoice_base_code = create_tax_code(tax, 'base', 'invoice')
|
||||
>>> invoice_base_code.save()
|
||||
>>> invoice_tax_code = create_tax_code(tax, 'tax', 'invoice')
|
||||
>>> invoice_tax_code.save()
|
||||
>>> credit_note_base_code = create_tax_code(tax, 'base', 'credit')
|
||||
>>> credit_note_base_code.save()
|
||||
>>> credit_note_tax_code = create_tax_code(tax, 'tax', 'credit')
|
||||
>>> credit_note_tax_code.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party(name='Party')
|
||||
>>> party.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.party = party
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = 5
|
||||
>>> line.unit_price = Decimal('10.0000')
|
||||
>>> line.taxes.append(Tax(tax.id))
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.account = accounts['revenue']
|
||||
>>> line.quantity = -2
|
||||
>>> line.unit_price = Decimal('5.0000')
|
||||
>>> line.taxes.append(Tax(tax.id))
|
||||
>>> invoice.invoice_date = fiscalyear.start_date
|
||||
>>> invoice.click('post')
|
||||
|
||||
>>> invoice.untaxed_amount
|
||||
Decimal('40.00')
|
||||
>>> invoice.tax_amount
|
||||
Decimal('4.00')
|
||||
>>> invoice.total_amount
|
||||
Decimal('44.00')
|
||||
|
||||
Test taxes::
|
||||
|
||||
>>> len(invoice.taxes)
|
||||
2
|
||||
|
||||
>>> accounts['tax'].reload()
|
||||
>>> accounts['tax'].debit, accounts['tax'].credit
|
||||
(Decimal('1.00'), Decimal('5.00'))
|
||||
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_base_code = TaxCode(invoice_base_code.id)
|
||||
... invoice_base_code.amount
|
||||
Decimal('50.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... invoice_tax_code = TaxCode(invoice_tax_code.id)
|
||||
... invoice_tax_code.amount
|
||||
Decimal('5.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_base_code = TaxCode(credit_note_base_code.id)
|
||||
... credit_note_base_code.amount
|
||||
Decimal('10.00')
|
||||
>>> with config.set_context(periods=period_ids):
|
||||
... credit_note_tax_code = TaxCode(credit_note_tax_code.id)
|
||||
... credit_note_tax_code.amount
|
||||
Decimal('1.00')
|
||||
99
modules/account_invoice/tests/scenario_renew_fiscalyear.rst
Normal file
99
modules/account_invoice/tests/scenario_renew_fiscalyear.rst
Normal file
@@ -0,0 +1,99 @@
|
||||
=========================
|
||||
Renew Fiscalyear Scenario
|
||||
=========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.modules.account.tests.tools import create_fiscalyear
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company)
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> InvoiceSequence = Model.get('account.fiscalyear.invoice_sequence')
|
||||
>>> fiscalyear = create_fiscalyear(today=today)
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(fiscalyear)
|
||||
>>> fiscalyear.click('create_period')
|
||||
>>> inv_seq, = fiscalyear.invoice_sequences
|
||||
>>> seq = inv_seq.out_invoice_sequence
|
||||
>>> for period in fiscalyear.periods:
|
||||
... seq, = seq.duplicate()
|
||||
... _ = inv_seq.duplicate(default={
|
||||
... 'period': period.id,
|
||||
... 'out_invoice_sequence': seq.id,
|
||||
... 'in_invoice_sequence': seq.id,
|
||||
... 'out_credit_note_sequence': seq.id,
|
||||
... 'in_credit_note_sequence': seq.id,
|
||||
... })
|
||||
>>> period = fiscalyear.periods.new()
|
||||
>>> period.name = 'Adjustment'
|
||||
>>> period.start_date = fiscalyear.end_date
|
||||
>>> period.end_date = fiscalyear.end_date
|
||||
>>> period.type = 'adjustment'
|
||||
>>> fiscalyear.save()
|
||||
|
||||
Set the sequence number::
|
||||
|
||||
>>> sequence = fiscalyear.move_sequence
|
||||
>>> sequence.number_next = 10
|
||||
>>> sequence.save()
|
||||
|
||||
>>> for i, seq in enumerate(fiscalyear.invoice_sequences):
|
||||
... seq.out_invoice_sequence.number_next = i
|
||||
... seq.out_invoice_sequence.save()
|
||||
|
||||
Renew fiscal year using the wizard::
|
||||
|
||||
>>> renew_fiscalyear = Wizard('account.fiscalyear.renew')
|
||||
>>> renew_fiscalyear.form.reset_sequences = False
|
||||
>>> renew_fiscalyear.execute('create_')
|
||||
>>> new_fiscalyear, = renew_fiscalyear.actions[0]
|
||||
>>> len(new_fiscalyear.periods)
|
||||
12
|
||||
>>> int(new_fiscalyear.move_sequence.number_next)
|
||||
10
|
||||
>>> [int(seq.out_invoice_sequence.number_next)
|
||||
... for seq in fiscalyear.invoice_sequences]
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
|
||||
Renew fiscal year resetting sequences::
|
||||
|
||||
>>> renew_fiscalyear = Wizard('account.fiscalyear.renew')
|
||||
>>> renew_fiscalyear.form.reset_sequences = True
|
||||
>>> renew_fiscalyear.execute('create_')
|
||||
>>> new_fiscalyear, = renew_fiscalyear.actions[0]
|
||||
>>> int(new_fiscalyear.move_sequence.number_next)
|
||||
1
|
||||
>>> [int(seq.out_invoice_sequence.number_next)
|
||||
... for seq in new_fiscalyear.invoice_sequences]
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||||
|
||||
|
||||
Set the sequence name::
|
||||
|
||||
>>> for seq in new_fiscalyear.invoice_sequences:
|
||||
... seq.out_invoice_sequence.name = ('Sequence %s' %
|
||||
... new_fiscalyear.name)
|
||||
... seq.out_invoice_sequence.save()
|
||||
|
||||
Renew fiscalyear and test sequence name is updated::
|
||||
|
||||
>>> renew_fiscalyear = Wizard('account.fiscalyear.renew')
|
||||
>>> renew_fiscalyear.form.reset_sequences = True
|
||||
>>> renew_fiscalyear.execute('create_')
|
||||
>>> new_fiscalyear, = renew_fiscalyear.actions[0]
|
||||
>>> for sequence in new_fiscalyear.invoice_sequences:
|
||||
... assertEqual(
|
||||
... sequence.out_invoice_sequence.name,
|
||||
... 'Sequence %s' % new_fiscalyear.name)
|
||||
255
modules/account_invoice/tests/test_module.py
Normal file
255
modules/account_invoice/tests/test_module.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# 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 datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from trytond.modules.account_invoice.exceptions import (
|
||||
PaymentTermValidationError)
|
||||
from trytond.modules.company.tests import (
|
||||
CompanyTestMixin, PartyCompanyCheckEraseMixin)
|
||||
from trytond.modules.currency.tests import create_currency
|
||||
from trytond.modules.party.tests import PartyCheckReplaceMixin
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
|
||||
|
||||
def set_invoice_sequences(fiscalyear):
|
||||
pool = Pool()
|
||||
Sequence = pool.get('ir.sequence.strict')
|
||||
SequenceType = pool.get('ir.sequence.type')
|
||||
InvoiceSequence = pool.get('account.fiscalyear.invoice_sequence')
|
||||
|
||||
sequence_type, = SequenceType.search([
|
||||
('name', '=', "Invoice"),
|
||||
], limit=1)
|
||||
sequence = Sequence(name=fiscalyear.name, sequence_type=sequence_type)
|
||||
sequence.company = fiscalyear.company
|
||||
sequence.save()
|
||||
invoice_sequence = InvoiceSequence()
|
||||
invoice_sequence.in_invoice_sequence = sequence
|
||||
invoice_sequence.in_credit_note_sequence = sequence
|
||||
invoice_sequence.out_invoice_sequence = sequence
|
||||
invoice_sequence.out_credit_note_sequence = sequence
|
||||
fiscalyear.invoice_sequences = [invoice_sequence]
|
||||
return fiscalyear
|
||||
|
||||
|
||||
class AccountInvoiceTestCase(
|
||||
PartyCompanyCheckEraseMixin, PartyCheckReplaceMixin, CompanyTestMixin,
|
||||
ModuleTestCase):
|
||||
'Test AccountInvoice module'
|
||||
module = 'account_invoice'
|
||||
|
||||
@with_transaction()
|
||||
def test_payment_term(self):
|
||||
'Test payment_term'
|
||||
pool = Pool()
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
|
||||
cu1 = create_currency('cu1')
|
||||
term, = PaymentTerm.create([{
|
||||
'name': '30 days, 1 month, 1 month + 15 days',
|
||||
'lines': [
|
||||
('create', [{
|
||||
'sequence': 0,
|
||||
'type': 'percent',
|
||||
'divisor': 4,
|
||||
'ratio': Decimal('.25'),
|
||||
'relativedeltas': [('create', [{
|
||||
'days': 30,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 1,
|
||||
'type': 'percent_on_total',
|
||||
'divisor': 4,
|
||||
'ratio': Decimal('.25'),
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 2,
|
||||
'type': 'fixed',
|
||||
'amount': Decimal('396.84'),
|
||||
'currency': cu1.id,
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
'days': 30,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 3,
|
||||
'type': 'remainder',
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 2,
|
||||
'days': 30,
|
||||
'day': 15,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}])]
|
||||
}])
|
||||
terms = term.compute(Decimal('1587.35'), cu1,
|
||||
date=datetime.date(2011, 10, 1))
|
||||
self.assertEqual(terms, [
|
||||
(datetime.date(2011, 10, 31), Decimal('396.84')),
|
||||
(datetime.date(2011, 11, 1), Decimal('396.84')),
|
||||
(datetime.date(2011, 12, 1), Decimal('396.84')),
|
||||
(datetime.date(2012, 1, 14), Decimal('396.83')),
|
||||
])
|
||||
|
||||
@with_transaction()
|
||||
def test_payment_term_with_repeating_decimal(self):
|
||||
"Test payment_term with repeating decimal"
|
||||
pool = Pool()
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
|
||||
PaymentTerm.create([{
|
||||
'name': "Repeating Decimal",
|
||||
'lines': [
|
||||
('create', [{
|
||||
'type': 'percent',
|
||||
'divisor': Decimal(3),
|
||||
'ratio': Decimal('0.3333333333'),
|
||||
}, {
|
||||
'type': 'remainder',
|
||||
}]),
|
||||
],
|
||||
}])
|
||||
|
||||
@with_transaction()
|
||||
def test_payment_term_with_invalid_ratio_divisor(self):
|
||||
"Test payment_term with invalid ratio and divisor"
|
||||
pool = Pool()
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
|
||||
with self.assertRaises(PaymentTermValidationError):
|
||||
PaymentTerm.create([{
|
||||
'name': "Invalid ratio and divisor",
|
||||
'lines': [
|
||||
('create', [{
|
||||
'type': 'percent',
|
||||
'divisor': Decimal(2),
|
||||
'ratio': Decimal('0.4'),
|
||||
}, {
|
||||
'type': 'remainder',
|
||||
}]),
|
||||
],
|
||||
}])
|
||||
|
||||
@with_transaction()
|
||||
def test_payment_term_with_empty_value(self):
|
||||
'Test payment_term with empty'
|
||||
pool = Pool()
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
|
||||
cu1 = create_currency('cu1')
|
||||
remainder_term, percent_term = PaymentTerm.create([{
|
||||
'name': 'Remainder',
|
||||
'lines': [
|
||||
('create', [{'type': 'remainder',
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}])]
|
||||
}, {
|
||||
'name': '25% tomorrow, remainder un month later ',
|
||||
'lines': [
|
||||
('create', [{'type': 'percent',
|
||||
'divisor': 4,
|
||||
'ratio': Decimal('.25'),
|
||||
'relativedeltas': [('create', [{
|
||||
'days': 1,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {'type': 'remainder',
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}])]
|
||||
}])
|
||||
terms = remainder_term.compute(Decimal('0.0'), cu1,
|
||||
date=datetime.date(2016, 5, 17))
|
||||
self.assertEqual(terms, [
|
||||
(datetime.date(2016, 5, 17), Decimal('0.0')),
|
||||
])
|
||||
terms = percent_term.compute(Decimal('0.0'), cu1,
|
||||
date=datetime.date(2016, 5, 17))
|
||||
self.assertEqual(terms, [
|
||||
(datetime.date(2016, 5, 17), Decimal('0.0')),
|
||||
])
|
||||
|
||||
@with_transaction()
|
||||
def test_negative_amount(self):
|
||||
'Test payment term with negative amount'
|
||||
pool = Pool()
|
||||
PaymentTerm = pool.get('account.invoice.payment_term')
|
||||
|
||||
cu1 = create_currency('cu1')
|
||||
term, = PaymentTerm.create([{
|
||||
'name': '30 days, 1 month, 1 month + 15 days',
|
||||
'lines': [
|
||||
('create', [{
|
||||
'sequence': 0,
|
||||
'type': 'percent',
|
||||
'divisor': 4,
|
||||
'ratio': Decimal('.25'),
|
||||
'relativedeltas': [('create', [{
|
||||
'days': 30,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 1,
|
||||
'type': 'percent_on_total',
|
||||
'divisor': 4,
|
||||
'ratio': Decimal('.25'),
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 2,
|
||||
'type': 'fixed',
|
||||
'amount': Decimal('4.0'),
|
||||
'currency': cu1.id,
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 1,
|
||||
'days': 30,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}, {
|
||||
'sequence': 3,
|
||||
'type': 'remainder',
|
||||
'relativedeltas': [('create', [{
|
||||
'months': 2,
|
||||
'days': 30,
|
||||
'day': 15,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}])]
|
||||
}])
|
||||
terms = term.compute(Decimal('-10.00'), cu1,
|
||||
date=datetime.date(2011, 10, 1))
|
||||
self.assertListEqual(terms, [
|
||||
(datetime.date(2011, 10, 31), Decimal('-2.5')),
|
||||
(datetime.date(2011, 11, 1), Decimal('-2.5')),
|
||||
(datetime.date(2011, 12, 1), Decimal('-4.0')),
|
||||
(datetime.date(2012, 1, 14), Decimal('-1.0')),
|
||||
])
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/account_invoice/tests/test_scenario.py
Normal file
8
modules/account_invoice/tests/test_scenario.py
Normal file
@@ -0,0 +1,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.
|
||||
|
||||
from trytond.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
35
modules/account_invoice/tests/tools.py
Normal file
35
modules/account_invoice/tests/tools.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# 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 proteus import Model
|
||||
|
||||
__all__ = ['set_fiscalyear_invoice_sequences']
|
||||
|
||||
|
||||
def set_fiscalyear_invoice_sequences(fiscalyear, config=None):
|
||||
"Set invoice sequences to fiscalyear"
|
||||
SequenceStrict = Model.get('ir.sequence.strict', config=config)
|
||||
SequenceType = Model.get('ir.sequence.type', config=config)
|
||||
|
||||
sequence_type, = SequenceType.find([
|
||||
('name', '=', "Invoice"),
|
||||
], limit=1)
|
||||
invoice_seq = SequenceStrict(
|
||||
name=fiscalyear.name,
|
||||
sequence_type=sequence_type,
|
||||
company=fiscalyear.company)
|
||||
invoice_seq.save()
|
||||
seq, = fiscalyear.invoice_sequences
|
||||
seq.out_invoice_sequence = invoice_seq
|
||||
seq.in_invoice_sequence = invoice_seq
|
||||
seq.out_credit_note_sequence = invoice_seq
|
||||
seq.in_credit_note_sequence = invoice_seq
|
||||
return fiscalyear
|
||||
|
||||
|
||||
def create_payment_term(config=None):
|
||||
"Create a direct payment term"
|
||||
PaymentTerm = Model.get('account.invoice.payment_term', config=config)
|
||||
|
||||
payment_term = PaymentTerm(name='Direct')
|
||||
payment_term.lines.new(type='remainder')
|
||||
return payment_term
|
||||
72
modules/account_invoice/tryton.cfg
Normal file
72
modules/account_invoice/tryton.cfg
Normal file
@@ -0,0 +1,72 @@
|
||||
[tryton]
|
||||
version=7.8.2
|
||||
depends:
|
||||
account
|
||||
account_product
|
||||
company
|
||||
currency
|
||||
ir
|
||||
party
|
||||
product
|
||||
res
|
||||
xml:
|
||||
invoice.xml
|
||||
payment_term.xml
|
||||
party.xml
|
||||
account.xml
|
||||
company.xml
|
||||
message.xml
|
||||
|
||||
[register]
|
||||
model:
|
||||
payment_term.PaymentTerm
|
||||
payment_term.PaymentTermLine
|
||||
payment_term.PaymentTermLineRelativeDelta
|
||||
payment_term.TestPaymentTermView
|
||||
payment_term.TestPaymentTermViewResult
|
||||
invoice.Invoice
|
||||
invoice.InvoiceAdditionalMove
|
||||
invoice.AlternativePayee
|
||||
invoice.InvoicePaymentLine
|
||||
invoice.InvoiceLine
|
||||
invoice.InvoiceLineTax
|
||||
invoice.InvoiceTax
|
||||
invoice.InvoiceEdocumentStart
|
||||
invoice.InvoiceEdocumentResult
|
||||
invoice.PayInvoiceStart
|
||||
invoice.PayInvoiceAsk
|
||||
invoice.CreditInvoiceStart
|
||||
invoice.InvoiceReportRevision
|
||||
party.Address
|
||||
party.ContactMechanism
|
||||
party.Party
|
||||
party.PartyPaymentTerm
|
||||
account.Configuration
|
||||
account.ConfigurationDefaultPaymentTerm
|
||||
account.ConfigurationPaymentReference
|
||||
account.InvoiceSequence
|
||||
# Match pattern migration fallbacks to Fiscalyear values so Period
|
||||
# must be registered before Fiscalyear
|
||||
account.Period
|
||||
account.FiscalYear
|
||||
account.Move
|
||||
account.MoveLine
|
||||
account.Reconciliation
|
||||
invoice.PaymentMethod
|
||||
company.Company
|
||||
wizard:
|
||||
payment_term.TestPaymentTerm
|
||||
invoice.InvoiceEdocument
|
||||
invoice.PayInvoice
|
||||
invoice.CreditInvoice
|
||||
invoice.RescheduleLinesToPay
|
||||
invoice.DelegateLinesToPay
|
||||
invoice.RefreshInvoiceReport
|
||||
party.Replace
|
||||
party.Erase
|
||||
account.RenewFiscalYear
|
||||
account.RescheduleLines
|
||||
account.DelegateLines
|
||||
account.CancelMoves
|
||||
report:
|
||||
invoice.InvoiceReport
|
||||
11
modules/account_invoice/view/address_form.xml
Normal file
11
modules/account_invoice/view/address_form.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath
|
||||
expr="//group[@id='checkboxes']/field[@name='active']"
|
||||
position="after">
|
||||
<label name="invoice"/>
|
||||
<field name="invoice" xexpand="0" width="25"/>
|
||||
</xpath>
|
||||
</data>
|
||||
9
modules/account_invoice/view/address_tree.xml
Normal file
9
modules/account_invoice/view/address_tree.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath
|
||||
expr="/tree/field[@name='subdivision']" position="after">
|
||||
<field name="invoice" optional="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
9
modules/account_invoice/view/address_tree_sequence.xml
Normal file
9
modules/account_invoice/view/address_tree_sequence.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath
|
||||
expr="/tree/field[@name='subdivision']" position="after">
|
||||
<field name="invoice" optional="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
12
modules/account_invoice/view/company_form.xml
Normal file
12
modules/account_invoice/view/company_form.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//page[@id='accounting']" position="inside">
|
||||
<separator string="Invoice" colspan="4" id="invoice"/>
|
||||
<label name="purchase_taxes_expense"/>
|
||||
<field name="purchase_taxes_expense"/>
|
||||
<label name="cancel_invoice_out"/>
|
||||
<field name="cancel_invoice_out"/>
|
||||
</xpath>
|
||||
</data>
|
||||
15
modules/account_invoice/view/configuration_form.xml
Normal file
15
modules/account_invoice/view/configuration_form.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="//field[@name='default_account_payable']" position="after">
|
||||
<label name="default_customer_payment_term"/>
|
||||
<field name="default_customer_payment_term"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
<xpath expr="//separator[@id='invoice']" position="after">
|
||||
<label name="customer_payment_reference_number"/>
|
||||
<field name="customer_payment_reference_number"/>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</data>
|
||||
9
modules/account_invoice/view/contact_mechanism_form.xml
Normal file
9
modules/account_invoice/view/contact_mechanism_form.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/form/group[@id='checkboxes']/field[@name='active']" position="after">
|
||||
<label name="invoice"/>
|
||||
<field name="invoice" xexpand="0" width="25"/>
|
||||
</xpath>
|
||||
</data>
|
||||
8
modules/account_invoice/view/contact_mechanism_tree.xml
Normal file
8
modules/account_invoice/view/contact_mechanism_tree.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/tree/field[@name='party']" position="after">
|
||||
<field name="invoice" optional="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/tree/field[@name='party']" position="after">
|
||||
<field name="invoice" optional="1"/>
|
||||
</xpath>
|
||||
</data>
|
||||
15
modules/account_invoice/view/credit_start_form.xml
Normal file
15
modules/account_invoice/view/credit_start_form.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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. -->
|
||||
<form col="2">
|
||||
<image name="tryton-warning" xexpand="0" xfill="0"/>
|
||||
<group col="2" id="credit_refund" yalign="0.5">
|
||||
<label
|
||||
string="Are you sure to credit these/this invoice(s)?"
|
||||
id="credit" colspan="2"/>
|
||||
<label name="with_refund"/>
|
||||
<field name="with_refund"/>
|
||||
<label name="invoice_date"/>
|
||||
<field name="invoice_date"/>
|
||||
</group>
|
||||
</form>
|
||||
7
modules/account_invoice/view/edocument_result_form.xml
Normal file
7
modules/account_invoice/view/edocument_result_form.xml
Normal file
@@ -0,0 +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. -->
|
||||
<form col="2">
|
||||
<label name="file"/>
|
||||
<field name="file"/>
|
||||
</form>
|
||||
9
modules/account_invoice/view/edocument_start_form.xml
Normal file
9
modules/account_invoice/view/edocument_start_form.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="format"/>
|
||||
<field name="format"/>
|
||||
<label name="template"/>
|
||||
<field name="template"/>
|
||||
</form>
|
||||
9
modules/account_invoice/view/fiscalyear_form.xml
Normal file
9
modules/account_invoice/view/fiscalyear_form.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<data>
|
||||
<xpath expr="/form/notebook/page[@id='sequences']/field[@name='move_sequence']" position="after">
|
||||
<field name="invoice_sequences" colspan="4" mode="form,tree"
|
||||
view_ids="account_invoice.invoice_sequence_view_form,account_invoice.invoice_sequence_view_list_sequence"/>
|
||||
</xpath>
|
||||
</data>
|
||||
115
modules/account_invoice/view/invoice_form.xml
Normal file
115
modules/account_invoice/view/invoice_form.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?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. -->
|
||||
<form col="6" cursor="party">
|
||||
<label name="type"/>
|
||||
<field name="type"/>
|
||||
<label name="journal"/>
|
||||
<field name="journal" widget="selection"/>
|
||||
<label name="number"/>
|
||||
<field name="number"/>
|
||||
<label name="party"/>
|
||||
<field name="party"/>
|
||||
<label name="invoice_address"/>
|
||||
<field name="invoice_address"/>
|
||||
<label name="party_tax_identifier" string="Tax Identifier:"/>
|
||||
<field name="party_tax_identifier"/>
|
||||
<label name="description"/>
|
||||
<field name="description" colspan="3"/>
|
||||
<label name="reference"/>
|
||||
<field name="reference"/>
|
||||
<notebook colspan="6">
|
||||
<page string="Invoice" id="invoice" col="6">
|
||||
<label name="invoice_date"/>
|
||||
<field name="invoice_date"/>
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<field name="lines" colspan="6"
|
||||
view_ids="account_invoice.invoice_line_view_tree_sequence"/>
|
||||
<group col="2" colspan="6" id="taxes_amount_state">
|
||||
<field name="taxes"
|
||||
view_ids="account_invoice.invoice_tax_view_tree_sequence"/>
|
||||
<group col="4" colspan="1" id="amount_state" yfill="1" yalign="1">
|
||||
<group col="2" colspan="2" id="reconciled_state" yfill="1" yalign="1">
|
||||
<label name="reconciled"/>
|
||||
<field name="reconciled"/>
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group col="2" colspan="2" id="amount" yfill="1" yalign="1">
|
||||
<label name="untaxed_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="total_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="total_amount" xalign="1.0" xexpand="0"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info" id="info">
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="tax_identifier"/>
|
||||
<field name="tax_identifier"/>
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
<label name="accounting_date"/>
|
||||
<field name="accounting_date"/>
|
||||
<label name="validated_by"/>
|
||||
<field name="validated_by"/>
|
||||
<label name="posted_by"/>
|
||||
<field name="posted_by"/>
|
||||
<field name="alternative_payees" colspan="4"/>
|
||||
<label name="move"/>
|
||||
<field name="move"/>
|
||||
<label name="cancel_move"/>
|
||||
<field name="cancel_move"/>
|
||||
<field name="additional_moves" colspan="4"/>
|
||||
<separator name="comment" colspan="4"/>
|
||||
<field name="comment" colspan="4"/>
|
||||
</page>
|
||||
<page string="Payment" id="payment">
|
||||
<label name="payment_term_date"/>
|
||||
<field name="payment_term_date"/>
|
||||
<newline/>
|
||||
|
||||
<label name="supplier_payment_reference"/>
|
||||
<field name="supplier_payment_reference"/>
|
||||
<label name="supplier_payment_reference_type"/>
|
||||
<field name="supplier_payment_reference_type"/>
|
||||
|
||||
<label name="customer_payment_reference"/>
|
||||
<field name="customer_payment_reference"/>
|
||||
<newline/>
|
||||
|
||||
<label name="amount_to_pay_today"/>
|
||||
<field name="amount_to_pay_today"/>
|
||||
<label name="amount_to_pay"/>
|
||||
<field name="amount_to_pay"/>
|
||||
<field name="lines_to_pay" colspan="4" view_ids="account_invoice.move_line_view_list_to_pay"/>
|
||||
<group id="lines_to_pay_buttons" colspan="4" col="-1">
|
||||
<button name="reschedule_lines_to_pay"/>
|
||||
<button name="delegate_lines_to_pay"/>
|
||||
</group>
|
||||
<field name="payment_lines" colspan="4"
|
||||
view_ids="account_invoice.move_line_view_list_payment"/>
|
||||
<field name="reconciliation_lines" colspan="4" view_ids="account_invoice.move_line_view_list_payment"/>
|
||||
</page>
|
||||
<page name="invoice_report_revisions">
|
||||
<field name="invoice_report_revisions" colspan="4"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<label id="empty" colspan="3"/>
|
||||
<group col="-1" colspan="3" id="buttons">
|
||||
<button name="cancel" icon="tryton-cancel"/>
|
||||
<button name="draft" icon="tryton-back"/>
|
||||
<button name="validate_invoice" icon="tryton-forward"/>
|
||||
<button name="post" icon="tryton-ok"/>
|
||||
<button name="pay" icon="tryton-forward"/>
|
||||
<button name="process" icon="tryton-refresh"/>
|
||||
</group>
|
||||
<field name="party_lang" invisible="1" colspan="6"/>
|
||||
</form>
|
||||
47
modules/account_invoice/view/invoice_line_form.xml
Normal file
47
modules/account_invoice/view/invoice_line_form.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?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. -->
|
||||
<form cursor="product">
|
||||
<label name="invoice"/>
|
||||
<field name="invoice" colspan="3"/>
|
||||
<label name="type"/>
|
||||
<field name="type"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<notebook colspan="4">
|
||||
<page string="General" id="general">
|
||||
<label name="product"/>
|
||||
<field name="product"/>
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit"/>
|
||||
<label name="unit_price"/>
|
||||
<field name="unit_price"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount"/>
|
||||
<label name="taxes_date"/>
|
||||
<field name="taxes_date"/>
|
||||
<label name="taxes_deductible_rate"/>
|
||||
<group col="2" id="taxes_deductible_rate">
|
||||
<field name="taxes_deductible_rate" factor="100" xexpand="0"/>
|
||||
<label name="taxes_deductible_rate" string="%" xalign="0.0" xexpand="1"/>
|
||||
</group>
|
||||
<field name="taxes" colspan="4"/>
|
||||
|
||||
<label name="origin"/>
|
||||
<field name="origin" colspan="3"/>
|
||||
</page>
|
||||
<page name="description">
|
||||
<separator name="description" colspan="4"/>
|
||||
<field name="description" colspan="4"/>
|
||||
</page>
|
||||
<page string="Notes" id="notes">
|
||||
<separator name="note" colspan="4"/>
|
||||
<field name="note" colspan="4"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<field name="party_lang" invisible="1" colspan="4"/>
|
||||
</form>
|
||||
16
modules/account_invoice/view/invoice_line_tree.xml
Normal file
16
modules/account_invoice/view/invoice_line_tree.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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>
|
||||
<field name="invoice" expand="1"/>
|
||||
<field name="invoice_party" expand="1" optional="0"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<field name="summary" expand="1" optional="1"/>
|
||||
<field name="account" expand="1" optional="1"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="unit_price" optional="1"/>
|
||||
<field name="taxes" optional="1"/>
|
||||
<field name="amount"/>
|
||||
<field name="invoice_description" expand="1" optional="1"/>
|
||||
</tree>
|
||||
14
modules/account_invoice/view/invoice_line_tree_sequence.xml
Normal file
14
modules/account_invoice/view/invoice_line_tree_sequence.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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 sequence="sequence">
|
||||
<field name="invoice" expand="1"/>
|
||||
<field name="type" optional="1"/>
|
||||
<field name="product" expand="1" optional="0"/>
|
||||
<field name="summary" expand="1" optional="1"/>
|
||||
<field name="account" expand="1" optional="0"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="unit_price"/>
|
||||
<field name="taxes" optional="0"/>
|
||||
<field name="amount"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="date"/>
|
||||
<field name="date"/>
|
||||
<label name="invoice_report_cache"/>
|
||||
<field name="invoice_report_cache"/>
|
||||
</form>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?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>
|
||||
<field name="invoice" expand="2"/>
|
||||
<field name="date" widget="date"/>
|
||||
<field name="date" widget="time" string="Time"/>
|
||||
<field name="invoice_report_cache" widget="binary" expand="1"/>
|
||||
</tree>
|
||||
23
modules/account_invoice/view/invoice_sequence_form.xml
Normal file
23
modules/account_invoice/view/invoice_sequence_form.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="company"/>
|
||||
<field name="company"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<label name="fiscalyear"/>
|
||||
<field name="fiscalyear"/>
|
||||
<separator id="pattern" colspan="4"/>
|
||||
<label name="period"/>
|
||||
<field name="period"/>
|
||||
<separator id="sequences" colspan="4"/>
|
||||
<label name="in_invoice_sequence"/>
|
||||
<field name="in_invoice_sequence"/>
|
||||
<label name="in_credit_note_sequence"/>
|
||||
<field name="in_credit_note_sequence"/>
|
||||
<label name="out_invoice_sequence"/>
|
||||
<field name="out_invoice_sequence"/>
|
||||
<label name="out_credit_note_sequence"/>
|
||||
<field name="out_credit_note_sequence"/>
|
||||
</form>
|
||||
12
modules/account_invoice/view/invoice_sequence_list.xml
Normal file
12
modules/account_invoice/view/invoice_sequence_list.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
<field name="company"/>
|
||||
<field name="fiscalyear"/>
|
||||
<field name="period"/>
|
||||
<field name="in_invoice_sequence"/>
|
||||
<field name="in_credit_note_sequence"/>
|
||||
<field name="out_invoice_sequence"/>
|
||||
<field name="out_credit_note_sequence"/>
|
||||
</tree>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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 sequence="sequence">
|
||||
<field name="company"/>
|
||||
<field name="fiscalyear"/>
|
||||
<field name="period"/>
|
||||
<field name="in_invoice_sequence"/>
|
||||
<field name="in_credit_note_sequence"/>
|
||||
<field name="out_invoice_sequence"/>
|
||||
<field name="out_credit_note_sequence"/>
|
||||
</tree>
|
||||
23
modules/account_invoice/view/invoice_tax_form.xml
Normal file
23
modules/account_invoice/view/invoice_tax_form.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?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. -->
|
||||
<form>
|
||||
<label name="invoice"/>
|
||||
<field name="invoice" colspan="3"/>
|
||||
<label name="tax"/>
|
||||
<field name="tax" colspan="3"/>
|
||||
<label name="description"/>
|
||||
<field name="description"/>
|
||||
<label name="sequence"/>
|
||||
<field name="sequence"/>
|
||||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
<label name="manual"/>
|
||||
<field name="manual"/>
|
||||
<label name="base"/>
|
||||
<field name="base"/>
|
||||
<label name="amount"/>
|
||||
<field name="amount"/>
|
||||
<separator name="legal_notice" colspan="4"/>
|
||||
<field name="legal_notice" colspan="4"/>
|
||||
</form>
|
||||
12
modules/account_invoice/view/invoice_tax_tree.xml
Normal file
12
modules/account_invoice/view/invoice_tax_tree.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
<field name="invoice" expand="1"/>
|
||||
<field name="tax" expand="1"/>
|
||||
<field name="description" expand="1"/>
|
||||
<field name="account" expand="1"/>
|
||||
<field name="manual"/>
|
||||
<field name="base"/>
|
||||
<field name="amount"/>
|
||||
</tree>
|
||||
13
modules/account_invoice/view/invoice_tax_tree_sequence.xml
Normal file
13
modules/account_invoice/view/invoice_tax_tree_sequence.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?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 sequence="sequence">
|
||||
<field name="description" expand="1"/>
|
||||
<field name="base"/>
|
||||
<field name="amount"/>
|
||||
<field name="sequence" tree_invisible="1"/>
|
||||
<field name="manual" tree_invisible="1"/>
|
||||
<field name="account" tree_invisible="1"/>
|
||||
<field name="tax" tree_invisible="1"/>
|
||||
<field name="legal_notice" tree_invisible="1"/>
|
||||
</tree>
|
||||
21
modules/account_invoice/view/invoice_tree.xml
Normal file
21
modules/account_invoice/view/invoice_tree.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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>
|
||||
<field name="company" optional="1"/>
|
||||
<field name="type"/>
|
||||
<field name="number"/>
|
||||
<field name="reference"/>
|
||||
<field name="invoice_date" optional="0"/>
|
||||
<field name="party" expand="2" optional="0"/>
|
||||
<field name="untaxed_amount" optional="0"/>
|
||||
<field name="tax_amount" optional="0"/>
|
||||
<field name="total_amount" optional="0"/>
|
||||
<field name="state" optional="0"/>
|
||||
<field name="amount_to_pay_today" optional="0"/>
|
||||
<field name="description" expand="1" optional="1"/>
|
||||
<button name="cancel" tree_invisible="1"/>
|
||||
<button name="draft" tree_invisible="1"/>
|
||||
<button name="validate_invoice" tree_invisible="1"/>
|
||||
<button name="post" tree_invisible="1"/>
|
||||
</tree>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user