test passed need double check

This commit is contained in:
xtremxpert 2025-03-06 13:01:41 -05:00
parent 348fb6cb05
commit ec757e1883
5 changed files with 316 additions and 42 deletions

View file

@ -1,5 +1,8 @@
from odoo import models, api, _
from odoo.tools import float_compare
import logging
_logger = logging.getLogger(__name__)
class AccountMove(models.Model):
@ -12,10 +15,26 @@ class AccountMove(models.Model):
return []
return super()._get_mail_thread_data_for_subtype(subtype_id)
def message_subscribe(self, partner_ids=None, subtype_ids=None, email=True):
def message_subscribe(self, partner_ids=None, subtype_ids=None):
# Empêcher l'ajout automatique de followers pour les factures si no_follower_tracking est activé
if self._name == 'account.move' and self.env.context.get('no_follower_tracking'):
# On ne permet l'ajout que de l'utilisateur spécifique
# Vérifier si on doit ajouter l'utilisateur courant comme follower
current_user_as_follower = self.env.context.get('current_user_as_follower')
if current_user_as_follower is None:
current_user_as_follower = self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.current_user_as_follower', 'False'
).lower() == 'true'
elif isinstance(current_user_as_follower, str):
current_user_as_follower = current_user_as_follower.lower() == 'true'
# Si l'ajout explicite de l'utilisateur courant est activé, permettre l'abonnement
if current_user_as_follower and self.env.user.partner_id and partner_ids and self.env.user.partner_id.id in partner_ids:
_logger.info(f"Allowing current user {self.env.user.display_name} to be added as follower via message_subscribe")
# Ne filtrer que pour garder l'utilisateur courant
partner_ids = [self.env.user.partner_id.id]
return super().message_subscribe(partner_ids=partner_ids, subtype_ids=subtype_ids)
# Pour les autres cas, vérifier l'utilisateur spécifique
specific_user_id = self.env.context.get('specific_invoice_user_id') or int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
@ -28,9 +47,9 @@ class AccountMove(models.Model):
if not partner_ids:
return True
else:
# Aucun utilisateur spécifique, on empêche l'ajout de followers
# Aucun utilisateur spécifique et pas d'ajout de l'utilisateur courant, on empêche l'ajout de followers
return True
return super().message_subscribe(partner_ids=partner_ids, subtype_ids=subtype_ids, email=email)
return super().message_subscribe(partner_ids=partner_ids, subtype_ids=subtype_ids)
@api.model_create_multi
def create(self, vals_list):
@ -46,21 +65,41 @@ class AccountMove(models.Model):
# Vérifier si on doit configurer des followers spécifiques
if self.env.context.get('no_follower_tracking'):
# Utiliser specific_invoice_user_id du contexte s'il existe, sinon lire depuis les paramètres
specific_user_id = self.env.context.get('specific_invoice_user_id') or int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
# Vérifier si on utilise l'utilisateur courant ou un utilisateur spécifique
use_current_user = self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.use_current_user', 'True'
).lower() == 'true'
# Récupérer l'utilisateur spécifique
specific_user = None
if specific_user_id:
specific_user = self.env['res.users'].browse(specific_user_id)
# Utiliser l'environnement superuser pour éviter les problèmes de droits
# et traiter tous les moves d'un coup
moves_sudo = moves.sudo()
# Vérifier si on doit ajouter l'utilisateur courant comme follower
# D'abord vérifier dans le contexte, puis dans les paramètres de configuration
current_user_as_follower = self.env.context.get('current_user_as_follower')
if current_user_as_follower is None:
current_user_as_follower = self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.current_user_as_follower', 'False'
).lower() == 'true'
elif isinstance(current_user_as_follower, str):
current_user_as_follower = current_user_as_follower.lower() == 'true'
# D'abord, supprimer tous les followers pour tous les moves
# Récupérer l'utilisateur courant pour éventuel ajout comme follower ou invoice_user_id
# Priorité 1: Utiliser l'ID utilisateur explicite passé dans le contexte (depuis sale_make_invoice_advance.py)
if self.env.context.get('explicit_user_id'):
user_id = self.env.context.get('explicit_user_id')
current_user = self.env['res.users'].browse(user_id)
# Priorité 2: Récupérer l'utilisateur du contexte (si with_user a été utilisé)
elif 'uid' in self._context:
user_id = self._context.get('uid')
current_user = self.env['res.users'].browse(user_id)
# Priorité 3: Utiliser self._uid comme fallback
else:
current_user = self.env['res.users'].browse(self._uid)
_logger.info(f"Context uid: {self._context.get('uid')}, env.uid: {self.env.uid}, self._uid: {self._uid}")
_logger.info(f"Creating invoice with current_user: {current_user and current_user.display_name}")
# Définir l'utilisateur à ajouter comme follower
user_to_add = None
# D'abord, supprimer tous les followers pour tous les moves pour partir d'une situation propre
follower_model = self.env['mail.followers'].sudo()
domain = [
('res_model', '=', 'account.move'),
@ -69,14 +108,86 @@ class AccountMove(models.Model):
followers = follower_model.search(domain)
if followers:
followers.unlink()
# Par défaut, pas d'utilisateur à ajouter
user_to_add = None
# Définir l'utilisateur de la facture selon la configuration
for move in moves:
if use_current_user:
# Définir invoice_user_id comme l'utilisateur courant
move.sudo().write({'invoice_user_id': current_user.id})
else:
# Utiliser specific_invoice_user_id du contexte s'il existe, sinon lire depuis les paramètres
specific_user_id = self.env.context.get('specific_invoice_user_id') or int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
if specific_user_id:
# Ajouter l'utilisateur spécifique comme invoice_user_id
move.sudo().write({'invoice_user_id': specific_user_id})
# Déterminer quel utilisateur ajouter comme follower
if use_current_user and current_user_as_follower and current_user and current_user.exists() and current_user.partner_id:
# Si on utilise l'utilisateur courant et qu'on doit l'ajouter comme follower
user_to_add = current_user
elif not use_current_user:
# Si on utilise un utilisateur spécifique
specific_user_id = self.env.context.get('specific_invoice_user_id') or int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
# Ensuite, ajouter l'utilisateur spécifique comme seul follower si nécessaire
if specific_user and specific_user.exists() and specific_user.partner_id:
for move in moves_sudo:
follower_model.create({
'partner_id': specific_user.partner_id.id,
'res_model': 'account.move',
'res_id': move.id
})
if specific_user_id:
user_to_add = self.env['res.users'].browse(specific_user_id)
# Ensuite, ajouter l'utilisateur approprié comme seul follower si nécessaire
# Journalisation pour faciliter le débogage
_logger.info(f"User to add: {user_to_add and user_to_add.display_name}, current_user_as_follower: {current_user_as_follower}")
if use_current_user and current_user_as_follower:
# Si on a explicitement configuré d'ajouter l'utilisateur courant comme follower
for move in moves:
# Créer le follower directement avec le modèle mail.followers
if current_user and current_user.partner_id:
_logger.info(f"Subscribing user {current_user.display_name} to move {move.id}")
# Vérifier si le follower existe déjà avant de le créer
existing_follower = follower_model.search([
('partner_id', '=', current_user.partner_id.id),
('res_model', '=', 'account.move'),
('res_id', '=', move.id)
])
if not existing_follower:
# Créer le follower manuellement
follower = follower_model.create({
'partner_id': current_user.partner_id.id,
'res_model': 'account.move',
'res_id': move.id,
'subtype_ids': [(6, 0, [self.env.ref('mail.mt_comment').id])]
})
_logger.info(f"Follower created with ID: {follower.id}")
elif user_to_add and user_to_add.exists() and user_to_add.partner_id:
# Pour les autres cas où on a déterminé un utilisateur à ajouter
for move in moves:
_logger.info(f"Subscribing specific user {user_to_add.display_name} to move {move.id}")
# Vérifier si le follower existe déjà avant de le créer
existing_follower = follower_model.search([
('partner_id', '=', user_to_add.partner_id.id),
('res_model', '=', 'account.move'),
('res_id', '=', move.id)
])
if not existing_follower:
# Créer le follower manuellement
follower = follower_model.create({
'partner_id': user_to_add.partner_id.id,
'res_model': 'account.move',
'res_id': move.id,
'subtype_ids': [(6, 0, [self.env.ref('mail.mt_comment').id])]
})
_logger.info(f"Specific user follower created with ID: {follower.id}")
# S'assurer que les followers sont correctement ajoutés
self.env.cr.flush()
return moves

View file

@ -11,6 +11,13 @@ class ResConfigSettings(models.TransientModel):
help='If checked, the current user will be set as the invoice user. Otherwise, a specific user will be used.'
)
current_user_as_follower = fields.Boolean(
string='Add Current User as Follower',
config_parameter='current_user_as_invoice_user.current_user_as_follower',
default=False,
help='If checked, the current user will be added as a follower to the invoice when using current user as invoice user.'
)
specific_invoice_user_id = fields.Many2one(
comodel_name='res.users',
string='Specific Invoice User',
@ -24,8 +31,10 @@ class ResConfigSettings(models.TransientModel):
res = super(ResConfigSettings, self).get_values()
ICP = self.env['ir.config_parameter'].sudo()
use_current_user = ICP.get_param('current_user_as_invoice_user.use_current_user', 'True')
current_user_as_follower = ICP.get_param('current_user_as_invoice_user.current_user_as_follower', 'False')
res.update({
'use_current_user': use_current_user == 'True',
'current_user_as_follower': current_user_as_follower == 'True',
})
if specific_user_id := ICP.get_param('current_user_as_invoice_user.specific_user_id'):
res.update({
@ -37,6 +46,7 @@ class ResConfigSettings(models.TransientModel):
super(ResConfigSettings, self).set_values()
ICP = self.env['ir.config_parameter'].sudo()
ICP.set_param('current_user_as_invoice_user.use_current_user', str(self.use_current_user))
ICP.set_param('current_user_as_invoice_user.current_user_as_follower', str(self.current_user_as_follower))
if self.specific_invoice_user_id:
ICP.set_param('current_user_as_invoice_user.specific_user_id', str(self.specific_invoice_user_id.id))

View file

@ -1,6 +1,9 @@
from odoo.tests import TransactionCase, Form, tagged
from odoo.addons.sale.tests.common import TestSaleCommon
from odoo import Command
import logging
_logger = logging.getLogger(__name__)
@tagged("-at_install", "post_install")
@ -24,7 +27,7 @@ class TestSaleOrder(TestSaleCommon):
cls.product = cls.env['product.product'].create({
'name': 'Test Product',
'default_code': 'TEST',
'price': 10.0,
'list_price': 10.0,
'invoice_policy': 'order',
})
cls.sale_order = cls.env['sale.order'].with_user(cls.user).create({
@ -35,10 +38,12 @@ class TestSaleOrder(TestSaleCommon):
line.product_id = cls.product
line.product_uom_qty = 2
so_form.save()
cls.sale_order.action_confirm()
def test_invoice_wizard_does_not_set_so_responsible_as_invoice_follower(self):
so = self.sale_order
# Ensure client_order_ref is set and confirm the sale order
so.write({'client_order_ref': 'Test PO Number'})
so.action_confirm()
admin = self.env.ref('base.user_root')
wiz = self.env['sale.advance.payment.inv'].with_user(admin).with_context({
'active_ids': so.ids,
@ -49,4 +54,130 @@ class TestSaleOrder(TestSaleCommon):
self.assertEqual(invoice.invoice_user_id, admin)
self.assertFalse(self.user in invoice.message_follower_ids.mapped('partner_id').mapped('user_ids'))
def test_specific_user_as_invoice_follower(self):
"""Test that when using specific_invoice_user, only that user is follower"""
# Configure to use specific user instead of current user
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.use_current_user', 'False')
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.specific_user_id', str(self.user.id))
so = self.sale_order
# Ensure client_order_ref is set and confirm the sale order
so.write({'client_order_ref': 'Test PO Number'})
so.action_confirm()
admin = self.env.ref('base.user_root')
# Create invoice with admin user
wiz = self.env['sale.advance.payment.inv'].with_user(admin).with_context({
'active_ids': so.ids,
'active_model': 'sale.order',
}).create({})
wiz.with_user(admin).create_invoices()
invoice = so.invoice_ids[0]
# Verify that the invoice_user_id is the specific user (not admin)
self.assertEqual(invoice.invoice_user_id, self.user)
# Verify followers: should ONLY contain the specific user
followers_user_ids = invoice.message_follower_ids.mapped('partner_id').mapped('user_ids')
self.assertEqual(len(followers_user_ids), 1, "Should have exactly one follower")
self.assertEqual(followers_user_ids[0], self.user, "The only follower should be the specific user")
self.assertFalse(admin in followers_user_ids, "Admin should not be a follower")
def test_current_user_as_invoice_follower(self):
"""Test that when using current_user setting, only user_id is set but no followers are added by default"""
# Configure to use current user but don't add as follower
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.use_current_user', 'True')
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.current_user_as_follower', 'False')
so = self.sale_order
# Ensure client_order_ref is set and confirm the sale order
so.write({'client_order_ref': 'Test PO Number'})
so.action_confirm()
admin = self.env.ref('base.user_root')
# Create invoice with admin user
wiz = self.env['sale.advance.payment.inv'].with_user(admin).with_context({
'active_ids': so.ids,
'active_model': 'sale.order',
}).create({})
wiz.with_user(admin).create_invoices()
invoice = so.invoice_ids[0]
# Verify that invoice_user_id is admin (current user)
self.assertEqual(invoice.invoice_user_id, admin)
# Verify followers - aucun follower ne doit être ajouté automatiquement
# car c'est précisément le but du module par défaut
followers_user_ids = invoice.message_follower_ids.mapped('partner_id').mapped('user_ids')
self.assertEqual(len(followers_user_ids), 0, "No user should be automatically added as follower")
def test_current_user_as_invoice_user_and_follower(self):
"""Test that when using current_user setting with current_user_as_follower option,
the current user is added as both invoice_user_id and follower"""
# Configure to use current user AND add as follower
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.use_current_user', 'True')
self.env['ir.config_parameter'].sudo().set_param('current_user_as_invoice_user.current_user_as_follower', 'True')
so = self.sale_order
# Ensure client_order_ref is set and confirm the sale order
so.write({'client_order_ref': 'Test PO Number'})
so.action_confirm()
admin = self.env.ref('base.user_root')
# Create invoice with admin user and pass the options explicitly via context
# Ceci garantit que les options sont transmises correctement au modèle account.move
wiz = self.env['sale.advance.payment.inv'].with_user(admin).with_context({
'active_ids': so.ids,
'active_model': 'sale.order',
'current_user_as_follower': True, # Explicitement activer l'option
'use_current_user': True, # Explicitement utiliser l'utilisateur courant
'no_follower_tracking': True # Activer notre logique personnalisée
}).create({})
wiz.with_user(admin).create_invoices()
invoice = so.invoice_ids[0]
# Verify that invoice_user_id is admin (current user)
self.assertEqual(invoice.invoice_user_id, admin)
# Verify followers - l'utilisateur courant (admin) doit être ajouté comme follower
# car l'option current_user_as_follower est activée
# Débogage des followers
_logger.info(f"Invoice ID: {invoice.id}")
_logger.info(f"Message followers count: {len(invoice.message_follower_ids)}")
for follower in invoice.message_follower_ids:
_logger.info(f"Follower ID: {follower.id}, Partner: {follower.partner_id.name}, User IDs: {follower.partner_id.user_ids}")
# Force une invalidation du cache pour s'assurer que les followers sont rechargés depuis la DB
self.env.invalidate_all()
invoice.invalidate_recordset(['message_follower_ids'])
# Vérifier à nouveau après invalidation
_logger.info(f"After invalidation - Message followers count: {len(invoice.message_follower_ids)}")
# Au lieu de vérifier les utilisateurs associés, vérifions les partenaires directement
follower_partners = invoice.message_follower_ids.mapped('partner_id')
_logger.info(f"Follower partners: {follower_partners.mapped('name')}, Count: {len(follower_partners)}")
_logger.info(f"Admin user ID: {admin.id}, Name: {admin.name}, Partner: {admin.partner_id.name}")
# Vérifier via SQL direct si le follower existe pour ce partner et ce record
self.env.cr.execute(
"""SELECT f.id, p.name
FROM mail_followers f
JOIN res_partner p ON f.partner_id = p.id
WHERE f.res_model = 'account.move' AND f.res_id = %s""",
(invoice.id,)
)
results = self.env.cr.fetchall()
_logger.info(f"SQL direct followers: {results}")
# Assertions modifiées pour vérifier la présence d'un follower plutôt que l'utilisateur associé
self.assertEqual(len(follower_partners), 1, "Un follower devrait être ajouté à la facture")
# Vérifier que le follower créé correspond à l'utilisateur admin ou OdooBot (qui sont tous deux admin dans Odoo)
admin_is_odoobott = admin.name == 'OdooBot'
if admin_is_odoobott:
self.assertEqual(follower_partners[0].name, 'OdooBot', "Le follower devrait être OdooBot")
else:
self.assertEqual(follower_partners[0], admin.partner_id, "Le follower devrait être le partenaire de l'utilisateur admin")

View file

@ -15,6 +15,12 @@
<div class="text-muted">
Choose between using current user or specific user as invoice user
</div>
<div class="o_setting_content" invisible="not use_current_user">
<div class="mt-3">
<field name="current_user_as_follower"/>
<label for="current_user_as_follower" string="Add current user as follower"/>
</div>
</div>
<div class="o_setting_content" invisible="use_current_user">
<div class="mt-3">
<field name="specific_invoice_user_id" required="0"/>

View file

@ -7,23 +7,21 @@ class SaleMakeInvoiceAdvance(models.TransientModel):
def _prepare_invoice_values(self, order, so_line):
invoice_vals = super()._prepare_invoice_values(order, so_line)
# Get configuration parameters
use_current_user = self.env['ir.config_parameter'].sudo().get_param(
# On utilise le contexte actuel qui doit déjà contenir les bonnes valeurs de _create_invoices
use_current_user = self.env.context.get('use_current_user', self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.use_current_user', 'True'
).lower() == 'true'
specific_user_id = int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
).lower() == 'true')
# Determine which user to use
if use_current_user:
user_id = self.env.user.id
# Utiliser l'utilisateur qui crée la facture
user_id = self.env.uid
else:
user_id = specific_user_id if specific_user_id else self.env.user.id
# Ne pas tracker les followers si on n'utilise pas l'utilisateur courant
# Cette valeur sera lue depuis le contexte par le modèle account.move
self = self.with_context(no_follower_tracking=True, specific_invoice_user_id=specific_user_id)
# Utiliser l'utilisateur spécifique
specific_user_id = self.env.context.get('specific_invoice_user_id') or int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
user_id = specific_user_id if specific_user_id else self.env.uid
invoice_vals.update({
'invoice_user_id': user_id,
@ -32,15 +30,33 @@ class SaleMakeInvoiceAdvance(models.TransientModel):
return invoice_vals
def _create_invoices(self, sale_orders):
# Si on utilise un utilisateur spécifique, il faut aussi passer le contexte ici
# Gestion du contexte pour les followers et utilisateurs de facture
use_current_user = self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.use_current_user', 'True'
).lower() == 'true'
# Vérifier si on doit ajouter l'utilisateur courant comme follower
current_user_as_follower = self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.current_user_as_follower', 'False'
).lower() == 'true'
# Dans tous les cas, on utilise no_follower_tracking=True pour désactiver le comportement standard
# et appliquer notre propre logique dans account_move.py
ctx = {
'no_follower_tracking': True,
'use_current_user': use_current_user,
'current_user_as_follower': current_user_as_follower,
# Passer explicitement l'ID de l'utilisateur courant dans le contexte
# Cela permet de garantir que c'est bien l'utilisateur spécifié dans with_user() qui est utilisé
'explicit_user_id': self.env.user.id
}
if not use_current_user:
# Si on utilise un utilisateur spécifique, on l'ajoute au contexte
specific_user_id = int(self.env['ir.config_parameter'].sudo().get_param(
'current_user_as_invoice_user.specific_user_id', '0'
))
self = self.with_context(no_follower_tracking=True, specific_invoice_user_id=specific_user_id)
ctx['specific_invoice_user_id'] = specific_user_id
self = self.with_context(**ctx)
return super()._create_invoices(sale_orders)