odoo-addon-stock-barcodes 15.0.2.0.0.7__py3-none-any.whl → 15.0.3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. odoo/addons/stock_barcodes/README.rst +1 -1
  2. odoo/addons/stock_barcodes/__manifest__.py +1 -1
  3. odoo/addons/stock_barcodes/i18n/it.po +5 -5
  4. odoo/addons/stock_barcodes/i18n/stock_barcodes.pot +107 -7
  5. odoo/addons/stock_barcodes/models/stock_barcodes_option.py +16 -0
  6. odoo/addons/stock_barcodes/models/stock_move_line.py +12 -2
  7. odoo/addons/stock_barcodes/models/stock_picking_type.py +10 -6
  8. odoo/addons/stock_barcodes/models/stock_quant.py +21 -2
  9. odoo/addons/stock_barcodes/static/description/index.html +1 -2
  10. odoo/addons/stock_barcodes/static/src/css/stock.scss +17 -0
  11. odoo/addons/stock_barcodes/static/src/js/basic_controller.js +10 -0
  12. odoo/addons/stock_barcodes/views/stock_barcodes_menu.xml +1 -0
  13. odoo/addons/stock_barcodes/views/stock_barcodes_option_view.xml +8 -0
  14. odoo/addons/stock_barcodes/views/stock_picking_views.xml +1 -0
  15. odoo/addons/stock_barcodes/wizard/stock_barcodes_read.py +116 -23
  16. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory.py +27 -11
  17. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml +35 -1
  18. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking.py +129 -37
  19. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +10 -1
  20. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo.py +2 -7
  21. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml +13 -1
  22. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_views.xml +57 -16
  23. {odoo_addon_stock_barcodes-15.0.2.0.0.7.dist-info → odoo_addon_stock_barcodes-15.0.3.0.0.dist-info}/METADATA +2 -2
  24. {odoo_addon_stock_barcodes-15.0.2.0.0.7.dist-info → odoo_addon_stock_barcodes-15.0.3.0.0.dist-info}/RECORD +26 -26
  25. {odoo_addon_stock_barcodes-15.0.2.0.0.7.dist-info → odoo_addon_stock_barcodes-15.0.3.0.0.dist-info}/WHEEL +1 -1
  26. {odoo_addon_stock_barcodes-15.0.2.0.0.7.dist-info → odoo_addon_stock_barcodes-15.0.3.0.0.dist-info}/top_level.txt +0 -0
@@ -92,6 +92,12 @@ class WizStockBarcodesRead(models.AbstractModel):
92
92
  )
93
93
  display_assign_serial = fields.Boolean(compute="_compute_display_assign_serial")
94
94
  keep_result_package = fields.Boolean()
95
+ total_product_uom_qty = fields.Float(
96
+ string="Product Demand", digits="Product Unit of Measure", store=False
97
+ )
98
+ total_product_qty_done = fields.Float(
99
+ string="Product Qty. Done", digits="Product Unit of Measure", store=False
100
+ )
95
101
 
96
102
  @api.depends("res_id")
97
103
  def _compute_action_ids(self):
@@ -149,6 +155,17 @@ class WizStockBarcodesRead(models.AbstractModel):
149
155
  if self.packaging_id:
150
156
  self.product_qty = self.packaging_qty * self.packaging_id.qty
151
157
 
158
+ @api.onchange(
159
+ "product_id",
160
+ "lot_id",
161
+ "package_id",
162
+ "result_package_id",
163
+ "packaging_qty",
164
+ "product_qty",
165
+ )
166
+ def onchange_visible_force_done(self):
167
+ self.visible_force_done = False
168
+
152
169
  def _set_messagge_info(self, message_type, message):
153
170
  """
154
171
  Set message type and message description.
@@ -191,7 +208,24 @@ class WizStockBarcodesRead(models.AbstractModel):
191
208
  )
192
209
  return False
193
210
  self.action_product_scaned_post(product)
194
- # self.action_done()
211
+ if (
212
+ self.option_group_id.fill_fields_from_lot
213
+ and self.location_id
214
+ and self.product_id
215
+ ):
216
+ quant_domain = [
217
+ ("location_id", "=", self.location_id.id),
218
+ ("product_id", "=", product.id),
219
+ ]
220
+ if self.lot_id:
221
+ quant_domain.append(("lot_id", "=", self.lot_id.id))
222
+ if self.package_id:
223
+ quant_domain.append(("package_id", "=", self.package_id.id))
224
+ if self.owner_id:
225
+ quant_domain.append(("owner_id", "=", self.owner_id.id))
226
+ quants = self.env["stock.quant"].search(quant_domain)
227
+ if quants:
228
+ self.set_info_from_quants(quants)
195
229
  return True
196
230
  return False
197
231
 
@@ -205,6 +239,7 @@ class WizStockBarcodesRead(models.AbstractModel):
205
239
  if self.option_group_id.fill_fields_from_lot:
206
240
  quant_domain = [
207
241
  ("lot_id.name", "=", self.barcode),
242
+ ("product_id", "=", self.product_id.id),
208
243
  ("quantity", ">", 0.0),
209
244
  ]
210
245
  if self.location_id:
@@ -255,14 +290,18 @@ class WizStockBarcodesRead(models.AbstractModel):
255
290
  ("package_id.name", "=", self.barcode),
256
291
  ("quantity", ">", 0.0),
257
292
  ]
258
- if self.location_id and self._name != "wiz.stock.barcodes.read.inventory":
293
+ if self.option_group_id.get_option_value("location_id", "forced"):
259
294
  quant_domain.append(("location_id", "=", self.location_id.id))
260
- else:
261
- quant_domain.append(("location_id.usage", "=", "internal"))
262
295
  if self.owner_id:
263
296
  quant_domain.append(("owner_id", "=", self.owner_id.id))
264
297
  quants = self.env["stock.quant"].search(quant_domain)
265
- if not quants:
298
+ internal_quants = quants.filtered(lambda q: q.location_id.usage == "internal")
299
+ if internal_quants:
300
+ quants = internal_quants
301
+ elif quants:
302
+ self = self.with_context(ignore_quant_location=True)
303
+ # self._set_messagge_info("more_match", _("Package located external location"))
304
+ else:
266
305
  # self._set_messagge_info("more_match", _("Package not fount or empty"))
267
306
  return False
268
307
  self.set_info_from_quants(quants)
@@ -282,21 +321,30 @@ class WizStockBarcodesRead(models.AbstractModel):
282
321
  """
283
322
  Fill wizard fields from stock quants
284
323
  """
324
+ if self.env.context.get("skip_set_info_from_quants"):
325
+ return
326
+ ignore_quant_location = self.env.context.get(
327
+ "ignore_quant_location", self.option_group_id.ignore_quant_location
328
+ )
285
329
  if len(quants) == 1:
286
330
  # All ok
287
331
  self.action_product_scaned_post(quants.product_id)
288
332
  self.package_id = quants.package_id
333
+ self.result_package_id = quants.package_id
289
334
  if quants.lot_id:
290
335
  self.action_lot_scaned_post(quants.lot_id)
291
336
  if quants.owner_id:
292
337
  self.owner_id = quants.owner_id
293
338
  # Review conditions
294
- if not self.location_id and self.option_group_id.code != "IN":
339
+ if (
340
+ not ignore_quant_location
341
+ and not self.option_group_id.get_option_value("location_id", "forced")
342
+ and self.option_group_id.code != "IN"
343
+ ):
295
344
  self.location_id = quants.location_id
296
- if not self.is_manual_qty and self.option_group_id.code not in [
297
- "OUT",
298
- "INV",
299
- ]:
345
+ if self.option_group_id.code != "OUT" and not self.env.context.get(
346
+ "skip_update_quantity_from_lot", False
347
+ ):
300
348
  self.product_qty = quants.quantity
301
349
  elif len(quants) > 1:
302
350
  # More than one record found with same barcode.
@@ -305,19 +353,20 @@ class WizStockBarcodesRead(models.AbstractModel):
305
353
  products = quants.mapped("product_id")
306
354
  if len(products) == 1:
307
355
  self.action_product_scaned_post(products[0])
308
- packages = quants.mapped("package_id")
309
- if len(packages) == 1:
310
- self.package_id = packages
356
+ package = quants[0].package_id
357
+ if not quants.filtered(lambda q: q.package_id != package):
358
+ self.package_id = package
311
359
  lots = quants.mapped("lot_id")
312
360
  if len(lots) == 1:
313
361
  self.action_lot_scaned_post(lots[0])
314
- owners = quants.mapped("owner_id")
315
- if len(owners) == 1:
316
- self.owner_id = owners
317
- locations = quants.mapped("location_id")
318
- if len(locations) == 1:
319
- if not self.location_id and self.option_group_id.code != "IN":
320
- self.location_id = locations
362
+ owner = quants[0].owner_id
363
+ if not quants.filtered(lambda q: q.owner_id != owner):
364
+ self.owner_id = owner
365
+ if not ignore_quant_location:
366
+ locations = quants.mapped("location_id")
367
+ if len(locations) == 1:
368
+ if not self.location_id and self.option_group_id.code != "IN":
369
+ self.location_id = locations
321
370
 
322
371
  def process_barcode_packaging_id(self):
323
372
  domain = self._barcode_domain(self.barcode)
@@ -388,11 +437,17 @@ class WizStockBarcodesRead(models.AbstractModel):
388
437
  self.product_id.tracking == "none" or self.auto_lot or self.lot_name
389
438
  ):
390
439
  continue
440
+ if self._option_required_hook(option):
441
+ continue
391
442
  self._set_messagge_info("info", option.name)
392
443
  self.action_show_step()
393
444
  return False
394
445
  return True
395
446
 
447
+ def _option_required_hook(self, option_required):
448
+ """Hook to evaluate is an option is required"""
449
+ return False
450
+
396
451
  def _scanned_location(self, barcode):
397
452
  location = self.env["stock.location"].search(self._barcode_domain(barcode))
398
453
  if location:
@@ -403,7 +458,8 @@ class WizStockBarcodesRead(models.AbstractModel):
403
458
  return False
404
459
 
405
460
  def _barcode_domain(self, barcode):
406
- return [("barcode", "=", barcode)]
461
+ field_name = self.env.context.get("barcode_domain_field", "barcode")
462
+ return [(field_name, "=", barcode)]
407
463
 
408
464
  def _clean_barcode_scanned(self, barcode):
409
465
  return barcode.rstrip()
@@ -436,7 +492,10 @@ class WizStockBarcodesRead(models.AbstractModel):
436
492
  result_ok = self.check_lot_contidion()
437
493
  if not result_ok:
438
494
  return False
439
- if not self.product_qty:
495
+ if (
496
+ not self.product_qty
497
+ and not self._name == "wiz.stock.barcodes.read.inventory"
498
+ ):
440
499
  self._set_messagge_info("info", _("Waiting quantities"))
441
500
  return False
442
501
  if (
@@ -480,6 +539,16 @@ class WizStockBarcodesRead(models.AbstractModel):
480
539
  def action_done(self):
481
540
  if not self.manual_entry and not self.product_qty and not self.is_manual_qty:
482
541
  self.product_qty = 1.0
542
+ limit_product_qty = float(
543
+ self.env["ir.config_parameter"]
544
+ .sudo()
545
+ .get_param("stock_barcodes.limit_product_qty", "999999")
546
+ )
547
+ if self.product_qty > limit_product_qty:
548
+ # HACK: Some times users scan a barcode into input element.
549
+ # At this time, to prevent this we check that the quantity be realistic.
550
+ self._set_messagge_info("more_match", _("The quantity is huge"))
551
+ return False
483
552
  if not self.check_done_conditions():
484
553
  return False
485
554
  if not self.env.context.get("_stock_barcodes_skip_read_log"):
@@ -517,7 +586,11 @@ class WizStockBarcodesRead(models.AbstractModel):
517
586
  self.set_product_qty()
518
587
 
519
588
  def set_product_qty(self):
520
- if self.manual_entry or self.is_manual_qty:
589
+ if (
590
+ self.manual_entry
591
+ or self.is_manual_qty
592
+ or self.option_group_id.get_option_value("product_qty", "filled_default")
593
+ ):
521
594
  return
522
595
  elif self.packaging_id:
523
596
  self.packaging_qty = 1.0
@@ -748,3 +821,23 @@ class WizStockBarcodesRead(models.AbstractModel):
748
821
 
749
822
  def action_keep_result_package(self):
750
823
  self.keep_result_package = not self.keep_result_package
824
+
825
+ def display_notification(
826
+ self, message, message_type="warning", title=False, sticky=True
827
+ ):
828
+ """Send notifications to web client
829
+ message_type:
830
+ [options.type='warning'] 'info', 'success', 'warning', 'danger' or ''
831
+ See web/static/src/legacy/js/core/service_mixins.js#L241 to implement more
832
+ options.
833
+ sticky: Permanent notification until user removes it
834
+ """
835
+ if self.option_group_id.display_notification:
836
+ message = {"message": message, "type": message_type, "sticky": sticky}
837
+ if title:
838
+ message["title"] = title
839
+ self.env["bus.bus"]._sendone(
840
+ "stock_barcodes-{}".format(self.ids[0]),
841
+ "stock_barcodes_notify-{}".format(self.ids[0]),
842
+ message,
843
+ )
@@ -17,18 +17,31 @@ class WizStockBarcodesReadInventory(models.TransientModel):
17
17
  inventory_quant_ids = fields.Many2many(
18
18
  comodel_name="stock.quant", compute="_compute_inventory_quant_ids"
19
19
  )
20
+ display_read_quant = fields.Boolean(string="Read items")
20
21
 
22
+ @api.depends("display_read_quant")
21
23
  def _compute_inventory_quant_ids(self):
22
24
  for wiz in self:
23
- quants = self.env["stock.quant"].search(
24
- [
25
- ("user_id", "=", self.env.user.id),
26
- "|",
27
- ("inventory_quantity_set", "=", True),
28
- ("inventory_date", "<=", fields.Date.context_today(self)),
29
- ],
30
- order="write_date DESC",
31
- )
25
+ domain = [
26
+ ("user_id", "=", self.env.user.id),
27
+ ("inventory_date", "<=", fields.Date.context_today(self)),
28
+ ]
29
+ if self.display_read_quant:
30
+ domain.append(("inventory_quantity_set", "=", True))
31
+ order = "write_date DESC"
32
+ else:
33
+ domain.append(("inventory_quantity_set", "=", False))
34
+ order = None
35
+ quants = self.env["stock.quant"].search(domain, order=order)
36
+ if order is None:
37
+ quants = quants.sorted(
38
+ lambda q: (
39
+ q.location_id.posx,
40
+ q.location_id.posy,
41
+ q.location_id.posz,
42
+ q.location_id.name,
43
+ )
44
+ )
32
45
  wiz.inventory_quant_ids = quants
33
46
 
34
47
  def _prepare_stock_quant_values(self):
@@ -48,10 +61,10 @@ class WizStockBarcodesReadInventory(models.TransientModel):
48
61
  "<=",
49
62
  fields.Date.context_today(self).strftime("%Y-%m-%d"),
50
63
  ),
51
- ("inventory_quantity_set", "=", True),
52
64
  ("product_id", "=", self.product_id.id),
53
65
  ("location_id", "=", self.location_id.id),
54
66
  ("lot_id", "=", self.lot_id.id),
67
+ ("package_id", "=", self.package_id.id),
55
68
  ]
56
69
 
57
70
  def _add_inventory_quant(self):
@@ -64,7 +77,10 @@ class WizStockBarcodesReadInventory(models.TransientModel):
64
77
  ):
65
78
  self._serial_tracking_message_fail()
66
79
  return False
67
- quant.inventory_quantity = self.product_qty
80
+ if self.option_group_id.accumulate_read_quantity:
81
+ quant.inventory_quantity += self.product_qty
82
+ else:
83
+ quant.inventory_quantity = self.product_qty
68
84
  else:
69
85
  if self.product_id.tracking == "serial" and self.product_qty != 1:
70
86
  self._serial_tracking_message_fail()
@@ -25,6 +25,11 @@
25
25
  />
26
26
  </div>
27
27
  </group>
28
+ <field name="location_id" position="attributes">
29
+ <attribute
30
+ name="domain"
31
+ >[('usage', 'in', ['internal', 'transit'])]</attribute>
32
+ </field>
28
33
  <group name="scan_fields" position="after">
29
34
  <group
30
35
  string="Inventory quants"
@@ -62,6 +67,14 @@
62
67
  groups="uom.group_uom"
63
68
  options="{'no_open': True}"
64
69
  />
70
+ <button
71
+ name="action_barcode_inventory_quant_edit"
72
+ type="object"
73
+ icon="fa-pencil"
74
+ class="btn mt0"
75
+ context="{'wiz_barcode_id': parent.id}"
76
+ >
77
+ </button>
65
78
  <button
66
79
  name="action_barcode_inventory_quant_unlink"
67
80
  type="object"
@@ -93,6 +106,18 @@
93
106
  </span>
94
107
  </div>
95
108
  <div class="col-3 text-right">
109
+ <button
110
+ name="action_barcode_inventory_quant_edit"
111
+ type="object"
112
+ class="btn mt0"
113
+ context="{'wiz_barcode_id': parent.id}"
114
+ >
115
+ <i
116
+ class="fa fa-pencil"
117
+ title="Edit inventory quantity"
118
+ style="font-size:1.5em"
119
+ />
120
+ </button>
96
121
  <button
97
122
  name="action_barcode_inventory_quant_unlink"
98
123
  type="object"
@@ -129,6 +154,15 @@
129
154
  </field>
130
155
  </group>
131
156
  </group>
157
+ <div name="manual_entry" position="after">
158
+ <div class="d-flex" name="display_read_quant">
159
+ <field
160
+ name="display_read_quant"
161
+ widget="FieldBarcodeBooleanToggle"
162
+ />
163
+ <label for="display_read_quant" />
164
+ </div>
165
+ </div>
132
166
  <button name="action_clean_values" position="before">
133
167
  <button
134
168
  name="apply_inventory"
@@ -138,7 +172,7 @@
138
172
  data-hotkey="7"
139
173
  groups="stock.group_stock_manager"
140
174
  >
141
- <span>Apply inventory</span>
175
+ <span>Apply</span>
142
176
  </button>
143
177
  </button>
144
178
  </field>