|
3 | 3 |
|
4 | 4 | import base64
|
5 | 5 | import io
|
| 6 | +import itertools |
6 | 7 | import logging
|
| 8 | +from collections import defaultdict |
7 | 9 |
|
8 | 10 | import requests
|
9 | 11 | from PIL import Image, ImageOps
|
@@ -86,9 +88,39 @@ class PrintingLabelZpl2(models.Model):
|
86 | 88 | labelary_height = fields.Float(string="Height in mm", default=70)
|
87 | 89 |
|
88 | 90 | @api.constrains("component_ids")
|
89 |
| - def _check_component_ids(self): |
90 |
| - if self._has_cycle("component_ids"): |
91 |
| - raise ValidationError(self.env._("You can not create recursive labels.")) |
| 91 | + def check_recursion(self): |
| 92 | + cr = self._cr |
| 93 | + self.flush_recordset(["component_ids"]) |
| 94 | + query = ( |
| 95 | + 'SELECT "{}", "{}" FROM "{}" ' |
| 96 | + 'WHERE "{}" IN %s AND "{}" IS NOT NULL'.format( |
| 97 | + "label_id", |
| 98 | + "sublabel_id", |
| 99 | + "printing_label_zpl2_component", |
| 100 | + "label_id", |
| 101 | + "sublabel_id", |
| 102 | + ) |
| 103 | + ) |
| 104 | + |
| 105 | + succs = defaultdict(set) # transitive closure of successors |
| 106 | + preds = defaultdict(set) # transitive closure of predecessors |
| 107 | + todo, done = set(self.ids), set() |
| 108 | + while todo: |
| 109 | + cr.execute(query, [tuple(todo)]) # pylint: disable=E8103 |
| 110 | + done.update(todo) |
| 111 | + todo.clear() |
| 112 | + for id1, id2 in cr.fetchall(): |
| 113 | + for x, y in itertools.product( |
| 114 | + [id1] + list(preds[id1]), [id2] + list(succs[id2]) |
| 115 | + ): |
| 116 | + if x == y: |
| 117 | + raise ValidationError( |
| 118 | + self.env._("You can not create recursive labels.") |
| 119 | + ) |
| 120 | + succs[x].add(y) |
| 121 | + preds[y].add(x) |
| 122 | + if id2 not in done: |
| 123 | + todo.add(id2) |
92 | 124 |
|
93 | 125 | def _get_component_data(self, record, component, eval_args):
|
94 | 126 | if component.data_autofill:
|
|
0 commit comments