odoo-addon-rma 17.0.1.1.1__py3-none-any.whl → 17.0.2.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.
- odoo/addons/rma/README.rst +6 -1
- odoo/addons/rma/__manifest__.py +1 -1
- odoo/addons/rma/hooks.py +12 -1
- odoo/addons/rma/i18n/rma.pot +22 -0
- odoo/addons/rma/migrations/17.0.1.2.0/post-migration.py +17 -0
- odoo/addons/rma/models/res_partner.py +1 -1
- odoo/addons/rma/models/rma.py +241 -276
- odoo/addons/rma/models/stock_move.py +17 -32
- odoo/addons/rma/models/stock_warehouse.py +100 -25
- odoo/addons/rma/readme/CONTRIBUTORS.md +1 -0
- odoo/addons/rma/readme/ROADMAP.md +3 -0
- odoo/addons/rma/static/description/index.html +5 -1
- odoo/addons/rma/tests/test_rma.py +78 -21
- odoo/addons/rma/wizard/stock_picking_return.py +90 -13
- odoo/addons/rma/wizard/stock_picking_return_views.xml +1 -0
- {odoo_addon_rma-17.0.1.1.1.dist-info → odoo_addon_rma-17.0.2.0.0.dist-info}/METADATA +7 -2
- {odoo_addon_rma-17.0.1.1.1.dist-info → odoo_addon_rma-17.0.2.0.0.dist-info}/RECORD +19 -18
- {odoo_addon_rma-17.0.1.1.1.dist-info → odoo_addon_rma-17.0.2.0.0.dist-info}/WHEEL +0 -0
- {odoo_addon_rma-17.0.1.1.1.dist-info → odoo_addon_rma-17.0.2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
2
|
+
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
2
3
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
3
4
|
|
4
5
|
from odoo import _, api, fields, models
|
@@ -23,7 +24,7 @@ class StockMove(models.Model):
|
|
23
24
|
string="RMA receivers",
|
24
25
|
copy=False,
|
25
26
|
)
|
26
|
-
# RMA that
|
27
|
+
# RMA that creates the out move
|
27
28
|
rma_id = fields.Many2one(
|
28
29
|
comodel_name="rma",
|
29
30
|
string="RMA return",
|
@@ -33,8 +34,8 @@ class StockMove(models.Model):
|
|
33
34
|
def unlink(self):
|
34
35
|
# A stock user could have no RMA permissions, so the ids wouldn't
|
35
36
|
# be accessible due to record rules.
|
36
|
-
rma_receiver = self.sudo().
|
37
|
-
rma = self.sudo().
|
37
|
+
rma_receiver = self.sudo().rma_receiver_ids
|
38
|
+
rma = self.sudo().rma_id
|
38
39
|
res = super().unlink()
|
39
40
|
rma_receiver.filtered(lambda x: x.state != "cancelled").write(
|
40
41
|
{"state": "draft"}
|
@@ -115,38 +116,22 @@ class StockMove(models.Model):
|
|
115
116
|
res["rma_id"] = self.sudo().rma_id.id
|
116
117
|
return res
|
117
118
|
|
118
|
-
def
|
119
|
-
|
120
|
-
self.
|
121
|
-
|
122
|
-
|
123
|
-
partner_invoice_id = original_picking.sale_id.partner_invoice_id.id
|
124
|
-
partner_shipping_id = original_picking.sale_id.partner_shipping_id.id
|
125
|
-
else:
|
126
|
-
partner_invoice_id = partner.address_get(["invoice"]).get("invoice", False)
|
127
|
-
partner_shipping_id = partner.address_get(["delivery"]).get(
|
128
|
-
"delivery", False
|
129
|
-
)
|
130
|
-
return {
|
131
|
-
"user_id": self.env.user.id,
|
132
|
-
"partner_id": partner.id,
|
133
|
-
"partner_shipping_id": partner_shipping_id,
|
134
|
-
"partner_invoice_id": partner_invoice_id,
|
135
|
-
"origin": original_picking.name,
|
136
|
-
"picking_id": original_picking.id,
|
137
|
-
"move_id": self.origin_returned_move_id.id,
|
138
|
-
"product_id": self.origin_returned_move_id.product_id.id,
|
139
|
-
"product_uom_qty": self.product_uom_qty,
|
140
|
-
"product_uom": self.product_uom.id,
|
141
|
-
"reception_move_id": self.id,
|
142
|
-
"company_id": self.company_id.id,
|
143
|
-
"location_id": self.location_dest_id.id,
|
144
|
-
"state": "confirmed",
|
145
|
-
}
|
119
|
+
def _prepare_procurement_values(self):
|
120
|
+
res = super()._prepare_procurement_values()
|
121
|
+
if self.rma_id:
|
122
|
+
res["rma_id"] = self.rma_id.id
|
123
|
+
return res
|
146
124
|
|
147
125
|
|
148
126
|
class StockRule(models.Model):
|
149
127
|
_inherit = "stock.rule"
|
150
128
|
|
151
129
|
def _get_custom_move_fields(self):
|
152
|
-
|
130
|
+
move_fields = super()._get_custom_move_fields()
|
131
|
+
move_fields += [
|
132
|
+
"rma_id",
|
133
|
+
"origin_returned_move_id",
|
134
|
+
"move_orig_ids",
|
135
|
+
"rma_receiver_ids",
|
136
|
+
]
|
137
|
+
return move_fields
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
2
|
+
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
2
3
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
3
4
|
|
4
|
-
from odoo import _,
|
5
|
+
from odoo import _, fields, models
|
5
6
|
|
6
7
|
|
7
8
|
class StockWarehouse(models.Model):
|
@@ -27,35 +28,39 @@ class StockWarehouse(models.Model):
|
|
27
28
|
comodel_name="stock.location",
|
28
29
|
string="RMA Location",
|
29
30
|
)
|
31
|
+
rma_in_route_id = fields.Many2one("stock.route", "RMA in Route")
|
32
|
+
rma_out_route_id = fields.Many2one("stock.route", "RMA out Route")
|
30
33
|
|
31
|
-
|
32
|
-
def create(self, vals_list):
|
33
|
-
"""To create an RMA location and link it with a new warehouse,
|
34
|
-
this method is overridden instead of '_get_locations_values'
|
35
|
-
method because the locations that are created with the
|
36
|
-
values returned by that method are forced to be children
|
37
|
-
of view_location_id, and we don't want that.
|
38
|
-
"""
|
39
|
-
res = super().create(vals_list)
|
40
|
-
stock_location = self.env["stock.location"]
|
41
|
-
for record in res:
|
42
|
-
rma_location_vals = record._get_rma_location_values()
|
43
|
-
record.rma_loc_id = stock_location.create(rma_location_vals).id
|
44
|
-
return res
|
45
|
-
|
46
|
-
def _get_rma_location_values(self):
|
34
|
+
def _get_rma_location_values(self, vals, code=False):
|
47
35
|
"""this method is intended to be used by 'create' method
|
48
36
|
to create a new RMA location to be linked to a new warehouse.
|
49
37
|
"""
|
38
|
+
company_id = vals.get(
|
39
|
+
"company_id", self.default_get(["company_id"])["company_id"]
|
40
|
+
)
|
41
|
+
code = vals.get("code") or code or ""
|
42
|
+
code = code.replace(" ", "").upper()
|
43
|
+
view_location_id = vals.get("view_location_id")
|
44
|
+
view_location = (
|
45
|
+
view_location_id
|
46
|
+
and self.view_location_id.browse(view_location_id)
|
47
|
+
or self.view_location_id
|
48
|
+
)
|
50
49
|
return {
|
51
|
-
"name":
|
50
|
+
"name": view_location.name,
|
52
51
|
"active": True,
|
53
52
|
"return_location": True,
|
54
53
|
"usage": "internal",
|
55
|
-
"company_id":
|
54
|
+
"company_id": company_id,
|
56
55
|
"location_id": self.env.ref("rma.stock_location_rma").id,
|
56
|
+
"barcode": self._valid_barcode(code + "-RMA", company_id),
|
57
57
|
}
|
58
58
|
|
59
|
+
def _get_locations_values(self, vals, code=False):
|
60
|
+
res = super()._get_locations_values(vals, code)
|
61
|
+
res["rma_loc_id"] = self._get_rma_location_values(vals, code)
|
62
|
+
return res
|
63
|
+
|
59
64
|
def _get_sequence_values(self, name=False, code=False):
|
60
65
|
values = super()._get_sequence_values(name=name, code=code)
|
61
66
|
values.update(
|
@@ -77,12 +82,14 @@ class StockWarehouse(models.Model):
|
|
77
82
|
return values
|
78
83
|
|
79
84
|
def _update_name_and_code(self, new_name=False, new_code=False):
|
85
|
+
res = super()._update_name_and_code(new_name, new_code)
|
80
86
|
for warehouse in self:
|
81
87
|
sequence_data = warehouse._get_sequence_values()
|
82
88
|
warehouse.rma_in_type_id.sequence_id.write(sequence_data["rma_in_type_id"])
|
83
89
|
warehouse.rma_out_type_id.sequence_id.write(
|
84
90
|
sequence_data["rma_out_type_id"]
|
85
91
|
)
|
92
|
+
return res
|
86
93
|
|
87
94
|
def _get_picking_type_create_values(self, max_sequence):
|
88
95
|
data, next_sequence = super()._get_picking_type_create_values(max_sequence)
|
@@ -116,12 +123,13 @@ class StockWarehouse(models.Model):
|
|
116
123
|
|
117
124
|
def _get_picking_type_update_values(self):
|
118
125
|
data = super()._get_picking_type_update_values()
|
119
|
-
|
120
|
-
{
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
126
|
+
picking_types = {
|
127
|
+
"rma_in_type_id": {"default_location_dest_id": self.rma_loc_id.id},
|
128
|
+
"rma_out_type_id": {"default_location_src_id": self.rma_loc_id.id},
|
129
|
+
}
|
130
|
+
if self.env.context.get("rma_post_init_hook"):
|
131
|
+
return picking_types
|
132
|
+
data.update(picking_types)
|
125
133
|
return data
|
126
134
|
|
127
135
|
def _create_or_update_sequences_and_picking_types(self):
|
@@ -138,3 +146,70 @@ class StockWarehouse(models.Model):
|
|
138
146
|
{"return_picking_type_id": data.get("rma_out_type_id", False)}
|
139
147
|
)
|
140
148
|
return data
|
149
|
+
|
150
|
+
def _get_routes_values(self):
|
151
|
+
res = super()._get_routes_values()
|
152
|
+
rma_routes = {
|
153
|
+
"rma_in_route_id": {
|
154
|
+
"routing_key": "rma_in",
|
155
|
+
"depends": ["active"],
|
156
|
+
"route_update_values": {
|
157
|
+
"name": self._format_routename("RMA In"),
|
158
|
+
"active": self.active,
|
159
|
+
},
|
160
|
+
"route_create_values": {
|
161
|
+
"warehouse_selectable": True,
|
162
|
+
"company_id": self.company_id.id,
|
163
|
+
"sequence": 100,
|
164
|
+
},
|
165
|
+
"rules_values": {
|
166
|
+
"active": True,
|
167
|
+
},
|
168
|
+
},
|
169
|
+
"rma_out_route_id": {
|
170
|
+
"routing_key": "rma_out",
|
171
|
+
"depends": ["active"],
|
172
|
+
"route_update_values": {
|
173
|
+
"name": self._format_routename("RMA Out"),
|
174
|
+
"active": self.active,
|
175
|
+
},
|
176
|
+
"route_create_values": {
|
177
|
+
"warehouse_selectable": True,
|
178
|
+
"company_id": self.company_id.id,
|
179
|
+
"sequence": 110,
|
180
|
+
},
|
181
|
+
"rules_values": {
|
182
|
+
"active": True,
|
183
|
+
},
|
184
|
+
},
|
185
|
+
}
|
186
|
+
if self.env.context.get("rma_post_init_hook"):
|
187
|
+
return rma_routes
|
188
|
+
res.update(rma_routes)
|
189
|
+
return res
|
190
|
+
|
191
|
+
def get_rules_dict(self):
|
192
|
+
res = super().get_rules_dict()
|
193
|
+
customer_loc, supplier_loc = self._get_partner_locations()
|
194
|
+
for warehouse in self:
|
195
|
+
res[warehouse.id].update(
|
196
|
+
{
|
197
|
+
"rma_in": [
|
198
|
+
self.Routing(
|
199
|
+
customer_loc,
|
200
|
+
warehouse.rma_loc_id,
|
201
|
+
warehouse.rma_in_type_id,
|
202
|
+
"pull",
|
203
|
+
)
|
204
|
+
],
|
205
|
+
"rma_out": [
|
206
|
+
self.Routing(
|
207
|
+
warehouse.rma_loc_id,
|
208
|
+
customer_loc,
|
209
|
+
warehouse.rma_out_type_id,
|
210
|
+
"pull",
|
211
|
+
)
|
212
|
+
],
|
213
|
+
}
|
214
|
+
)
|
215
|
+
return res
|
@@ -1,3 +1,6 @@
|
|
1
1
|
- As soon as the picking is selected, the user should select the move,
|
2
2
|
but perhaps stock.move \_rec_name could be improved to better show
|
3
3
|
what the product of that move is.
|
4
|
+
- Add RMA reception and/or RMA delivery on several steps - 2 or 3 - like
|
5
|
+
normal receptions/deliveries. It should be a separate option inside the
|
6
|
+
warehouse definition.
|
@@ -366,7 +366,7 @@ ul.auto-toc {
|
|
366
366
|
!! This file is generated by oca-gen-addon-readme !!
|
367
367
|
!! changes will be overwritten. !!
|
368
368
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
369
|
-
!! source digest: sha256:
|
369
|
+
!! source digest: sha256:8f36869aece97a0f6af8aa5d76b446e9cf0bd589d914c1f5e12c628e87317021
|
370
370
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
371
371
|
<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&target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
372
372
|
<p>This module allows you to manage <a class="reference external" href="https://en.wikipedia.org/wiki/Return_merchandise_authorization">Return Merchandise Authorization
|
@@ -493,6 +493,9 @@ team will be the default one if no team is set.</li>
|
|
493
493
|
<li>As soon as the picking is selected, the user should select the move,
|
494
494
|
but perhaps stock.move _rec_name could be improved to better show
|
495
495
|
what the product of that move is.</li>
|
496
|
+
<li>Add RMA reception and/or RMA delivery on several steps - 2 or 3 -
|
497
|
+
like normal receptions/deliveries. It should be a separate option
|
498
|
+
inside the warehouse definition.</li>
|
496
499
|
</ul>
|
497
500
|
</div>
|
498
501
|
<div class="section" id="bug-tracker">
|
@@ -527,6 +530,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
|
527
530
|
<li>Antoni Marroig <<a class="reference external" href="mailto:amarroig@apsl.net">amarroig@apsl.net</a>></li>
|
528
531
|
</ul>
|
529
532
|
</li>
|
533
|
+
<li>Michael Tietz (MT Software) <a class="reference external" href="mailto:mtietz@mt-software.de">mtietz@mt-software.de</a></li>
|
530
534
|
</ul>
|
531
535
|
</div>
|
532
536
|
<div class="section" id="maintainers">
|
@@ -1,24 +1,20 @@
|
|
1
1
|
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
2
|
+
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
2
3
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
3
4
|
|
4
5
|
from odoo.exceptions import UserError, ValidationError
|
5
|
-
from odoo.tests import Form,
|
6
|
+
from odoo.tests import Form, new_test_user, users
|
7
|
+
from odoo.tools import mute_logger
|
6
8
|
|
9
|
+
from odoo.addons.base.tests.common import BaseCommon
|
7
10
|
|
8
|
-
|
11
|
+
from .. import hooks
|
12
|
+
|
13
|
+
|
14
|
+
class TestRma(BaseCommon):
|
9
15
|
@classmethod
|
10
16
|
def setUpClass(cls):
|
11
17
|
super().setUpClass()
|
12
|
-
cls.env = cls.env(
|
13
|
-
context=dict(
|
14
|
-
cls.env.context,
|
15
|
-
mail_create_nolog=True,
|
16
|
-
mail_create_nosubscribe=True,
|
17
|
-
mail_notrack=True,
|
18
|
-
no_reset_password=True,
|
19
|
-
tracking_disable=True,
|
20
|
-
)
|
21
|
-
)
|
22
18
|
cls.user_rma = new_test_user(
|
23
19
|
cls.env,
|
24
20
|
login="user_rma",
|
@@ -72,6 +68,7 @@ class TestRma(TransactionCase):
|
|
72
68
|
{"name": "[Test] It's out of warranty. To be scrapped"}
|
73
69
|
)
|
74
70
|
cls.env.ref("rma.group_rma_manual_finalization").users |= cls.env.user
|
71
|
+
cls.warehouse = cls.env.ref("stock.warehouse0")
|
75
72
|
# Ensure grouping
|
76
73
|
cls.env.company.rma_return_grouping = True
|
77
74
|
|
@@ -95,7 +92,7 @@ class TestRma(TransactionCase):
|
|
95
92
|
rma = self._create_rma(partner, product, qty, location)
|
96
93
|
rma.action_confirm()
|
97
94
|
rma.reception_move_id.quantity = rma.product_uom_qty
|
98
|
-
rma.reception_move_id.picking_id.
|
95
|
+
rma.reception_move_id.picking_id.button_validate()
|
99
96
|
return rma
|
100
97
|
|
101
98
|
def _create_delivery(self):
|
@@ -132,6 +129,49 @@ class TestRma(TransactionCase):
|
|
132
129
|
|
133
130
|
|
134
131
|
class TestRmaCase(TestRma):
|
132
|
+
def test_post_init_hook(self):
|
133
|
+
warehouse = self.env["stock.warehouse"].create(
|
134
|
+
{
|
135
|
+
"name": "Test warehouse",
|
136
|
+
"code": "code",
|
137
|
+
"company_id": self.env.company.id,
|
138
|
+
}
|
139
|
+
)
|
140
|
+
hooks.post_init_hook(self.env)
|
141
|
+
self.assertTrue(warehouse.rma_in_type_id)
|
142
|
+
self.assertEqual(
|
143
|
+
warehouse.rma_in_type_id.default_location_dest_id, warehouse.rma_loc_id
|
144
|
+
)
|
145
|
+
self.assertEqual(
|
146
|
+
warehouse.rma_out_type_id.default_location_src_id, warehouse.rma_loc_id
|
147
|
+
)
|
148
|
+
self.assertTrue(warehouse.rma_loc_id)
|
149
|
+
self.assertTrue(warehouse.rma_in_route_id)
|
150
|
+
self.assertTrue(warehouse.rma_out_route_id)
|
151
|
+
|
152
|
+
def test_rma_replace_pick_ship(self):
|
153
|
+
self.warehouse.write({"delivery_steps": "pick_ship"})
|
154
|
+
rma = self._create_rma(self.partner, self.product, 1, self.rma_loc)
|
155
|
+
rma.action_confirm()
|
156
|
+
rma.reception_move_id.quantity = 1
|
157
|
+
rma.reception_move_id.picking_id.button_validate()
|
158
|
+
self.assertEqual(rma.reception_move_id.picking_id.state, "done")
|
159
|
+
self.assertEqual(rma.state, "received")
|
160
|
+
res = rma.action_replace()
|
161
|
+
wizard_form = Form(self.env[res["res_model"]].with_context(**res["context"]))
|
162
|
+
wizard_form.product_id = self.product
|
163
|
+
wizard_form.product_uom_qty = rma.product_uom_qty
|
164
|
+
wizard = wizard_form.save()
|
165
|
+
wizard.action_deliver()
|
166
|
+
self.assertEqual(rma.delivery_picking_count, 2)
|
167
|
+
out_pickings = rma.mapped("delivery_move_ids.picking_id")
|
168
|
+
self.assertIn(
|
169
|
+
self.warehouse.pick_type_id, out_pickings.mapped("picking_type_id")
|
170
|
+
)
|
171
|
+
self.assertIn(
|
172
|
+
self.warehouse.out_type_id, out_pickings.mapped("picking_type_id")
|
173
|
+
)
|
174
|
+
|
135
175
|
def test_computed(self):
|
136
176
|
# If partner changes, the invoice address is set
|
137
177
|
rma = self.env["rma"].new()
|
@@ -169,7 +209,7 @@ class TestRmaCase(TestRma):
|
|
169
209
|
move.product_id = product_2
|
170
210
|
move.product_uom_qty = 15
|
171
211
|
picking = picking_form.save()
|
172
|
-
picking.
|
212
|
+
picking.button_validate()
|
173
213
|
rma.picking_id = picking
|
174
214
|
rma.move_id = picking.move_ids
|
175
215
|
self.assertEqual(rma.product_id, product_2)
|
@@ -207,13 +247,18 @@ class TestRmaCase(TestRma):
|
|
207
247
|
self.assertEqual(rma.state, "confirmed")
|
208
248
|
rma.reception_move_id.quantity = 9
|
209
249
|
with self.assertRaises(ValidationError):
|
210
|
-
rma.reception_move_id.picking_id.
|
250
|
+
res = rma.reception_move_id.picking_id.button_validate()
|
251
|
+
wizard = (
|
252
|
+
self.env[res["res_model"]].with_context(**res["context"]).create({})
|
253
|
+
)
|
254
|
+
wizard.process()
|
211
255
|
rma.reception_move_id.quantity = 10
|
212
|
-
rma.reception_move_id.picking_id.
|
256
|
+
rma.reception_move_id.picking_id.button_validate()
|
213
257
|
self.assertEqual(rma.reception_move_id.picking_id.state, "done")
|
214
258
|
self.assertEqual(rma.reception_move_id.quantity, 10)
|
215
259
|
self.assertEqual(rma.state, "received")
|
216
260
|
|
261
|
+
@mute_logger("odoo.models.unlink")
|
217
262
|
def test_cancel(self):
|
218
263
|
# cancel a draft RMA
|
219
264
|
rma = self._create_rma(self.partner, self.product)
|
@@ -333,7 +378,7 @@ class TestRmaCase(TestRma):
|
|
333
378
|
# line of refund_1
|
334
379
|
self.assertEqual(len(refund_1.invoice_line_ids), 3)
|
335
380
|
self.assertEqual(
|
336
|
-
refund_1.invoice_line_ids.
|
381
|
+
refund_1.invoice_line_ids.rma_id,
|
337
382
|
(rma_1 | rma_2 | rma_3),
|
338
383
|
)
|
339
384
|
self.assertEqual(
|
@@ -524,6 +569,13 @@ class TestRmaCase(TestRma):
|
|
524
569
|
all_rmas = rma_1 | rma_2 | rma_3 | rma_4
|
525
570
|
self.assertEqual(all_rmas.mapped("state"), ["received"] * 4)
|
526
571
|
self.assertEqual(all_rmas.mapped("can_be_returned"), [True] * 4)
|
572
|
+
all_in_pickings = all_rmas.mapped("reception_move_id.picking_id")
|
573
|
+
self.assertEqual(
|
574
|
+
all_in_pickings.mapped("picking_type_id"), self.warehouse.rma_in_type_id
|
575
|
+
)
|
576
|
+
self.assertEqual(
|
577
|
+
all_in_pickings.mapped("location_dest_id"), self.warehouse.rma_loc_id
|
578
|
+
)
|
527
579
|
# Mass return of those four RMAs
|
528
580
|
delivery_wizard = (
|
529
581
|
self.env["rma.delivery.wizard"]
|
@@ -534,6 +586,10 @@ class TestRmaCase(TestRma):
|
|
534
586
|
# Two pickings were created
|
535
587
|
pick_1 = (rma_1 | rma_2 | rma_3).mapped("delivery_move_ids.picking_id")
|
536
588
|
pick_2 = rma_4.delivery_move_ids.picking_id
|
589
|
+
self.assertEqual(pick_1.picking_type_id, self.warehouse.rma_out_type_id)
|
590
|
+
self.assertEqual(pick_1.location_id, self.warehouse.rma_loc_id)
|
591
|
+
self.assertEqual(pick_2.picking_type_id, self.warehouse.rma_out_type_id)
|
592
|
+
self.assertEqual(pick_2.location_id, self.warehouse.rma_loc_id)
|
537
593
|
self.assertEqual(len(pick_1), 1)
|
538
594
|
self.assertEqual(len(pick_2), 1)
|
539
595
|
self.assertNotEqual(pick_1, pick_2)
|
@@ -549,7 +605,7 @@ class TestRmaCase(TestRma):
|
|
549
605
|
# line of picking_1
|
550
606
|
self.assertEqual(len(pick_1.move_ids), 3)
|
551
607
|
self.assertEqual(
|
552
|
-
pick_1.move_ids.
|
608
|
+
pick_1.move_ids.rma_id,
|
553
609
|
(rma_1 | rma_2 | rma_3),
|
554
610
|
)
|
555
611
|
self.assertEqual(
|
@@ -620,14 +676,14 @@ class TestRmaCase(TestRma):
|
|
620
676
|
origin_moves = origin_delivery.move_ids
|
621
677
|
self.assertTrue(origin_moves[0].rma_ids)
|
622
678
|
self.assertTrue(origin_moves[1].rma_ids)
|
623
|
-
rmas = origin_moves.
|
679
|
+
rmas = origin_moves.rma_ids
|
624
680
|
self.assertEqual(rmas.mapped("state"), ["confirmed"] * 2)
|
625
681
|
# Each reception move is linked one of the generated RMAs
|
626
682
|
reception = self.env["stock.picking"].browse(picking_action["res_id"])
|
627
683
|
reception_moves = reception.move_ids
|
628
684
|
self.assertTrue(reception_moves[0].rma_receiver_ids)
|
629
685
|
self.assertTrue(reception_moves[1].rma_receiver_ids)
|
630
|
-
self.assertEqual(reception_moves.
|
686
|
+
self.assertEqual(reception_moves.rma_receiver_ids, rmas)
|
631
687
|
# Validate the reception picking to set rmas to 'received' state
|
632
688
|
reception_moves[0].quantity = reception_moves[0].product_uom_qty
|
633
689
|
reception_moves[1].quantity = reception_moves[1].product_uom_qty
|
@@ -645,7 +701,7 @@ class TestRmaCase(TestRma):
|
|
645
701
|
rma = rma_form.save()
|
646
702
|
rma.action_confirm()
|
647
703
|
rma.reception_move_id.quantity = 10
|
648
|
-
rma.reception_move_id.picking_id.
|
704
|
+
rma.reception_move_id.picking_id.button_validate()
|
649
705
|
# Return quantity 4 of the same product to the customer
|
650
706
|
delivery_form = Form(
|
651
707
|
self.env["rma.delivery.wizard"].with_context(
|
@@ -686,6 +742,7 @@ class TestRmaCase(TestRma):
|
|
686
742
|
self.assertEqual(new_rma.move_id.quantity, 10)
|
687
743
|
self.assertEqual(new_rma.reception_move_id.quantity, 10)
|
688
744
|
|
745
|
+
@mute_logger("odoo.models.unlink")
|
689
746
|
def test_rma_to_receive_on_delete_invoice(self):
|
690
747
|
rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc)
|
691
748
|
rma.action_refund()
|
@@ -1,8 +1,25 @@
|
|
1
1
|
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
2
|
+
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
2
3
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
4
|
+
from copy import deepcopy
|
3
5
|
|
4
6
|
from odoo import _, api, fields, models
|
5
7
|
from odoo.exceptions import ValidationError
|
8
|
+
from odoo.tools import float_compare
|
9
|
+
|
10
|
+
|
11
|
+
class ReturnPickingLine(models.TransientModel):
|
12
|
+
_inherit = "stock.return.picking.line"
|
13
|
+
|
14
|
+
def _prepare_rma_vals(self):
|
15
|
+
self.ensure_one()
|
16
|
+
return {
|
17
|
+
"move_id": self.move_id.id,
|
18
|
+
"product_id": self.move_id.product_id.id,
|
19
|
+
"product_uom_qty": self.quantity,
|
20
|
+
"product_uom": self.product_id.uom_id.id,
|
21
|
+
"location_id": self.wizard_id.location_id.id or self.move_id.location_id.id,
|
22
|
+
}
|
6
23
|
|
7
24
|
|
8
25
|
class ReturnPicking(models.TransientModel):
|
@@ -48,6 +65,52 @@ class ReturnPicking(models.TransientModel):
|
|
48
65
|
location_id = return_picking_type.default_location_dest_id.id
|
49
66
|
self.location_id = location_id
|
50
67
|
|
68
|
+
def _prepare_rma_partner_values(self):
|
69
|
+
self.ensure_one()
|
70
|
+
partner = self.picking_id.partner_id
|
71
|
+
partner_address = partner.address_get(["invoice", "delivery"])
|
72
|
+
partner_invoice_id = partner_address.get("invoice", False)
|
73
|
+
partner_shipping_id = partner_address.get("delivery", False)
|
74
|
+
return (
|
75
|
+
partner,
|
76
|
+
partner_invoice_id and partner.browse(partner_invoice_id) or partner,
|
77
|
+
partner_shipping_id and partner.browse(partner_shipping_id) or partner,
|
78
|
+
)
|
79
|
+
|
80
|
+
def _prepare_rma_vals(self):
|
81
|
+
partner, partner_invoice, partner_shipping = self._prepare_rma_partner_values()
|
82
|
+
origin = self.picking_id.name
|
83
|
+
vals = self.env["rma"]._prepare_procurement_group_vals()
|
84
|
+
vals["partner_id"] = partner_shipping.id
|
85
|
+
vals["name"] = origin
|
86
|
+
group = self.env["procurement.group"].create(vals)
|
87
|
+
return {
|
88
|
+
"user_id": self.env.user.id,
|
89
|
+
"partner_id": partner.id,
|
90
|
+
"partner_shipping_id": partner_shipping.id,
|
91
|
+
"partner_invoice_id": partner_invoice.id,
|
92
|
+
"origin": origin,
|
93
|
+
"picking_id": self.picking_id.id,
|
94
|
+
"company_id": self.company_id.id,
|
95
|
+
"procurement_group_id": group.id,
|
96
|
+
}
|
97
|
+
|
98
|
+
def _prepare_rma_vals_list(self):
|
99
|
+
vals_list = []
|
100
|
+
for return_picking in self:
|
101
|
+
global_vals = return_picking._prepare_rma_vals()
|
102
|
+
for line in return_picking.product_return_moves:
|
103
|
+
if (
|
104
|
+
not line.move_id
|
105
|
+
or float_compare(line.quantity, 0, line.product_id.uom_id.rounding)
|
106
|
+
<= 0
|
107
|
+
):
|
108
|
+
continue
|
109
|
+
vals = deepcopy(global_vals)
|
110
|
+
vals.update(line._prepare_rma_vals())
|
111
|
+
vals_list.append(vals)
|
112
|
+
return vals_list
|
113
|
+
|
51
114
|
def create_returns(self):
|
52
115
|
"""Override create_returns method for creating one or more
|
53
116
|
'confirmed' RMAs after return a delivery picking in case
|
@@ -57,10 +120,6 @@ class ReturnPicking(models.TransientModel):
|
|
57
120
|
as the 'Receipt'.
|
58
121
|
"""
|
59
122
|
if self.create_rma:
|
60
|
-
# set_rma_picking_type is to override the copy() method of stock
|
61
|
-
# picking and change the default picking type to rma picking type
|
62
|
-
self_with_context = self.with_context(set_rma_picking_type=True)
|
63
|
-
res = super(ReturnPicking, self_with_context).create_returns()
|
64
123
|
if not self.picking_id.partner_id:
|
65
124
|
raise ValidationError(
|
66
125
|
_(
|
@@ -68,12 +127,30 @@ class ReturnPicking(models.TransientModel):
|
|
68
127
|
"'Stock Picking' from which RMAs will be created"
|
69
128
|
)
|
70
129
|
)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
]
|
76
|
-
self.env
|
77
|
-
|
78
|
-
|
79
|
-
|
130
|
+
vals_list = self._prepare_rma_vals_list()
|
131
|
+
rmas = self.env["rma"].create(vals_list)
|
132
|
+
rmas.action_confirm()
|
133
|
+
picking = rmas.reception_move_id.picking_id
|
134
|
+
picking = picking and picking[0] or picking
|
135
|
+
ctx = dict(self.env.context)
|
136
|
+
ctx.update(
|
137
|
+
{
|
138
|
+
"default_partner_id": picking.partner_id.id,
|
139
|
+
"search_default_picking_type_id": picking.picking_type_id.id,
|
140
|
+
"search_default_draft": False,
|
141
|
+
"search_default_assigned": False,
|
142
|
+
"search_default_confirmed": False,
|
143
|
+
"search_default_ready": False,
|
144
|
+
"search_default_planning_issues": False,
|
145
|
+
"search_default_available": False,
|
146
|
+
}
|
147
|
+
)
|
148
|
+
return {
|
149
|
+
"name": _("Returned Picking"),
|
150
|
+
"view_mode": "form,tree,calendar",
|
151
|
+
"res_model": "stock.picking",
|
152
|
+
"res_id": picking.id,
|
153
|
+
"type": "ir.actions.act_window",
|
154
|
+
"context": ctx,
|
155
|
+
}
|
156
|
+
return super().create_returns()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: odoo-addon-rma
|
3
|
-
Version: 17.0.
|
3
|
+
Version: 17.0.2.0.0
|
4
4
|
Requires-Python: >=3.10
|
5
5
|
Requires-Dist: odoo>=17.0a,<17.1dev
|
6
6
|
Summary: Return Merchandise Authorization (RMA)
|
@@ -23,7 +23,7 @@ Return Merchandise Authorization Management
|
|
23
23
|
!! This file is generated by oca-gen-addon-readme !!
|
24
24
|
!! changes will be overwritten. !!
|
25
25
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
26
|
-
!! source digest: sha256:
|
26
|
+
!! source digest: sha256:8f36869aece97a0f6af8aa5d76b446e9cf0bd589d914c1f5e12c628e87317021
|
27
27
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
28
28
|
|
29
29
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
|
@@ -156,6 +156,9 @@ Known issues / Roadmap
|
|
156
156
|
- As soon as the picking is selected, the user should select the move,
|
157
157
|
but perhaps stock.move \_rec_name could be improved to better show
|
158
158
|
what the product of that move is.
|
159
|
+
- Add RMA reception and/or RMA delivery on several steps - 2 or 3 -
|
160
|
+
like normal receptions/deliveries. It should be a separate option
|
161
|
+
inside the warehouse definition.
|
159
162
|
|
160
163
|
Bug Tracker
|
161
164
|
===========
|
@@ -191,6 +194,8 @@ Contributors
|
|
191
194
|
|
192
195
|
- Antoni Marroig <amarroig@apsl.net>
|
193
196
|
|
197
|
+
- Michael Tietz (MT Software) mtietz@mt-software.de
|
198
|
+
|
194
199
|
Maintainers
|
195
200
|
-----------
|
196
201
|
|