diff --git a/CHANGELOG b/CHANGELOG
index 7eb3be652d..09f147e159 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Add method to post invoices by batch
* Add payment term date in invoice
* Raise warning on invoice with maturity dates on the past
* Remove foreign key for sequence of Invoice
diff --git a/account.py b/account.py
index ecce3bb4c6..b5dd518bf7 100644
--- a/account.py
+++ b/account.py
@@ -5,13 +5,15 @@ from collections import OrderedDict
from sql import Literal
from sql.conditionals import Coalesce
-from trytond.model import (fields, ModelView, ModelSQL, MatchMixin,
+from trytond.i18n import gettext
+from trytond.model import (fields, ModelView, ModelSQL, Workflow, MatchMixin,
sequence_ordered)
from trytond.pyson import Eval, If, Bool
from trytond.pool import Pool, PoolMeta
from trytond.tools import grouped_slice
from trytond.transaction import Transaction
+from trytond.modules.account.exceptions import ClosePeriodError
from trytond.modules.company.model import CompanyValueMixin
@@ -136,6 +138,27 @@ class Period(metaclass=PoolMeta):
table.drop_column('in_invoice_sequence')
table.drop_column('in_credit_note_sequence')
+ @classmethod
+ @ModelView.button
+ @Workflow.transition('close')
+ 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()
+
class InvoiceSequence(sequence_ordered(), ModelSQL, ModelView, MatchMixin):
'Invoice Sequence'
diff --git a/invoice.py b/invoice.py
index 1797cbcd53..77992b99b6 100644
--- a/invoice.py
+++ b/invoice.py
@@ -244,8 +244,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
@classmethod
def __setup__(cls):
super(Invoice, cls).__setup__()
- cls._check_modify_exclude = ['state', 'payment_lines', 'cancel_move',
- 'invoice_report_cache', 'invoice_report_format', 'lines']
+ cls._check_modify_exclude = {
+ 'state', 'payment_lines', 'move', 'cancel_move',
+ 'invoice_report_cache', 'invoice_report_format', 'lines'}
cls._order = [
('number', 'DESC NULLS FIRST'),
('id', 'DESC'),
@@ -265,6 +266,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
('draft', 'validated'),
('validated', 'posted'),
('draft', 'posted'),
+ ('posted', 'posted'),
('posted', 'paid'),
('validated', 'draft'),
('paid', 'posted'),
@@ -302,8 +304,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
('invoice_date', '!=', None),
('type', '!=', 'in'),
],
- 'invisible': ~Eval('state').in_(['draft', 'validated']),
- 'depends': ['state'],
+ 'invisible': (~Eval('state').in_(['draft', 'validated'])
+ | ((Eval('state') == 'posted') & Bool(Eval('move')))),
+ 'depends': ['state', 'move'],
},
'pay': {
'invisible': Eval('state') != 'posted',
@@ -1200,7 +1203,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
actions = iter(args)
all_invoices = []
for invoices, values in zip(actions, actions):
- if set(values) - set(cls._check_modify_exclude):
+ if set(values) - cls._check_modify_exclude:
cls.check_modify(invoices)
all_invoices += invoices
update_tax = [i for i in all_invoices if i.state == 'draft']
@@ -1463,11 +1466,30 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
Move.save(moves)
cls.save(invoices_in)
+ @classmethod
+ @Workflow.transition('posted')
+ def post_batch(cls, invoices):
+ pool = Pool()
+ Date = pool.get('ir.date')
+ today = Date.today()
+ cls.set_number(invoices)
+ for invoice in invoices:
+ if not invoice.payment_term_date:
+ invoice.payment_term_date = today
+ cls.save(invoices)
+ with Transaction().set_context(_skip_warnings=True):
+ cls.__queue__._post(invoices)
+
@classmethod
@ModelView.button
@Workflow.transition('posted')
def post(cls, invoices):
- Move = Pool().get('account.move')
+ cls._post(invoices)
+
+ @classmethod
+ def _post(cls, invoices):
+ pool = Pool()
+ Move = pool.get('account.move')
cls.set_number(invoices)
moves = []
@@ -1528,7 +1550,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
delete_moves = []
to_save = []
for invoice in invoices:
- if invoice.move:
+ if invoice.move or invoice.number:
if invoice.move.state == 'draft':
delete_moves.append(invoice.move)
elif not invoice.cancel_move:
diff --git a/message.xml b/message.xml
index be673fabc3..46dbd96873 100644
--- a/message.xml
+++ b/message.xml
@@ -69,6 +69,9 @@ this repository contains the full copyright notices and license terms. -->
The invoice "%(invoice)s" generates a payment date "%(date)s" in the past.
+
+ To close the periods you must post the invoices "%(moves)s".
+