first commit

This commit is contained in:
root
2026-03-14 09:42:12 +00:00
commit 0adbd20c2c
10991 changed files with 1646955 additions and 0 deletions

View 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.

View 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()

View 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>

View 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

View 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>

View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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>

View 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))

View 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>

View 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")

View 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>

View 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']

View File

@@ -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'

View 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')]

View 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')

View File

@@ -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')

View File

@@ -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')

View File

@@ -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'

View File

@@ -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')

View File

@@ -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
[]

View File

@@ -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')

View 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')

View 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'

View File

@@ -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')

View 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')

View File

@@ -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'

View File

@@ -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

View 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

View File

@@ -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'

View File

@@ -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

View 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')

View 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)

View 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

View 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)

View 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

View 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

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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="date"/>
<field name="date"/>
<label name="invoice_report_cache"/>
<field name="invoice_report_cache"/>
</form>

View 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. -->
<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>

View 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>

View 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>

View 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 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>

View 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>

View 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>

View 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>

View 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