mirror of
https://github.com/NaN-tic/trytond-account_invoice_posted2draft.git
synced 2023-12-14 02:22:57 +01:00
* Change the way that the invoices are setted to draft. With the possibilty to enter a new law that not allow to remove any account move, the invoice is setted to draft creating a compesantion move and associateting it to the invoice throwe the additiona_move field. Task: #047268 * Fix some bugs and improve code reducing lines. Task: #047268 * Remove commented line not needed. Tasks: #047268 * Remove Warning definition not used. Tasks: #047268 * Fix some text. Task: 047268 --------- Co-authored-by: Bernat Brunet <bernat@nan-tic.com>
This commit is contained in:
parent
dbbd311c55
commit
13a65d8beb
8 changed files with 197 additions and 114 deletions
|
@ -5,12 +5,14 @@ from trytond.pool import Pool
|
|||
from . import invoice
|
||||
from . import commission
|
||||
from . import payment
|
||||
from . import move
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
invoice.Invoice,
|
||||
invoice.Move,
|
||||
move.Move,
|
||||
move.Line,
|
||||
module='account_invoice_posted2draft', type_='model')
|
||||
Pool.register(
|
||||
payment.Invoice,
|
||||
|
|
|
@ -4,27 +4,43 @@
|
|||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.tools import grouped_slice
|
||||
|
||||
__all__ = ['Invoice']
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice'
|
||||
|
||||
def get_allow_draft(self, name):
|
||||
pool = Pool()
|
||||
Commission = pool.get('commission')
|
||||
|
||||
result = super().get_allow_draft(name)
|
||||
|
||||
invoiced = Commission.search([
|
||||
('origin.invoice', '=', self.id, 'account.invoice.line'),
|
||||
('invoice_line', '!=', None),
|
||||
])
|
||||
if invoiced:
|
||||
result = False
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def draft(cls, invoices):
|
||||
Commission = Pool().get('commission')
|
||||
pool = Pool()
|
||||
Commission = pool.get('commission')
|
||||
|
||||
to_delete = []
|
||||
for sub_invoices in grouped_slice(invoices):
|
||||
ids = [i.id for i in sub_invoices]
|
||||
commissions = Commission.search([
|
||||
to_delete = Commission.search([
|
||||
('origin.invoice', 'in', ids, 'account.invoice.line'),
|
||||
('invoice_line', '=', None),
|
||||
])
|
||||
if commissions:
|
||||
commissions_origin = Commission.search([
|
||||
('origin.id', 'in', [c.id for c in commissions], 'commission'),
|
||||
if to_delete:
|
||||
to_delete_origin = Commission.search([
|
||||
('origin.id', 'in',
|
||||
[x.id for x in to_delete], 'commission'),
|
||||
('invoice_line', '=', None),
|
||||
])
|
||||
if commissions_origin:
|
||||
commissions += commissions_origin
|
||||
Commission.delete(commissions)
|
||||
|
||||
if to_delete_origin:
|
||||
to_delete += to_delete_origin
|
||||
Commission.delete(to_delete)
|
||||
return super(Invoice, cls).draft(invoices)
|
||||
|
|
130
invoice.py
130
invoice.py
|
@ -1,8 +1,9 @@
|
|||
# This file is part account_invoice_posted2draft module for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
from trytond.pyson import Eval
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.model import fields
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.i18n import gettext
|
||||
from trytond.exceptions import UserError
|
||||
|
@ -11,14 +12,35 @@ from trytond.exceptions import UserError
|
|||
class Invoice(metaclass=PoolMeta):
|
||||
__name__ = 'account.invoice'
|
||||
|
||||
allow_draft = fields.Function(
|
||||
fields.Boolean("Allow Draft Invoice"), 'get_allow_draft')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Invoice, cls).__setup__()
|
||||
cls._check_modify_exclude.add('move')
|
||||
cls._transitions |= set((('posted', 'draft'),))
|
||||
cls._buttons['draft']['invisible'] = (
|
||||
Eval('state').in_(['draft', 'paid']) | (
|
||||
(Eval('state') == 'cancelled') & Eval('cancel_move')))
|
||||
cls._buttons['draft']['invisible'] = ~Eval('allow_draft', False)
|
||||
cls._buttons['draft']['depends'] += tuple(['allow_draft'])
|
||||
|
||||
def get_allow_draft(self, name):
|
||||
# when IN invoice is validate from scratch, the move is in 'draft'
|
||||
# state, so in this case could be draft in a "normal" way
|
||||
if (self.state == 'validated' and self.move
|
||||
and self.move.state != 'draft'):
|
||||
return False
|
||||
elif self.state == 'cancelled' and self.number is not None:
|
||||
return False
|
||||
elif self.state in {'paid', 'draft'}:
|
||||
return False
|
||||
elif self.state == 'posted':
|
||||
lines_to_pay = [l for l in self.lines_to_pay
|
||||
if not l.reconciliation]
|
||||
# Invoice already paid or partial paid, should not be possible
|
||||
# to change state to draft.
|
||||
if (not lines_to_pay
|
||||
or self.amount_to_pay != self.total_amount):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def draft(cls, invoices):
|
||||
|
@ -26,64 +48,54 @@ class Invoice(metaclass=PoolMeta):
|
|||
Move = pool.get('account.move')
|
||||
MoveLine = pool.get('account.move.line')
|
||||
JournalPeriod = pool.get('account.journal.period')
|
||||
Warning = pool.get('res.user.warning')
|
||||
|
||||
moves = []
|
||||
payment_lines = []
|
||||
move_lines = []
|
||||
to_draft = []
|
||||
to_save = []
|
||||
for invoice in invoices:
|
||||
if invoice.move:
|
||||
# check period is closed
|
||||
if invoice.move.period.state == 'close':
|
||||
raise UserError(gettext(
|
||||
'account_invoice_posted2draft.msg_draft_closed_period',
|
||||
invoice=invoice.rec_name,
|
||||
period=invoice.move.period.rec_name,
|
||||
))
|
||||
# check period and journal is closed
|
||||
journal_periods = JournalPeriod.search([
|
||||
('journal', '=', invoice.move.journal.id),
|
||||
('period', '=', invoice.move.period.id),
|
||||
], limit=1)
|
||||
if journal_periods:
|
||||
journal_period, = journal_periods
|
||||
if journal_period.state == 'close':
|
||||
raise UserError(gettext(
|
||||
'account_invoice_posted2draft.'
|
||||
'msg_modify_closed_journal_period',
|
||||
invoice=invoice.rec_name,
|
||||
journal_period=journal_period.rec_name))
|
||||
moves.append(invoice.move)
|
||||
if invoice.payment_lines:
|
||||
for payment_line in invoice.payment_lines:
|
||||
if payment_line.move and payment_line.move.lines:
|
||||
for lines in payment_line.move.lines:
|
||||
payment_lines.append(lines)
|
||||
if not invoice.allow_draft:
|
||||
continue
|
||||
|
||||
if moves:
|
||||
with Transaction().set_context(draft_invoices=True):
|
||||
Move.write(moves, {'state': 'draft'})
|
||||
# If the payment lines dont have a reconciliation, then the field
|
||||
# invoice_payment will be fill up, and when we try to draft an
|
||||
# invoice it will give us an error
|
||||
if payment_lines:
|
||||
MoveLine.write(payment_lines, {'invoice_payment':None})
|
||||
cls.write(invoices, {
|
||||
'invoice_report_format': None,
|
||||
'invoice_report_cache': None,
|
||||
})
|
||||
with Transaction().set_context(draft_invoices=True):
|
||||
return super(Invoice, cls).draft(invoices)
|
||||
move = invoice.move
|
||||
if move:
|
||||
if move.state == 'draft':
|
||||
to_draft.append(invoice)
|
||||
else:
|
||||
to_save.append(invoice)
|
||||
cancel_move = move.cancel(reversal=True)
|
||||
Move.post([cancel_move])
|
||||
moves.extend((invoice.move, cancel_move))
|
||||
invoice.move = None
|
||||
else:
|
||||
to_draft.append(invoice)
|
||||
if invoice.cancel_move:
|
||||
moves.append(invoice.cancel_move)
|
||||
invoice.cancel_move = None
|
||||
invoice.additional_moves += tuple(moves)
|
||||
|
||||
@classmethod
|
||||
def credit(cls, invoices, refund=False, **values):
|
||||
with Transaction().set_context(cancel_from_credit=True):
|
||||
return super().credit(invoices, refund, **values)
|
||||
# Only make the special steps for the invoices that came from 'posted'
|
||||
# state or 'validated', 'cancelled' with number, so the invoice have one
|
||||
# or more move associated.
|
||||
# The other possible invoices follow the standard workflow.
|
||||
if to_draft:
|
||||
super().draft(to_draft)
|
||||
if to_save:
|
||||
cls.save(to_save)
|
||||
with Transaction().set_context(invoice_posted2draft=True):
|
||||
super().draft(to_save)
|
||||
|
||||
for invoice in to_save:
|
||||
to_reconcile = []
|
||||
for move in invoice.additional_moves:
|
||||
for line in move.lines:
|
||||
if (not line.reconciliation
|
||||
and line.account == invoice.account):
|
||||
to_reconcile.append(line)
|
||||
if to_reconcile:
|
||||
MoveLine.reconcile(to_reconcile)
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'account.move'
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, *args, **kwargs):
|
||||
if Transaction().context.get('draft_invoices', False):
|
||||
return
|
||||
return super(Move, cls).check_modify(*args, **kwargs)
|
||||
# Remove links to lines which actually do not pay the invoice
|
||||
if to_save:
|
||||
cls._clean_payments(to_save)
|
||||
|
|
28
locale/ca.po
28
locale/ca.po
|
@ -2,22 +2,26 @@
|
|||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_cancel_invoice_with_number"
|
||||
msgid "You cannot cancel invoice \"%(invoice)s\" because it already has a number."
|
||||
msgstr "No podeu cancel·lar la factura \"%(invoice)s\" amb un número assignat."
|
||||
msgctxt "field:account.invoice,allow_draft:"
|
||||
msgid "Allow Draft Invoice"
|
||||
msgstr "Permet factura esborrany"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_draft_closed_period"
|
||||
msgid ""
|
||||
"You can not set to draft invoice \"%(invoice)s\" because period "
|
||||
"\"%(period)s\" is closed."
|
||||
"You cannot create an account compesantion move in the period %(period)s "
|
||||
"because is closed."
|
||||
msgstr ""
|
||||
"No es pot passar a esborrany la factura \"%(invoice)s\" perquè el període "
|
||||
"\"%(period)s\" està tancat."
|
||||
"No podeu crear un assentamet de compensació en el període %(period)s perquè "
|
||||
"està tancat."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_modify_closed_journal_period"
|
||||
msgctxt "model:ir.message,text:msg_invoice_in_payment"
|
||||
msgid ""
|
||||
"You can not set to draft invoice \"%(invoice)s\" on closed journal-period "
|
||||
"\"%(journal_period)s\"."
|
||||
"The invoice %(invoice)s could not be possible to draft, becasue it have one "
|
||||
"or more move lines in payments [IDs: %(payments)s]. This means that is "
|
||||
"possible that this payment will be in a payment group and this group upload "
|
||||
"on a Bank."
|
||||
msgstr ""
|
||||
"No es pot passar a esborrany la factura \"%(invoice)s\" perquè el diari-"
|
||||
"període \"%(journal_period)s\". està tancat."
|
||||
"La factura %(invoice)s no es pot passar a esborrany, perquè té un o més "
|
||||
"apunts relacionats a pagaments [ID: %(payments)s]. Això vol dir que és "
|
||||
"possible que aquest pagament estigui en una remesa bancària i aquesta estigui "
|
||||
"pujada ja al Banc."
|
||||
|
|
28
locale/es.po
28
locale/es.po
|
@ -2,22 +2,26 @@
|
|||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_cancel_invoice_with_number"
|
||||
msgid "You cannot cancel invoice \"%(invoice)s\" because it already has a number."
|
||||
msgstr "No puede cancelar la factura \"%(invoice)s\" con un número asignado."
|
||||
msgctxt "field:account.invoice,allow_draft:"
|
||||
msgid "Allow Draft Invoice"
|
||||
msgstr "Permitir factura borrador"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_draft_closed_period"
|
||||
msgid ""
|
||||
"You can not set to draft invoice \"%(invoice)s\" because period "
|
||||
"\"%(period)s\" is closed."
|
||||
"You cannot create an account compesantion move in the period %(period)s "
|
||||
"because is closed."
|
||||
msgstr ""
|
||||
"No se puede pasar a borrador la factura \"%(invoice)s\" porqué el período "
|
||||
"\"%(period)s\" está cerrado."
|
||||
"No puedes crear un asiento de compensación en el periodo %(period)s porque "
|
||||
"está cerrado."
|
||||
|
||||
msgctxt "model:ir.message,text:msg_modify_closed_journal_period"
|
||||
msgctxt "model:ir.message,text:msg_invoice_in_payment"
|
||||
msgid ""
|
||||
"You can not set to draft invoice \"%(invoice)s\" on closed journal-period "
|
||||
"\"%(journal_period)s\"."
|
||||
"The invoice %(invoice)s could not be possible to draft, becasue it have one "
|
||||
"or more move lines in payments [IDs: %(payments)s]. This means that is "
|
||||
"possible that this payment will be in a payment group and this group upload "
|
||||
"on a Bank."
|
||||
msgstr ""
|
||||
"No se puede pasar a borrador la factura \"%(invoice)s\" porqué el diario-"
|
||||
"período \"%(journal_period)s\" está cerrado."
|
||||
"La factura %(invoice)s no se puede pasar a borrador, porque tiene uno o más "
|
||||
"apuntes relacionados a pagos [ID: %(payments)s]. Esto quiere decir que és "
|
||||
"posible que estos pagos esten ya en una remesa bancárea y ésta esté subida "
|
||||
"al Banco."
|
||||
|
|
|
@ -4,13 +4,10 @@ this repository contains the full copyright notices and license terms. -->
|
|||
<tryton>
|
||||
<data grouped="1">
|
||||
<record model="ir.message" id="msg_draft_closed_period">
|
||||
<field name="text">You can not set to draft invoice "%(invoice)s" because period "%(period)s" is closed.</field>
|
||||
<field name="text">You cannot create an account compesantion move in the period %(period)s because is closed.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_cancel_invoice_with_number">
|
||||
<field name="text">You cannot cancel invoice "%(invoice)s" because it already has a number.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_modify_closed_journal_period">
|
||||
<field name="text">You can not set to draft invoice "%(invoice)s" on closed journal-period "%(journal_period)s".</field>
|
||||
<record model="ir.message" id="msg_invoice_in_payment">
|
||||
<field name="text">The invoice %(invoice)s could not be possible to draft, becasue it have one or more move lines in payments [IDs: %(payments)s]. This means that is possible that this payment will be in a payment group and this group upload on a Bank.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
40
move.py
Normal file
40
move.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# This file is part account_invoice_posted2draft module for Tryton.
|
||||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class Move(metaclass=PoolMeta):
|
||||
__name__ = 'account.move'
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, *args, **kwargs):
|
||||
# As now the moves related to an invoice are not delete when 'draft'
|
||||
# the invoice, is needed to modify some restricted fields when the
|
||||
# move is in post state
|
||||
if Transaction().context.get('invoice_posted2draft', False):
|
||||
return
|
||||
return super().check_modify(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def delete(cls, moves):
|
||||
# When invoice is set to 'draft', try to delete the move's associated
|
||||
# in 'move' and 'additional_move' fields. If these moves are posted
|
||||
# they cannot be deleted but keep them as history
|
||||
if Transaction().context.get('invoice_posted2draft', False):
|
||||
return
|
||||
super().delete(moves)
|
||||
|
||||
|
||||
class Line(metaclass=PoolMeta):
|
||||
__name__ = 'account.move.line'
|
||||
|
||||
@classmethod
|
||||
def check_modify(cls, lines, modified_fields=None):
|
||||
# As now the moves related to an invoice are not delete when 'draft'
|
||||
# the invoice, is needed to modify some restricted fields when the
|
||||
# move is in post state
|
||||
if Transaction().context.get('invoice_posted2draft', False):
|
||||
return
|
||||
return super().check_modify(lines, modified_fields)
|
34
payment.py
34
payment.py
|
@ -2,8 +2,8 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains
|
||||
# the full copyright notices and license terms.
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
|
||||
__all__ = ['Invoice']
|
||||
from trytond.i18n import gettext
|
||||
from trytond.exceptions import UserError
|
||||
|
||||
|
||||
class Invoice(metaclass=PoolMeta):
|
||||
|
@ -11,18 +11,26 @@ class Invoice(metaclass=PoolMeta):
|
|||
|
||||
@classmethod
|
||||
def draft(cls, invoices):
|
||||
Payment = Pool().get('account.payment')
|
||||
pool = Pool()
|
||||
Payment = pool.get('account.payment')
|
||||
|
||||
lines = []
|
||||
for invoice in invoices:
|
||||
moves = []
|
||||
if invoice.move:
|
||||
lines.extend([l.id for l in invoice.move.lines])
|
||||
if lines:
|
||||
payments = Payment.search([
|
||||
('line', 'in', lines),
|
||||
('state', '=', 'failed'),
|
||||
])
|
||||
if payments:
|
||||
Payment.write(payments, {'line': None})
|
||||
moves.append(invoice.move)
|
||||
if invoice.additional_moves:
|
||||
moves.extend(invoice.additional_moves)
|
||||
|
||||
return super(Invoice, cls).draft(invoices)
|
||||
if moves:
|
||||
lines = [l.id for m in moves for l in m.lines]
|
||||
if lines:
|
||||
payments = Payment.search([
|
||||
('line', 'in', lines),
|
||||
('state', '!=', 'failed'),
|
||||
])
|
||||
if payments:
|
||||
raise UserError(gettext('account_invoice_posted2draft'
|
||||
'.msg_invoice_in_payment',
|
||||
invoice=invoice.rec_name,
|
||||
payments=", ".join([p.id for p in payments])))
|
||||
return super().draft(invoices)
|
||||
|
|
Loading…
Reference in a new issue