mirror of
https://github.com/NaN-tic/trytond-account_invoice_posted2draft.git
synced 2023-12-14 02:22:57 +01:00
parent
b22c6adfcf
commit
c1a4cca122
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
130
invoice.py
130
invoice.py
|
@ -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)
|
||||||
|
|
28
locale/ca.po
28
locale/ca.po
|
@ -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."
|
|
||||||
|
|
28
locale/es.po
28
locale/es.po
|
@ -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."
|
|
||||||
|
|
|
@ -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
40
move.py
|
@ -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)
|
|
34
payment.py
34
payment.py
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in a new issue