Initial import from Docker volume

This commit is contained in:
root
2025-12-26 13:11:43 +00:00
commit 4998dc066a
13336 changed files with 1767801 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# 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.pool import Pool
from . import account, company, invoice, party, payment_term
def register():
Pool.register(
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.PayInvoiceStart,
invoice.PayInvoiceAsk,
invoice.CreditInvoiceStart,
invoice.InvoiceReportRevision,
party.Address,
party.ContactMechanism,
party.Party,
party.PartyPaymentTerm,
account.Configuration,
account.ConfigurationDefaultPaymentTerm,
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,
module='account_invoice', type_='model')
Pool.register(
payment_term.TestPaymentTerm,
invoice.PayInvoice,
invoice.CreditInvoice,
invoice.RescheduleLinesToPay,
invoice.DelegateLinesToPay,
invoice.RefreshInvoiceReport,
party.Replace,
party.Erase,
account.RenewFiscalYear,
account.RescheduleLines,
account.DelegateLines,
account.CancelMoves,
module='account_invoice', type_='wizard')
Pool.register(
invoice.InvoiceReport,
module='account_invoice', type_='report')

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,419 @@
# 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"))
@classmethod
def multivalue_model(cls, field):
pool = Pool()
if field in 'default_customer_payment_term':
return pool.get('account.configuration.default_payment_term')
return super().multivalue_model(field)
class ConfigurationDefaultPaymentTerm(ModelSQL, CompanyValueMixin):
"Account Configuration Default Payment Term"
__name__ = 'account.configuration.default_payment_term'
default_customer_payment_term = fields.Many2One(
'account.invoice.payment_term', "Default Customer Payment Term")
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):
'Invoice Sequence'
__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')),
('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')),
])
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')),
])
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')),
])
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')),
])
@classmethod
def __setup__(cls):
super(InvoiceSequence, cls).__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(Move, cls)._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')),
(),
),
],
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(MoveLine, cls).__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 create(cls, vlist):
Invoice = Pool().get('account.invoice')
transaction = Transaction()
context = transaction.context
reconciliations = super(Reconciliation, cls).create(vlist)
with transaction.set_context(
queue_batch=context.get('queue_batch', True)):
Invoice.__queue__.process(
list(_invoices_to_process(reconciliations)))
return reconciliations
@classmethod
def delete(cls, reconciliations):
Invoice = Pool().get('account.invoice')
transaction = Transaction()
context = transaction.context
invoices_to_process = _invoices_to_process(reconciliations)
super(Reconciliation, cls).delete(reconciliations)
with transaction.set_context(
queue_batch=context.get('queue_batch', True)):
Invoice.__queue__.process(list(invoices_to_process))
class RenewFiscalYear(metaclass=PoolMeta):
__name__ = 'account.fiscalyear.renew'
def fiscalyear_defaults(self):
defaults = super(RenewFiscalYear, self).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(RenewFiscalYear, self).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

4088
modules/account_invoice/invoice.py Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,512 @@
<?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="{'type': 'out'}" pyson="1"/>
<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="{'type': 'in'}" pyson="1"/>
<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.report" id="report_prepayment">
<field name="name">Prepayment</field>
<field name="model">account.invoice</field>
<field name="report_name">account.invoice</field>
<field name="report">account_invoice/prepayment.fodt</field>
<field name="single" eval="True"/>
</record>
<record model="ir.action.keyword" id="report_prepayment_keyword">
<field name="keyword">form_print</field>
<field name="model">account.invoice,-1</field>
<field name="action" ref="report_prepayment"/>
</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,101 @@
<?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_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_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">
<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,98 @@
# 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(ContactMechanism, cls).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(Party, cls).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):
"Party Payment Term"
__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,367 @@
# 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 import backend
from trytond.i18n import gettext
from trytond.model import (
DeactivableMixin, ModelSQL, ModelView, fields, sequence_ordered)
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.transaction import Transaction
from trytond.wizard import Button, StateView, Wizard
from .exceptions import PaymentTermComputeError, PaymentTermValidationError
class PaymentTerm(DeactivableMixin, ModelSQL, ModelView):
'Payment Term'
__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(PaymentTerm, cls).__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 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, purchase_line = None):
"""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, purchase_line)
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):
'Payment Term Line'
__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),
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__()
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 = Decimal(0)
self.divisor = Decimal(0)
@fields.depends('ratio')
def on_change_ratio(self):
if not self.ratio:
self.divisor = Decimal(0)
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 = Decimal(0)
else:
self.ratio = self.round(1 / self.divisor,
self.__class__.ratio.digits[1])
def get_date(self, date, purchase_line = None):
#find date based on trigger:
if purchase_line and self.trigger_event:
PurchaseLine = Pool().get('purchase.line')
purchase_line = PurchaseLine(purchase_line)
trigger_date = purchase_line.get_date(self.trigger_event)
if trigger_date:
date = trigger_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):
'Payment Term Line Relative Delta'
__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')
@classmethod
def __register__(cls, module_name):
transaction = Transaction()
cursor = transaction.connection.cursor()
pool = Pool()
Month = pool.get('ir.calendar.month')
Day = pool.get('ir.calendar.day')
sql_table = cls.__table__()
month = Month.__table__()
day = Day.__table__()
table_h = cls.__table_handler__(module_name)
# Migration from 5.0: use ir.calendar
migrate_calendar = False
if (backend.TableHandler.table_exist(cls._table)
and table_h.column_exist('month')
and table_h.column_exist('weekday')):
migrate_calendar = (
table_h.column_is_type('month', 'VARCHAR')
or table_h.column_is_type('weekday', 'VARCHAR'))
if migrate_calendar:
table_h.column_rename('month', '_temp_month')
table_h.column_rename('weekday', '_temp_weekday')
super(PaymentTermLineRelativeDelta, cls).__register__(module_name)
table_h = cls.__table_handler__(module_name)
# Migration from 5.0: use ir.calendar
if migrate_calendar:
update = transaction.connection.cursor()
cursor.execute(*month.select(month.id, month.index))
for month_id, index in cursor:
update.execute(*sql_table.update(
[sql_table.month], [month_id],
where=sql_table._temp_month == str(index)))
table_h.drop_column('_temp_month')
cursor.execute(*day.select(day.id, day.index))
for day_id, index in cursor:
update.execute(*sql_table.update(
[sql_table.weekday], [day_id],
where=sql_table._temp_weekday == str(index)))
table_h.drop_column('_temp_weekday')
@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):
'Test Payment Term'
__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):
'Test Payment Term'
__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:
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):
'Test Payment Term'
__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,125 @@
<?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>
</tryton>

File diff suppressed because it is too large Load Diff

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,105 @@
================================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
>>> 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 company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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 e:
... Warning(user=config.user, name=e.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,141 @@
====================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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,426 @@
================
Invoice 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, get_company
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_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(company, today))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
>>> period_ids = [p.id for p in fiscalyear.periods]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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')
>>> sequence_journal, = Sequence.find(
... [('sequence_type.name', '=', "Account Journal")], limit=1)
>>> journal_writeoff = Journal(name='Write-Off', type='write-off',
... sequence=sequence_journal)
>>> journal_writeoff.save()
>>> 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'
>>> 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.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')
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,202 @@
===================================
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, get_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')
>>> Warning = Model.get('res.user.warning')
Create company::
>>> currency = get_currency('USD')
>>> eur = get_currency('EUR')
>>> _ = create_company(currency=currency)
>>> company = get_company()
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(company, (today, tomorrow)))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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')
>>> 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 writeoff method::
>>> WriteOff = Model.get('account.move.reconcile.write_off')
>>> sequence_journal, = Sequence.find(
... [('sequence_type.name', '=', "Account Journal")])
>>> journal_writeoff = Journal(name='Write-Off', type='write-off',
... sequence=sequence_journal)
>>> 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:
... _, (key, *_) = warning.args
... raise
Traceback (most recent call last):
...
InvoiceTaxesWarning: ...
>>> Warning(user=config.user, name=key).save()
>>> 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,100 @@
=================================================
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')
>>> Configuration = Model.get('account.configuration')
>>> Invoice = Model.get('account.invoice')
>>> Party = Model.get('party.party')
>>> PaymentTerm = Model.get('account.invoice.payment_term')
Create company::
>>> currency = get_currency('USD')
>>> eur = get_currency('EUR')
>>> _ = create_company(currency=currency)
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')
Create chart of accounts::
>>> _ = create_chart()
>>> 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,99 @@
==============================================
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, get_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::
>>> currency = get_currency('USD')
>>> eur = get_currency('EUR')
>>> _ = create_company(currency=currency)
>>> company = get_company()
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(company, today))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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,119 @@
===============================================
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')
>>> 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')
Create company::
>>> currency = get_currency('USD')
>>> eur = get_currency('EUR')
>>> _ = create_company(currency=currency)
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')
Create chart of accounts::
>>> _ = create_chart()
>>> 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,141 @@
==================================
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, get_company
>>> from trytond.tests.tools import activate_modules, assertEqual
Activate modules::
>>> config = activate_modules('account_invoice')
>>> Invoice = Model.get('account.invoice')
>>> Journal = Model.get('account.journal')
>>> PaymentMethod = Model.get('account.invoice.payment.method')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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.0')
>>> party2.reload()
>>> party2.receivable
Decimal('10.00')
>>> party3.reload()
>>> party3.receivable
Decimal('0.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.0')
>>> party2.reload()
>>> party2.receivable
Decimal('0.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,92 @@
===========================
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, get_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_company()
>>> company = get_company()
Create fiscal years::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, past_year))
>>> fiscalyear.click('create_period')
>>> renew_fiscalyear = Wizard('account.fiscalyear.renew')
>>> renew_fiscalyear.execute('create_')
>>> next_fiscalyear, = renew_fiscalyear.actions[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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,154 @@
================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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,72 @@
=================
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, get_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_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, (today, tomorrow)))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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,87 @@
==================
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, get_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
>>> Invoice = Model.get('account.invoice')
>>> Party = Model.get('party.party')
>>> TaxCode = Model.get('account.tax.code')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, today))
>>> fiscalyear.click('create_period')
>>> period_ids = [p.id for p in fiscalyear.periods]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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,118 @@
============================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal years::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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 write-off method::
>>> WriteOff = Model.get('account.move.reconcile.write_off')
>>> sequence_journal, = Sequence.find(
... [('sequence_type.name', '=', "Account Journal")], limit=1)
>>> journal_writeoff = Journal(
... name='Write-Off', type='write-off', sequence=sequence_journal)
>>> 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,89 @@
================================
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, get_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
>>> 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 company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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,108 @@
=================================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
>>> Invoice = Model.get('account.invoice')
>>> Journal = Model.get('account.journal')
>>> PaymentMethod = Model.get('account.invoice.payment.method')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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,246 @@
=========================
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, get_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, today))
>>> fiscalyear.click('create_period')
>>> period_ids = [p.id for p in fiscalyear.periods]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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'
>>> 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,123 @@
==========================
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, get_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, today))
>>> fiscalyear.click('create_period')
>>> period = fiscalyear.periods[0]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
>>> 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,127 @@
======================
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, get_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
>>> 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 company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company, today))
>>> fiscalyear.click('create_period')
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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:
... _, (key, *_) = warning.args
... raise
Traceback (most recent call last):
...
InvoiceTaxesWarning: ...
>>> Warning(user=config.user, name=key).save()
>>> 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,108 @@
============================
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, get_company
>>> from trytond.tests.tools import activate_modules
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> fiscalyear = set_fiscalyear_invoice_sequences(
... create_fiscalyear(company))
>>> fiscalyear.click('create_period')
>>> period_ids = [p.id for p in fiscalyear.periods]
Create chart of accounts::
>>> _ = create_chart(company)
>>> accounts = get_accounts(company)
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,104 @@
=========================
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, get_company
>>> from trytond.tests.tools import activate_modules, assertEqual
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_invoice')
Create company::
>>> _ = create_company()
>>> company = get_company()
Create fiscal year::
>>> InvoiceSequence = Model.get('account.fiscalyear.invoice_sequence')
>>> fiscalyear = create_fiscalyear(company, 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.post_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.post_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.post_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.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()
fiscalyear.invoice_sequences = []
invoice_sequence = InvoiceSequence()
invoice_sequence.fiscalyear = fiscalyear
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
invoice_sequence.save()
return fiscalyear
class AccountInvoiceTestCase(
PartyCompanyCheckEraseMixin, 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,18 @@
[tryton]
version=7.2.4
depends:
account
account_product
company
currency
ir
party
product
res
xml:
invoice.xml
payment_term.xml
party.xml
account.xml
company.xml
message.xml

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,10 @@
<?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>
</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,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. -->
<form col="2">
<image name="tryton-warning" xexpand="0" xfill="0"/>
<group col="2" id="credit_refund">
<label
string="Are you sure to credit these/this invoice(s)?"
id="credit" colspan="2"
yalign="0.0" xalign="0.0" xexpand="1"/>
<label name="with_refund"/>
<field name="with_refund"/>
<label name="invoice_date"/>
<field name="invoice_date"/>
</group>
</form>

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="/form/notebook/page[@id='sequences']/field[@name='post_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,118 @@
<?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"/>
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
<label name="tax_amount" xalign="1.0" xexpand="1"/>
<field name="tax_amount" xalign="1.0" xexpand="0"/>
<label name="total_amount" xalign="1.0" xexpand="1"/>
<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="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>
<page string="Rate management" id="rate">
<label name="warning"/>
<field name="warning"/>
<newline/>
<label name="rate"/>
<field name="rate"/>
<newline/>
<label name="selection_rate"/>
<field name="selection_rate"/>
<newline/>
<label name="rate_date"/>
<field name="rate_date"/>
</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>

Some files were not shown because too many files have changed in this diff Show More