[FIX] liste de prix : ne rien créer quand il en existe déjà une
`_activate_or_create_pricelists` ne compte pas une liste de prix partagée entre sociétés (company_id vide) comme appartenant à la société : elle voit une société « sans liste » et lui en fabrique une. Sur une base migrée qui n'a rien perdu, cela ajoute un doublon vide à côté de la liste existante. Elle interroge maintenant le détecteur `pricelist_missing` du même outil, qui distingue déjà les deux cas. Vérifié dans les deux sens sur copie jetable : liste présente, rien n'est créé ; aucune liste, la réparation en crée une. --- EN --- `_activate_or_create_pricelists` does not count a pricelist shared across companies (empty company_id) as belonging to the company: it sees a company "without a pricelist" and makes one. On a migrated database that lost nothing, that adds an empty duplicate beside the existing list. It now asks the same tool's `pricelist_missing` detector, which already tells the two cases apart. Verified both ways on a throwaway copy: a pricelist present, nothing is created; no pricelist, the repair creates one. Assisted-by: Claude Opus 5 (cherry picked from commit b5bd2e4e44e89bdba4d639672611dcb964265987)
This commit is contained in:
parent
2534d4d022
commit
ccbb2a8597
2 changed files with 62 additions and 1 deletions
|
|
@ -90,7 +90,15 @@ try:
|
|||
rapport["pricelist_group"] = bool(
|
||||
fonction and employe and fonction in employe.implied_ids
|
||||
)
|
||||
if not DRY and rapport["pricelist_group"]:
|
||||
# Et seulement s'il n'y en a AUCUNE. `_activate_or_create_pricelists`
|
||||
# ne compte pas une liste PARTAGÉE (company_id vide) comme
|
||||
# appartenant à la société : appelée alors qu'une liste existe, elle
|
||||
# en ajoute une seconde. Mesuré sur une migration où la liste avait
|
||||
# traversé les six paliers — la réparation a créé un doublon vide à
|
||||
# côté d'elle. Cet outil a son détecteur ; il le consulte.
|
||||
if not DRY and rapport["pricelist_group"] and not rapport[
|
||||
"pricelist_before"
|
||||
]:
|
||||
Societe._activate_or_create_pricelists()
|
||||
env.cr.commit()
|
||||
rapport["pricelist_after"] = Liste.search_count([])
|
||||
|
|
|
|||
|
|
@ -383,3 +383,56 @@ class TestAMissingPricelistOnlyCountsWhenTheFeatureIsOn(unittest.TestCase):
|
|||
sql = " ".join(self._controle()["sql"].split())
|
||||
self.assertIn("ir_module_module", sql)
|
||||
self.assertIn("to_regclass", sql)
|
||||
|
||||
|
||||
class TestTheRepairAsksItsOwnDetector(unittest.TestCase):
|
||||
"""Une réparation qui n'écoute pas son détecteur fabrique des doublons.
|
||||
|
||||
Mesuré sur une migration de bout en bout : la liste de prix avait
|
||||
traversé les six paliers, PARTAGÉE entre sociétés (company_id vide).
|
||||
`_activate_or_create_pricelists` ne compte pas une liste partagée
|
||||
comme appartenant à la société — elle en a donc créé une seconde,
|
||||
vide, à côté de celle du client.
|
||||
|
||||
Le détecteur `pricelist_missing`, lui, disait déjà « rien ne manque ».
|
||||
Il fallait que la réparation le lui demande.
|
||||
"""
|
||||
|
||||
def _source(self):
|
||||
from pathlib import Path
|
||||
|
||||
chemin = (
|
||||
Path(__file__).resolve().parent.parent
|
||||
/ "script"
|
||||
/ "odoo"
|
||||
/ "migration"
|
||||
/ "restore_config_defaults.py"
|
||||
)
|
||||
return chemin.read_text(encoding="utf-8")
|
||||
|
||||
def _garde(self):
|
||||
"""Le texte qui précède l'APPEL, pas sa mention dans la docstring.
|
||||
|
||||
`index` trouvait la PREMIÈRE occurrence — celle de l'en-tête du
|
||||
module, qui explique justement ce que fait cette méthode. Le test
|
||||
s'évaluait alors sur un extrait de prose et échouait. `rindex`
|
||||
prend la dernière, qui est l'appel.
|
||||
"""
|
||||
source = self._source()
|
||||
debut = source.rindex("_activate_or_create_pricelists()")
|
||||
return source[max(0, debut - 400) : debut]
|
||||
|
||||
def test_the_repair_is_gated_on_an_empty_count(self):
|
||||
self.assertIn("pricelist_before", self._garde())
|
||||
|
||||
def test_the_feature_condition_is_still_there(self):
|
||||
self.assertIn("pricelist_group", self._garde())
|
||||
|
||||
def test_the_feature_is_read_from_the_implication(self):
|
||||
"""Et non de l'appartenance de celui qui exécute."""
|
||||
source = self._source()
|
||||
self.assertIn("implied_ids", source)
|
||||
self.assertNotIn(
|
||||
'env.user.has_group(\n "product.group_product_pricelist"',
|
||||
source,
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue