summaryrefslogtreecommitdiff
path: root/addons/account/wizard/account_move_reversal.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/account/wizard/account_move_reversal.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/account/wizard/account_move_reversal.py')
-rw-r--r--addons/account/wizard/account_move_reversal.py129
1 files changed, 129 insertions, 0 deletions
diff --git a/addons/account/wizard/account_move_reversal.py b/addons/account/wizard/account_move_reversal.py
new file mode 100644
index 00000000..7724ca44
--- /dev/null
+++ b/addons/account/wizard/account_move_reversal.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+from odoo import models, fields, api
+from odoo.tools.translate import _
+from odoo.exceptions import UserError
+
+
+class AccountMoveReversal(models.TransientModel):
+ """
+ Account move reversal wizard, it cancel an account move by reversing it.
+ """
+ _name = 'account.move.reversal'
+ _description = 'Account Move Reversal'
+ _check_company_auto = True
+
+ 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.')
+ journal_id = fields.Many2one('account.journal', string='Use Specific Journal', help='If empty, uses the journal of the journal entry to be reversed.', check_company=True)
+ company_id = fields.Many2one('res.company', required=True, readonly=True)
+
+ # computed fields
+ residual = fields.Monetary(compute="_compute_from_moves")
+ currency_id = fields.Many2one('res.currency', compute="_compute_from_moves")
+ move_type = fields.Char(compute="_compute_from_moves")
+
+ @api.model
+ def default_get(self, fields):
+ 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 any(move.state != "posted" for move in move_ids):
+ raise UserError(_('You can only reverse posted moves.'))
+ 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')
+ def _compute_from_moves(self):
+ for record in self:
+ move_ids = record.move_ids._origin
+ record.residual = len(move_ids) == 1 and move_ids.amount_residual or 0
+ record.currency_id = len(move_ids.currency_id) == 1 and move_ids.currency_id or False
+ 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
+ return {
+ 'ref': _('Reversal of: %(move_name)s, %(reason)s', move_name=move.name, reason=self.reason)
+ if self.reason
+ else _('Reversal of: %s', move.name),
+ 'date': reverse_date,
+ 'invoice_date': move.is_invoice(include_receipts=True) and (self.date or move.date) or False,
+ 'journal_id': self.journal_id and self.journal_id.id or move.journal_id.id,
+ 'invoice_payment_term_id': None,
+ 'invoice_user_id': move.invoice_user_id.id,
+ 'auto_post': True if reverse_date > fields.Date.context_today(self) else False,
+ }
+
+ def _reverse_moves_post_hook(self, moves):
+ # DEPRECATED: TO REMOVE IN MASTER
+ return
+
+ def reverse_moves(self):
+ self.ensure_one()
+ moves = self.move_ids
+
+ # Create default values.
+ default_values_list = []
+ for move in moves:
+ default_values_list.append(self._prepare_default_reversal(move))
+
+ batches = [
+ [self.env['account.move'], [], True], # Moves to be cancelled by the reverses.
+ [self.env['account.move'], [], False], # Others.
+ ]
+ for move, default_vals in zip(moves, default_values_list):
+ is_auto_post = bool(default_vals.get('auto_post'))
+ is_cancel_needed = not is_auto_post and self.refund_method in ('cancel', 'modify')
+ batch_index = 0 if is_cancel_needed else 1
+ batches[batch_index][0] |= move
+ batches[batch_index][1].append(default_vals)
+
+ # Handle reverse method.
+ 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)
+
+ if self.refund_method == '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])
+ new_moves = self.env['account.move'].create(moves_vals_list)
+
+ moves_to_redirect |= new_moves
+
+ self.new_move_ids = moves_to_redirect
+
+ # Create action.
+ action = {
+ 'name': _('Reverse Moves'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'account.move',
+ }
+ if len(moves_to_redirect) == 1:
+ action.update({
+ 'view_mode': 'form',
+ 'res_id': moves_to_redirect.id,
+ })
+ else:
+ action.update({
+ 'view_mode': 'tree,form',
+ 'domain': [('id', 'in', moves_to_redirect.ids)],
+ })
+ return action