mirror of
https://github.com/bringout/oca-ocb-accounting.git
synced 2026-04-24 09:02:01 +02:00
19.0 vanilla
This commit is contained in:
parent
ba20ce7443
commit
768b70e05e
2357 changed files with 1057103 additions and 712486 deletions
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
from odoo import Command, models, fields, api
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
|
@ -14,21 +14,11 @@ class AccountMoveReversal(models.TransientModel):
|
|||
|
||||
move_ids = fields.Many2many('account.move', 'account_move_reversal_move', 'reversal_id', 'move_id', domain=[('state', '=', 'posted')])
|
||||
new_move_ids = fields.Many2many('account.move', 'account_move_reversal_new_move', 'reversal_id', 'new_move_id')
|
||||
date_mode = fields.Selection(selection=[
|
||||
('custom', 'Specific'),
|
||||
('entry', 'Journal Entry Date')
|
||||
], required=True, default='custom')
|
||||
date = fields.Date(string='Reversal date', default=fields.Date.context_today)
|
||||
reason = fields.Char(string='Reason')
|
||||
refund_method = fields.Selection(selection=[
|
||||
('refund', 'Partial Refund'),
|
||||
('cancel', 'Full Refund'),
|
||||
('modify', 'Full refund and new draft invoice')
|
||||
], string='Credit Method', required=True,
|
||||
help='Choose how you want to credit this invoice. You cannot "modify" nor "cancel" if the invoice is already reconciled.')
|
||||
reason = fields.Char(string='Reason displayed on Credit Note')
|
||||
journal_id = fields.Many2one(
|
||||
comodel_name='account.journal',
|
||||
string='Use Specific Journal',
|
||||
string='Journal',
|
||||
required=True,
|
||||
compute='_compute_journal_id',
|
||||
readonly=False,
|
||||
|
|
@ -59,11 +49,13 @@ class AccountMoveReversal(models.TransientModel):
|
|||
for record in self:
|
||||
if record.move_ids:
|
||||
record.available_journal_ids = self.env['account.journal'].search([
|
||||
('company_id', '=', record.company_id.id),
|
||||
*self.env['account.journal']._check_company_domain(record.company_id),
|
||||
('type', 'in', record.move_ids.journal_id.mapped('type')),
|
||||
])
|
||||
else:
|
||||
record.available_journal_ids = self.env['account.journal'].search([('company_id', '=', record.company_id.id)])
|
||||
record.available_journal_ids = self.env['account.journal'].search([
|
||||
*self.env['account.journal']._check_company_domain(record.company_id),
|
||||
])
|
||||
|
||||
@api.constrains('journal_id', 'move_ids')
|
||||
def _check_journal_type(self):
|
||||
|
|
@ -76,14 +68,17 @@ class AccountMoveReversal(models.TransientModel):
|
|||
res = super(AccountMoveReversal, self).default_get(fields)
|
||||
move_ids = self.env['account.move'].browse(self.env.context['active_ids']) if self.env.context.get('active_model') == 'account.move' else self.env['account.move']
|
||||
|
||||
if len(move_ids.company_id) > 1:
|
||||
raise UserError(_("All selected moves for reversal must belong to the same company."))
|
||||
|
||||
if any(move.state != "posted" for move in move_ids):
|
||||
raise UserError(_('You can only reverse posted moves.'))
|
||||
raise UserError(_(
|
||||
'To reverse a journal entry, it has to be posted first.'
|
||||
))
|
||||
if 'company_id' in fields:
|
||||
res['company_id'] = move_ids.company_id.id or self.env.company.id
|
||||
if 'move_ids' in fields:
|
||||
res['move_ids'] = [(6, 0, move_ids.ids)]
|
||||
if 'refund_method' in fields:
|
||||
res['refund_method'] = (len(move_ids) > 1 or move_ids.move_type == 'entry') and 'cancel' or 'refund'
|
||||
return res
|
||||
|
||||
@api.depends('move_ids')
|
||||
|
|
@ -95,41 +90,32 @@ class AccountMoveReversal(models.TransientModel):
|
|||
record.move_type = move_ids.move_type if len(move_ids) == 1 else (any(move.move_type in ('in_invoice', 'out_invoice') for move in move_ids) and 'some_invoice' or False)
|
||||
|
||||
def _prepare_default_reversal(self, move):
|
||||
reverse_date = self.date if self.date_mode == 'custom' else move.date
|
||||
mixed_payment_term = move.invoice_payment_term_id.id if move.invoice_payment_term_id and move.company_id.early_pay_discount_computation == 'mixed' else None
|
||||
reverse_date = self.date
|
||||
mixed_payment_term = move.invoice_payment_term_id.id if move.invoice_payment_term_id.early_pay_discount_computation == 'mixed' else None
|
||||
lang = move.partner_id.lang or self.env.lang
|
||||
return {
|
||||
'ref': _('Reversal of: %(move_name)s, %(reason)s', move_name=move.name, reason=self.reason)
|
||||
'ref': self.with_context(lang=lang).env._('Reversal of: %(move_name)s, %(reason)s', move_name=move.name, reason=self.reason)
|
||||
if self.reason
|
||||
else _('Reversal of: %s', move.name),
|
||||
else self.with_context(lang=lang).env._('Reversal of: %s', move.name),
|
||||
'date': reverse_date,
|
||||
'invoice_date_due': reverse_date,
|
||||
'invoice_date': move.is_invoice(include_receipts=True) and reverse_date or False,
|
||||
'invoice_date': move.is_invoice(include_receipts=True) and (self.date or move.date) or False,
|
||||
'journal_id': self.journal_id.id,
|
||||
'invoice_payment_term_id': mixed_payment_term,
|
||||
'invoice_user_id': move.invoice_user_id.id,
|
||||
'auto_post': 'at_date' if reverse_date > fields.Date.context_today(self) else 'no',
|
||||
'invoice_origin': move.invoice_origin,
|
||||
}
|
||||
|
||||
def reverse_moves(self):
|
||||
def reverse_moves(self, is_modify=False):
|
||||
self.ensure_one()
|
||||
moves = self.move_ids
|
||||
|
||||
# Create default values.
|
||||
partners = moves.company_id.partner_id + moves.commercial_partner_id
|
||||
|
||||
bank_ids = self.env['res.partner.bank'].search([
|
||||
('partner_id', 'in', partners.ids),
|
||||
('company_id', 'in', moves.company_id.ids + [False]),
|
||||
], order='sequence DESC')
|
||||
partner_to_bank = {bank.partner_id: bank for bank in bank_ids}
|
||||
default_values_list = []
|
||||
for move in moves:
|
||||
if move.is_outbound():
|
||||
partner = move.company_id.partner_id
|
||||
else:
|
||||
partner = move.commercial_partner_id
|
||||
default_values_list.append({
|
||||
'partner_bank_id': partner_to_bank.get(partner, self.env['res.partner.bank']).id,
|
||||
'partner_bank_id': False, # Resets the partner_bank_id as we'll force its recomputation
|
||||
**self._prepare_default_reversal(move),
|
||||
})
|
||||
|
||||
|
|
@ -139,7 +125,7 @@ class AccountMoveReversal(models.TransientModel):
|
|||
]
|
||||
for move, default_vals in zip(moves, default_values_list):
|
||||
is_auto_post = default_vals.get('auto_post') != 'no'
|
||||
is_cancel_needed = not is_auto_post and self.refund_method in ('cancel', 'modify')
|
||||
is_cancel_needed = not is_auto_post and (is_modify or self.move_type == 'entry')
|
||||
batch_index = 0 if is_cancel_needed else 1
|
||||
batches[batch_index][0] |= move
|
||||
batches[batch_index][1].append(default_vals)
|
||||
|
|
@ -148,12 +134,19 @@ class AccountMoveReversal(models.TransientModel):
|
|||
moves_to_redirect = self.env['account.move']
|
||||
for moves, default_values_list, is_cancel_needed in batches:
|
||||
new_moves = moves._reverse_moves(default_values_list, cancel=is_cancel_needed)
|
||||
new_moves._compute_partner_bank_id()
|
||||
moves._message_log_batch(
|
||||
bodies={move.id: move.env._('This entry has been %s', reverse._get_html_link(title=move.env._("reversed"))) for move, reverse in zip(moves, new_moves)}
|
||||
)
|
||||
|
||||
if self.refund_method == 'modify':
|
||||
if is_modify:
|
||||
moves_vals_list = []
|
||||
for move in moves.with_context(include_business_fields=True):
|
||||
moves_vals_list.append(move.copy_data({'date': self.date if self.date_mode == 'custom' else move.date})[0])
|
||||
data = move.copy_data(self._modify_default_reverse_values(move))[0]
|
||||
data['line_ids'] = [line for line in data['line_ids'] if line[2]['display_type'] in ('product', 'line_section', 'line_subsection', 'line_note')]
|
||||
moves_vals_list.append(data)
|
||||
new_moves = self.env['account.move'].create(moves_vals_list)
|
||||
new_moves._compute_partner_bank_id()
|
||||
|
||||
moves_to_redirect |= new_moves
|
||||
|
||||
|
|
@ -173,9 +166,30 @@ class AccountMoveReversal(models.TransientModel):
|
|||
})
|
||||
else:
|
||||
action.update({
|
||||
'view_mode': 'tree,form',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', moves_to_redirect.ids)],
|
||||
})
|
||||
if len(set(moves_to_redirect.mapped('move_type'))) == 1:
|
||||
action['context'] = {'default_move_type': moves_to_redirect.mapped('move_type').pop()}
|
||||
return action
|
||||
|
||||
def refund_moves(self):
|
||||
return self.reverse_moves(is_modify=False)
|
||||
|
||||
def modify_moves(self):
|
||||
return self.reverse_moves(is_modify=True)
|
||||
|
||||
def _modify_default_reverse_values(self, origin_move):
|
||||
data = {
|
||||
'date': self.date
|
||||
}
|
||||
|
||||
# if has vendor attachment, keep it
|
||||
if origin_move.move_type.startswith('in_') and origin_move.message_main_attachment_id:
|
||||
new_main_attachment_id = origin_move.message_main_attachment_id.copy({'res_id': False}).id
|
||||
data.update({
|
||||
'message_main_attachment_id': new_main_attachment_id,
|
||||
'attachment_ids': [Command.link(new_main_attachment_id)],
|
||||
})
|
||||
|
||||
return data
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue