This reverts commit 13a65d8beb.
This commit is contained in:
Bernat Brunet 2023-12-13 15:28:54 +01:00
parent b22c6adfcf
commit c1a4cca122
8 changed files with 114 additions and 197 deletions

View file

@ -5,14 +5,12 @@ from trytond.pool import Pool
from . import invoice from . import invoice
from . import commission from . import commission
from . import payment from . import payment
from . import move
def register(): def register():
Pool.register( Pool.register(
invoice.Invoice, invoice.Invoice,
move.Move, invoice.Move,
move.Line,
module='account_invoice_posted2draft', type_='model') module='account_invoice_posted2draft', type_='model')
Pool.register( Pool.register(
payment.Invoice, payment.Invoice,

View file

@ -4,43 +4,27 @@
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.tools import grouped_slice from trytond.tools import grouped_slice
__all__ = ['Invoice']
class Invoice(metaclass=PoolMeta): class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice' __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 @classmethod
def draft(cls, invoices): def draft(cls, invoices):
pool = Pool() Commission = Pool().get('commission')
Commission = pool.get('commission')
to_delete = []
for sub_invoices in grouped_slice(invoices): for sub_invoices in grouped_slice(invoices):
ids = [i.id for i in sub_invoices] ids = [i.id for i in sub_invoices]
to_delete = Commission.search([ commissions = Commission.search([
('origin.invoice', 'in', ids, 'account.invoice.line'), ('origin.invoice', 'in', ids, 'account.invoice.line'),
('invoice_line', '=', None),
]) ])
if to_delete: if commissions:
to_delete_origin = Commission.search([ commissions_origin = Commission.search([
('origin.id', 'in', ('origin.id', 'in', [c.id for c in commissions], 'commission'),
[x.id for x in to_delete], 'commission'),
('invoice_line', '=', None),
]) ])
if to_delete_origin: if commissions_origin:
to_delete += to_delete_origin commissions += commissions_origin
Commission.delete(to_delete) Commission.delete(commissions)
return super(Invoice, cls).draft(invoices) return super(Invoice, cls).draft(invoices)

View file

@ -1,9 +1,8 @@
# This file is part account_invoice_posted2draft module for Tryton. # This file is part account_invoice_posted2draft module for Tryton.
# The COPYRIGHT file at the top level of this repository contains # The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms. # the full copyright notices and license terms.
from trytond.pool import Pool, PoolMeta
from trytond.model import fields
from trytond.pyson import Eval from trytond.pyson import Eval
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.i18n import gettext from trytond.i18n import gettext
from trytond.exceptions import UserError from trytond.exceptions import UserError
@ -12,35 +11,14 @@ from trytond.exceptions import UserError
class Invoice(metaclass=PoolMeta): class Invoice(metaclass=PoolMeta):
__name__ = 'account.invoice' __name__ = 'account.invoice'
allow_draft = fields.Function(
fields.Boolean("Allow Draft Invoice"), 'get_allow_draft')
@classmethod @classmethod
def __setup__(cls): def __setup__(cls):
super(Invoice, cls).__setup__() super(Invoice, cls).__setup__()
cls._check_modify_exclude.add('move')
cls._transitions |= set((('posted', 'draft'),)) cls._transitions |= set((('posted', 'draft'),))
cls._buttons['draft']['invisible'] = ~Eval('allow_draft', False) cls._buttons['draft']['invisible'] = (
cls._buttons['draft']['depends'] += tuple(['allow_draft']) Eval('state').in_(['draft', 'paid']) | (
(Eval('state') == 'cancelled') & Eval('cancel_move')))
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 @classmethod
def draft(cls, invoices): def draft(cls, invoices):
@ -48,54 +26,64 @@ class Invoice(metaclass=PoolMeta):
Move = pool.get('account.move') Move = pool.get('account.move')
MoveLine = pool.get('account.move.line') MoveLine = pool.get('account.move.line')
JournalPeriod = pool.get('account.journal.period') JournalPeriod = pool.get('account.journal.period')
Warning = pool.get('res.user.warning')
moves = [] moves = []
move_lines = [] payment_lines = []
to_draft = []
to_save = []
for invoice in invoices: for invoice in invoices:
if not invoice.allow_draft: if invoice.move:
continue # 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)
move = invoice.move if moves:
if move: with Transaction().set_context(draft_invoices=True):
if move.state == 'draft': Move.write(moves, {'state': 'draft'})
to_draft.append(invoice) # If the payment lines dont have a reconciliation, then the field
else: # invoice_payment will be fill up, and when we try to draft an
to_save.append(invoice) # invoice it will give us an error
cancel_move = move.cancel() if payment_lines:
Move.post([cancel_move]) MoveLine.write(payment_lines, {'invoice_payment':None})
moves.extend((invoice.move, cancel_move)) cls.write(invoices, {
invoice.move = None 'invoice_report_format': None,
else: 'invoice_report_cache': None,
to_draft.append(invoice) })
if invoice.cancel_move: with Transaction().set_context(draft_invoices=True):
moves.append(invoice.cancel_move) return super(Invoice, cls).draft(invoices)
invoice.cancel_move = None
invoice.additional_moves += tuple(moves)
# Only make the special steps for the invoices that came from 'posted' @classmethod
# state or 'validated', 'cancelled' with number, so the invoice have one def credit(cls, invoices, refund=False, **values):
# or more move associated. with Transaction().set_context(cancel_from_credit=True):
# The other possible invoices follow the standard workflow. return super().credit(invoices, refund, **values)
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)
# Remove links to lines which actually do not pay the invoice class Move(metaclass=PoolMeta):
if to_save: __name__ = 'account.move'
cls._clean_payments(to_save)
@classmethod
def check_modify(cls, *args, **kwargs):
if Transaction().context.get('draft_invoices', False):
return
return super(Move, cls).check_modify(*args, **kwargs)

View file

@ -2,26 +2,22 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n" msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:account.invoice,allow_draft:" msgctxt "model:ir.message,text:msg_cancel_invoice_with_number"
msgid "Allow Draft Invoice" msgid "You cannot cancel invoice \"%(invoice)s\" because it already has a number."
msgstr "Permet factura esborrany" msgstr "No podeu cancel·lar la factura \"%(invoice)s\" amb un número assignat."
msgctxt "model:ir.message,text:msg_draft_closed_period" msgctxt "model:ir.message,text:msg_draft_closed_period"
msgid "" msgid ""
"You cannot create an account compesantion move in the period %(period)s " "You can not set to draft invoice \"%(invoice)s\" because period "
"because is closed." "\"%(period)s\" is closed."
msgstr "" msgstr ""
"No podeu crear un assentamet de compensació en el període %(period)s perquè " "No es pot passar a esborrany la factura \"%(invoice)s\" perquè el període "
"està tancat." "\"%(period)s\" està tancat."
msgctxt "model:ir.message,text:msg_invoice_in_payment" msgctxt "model:ir.message,text:msg_modify_closed_journal_period"
msgid "" msgid ""
"The invoice %(invoice)s could not be possible to draft, becasue it have one " "You can not set to draft invoice \"%(invoice)s\" on closed journal-period "
"or more move lines in payments [IDs: %(payments)s]. This means that is " "\"%(journal_period)s\"."
"possible that this payment will be in a payment group and this group upload "
"on a Bank."
msgstr "" msgstr ""
"La factura %(invoice)s no es pot passar a esborrany, perquè té un o més " "No es pot passar a esborrany la factura \"%(invoice)s\" perquè el diari-"
"apunts relacionats a pagaments [ID: %(payments)s]. Això vol dir que és " "període \"%(journal_period)s\". està tancat."
"possible que aquest pagament estigui en una remesa bancària i aquesta estigui "
"pujada ja al Banc."

View file

@ -2,26 +2,22 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=utf-8\n" msgstr "Content-Type: text/plain; charset=utf-8\n"
msgctxt "field:account.invoice,allow_draft:" msgctxt "model:ir.message,text:msg_cancel_invoice_with_number"
msgid "Allow Draft Invoice" msgid "You cannot cancel invoice \"%(invoice)s\" because it already has a number."
msgstr "Permitir factura borrador" msgstr "No puede cancelar la factura \"%(invoice)s\" con un número asignado."
msgctxt "model:ir.message,text:msg_draft_closed_period" msgctxt "model:ir.message,text:msg_draft_closed_period"
msgid "" msgid ""
"You cannot create an account compesantion move in the period %(period)s " "You can not set to draft invoice \"%(invoice)s\" because period "
"because is closed." "\"%(period)s\" is closed."
msgstr "" msgstr ""
"No puedes crear un asiento de compensación en el periodo %(period)s porque " "No se puede pasar a borrador la factura \"%(invoice)s\" porqué el período "
"está cerrado." "\"%(period)s\" está cerrado."
msgctxt "model:ir.message,text:msg_invoice_in_payment" msgctxt "model:ir.message,text:msg_modify_closed_journal_period"
msgid "" msgid ""
"The invoice %(invoice)s could not be possible to draft, becasue it have one " "You can not set to draft invoice \"%(invoice)s\" on closed journal-period "
"or more move lines in payments [IDs: %(payments)s]. This means that is " "\"%(journal_period)s\"."
"possible that this payment will be in a payment group and this group upload "
"on a Bank."
msgstr "" msgstr ""
"La factura %(invoice)s no se puede pasar a borrador, porque tiene uno o más " "No se puede pasar a borrador la factura \"%(invoice)s\" porqué el diario-"
"apuntes relacionados a pagos [ID: %(payments)s]. Esto quiere decir que és " "período \"%(journal_period)s\" está cerrado."
"posible que estos pagos esten ya en una remesa bancárea y ésta esté subida "
"al Banco."

View file

@ -4,10 +4,13 @@ this repository contains the full copyright notices and license terms. -->
<tryton> <tryton>
<data grouped="1"> <data grouped="1">
<record model="ir.message" id="msg_draft_closed_period"> <record model="ir.message" id="msg_draft_closed_period">
<field name="text">You cannot create an account compesantion move in the period %(period)s because is closed.</field> <field name="text">You can not set to draft invoice "%(invoice)s" because period "%(period)s" is closed.</field>
</record> </record>
<record model="ir.message" id="msg_invoice_in_payment"> <record model="ir.message" id="msg_cancel_invoice_with_number">
<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> <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> </record>
</data> </data>
</tryton> </tryton>

40
move.py
View file

@ -1,40 +0,0 @@
# 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)

View file

@ -2,8 +2,8 @@
# The COPYRIGHT file at the top level of this repository contains # The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms. # the full copyright notices and license terms.
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.i18n import gettext
from trytond.exceptions import UserError __all__ = ['Invoice']
class Invoice(metaclass=PoolMeta): class Invoice(metaclass=PoolMeta):
@ -11,26 +11,18 @@ class Invoice(metaclass=PoolMeta):
@classmethod @classmethod
def draft(cls, invoices): def draft(cls, invoices):
pool = Pool() Payment = Pool().get('account.payment')
Payment = pool.get('account.payment')
lines = []
for invoice in invoices: for invoice in invoices:
moves = []
if invoice.move: if invoice.move:
moves.append(invoice.move) lines.extend([l.id for l in invoice.move.lines])
if invoice.additional_moves: if lines:
moves.extend(invoice.additional_moves) payments = Payment.search([
('line', 'in', lines),
('state', '=', 'failed'),
])
if payments:
Payment.write(payments, {'line': None})
if moves: return super(Invoice, cls).draft(invoices)
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)