[MIG] account_usability_akretion to v18
Move code from account_usability_akretion to base_usability_akretion
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
# Copyright 2015-2022 Akretion (http://www.akretion.com)
|
||||
# Copyright 2015-2024 Akretion France (https://www.akretion.com)
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import timedelta
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import float_is_zero
|
||||
from odoo.tools.misc import format_date
|
||||
from odoo.tools.safe_eval import safe_eval, time
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -19,9 +16,6 @@ _logger = logging.getLogger(__name__)
|
||||
class AccountMove(models.Model):
|
||||
_inherit = 'account.move'
|
||||
|
||||
# By default, we can still modify "ref" when account move is posted
|
||||
# which seems a bit lazy for me...
|
||||
ref = fields.Char(states={'posted': [('readonly', True)]})
|
||||
invoice_date_due = fields.Date(tracking=True)
|
||||
invoice_payment_term_id = fields.Many2one(tracking=True)
|
||||
journal_id = fields.Many2one(tracking=True)
|
||||
@@ -38,36 +32,34 @@ class AccountMove(models.Model):
|
||||
compute="_compute_sales_dates",
|
||||
help="This information appear on invoice qweb report "
|
||||
"(you may use it for your own report)")
|
||||
# There is a native "blocked" field (bool) on account.move.line
|
||||
# We want to have that field on invoices to improve usability
|
||||
# while keeping compatibility with the standard Odoo datamodel
|
||||
blocked = fields.Boolean(
|
||||
compute="_compute_blocked",
|
||||
inverse="_inverse_blocked",
|
||||
store=True,
|
||||
string="Dispute",
|
||||
tracking=True,
|
||||
)
|
||||
# The native "blocked" field (bool) on account.move.line has been removed in v18
|
||||
# blocked = fields.Boolean(
|
||||
# compute="_compute_blocked",
|
||||
# inverse="_inverse_blocked",
|
||||
# store=True,
|
||||
# string="Dispute",
|
||||
# tracking=True,
|
||||
# )
|
||||
# Field search_account_id is just for search view
|
||||
search_account_id = fields.Many2one(related='line_ids.account_id')
|
||||
|
||||
@api.depends("line_ids", "line_ids.blocked")
|
||||
def _compute_blocked(self):
|
||||
for move in self:
|
||||
move.blocked = any(
|
||||
[
|
||||
l.blocked
|
||||
for l in move.line_ids
|
||||
if l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
]
|
||||
)
|
||||
# @api.depends("line_ids", "line_ids.blocked")
|
||||
# def _compute_blocked(self):
|
||||
# for move in self:
|
||||
# move.blocked = any(
|
||||
# [
|
||||
# l.blocked
|
||||
# for l in move.line_ids
|
||||
# if l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
# ]
|
||||
# )
|
||||
|
||||
def _inverse_blocked(self):
|
||||
for move in self:
|
||||
for line in move.line_ids.filtered(
|
||||
lambda l: l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
):
|
||||
line.blocked = move.blocked
|
||||
# def _inverse_blocked(self):
|
||||
# for move in self:
|
||||
# for line in move.line_ids.filtered(
|
||||
# lambda l: l.account_id.account_type in ("liability_payable", "asset_receivable")
|
||||
# ):
|
||||
# line.blocked = move.blocked
|
||||
|
||||
def _compute_has_discount(self):
|
||||
prec = self.env['decimal.precision'].precision_get('Discount')
|
||||
@@ -103,35 +95,6 @@ class AccountMove(models.Model):
|
||||
res = [('id', value and 'in' or 'not in', list(att_inv_ids))]
|
||||
return res
|
||||
|
||||
# when you have an invoice created from a lot of sale orders, the 'name'
|
||||
# field is very large, which makes the name_get() of that invoice very big
|
||||
# which screws-up the form view of that invoice because of the link at the
|
||||
# top of the screen
|
||||
# That's why we have to cut the name_get() when it's too long
|
||||
def name_get(self):
|
||||
old_res = super().name_get()
|
||||
res = []
|
||||
for old_re in old_res:
|
||||
name = old_re[1]
|
||||
if name and len(name) > 100:
|
||||
# nice cut
|
||||
name = '%s ...' % ', '.join(name.split(', ')[:3])
|
||||
# if not enough, hard cut
|
||||
if len(name) > 120:
|
||||
name = '%s ...' % old_re[1][:120]
|
||||
res.append((old_re[0], name))
|
||||
return res
|
||||
|
||||
def _reverse_moves(self, default_values_list=None, cancel=False):
|
||||
reverse_moves = super()._reverse_moves(
|
||||
default_values_list=default_values_list, cancel=cancel)
|
||||
# In the simple scenario 1 invoice -> 1 refund, we add a message in the chatter
|
||||
# of the invoice and in the chatter of the refund
|
||||
if len(self) == 1 and len(reverse_moves) == 1:
|
||||
self.message_post(body=_("A reverse journal entry <a href=# data-oe-model=account.move data-oe-id=%d>%s</a> has been generated.") % (reverse_moves.id, reverse_moves.display_name))
|
||||
reverse_moves.message_post(body=_("This journal entry has been generated as the reverse of <a href=# data-oe-model=account.move data-oe-id=%d>%s</a>.") % (self.id, self.display_name))
|
||||
return reverse_moves
|
||||
|
||||
def delete_lines_qty_zero(self):
|
||||
lines = self.env['account.move.line'].search([
|
||||
('display_type', '=', 'product'),
|
||||
@@ -204,94 +167,27 @@ class AccountMove(models.Model):
|
||||
])
|
||||
move.suitable_journal_ids = self.env['account.journal'].search(domain)
|
||||
|
||||
def button_draft(self):
|
||||
# There is no more attachment by default on invoice reports...
|
||||
# TODO check what's the editor strategy on this
|
||||
# def button_draft(self):
|
||||
# Delete attached pdf invoice
|
||||
for move in self.filtered(lambda x: x.move_type in ('out_invoice', 'out_refund')):
|
||||
for report_xmlid in ('account.account_invoices', 'account.account_invoices_without_payment'):
|
||||
report = self.env.ref(report_xmlid)
|
||||
attach = report.retrieve_attachment(move)
|
||||
if attach:
|
||||
attach.unlink()
|
||||
super().button_draft()
|
||||
# for move in self.filtered(lambda x: x.move_type in ('out_invoice', 'out_refund')):
|
||||
# for report_xmlid in ('account.account_invoices', 'account.account_invoices_without_payment'):
|
||||
# report = self.env.ref(report_xmlid)
|
||||
# attach = report.retrieve_attachment(move)
|
||||
# if attach:
|
||||
# attach.unlink()
|
||||
# super().button_draft()
|
||||
|
||||
def _get_accounting_date(self, invoice_date, has_tax):
|
||||
def _get_accounting_date(self, invoice_date, has_tax, lock_dates=None):
|
||||
# On vendor bills/refunds, we want date = invoice_date unless
|
||||
# we have a company tax_lock_date and the invoice has taxes
|
||||
# and invoice_date <= tax_lock_date
|
||||
date = super()._get_accounting_date(invoice_date, has_tax)
|
||||
if self.is_purchase_document(include_receipts=True):
|
||||
tax_lock_date = self.company_id.tax_lock_date
|
||||
if invoice_date and tax_lock_date and has_tax and invoice_date <= tax_lock_date:
|
||||
invoice_date = tax_lock_date + timedelta(days=1)
|
||||
date = invoice_date
|
||||
date = super()._get_accounting_date(invoice_date, has_tax, lock_dates=lock_dates)
|
||||
lock_dates = lock_dates or self._get_violated_lock_dates(invoice_date, has_tax)
|
||||
if self.is_purchase_document(include_receipts=True) and invoice_date:
|
||||
if lock_dates:
|
||||
date = max([entry[0] for entry in lock_dates]) + timedelta(1)
|
||||
else:
|
||||
date = invoice_date
|
||||
return date
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = 'account.move.line'
|
||||
# Native order:
|
||||
# _order = "date desc, move_name desc, id"
|
||||
# Problem: when you manually create a journal entry, the
|
||||
# order of the lines is inverted when you save ! It is quite annoying for
|
||||
# the user...
|
||||
_order = "date desc, id asc"
|
||||
|
||||
# In the 'account' module, we have related stored field for:
|
||||
# name (move_name), date, ref, state (parent_state),
|
||||
# journal_id, company_id, payment_id, statement_line_id,
|
||||
account_reconcile = fields.Boolean(related='account_id.reconcile')
|
||||
full_reconcile_id = fields.Many2one(string='Full Reconcile')
|
||||
matched_debit_ids = fields.One2many(string='Partial Reconcile Debit')
|
||||
matched_credit_ids = fields.One2many(string='Partial Reconcile Credit')
|
||||
# for optional display in tree view
|
||||
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
|
||||
|
||||
def show_account_move_form(self):
|
||||
self.ensure_one()
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
'account.action_move_line_form')
|
||||
action.update({
|
||||
'res_id': self.move_id.id,
|
||||
'view_id': False,
|
||||
'views': False,
|
||||
'view_mode': 'form,tree',
|
||||
})
|
||||
return action
|
||||
|
||||
def update_matching_number(self):
|
||||
records = self.search([("matching_number", "=", "P")])
|
||||
_logger.info(f"Update partial reconcile number for {len(records)} lines")
|
||||
records._compute_matching_number()
|
||||
|
||||
# def _compute_matching_number(self):
|
||||
# TODO maybe it will be better to have the same maching_number for
|
||||
# all partial so it will be easier to group by
|
||||
# super()._compute_matching_number()
|
||||
# for record in self:
|
||||
# if record.matching_number == "P":
|
||||
# record.matching_number = ", ".join([
|
||||
# "a%d" % pr.id
|
||||
# for pr in record.matched_debit_ids + record.matched_credit_ids
|
||||
# ])
|
||||
|
||||
def _compute_name(self):
|
||||
# This is useful when you want to have the product code in a dedicated
|
||||
# column in your customer invoice report
|
||||
# The same ir.config_parameter is used in sale_usability,
|
||||
# purchase_usability and account_usability
|
||||
no_product_code_param = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'usability.line_name_no_product_code')
|
||||
if no_product_code_param and no_product_code_param == 'True':
|
||||
self = self.with_context(display_default_code=False)
|
||||
return super()._compute_name()
|
||||
|
||||
def reconcile(self):
|
||||
"""Explicit error message if unposted lines"""
|
||||
unposted_ids = self.filtered(lambda l: l.move_id.state != "posted")
|
||||
if unposted_ids:
|
||||
m = _("Please post the following entries before reconciliation :")
|
||||
sep = "\n - "
|
||||
unpost = sep.join([am.display_name for am in unposted_ids.move_id])
|
||||
raise UserError(m + sep + unpost)
|
||||
|
||||
return super().reconcile()
|
||||
|
||||
Reference in New Issue
Block a user