odoo-addon-rma 17.0.2.2.1__py3-none-any.whl → 18.0.1.0.0.3__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 (45) hide show
  1. odoo/addons/rma/README.rst +6 -6
  2. odoo/addons/rma/__manifest__.py +1 -1
  3. odoo/addons/rma/controllers/main.py +4 -7
  4. odoo/addons/rma/data/mail_data.xml +12 -13
  5. odoo/addons/rma/i18n/de.po +134 -350
  6. odoo/addons/rma/i18n/de_AT.po +65 -97
  7. odoo/addons/rma/i18n/es.po +212 -349
  8. odoo/addons/rma/i18n/fr.po +300 -222
  9. odoo/addons/rma/i18n/it.po +282 -454
  10. odoo/addons/rma/i18n/nl.po +65 -97
  11. odoo/addons/rma/i18n/pt.po +66 -242
  12. odoo/addons/rma/i18n/pt_BR.po +65 -239
  13. odoo/addons/rma/i18n/rma.pot +65 -96
  14. odoo/addons/rma/i18n/ro.po +79 -220
  15. odoo/addons/rma/i18n/zh_CN.po +65 -97
  16. odoo/addons/rma/models/account_move.py +2 -2
  17. odoo/addons/rma/models/res_company.py +2 -2
  18. odoo/addons/rma/models/res_config_settings.py +3 -0
  19. odoo/addons/rma/models/rma.py +40 -39
  20. odoo/addons/rma/models/rma_team.py +12 -13
  21. odoo/addons/rma/models/stock_move.py +2 -2
  22. odoo/addons/rma/models/stock_picking.py +0 -12
  23. odoo/addons/rma/models/stock_warehouse.py +20 -14
  24. odoo/addons/rma/security/rma_security.xml +3 -9
  25. odoo/addons/rma/static/description/index.html +4 -4
  26. odoo/addons/rma/tests/test_rma.py +58 -16
  27. odoo/addons/rma/views/report_rma.xml +9 -3
  28. odoo/addons/rma/views/res_config_settings_views.xml +8 -11
  29. odoo/addons/rma/views/rma_finalization_views.xml +2 -2
  30. odoo/addons/rma/views/rma_portal_templates.xml +303 -361
  31. odoo/addons/rma/views/rma_tag_views.xml +2 -2
  32. odoo/addons/rma/views/rma_team_views.xml +35 -36
  33. odoo/addons/rma/views/rma_views.xml +7 -29
  34. odoo/addons/rma/wizard/rma_delivery.py +2 -2
  35. odoo/addons/rma/wizard/rma_delivery_views.xml +0 -3
  36. odoo/addons/rma/wizard/rma_split.py +2 -2
  37. odoo/addons/rma/wizard/rma_split_views.xml +0 -1
  38. odoo/addons/rma/wizard/stock_picking_return.py +12 -42
  39. odoo/addons/rma/wizard/stock_picking_return_views.xml +1 -5
  40. {odoo_addon_rma-17.0.2.2.1.dist-info → odoo_addon_rma-18.0.1.0.0.3.dist-info}/METADATA +9 -9
  41. odoo_addon_rma-18.0.1.0.0.3.dist-info/RECORD +70 -0
  42. odoo/addons/rma/migrations/17.0.1.2.0/post-migration.py +0 -17
  43. odoo_addon_rma-17.0.2.2.1.dist-info/RECORD +0 -71
  44. {odoo_addon_rma-17.0.2.2.1.dist-info → odoo_addon_rma-18.0.1.0.0.3.dist-info}/WHEEL +0 -0
  45. {odoo_addon_rma-17.0.2.2.1.dist-info → odoo_addon_rma-18.0.1.0.0.3.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,7 @@ from itertools import groupby
9
9
 
10
10
  from markupsafe import Markup
11
11
 
12
- from odoo import _, api, fields, models
12
+ from odoo import api, fields, models
13
13
  from odoo.exceptions import AccessError, ValidationError
14
14
  from odoo.tools import html2plaintext
15
15
 
@@ -40,7 +40,7 @@ class Rma(models.Model):
40
40
  name = fields.Char(
41
41
  index=True,
42
42
  copy=False,
43
- default=lambda self: _("New"),
43
+ default=lambda self: self.env._("New"),
44
44
  )
45
45
  origin = fields.Char(
46
46
  string="Source Document",
@@ -314,9 +314,7 @@ class Rma(models.Model):
314
314
  for r in self:
315
315
  r.remaining_qty = r.product_uom_qty - r.delivered_qty
316
316
 
317
- @api.depends(
318
- "state",
319
- )
317
+ @api.depends("state")
320
318
  def _compute_can_be_refunded(self):
321
319
  """Compute 'can_be_refunded'. This field controls the visibility
322
320
  of 'Refund' button in the rma form view and determinates if
@@ -497,7 +495,7 @@ class Rma(models.Model):
497
495
  @api.model_create_multi
498
496
  def create(self, vals_list):
499
497
  for vals in vals_list:
500
- if vals.get("name", _("New")) == _("New"):
498
+ if vals.get("name", self.env._("New")) == self.env._("New"):
501
499
  ir_sequence = self.env["ir.sequence"]
502
500
  if "company_id" in vals:
503
501
  ir_sequence = ir_sequence.with_company(vals["company_id"])
@@ -514,18 +512,19 @@ class Rma(models.Model):
514
512
  return rmas
515
513
 
516
514
  def copy(self, default=None):
517
- team = super().copy(default)
518
- for follower in self.message_follower_ids:
519
- team.message_subscribe(
520
- partner_ids=follower.partner_id.ids,
521
- subtype_ids=follower.subtype_ids.ids,
522
- )
523
- return team
515
+ new_rmas = super().copy(default)
516
+ for old_rma, new_rma in zip(self, new_rmas, strict=False):
517
+ for follower in old_rma.message_follower_ids:
518
+ new_rma.message_subscribe(
519
+ partner_ids=follower.partner_id.ids,
520
+ subtype_ids=follower.subtype_ids.ids,
521
+ )
522
+ return new_rmas
524
523
 
525
524
  def unlink(self):
526
525
  if self.filtered(lambda r: r.state != "draft"):
527
526
  raise ValidationError(
528
- _("You cannot delete RMAs that are not in draft state")
527
+ self.env._("You cannot delete RMAs that are not in draft state")
529
528
  )
530
529
  return super().unlink()
531
530
 
@@ -601,10 +600,6 @@ class Rma(models.Model):
601
600
  if self.partner_id and self.partner_id not in self.message_partner_ids:
602
601
  self.message_subscribe([self.partner_id.id])
603
602
 
604
- def _product_is_storable(self, product=None):
605
- product = product or self.product_id
606
- return product.type in ["product", "consu"]
607
-
608
603
  def _prepare_procurement_group_vals(self):
609
604
  return {
610
605
  "move_type": "direct",
@@ -643,7 +638,7 @@ class Rma(models.Model):
643
638
  procurements = []
644
639
  group_model = self.env["procurement.group"]
645
640
  for rma in self:
646
- if not rma._product_is_storable():
641
+ if not rma.product_id.is_storable:
647
642
  continue
648
643
  group = rma.procurement_group_id
649
644
  if not group:
@@ -824,7 +819,7 @@ class Rma(models.Model):
824
819
  """Invoked when 'Refund' smart button in rma form view is clicked."""
825
820
  self.ensure_one()
826
821
  return {
827
- "name": _("Refund"),
822
+ "name": self.env._("Refund"),
828
823
  "type": "ir.actions.act_window",
829
824
  "view_type": "form",
830
825
  "view_mode": "form",
@@ -873,7 +868,7 @@ class Rma(models.Model):
873
868
  )
874
869
  desc += f"\n{field_record.field_description}"
875
870
  if desc:
876
- raise ValidationError(_("Required field(s):%s") % desc)
871
+ raise ValidationError(self.env._("Required field(s):%s") % desc)
877
872
 
878
873
  def _ensure_can_be_returned(self):
879
874
  """This method is intended to be invoked after user click on
@@ -888,9 +883,11 @@ class Rma(models.Model):
888
883
  """
889
884
  if len(self) == 1:
890
885
  if not self.can_be_returned:
891
- raise ValidationError(_("This RMA cannot perform a return."))
886
+ raise ValidationError(self.env._("This RMA cannot perform a return."))
892
887
  elif not self.filtered("can_be_returned"):
893
- raise ValidationError(_("None of the selected RMAs can perform a return."))
888
+ raise ValidationError(
889
+ self.env._("None of the selected RMAs can perform a return.")
890
+ )
894
891
 
895
892
  def _ensure_can_be_replaced(self):
896
893
  """This method is intended to be invoked after user click on
@@ -903,10 +900,12 @@ class Rma(models.Model):
903
900
  """
904
901
  if len(self) == 1:
905
902
  if not self.can_be_replaced:
906
- raise ValidationError(_("This RMA cannot perform a replacement."))
903
+ raise ValidationError(
904
+ self.env._("This RMA cannot perform a replacement.")
905
+ )
907
906
  elif not self.filtered("can_be_replaced"):
908
907
  raise ValidationError(
909
- _("None of the selected RMAs can perform a replacement.")
908
+ self.env._("None of the selected RMAs can perform a replacement.")
910
909
  )
911
910
 
912
911
  def _ensure_can_be_split(self):
@@ -917,7 +916,7 @@ class Rma(models.Model):
917
916
  """
918
917
  self.ensure_one()
919
918
  if not self.can_be_split:
920
- raise ValidationError(_("This RMA cannot be split."))
919
+ raise ValidationError(self.env._("This RMA cannot be split."))
921
920
 
922
921
  def _ensure_qty_to_return(self, qty=None, uom=None):
923
922
  """This method is intended to be invoked after confirm the wizard.
@@ -928,7 +927,9 @@ class Rma(models.Model):
928
927
  qty = uom._compute_quantity(qty, self.product_uom)
929
928
  if qty > self.remaining_qty:
930
929
  raise ValidationError(
931
- _("The quantity to return is greater than " "remaining quantity.")
930
+ self.env._(
931
+ "The quantity to return is greater than " "remaining quantity."
932
+ )
932
933
  )
933
934
 
934
935
  def _ensure_qty_to_extract(self, qty, uom):
@@ -940,7 +941,7 @@ class Rma(models.Model):
940
941
  to_split_uom_qty = uom._compute_quantity(qty, self.product_uom)
941
942
  if to_split_uom_qty > self.remaining_qty:
942
943
  raise ValidationError(
943
- _(
944
+ self.env._(
944
945
  "Quantity to extract cannot be greater than remaining"
945
946
  " delivery quantity (%(remaining_qty)s %(product_uom)s)"
946
947
  )
@@ -980,7 +981,7 @@ class Rma(models.Model):
980
981
  )
981
982
  self.message_post(
982
983
  body=Markup(
983
- _(
984
+ self.env._(
984
985
  'Split: <a href="#" data-oe-model="rma" '
985
986
  'data-oe-id="%(id)d">%(name)s</a> has been created.'
986
987
  )
@@ -1103,7 +1104,7 @@ class Rma(models.Model):
1103
1104
  self._ensure_can_be_returned()
1104
1105
  self._ensure_qty_to_return(qty, uom)
1105
1106
  rmas_to_return = self.filtered(
1106
- lambda rma: rma.can_be_returned and rma._product_is_storable()
1107
+ lambda rma: rma.can_be_returned and rma.product_id.is_storable
1107
1108
  )
1108
1109
  procurements = rmas_to_return._prepare_delivery_procurements(
1109
1110
  scheduled_date, qty, uom
@@ -1116,7 +1117,7 @@ class Rma(models.Model):
1116
1117
  pickings[picking] |= rma
1117
1118
  rma.message_post(
1118
1119
  body=Markup(
1119
- _(
1120
+ self.env._(
1120
1121
  'Return: <a href="#" data-oe-model="stock.picking" '
1121
1122
  'data-oe-id="%(id)d">%(name)s</a> has been created.'
1122
1123
  )
@@ -1146,7 +1147,7 @@ class Rma(models.Model):
1146
1147
  procurements = []
1147
1148
  group_model = self.env["procurement.group"]
1148
1149
  for rma in self:
1149
- if not rma._product_is_storable(product):
1150
+ if not product.is_storable:
1150
1151
  continue
1151
1152
 
1152
1153
  if not rma.procurement_group_id:
@@ -1186,7 +1187,7 @@ class Rma(models.Model):
1186
1187
  # MRP BoM Kits
1187
1188
  for new_move in new_moves:
1188
1189
  body += Markup(
1189
- _(
1190
+ self.env._(
1190
1191
  'Replacement: Move <a href="#" data-oe-model="stock.move"'
1191
1192
  ' data-oe-id="%(move_id)d">%(move_name)s</a> (Picking <a'
1192
1193
  ' href="#" data-oe-model="stock.picking"'
@@ -1212,7 +1213,7 @@ class Rma(models.Model):
1212
1213
  self.message_post(
1213
1214
  body=body
1214
1215
  or Markup(
1215
- _(
1216
+ self.env._(
1216
1217
  "Replacement:<br/>"
1217
1218
  'Product <a href="#" data-oe-model="product.product" '
1218
1219
  'data-oe-id="%(id)d">%(name)s</a><br/>'
@@ -1255,14 +1256,14 @@ class Rma(models.Model):
1255
1256
  custom_values = {}
1256
1257
  subject = msg_dict.get("subject", "")
1257
1258
  body = html2plaintext(msg_dict.get("body", ""))
1258
- desc = _(
1259
+ desc = self.env._(
1259
1260
  "<b>E-mail subject:</b> %(subject)s<br/><br/><b>E-mail"
1260
1261
  " body:</b><br/>%(body)s"
1261
1262
  ) % ({"subject": subject, "body": body})
1262
1263
  defaults = {
1263
1264
  "description": desc,
1264
- "name": _("New"),
1265
- "origin": _("Incoming e-mail"),
1265
+ "name": self.env._("New"),
1266
+ "origin": self.env._("Incoming e-mail"),
1266
1267
  }
1267
1268
  if msg_dict.get("author_id"):
1268
1269
  partner = self.env["res.partner"].browse(msg_dict.get("author_id"))
@@ -1298,7 +1299,7 @@ class Rma(models.Model):
1298
1299
  try:
1299
1300
  for record in self.filtered("partner_id"):
1300
1301
  record._message_add_suggested_recipient(
1301
- recipients, partner=record.partner_id, reason=_("Customer")
1302
+ recipients, partner=record.partner_id, reason=self.env._("Customer")
1302
1303
  )
1303
1304
  except AccessError as e: # no read access rights
1304
1305
  _logger.debug(e)
@@ -1307,7 +1308,7 @@ class Rma(models.Model):
1307
1308
  # Reporting business methods
1308
1309
  def _get_report_base_filename(self):
1309
1310
  self.ensure_one()
1310
- return "RMA Report - %s" % self.name
1311
+ return f"RMA Report - {self.name}"
1311
1312
 
1312
1313
  # Other business methods
1313
1314
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  import ast
5
5
 
6
- from odoo import _, fields, models
6
+ from odoo import fields, models
7
7
 
8
8
 
9
9
  class RmaTeam(models.Model):
@@ -40,18 +40,17 @@ class RmaTeam(models.Model):
40
40
  )
41
41
 
42
42
  def copy(self, default=None):
43
- self.ensure_one()
44
- if default is None:
45
- default = {}
46
- if not default.get("name"):
47
- default["name"] = _("%s (copy)") % self.name
48
- team = super().copy(default)
49
- for follower in self.message_follower_ids:
50
- team.message_subscribe(
51
- partner_ids=follower.partner_id.ids,
52
- subtype_ids=follower.subtype_ids.ids,
53
- )
54
- return team
43
+ default = dict(default or {})
44
+ new_teams = super().copy(default)
45
+ for old_team, new_team in zip(self, new_teams, strict=False):
46
+ if not default.get("name"):
47
+ new_team.name = self.env._("%s (copy)") % old_team.name
48
+ for follower in old_team.message_follower_ids:
49
+ new_team.message_subscribe(
50
+ partner_ids=follower.partner_id.ids,
51
+ subtype_ids=follower.subtype_ids.ids,
52
+ )
53
+ return new_teams
55
54
 
56
55
  def _alias_get_creation_values(self):
57
56
  values = super()._alias_get_creation_values()
@@ -2,7 +2,7 @@
2
2
  # Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
3
3
  # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
4
4
 
5
- from odoo import _, api, fields, models
5
+ from odoo import api, fields, models
6
6
  from odoo.exceptions import ValidationError
7
7
  from odoo.tools import float_compare
8
8
 
@@ -72,7 +72,7 @@ class StockMove(models.Model):
72
72
  != 0
73
73
  ):
74
74
  raise ValidationError(
75
- _(
75
+ self.env._(
76
76
  "The quantity done for the product '%(id)s' must "
77
77
  "be equal to its initial demand because the "
78
78
  "stock move is linked to an RMA (%(name)s)."
@@ -16,18 +16,6 @@ class StockPicking(models.Model):
16
16
  for rec in self:
17
17
  rec.rma_count = len(rec.move_ids.mapped("rma_ids"))
18
18
 
19
- def copy(self, default=None):
20
- self.ensure_one()
21
- if self.env.context.get("set_rma_picking_type"):
22
- location_dest_id = default.get("location_dest_id")
23
- if location_dest_id:
24
- warehouse = self.env["stock.warehouse"].search(
25
- [("rma_loc_id", "parent_of", location_dest_id)], limit=1
26
- )
27
- if warehouse:
28
- default["picking_type_id"] = warehouse.rma_in_type_id.id
29
- return super().copy(default)
30
-
31
19
  def action_view_rma(self):
32
20
  self.ensure_one()
33
21
  action = self.env["ir.actions.act_window"]._for_xml_id("rma.rma_action")
@@ -2,7 +2,7 @@
2
2
  # Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
3
3
  # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
4
4
 
5
- from odoo import _, fields, models
5
+ from odoo import fields, models
6
6
 
7
7
 
8
8
  class StockWarehouse(models.Model):
@@ -19,17 +19,24 @@ class StockWarehouse(models.Model):
19
19
  rma_in_type_id = fields.Many2one(
20
20
  comodel_name="stock.picking.type",
21
21
  string="RMA In Type",
22
+ check_company=True,
23
+ copy=False,
22
24
  )
23
25
  rma_out_type_id = fields.Many2one(
24
26
  comodel_name="stock.picking.type",
25
27
  string="RMA Out Type",
28
+ check_company=True,
29
+ copy=False,
26
30
  )
27
31
  rma_loc_id = fields.Many2one(
28
- comodel_name="stock.location",
29
- string="RMA Location",
32
+ comodel_name="stock.location", string="RMA Location", check_company=True
33
+ )
34
+ rma_in_route_id = fields.Many2one(
35
+ "stock.route", "RMA in Route", ondelete="restrict", copy=False
36
+ )
37
+ rma_out_route_id = fields.Many2one(
38
+ "stock.route", "RMA out Route", ondelete="restrict", copy=False
30
39
  )
31
- rma_in_route_id = fields.Many2one("stock.route", "RMA in Route")
32
- rma_out_route_id = fields.Many2one("stock.route", "RMA out Route")
33
40
 
34
41
  def _get_rma_location_values(self, vals, code=False):
35
42
  """this method is intended to be used by 'create' method
@@ -49,7 +56,6 @@ class StockWarehouse(models.Model):
49
56
  return {
50
57
  "name": view_location.name,
51
58
  "active": True,
52
- "return_location": True,
53
59
  "usage": "internal",
54
60
  "company_id": company_id,
55
61
  "location_id": self.env.ref("rma.stock_location_rma").id,
@@ -63,17 +69,19 @@ class StockWarehouse(models.Model):
63
69
 
64
70
  def _get_sequence_values(self, name=False, code=False):
65
71
  values = super()._get_sequence_values(name=name, code=code)
72
+ name = name if name else self.name
73
+ code = code if code else self.code
66
74
  values.update(
67
75
  {
68
76
  "rma_in_type_id": {
69
- "name": self.name + " " + _("Sequence RMA in"),
70
- "prefix": self.code + "/RMA/IN/",
77
+ "name": name + " " + self.env._("Sequence RMA in"),
78
+ "prefix": code + "/RMA/IN/",
71
79
  "padding": 5,
72
80
  "company_id": self.company_id.id,
73
81
  },
74
82
  "rma_out_type_id": {
75
- "name": self.name + " " + _("Sequence RMA out"),
76
- "prefix": self.code + "/RMA/OUT/",
83
+ "name": name + " " + self.env._("Sequence RMA out"),
84
+ "prefix": code + "/RMA/OUT/",
77
85
  "padding": 5,
78
86
  "company_id": self.company_id.id,
79
87
  },
@@ -96,23 +104,21 @@ class StockWarehouse(models.Model):
96
104
  data.update(
97
105
  {
98
106
  "rma_in_type_id": {
99
- "name": _("RMA Receipts"),
107
+ "name": self.env._("RMA Receipts"),
100
108
  "code": "incoming",
101
109
  "use_create_lots": False,
102
110
  "use_existing_lots": True,
103
- "default_location_src_id": False,
104
111
  "default_location_dest_id": self.rma_loc_id.id,
105
112
  "sequence": max_sequence + 1,
106
113
  "sequence_code": "RMA/IN",
107
114
  "company_id": self.company_id.id,
108
115
  },
109
116
  "rma_out_type_id": {
110
- "name": _("RMA Delivery Orders"),
117
+ "name": self.env._("RMA Delivery Orders"),
111
118
  "code": "outgoing",
112
119
  "use_create_lots": False,
113
120
  "use_existing_lots": True,
114
121
  "default_location_src_id": self.rma_loc_id.id,
115
- "default_location_dest_id": False,
116
122
  "sequence": max_sequence + 2,
117
123
  "sequence_code": "RMA/OUT",
118
124
  "company_id": self.company_id.id,
@@ -71,25 +71,19 @@
71
71
  <field name="name">RMA multi-company</field>
72
72
  <field name="model_id" ref="model_rma" />
73
73
  <field name="global" eval="True" />
74
- <field
75
- name="domain_force"
76
- >['|',('company_id','=',False),('company_id','in',company_ids)]</field>
74
+ <field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
77
75
  </record>
78
76
  <record id="rma_team_rule_multi_company" model="ir.rule">
79
77
  <field name="name">RMA team multi-company</field>
80
78
  <field name="model_id" ref="model_rma_team" />
81
79
  <field name="global" eval="True" />
82
- <field
83
- name="domain_force"
84
- >['|',('company_id','=',False),('company_id','in',company_ids)]</field>
80
+ <field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
85
81
  </record>
86
82
  <record id="rma_finalization_comp_rule" model="ir.rule">
87
83
  <field name="name">RMA Finalization Reason multi-company</field>
88
84
  <field name="model_id" ref="model_rma_finalization" />
89
85
  <field name="global" eval="True" />
90
- <field
91
- name="domain_force"
92
- > ['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
86
+ <field name="domain_force">[('company_id', 'in', company_ids + [False])]</field>
93
87
  </record>
94
88
  <!-- Allow to refund RMAs -->
95
89
  <record id="rma_account_move_personal_rule" model="ir.rule">
@@ -367,9 +367,9 @@ ul.auto-toc {
367
367
  !! This file is generated by oca-gen-addon-readme !!
368
368
  !! changes will be overwritten. !!
369
369
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
370
- !! source digest: sha256:4226ff2ccfc01069320dd563138ac30c4252f24babc494801a90c1e3411ae38f
370
+ !! source digest: sha256:6ea461b6c1041acb71b7933199e58032ecb6d1fb5cb65544d84ec0e1758065d0
371
371
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
372
- <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/rma/tree/17.0/rma"><img alt="OCA/rma" src="https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/rma-17-0/rma-17-0-rma"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/rma&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
372
+ <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/rma/tree/18.0/rma"><img alt="OCA/rma" src="https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/rma-18-0/rma-18-0-rma"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/rma&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
373
373
  <p>This module allows you to manage <a class="reference external" href="https://en.wikipedia.org/wiki/Return_merchandise_authorization">Return Merchandise Authorization
374
374
  (RMA)</a>.
375
375
  RMA documents can be created from scratch, from a delivery order or from
@@ -504,7 +504,7 @@ the warehouse definition.</li>
504
504
  <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/rma/issues">GitHub Issues</a>.
505
505
  In case of trouble, please check there if your issue has already been reported.
506
506
  If you spotted it first, help us to smash it by providing a detailed and welcomed
507
- <a class="reference external" href="https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
507
+ <a class="reference external" href="https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
508
508
  <p>Do not contact contributors directly about support or help with technical issues.</p>
509
509
  </div>
510
510
  <div class="section" id="credits">
@@ -547,7 +547,7 @@ mission is to support the collaborative development of Odoo features and
547
547
  promote its widespread use.</p>
548
548
  <p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
549
549
  <p><a class="reference external image-reference" href="https://github.com/pedrobaeza"><img alt="pedrobaeza" src="https://github.com/pedrobaeza.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/chienandalu"><img alt="chienandalu" src="https://github.com/chienandalu.png?size=40px" /></a></p>
550
- <p>This module is part of the <a class="reference external" href="https://github.com/OCA/rma/tree/17.0/rma">OCA/rma</a> project on GitHub.</p>
550
+ <p>This module is part of the <a class="reference external" href="https://github.com/OCA/rma/tree/18.0/rma">OCA/rma</a> project on GitHub.</p>
551
551
  <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
552
552
  </div>
553
553
  </div>
@@ -1,5 +1,6 @@
1
1
  # Copyright 2020 Tecnativa - Ernesto Tejeda
2
2
  # Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
3
+ # Copyright 2025 Tecnativa - Víctor Martínez
3
4
  # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
4
5
 
5
6
  from odoo.exceptions import UserError, ValidationError
@@ -28,7 +29,7 @@ class TestRma(BaseCommon):
28
29
  )
29
30
  cls.rma_loc = cls.warehouse_company.rma_loc_id
30
31
  cls.product = cls.product_product.create(
31
- {"name": "Product test 1", "type": "product"}
32
+ {"name": "Product test 1", "type": "consu", "is_storable": True}
32
33
  )
33
34
  cls.account_receiv = cls.env["account.account"].create(
34
35
  {
@@ -123,7 +124,7 @@ class TestRma(BaseCommon):
123
124
  move.product_uom_qty = 10
124
125
  with picking_form.move_ids_without_package.new() as move:
125
126
  move.product_id = self.product_product.create(
126
- {"name": "Product 2 test", "type": "product"}
127
+ {"name": "Product 2 test", "type": "consu", "is_storable": True}
127
128
  )
128
129
  move.product_uom_qty = 20
129
130
  picking = picking_form.save()
@@ -169,14 +170,44 @@ class TestRmaCase(TestRma):
169
170
  wizard_form.product_uom_qty = rma.product_uom_qty
170
171
  wizard = wizard_form.save()
171
172
  wizard.action_deliver()
172
- self.assertEqual(rma.delivery_picking_count, 2)
173
- out_pickings = rma.mapped("delivery_move_ids.picking_id")
174
- self.assertIn(
175
- self.warehouse.pick_type_id, out_pickings.mapped("picking_type_id")
176
- )
177
- self.assertIn(
178
- self.warehouse.out_type_id, out_pickings.mapped("picking_type_id")
173
+ out_picking = rma.delivery_move_ids.picking_id
174
+ out_picking.move_ids.quantity = 1
175
+ out_picking.button_validate()
176
+ self.assertEqual(out_picking.state, "done")
177
+ self.assertEqual(out_picking.picking_type_id, self.warehouse.pick_type_id)
178
+ next_transfer = out_picking._get_next_transfers()
179
+ self.assertEqual(next_transfer.picking_type_id, self.warehouse.out_type_id)
180
+ self.assertEqual(rma.delivery_picking_count, 1)
181
+
182
+ def test_rma_replace_pick_pack_ship(self):
183
+ self.warehouse.write({"delivery_steps": "pick_pack_ship"})
184
+ rma = self._create_rma(self.partner, self.product, 1, self.rma_loc)
185
+ rma.action_confirm()
186
+ rma.reception_move_id.quantity = 1
187
+ rma.reception_move_id.picking_id.button_validate()
188
+ self.assertEqual(rma.reception_move_id.picking_id.state, "done")
189
+ self.assertEqual(rma.state, "received")
190
+ res = rma.action_replace()
191
+ wizard_form = Form(self.env[res["res_model"]].with_context(**res["context"]))
192
+ wizard_form.product_id = self.product
193
+ wizard_form.product_uom_qty = rma.product_uom_qty
194
+ wizard = wizard_form.save()
195
+ wizard.action_deliver()
196
+ out_picking = rma.delivery_move_ids.picking_id
197
+ out_picking.move_ids.quantity = 1
198
+ out_picking.button_validate()
199
+ self.assertEqual(out_picking.state, "done")
200
+ self.assertEqual(out_picking.picking_type_id, self.warehouse.pick_type_id)
201
+ next_transfer = out_picking._get_next_transfers()
202
+ self.assertEqual(next_transfer.picking_type_id, self.warehouse.pack_type_id)
203
+ next_transfer.move_ids.quantity = 1
204
+ next_transfer.button_validate()
205
+ self.assertEqual(next_transfer.state, "done")
206
+ next_transfer_extra = next_transfer._get_next_transfers()
207
+ self.assertEqual(
208
+ next_transfer_extra.picking_type_id, self.warehouse.out_type_id
179
209
  )
210
+ self.assertEqual(rma.delivery_picking_count, 1)
180
211
 
181
212
  def test_computed(self):
182
213
  # If partner changes, the invoice address is set
@@ -193,7 +224,12 @@ class TestRmaCase(TestRma):
193
224
  }
194
225
  )
195
226
  product_2 = self.product_product.create(
196
- {"name": "Product test 2", "type": "product", "uom_id": uom_ten.id}
227
+ {
228
+ "name": "Product test 2",
229
+ "type": "consu",
230
+ "is_storable": True,
231
+ "uom_id": uom_ten.id,
232
+ }
197
233
  )
198
234
  outgoing_picking_type = self.env["stock.picking.type"].search(
199
235
  [
@@ -305,6 +341,7 @@ class TestRmaCase(TestRma):
305
341
  self.assertEqual(rma_2.state, "received")
306
342
 
307
343
  @users("__system__", "user_rma")
344
+ @mute_logger("odoo.models.unlink")
308
345
  def test_action_refund(self):
309
346
  rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc)
310
347
  self.assertEqual(rma.state, "received")
@@ -339,6 +376,7 @@ class TestRmaCase(TestRma):
339
376
  self.assertFalse(rma.can_be_returned)
340
377
  self.assertFalse(rma.can_be_replaced)
341
378
 
379
+ @mute_logger("odoo.models.unlink")
342
380
  def test_mass_refund(self):
343
381
  # Create, confirm and receive rma_1
344
382
  rma_1 = self._create_confirm_receive(
@@ -351,7 +389,7 @@ class TestRmaCase(TestRma):
351
389
  )
352
390
  # rma_3: Same partner and different product than rma_1
353
391
  product = self.product_product.create(
354
- {"name": "Product 2 test", "type": "product"}
392
+ {"name": "Product 2 test", "type": "consu", "is_storable": True}
355
393
  )
356
394
  rma_3 = self._create_confirm_receive(self.partner, product, 20, self.rma_loc)
357
395
  # rma_4: Different partner and same product as rma_1
@@ -424,7 +462,7 @@ class TestRmaCase(TestRma):
424
462
  rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc)
425
463
  # Replace with another product with quantity 2.
426
464
  product_2 = self.product_product.create(
427
- {"name": "Product 2 test", "type": "product"}
465
+ {"name": "Product 2 test", "type": "consu", "is_storable": True}
428
466
  )
429
467
  delivery_form = Form(
430
468
  self.env["rma.delivery.wizard"].with_context(
@@ -450,7 +488,7 @@ class TestRmaCase(TestRma):
450
488
  picking = first_move.picking_id
451
489
  # Replace again with another product with the remaining quantity
452
490
  product_3 = self.product_product.create(
453
- {"name": "Product 3 test", "type": "product"}
491
+ {"name": "Product 3 test", "type": "consu", "is_storable": True}
454
492
  )
455
493
  delivery_form = Form(
456
494
  self.env["rma.delivery.wizard"].with_context(
@@ -572,7 +610,7 @@ class TestRmaCase(TestRma):
572
610
  )
573
611
  # rma_3: Same partner and different product than rma_1
574
612
  product = self.product_product.create(
575
- {"name": "Product 2 test", "type": "product"}
613
+ {"name": "Product 2 test", "type": "consu", "is_storable": True}
576
614
  )
577
615
  rma_3 = self._create_confirm_receive(self.partner, product, 20, self.rma_loc)
578
616
  # rma_4: Different partner and same product as rma_1
@@ -653,7 +691,7 @@ class TestRmaCase(TestRma):
653
691
  )
654
692
  # rma_3: Same partner and different product than rma_1
655
693
  product = self.product_product.create(
656
- {"name": "Product 2 test", "type": "product"}
694
+ {"name": "Product 2 test", "type": "consu", "is_storable": True}
657
695
  )
658
696
  rma_3 = self._create_confirm_receive(self.partner, product, 20, self.rma_loc)
659
697
  # rma_4: Different partner and same product as rma_1
@@ -685,7 +723,11 @@ class TestRmaCase(TestRma):
685
723
  stock_return_picking_form.create_rma = True
686
724
  stock_return_picking_form.rma_operation_id = self.operation
687
725
  return_wizard = stock_return_picking_form.save()
688
- picking_action = return_wizard.create_returns()
726
+ for move in origin_delivery.move_ids_without_package:
727
+ return_wizard.product_return_moves.filtered(
728
+ lambda x, move=move: x.move_id == move
729
+ ).quantity = move.quantity
730
+ picking_action = return_wizard.action_create_returns()
689
731
  # Each origin move is linked to a different RMA
690
732
  origin_moves = origin_delivery.move_ids
691
733
  self.assertTrue(origin_moves[0].rma_ids)