Skip to content

[19.0][MIG] rma: Migration from 18.0 to 19.0 - #616

Open
eugenios73 wants to merge 269 commits into
OCA:19.0from
Studio73:19.0-mig-rma
Open

[19.0][MIG] rma: Migration from 18.0 to 19.0#616
eugenios73 wants to merge 269 commits into
OCA:19.0from
Studio73:19.0-mig-rma

Conversation

@eugenios73

@eugenios73 eugenios73 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Additional changes included during migration

The migration preserves the existing functional behavior of the module. However,
during the adaptation, some pre-existing issues were identified and fixed, and
test coverage was added for behaviors that could suffer regressions in Odoo 19.

Fix in multiple receptions preparation

The method that prepares reception procurements iterates over each RMA in the
recordset, but some values were obtained from self instead of using the
rma record being processed in each iteration.

This behavior did not usually affect single RMA confirmations, but it could trigger
singleton errors or use data from an incorrect RMA when confirming multiple
returns with different products or operations together.

The preparation now consistently uses the product, return product configuration,
and alternative product from the RMA being processed in each iteration. This fixes
the existing confirmation and batch grouping flow without introducing new
functionality.

Explicit account assignment on refund lines

Credit note lines created directly from an RMA now explicitly receive the income
account configured for the product in the RMA's company.

The module creates these lines programmatically without going through the invoice
form view. Resolving the account during value preparation avoids relying on
implicit environment defaults and ensures that the generated line has a valid,
company-consistent accounting configuration.

The test configuration also explicitly creates a sales journal and an income
account. This avoids relying on demo data, which is no longer installed by
default in Odoo 19. The refund flow test additionally verifies that the expected
account is assigned to the resulting line.

Fix in reception grouping test coverage

The test verifying reception grouping was accidentally defined inside another test
method. As a result, the framework did not discover or execute it.

The test has been converted into an independent method to ensure that two
compatible RMAs are grouped into the same picking.

An assertion that compared the reference of the first RMA against itself (and thus
always returned a positive result) has also been fixed. Now, it verifies that RMAs
that should be grouped share the same reference, and that an RMA belonging to
another customer receives a different reference.

These changes do not modify the grouping rules: they ensure that the existing
behavior is properly covered against regressions.

Suggested recipient coverage in chatter

A test has been added to verify that the RMA customer continues to appear among
the suggested recipients when composing a message.

This coverage protects the existing functional behavior after adapting the
customization to Odoo 19's new suggested recipients API. The goal is not to change
which contacts are proposed, but to guarantee that the customer remains available
as a recipient after migration.

Picking dates coverage in portal

A dedicated HTTP test has been added for the RMA portal. The test creates an RMA
with automatic reception and delivery, initially accesses its portal page, validates
the related pickings, and accesses the page again.

This test covers portal rendering for both pending and completed pickings. Its
purpose is to catch template errors when rendering the scheduled date before
transfer and the effective date after validation.

This coverage accompanies the replacement of the old stock.picking.date field:
pending pickings show scheduled_date and completed ones show date_done.
Dates are displayed only when available and without including the time component,
keeping portal information aligned with the actual transfer state.

Functional scope

Beyond the described fixes, no new business functionality is intended to be
introduced. The remaining changes respond to framework, model, and API updates
required to run the module on Odoo 19.

Coverage Note

Functional tests pass successfully with both Odoo and OCB.

The failure of codecov/project does not represent an actual regression in coverage. The base branch 19.0 did not yet contain the rma module, and its report only measured 71 lines—mostly belonging to product_warranty—with a coverage of 98.59%.

With the addition of rma, Codecov now measures 2,360 lines. The resulting overall coverage is 89.74%, which appears as an 8.85% drop even though the compared sets have completely different scopes. In practice, the coverage of an almost empty branch is being compared to the full coverage of the migrated module.

The codecov/patch check is independent of this scope difference and currently shows 64%. Targeted functional tests are being added for the lines modified during the migration to improve this coverage, without lowering thresholds or excluding code from analysis.

Therefore:

  • codecov/project mainly reflects the addition of a complete module that did not yet exist in the base branch.
  • codecov/patch reflects the coverage of the changes in this migration and is the indicator currently being addressed with additional tests.
  • Test suites with Odoo and OCB execute successfully.

chienandalu and others added 30 commits July 30, 2026 13:14
Allow to set the desired shipping address where the goods must be
returned after the RMA is processed.
Now we can configure if an automatic notification should be sent when we
receive the goods from an RMA in our warehouse

If we've got `rma_sale` or `website_rma` we can also configure draft
notifications so when the customer places an RMA from the portal the
receive an acknowledge email.

TT29595
Now we can decide which tags are visible for the customer. This way, we
can use them as pseudo-states

TT29594
Co-authored-by: Ernesto Tejeda <ernesto.tejeda87@gmail.com>
Having a default responsible value as it was defined doesn't behave
right when the user it's a portal one or the petition comes from a
sudo().

Also, by design it was expected to have a default team if it wasn't
defined, but as it was implemented that wasn't possible.

TT32056
Since odoo/odoo#75823, the wizard lines have the field uom_id related to
move.product_uom and readonly=False, so if you call directly to
wiz.onchage_picking_id a write in stock move is executed and the warning raises,
although we don't really do any UoM change.

We avoid it using the `Form` to perform the wizard update.
The new implementation wasn't getting the value set in the wizard.

TT32046
Translated using Weblate (Portuguese)

Currently translated at 83.8% (269 of 321 strings)

Translation: rma-14.0/rma-14.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-14-0/rma-14-0-rma/pt/
Currently translated at 96.5% (310 of 321 strings)

Translation: rma-14.0/rma-14.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-14-0/rma-14-0-rma/it/
Currently translated at 98.7% (317 of 321 strings)

Translation: rma-14.0/rma-14.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-14-0/rma-14-0-rma/it/
Currently translated at 79.8% (257 of 322 strings)

Translation: rma-14.0/rma-14.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-14-0/rma-14-0-rma/es/
To create the rma we create the core return wizard, which now sets
to_refund to True as default. This isn't right for RMAs anyway.

TT33706
If rma_in_type_id or rma_out_type_id are already set, then the hook
fails. With this alternative code, we avoid it and clean it for being
clearer.
This way, we can reuse them in other modules easily
Sometimes there's no choice to return, refund or replace an RMA for
different reasons. For example, when the customer doesn't want to repair
the product because that would be too expensive. We still want finish
the RMA and document the reasons. This improvement allows it.

TT34164
In the tree view, the RMA code (`name` field) tends to lose in the
column width share. The result is will be the trimming of the reference
with ellipsis (...).

As this is very relevant information, we want to ensure a minimum width
for such reference.

TT35107
Currently translated at 99.1% (343 of 346 strings)

Translation: rma-14.0/rma-14.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-14-0/rma-14-0-rma/it/
victoralmau and others added 14 commits July 30, 2026 13:14
Use Bootstrap col for metadata row and w-25 for table labels.
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: rma-18.0/rma-18.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-18-0/rma-18-0-rma/
Currently translated at 99.5% (408 of 410 strings)

Translation: rma-18.0/rma-18.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-18-0/rma-18-0-rma/it/
Currently translated at 100.0% (410 of 410 strings)

Translation: rma-18.0/rma-18.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-18-0/rma-18-0-rma/it/
Steps to reproduce:
- Install rma + mrp
- Modify the ID of an rma operation (e.g., refund) in the database and set it to 90. The goal is to define an ID that does not exist in mrp_routing_workcenter table.
- Manually create an RMA using any product and select the operation from the previous step.
- Go to RMA > Overview and click on the previous operation from the Kanban view > Draft to access the previously created RMA.
- Click the Confirm button

Fixes OCA#523
Currently translated at 100.0% (410 of 410 strings)

Translation: rma-18.0/rma-18.0-rma
Translate-URL: https://translation.odoo-community.org/projects/rma-18-0/rma-18-0-rma/es/
Comment thread rma/migrations/19.0.1.0.0/pre-migrate.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/stock_move.py
"origin_returned_move_id",
"move_orig_ids",
"rma_receiver_ids",
"to_refund",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary now, when it wasn't necessary before (v18)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@victoralmau this is necessary in version 19 because stock.move.to_refund now defaults to True. RMA explicitly sets this value to False for operations other than update_quantity, but provisioning values ​​are only propagated to the generated movement when the field is included in _get_custom_move_fields().

Without this input, the explicit False value is discarded, and the new v19 default value is used, which modifies existing RMA behavior. Existing tests cover both cases: regular refund operations should result in to_refund=False, while update_quantity should result in to_refund=True. Please correct me if this is incorrect.

Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
Comment thread rma/models/rma.py Outdated
@victoralmau

Copy link
Copy Markdown
Member

Sorry, but I don't understand the suggestions to remove filtered to achieve the same result conceptually.
I think it's simply a matter of code design preferences and isn't related to the migration.

@Reyes4711-S73

Copy link
Copy Markdown

@victoralmau
Both options are O(n), but a for loop with continue is usually slightly more efficient because it does not create an intermediate recordset or execute a lambda:

for line in self.line_ids:
    if not line.is_valid:
        continue
    # Process line

Using filtered() can be more expressive:

for line in self.line_ids.filtered("is_valid"):
    # Process line

My rule of thumb:

  • Use continue if you only need the filtering for that loop or the condition is complex.
  • Use filtered() if it clearly improves readability or you will reuse the filtered recordset.
  • If you can avoid loading unnecessary lines by using a search() with a domain, that will have a much greater impact than choosing between these two options.

In practice, the difference is negligible for small recordsets, so prioritize readability. For large loops, continue has a slight performance advantage.

Also note that filtered() filters the recordset in Python; it does not execute a SQL query.

(thanks codex)

@pablo-cort-s73 pablo-cort-s73 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.